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}