diff options
| author | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
| commit | 153acb7f644d755c45533b7c5311e3ffa47e8973 (patch) | |
| tree | 7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/game/hardening_craft_message_item_test.go | |
| parent | 87d08f0151e643951769e323995f1c5ed89ee200 (diff) | |
| download | thehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz | |
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/game/hardening_craft_message_item_test.go')
| -rw-r--r-- | internal/game/hardening_craft_message_item_test.go | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/internal/game/hardening_craft_message_item_test.go b/internal/game/hardening_craft_message_item_test.go new file mode 100644 index 0000000..b516852 --- /dev/null +++ b/internal/game/hardening_craft_message_item_test.go @@ -0,0 +1,85 @@ +package game + +// Regression: a %i<n> success message must name the ingredient alternative +// the player actually used. Production resolved the message AFTER +// consumeIngredients, so p.HasItem was false for everything consumed and +// resolveCraftVar fell back to the entry's first alternative — naming the +// item the player never touched when an entry lists several (bucket vs jug +// of water). + +import ( + "strings" + "testing" + + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +const craftMessageOutputYAML = `id: test_water_potion +name: test water potion +stackable: false +value: 3 +craft: + - type: crafting + ingredients: + - items: + - bucket_of_water + - jug_of_water + quantity: 1 + byproducts: + - empty_bucket + - empty_jug + success_message: "You mix the %i1 into a %n." + output_qty: 1 +` + +func TestCraftSuccessMessageNamesConsumedAlternative(t *testing.T) { + dir := t.TempDir() + seedWorld(t, dir) + writeDataFile(t, dir, "items", "bucket_of_water.yaml", "id: bucket_of_water\nname: bucket of water\nstackable: false\nvalue: 1\n") + writeDataFile(t, dir, "items", "jug_of_water.yaml", "id: jug_of_water\nname: jug of water\nstackable: false\nvalue: 1\n") + writeDataFile(t, dir, "items", "empty_bucket.yaml", "id: empty_bucket\nname: empty bucket\nstackable: false\nvalue: 1\n") + writeDataFile(t, dir, "items", "empty_jug.yaml", "id: empty_jug\nname: empty jug\nstackable: false\nvalue: 1\n") + writeDataFile(t, dir, "items", "test_water_potion.yaml", craftMessageOutputYAML) + + g := newHardeningGame(t, dir) + + g.RunOnOwner(func() { + p := player.New("alice") + p.RoomID = 1 + // Only the jug is present: the entered alternative is bucket_of_water, + // so a post-consumption p.HasItem fallback would name the bucket. + p.SetInvSlot(0, &player.InventorySlot{ItemID: "jug_of_water", Quantity: 1}) + + conn := newChanConn() + sess := &net.Session{Conn: conn, Player: p} + sess.SetState(net.StateGame) + + def, err := g.ItemStore.Load("test_water_potion") + if err != nil { + t.Fatalf("load recipe: %v", err) + } + + g.startProductionFromItem(sess, p, def, 1) + if p.Action == nil { + t.Fatalf("production did not start; output=%q", conn.Bytes()) + } + g.advanceProduction(sess, p) // phase 0 -> 1 + g.advanceProduction(sess, p) // phase 1: consume + produce + + if got := p.CountItem("test_water_potion"); got != 1 { + t.Fatalf("potion = %d, want 1; output=%q", got, conn.Bytes()) + } + if got := p.CountItem("empty_jug"); got != 1 { + t.Fatalf("empty_jug = %d, want 1 (byproduct for the consumed jug)", got) + } + + out := string(conn.Bytes()) + if !strings.Contains(out, "jug of water") { + t.Errorf("success message does not name the consumed jug of water: %q", out) + } + if strings.Contains(out, "bucket of water") { + t.Errorf("success message names the bucket the player never used: %q", out) + } + }) +} |
