overlord_event_system/logic/
abilities.rs1use crate::{
2 TICKER_UNIT_DURATION_MS, entities::make_active_abilities_from_equipped, event::OverlordEvent,
3 game_config_helpers::GameConfigLookup, logic::handler::OverlordLogic, state::OverlordState,
4};
5
6use essences::{
7 abilities::{Ability, AbilityId, AbilitySlotId, ActiveAbility, EquippedAbilities},
8 effect::EffectId,
9 entity::{ActionWithDeadline, EntityAction, EntityId},
10};
11use event_system::system::EventHandleResult;
12
13impl OverlordLogic {
14 pub fn handle_equip_ability(
15 &self,
16 slot_id: u64,
17 ability_id: AbilityId,
18 current_tick: u64,
19 mut state: OverlordState,
20 ) -> EventHandleResult<OverlordEvent, OverlordState> {
21 let game_config = self.game_config.get();
22
23 if let Some((slot_id, _)) = state
24 .character_state
25 .equipped_abilities
26 .slotted
27 .iter()
28 .find(|(_, a)| a.template_id == ability_id)
29 {
30 tracing::error!(
31 "Ability_id = {ability_id} is already equipped in another slot in state: slot_id={slot_id}"
32 );
33 return EventHandleResult::fail(state);
34 }
35
36 let Some(ability) = state
37 .character_state
38 .all_abilities
39 .iter()
40 .find(|ab| ab.template_id == ability_id)
41 .cloned()
42 else {
43 tracing::error!("No ability with id = {} state exists", ability_id);
44 return EventHandleResult::fail(state);
45 };
46
47 let Some(ability_template) = game_config.ability_template(ability_id) else {
48 tracing::error!("No template found for ability_id = {}", ability_id);
49 return EventHandleResult::fail(state);
50 };
51
52 if !ability_template.fight_ui_visibility.is_player_equippable() {
53 tracing::error!("Trying to equip not visible ability {}", ability_id);
54 return EventHandleResult::fail(state);
55 }
56
57 let Some(ability_slots) = game_config
58 .ability_slots_for_chapter_level(state.character_state.character.current_chapter_level)
59 else {
60 tracing::error!(
61 "No ability slots for current_chapter_level = {}",
62 state.character_state.character.current_chapter_level
63 );
64 return EventHandleResult::fail(state);
65 };
66
67 if slot_id >= ability_slots {
68 tracing::error!(
69 "Slot_id is too high = {}, current max slots = {}",
70 slot_id,
71 ability_slots,
72 );
73 return EventHandleResult::fail(state);
74 }
75
76 self.set_ability_in_slot(&mut state, &ability, slot_id as usize, current_tick);
77
78 EventHandleResult::ok(state)
79 }
80
81 pub fn handle_unequip_ability(
82 &self,
83 slot_id: AbilitySlotId,
84 mut state: OverlordState,
85 ) -> EventHandleResult<OverlordEvent, OverlordState> {
86 if let Some(player) = state.get_active_fight_player_mut()
87 && let Some(pos) = player
88 .abilities
89 .iter()
90 .position(|a| a.slot_id == Some(slot_id))
91 {
92 let active_ability = player.abilities.swap_remove(pos);
93
94 player
95 .actions_queue
96 .remove_start_cast_ability_action(active_ability.ability.template_id);
97 }
98
99 state
100 .character_state
101 .equipped_abilities
102 .slotted
103 .remove(&slot_id);
104
105 EventHandleResult::ok(state)
106 }
107
108 fn set_ability_in_slot(
109 &self,
110 state: &mut OverlordState,
111 ability: &Ability,
112 slot_id: AbilitySlotId,
113 current_tick: u64,
114 ) {
115 let game_config = self.game_config.get();
116 let cooldown = game_config
117 .ability_template(ability.template_id)
118 .map(|t| t.cooldown)
119 .unwrap_or(0);
120
121 if let Some(player) = state.get_active_fight_player_mut() {
122 if let Some(pos) = player
123 .abilities
124 .iter()
125 .position(|a| a.slot_id == Some(slot_id))
126 {
127 let active_ability = player.abilities.swap_remove(pos);
128
129 player
130 .actions_queue
131 .remove_start_cast_ability_action(active_ability.ability.template_id);
132 }
133
134 player
135 .abilities
136 .push(self.make_active_ability(ability, Some(slot_id)));
137
138 player.actions_queue.push(&ActionWithDeadline {
139 action: self.make_start_cast_ability_action(player.id, ability.template_id),
140 deadline_tick: current_tick + cooldown,
141 });
142 }
143
144 state
145 .character_state
146 .equipped_abilities
147 .slotted
148 .insert(slot_id, ability.clone());
149 }
150
151 pub fn handle_equip_abilities(
152 &self,
153 equipped_abilities: EquippedAbilities,
154 current_tick: u64,
155 mut state: OverlordState,
156 ) -> EventHandleResult<OverlordEvent, OverlordState> {
157 let game_config = self.game_config.get();
158
159 if let Some(player) = state.get_active_fight_player_mut() {
160 for ability in &player.abilities {
161 player
162 .actions_queue
163 .remove_start_cast_ability_action(ability.ability.template_id);
164 }
165
166 player.abilities =
167 make_active_abilities_from_equipped(&equipped_abilities, &game_config, true);
168
169 for ability in &player.abilities {
170 let cooldown = game_config
171 .ability_template(ability.ability.template_id)
172 .map(|t| t.cooldown)
173 .unwrap_or(0);
174 player.actions_queue.push(&ActionWithDeadline {
175 action: self
176 .make_start_cast_ability_action(player.id, ability.ability.template_id),
177 deadline_tick: current_tick + cooldown,
178 });
179 }
180 }
181
182 state.character_state.equipped_abilities = equipped_abilities;
183
184 EventHandleResult::ok(state)
185 }
186
187 pub fn make_cast_effect_action(
188 &self,
189 entity_id: EntityId,
190 effect_id: EffectId,
191 ) -> EntityAction {
192 EntityAction::CastEffect {
193 entity_id,
194 effect_id,
195 }
196 }
197
198 pub fn make_start_cast_ability_action(
199 &self,
200 by_entity_id: EntityId,
201 ability_id: AbilityId,
202 ) -> EntityAction {
203 EntityAction::StartCastAbility {
204 ability_id,
205 by_entity_id,
206 pet_id: None,
207 }
208 }
209
210 fn make_active_ability(
211 &self,
212 ability: &Ability,
213 slot_id: Option<AbilitySlotId>,
214 ) -> ActiveAbility {
215 let game_config = self.game_config.get();
216 let cooldown = game_config
217 .ability_template(ability.template_id)
218 .map(|t| t.cooldown)
219 .unwrap_or(0);
220
221 ActiveAbility {
222 ability: ability.clone(),
223 deadline: Some(
224 ::time::utc_now()
225 + chrono::TimeDelta::milliseconds(
226 (cooldown as u128 * TICKER_UNIT_DURATION_MS) as i64,
227 ),
228 ),
229 slot_id,
230 }
231 }
232}