Software für Hearthstone

Mich interessiert es, welche Software ihr in Hearthstone so einsetzt und wie ihr diese bewertet.

Bis zu dieser Erweiterung war ich ein Purist ohne zusätzliche Software, aber ich es wurde mir zu nervig. Ich habe mir meine eigenen Skripte geschrieben (bei Interesse siehe unten (1) und (2)), bin mir jedoch nicht sicher, ob ich weitere Software einsetzen sollte (aber auch irgendwie in Stimmung dazu gekommen).

Für den Fall dass noch jemand diese Skripts nützlich findet, füge ich diese hier mit einer kurzen Beschreibung ein (und um mich rechtlich abzusichern: Benutzung auf eigene Gefahr).


(1)

Meine Motivation Bislang habe ich immer alle meine Decks ohne Software per Hand abgespeichert \(Deckstring kopieren; Notepad öffnen, einfügen, speichern, beenden; Wordpad öffnen, speichern, beenden\) war zwar etwas umständlich, aber ich habe selten Decks als "speicherwürdig" angesehen. Aber irgendwann hatte ich keine Lust mehr, und so hatte ich 27 Decks "rumfliegen", die ich noch speichern wollte...).

Um das Ganze zu vereinfachen habe ich mir eine hybride JScript-Batch geschrieben um den Deckstring in das Verzeichnis der Batch-Datei zu speichern (Deckstring kopieren; Doppelclick auf die entsprechende Batch Datei).

Um es mir noch einfacher zu machen habe ich an den Namen der Skriptdatei etwas Funktion gebunden:
Heisst die “saveDeck..bat”, so speichert sie das Deck unter “<Datum> <optional> <Titel>[000-999].<Klasse>.txt”.
Heisst sie “saveDeck.<optional>.brawl.bat” so ist das Format der Textdatei “<optional>[000-999].txt”
(Die Nummern 000 bis 999 werden bei doppelten Dateinamen automatisch generiert.)

Ich benutze (ist aber Geschmackssache) die Datei derzeit für Wild/Standard mit den folgenden Dateinamen:
“saveDeck.EU.bat”, “saveDeck.US.bat”, “saveDeck.Asia.bat”.

Für wild werde ich den Titel immer an das Format
“saveDeck.<Kalenderwoche> [EU|US|Asia] <Titel des Kartenchaos’>.brawl.bat” anpassen, diese Woche also:
“saveDeck.2019 KW 15 EU Kartenkollosseum - Standard.brawl.bat”
(Ich werde aber nur eine Datei erstellen, um nicht ständig den Titel ändern zu müssen; nur EU, US, Asia ändere ich bei Bedarf falls nötig.)

Quelltext Speichermodul
@if (true == false) @end /*
@echo off
cls
setlocal enableExtensions disableDelayedExpansion
cscript //nologo //e:JScript "%~f0" "location:%~dp0"
if errorlevel 1 ( pause & pause )
goto :eof
*/

function getClipBoard() {
	var html = new ActiveXObject("HTMLFile");
	var text = html.parentWindow.clipboardData.getData("text");

	delete html;

	return text;
}

function getHsRegExp() {
	var d = "[A-Za-z0-9+/]";		// base64 digit
	var p = "=";				// base 64 padding code
	var b4 = d + "{4}";			// base 64 4 digits block, no padding
	var b3 = d + "{3}" + p + "?";		// base 64 2 digits block, 0 or 1 padding chars
	var b2 = d + "{2}(?:" + p + "{2})?";	// base 64 1 digits block, 0 or 2 padding chars

	return new RegExp (
		"^" +
		"(?:### .*\\n)" +
		"(?:# [^:]+:[^:]+\\n){2}" +
		"(?:#.*\\n)+" +
		"(?:" + b3 + ")*(?:.*)\\n" + // "(?:" + b3 + "|" + b2 + "|" + b1 + "" +
		"(?:#.*\\n)+" +
		"$"
	);
}

