Skip to content

Commit 5dbded2

Browse files
committed
fix title/progression based on expansion_type and name rules
1 parent b56f8fd commit 5dbded2

10 files changed

Lines changed: 146 additions & 93 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ bit = "0.1.1"
1515
log = "0.4"
1616
serde = { version = "1", features = ["derive"] }
1717
serde_with = "3"
18+
unicode-script = "0.5.8"
19+
unicode-segmentation = "1"

src/character/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Character section model and format-specific codecs.
22
//!
33
//! [`title_for_progression_d2r`] and [`Character::title_d2r`] implement the default D2R
4-
//! title rules. Modded title systems are out of scope.
4+
//! title rules. `Save::title_d2r()` is the canonical entry point. Modded title systems
5+
//! are out of scope.
56
67
use std::fmt;
78

@@ -252,12 +253,13 @@ impl Character {
252253

253254
/// Return the default D2R title for this character state.
254255
///
256+
/// Pass the canonical [`crate::ExpansionType`] from [`crate::Save::expansion_type`].
255257
/// Mods may use different title rules.
256-
pub fn title_d2r(&self) -> Option<&'static str> {
258+
pub fn title_d2r(&self, expansion_type: crate::ExpansionType) -> Option<&'static str> {
257259
title_for_progression_d2r(
258260
self.progression,
259261
self.class,
260-
self.status.expansion,
262+
!matches!(expansion_type, crate::ExpansionType::Classic),
261263
self.status.hardcore,
262264
)
263265
}

src/character/tests.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#[cfg(test)]
22
mod tests {
3-
use crate::character::v105::{MODE_ROTW, OFFSET_MODE_MARKER};
3+
use crate::ExpansionType;
44
use crate::character::*;
5+
use crate::character::v105::{MODE_ROTW, OFFSET_MODE_MARKER};
56
use crate::format::FormatId;
67

78
#[test]
@@ -182,34 +183,34 @@ mod tests {
182183
character.set_legacy_expansion_flag(false);
183184
character.set_hardcore(false);
184185
character.progression = 4;
185-
assert_eq!(character.title_d2r(), Some("Dame"));
186+
assert_eq!(character.title_d2r(ExpansionType::Classic), Some("Dame"));
186187

187188
character.set_legacy_expansion_flag(false);
188189
character.set_hardcore(true);
189190
character.progression = 12;
190-
assert_eq!(character.title_d2r(), Some("Queen"));
191+
assert_eq!(character.title_d2r(ExpansionType::Classic), Some("Queen"));
191192

192193
character.set_legacy_expansion_flag(true);
193194
character.set_hardcore(false);
194195
character.progression = 10;
195-
assert_eq!(character.title_d2r(), Some("Champion"));
196+
assert_eq!(character.title_d2r(ExpansionType::Expansion), Some("Champion"));
196197

197198
character.set_legacy_expansion_flag(true);
198199
character.set_hardcore(true);
199200
character.progression = 15;
200-
assert_eq!(character.title_d2r(), Some("Guardian"));
201+
assert_eq!(character.title_d2r(ExpansionType::Expansion), Some("Guardian"));
201202

202203
// Warlock uses male title variants.
203204
character.class = Class::Warlock;
204205
character.set_legacy_expansion_flag(false);
205206
character.set_hardcore(false);
206207
character.progression = 12;
207-
assert_eq!(character.title_d2r(), Some("Baron"));
208+
assert_eq!(character.title_d2r(ExpansionType::Classic), Some("Baron"));
208209

209210
// Expansion normally skips these values.
210211
character.set_legacy_expansion_flag(true);
211212
character.set_hardcore(false);
212213
character.progression = 9;
213-
assert_eq!(character.title_d2r(), None);
214+
assert_eq!(character.title_d2r(ExpansionType::Expansion), None);
214215
}
215216
}

src/format/compatibility.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn class_compatibility_issues(
1616
code: CompatibilityCode::UnknownClassRequiresKnownTarget,
1717
blocking: true,
1818
message: format!(
19-
"Unknown class id {class_id} cannot be safely converted to known format {target:?}."
19+
"Unknown class id {class_id} cannot be converted to {target:?}."
2020
),
2121
});
2222
}
@@ -26,15 +26,15 @@ fn class_compatibility_issues(
2626
issues.push(CompatibilityIssue {
2727
code: CompatibilityCode::WarlockRequiresRotW,
2828
blocking: true,
29-
message: "Warlock class is only supported in RotW editions.".to_string(),
29+
message: "Warlock can only be encoded as RotW.".to_string(),
3030
});
3131
}
3232

