aboutsummaryrefslogtreecommitdiff
path: root/internal/game
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-29 13:28:06 -0400
committerhistoria <[not public]>2026-09-29 13:28:06 -0400
commit9803facd4368420a5dce3af59c835e13cb015cf5 (patch)
tree4ecb75f9c88b16ae72d3573bef026d760eb8d7a8 /internal/game
parent13db3e50cb6e29eedbf25416e5284bf08b0adefb (diff)
downloadthehouseoficarus-9803facd4368420a5dce3af59c835e13cb015cf5.tar.gz
feat: roulette in domita casino
Diffstat (limited to 'internal/game')
-rw-r--r--internal/game/blackjack_rules.go10
-rw-r--r--internal/game/casino.go2
-rw-r--r--internal/game/casino_ux.go13
-rw-r--r--internal/game/look_target.go2
-rw-r--r--internal/game/roulette_test.go78
5 files changed, 94 insertions, 11 deletions
diff --git a/internal/game/blackjack_rules.go b/internal/game/blackjack_rules.go
index 61aab47..b56d3ee 100644
--- a/internal/game/blackjack_rules.go
+++ b/internal/game/blackjack_rules.go
@@ -16,6 +16,10 @@ func (g *Game) lookBaccaratTable(sess *net.Session, target string) bool {
return g.lookCardTable(sess, target, casino.GameBaccarat)
}
+func (g *Game) lookRouletteTable(sess *net.Session, target string) bool {
+ return g.lookCardTable(sess, target, casino.GameRoulette)
+}
+
func (g *Game) lookCardTable(sess *net.Session, target string, game casino.GameName) bool {
room, err := g.World.LoadRoom(sess.Player.RoomID)
if err != nil {
@@ -35,11 +39,7 @@ func (g *Game) lookCardTable(sess *net.Session, target string, game casino.GameN
if !ok {
return false
}
- if game == casino.GameBaccarat {
- sess.WriteLine(table.Config.BaccaratRulesText())
- } else {
- sess.WriteLine(table.Config.BlackjackRulesText())
- }
+ sess.WriteLine(table.Config.RulesText())
return true
}
return false
diff --git a/internal/game/casino.go b/internal/game/casino.go
index 579371c..857a6a8 100644
--- a/internal/game/casino.go
+++ b/internal/game/casino.go
@@ -673,6 +673,8 @@ func (g *Game) renderCasinoSnapshot(sess *net.Session, snapshot casino.Snapshot)
size = 1
}
switch s := snapshot.(type) {
+ case *casino.RouletteSnapshot:
+ return s.ASCII()
case *casino.SlotSnapshot:
return renderSlotSnapshot(s, mode, unicode)
case *casino.BlackjackSnapshot:
diff --git a/internal/game/casino_ux.go b/internal/game/casino_ux.go
index fdd4533..d5ed67e 100644
--- a/internal/game/casino_ux.go
+++ b/internal/game/casino_ux.go
@@ -107,10 +107,7 @@ func (g *Game) executeTableStatus(sess *net.Session, args []string, _ string) {
return
}
if len(args) > 0 && args[0] == "rules" {
- text := t.Config.BlackjackRulesText()
- if t.Config.Game == casino.GameBaccarat {
- text = t.Config.BaccaratRulesText()
- }
+ text := t.Config.RulesText()
sess.WriteLine(g.colorize(sess, "casino_action", text))
return
}
@@ -154,7 +151,7 @@ func (g *Game) executeWatch(sess *net.Session, args []string, _ string) {
}
c, ok := resolveCasinoChoice(choices, input)
if !ok {
- sess.WriteLine("Choose a table to watch: watch blackjack or watch baccarat.")
+ sess.WriteLine("Choose a table to watch: watch blackjack, watch baccarat, or watch roulette.")
return
}
t, ok := g.casinoTable(sess.Player.RoomID, c.address)
@@ -181,6 +178,12 @@ func (g *Game) seesCasinoDetails(sess *net.Session, event casino.Event) bool {
// Settlement may have removed a queued departure before these events
// are delivered. Their own final cards must still be visible.
switch s := event.Snapshot.(type) {
+ case *casino.RouletteSnapshot:
+ for _, name := range s.Players {
+ if name == sess.Player.Name {
+ return true
+ }
+ }
case *casino.BlackjackSnapshot:
for _, p := range s.Players {
if p.Name == sess.Player.Name {
diff --git a/internal/game/look_target.go b/internal/game/look_target.go
index 76836d6..8d970e4 100644
--- a/internal/game/look_target.go
+++ b/internal/game/look_target.go
@@ -47,7 +47,7 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
return
}
- if g.lookBlackjackTable(sess, lower) || g.lookBaccaratTable(sess, lower) {
+ if g.lookBlackjackTable(sess, lower) || g.lookBaccaratTable(sess, lower) || g.lookRouletteTable(sess, lower) {
return
}
diff --git a/internal/game/roulette_test.go b/internal/game/roulette_test.go
new file mode 100644
index 0000000..0487b0b
--- /dev/null
+++ b/internal/game/roulette_test.go
@@ -0,0 +1,78 @@
+package game
+
+import (
+ "strings"
+ "testing"
+
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func TestRouletteNumericTargetsAndRendering(t *testing.T) {
+ for _, double := range []bool{false, true} {
+ g := &Game{Casino: casino.NewManager(1)}
+ cfg := casino.TableConfig{ID: "roulette", Game: casino.GameRoulette, Roulette: casino.RouletteConfig{DoubleZero: double}}
+ g.Casino.Join(1, cfg, "alice")
+ conn := &casinoTestConn{}
+ sess := &net.Session{Player: player.New("alice"), Conn: conn}
+ g.loggedInChars = map[string]*net.Session{"alice": sess}
+ for _, target := range []string{"0", "17", "00", "red", "column2"} {
+ conn.data = nil
+ g.executeBet(sess, []string{"10", "on", target}, "bet 10 on "+target)
+ expected := "You don't have enough chips and credits"
+ if target == "00" && !double {
+ expected = "Unknown roulette target"
+ }
+ if !strings.Contains(string(conn.data), expected) {
+ t.Fatalf("target=%s double=%v: %s", target, double, conn.data)
+ }
+ }
+ conn.data = nil
+ g.executeTableStatus(sess, []string{"rules"}, "table rules")
+ if !strings.Contains(string(conn.data), "Straight numbers pay 35:1") || strings.Contains(string(conn.data), "Blackjack") {
+ t.Fatal("wrong rules", string(conn.data))
+ }
+ snapshot := &casino.RouletteSnapshot{DoubleZero: double, Phase: "playing", Players: []string{"alice"}}
+ if got := g.renderCasinoSnapshot(sess, snapshot); got != snapshot.ASCII() {
+ t.Fatal("snapshot not rendered", got)
+ }
+ if !g.seesCasinoDetails(sess, casino.Event{RoomID: 1, TableID: "roulette", Snapshot: snapshot}) {
+ t.Fatal("player cannot see wheel")
+ }
+ }
+}
+
+func TestCasinoTestPortalAuthoredContract(t *testing.T) {
+ g, rooms, _ := loadVesta(t)
+ found := false
+ for _, obj := range rooms[1001].Objects {
+ found = found || obj.ID == "casino_test_portal"
+ }
+ if !found {
+ t.Fatal("portal missing from starting room")
+ }
+ obj, err := g.ObjectStore.Load("casino_test_portal")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(obj.OnUse) != 1 || len(obj.OnUse[0].Steps) != 2 {
+ t.Fatal("unexpected portal sequence")
+ }
+ steps := obj.OnUse[0].Steps
+ if len(steps[0].DropTable) != 1 || steps[0].DropTable[0].ItemID != "chips" || steps[0].DropTable[0].Quantity != 1000 {
+ t.Fatal("portal does not grant 1000 chips")
+ }
+ if steps[1].Teleport != 6100 || rooms[6100] == nil {
+ t.Fatal("wrong casino destination")
+ }
+ for _, id := range []int{6182, 6183} {
+ cfg := rooms[id].CasinoTables[0]
+ if err := cfg.Validate(); err != nil {
+ t.Fatal(err)
+ }
+ if cfg.Game != casino.GameRoulette || cfg.Roulette.DoubleZero != (id == 6183) {
+ t.Fatal("wrong roulette rules", id)
+ }
+ }
+}