1use rakata_core::ResRef;
4use rakata_formats::gff::{get_f32, get_u8, upsert_field};
5use rakata_formats::gff_label;
6use rakata_formats::{GffModel, GffStruct, GffValue};
7
8use crate::shared::ObjectId;
9#[derive(Debug, Clone, PartialEq, GffModel)]
22#[gff_entry(Shape, wire = u8, stamped)]
23#[gff_entry(Radius, wire = f32, not_a_constant)]
24#[gff_entry(Length, wire = f32, not_a_constant)]
25#[gff_entry(Width, wire = f32, not_a_constant)]
26pub struct GitAreaEffectShapeLabels {}
27
28#[derive(Debug, Clone, PartialEq)]
30pub enum GitAreaEffectShape {
31 Circle {
33 radius: f32,
35 },
36 Rectangle {
38 length: f32,
40 width: f32,
42 },
43 None(u8),
45}
46
47impl Default for GitAreaEffectShape {
48 fn default() -> Self {
49 Self::Circle { radius: 0.0 }
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, GffModel)]
67pub struct GitAreaEffect {
68 #[gff(flatten = GitAreaEffectShapeLabels, manual_read, manual_write)]
70 pub shape: GitAreaEffectShape,
71 #[gff(Tag, stamped)]
73 pub tag: String,
74 #[gff(AreaEffectId, stamped)]
76 pub area_effect_id: i32,
77 #[gff(SpellId, stamped)]
79 pub spell_id: u32,
80 #[gff(SpellSaveDC, stamped)]
82 pub spell_save_dc: i32,
83 #[gff(SpellLevel, stamped)]
85 pub spell_level: i32,
86 #[gff(MetaMagicType, stamped)]
88 pub meta_magic_type: u8,
89 #[gff(CreatorId, stamped)]
91 pub creator_id: u32,
92 #[gff(LinkedToObject, stamped)]
94 pub linked_to_object: u32,
95 #[gff(LastEntered, stamped)]
97 pub last_entered: u32,
98 #[gff(LastLeft, stamped)]
100 pub last_left: u32,
101 #[gff(Duration, stamped)]
103 pub duration: u32,
104 #[gff(DurationType, stamped)]
106 pub duration_type: u8,
107 #[gff(LastHrtbtDay, stamped)]
109 pub last_heartbeat_day: u32,
110 #[gff(LastHrtbtTime, stamped)]
112 pub last_heartbeat_time: u32,
113 #[gff(
115 OnHeartbeat,
116 write_only_dead = "the save writer emits it and no loader reads it back, so a restored effect never fires this hook again; the 2DA-driven re-derivation exists only on the fresh spell-cast path",
117 not_a_constant
118 )]
119 pub on_heartbeat: ResRef,
120 #[gff(
122 OnUserDefined,
123 write_only_dead = "no path populates it, fresh creation included, so it is dead in this build regardless of load versus save",
124 not_a_constant
125 )]
126 pub on_user_defined: ResRef,
127 #[gff(
129 OnObjEnter,
130 write_only_dead = "the save writer emits it and no loader reads it back, so a restored effect never fires this hook again; the 2DA-driven re-derivation exists only on the fresh spell-cast path",
131 not_a_constant
132 )]
133 pub on_obj_enter: ResRef,
134 #[gff(
136 OnObjExit,
137 write_only_dead = "the save writer emits it and no loader reads it back, so a restored effect never fires this hook again; the 2DA-driven re-derivation exists only on the fresh spell-cast path",
138 not_a_constant
139 )]
140 pub on_obj_exit: ResRef,
141 #[gff(ObjectId, stamped = ObjectId::INVALID)]
146 pub object_id: ObjectId,
147 #[gff(OrientationX, not_a_constant)]
149 pub orientation_x: f32,
150 #[gff(OrientationY, not_a_constant)]
152 pub orientation_y: f32,
153 #[gff(OrientationZ, not_a_constant)]
155 pub orientation_z: f32,
156 #[gff(PositionX, stamped)]
158 pub position_x: f32,
159 #[gff(PositionY, stamped)]
161 pub position_y: f32,
162 #[gff(PositionZ, stamped)]
164 pub position_z: f32,
165}
166
167impl GitAreaEffect {
168 pub fn read_element(structure: &GffStruct) -> Self {
170 let shape = match get_u8(structure, "Shape").unwrap_or(0) {
171 0 => GitAreaEffectShape::Circle {
172 radius: get_f32(structure, "Radius").unwrap_or(0.0),
173 },
174 1 => GitAreaEffectShape::Rectangle {
175 length: get_f32(structure, "Length").unwrap_or(0.0),
176 width: get_f32(structure, "Width").unwrap_or(0.0),
177 },
178 other => GitAreaEffectShape::None(other),
179 };
180 Self {
181 shape,
182 ..Self::read_declared(structure)
183 }
184 }
185
186 pub fn write_element(&self, structure: &mut GffStruct) {
190 self.write_declared(structure);
191 match &self.shape {
192 GitAreaEffectShape::Circle { radius } => {
193 upsert_field(structure, gff_label!("Shape"), GffValue::UInt8(0));
194 upsert_field(structure, gff_label!("Radius"), GffValue::Single(*radius));
195 }
196 GitAreaEffectShape::Rectangle { length, width } => {
197 upsert_field(structure, gff_label!("Shape"), GffValue::UInt8(1));
198 upsert_field(structure, gff_label!("Length"), GffValue::Single(*length));
199 upsert_field(structure, gff_label!("Width"), GffValue::Single(*width));
200 }
201 GitAreaEffectShape::None(other) => {
202 upsert_field(structure, gff_label!("Shape"), GffValue::UInt8(*other));
203 }
204 }
205 }
206}