1use rakata_core::ResRef;
4use rakata_formats::{
5 gff_schema::{AbsentDefault, FieldLife, FieldSchema, GffType},
6 GffStruct, GffValue,
7};
8
9use super::object_id_or_invalid;
10use crate::gff_helpers::{get_f32, get_i32, get_resref, get_string, get_u32, get_u8, upsert_field};
11use crate::shared::ObjectId;
12
13#[derive(Debug, Clone, PartialEq)]
21pub enum GitAreaEffectShape {
22 Circle {
24 radius: f32,
26 },
27 Rectangle {
29 length: f32,
31 width: f32,
33 },
34 None(u8),
36}
37
38impl Default for GitAreaEffectShape {
39 fn default() -> Self {
40 Self::Circle { radius: 0.0 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Default)]
58pub struct GitAreaEffect {
59 pub tag: String,
61 pub object_id: ObjectId,
66 pub area_effect_id: i32,
68 pub spell_id: u32,
70 pub spell_save_dc: i32,
72 pub spell_level: i32,
74 pub meta_magic_type: u8,
76
77 pub shape: GitAreaEffectShape,
79
80 pub creator_id: u32,
82 pub linked_to_object: u32,
84 pub last_entered: u32,
86 pub last_left: u32,
88
89 pub duration: u32,
91 pub duration_type: u8,
93 pub last_heartbeat_day: u32,
95 pub last_heartbeat_time: u32,
97
98 pub position_x: f32,
100 pub position_y: f32,
102 pub position_z: f32,
104 pub orientation_x: f32,
106 pub orientation_y: f32,
108 pub orientation_z: f32,
110
111 pub on_heartbeat: ResRef,
113 pub on_user_defined: ResRef,
115 pub on_obj_enter: ResRef,
117 pub on_obj_exit: ResRef,
119}
120
121impl GitAreaEffect {
122 pub(crate) fn from_gff_struct(s: &GffStruct) -> Self {
123 let shape = match get_u8(s, "Shape").unwrap_or(0) {
124 0 => GitAreaEffectShape::Circle {
125 radius: get_f32(s, "Radius").unwrap_or(0.0),
126 },
127 1 => GitAreaEffectShape::Rectangle {
128 length: get_f32(s, "Length").unwrap_or(0.0),
129 width: get_f32(s, "Width").unwrap_or(0.0),
130 },
131 other => GitAreaEffectShape::None(other),
132 };
133
134 Self {
135 tag: get_string(s, "Tag").unwrap_or_default(),
136 object_id: object_id_or_invalid(s),
137 area_effect_id: get_i32(s, "AreaEffectId").unwrap_or(0),
138 spell_id: get_u32(s, "SpellId").unwrap_or(0),
139 spell_save_dc: get_i32(s, "SpellSaveDC").unwrap_or(0),
140 spell_level: get_i32(s, "SpellLevel").unwrap_or(0),
141 meta_magic_type: get_u8(s, "MetaMagicType").unwrap_or(0),
142
143 shape,
144
145 creator_id: get_u32(s, "CreatorId").unwrap_or(0),
146 linked_to_object: get_u32(s, "LinkedToObject").unwrap_or(0),
147 last_entered: get_u32(s, "LastEntered").unwrap_or(0),
148 last_left: get_u32(s, "LastLeft").unwrap_or(0),
149
150 duration: get_u32(s, "Duration").unwrap_or(0),
151 duration_type: get_u8(s, "DurationType").unwrap_or(0),
152 last_heartbeat_day: get_u32(s, "LastHrtbtDay").unwrap_or(0),
153 last_heartbeat_time: get_u32(s, "LastHrtbtTime").unwrap_or(0),
154
155 position_x: get_f32(s, "PositionX").unwrap_or(0.0),
156 position_y: get_f32(s, "PositionY").unwrap_or(0.0),
157 position_z: get_f32(s, "PositionZ").unwrap_or(0.0),
158 orientation_x: get_f32(s, "OrientationX").unwrap_or(0.0),
159 orientation_y: get_f32(s, "OrientationY").unwrap_or(0.0),
160 orientation_z: get_f32(s, "OrientationZ").unwrap_or(0.0),
161
162 on_heartbeat: get_resref(s, "OnHeartbeat").unwrap_or_default(),
163 on_user_defined: get_resref(s, "OnUserDefined").unwrap_or_default(),
164 on_obj_enter: get_resref(s, "OnObjEnter").unwrap_or_default(),
165 on_obj_exit: get_resref(s, "OnObjExit").unwrap_or_default(),
166 }
167 }
168
169 pub(crate) fn to_gff_struct(&self) -> GffStruct {
170 let mut s = GffStruct::new(13);
171
172 upsert_field(&mut s, "Tag", GffValue::String(self.tag.clone()));
173 upsert_field(&mut s, "ObjectId", GffValue::UInt32(self.object_id.get()));
174 upsert_field(&mut s, "AreaEffectId", GffValue::Int32(self.area_effect_id));
175 upsert_field(&mut s, "SpellId", GffValue::UInt32(self.spell_id));
176 upsert_field(&mut s, "SpellSaveDC", GffValue::Int32(self.spell_save_dc));
177 upsert_field(&mut s, "SpellLevel", GffValue::Int32(self.spell_level));
178 upsert_field(
179 &mut s,
180 "MetaMagicType",
181 GffValue::UInt8(self.meta_magic_type),
182 );
183
184 match &self.shape {
187 GitAreaEffectShape::Circle { radius } => {
188 upsert_field(&mut s, "Shape", GffValue::UInt8(0));
189 upsert_field(&mut s, "Radius", GffValue::Single(*radius));
190 }
191 GitAreaEffectShape::Rectangle { length, width } => {
192 upsert_field(&mut s, "Shape", GffValue::UInt8(1));
193 upsert_field(&mut s, "Length", GffValue::Single(*length));
194 upsert_field(&mut s, "Width", GffValue::Single(*width));
195 }
196 GitAreaEffectShape::None(raw) => {
197 upsert_field(&mut s, "Shape", GffValue::UInt8(*raw));
198 }
199 }
200
201 upsert_field(&mut s, "CreatorId", GffValue::UInt32(self.creator_id));
202 upsert_field(
203 &mut s,
204 "LinkedToObject",
205 GffValue::UInt32(self.linked_to_object),
206 );
207 upsert_field(&mut s, "LastEntered", GffValue::UInt32(self.last_entered));
208 upsert_field(&mut s, "LastLeft", GffValue::UInt32(self.last_left));
209
210 upsert_field(&mut s, "Duration", GffValue::UInt32(self.duration));
211 upsert_field(&mut s, "DurationType", GffValue::UInt8(self.duration_type));
212 upsert_field(
213 &mut s,
214 "LastHrtbtDay",
215 GffValue::UInt32(self.last_heartbeat_day),
216 );
217 upsert_field(
218 &mut s,
219 "LastHrtbtTime",
220 GffValue::UInt32(self.last_heartbeat_time),
221 );
222
223 upsert_field(&mut s, "PositionX", GffValue::Single(self.position_x));
224 upsert_field(&mut s, "PositionY", GffValue::Single(self.position_y));
225 upsert_field(&mut s, "PositionZ", GffValue::Single(self.position_z));
226 upsert_field(&mut s, "OrientationX", GffValue::Single(self.orientation_x));
227 upsert_field(&mut s, "OrientationY", GffValue::Single(self.orientation_y));
228 upsert_field(&mut s, "OrientationZ", GffValue::Single(self.orientation_z));
229
230 upsert_field(&mut s, "OnHeartbeat", GffValue::ResRef(self.on_heartbeat));
234 upsert_field(
235 &mut s,
236 "OnUserDefined",
237 GffValue::ResRef(self.on_user_defined),
238 );
239 upsert_field(&mut s, "OnObjEnter", GffValue::ResRef(self.on_obj_enter));
240 upsert_field(&mut s, "OnObjExit", GffValue::ResRef(self.on_obj_exit));
241
242 s
243 }
244}
245
246pub(crate) static AREA_EFFECT_LIST_CHILDREN: &[FieldSchema] = &[
248 FieldSchema {
249 label: "Tag",
250 expected_type: GffType::String,
251 life: FieldLife::Live,
252 required: false,
253 absent: AbsentDefault::Unverified,
254 children: None,
255 constraint: None,
256 },
257 FieldSchema {
258 label: "AreaEffectId",
259 expected_type: GffType::Int32,
260 life: FieldLife::Live,
261 required: false,
262 absent: AbsentDefault::Unverified,
263 children: None,
264 constraint: None,
265 },
266 FieldSchema {
267 label: "SpellId",
268 expected_type: GffType::UInt32,
269 life: FieldLife::Live,
270 required: false,
271 absent: AbsentDefault::Unverified,
272 children: None,
273 constraint: None,
274 },
275 FieldSchema {
276 label: "SpellSaveDC",
277 expected_type: GffType::Int32,
278 life: FieldLife::Live,
279 required: false,
280 absent: AbsentDefault::Unverified,
281 children: None,
282 constraint: None,
283 },
284 FieldSchema {
285 label: "SpellLevel",
286 expected_type: GffType::Int32,
287 life: FieldLife::Live,
288 required: false,
289 absent: AbsentDefault::Unverified,
290 children: None,
291 constraint: None,
292 },
293 FieldSchema {
294 label: "MetaMagicType",
295 expected_type: GffType::UInt8,
296 life: FieldLife::Live,
297 required: false,
298 absent: AbsentDefault::Unverified,
299 children: None,
300 constraint: None,
301 },
302 FieldSchema {
303 label: "Shape",
304 expected_type: GffType::UInt8,
305 life: FieldLife::Live,
306 required: false,
307 absent: AbsentDefault::Unverified,
308 children: None,
309 constraint: None,
310 },
311 FieldSchema {
312 label: "Radius",
313 expected_type: GffType::Single,
314 life: FieldLife::Live,
315 required: false,
316 absent: AbsentDefault::Unverified,
317 children: None,
318 constraint: None,
319 },
320 FieldSchema {
321 label: "Length",
322 expected_type: GffType::Single,
323 life: FieldLife::Live,
324 required: false,
325 absent: AbsentDefault::Unverified,
326 children: None,
327 constraint: None,
328 },
329 FieldSchema {
330 label: "Width",
331 expected_type: GffType::Single,
332 life: FieldLife::Live,
333 required: false,
334 absent: AbsentDefault::Unverified,
335 children: None,
336 constraint: None,
337 },
338 FieldSchema {
339 label: "CreatorId",
340 expected_type: GffType::UInt32,
341 life: FieldLife::Live,
342 required: false,
343 absent: AbsentDefault::Unverified,
344 children: None,
345 constraint: None,
346 },
347 FieldSchema {
348 label: "LinkedToObject",
349 expected_type: GffType::UInt32,
350 life: FieldLife::Live,
351 required: false,
352 absent: AbsentDefault::Unverified,
353 children: None,
354 constraint: None,
355 },
356 FieldSchema {
357 label: "LastEntered",
358 expected_type: GffType::UInt32,
359 life: FieldLife::Live,
360 required: false,
361 absent: AbsentDefault::Unverified,
362 children: None,
363 constraint: None,
364 },
365 FieldSchema {
366 label: "LastLeft",
367 expected_type: GffType::UInt32,
368 life: FieldLife::Live,
369 required: false,
370 absent: AbsentDefault::Unverified,
371 children: None,
372 constraint: None,
373 },
374 FieldSchema {
375 label: "Duration",
376 expected_type: GffType::UInt32,
377 life: FieldLife::Live,
378 required: false,
379 absent: AbsentDefault::Unverified,
380 children: None,
381 constraint: None,
382 },
383 FieldSchema {
384 label: "DurationType",
385 expected_type: GffType::UInt8,
386 life: FieldLife::Live,
387 required: false,
388 absent: AbsentDefault::Unverified,
389 children: None,
390 constraint: None,
391 },
392 FieldSchema {
393 label: "LastHrtbtDay",
394 expected_type: GffType::UInt32,
395 life: FieldLife::Live,
396 required: false,
397 absent: AbsentDefault::Unverified,
398 children: None,
399 constraint: None,
400 },
401 FieldSchema {
402 label: "LastHrtbtTime",
403 expected_type: GffType::UInt32,
404 life: FieldLife::Live,
405 required: false,
406 absent: AbsentDefault::Unverified,
407 children: None,
408 constraint: None,
409 },
410 FieldSchema {
411 label: "OnHeartbeat",
412 expected_type: GffType::ResRef,
413 life: FieldLife::Live,
414 required: false,
415 absent: AbsentDefault::Unverified,
416 children: None,
417 constraint: None,
418 },
419 FieldSchema {
420 label: "OnUserDefined",
421 expected_type: GffType::ResRef,
422 life: FieldLife::Live,
423 required: false,
424 absent: AbsentDefault::Unverified,
425 children: None,
426 constraint: None,
427 },
428 FieldSchema {
429 label: "OnObjEnter",
430 expected_type: GffType::ResRef,
431 life: FieldLife::Live,
432 required: false,
433 absent: AbsentDefault::Unverified,
434 children: None,
435 constraint: None,
436 },
437 FieldSchema {
438 label: "OnObjExit",
439 expected_type: GffType::ResRef,
440 life: FieldLife::Live,
441 required: false,
442 absent: AbsentDefault::Unverified,
443 children: None,
444 constraint: None,
445 },
446 FieldSchema {
447 label: "ObjectId",
448 expected_type: GffType::UInt32,
449 life: FieldLife::Live,
450 required: false,
451 absent: AbsentDefault::Unverified,
452 children: None,
453 constraint: None,
454 },
455 FieldSchema {
456 label: "OrientationX",
457 expected_type: GffType::Single,
458 life: FieldLife::Live,
459 required: false,
460 absent: AbsentDefault::Unverified,
461 children: None,
462 constraint: None,
463 },
464 FieldSchema {
465 label: "OrientationY",
466 expected_type: GffType::Single,
467 life: FieldLife::Live,
468 required: false,
469 absent: AbsentDefault::Unverified,
470 children: None,
471 constraint: None,
472 },
473 FieldSchema {
474 label: "OrientationZ",
475 expected_type: GffType::Single,
476 life: FieldLife::Live,
477 required: false,
478 absent: AbsentDefault::Unverified,
479 children: None,
480 constraint: None,
481 },
482 FieldSchema {
483 label: "PositionX",
484 expected_type: GffType::Single,
485 life: FieldLife::Live,
486 required: false,
487 absent: AbsentDefault::Unverified,
488 children: None,
489 constraint: None,
490 },
491 FieldSchema {
492 label: "PositionY",
493 expected_type: GffType::Single,
494 life: FieldLife::Live,
495 required: false,
496 absent: AbsentDefault::Unverified,
497 children: None,
498 constraint: None,
499 },
500 FieldSchema {
501 label: "PositionZ",
502 expected_type: GffType::Single,
503 life: FieldLife::Live,
504 required: false,
505 absent: AbsentDefault::Unverified,
506 children: None,
507 constraint: None,
508 },
509];