overlord_event_system/logic/
vassals.rs

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