Skip to main content

rakata_generics/git/
area_properties.rs

1//! Area-level properties and the map block, both single structs on the GIT root.
2
3use rakata_formats::{
4    gff_schema::{AbsentDefault, FieldLife, FieldSchema, GffType},
5    GffStruct, GffValue,
6};
7
8use crate::gff_helpers::{get_bool, get_i32, get_u32, get_u8, upsert_field};
9
10/// Area-level properties (music, ambient sound, stealth XP, fog).
11///
12/// This is a single `Struct` in the GIT root (field label `AreaProperties`).
13#[derive(Debug, Clone, PartialEq, Default)]
14pub struct GitAreaProperties {
15    /// Unescapable flag (`Unescapable`).
16    pub unescapable: bool,
17    /// Stealth XP maximum (`StealthXPMax`).
18    pub stealth_xp_max: u32,
19    /// Current stealth XP count (`StealthXPCurrent`). Save-game state. Natively clamped by the engine to not exceed `StealthXPMax`.
20    pub stealth_xp_current: u32,
21    /// Stealth XP loss per detection (`StealthXPLoss`).
22    pub stealth_xp_loss: u32,
23    /// Stealth XP enabled flag (`StealthXPEnabled`).
24    pub stealth_xp_enabled: bool,
25    /// Transition pending flag (`TransPending`). Save-game state.
26    pub trans_pending: bool,
27    /// Pending transition next ID (`TransPendNextID`). Save-game state.
28    pub trans_pend_next_id: u8,
29    /// Pending transition current ID (`TransPendCurrID`). Save-game state.
30    pub trans_pend_curr_id: u8,
31    /// Sun/fog color packed as RGBA (`SunFogColor`).
32    pub sun_fog_color: u32,
33    /// Music delay in ms (`MusicDelay`).
34    pub music_delay: i32,
35    /// Daytime music track ID (`MusicDay`). References ambientmusic.2da.
36    pub music_day: i32,
37    /// Night-time music track ID (`MusicNight`). References ambientmusic.2da.
38    pub music_night: i32,
39    /// Battle music track ID (`MusicBattle`). References ambientmusic.2da.
40    pub music_battle: i32,
41    /// Daytime ambient sound ID (`AmbientSndDay`). References ambientsound.2da.
42    pub ambient_snd_day: i32,
43    /// Night-time ambient sound ID (`AmbientSndNight`). References ambientsound.2da.
44    pub ambient_snd_night: i32,
45    /// Daytime ambient sound volume (`AmbientSndDayVol`). Values outside 0-255 are truncated and wrapped to an 8-bit byte.
46    pub ambient_snd_day_vol: i32,
47    /// Night-time ambient sound volume (`AmbientSndNitVol`). Values outside 0-255 are truncated and wrapped to an 8-bit byte.
48    pub ambient_snd_nit_vol: i32,
49}
50
51impl GitAreaProperties {
52    pub(crate) fn from_gff_struct(s: &GffStruct) -> Self {
53        Self {
54            unescapable: get_bool(s, "Unescapable").unwrap_or(false),
55            stealth_xp_max: get_u32(s, "StealthXPMax").unwrap_or(0),
56            stealth_xp_current: get_u32(s, "StealthXPCurrent").unwrap_or(0),
57            stealth_xp_loss: get_u32(s, "StealthXPLoss").unwrap_or(0),
58            stealth_xp_enabled: get_bool(s, "StealthXPEnabled").unwrap_or(false),
59            trans_pending: get_bool(s, "TransPending").unwrap_or(false),
60            trans_pend_next_id: get_u8(s, "TransPendNextID").unwrap_or(0),
61            trans_pend_curr_id: get_u8(s, "TransPendCurrID").unwrap_or(0),
62            sun_fog_color: get_u32(s, "SunFogColor").unwrap_or(0),
63            music_delay: get_i32(s, "MusicDelay").unwrap_or(0),
64            music_day: get_i32(s, "MusicDay").unwrap_or(0),
65            music_night: get_i32(s, "MusicNight").unwrap_or(0),
66            music_battle: get_i32(s, "MusicBattle").unwrap_or(0),
67            ambient_snd_day: get_i32(s, "AmbientSndDay").unwrap_or(0),
68            ambient_snd_night: get_i32(s, "AmbientSndNight").unwrap_or(0),
69            ambient_snd_day_vol: get_i32(s, "AmbientSndDayVol").unwrap_or(0),
70            ambient_snd_nit_vol: get_i32(s, "AmbientSndNitVol").unwrap_or(0),
71        }
72    }
73
74    pub(crate) fn to_gff_struct(&self) -> GffStruct {
75        let mut s = GffStruct::new(0);
76        upsert_field(
77            &mut s,
78            "Unescapable",
79            GffValue::UInt8(u8::from(self.unescapable)),
80        );
81        upsert_field(
82            &mut s,
83            "StealthXPMax",
84            GffValue::UInt32(self.stealth_xp_max),
85        );
86        upsert_field(
87            &mut s,
88            "StealthXPCurrent",
89            GffValue::UInt32(self.stealth_xp_current),
90        );
91        upsert_field(
92            &mut s,
93            "StealthXPLoss",
94            GffValue::UInt32(self.stealth_xp_loss),
95        );
96        upsert_field(
97            &mut s,
98            "StealthXPEnabled",
99            GffValue::UInt8(u8::from(self.stealth_xp_enabled)),
100        );
101        upsert_field(
102            &mut s,
103            "TransPending",
104            GffValue::UInt8(u8::from(self.trans_pending)),
105        );
106        upsert_field(
107            &mut s,
108            "TransPendNextID",
109            GffValue::UInt8(self.trans_pend_next_id),
110        );
111        upsert_field(
112            &mut s,
113            "TransPendCurrID",
114            GffValue::UInt8(self.trans_pend_curr_id),
115        );
116        upsert_field(&mut s, "SunFogColor", GffValue::UInt32(self.sun_fog_color));
117        upsert_field(&mut s, "MusicDelay", GffValue::Int32(self.music_delay));
118        upsert_field(&mut s, "MusicDay", GffValue::Int32(self.music_day));
119        upsert_field(&mut s, "MusicNight", GffValue::Int32(self.music_night));
120        upsert_field(&mut s, "MusicBattle", GffValue::Int32(self.music_battle));
121        upsert_field(
122            &mut s,
123            "AmbientSndDay",
124            GffValue::Int32(self.ambient_snd_day),
125        );
126        upsert_field(
127            &mut s,
128            "AmbientSndNight",
129            GffValue::Int32(self.ambient_snd_night),
130        );
131        upsert_field(
132            &mut s,
133            "AmbientSndDayVol",
134            GffValue::Int32(self.ambient_snd_day_vol),
135        );
136        upsert_field(
137            &mut s,
138            "AmbientSndNitVol",
139            GffValue::Int32(self.ambient_snd_nit_vol),
140        );
141        s
142    }
143}
144
145/// GIT `AreaProperties` struct child schema.
146pub(crate) static AREA_PROPERTIES_CHILDREN: &[FieldSchema] = &[
147    // Dead, and the strongest negative in the GIT set. Not "no reader was
148    // found" but "there is nowhere else it could be read from": the
149    // "EnvAudio" string occurs once in the executable with one
150    // cross-reference, and that reference is ARE's per-room reader. A
151    // different field that happens to share the label, not this one.
152    FieldSchema {
153        label: "EnvAudio",
154        expected_type: GffType::Int32,
155        life: FieldLife::ReadOnlyDead(
156            "the only EnvAudio the engine reads is the ARE per-room field, a different field sharing the label",
157        ),
158        required: false,
159        absent: AbsentDefault::Unverified,
160        children: None,
161        constraint: None,
162    },
163    FieldSchema {
164        label: "Unescapable",
165        expected_type: GffType::UInt8,
166        life: FieldLife::Live,
167        required: false,
168        absent: AbsentDefault::Unverified,
169        children: None,
170        constraint: None,
171    },
172    FieldSchema {
173        label: "StealthXPMax",
174        expected_type: GffType::UInt32,
175        life: FieldLife::Live,
176        required: false,
177        absent: AbsentDefault::Unverified,
178        children: None,
179        constraint: None,
180    },
181    FieldSchema {
182        label: "StealthXPCurrent",
183        expected_type: GffType::UInt32,
184        life: FieldLife::Live,
185        required: false,
186        absent: AbsentDefault::Unverified,
187        children: None,
188        constraint: None,
189    },
190    FieldSchema {
191        label: "StealthXPLoss",
192        expected_type: GffType::UInt32,
193        life: FieldLife::Live,
194        required: false,
195        absent: AbsentDefault::Unverified,
196        children: None,
197        constraint: None,
198    },
199    FieldSchema {
200        label: "StealthXPEnabled",
201        expected_type: GffType::UInt8,
202        life: FieldLife::Live,
203        required: false,
204        absent: AbsentDefault::Unverified,
205        children: None,
206        constraint: None,
207    },
208    FieldSchema {
209        label: "TransPending",
210        expected_type: GffType::UInt8,
211        life: FieldLife::Live,
212        required: false,
213        absent: AbsentDefault::Unverified,
214        children: None,
215        constraint: None,
216    },
217    FieldSchema {
218        label: "TransPendNextID",
219        expected_type: GffType::UInt8,
220        life: FieldLife::Live,
221        required: false,
222        absent: AbsentDefault::Unverified,
223        children: None,
224        constraint: None,
225    },
226    FieldSchema {
227        label: "TransPendCurrID",
228        expected_type: GffType::UInt8,
229        life: FieldLife::Live,
230        required: false,
231        absent: AbsentDefault::Unverified,
232        children: None,
233        constraint: None,
234    },
235    FieldSchema {
236        label: "SunFogColor",
237        expected_type: GffType::UInt32,
238        life: FieldLife::Live,
239        required: false,
240        absent: AbsentDefault::Unverified,
241        children: None,
242        constraint: None,
243    },
244    // --- Ambient sound fields (CSWSAmbientSound::Load) ---
245    FieldSchema {
246        label: "MusicDelay",
247        expected_type: GffType::Int32,
248        life: FieldLife::Live,
249        required: false,
250        absent: AbsentDefault::Unverified,
251        children: None,
252        constraint: None,
253    },
254    FieldSchema {
255        label: "MusicDay",
256        expected_type: GffType::Int32,
257        life: FieldLife::Live,
258        required: false,
259        absent: AbsentDefault::Unverified,
260        children: None,
261        constraint: None,
262    },
263    FieldSchema {
264        label: "MusicNight",
265        expected_type: GffType::Int32,
266        life: FieldLife::Live,
267        required: false,
268        absent: AbsentDefault::Unverified,
269        children: None,
270        constraint: None,
271    },
272    FieldSchema {
273        label: "MusicBattle",
274        expected_type: GffType::Int32,
275        life: FieldLife::Live,
276        required: false,
277        absent: AbsentDefault::Unverified,
278        children: None,
279        constraint: None,
280    },
281    FieldSchema {
282        label: "AmbientSndDay",
283        expected_type: GffType::Int32,
284        life: FieldLife::Live,
285        required: false,
286        absent: AbsentDefault::Unverified,
287        children: None,
288        constraint: None,
289    },
290    FieldSchema {
291        label: "AmbientSndNight",
292        expected_type: GffType::Int32,
293        life: FieldLife::Live,
294        required: false,
295        absent: AbsentDefault::Unverified,
296        children: None,
297        constraint: None,
298    },
299    FieldSchema {
300        label: "AmbientSndDayVol",
301        expected_type: GffType::Int32,
302        life: FieldLife::Live,
303        required: false,
304        absent: AbsentDefault::Unverified,
305        children: None,
306        constraint: None,
307    },
308    FieldSchema {
309        label: "AmbientSndNitVol",
310        expected_type: GffType::Int32,
311        life: FieldLife::Live,
312        required: false,
313        absent: AbsentDefault::Unverified,
314        children: None,
315        constraint: None,
316    },
317];
318
319/// GIT `AreaMap` struct child schema. Save-game only.
320pub(crate) static AREA_MAP_CHILDREN: &[FieldSchema] = &[
321    FieldSchema {
322        label: "AreaMapResX",
323        expected_type: GffType::Int32,
324        life: FieldLife::Live,
325        required: false,
326        absent: AbsentDefault::Unverified,
327        children: None,
328        constraint: None,
329    },
330    FieldSchema {
331        label: "AreaMapResY",
332        expected_type: GffType::Int32,
333        life: FieldLife::Live,
334        required: false,
335        absent: AbsentDefault::Unverified,
336        children: None,
337        constraint: None,
338    },
339    FieldSchema {
340        label: "AreaMapDataSize",
341        expected_type: GffType::UInt32,
342        life: FieldLife::Live,
343        required: false,
344        absent: AbsentDefault::Unverified,
345        children: None,
346        constraint: None,
347    },
348    FieldSchema {
349        label: "AreaMapData",
350        expected_type: GffType::Binary,
351        life: FieldLife::Live,
352        required: false,
353        absent: AbsentDefault::Unverified,
354        children: None,
355        constraint: None,
356    },
357];