package world import "testing" // TestRekeyRoomsRewritesSeededInstanceID: a seeded instance's map key embeds // its home room ("__"). Renaming the room must rewrite the key, // or SeedMobs creates a duplicate instance for the renamed room and a later // recycled ID skips seeding entirely. func TestRekeyRoomsRewritesSeededInstanceID(t *testing.T) { s := &MobStore{instances: map[string]*MobInstance{ "rat_2_0": {InstanceID: "rat_2_0", DefID: "rat", RoomID: 2, HomeRoomID: 2}, "bat_5_1": {InstanceID: "bat_5_1", DefID: "bat", RoomID: 5, HomeRoomID: 5}, }} s.RekeyRooms(map[int]int{2: 4}) if _, stale := s.instances["rat_2_0"]; stale { t.Fatal("stale seeded key survived the rekey") } inst, ok := s.instances["rat_4_0"] if !ok || inst.RoomID != 4 || inst.HomeRoomID != 4 { t.Fatalf("rekeyed instance = %+v (present=%v), want rat_4_0 in room 4", inst, ok) } if _, ok := s.instances["bat_5_1"]; !ok { t.Fatal("an unrelated instance was dropped") } } // TestRekeyRoomsUpdatesMobDespawnRooms: DespawnRooms is a live room reference // (sys_triggers compares it against the owner's current room). A room rename // must remap it alongside RoomID/HomeRoomID/WanderRooms, or a transient mob // despawns while its owner never left the allowed room. func TestRekeyRoomsUpdatesMobDespawnRooms(t *testing.T) { s := &MobStore{instances: map[string]*MobInstance{ "m1": { InstanceID: "m1", RoomID: 1, HomeRoomID: 1, WanderRooms: []int{1, 5}, DespawnRooms: []int{1, 5}, }, }} s.RekeyRooms(map[int]int{1: 3, 5: 7}) inst := s.instances["m1"] if inst.RoomID != 3 || inst.HomeRoomID != 3 { t.Fatalf("room/home = %d/%d, want 3/3", inst.RoomID, inst.HomeRoomID) } if got := inst.WanderRooms; len(got) != 2 || got[0] != 3 || got[1] != 7 { t.Fatalf("wander_rooms = %v, want [3 7]", got) } if got := inst.DespawnRooms; len(got) != 2 || got[0] != 3 || got[1] != 7 { t.Fatalf("despawn_rooms = %v, want [3 7]", got) } } // TestSeedMobsKeysByDefOccurrenceAndPrunes: reordering a room's mobs list // must not orphan old instances (spawning duplicates) and removing an entry // must delete its seeded instance. Keys use a per-defID occurrence counter, so // the same declaration keeps the same key across reorders. func TestSeedMobsKeysByDefOccurrenceAndPrunes(t *testing.T) { s := &MobStore{ defs: map[string]*MobDef{ "rat": {ID: "rat", Name: "rat"}, "bat": {ID: "bat", Name: "bat"}, }, instances: map[string]*MobInstance{ "rat_1_0": {InstanceID: "rat_1_0", DefID: "rat", RoomID: 1, HomeRoomID: 1, HP: 1, MaxHP: 1}, "bat_1_0": {InstanceID: "bat_1_0", DefID: "bat", RoomID: 1, HomeRoomID: 1, HP: 1, MaxHP: 1}, }, } // Reorder and drop rat: bat keeps its key, rat is pruned, no duplicates. s.SeedMobs(1, []RoomMob{{ID: "bat"}}) if s.instances["bat_1_0"] == nil { t.Fatal("surviving declared mob was dropped") } if _, ok := s.instances["rat_1_0"]; ok { t.Fatal("removed mob was not pruned") } if len(s.instances) != 1 { t.Fatalf("instances = %v, want only bat_1_0", s.instances) } } func TestSeedMobsReorderDoesNotDuplicate(t *testing.T) { s := &MobStore{ defs: map[string]*MobDef{ "rat": {ID: "rat", Name: "rat"}, "bat": {ID: "bat", Name: "bat"}, }, instances: map[string]*MobInstance{ "rat_1_0": {InstanceID: "rat_1_0", DefID: "rat", RoomID: 1, HomeRoomID: 1, HP: 1, MaxHP: 1}, "bat_1_0": {InstanceID: "bat_1_0", DefID: "bat", RoomID: 1, HomeRoomID: 1, HP: 1, MaxHP: 1}, }, } s.SeedMobs(1, []RoomMob{{ID: "bat"}, {ID: "rat"}}) if len(s.instances) != 2 { t.Fatalf("reorder changed instance count: %v", s.instances) } if s.instances["rat_1_0"] == nil || s.instances["bat_1_0"] == nil { t.Fatalf("reorder lost a declared mob: %v", s.instances) } }