package game // Regression: a %i 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) } }) }