3333
if expansion_type != ExpansionType::RotW {
3434
issues.push(CompatibilityIssue {
3535
code: CompatibilityCode::WarlockRequiresRotWExpansion,
3636
blocking: true,
37-
message: "Warlock class requires RotW expansion type.".to_string(),
37+
message: "Warlock requires RotW expansion mode.".to_string(),
3838
});
3939
}
4040
}
@@ -43,8 +43,7 @@ fn class_compatibility_issues(
4343
issues.push(CompatibilityIssue {
4444
code: CompatibilityCode::ExpansionClassRequiresExpansionMode,
4545
blocking: true,
46-
message: "Druid and Assassin classes are not valid in Classic mode."
47-
.to_string(),
46+
message: "Druid and Assassin cannot be encoded in Classic mode.".to_string(),
4847
});
4948
}
5049
}
@@ -64,8 +63,7 @@ pub(crate) fn compatibility_issues(save: &Save, target: FormatId) -> Vec<Compati
6463
issues.push(CompatibilityIssue {
6564
code: CompatibilityCode::RotWExpansionRequiresRotWEdition,
6665
blocking: true,
67-
message: "Cannot encode RotW expansion type as v99 or other non-RotW editions."
68-
.to_string(),
66+
message: "RotW expansion mode cannot be encoded to non-RotW formats.".to_string(),
6967
});
7068
}
7169

src/format/summary.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use crate::character::decode_for_format as decode_character_for_format;
2-
use crate::{
3-
ExpansionType, IssueKind, IssueSeverity, ParseHardError, ParseIssue, SaveSummary, Strictness,
4-
};
2+
use crate::{IssueKind, IssueSeverity, ParseHardError, ParseIssue, SaveSummary, Strictness};
53

64
use super::layout::{
75
expansion_type_from_decoded_character, layout_for_decode, push_issue, range_readable,
@@ -87,18 +85,10 @@ pub(crate) fn summarize(
8785
Ok(character) => {
8886
let expansion_type =
8987
expansion_type_from_decoded_character(selected_layout.format_id(), &character);
88+
let title = character.title_d2r(expansion_type).map(str::to_string);
9089
let class = character.class;
91-
let progression = character.progression;
92-
let is_hardcore = character.is_hardcore();
9390
let level = character.level();
9491
let name = character.name;
95-
let title = crate::character::title_for_progression_d2r(
96-
progression,
97-
class,
98-
!matches!(expansion_type, ExpansionType::Classic),
99-
is_hardcore,
100-
)
101-
.map(str::to_string);
10292
summary.expansion_type = Some(expansion_type);
10393
summary.name = Some(name);
10494
summary.class = Some(class);

src/format/tests.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,9 @@ fn encode_v99_rejects_rotw_expansion_type() {
153153
let error = encode(&save, FormatId::V99, CompatibilityChecks::Enforce)
154154
.expect_err("v99 should reject RotW expansion type");
155155
assert!(
156-
error.to_string().contains("Cannot encode RotW expansion type as v99"),
156+
error
157+
.to_string()
158+
.contains("RotW expansion mode cannot be encoded to non-RotW formats"),
157159
"unexpected error message: {error}"
158160
);
159161
}
@@ -205,7 +207,7 @@ fn summarize_extracts_core_fields_v99() {
205207
assert_eq!(summary.name.as_deref(), Some(parsed.save.character.name.as_str()));
206208
assert_eq!(summary.class, Some(parsed.save.character.class));
207209
assert_eq!(summary.level, Some(parsed.save.character.level()));
208-
assert_eq!(summary.title, parsed.save.character.title_d2r().map(str::to_string));
210+
assert_eq!(summary.title, parsed.save.title_d2r().map(str::to_string));
209211
assert!(summary.issues.is_empty());
210212
}
211213

src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ impl Save {
438438
format::compatibility_issues(self, target)
439439
}
440440

441+
/// Return the default D2R title for this save using the canonical expansion mode.
442+
pub fn title_d2r(&self) -> Option<&'static str> {
443+
self.character.title_d2r(self.expansion_type())
444+
}
445+
441446
/// Validate the current save using backend-owned canonical rules.
442447
pub fn validate(&self) -> validation::ValidationReport {
443448
validation::build_validation_report(self)
@@ -671,6 +676,16 @@ mod tests {
671676
assert_eq!(save.game_edition(), None);
672677
}
673678

679+
#[test]
680+
fn save_title_uses_canonical_expansion_type() {
681+
let mut save = Save::new(FormatId::V105, Class::Amazon);
682+
save.set_expansion_type(ExpansionType::Classic);
683+
save.character.set_legacy_expansion_flag(true);
684+
save.character.progression = 4;
685+
686+
assert_eq!(save.title_d2r(), Some("Dame"));
687+
}
688+
674689
#[test]
675690
fn set_expansion_type_updates_v105_mode_marker_without_touching_status_bit() {
676691
let mut save = Save::new(FormatId::V105, Class::Amazon);

0 commit comments

Comments
 (0)