diff --git a/crates/benilla-formats/src/chat_channels.rs b/crates/benilla-formats/src/chat_channels.rs index 38e2a88d..3c32da6b 100644 --- a/crates/benilla-formats/src/chat_channels.rs +++ b/crates/benilla-formats/src/chat_channels.rs @@ -191,12 +191,21 @@ mod tests { (23, 0x10004, "WorldDefense", "WorldDefense"), (24, 0x00000, "LookingForGroup", "LookingForGroup"), (25, 0x20032, "GuildRecruitment - %s", "GuildRecruitment"), + // The realm's own: a global **World** channel, which 1.12 had no row for at all — + // there it was a player-made channel like any other. Flags `0x9` is + // `0x1 | 0x8`, so it is auto-joined and global rather than zone-scoped, and its + // pattern carries no `%s` because there is one of it. + (27, 0x00009, "World", "World"), ] ); - // The auto-join set is the reference's `chat-cache.txt` `ZONECHANNELS` mask. + // The auto-join set is the reference's `chat-cache.txt` `ZONECHANNELS` mask. World joins + // itself too, so the mask gains bit 26. let mask: u32 = cat.auto_join_rows().map(|r| 1 << (r.id - 1)).sum(); - assert_eq!(mask, 0x0020_0003, "General + Trade + LocalDefense"); + assert_eq!( + mask, 0x0420_0003, + "General + Trade + LocalDefense + the realm's World" + ); } #[test] @@ -234,6 +243,17 @@ mod tests { assert_eq!(cat.zone_channel_id("General - Elwynn Forest"), 1); assert_eq!(cat.zone_channel_id("Trade - City"), 2); assert_eq!(cat.zone_channel_id("LocalDefense - Durotar"), 22); - assert_eq!(cat.zone_channel_id("World"), 0, "a custom channel is 0"); + // "World" is a **shipped** channel here (row 27), not a player-made one, so it resolves. + assert_eq!( + cat.zone_channel_id("World"), + 27, + "the realm's World channel" + ); + // A genuinely player-made name still answers 0, which is what that check is for. + assert_eq!( + cat.zone_channel_id("GromsPartyChat"), + 0, + "a custom channel is 0" + ); } } diff --git a/crates/benilla-formats/src/emote_text.rs b/crates/benilla-formats/src/emote_text.rs index 2c4c906f..2e3dc06f 100644 --- a/crates/benilla-formats/src/emote_text.rs +++ b/crates/benilla-formats/src/emote_text.rs @@ -224,7 +224,8 @@ mod tests { let data = crate::wow_data_or_skip!(); let mut chain = Chain::open(&data).expect("open patch chain"); let cat = load_emote_text_catalog(&mut chain).expect("load"); - assert_eq!(cat.len(), 169, "one form table per EmotesText row"); + // 171, where 1.12 shipped 169: the realm adds two emotes. + assert_eq!(cat.len(), 171, "one form table per EmotesText row"); // ctx 0: someone else waves at someone else. assert_eq!( @@ -298,12 +299,41 @@ mod tests { } /// An unshipped locale is empty everywhere, so the ladder runs dry: no line (`0x49b4bd`). + /// + /// Which columns are shipped moved with the realm: it fills **five** locales where 1.12's + /// enGB chain filled four. Column 5 (ruRU) is the one that arrived — this test used to use it + /// as its empty case — so the dry ladder is now shown on 1, 2 or 8, and the five that compose + /// are asserted so a column sliding under the ladder cannot pass unnoticed. #[test] fn an_unshipped_locale_composes_nothing() { let data = crate::wow_data_or_skip!(); let mut chain = Chain::open(&data).expect("open patch chain"); let cat = load_emote_text_catalog(&mut chain).expect("load"); - assert_eq!(cat.compose(WAVE, &line("Bob", "Jane", "Me"), 5), None); + for empty in [1, 2, 8] { + assert_eq!( + cat.compose(WAVE, &line("Bob", "Jane", "Me"), empty), + None, + "locale {empty} ships nothing, so the ladder runs dry" + ); + } + for (loc, want) in [ + (0usize, "Bob waves at Jane."), + (3, "Bob winkt Jane zu."), + (5, "Bob машет Jane рукой."), + (6, "Bob saluda con la mano a Jane."), + (7, "Bob acena para Jane."), + ] { + assert_eq!( + cat.compose(WAVE, &line("Bob", "Jane", "Me"), loc) + .as_deref(), + Some(want), + "locale {loc}" + ); + } + // zhCN composes too; its text is asserted apart from the Latin ones for legibility. + assert!(cat + .compose(WAVE, &line("Bob", "Jane", "Me"), 4) + .is_some_and(|s| s.contains("Bob") && s.contains("Jane"))); assert_eq!(cat.compose(9999, &line("Bob", "Jane", "Me"), 0), None); } diff --git a/crates/benilla-formats/src/itembagfamily.rs b/crates/benilla-formats/src/itembagfamily.rs index d3b48740..34d13e75 100644 --- a/crates/benilla-formats/src/itembagfamily.rs +++ b/crates/benilla-formats/src/itembagfamily.rs @@ -84,6 +84,11 @@ mod tests { assert_eq!(cat.name(4), None); assert_eq!(cat.name(5), None); assert_eq!(cat.name(0), None, "family 0 must not render as \"NONE\""); - assert_eq!(cat.len(), 8, "the whole shipped table"); + // The realm's four: the profession bags 1.12 never shipped a family row for. + assert_eq!(cat.name(10), Some("Meat")); + assert_eq!(cat.name(11), Some("Fish")); + assert_eq!(cat.name(12), Some("Leather")); + assert_eq!(cat.name(13), Some("Mining Supplies")); + assert_eq!(cat.len(), 12, "the whole shipped table"); } } diff --git a/crates/benilla-formats/src/languages.rs b/crates/benilla-formats/src/languages.rs index 58034f31..0d81209c 100644 --- a/crates/benilla-formats/src/languages.rs +++ b/crates/benilla-formats/src/languages.rs @@ -264,8 +264,14 @@ mod tests { let mut chain = Chain::open(&data).expect("open patch chain"); let langs = load_default_languages(&mut chain).expect("load"); - // All nine `ChrRaces` rows resolve: the eight playable races and the Goblin. - assert_eq!(langs.len(), 9, "one entry per ChrRaces row"); + // Ten `ChrRaces` rows resolve, where 1.12 had nine: the eight playable races, the Goblin, + // and the realm's own tenth race, which speaks **Common** — so it is Alliance-side. + assert_eq!(langs.len(), 10, "one entry per ChrRaces row"); + assert_eq!( + langs.name(10, 0), + Some("Common"), + "the realm's tenth race is Alliance-side" + ); // Alliance speaks Common, Horde Orcish; the racial language would give Darnassian. for race in [1, 3, 4, 7] { assert_eq!(langs.name(race, 0), Some("Common"), "race {race}"); diff --git a/crates/benilla-formats/src/lock_type.rs b/crates/benilla-formats/src/lock_type.rs index edccb34c..238e6443 100644 --- a/crates/benilla-formats/src/lock_type.rs +++ b/crates/benilla-formats/src/lock_type.rs @@ -100,10 +100,14 @@ mod tests { assert_eq!(cat.cursor_name(13), None); assert_eq!(cat.cursor_name(19), None); // Fishing assert_eq!(cat.cursor_name(0), None); // an empty lock slot's index + // A fourth row carries one now: id 20, "Survival", reusing the `PickLock` cursor. It is + // the realm's own lock type, and the cursor is what a player sees on a skinnable corpse. + assert_eq!(cat.cursor_name(20), Some("PickLock")); + assert_eq!(cat.name(20), Some("Survival")); assert_eq!( cat.len(), - 3, - "only three LockType rows carry a CursorName in 5875" + 4, + "four LockType rows carry a CursorName on this realm, three in 5875" ); // A slip to `ResourceName` (field 10) would print "Requires Herbs". diff --git a/crates/benilla-formats/src/spells/catalog_tests.rs b/crates/benilla-formats/src/spells/catalog_tests.rs index 111a473c..50961862 100644 --- a/crates/benilla-formats/src/spells/catalog_tests.rs +++ b/crates/benilla-formats/src/spells/catalog_tests.rs @@ -217,7 +217,7 @@ fn real_spell_catalog_reads_open_lock_types() { ); // The cast bar shows no name for `AttributesEx3 & 0x4` (`SPELL_ATTR_EX3_NO_CASTING_BAR_TEXT`), - // the one column separating 22810 from 6478; three shipped rows carry it. + // the one column separating 22810 from 6478; four shipped rows carry it (three in 5875). assert!(cat.get(22810).unwrap().no_casting_bar_text()); assert!(!cat.get(6478).unwrap().no_casting_bar_text()); let silent: Vec = { @@ -231,21 +231,36 @@ fn real_spell_catalog_reads_open_lock_types() { }; assert_eq!( silent, - vec![6477, 22810, 26380], - "6477 Opening / 22810 Opening - No Text / 26380 zzOLDSummon Mouth Tentacle Visual" + vec![6477, 22810, 26380, 47405], + "6477 Opening / 22810 Opening - No Text / 26380 zzOLDSummon Mouth Tentacle Visual, plus \ + one of the realm's own (47405) — four rows carry the attribute, three in 5875" ); // The tool and reagent columns the pre-send possession check reads (`0x6e4000`): totems at // columns 40-41, reagents at 42-49 with their counts at 50-57. + // **The realm removed the professions' tool requirements.** Mining (2575) carried the Mining + // Pick (2901) in 1.12 and Skinning (8613) the Skinning Knife (7005); both are `[0, 0]` here, + // as are Herbalism, Blacksmithing, Cooking and First Aid. So you mine and skin bare-handed on + // this realm, and the pre-send possession check (`0x6e4000`) has nothing to refuse. + // + // Read, not assumed: the column still works. 362 rows carry a tool — Pick Lock's thieves' + // tools (`(1804, [5060, 0])`), the lockpicking trainer ranks, Rogue poisons at 11130 — so + // this is six rows blanked in the data, not a column slip. The reagent columns beside them + // are untouched, which `Slow Fall` below shows. assert_eq!( cat.get(2575).unwrap().totems, - [2901, 0], - "Mining requires the Mining Pick (2901)" + [0, 0], + "Mining needs no Mining Pick on this realm" ); assert_eq!( cat.get(8613).unwrap().totems, - [7005, 0], - "Skinning requires the Skinning Knife (7005)" + [0, 0], + "Skinning needs no Skinning Knife on this realm" + ); + assert_eq!( + cat.get(1804).unwrap().totems, + [5060, 0], + "…while Pick Lock still wants its thieves' tools (5060), so the column is read" ); let slow_fall = cat.get(130).unwrap(); assert_eq!( @@ -1177,12 +1192,15 @@ fn the_language_declaring_spells_cover_nine_of_thirteen_languages() { // (spell, declared language, SkillLine.dbc id): every SPELL_EFFECT_LANGUAGE row but 25674. const DECLARED: &[(u32, u32, u32)] = &[ - (668, 7, 98), // Common - (669, 1, 109), // Orcish - (670, 3, 115), // Taurahe - (671, 2, 113), // Darnassian - (672, 6, 111), // Dwarvish - (813, 7, 137), // "Thalassian (NYI)", declaring Common + (668, 7, 98), // Common + (669, 1, 109), // Orcish + (670, 3, 115), // Taurahe + (671, 2, 113), // Darnassian + (672, 6, 111), // Dwarvish + // The realm **implemented** Thalassian: 1.12's row declared Common (7) because the + // language was not in, and this one declares 10, the `Languages.dbc` Thalassian row. That + // goes with its tenth playable race (see `languages.rs`). + (813, 10, 137), // Thalassian, declaring Thalassian (814, 7, 138), // "Draconic (NYI)", declaring Common (815, 7, 139), // Demon Tongue, declaring Common (816, 7, 140), // "Titan (NYI)", declaring Common @@ -1224,8 +1242,9 @@ fn the_language_declaring_spells_cover_nine_of_thirteen_languages() { .collect(); assert_eq!( understandable, - [1, 2, 3, 6, 7, 13, 14, 33].into_iter().collect(), - "only the eight player languages have both a declaring spell and a skill line" + [1, 2, 3, 6, 7, 10, 13, 14, 33].into_iter().collect(), + "nine player languages here, where 1.12 had eight: Thalassian (10) now has both a \ + declaring spell and a skill line, which is what implementing it means" ); // An ordinary ability declares nothing.