function nextToken(text, tokenPos, prefixDelim, postfixDelim) {
	tokenPos.sot = text.indexOf(prefixDelim, tokenPos.eot) + 1;
	tokenPos.eot = text.indexOf(postfixDelim, tokenPos.sot);
	if (tokenPos.sot > tokenPos.eot) tokenPos.eot = tokenPos.sot;
	return text.substring(tokenPos.sot, tokenPos.eot);
}

function getFilename(prefix, extension) {
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var shell = new ActiveXObject("WScript.Shell");
	var folder = fso.GetFolder(fso.GetAbsolutePathName("."));
	var filename = null;

	if (!fso.FileExists(prefix + extension)) {
		filename = prefix + extension;
	} else {
		var N = folder.files.Count;
		for (var i = 0; i < N; ++i) {
			counter = " " + ("" + (1000 + i)).substring(1);

			if (!fso.FileExists(prefix + counter + extension)) {
				filename = prefix + counter + extension;
				break;
			}
		}
	}

	delete shell;
	delete fso;

	return filename;
}

function saveFile(filename, text) {
	var stream = WScript.CreateObject("ADODB.Stream");
	stream.Open();
	stream.Charset = "UTF-8";
	stream.WriteText("");				// force bom creation
	stream.WriteText(text);
	stream.SaveToFile(filename, 2);			// adSaveCreateOverWrite = 2
	stream.Flush();

	delete stream;
}

function main() {
	var text = getClipBoard().replace(/\r\n/g, "\n");
	var regexp = getHsRegExp();

	if (regexp.test(text) == true) {
		var date = new Date();
		var tokenPos = {
			sot : 0,	// start of token
			eot : 0		// end of token
		};

		var title = nextToken(text, tokenPos, " ", "\n");
		var avatarClass = nextToken(text, tokenPos, ":", "\n").substring(1);
		var format = nextToken(text, tokenPos, ":", "\n").substring(1);
		var era = nextToken(text, tokenPos, "#", "\n").substring(1);

		tokenPos.sot = 0; tokenPos.eot = 0;
		var optionalString = nextToken(WScript.ScriptName, tokenPos, ".", ".");
		var optionalType = (optionalString.length != 0) ? nextToken(WScript.ScriptName, tokenPos, ".", ".") : "";

		var year = date.getFullYear();
		var month = date.getMonth() + 1; month = (month < 10) ? "0" + month : "" + month;
		var day = date.getDate(); day = (day < 10) ? "0" + day : "" + day;

		var filename = (optionalType == "brawl") ? 
			getFilename(optionalString, "." + avatarClass + ".txt") :
			getFilename(year + " " + month + " " + day + " " + optionalString + " " + title, "." + avatarClass + ".txt");
/** /
		WScript.echo("title         : \"" + title + "\"");
		WScript.echo("avatarClass   : \"" + avatarClass + "\"");
		WScript.echo("format        : \"" + format + "\"");
		WScript.echo("era           : \"" + era + "\"");		// brawl only?
		WScript.echo("optionalString: \"" + optionalString + "\"");
		WScript.echo("filename      : \"" + filename + "\"");
		WScript.echo("text          : \"" + text + "\"");

/**/
		saveFile(filename, text.replace(/\n/g, "\r\n"));

		delete date;

		WScript.Quit(0);
	} else {
		WScript.echo("Invalid File: " + text);
		WScript.Quit(1);
	}
}

main();

(2)
Zum Laden der Decks habe ich mir ein kleines Batch-Skript geschrieben, dass die Datei die den Deckstring enthält per Drag-and-Drop auf die Batch in die Zwischenablage lädt).

"loadDeck.bat"
@echo off
setlocal enableExtensions disableDelayedExpansion
>nul chcp 65001
if not "%~1" == "" type "%~1" | @(>nul (pause & pause & pause) & findstr "^") | clip
goto :eof

PS: Ich hoffe, dass man die Quelltexte ohne Nebeneffekte der Forensoftware mit copy-paste in eine Batch transferieren kann:
Dennoch sollte man den Quelltext vergleichen, ehe man die Datei abspeichert.

