package game // Regression: credit pickups must never destroy value at the economy cap. // Only the balance headroom below casino.MaxCredits may be removed from the // ground; the remainder stays there. The previous code removed the whole // ground stack and then let grantCredits clamp the excess away. import ( "strings" "testing" "thehouseoficarus/internal/casino" "thehouseoficarus/internal/net" ) func TestPickupCreditsAtCapLeavesRemainderOnGround(t *testing.T) { h := newHarness(t) h.seedAccount("alice") p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = casino.MaxCredits - 5 h.g.World.AddGroundItem(p.RoomID, "credits", 20) sess := &net.Session{Conn: newChanConn(), Player: p} sess.SetState(net.StateGame) h.g.pickupCredits(sess, p, "credits", 0) if p.Credits != casino.MaxCredits { t.Errorf("balance = %d, want the cap %d", p.Credits, casino.MaxCredits) } if got := h.g.World.GroundItems(p.RoomID)["credits"]; got != 15 { t.Errorf("ground credits = %d, want 15 left behind", got) } }) } func TestPickupCreditsAtCapDestroysNothing(t *testing.T) { h := newHarness(t) h.seedAccount("alice") p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = casino.MaxCredits h.g.World.AddGroundItem(p.RoomID, "credits", 20) sess := &net.Session{Conn: newChanConn(), Player: p} sess.SetState(net.StateGame) h.g.pickupCredits(sess, p, "credits", 0) if p.Credits != casino.MaxCredits { t.Errorf("balance = %d, want the cap %d", p.Credits, casino.MaxCredits) } if got := h.g.World.GroundItems(p.RoomID)["credits"]; got != 20 { t.Errorf("ground credits = %d, want the full stack to survive", got) } if out := string(sess.Conn.(*chanConn).Bytes()); !strings.Contains(out, "can't hold any more credits") { t.Errorf("expected a cap message, got %q", out) } }) } func TestGetAllCreditsAtCapLeavesRemainderOnGround(t *testing.T) { h := newHarness(t) h.seedAccount("alice") p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = casino.MaxCredits - 5 h.g.World.AddGroundItem(p.RoomID, "credits", 20) sess := &net.Session{Conn: newChanConn(), Player: p} sess.SetState(net.StateGame) h.g.doGetAll(sess) if p.Credits != casino.MaxCredits { t.Errorf("balance = %d, want the cap %d", p.Credits, casino.MaxCredits) } if got := h.g.World.GroundItems(p.RoomID)["credits"]; got != 15 { t.Errorf("ground credits = %d, want 15 left behind", got) } }) } func TestGetAllCreditsAtCapReportsShortfall(t *testing.T) { h := newHarness(t) h.seedAccount("alice") p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = casino.MaxCredits h.g.World.AddGroundItem(p.RoomID, "credits", 20) sess := &net.Session{Conn: newChanConn(), Player: p} sess.SetState(net.StateGame) h.g.doGetAll(sess) if got := h.g.World.GroundItems(p.RoomID)["credits"]; got != 20 { t.Errorf("ground credits = %d, want the full stack to survive", got) } out := string(sess.Conn.(*chanConn).Bytes()) if !strings.Contains(out, "can't hold any more credits") { t.Errorf("expected a cap message, got %q", out) } if strings.Contains(out, "inventory is full") { t.Errorf("cap shortfall misreported as a full inventory: %q", out) } }) }