Zufall & Wartezeiten: Between, NextDouble, Wait
Würfeln, Prozent-Chancen, gewichtete Loot-Tabellen, kleine Pausen zwischen Chat-Zeilen: dafür liefert das CPH-Objekt zwei Zufallshelfer und eine Pause-Methode. Du musst dir kein eigenes Random bauen und solltest es auch nicht, denn genau dort lauert eine der häufigsten Fallen. Geschrieben wird der Code wie immer in der Sub-Action Execute C# Code.
Doku: docs.streamer.bot · Core Misc Methoden
CPH.Between für Ganzzahlen
Abschnitt betitelt „CPH.Between für Ganzzahlen“CPH.Between(int min, int max) gibt eine zufällige Ganzzahl zurück. Beide Grenzen sind inklusiv: CPH.Between(1, 6) kann jeden Wert von 1 bis 6 liefern, also auch die 1 und die 6. Das ist anders als das blanke new Random().Next(1, 6), dessen Obergrenze exklusiv ist. Diese Diskrepanz ist eine der typischen Off-by-one-Fallen, wenn man von eigenem Random umsteigt.
public class CPHInline { public bool Execute() { int wurf = CPH.Between(1, 6); CPH.SendMessage($"🎲 Du würfelst eine {wurf}."); return true; }}Ein W6-Wurf wie oben deckt also sauber die 1 bis 6 ab. Für einen Münzwurf reicht CPH.Between(0, 1), für ein Prozent-Roll auf Ganzzahl-Basis CPH.Between(1, 100).
CPH.NextDouble für Chancen
Abschnitt betitelt „CPH.NextDouble für Chancen“CPH.NextDouble() gibt einen double zwischen 0.0 und 1.0 zurück. Das ist ideal für Prozent-Chancen, weil du den Wert direkt mit einem Schwellenwert vergleichen kannst.
public class CPHInline { public bool Execute() { // 25% Chance auf einen Crit if (CPH.NextDouble() < 0.25) { CPH.SendMessage("💥 Kritischer Treffer!"); } else { CPH.SendMessage("Normaler Treffer."); } return true; }}CPH.NextDouble() < 0.25 ist in genau 25 Prozent der Fälle wahr. Eine 5-Prozent-Chance wäre < 0.05, eine 70-Prozent-Chance < 0.70. Den Schwellenwert kannst du natürlich aus einer Global ziehen, etwa per CPH.GetGlobalVar<double>("critChance", true), falls du ihn ohne Code-Änderung anpassbar halten willst.
Warum kein eigenes new Random()
Abschnitt betitelt „Warum kein eigenes new Random()“Es ist verlockend, im Code einfach var rnd = new Random(); zu schreiben. Das funktioniert in einem einzelnen, ruhigen Aufruf auch. Das Problem zeigt sich, sobald die Action schnell hintereinander läuft: new Random() wird in der klassischen Variante mit der Systemzeit als Seed initialisiert, und die Auflösung dieser Zeit ist grob. Zwei Random-Instanzen, die im selben Millisekunden-Tick erzeugt werden, bekommen denselben Seed und liefern danach dieselbe Zahlenfolge. Bei einem Command, den mehrere Zuschauer in derselben Sekunde abfeuern, würfeln dann alle exakt gleich.
CPH.Between und CPH.NextDouble umgehen das, weil Streamer.bot intern eine geteilte Zufallsquelle nutzt statt pro Aufruf eine neue Instanz zu bauen. Nimm also immer die CPH-Methoden und konstruiere kein eigenes Random pro Execute()-Lauf.
CPH.Wait für Pausen
Abschnitt betitelt „CPH.Wait für Pausen“CPH.Wait(int milliseconds) pausiert die Ausführung für die angegebene Zeit. CPH.Wait(1000) wartet eine Sekunde.
Für einen kurzen Countdown ist Wait aber genau richtig:
public class CPHInline { public bool Execute() { CPH.SendMessage("⏳ Start in 3 ..."); CPH.Wait(1000); CPH.SendMessage("2 ..."); CPH.Wait(1000); CPH.SendMessage("1 ..."); CPH.Wait(1000); CPH.SendMessage("🚀 Los!"); return true; }}Die vier Zeilen erscheinen im Sekundentakt. Wichtig: Während dieser drei Sekunden ist der Worker belegt. Für einen einmaligen Countdown ist das unkritisch, für eine Action, die alle paar Sekunden läuft und jedes Mal wartet, eher nicht.
Beispiel: gewichtete Zufallsauswahl
Abschnitt betitelt „Beispiel: gewichtete Zufallsauswahl“Bei einer Loot-Tabelle sollen die Ergebnisse nicht gleich wahrscheinlich sein. Mit einem einzigen CPH.NextDouble() und kumulierten Schwellen lässt sich das sauber abbilden:
public class CPHInline { public bool Execute() { double roll = CPH.NextDouble();
string loot; if (roll < 0.60) { loot = "🟢 Common (60%)"; // 0.00 .. 0.60 } else if (roll < 0.90) { loot = "🔵 Rare (30%)"; // 0.60 .. 0.90 } else if (roll < 0.99) { loot = "🟣 Epic (9%)"; // 0.90 .. 0.99 } else { loot = "🟠 Legendary (1%)"; // 0.99 .. 1.00 }
CPH.SendMessage($"Du lootest: {loot}"); return true; }}Der Trick: Die Bereiche stoßen lückenlos aneinander. roll < 0.60 deckt 60 Prozent ab, das else if (roll < 0.90) greift nur für die restlichen Werte ab 0.60, also weitere 30 Prozent, und so weiter. Die Prozente in den Schwellen müssen sich auf 1.0 summieren, sonst stimmen die Chancen nicht.
Häufige Fallen
Abschnitt betitelt „Häufige Fallen“new Random()in einer Schleife oder pro Aufruf: Mehrere Instanzen im selben Millisekunden-Tick teilen sich den Seed und liefern identische Zahlen. NimmCPH.BetweenundCPH.NextDouble, die eine geteilte Quelle nutzen.- Inklusivität von
CPH.Betweenfalsch angenommen: Beide Grenzen sind inklusiv.CPH.Between(1, 6)schließt die 6 mit ein, anders als das exklusivenew Random().Next(1, 6). Wer das verwechselt, baut sich einen Off-by-one-Bug ein. CPH.Waitfür lange Pausen in viel genutzten Actions:Waitblockiert den Worker-Thread, nicht nur deine Action. Lange oder häufige Waits stauen die Queue. Details und Alternativen unter Threading & Performance.- Gewichtete Schwellen summieren sich nicht auf 1.0: Bei kumulierten
NextDouble-Vergleichen müssen die Bereiche lückenlos bis 1.0 reichen. Fehlt ein Stück, fällt es unbemerkt in denelse-Zweig.