Ich hab ein paar Dinge. Aber ich spiel im Moment nicht so viel. Einen rudimentären Replay Viewer, der sich die Replays aus den Log-Dateien holt nachdem HS wieder geschlossen ist, einen Deckstring-Manager + Kartendatenbank mit ein paar Suchmöglichkeiten und für den Discord-Server einen Bot der ein paar Dinge im zusammenhang mit Hearthstone und den vergangenen Forenturniere regelt, wie beispielsweise das man einen Decksstring gegen fürs Turnier gebannte Karten prüfen konnte und sowas wie Kartenlinks.

Da es mir zu blöd war nach jedem absolvierten Erfolg den nächsten nachzuschlagen, habe ich eine neue Batch-Datei geschrieben, die eine Textdatei („Achievements.todo.txt“) mit allen noch benötigten Erfolgen (nur die jeweils nächste Stufe) erstellt und diese dann öffnet.
(Ist vermutlich nur nützlich im Window-Modus, den ich benutze.)

Im Sourcecode vor allen erreichten Erfolgen ein Semikolon, setzen um dem Programm mitzuteilen, dass ihr diese schon erhalten habt und dann die Batch doppelklicken um eine aktualisierte Fassung dieser Datei zu erstellen.

Ich habe noch ein paar Lücken („Gebt Euch der leere hin“ 1-4 und „Kostet nur den Verstand“ 1-4), da ich diese Erfolge schon versehentlich erfüllt habe (und diese vor Stufe 35 nicht abgeben will).
Auch weiß ich nicht genau, wo die Quest „Dermatologie“ positioniert war, die ich erfüllt und abgegeben hatte um zu sehen, ob es Bonus-XP geben würde (leider ohne vorher ins Forum zu schauen und zu sehen, dass VBrainstone das ebenfalls schon getestet hatte, siehe hier.

Sourcecode "Achievements.bat"
@echo off
setlocal enableExtensions enableDelayedExpansion
>"Achievements.todo.txt" call :main
start "" "Achievements.todo.txt"
goto :eof

:main
if not defined skip for /f "delims=: eol=" %%a in ('findstr /n /r "^;Erfolge$" "%~f0"') do set "skip=%%~a"

set /a "available_XP=0, available_EP=0"
for %%a in ("category" "subcategory" "group" "title" "text" "level" "XP=" "EP="  "treasure=") do (
	set "previous_%%~a="
	set "%%~a="
)

for /f "tokens=1* skip=%skip% eol=; delims=:" %%a in ('findstr /r "^" "%~f0"') do (
	for %%A in (%%a) do set "%%~A"
	set /a "available_XP+=XP, available_EP+=EP"
	if defined XP set "XP=!XP! Belohnungserfarung, "

	if NOT "!previous_category!" == "!category!" (
		echo([!category!]
	) else if NOT "!previous_subcategory!" == "!subcategory!" (
		echo([[!subcategory!]]
	) else if NOT "!previous_group!" == "!group!" (
		echo(
		echo(;!group!
	)
	if NOT "!previous_title!" == "!title!" (
		echo(!title!
		echo(!text!
		echo(!XP!!EP! Erfolgspunkte
		echo(
	)
	for %%a in ("category" "subcategory" "group" "title" "text" "level" "XP=" "EP="  "treasure=") do set "previous_%%~a=!%%~a!"
)

echo(;Es sind noch !available_XP! Belohnungserfarung (ohne Boost) und !available_EP! Erfolgspunkte verfügbar.

goto :eof

;Erfolge
;Achievements
;...
;===
"category=Gameplay"
"subcategory=Gameplay - Dunkelmond-Wahnsinn"
"group=Dämonenjäger"
"title=Guter Wurf" "text=Vernichtet in einem einzigen Zug 5 Diener mit einer Gleve" "XP=200" "EP=20"
"title=Gemeinsam einsam" "text=Kontrolliert einen Geläuterten Pariah mit 10 Angriff oder mehr." "XP=200" "EP=20"
"title=Auf Entzug" "text=Verursacht 10 Schaden mit dem Effekt von Il'gynoth" "level=1" "XP=100" "EP=10"
"title=Auf Entzug" "text=Verursacht 30 Schaden mit dem Effekt von Il'gynoth" "level=2" "XP=100" "EP=10"
"title=Auf Entzug" "text=Verursacht 100 Schaden mit dem Effekt von Il'gynoth" "level=3" "XP=500" "EP=20"
"group=Druide"
"title=BETRUG^^^!" "text=Erratet das Gewicht eurer nächsten Karte 10-mal richtig." "XP=200" "EP=20"
"title=Sonne, Mond ... und keine Sterne" "text=Wirkt 6 Sonnen oder Mondfinsternisse in einem einzigen Spiel." "XP=200" "EP=20"
"title=Verzweigt" "text=Ruft 20-mal Grauzweig herbei." "level=1" "XP=100" "EP=10"
"title=Verzweigt" "text=Ruft 100-mal Grauzweig herbei." "level=2" "XP=100" "EP=10"
"title=Verzweigt" "text=Ruft 200-mal Grauzweig herbei." "level=3" "XP=500" "EP=20"
"group=Jäger"
"title=Wir stehen und fallen zusammen" "text=Kontrolliert 3 identische Diener mit Todesröcheln." "XP=200" "EP=20"
"title=Werft mich^^^!" "text=Besiegt euren Gegner, indem ihr einen Diener auf ihn abfeuert." "XP=200" "EP=20"
"title=Panzergeneral" "text=Verursacht 50 Schaden mit dem Dunkelmondpanzer." "level=1" "XP=100" "EP=10"
"title=Panzergeneral" "text=Verursacht 150 Schaden mit dem Dunkelmondpanzer." "level=2" "XP=100" "EP=10"
"title=Panzergeneral" "text=Verursacht 500 Schaden mit dem Dunkelmondpanzer." "level=3" "XP=500" "EP=20"
"group=Magier"
"title=Totenmaske" "text=Verursacht tödlichen Schaden mit der Maske von C'Thun." "XP=200" "EP=20"
"title=Spiegelsaal" "text=Kontrolliert 4 Okkulte Beschwörerinnen gleichzeitig" "XP=200" "EP=20"
"title=Eure Zukunft ist umschattet" "text=Zieht 5 Karten mit Sayge" "level=1" "XP=100" "EP=10"
"title=Eure Zukunft ist umschattet" "text=Zieht 20 Karten mit Sayge" "level=2" "XP=100" "EP=10"
"title=Eure Zukunft ist umschattet" "text=Zieht 50 Karten mit Sayge" "level=3" "XP=500" "EP=20"
"group=Paladin"
"title=Ihr habt geläutet❔" "text=Stellt mit Hochexarchin Yrel 100 Leben wieder her." "XP=200" "EP=20"
"title=Yogg Champ" "text=Verursacht Tödlichen Schaden mit Oh mein Yogg^^^!" "XP=200" "EP=20"
"title=Lothraxions Ballonladen" "text=Verleiht 10 Rekruten der Silbernen Hand Gottesschild" "level=1" "XP=100" "EP=10"
"title=Lothraxions Ballonladen" "text=Verleiht 30 Rekruten der Silbernen Hand Gottesschild" "level=2" "XP=100" "EP=10"
"title=Lothraxions Ballonladen" "text=Verleiht 100 Rekruten der Silbernen Hand Gottesschild" "level=3" "XP=500" "EP=20"
"group=Priester"
"title=Spoiler^^^!" "text=Ruft einen Wahrsager mit 10 Angriff oder mehr herbei." "XP=200" "EP=20"
"title=Einer hat keinen Namen" "text=Stehlt gleichzeitig 3 oder mehr Verzauberungen der Namenlosen." "XP=200" "EP=20"
"title=Blut für den Blutgott" "text=Verbraucht als Priester 10 Leben, um Karten auszuspielen." "level=1" "XP=100" "EP=10"
"title=Blut für den Blutgott" "text=Verbraucht als Priester 30 Leben, um Karten auszuspielen." "level=2" "XP=100" "EP=10"
"title=Blut für den Blutgott" "text=Verbraucht als Priester 100 Leben, um Karten auszuspielen." "level=3" "XP=500" "EP=20"
"group=Schurke"
"title=Verbotenes Jutsu" "text=Ruft mit Schattenklon einen Diener herbei, der 10 oder mehr kostet." "XP=200" "EP=20"
"title=Myras instabile Kaiserin" "text=Zieht mit dem Kampfschrei einer einzigen Großkaiserin Shek'zara 5 Karten." "XP=200" "EP=20"
"title=ES IST SOOO FLAUSCHIG" "text=Verursacht 10 Schaden mit dem Combo-Effekt des Preisplünderers." "level=1" "XP=100" "EP=10"
"title=ES IST SOOO FLAUSCHIG" "text=Verursacht 30 Schaden mit dem Combo-Effekt des Preisplünderers." "level=2" "XP=100" "EP=10"
"title=ES IST SOOO FLAUSCHIG" "text=Verursacht 100 Schaden mit dem Combo-Effekt des Preisplünderers." "level=3" "XP=500" "EP=20"
"group=Schamane"
"title=Schlaghagel" "text=Verursacht in einem einzigen Zug 16 Schaden mit Heldenangriffen." "XP=200" "EP=20"
"title=Drehwurm" "text=Verwandelt mit einem einzigen Karussel 14 Diener gleichzeitg." "XP=200" "EP=20"
"title=Dunkelmond-Schreinerei" "text=Verstärkt 10 Totems mit Eys'or" "level=1" "XP=100" "EP=10"
"title=Dunkelmond-Schreinerei" "text=Verstärkt 30 Totems mit Eys'or" "level=2" "XP=100" "EP=10"
"title=Dunkelmond-Schreinerei" "text=Verstärkt 100 Totems mit Eys'or" "level=3" "XP=" "XP=500" "EP=20"
"group=Hexenmeister"
"title=Heilige Handgranate gesucht" "text=Spielt ein Dunkelmond-Kannchen aus, das (1) kostet." "XP=200" "EP=20"
"title=Mein Freund ist ganz vorn" "text=Zieht 2 Dämonen mit Freier Eintritt und verringert die Kosten von beiden auf (0)." "XP=200" "EP=20"
"title=Eskalierende Eskarpaden" "text=Vernichtet 5 Diener mit der Katastrophenkaskade." "level=1" "XP=100" "EP=10"
"title=Eskalierende Eskarpaden" "text=Vernichtet 15 Diener mit der Katastrophenkaskade." "level=2" "XP=100" "EP=10"
"title=Eskalierende Eskarpaden" "text=Vernichtet 50 Diener mit der Katastrophenkaskade." "level=3" "XP=500" "EP=20"
"group=Krieger"
"title=Gute Mine zum Bösen Spiel" "text=Vernichtet 5 Diener mit einem einzigen Minenfeld." "XP=200" "EP=20"
"title=Der Zeltenbrecher" "text=Spielt einen Zeltzerstörer aus, der (0) kostet." "XP=200" "EP=20"
"title=Trommelwirbel" "text=Löst 5 Schlagzeugsolos mit E.T.C., Gott des Metal aus." "level=1" "XP=100" "EP=10"
"title=Trommelwirbel" "text=Löst 15 Schlagzeugsolos mit E.T.C., Gott des Metal aus." "level=2" "XP=100" "EP=10"
"title=Trommelwirbel" "text=Löst 50 Schlagzeugsolos mit E.T.C., Gott des Metal aus." "level=3" "XP=500" "EP=20"
"group=Neutral"
"title=Gebt Euch der leere hin" "text=Spielt 15 verderbte Karten aus." "level=1" "XP=" "EP="
"title=Gebt Euch der leere hin" "text=Spielt ❔ verderbte Karten aus." "level=2" "XP=" "EP="
"title=Gebt Euch der leere hin" "text=Spielt ❔ verderbte Karten aus." "level=3" "XP=" "EP="
"title=Gebt Euch der leere hin" "text=Spielt ❔ verderbte Karten aus." "level=4" "XP=" "EP="
"title=Kostet nur den Verstand" "text=Spielt eine verderbte Karte aus, die (0) kostet." "level=1" "XP=" "EP="
"title=Kostet nur den Verstand" "text=Spielt ❔ verderbte Karte aus, die (0) kostet." "level=2" "XP=" "EP="
"title=Kostet nur den Verstand" "text=Spielt ❔ verderbte Karte aus, die (0) kostet." "level=3" "XP=" "EP="
"title=Kostet nur den Verstand" "text=Spielt ❔ verderbte Karte aus, die (0) kostet." "level=4" "XP=" "EP="
"title=Coulrophobie" "text=Füllt Eure Seite des Schlachtfelds mit Jahrmarktsclowns." "XP=200" "EP=20"
"title=Harmloser kleiner Nager" "text=Vernichtet 5 mit einem Dunkelmond-Kaninchen." "level=1" "XP=100" "EP=10"
"title=Harmloser kleiner Nager" "text=Vernichtet 10 mit einem Dunkelmond-Kaninchen." "level=2" "XP=100" "EP=10"
"title=Harmloser kleiner Nager" "text=Vernichtet 25 mit einem Dunkelmond-Kaninchen." "level=3" "XP=200" "EP=20"
"title=Exitus" "text=Besiegt Euren Gegner, der vorher volles Leben hatte, mit C'Thun dem Zerschlagenen." "XP=500" "EP=20"
"title=Die Toten sollen dienen" "text=Belebt 6 Diener gleichzeitig mit N'Zoth, Gott der Tiefen wieder." "XP=500" "EP=20"
"title=Gutes Rad ist teuer" "text=Dreht das Rad von Yogg-Saron" "level=1" "XP=100" "EP=10"
"title=Gutes Rad ist teuer" "text=Dreht das Rad von Yogg-Saron, um zu gewinnen." "level=2" "XP=200" "EP=20"
"title=Unglücksrad" "text=Löst 3 verschiedene Effekte des Rads von Yogg-Saron aus." "level=1" "XP=100" "EP=10"
"title=Unglücksrad" "text=Löst alle Effekte des Rads von Yogg-Saron aus." "level=2" "XP=200" "EP=20"
"title=Dermatologie" "text=Spielt ein Entsetzliches Geschwür mit 7 Angriff oder mehr aus." "level=1" "XP=100" "EP=10"
"title=Dermatologie" "text=Spielt ein Entsetzliches Geschwür mit 15 Angriff oder mehr aus." "level=2" "XP=200" "EP=20"
;Gameplay - Allgemein
;Klassen
;...
;Neutral
;...
;===
;...
;
;;Kategorie
;"title=" "text=" ["level="] ["XP="] "EP=" ["treasure="]

Edit: Da mich gerade jemand darauf angesprochen hatte:
Nein, das soll kein Necromancing sein, da dieses Thema eine Sammlung von HS-Software sein soll und meine neue Batch das thematisch halt exakt trifft. Und für diese eine Batch ein neues Thema „aufzumachen“, fände ich zu merkwürdig.

Edit2: Bugfixes: (Falls jemand weitere findet, bitte melden.)
(1) Korrektur „Stelht“ zu „Stehlt“.
(2) Die for-Schleife hat anscheinend Probleme mit „?“ in den Daten und ignoriert dadurch die set-Befehle, daher habe ich alle „?“ durch „:grey_question:“ ersetzt (und hoffe, dass das Forum dieses Unicode-Zeichen nicht weiter verändert).

2 Likes