aboutsummaryrefslogtreecommitdiff
path: root/internal/game/polish_wear_swap_quality_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/polish_wear_swap_quality_test.go')
-rw-r--r--internal/game/polish_wear_swap_quality_test.go140
1 files changed, 140 insertions, 0 deletions
diff --git a/internal/game/polish_wear_swap_quality_test.go b/internal/game/polish_wear_swap_quality_test.go
new file mode 100644
index 0000000..bb9e996
--- /dev/null
+++ b/internal/game/polish_wear_swap_quality_test.go
@@ -0,0 +1,140 @@
+package game
+
+// Regression: swapping worn equipment used to return the displaced item as a
+// raw Quality 0/MaxQuality 0 slot and leave its stale EquipQuality entry
+// behind. Reachable with the shipped lighter (the only max_quality > 0 item):
+// wielding a weapon after the lighter returned it zeroed, and act_burn charged
+// the stale equipment record instead of the carried stack. The displaced
+// item's equipped quality must move onto the returned slot and out of the map.
+
+import (
+ "path/filepath"
+ "testing"
+
+ "thehouseoficarus/internal/item"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+const polishLighterYAML = `id: polish_lighter
+name: polish lighter
+stackable: false
+max_quality: 100
+equipment:
+ slot: main_hand
+ type: tool
+tool:
+ type: fire
+`
+
+const polishBladeYAML = `id: polish_blade
+name: polish blade
+stackable: false
+equipment:
+ slot: main_hand
+ type: melee_weapon
+`
+
+func seedPolishWearItems(t *testing.T, dir string) {
+ t.Helper()
+ writeSeedFile(t, filepath.Join(dir, "items", "polish_lighter.yaml"), []byte(polishLighterYAML))
+ writeSeedFile(t, filepath.Join(dir, "items", "polish_blade.yaml"), []byte(polishBladeYAML))
+}
+
+// carriedInventSlot returns the first inventory slot holding itemID.
+func carriedInventSlot(p *player.Player, itemID string) *player.InventorySlot {
+ for i := 0; i < 28; i++ {
+ if s := p.InvSlot(i); s != nil && s.ItemID == itemID {
+ return s
+ }
+ }
+ return nil
+}
+
+func TestWearSwapReturnsEquippedQualityToInventory(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice")
+ p := h.seedCharacter("alice", "alice")
+ seedPolishWearItems(t, h.dir)
+
+ sess := &net.Session{Conn: newChanConn(), Player: p}
+ sess.SetState(net.StateGame)
+
+ h.g.RunOnOwner(func() {
+ h.g.ItemStore.Reload(h.dir)
+ p.SetInvSlot(0, h.g.newInventorySlot("polish_lighter", 1))
+
+ h.g.doWear(sess, "lighter")
+ if got := p.Equipment[item.SlotMainHand]; got != "polish_lighter" {
+ t.Fatalf("lighter not equipped: %q", got)
+ }
+ // Burn two charges while the lighter is equipped.
+ for i := 0; i < 2; i++ {
+ if !h.g.consumeFireTool(sess, p, "polish_lighter") {
+ t.Fatal("consumeFireTool refused a fresh lighter")
+ }
+ }
+ if got := p.EquipQuality["polish_lighter"]; got != 98 {
+ t.Fatalf("equipped lighter charge = %d, want 98", got)
+ }
+
+ // Wielding a weapon displaces the lighter back into the inventory.
+ p.SetInvSlot(1, h.g.newInventorySlot("polish_blade", 1))
+ h.g.doWear(sess, "blade")
+ if got := p.Equipment[item.SlotMainHand]; got != "polish_blade" {
+ t.Fatalf("blade not equipped: %q", got)
+ }
+
+ lighter := carriedInventSlot(p, "polish_lighter")
+ if lighter == nil {
+ t.Fatal("the displaced lighter was not returned to the inventory")
+ }
+ if lighter.Quality != 98 || lighter.MaxQuality != 100 {
+ t.Errorf("returned lighter = %+v, want quality 98/100", lighter)
+ }
+ if _, ok := p.EquipQuality["polish_lighter"]; ok {
+ t.Error("stale EquipQuality entry left behind after the swap")
+ }
+
+ // act_burn must charge the carried stack, not a stale equipment record.
+ if !h.g.consumeFireTool(sess, p, "polish_lighter") {
+ t.Fatal("consumeFireTool refused the carried lighter stack")
+ }
+ if lighter.Quality != 97 {
+ t.Errorf("carried lighter charge = %d, want 97", lighter.Quality)
+ }
+ })
+}
+
+func TestWearAllSwapReturnsEquippedQualityToInventory(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice")
+ p := h.seedCharacter("alice", "alice")
+ seedPolishWearItems(t, h.dir)
+
+ sess := &net.Session{Conn: newChanConn(), Player: p}
+ sess.SetState(net.StateGame)
+
+ h.g.RunOnOwner(func() {
+ h.g.ItemStore.Reload(h.dir)
+ p.SetInvSlot(0, h.g.newInventorySlot("polish_lighter", 1))
+ h.g.doWear(sess, "lighter")
+ if got := p.EquipQuality["polish_lighter"]; got != 100 {
+ t.Fatalf("equipped lighter charge = %d, want 100", got)
+ }
+
+ p.SetInvSlot(1, h.g.newInventorySlot("polish_blade", 1))
+ h.g.doWearAll(sess)
+
+ lighter := carriedInventSlot(p, "polish_lighter")
+ if lighter == nil {
+ t.Fatal("wear all did not return the displaced lighter")
+ }
+ if lighter.Quality != 100 || lighter.MaxQuality != 100 {
+ t.Errorf("returned lighter = %+v, want quality 100/100", lighter)
+ }
+ if _, ok := p.EquipQuality["polish_lighter"]; ok {
+ t.Error("stale EquipQuality entry left behind by wear all")
+ }
+ })
+}