Zum Inhalt springen
  • Dunkel
  • Hell
  • System

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.

Core C# Code Execute C# Code

Doku: docs.streamer.bot · Core Misc Methoden

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() 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.

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(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.

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.

  • new Random() in einer Schleife oder pro Aufruf: Mehrere Instanzen im selben Millisekunden-Tick teilen sich den Seed und liefern identische Zahlen. Nimm CPH.Between und CPH.NextDouble, die eine geteilte Quelle nutzen.
  • Inklusivität von CPH.Between falsch angenommen: Beide Grenzen sind inklusiv. CPH.Between(1, 6) schließt die 6 mit ein, anders als das exklusive new Random().Next(1, 6). Wer das verwechselt, baut sich einen Off-by-one-Bug ein.
  • CPH.Wait für lange Pausen in viel genutzten Actions: Wait blockiert 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 den else-Zweig.