overlord_event_system/async_handler/
vassals.rs

1use crate::{
2    async_handler::handler::OverlordAsyncEventHandler, event::OverlordEvent,
3    game_config_helpers::GameConfigLookup, state::OverlordState,
4};
5
6use chrono::Utc;
7
8use essences::{
9    currency::CurrencySource,
10    vassals::{Suzerain, VassalTask, VassalTaskStatus},
11};
12
13use event_system::system::EventHandleResult;
14use uuid::Uuid;
15
16impl OverlordAsyncEventHandler {
17    pub fn handle_new_task(
18        &self,
19        new_task: VassalTask,
20        mut state: OverlordState,
21    ) -> EventHandleResult<OverlordEvent, OverlordState> {
22        if state.character_state.vassal_tasks.contains(&new_task) {
23            tracing::error!("Tried pushing new task {new_task:?}, but its already in state");
24            return EventHandleResult::fail(state);
25        };
26
27        state.character_state.vassal_tasks.push(new_task);
28
29        EventHandleResult::ok(state)
30    }
31
32    pub fn handle_give_resist_task(
33        &self,
34        new_task: VassalTask,
35        mut state: OverlordState,
36    ) -> EventHandleResult<OverlordEvent, OverlordState> {
37        let game_config = self.game_config.get();
38
39        if new_task.template_task_id != game_config.game_settings.resist_task_id {
40            tracing::error!(
41                "Tried giving resist task, but it's id is {}, while in config it's {}",
42                new_task.template_task_id,
43                game_config.game_settings.resist_task_id,
44            );
45            return EventHandleResult::fail(state);
46        }
47        if state.character_state.vassal_tasks.iter().any(|task| {
48            task.template_task_id == game_config.game_settings.resist_task_id
49                && !matches!(
50                    task.task_status,
51                    VassalTaskStatus::FinishedGood | VassalTaskStatus::FinishedBad
52                )
53        }) {
54            tracing::error!(
55                "Tried giving new resist task {new_task:?} to vassal, but its already in state"
56            );
57            return EventHandleResult::fail(state);
58        };
59        state.character_state.vassal_tasks.push(new_task);
60        EventHandleResult::ok(state)
61    }
62
63    pub fn handle_accept_task(
64        &self,
65        task_id: Uuid,
66        is_good: bool,
67        mut state: OverlordState,
68    ) -> EventHandleResult<OverlordEvent, OverlordState> {
69        let game_config = self.game_config.get();
70
71        let Some(task) = state
72            .character_state
73            .vassal_tasks
74            .iter_mut()
75            .find(|task| task.id == task_id)
76        else {
77            tracing::error!(
78                "Tried accepting task with id = {}, but didn't find it in state",
79                task_id
80            );
81            return EventHandleResult::fail(state);
82        };
83
84        let Ok(task_template) = game_config.require_vassal_task_template(task.template_task_id)
85        else {
86            tracing::error!(
87                "Tried finding task with template_id = {}, but didn't find it in config",
88                task.template_task_id
89            );
90            return EventHandleResult::fail(state);
91        };
92
93        if task.task_status != VassalTaskStatus::NotStarted {
94            tracing::error!(
95                "Tried accepting task with id = {}, but its not in NotStarted state",
96                task_id
97            );
98            return EventHandleResult::fail(state);
99        }
100
101        let started_time = ::time::utc_now();
102
103        if is_good {
104            task.task_status = VassalTaskStatus::StartedGood;
105        } else {
106            task.task_status = VassalTaskStatus::StartedBad;
107        }
108
109        task.started_at = Some(started_time);
110        task.finish_at = Some(started_time + time::Duration::from_secs(task_template.duration_sec));
111
112        EventHandleResult::ok(state)
113    }
114
115    pub fn handle_task_accepted(
116        &self,
117        task_id: Uuid,
118        started_good: bool,
119        started_at: chrono::DateTime<Utc>,
120        finish_at: chrono::DateTime<Utc>,
121        mut state: OverlordState,
122    ) -> EventHandleResult<OverlordEvent, OverlordState> {
123        let Some(task) = state
124            .character_state
125            .vassal_tasks
126            .iter_mut()
127            .find(|task| task.id == task_id)
128        else {
129            tracing::error!(
130                "Tried accepting task with id = {}, but didn't find it in state",
131                task_id
132            );
133            return EventHandleResult::fail(state);
134        };
135
136        if started_good {
137            task.task_status = VassalTaskStatus::StartedGood
138        } else {
139            task.task_status = VassalTaskStatus::StartedBad
140        };
141
142        task.started_at = Some(started_at);
143        task.finish_at = Some(finish_at);
144
145        EventHandleResult::ok(state)
146    }
147
148    pub fn handle_resist_task_accepted(
149        &self,
150        resist_task: VassalTask,
151        mut state: OverlordState,
152    ) -> EventHandleResult<OverlordEvent, OverlordState> {
153        if let Some(existing) = state
154            .character_state
155            .vassal_tasks
156            .iter_mut()
157            .find(|t| t.id == resist_task.id)
158        {
159            *existing = resist_task;
160        } else {
161            state.character_state.vassal_tasks.push(resist_task);
162        }
163        EventHandleResult::ok(state)
164    }
165
166    pub fn handle_hit_hands(
167        &self,
168        task_id: Uuid,
169        mut state: OverlordState,
170    ) -> EventHandleResult<OverlordEvent, OverlordState> {
171        let Some(task) = state
172            .character_state
173            .vassal_tasks
174            .iter_mut()
175            .find(|task| task.id == task_id)
176        else {
177            tracing::error!(
178                "Tried hitting hands on task with id = {}, but didn't find it in state",
179                task_id
180            );
181            return EventHandleResult::fail(state);
182        };
183
184        if task.task_status != VassalTaskStatus::StartedBad {
185            tracing::error!(
186                "Tried hitting hands on task with id = {}, but its not in StartedBad state",
187                task_id
188            );
189            return EventHandleResult::fail(state);
190        };
191
192        if task.finish_at.unwrap() < ::time::utc_now() {
193            tracing::error!(
194                "Tried hitting hands on task with id = {}, but its finished by time",
195                task_id
196            );
197            return EventHandleResult::fail(state);
198        };
199
200        task.task_status = VassalTaskStatus::ChangedBadToGood;
201
202        EventHandleResult::ok(state)
203    }
204
205    pub fn handle_catch_resist_task(
206        &self,
207        task_id: Uuid,
208        mut state: OverlordState,
209    ) -> EventHandleResult<OverlordEvent, OverlordState> {
210        let Some(task) = state
211            .character_state
212            .vassal_tasks
213            .iter_mut()
214            .find(|task| task.id == task_id)
215        else {
216            tracing::error!(
217                "Tried catching resist task with id = {}, but didn't find it in state",
218                task_id
219            );
220            return EventHandleResult::fail(state);
221        };
222
223        if task.task_status != VassalTaskStatus::StartedBad {
224            tracing::error!(
225                "Tried catching resist task with id = {}, but its not in StartedBad state",
226                task_id
227            );
228            return EventHandleResult::fail(state);
229        };
230
231        if task.finish_at.unwrap() < ::time::utc_now() {
232            tracing::error!(
233                "Tried catching task with id = {}, but its finished by time {}",
234                task_id,
235                ::time::utc_now(),
236            );
237            return EventHandleResult::fail(state);
238        };
239
240        task.task_status = VassalTaskStatus::FinishedGood;
241
242        EventHandleResult::ok(state)
243    }
244
245    pub fn handle_hands_hitted(
246        &self,
247        task_id: Uuid,
248        mut state: OverlordState,
249    ) -> EventHandleResult<OverlordEvent, OverlordState> {
250        let Some(task) = state
251            .character_state
252            .vassal_tasks
253            .iter_mut()
254            .find(|task| task.id == task_id)
255        else {
256            tracing::error!(
257                "Tried hitting hands on task with id = {}, but didn't find it in state",
258                task_id
259            );
260            return EventHandleResult::fail(state);
261        };
262
263        if task.task_status != VassalTaskStatus::StartedBad {
264            tracing::error!(
265                "Tried hitting hands on task with id = {}, but its not in StartedBad state",
266                task_id
267            );
268            return EventHandleResult::fail(state);
269        }
270        task.task_status = VassalTaskStatus::ChangedBadToGood;
271
272        EventHandleResult::ok(state)
273    }
274
275    pub fn handle_resist_task_catched(
276        &self,
277        task_id: Uuid,
278        mut state: OverlordState,
279    ) -> EventHandleResult<OverlordEvent, OverlordState> {
280        let Some(task) = state
281            .character_state
282            .vassal_tasks
283            .iter_mut()
284            .find(|task| task.id == task_id)
285        else {
286            tracing::error!(
287                "Tried catching resist task with id = {}, but didn't find it in state",
288                task_id
289            );
290            return EventHandleResult::fail(state);
291        };
292
293        if task.task_status != VassalTaskStatus::StartedBad {
294            tracing::error!(
295                "Tried catching resist task with id = {}, but its not in StartedBad state",
296                task_id
297            );
298            return EventHandleResult::fail(state);
299        }
300
301        task.task_status = VassalTaskStatus::FinishedGood;
302
303        EventHandleResult::ok(state)
304    }
305
306    pub fn handle_finish_task(
307        &self,
308        task_id: Uuid,
309        mut state: OverlordState,
310    ) -> EventHandleResult<OverlordEvent, OverlordState> {
311        let Some(vassal_task) = state
312            .character_state
313            .vassal_tasks
314            .iter_mut()
315            .find(|task| task.id == task_id)
316        else {
317            tracing::error!(
318                "Tried finish task with id = {}, but didn't find it in state",
319                task_id
320            );
321            return EventHandleResult::fail(state);
322        };
323
324        if !matches!(
325            vassal_task.task_status,
326            VassalTaskStatus::StartedBad
327                | VassalTaskStatus::StartedGood
328                | VassalTaskStatus::ChangedBadToGood
329        ) {
330            tracing::error!(
331                "Task with id = {} is not being done, state: {}",
332                task_id,
333                vassal_task.task_status
334            );
335            return EventHandleResult::fail(state);
336        };
337
338        let finish_time = match vassal_task.finish_at {
339            Some(finish_time) => finish_time,
340            None => {
341                tracing::error!("Task with id = {} has no finish_time", task_id);
342                return EventHandleResult::fail(state);
343            }
344        };
345
346        if finish_time > ::time::utc_now() {
347            tracing::error!(
348                "Tried finishing task with id = {}, but its not finished {}, {}",
349                task_id,
350                finish_time,
351                ::time::utc_now(),
352            );
353            return EventHandleResult::fail(state);
354        };
355
356        if vassal_task.suzerain_id == state.character_state.character.id {
357            let Some(vassal) = state
358                .character_state
359                .vassals
360                .iter_mut()
361                .find(|vassal| vassal.character_id == vassal_task.vassal_id)
362            else {
363                tracing::error!(
364                    "Tried finishing task with id = {}, in which character is the suzerain, but didn't find vassal in state with id: {}",
365                    task_id,
366                    vassal_task.vassal_id
367                );
368                return EventHandleResult::fail(state);
369            };
370
371            if matches!(
372                vassal_task.task_status,
373                VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
374            ) {
375                vassal.loyalty += vassal_task.good_loyalty;
376            } else if vassal_task.task_status == VassalTaskStatus::StartedBad {
377                vassal.loyalty += vassal_task.bad_loyalty;
378            } else {
379                tracing::error!(
380                    "Tried finishing task with id = {}, but its in weird state {}",
381                    task_id,
382                    vassal_task.task_status,
383                );
384                return EventHandleResult::fail(state);
385            }
386        } else if vassal_task.vassal_id == state.character_state.character.id {
387            let Some(suzerain) = state.character_state.suzerain.as_mut() else {
388                tracing::error!(
389                    "Tried finishing task with id = {}, in which character is the vassal, but didn't find suzerain in state",
390                    task_id
391                );
392                return EventHandleResult::fail(state);
393            };
394            if matches!(
395                vassal_task.task_status,
396                VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
397            ) {
398                suzerain.loyalty += vassal_task.good_loyalty;
399            } else if vassal_task.task_status == VassalTaskStatus::StartedBad {
400                suzerain.loyalty += vassal_task.bad_loyalty;
401            } else {
402                tracing::error!(
403                    "Tried finishing task with id = {}, but its in weird state {}",
404                    task_id,
405                    vassal_task.task_status,
406                );
407                return EventHandleResult::fail(state);
408            }
409        } else {
410            tracing::error!(
411                "Tried finishing task with id = {}, but you are not Vassal or Suzerain",
412                task_id
413            );
414            return EventHandleResult::fail(state);
415        }
416
417        vassal_task.task_status = if matches!(
418            vassal_task.task_status,
419            VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
420        ) {
421            VassalTaskStatus::FinishedGood
422        } else {
423            VassalTaskStatus::FinishedBad
424        };
425
426        EventHandleResult::ok(state)
427    }
428
429    pub fn handle_resist_task_finished(
430        &self,
431        task_id: Uuid,
432        mut state: OverlordState,
433    ) -> EventHandleResult<OverlordEvent, OverlordState> {
434        let Some(resist_task) = state
435            .character_state
436            .vassal_tasks
437            .iter_mut()
438            .find(|task| task.id == task_id)
439        else {
440            tracing::error!(
441                "Tried finish resist task with id = {}, but didn't find it in state",
442                task_id
443            );
444            return EventHandleResult::fail(state);
445        };
446
447        if resist_task.task_status != VassalTaskStatus::StartedBad {
448            tracing::error!(
449                "Resist task with id = {} is not being done, state: {}",
450                task_id,
451                resist_task.task_status
452            );
453            return EventHandleResult::fail(state);
454        };
455
456        let finish_time = match resist_task.finish_at {
457            Some(finish_time) => finish_time,
458            None => {
459                tracing::error!("Resist task with id = {} has no finish_time", task_id);
460                return EventHandleResult::fail(state);
461            }
462        };
463
464        if finish_time > ::time::utc_now() {
465            tracing::error!(
466                "Tried finishing resist task with id = {}, but its not finished",
467                task_id
468            );
469            return EventHandleResult::fail(state);
470        };
471
472        if resist_task.suzerain_id == state.character_state.character.id {
473            let Some(vassal) = state
474                .character_state
475                .vassals
476                .iter_mut()
477                .find(|vassal| vassal.character_id == resist_task.vassal_id)
478            else {
479                tracing::error!(
480                    "Tried finishing resist task with id = {}, in which character is the suzerain, but didn't find vassal in state with id: {}",
481                    task_id,
482                    resist_task.vassal_id
483                );
484                return EventHandleResult::fail(state);
485            };
486            if matches!(
487                resist_task.task_status,
488                VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
489            ) {
490                vassal.loyalty += resist_task.good_loyalty;
491            } else if resist_task.task_status == VassalTaskStatus::StartedBad {
492                vassal.loyalty += resist_task.bad_loyalty;
493            } else {
494                tracing::error!(
495                    "Tried finishing resist task with id = {}, but its in weird state {}",
496                    task_id,
497                    resist_task.task_status,
498                );
499                return EventHandleResult::fail(state);
500            }
501        } else if resist_task.vassal_id == state.character_state.character.id {
502            let Some(suzerain) = state.character_state.suzerain.as_mut() else {
503                tracing::error!(
504                    "Tried finishing resist task with id = {}, in which character is the vassal, but didn't find suzerain in state",
505                    task_id
506                );
507                return EventHandleResult::fail(state);
508            };
509            if matches!(
510                resist_task.task_status,
511                VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
512            ) {
513                suzerain.loyalty += resist_task.good_loyalty;
514            } else if resist_task.task_status == VassalTaskStatus::StartedBad {
515                suzerain.loyalty += resist_task.bad_loyalty;
516            } else {
517                tracing::error!(
518                    "Tried finishing resist task with id = {}, but its in weird state {}",
519                    task_id,
520                    resist_task.task_status,
521                );
522                return EventHandleResult::fail(state);
523            }
524        } else {
525            tracing::error!(
526                "Tried finishing resist task with id = {}, but you are not Vassal or Suzerain",
527                task_id
528            );
529            return EventHandleResult::fail(state);
530        }
531
532        resist_task.task_status = if matches!(
533            resist_task.task_status,
534            VassalTaskStatus::StartedGood | VassalTaskStatus::ChangedBadToGood
535        ) {
536            VassalTaskStatus::FinishedGood
537        } else {
538            VassalTaskStatus::FinishedBad
539        };
540
541        EventHandleResult::ok(state)
542    }
543
544    pub fn handle_claim_task_reward(
545        &self,
546        task_id: Uuid,
547        mut state: OverlordState,
548    ) -> EventHandleResult<OverlordEvent, OverlordState> {
549        let mut vassal_tasks = state.character_state.vassal_tasks.clone();
550
551        let Some(vassal_task) = vassal_tasks.iter_mut().find(|task| task.id == task_id) else {
552            tracing::error!(
553                "Tried claiming reward on task with id = {}, but didn't find it in state",
554                task_id
555            );
556            return EventHandleResult::fail(state);
557        };
558
559        if !matches!(
560            vassal_task.task_status,
561            VassalTaskStatus::FinishedGood | VassalTaskStatus::FinishedBad
562        ) {
563            tracing::error!(
564                "Tried claiming reward on task with id = {}, but its not finished",
565                task_id
566            );
567            return EventHandleResult::fail(state);
568        };
569
570        if let Some(pos) = state
571            .character_state
572            .vassal_tasks
573            .iter_mut()
574            .position(|x| x == vassal_task)
575        {
576            state.character_state.vassal_tasks.swap_remove(pos);
577        } else {
578            tracing::error!(
579                "Tried claiming reward on task with id = {}, but didn't find it in state",
580                task_id
581            );
582            return EventHandleResult::fail(state);
583        };
584
585        let mut events = vec![];
586
587        if vassal_task.task_status == VassalTaskStatus::FinishedGood {
588            events.push(Self::currency_increase(
589                &vassal_task.good_reward,
590                CurrencySource::VassalTaskCompletion,
591            ));
592        } else if vassal_task.task_status == VassalTaskStatus::FinishedBad {
593            events.push(Self::currency_increase(
594                &vassal_task.bad_reward,
595                CurrencySource::VassalTaskCompletion,
596            ));
597        } else {
598            tracing::error!(
599                "Tried finishing task with id = {}, but its in weird state {}",
600                task_id,
601                vassal_task.task_status,
602            );
603            return EventHandleResult::fail(state);
604        }
605
606        EventHandleResult::ok_events(state, events)
607    }
608
609    pub fn handle_claim_resist_task_reward(
610        &self,
611        task_id: Uuid,
612        mut state: OverlordState,
613    ) -> EventHandleResult<OverlordEvent, OverlordState> {
614        let mut vassal_tasks = state.character_state.vassal_tasks.clone();
615
616        let Some(resist_task) = vassal_tasks.iter_mut().find(|task| task.id == task_id) else {
617            tracing::error!(
618                "Tried claiming reward on resist task with id = {}, but didn't find it in state",
619                task_id
620            );
621            return EventHandleResult::fail(state);
622        };
623
624        if !matches!(
625            resist_task.task_status,
626            VassalTaskStatus::FinishedGood | VassalTaskStatus::FinishedBad
627        ) {
628            tracing::error!(
629                "Tried claiming reward on resist task with id = {}, but its not finished",
630                task_id
631            );
632            return EventHandleResult::fail(state);
633        };
634
635        if let Some(pos) = state
636            .character_state
637            .vassal_tasks
638            .iter_mut()
639            .position(|x| x == resist_task)
640        {
641            state.character_state.vassal_tasks.remove(pos);
642        } else {
643            tracing::error!(
644                "Tried claiming reward on resist task with id = {}, but didn't find it in state",
645                task_id
646            );
647            return EventHandleResult::fail(state);
648        };
649
650        let mut events = vec![];
651
652        if resist_task.task_status == VassalTaskStatus::FinishedGood {
653            events.push(Self::currency_increase(
654                &resist_task.good_reward,
655                CurrencySource::VassalTaskCompletion,
656            ));
657        } else if resist_task.task_status == VassalTaskStatus::FinishedBad {
658            events.push(Self::currency_increase(
659                &resist_task.bad_reward,
660                CurrencySource::VassalTaskCompletion,
661            ));
662        } else {
663            tracing::error!(
664                "Tried claiming resist task reward with id = {}, but its in weird state {}",
665                task_id,
666                resist_task.task_status,
667            );
668            return EventHandleResult::fail(state);
669        }
670
671        EventHandleResult::ok_events(state, events)
672    }
673
674    pub fn handle_new_suzerain(
675        &self,
676        new_suzerain: Option<Suzerain>,
677        mut state: OverlordState,
678    ) -> EventHandleResult<OverlordEvent, OverlordState> {
679        state.character_state.suzerain = new_suzerain;
680
681        state
682            .character_state
683            .vassal_tasks
684            .retain(|task| task.vassal_id != state.character_state.character.id);
685
686        EventHandleResult::ok(state)
687    }
688
689    pub fn handle_remove_vassal(
690        &self,
691        vassal_id: Uuid,
692        mut state: OverlordState,
693    ) -> EventHandleResult<OverlordEvent, OverlordState> {
694        let Some(vassal_position) = state
695            .character_state
696            .vassals
697            .iter()
698            .position(|vassal| vassal.character_id == vassal_id)
699        else {
700            tracing::error!(
701                "Tried removing vassal with id = {}, but didnt find it in state",
702                vassal_id
703            );
704            return EventHandleResult::fail(state);
705        };
706
707        state.character_state.vassals.remove(vassal_position);
708
709        EventHandleResult::ok(state)
710    }
711}