1use std::collections::HashMap;
7use std::time::{Duration, Instant};
8
9use super::combat::{CombatState, CombatResult, Enemy};
10use super::perception::PerceptionManager;
11use super::runtime::{
12 Alter, AlterCategory, AlterPresenceState, AnimaState, FrontingState,
13 MemoryAccess, PluralSystem, RealityLayer, Trigger, TriggerCategory,
14};
15
16#[derive(Debug)]
22pub struct GameState {
23 pub system: PluralSystem,
25 pub perception: PerceptionManager,
27 pub combat: Option<CombatState>,
29 pub scene: Scene,
31 pub game_time: u64,
33 pub real_time: Instant,
35 pub phase: GamePhase,
37 pub events: Vec<GameEvent>,
39 pub flags: HashMap<String, FlagValue>,
41 pub inventory: Vec<Item>,
43 pub unlocked_abilities: Vec<String>,
45 pub processed_traumas: Vec<String>,
47}
48
49impl GameState {
50 pub fn new() -> Self {
52 let mut system = PluralSystem::new("The Council");
53
54 system.add_alter(create_host_alter());
56 system.add_alter(create_protector_alter());
57 system.fronting = FrontingState::Single("host".to_string());
58
59 Self {
60 system,
61 perception: PerceptionManager::new(),
62 combat: None,
63 scene: Scene::default(),
64 game_time: 0,
65 real_time: Instant::now(),
66 phase: GamePhase::Exploration,
67 events: Vec::new(),
68 flags: HashMap::new(),
69 inventory: Vec::new(),
70 unlocked_abilities: Vec::new(),
71 processed_traumas: Vec::new(),
72 }
73 }
74
75 pub fn from_save(save: SaveData) -> Self {
77 Self {
78 system: save.system,
79 perception: PerceptionManager::new(),
80 combat: None,
81 scene: save.scene,
82 game_time: save.game_time,
83 real_time: Instant::now(),
84 phase: GamePhase::Exploration,
85 events: Vec::new(),
86 flags: save.flags,
87 inventory: save.inventory,
88 unlocked_abilities: save.unlocked_abilities,
89 processed_traumas: save.processed_traumas,
90 }
91 }
92
93 pub fn to_save(&self) -> SaveData {
95 SaveData {
96 system: self.system.clone(),
97 scene: self.scene.clone(),
98 game_time: self.game_time,
99 flags: self.flags.clone(),
100 inventory: self.inventory.clone(),
101 unlocked_abilities: self.unlocked_abilities.clone(),
102 processed_traumas: self.processed_traumas.clone(),
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq)]
109pub enum GamePhase {
110 Exploration,
112 Dialogue(DialogueState),
114 Combat,
116 Cutscene,
118 Headspace,
120 Paused,
122 GameOver(GameOverReason),
124}
125
126#[derive(Debug, Clone, PartialEq)]
128pub enum GameOverReason {
129 SystemCollapse,
131 PlayerChoice,
133 Ending(String),
135}
136
137pub struct GameLoop {
143 pub state: GameState,
145 pub target_ups: u32,
147 pub running: bool,
149 pub input_queue: Vec<PlayerInput>,
151}
152
153impl GameLoop {
154 pub fn new() -> Self {
156 Self {
157 state: GameState::new(),
158 target_ups: 60,
159 running: true,
160 input_queue: Vec::new(),
161 }
162 }
163
164 pub fn run(&mut self) {
166 let frame_duration = Duration::from_secs(1) / self.target_ups;
167 let mut last_update = Instant::now();
168
169 while self.running {
170 let now = Instant::now();
171 let delta = now.duration_since(last_update);
172
173 if delta >= frame_duration {
174 self.update(delta);
175 last_update = now;
176 }
177
178 std::thread::sleep(Duration::from_millis(1));
180 }
181 }
182
183 pub fn update(&mut self, delta: Duration) {
185 self.process_input();
187
188 match &self.state.phase {
190 GamePhase::Exploration => self.update_exploration(delta),
191 GamePhase::Dialogue(_) => self.update_dialogue(delta),
192 GamePhase::Combat => self.update_combat(delta),
193 GamePhase::Cutscene => self.update_cutscene(delta),
194 GamePhase::Headspace => self.update_headspace(delta),
195 GamePhase::Paused => {}
196 GamePhase::GameOver(_) => {}
197 }
198
199 self.update_plural_system(delta);
201 self.update_perception(delta);
202 self.process_events();
203
204 self.state.game_time += delta.as_millis() as u64 / 100; self.check_game_over();
209 }
210
211 fn process_input(&mut self) {
213 while let Some(input) = self.input_queue.pop() {
214 match input {
215 PlayerInput::Move(direction) => {
216 if self.state.phase == GamePhase::Exploration {
217 self.handle_movement(direction);
218 }
219 }
220 PlayerInput::Interact => {
221 self.handle_interaction();
222 }
223 PlayerInput::OpenMenu(menu) => {
224 self.open_menu(menu);
225 }
226 PlayerInput::SwitchAlter(alter_id) => {
227 self.request_alter_switch(&alter_id);
228 }
229 PlayerInput::UseAbility(ability_id) => {
230 self.use_ability(&ability_id);
231 }
232 PlayerInput::Ground => {
233 self.attempt_grounding();
234 }
235 PlayerInput::Pause => {
236 self.toggle_pause();
237 }
238 PlayerInput::DialogueChoice(choice) => {
239 if let GamePhase::Dialogue(ref mut dialogue) = self.state.phase {
240 self.select_dialogue_choice(choice);
241 }
242 }
243 PlayerInput::CombatAction(action) => {
244 if self.state.phase == GamePhase::Combat {
245 self.execute_combat_action(action);
246 }
247 }
248 }
249 }
250 }
251
252 fn update_exploration(&mut self, delta: Duration) {
254 for entity in &mut self.state.scene.entities {
256 entity.update(delta);
257 }
258
259 self.check_trigger_zones();
261
262 if let Some(enemy) = self.check_enemy_encounter() {
264 self.start_combat(enemy);
265 }
266
267 self.update_interactables();
269 }
270
271 fn update_combat(&mut self, _delta: Duration) {
273 let combat_result = if let Some(ref mut combat) = self.state.combat {
275 combat.check_combat_end();
277 if combat.is_over { combat.result.clone() } else { None }
278 } else {
279 None
280 };
281
282 if let Some(result) = combat_result {
284 self.end_combat(Some(result));
285 }
286 }
287
288 fn update_dialogue(&mut self, _delta: Duration) {
290 }
292
293 fn update_cutscene(&mut self, delta: Duration) {
295 if let Some(cutscene) = &mut self.state.scene.active_cutscene {
297 cutscene.advance(delta);
298 if cutscene.is_complete() {
299 self.end_cutscene();
300 }
301 }
302 }
303
304 fn update_headspace(&mut self, delta: Duration) {
306 self.update_inner_world(delta);
308 }
309
310 fn update_plural_system(&mut self, delta: Duration) {
312 let delta_secs = delta.as_secs_f32();
313
314 for alter in self.state.system.alters.values_mut() {
316 if !matches!(alter.state, AlterPresenceState::Fronting) {
317 alter.time_since_front += (delta_secs * 1000.0) as u64;
318 }
319 }
320
321 if self.state.phase == GamePhase::Exploration && self.state.scene.safety_level > 0.5 {
323 self.state.system.stability += delta_secs * 0.01;
324 self.state.system.stability = self.state.system.stability.min(1.0);
325 }
326
327 if self.state.system.dissociation > 0.0 {
329 self.state.system.dissociation -= delta_secs * 0.005;
330 self.state.system.dissociation = self.state.system.dissociation.max(0.0);
331 }
332
333 self.state.system.update_blended_anima();
335
336 let triggers: Vec<_> = self.state.system.active_triggers.drain(..).collect();
338 for trigger in triggers {
339 let result = self.state.system.process_trigger(trigger);
340 self.handle_trigger_result(result);
341 }
342 }
343
344 fn update_perception(&mut self, _delta: Duration) {
346 self.state.perception.update(&self.state.system);
347
348 let triggers = self.state.perception.drain_triggers();
350 for trigger in triggers {
351 self.state.system.active_triggers.push(trigger);
352 }
353
354 match self.state.perception.state.layer {
356 RealityLayer::Grounded => {
357 }
359 RealityLayer::Fractured => {
360 self.reveal_fractured_content();
362 }
363 RealityLayer::Shattered => {
364 self.handle_shattered_reality();
366 }
367 RealityLayer::Custom(_) => {}
368 }
369 }
370
371 fn process_events(&mut self) {
373 let events: Vec<_> = self.state.events.drain(..).collect();
374 for event in events {
375 self.handle_event(event);
376 }
377 }
378
379 fn check_game_over(&mut self) {
381 if self.state.system.stability <= 0.0 && self.state.system.dissociation >= 1.0 {
383 self.state.phase = GamePhase::GameOver(GameOverReason::SystemCollapse);
384 }
385 }
386
387 fn handle_movement(&mut self, direction: Direction) {
393 self.state.scene.move_player(direction);
395
396 if let Some(transition) = self.state.scene.check_transition() {
398 self.transition_scene(transition);
399 }
400 }
401
402 fn handle_interaction(&mut self) {
404 let interaction = self.state.scene.nearest_interactable().cloned();
406
407 if let Some(target) = interaction {
408 match &target.interaction_type {
409 InteractionType::Examine => {
410 self.show_examination(&target);
411 }
412 InteractionType::Talk(npc_id) => {
413 self.start_dialogue(npc_id.clone());
414 }
415 InteractionType::PickUp(item) => {
416 self.pick_up_item(item.clone());
417 }
418 InteractionType::Use => {
419 self.use_object(&target);
420 }
421 InteractionType::Enter(scene_id) => {
422 self.transition_scene(scene_id.clone());
423 }
424 }
425 }
426 }
427
428 fn request_alter_switch(&mut self, alter_id: &str) {
430 let urgency = if self.state.phase == GamePhase::Combat { 0.8 } else { 0.5 };
431 let result = self.state.system.request_switch(alter_id, urgency, false);
432
433 match result {
434 super::runtime::SwitchResult::Success => {
435 self.state.events.push(GameEvent::AlterSwitched(alter_id.to_string()));
436
437 if let Some(alter) = self.state.system.alters.get(alter_id) {
439 self.state.perception.state.layer = alter.preferred_reality.clone();
440 }
441 }
442 super::runtime::SwitchResult::Resisted { resistance } => {
443 self.state.events.push(GameEvent::SwitchResisted(resistance));
444 }
445 super::runtime::SwitchResult::Failed(reason) => {
446 self.state.events.push(GameEvent::SwitchFailed(format!("{:?}", reason)));
447 }
448 _ => {}
449 }
450 }
451
452 fn attempt_grounding(&mut self) {
454 let success_chance = 0.5 + self.state.system.stability * 0.3;
456
457 if rand_float() < success_chance {
458 self.state.system.dissociation -= 0.2;
459 self.state.system.dissociation = self.state.system.dissociation.max(0.0);
460
461 if self.state.perception.state.layer != RealityLayer::Grounded {
463 self.state.perception.begin_layer_transition(RealityLayer::Grounded, 0.1);
464 }
465
466 self.state.events.push(GameEvent::GroundingSuccess);
467 } else {
468 self.state.events.push(GameEvent::GroundingFailed);
469 }
470 }
471
472 fn start_combat(&mut self, enemy: Enemy) {
474 let combat = CombatState::new(&self.state.system, vec![enemy]);
475 self.state.combat = Some(combat);
476 self.state.phase = GamePhase::Combat;
477 self.state.events.push(GameEvent::CombatStarted);
478 }
479
480 fn end_combat(&mut self, result: Option<CombatResult>) {
482 self.state.combat = None;
483 self.state.phase = GamePhase::Exploration;
484
485 match result {
486 Some(CombatResult::Victory) => {
487 self.state.events.push(GameEvent::CombatVictory);
488 }
489 Some(CombatResult::Defeat) => {
490 self.handle_combat_defeat();
492 }
493 Some(CombatResult::Flee) => {
494 self.state.events.push(GameEvent::CombatFled);
495 }
496 _ => {}
497 }
498 }
499
500 fn handle_combat_defeat(&mut self) {
502 if let Some(protector) = self.find_protector() {
504 self.state.system.request_switch(&protector, 1.0, true);
505 }
506
507 self.state.system.dissociation += 0.3;
509
510 self.state.events.push(GameEvent::EmergencyRetreat);
512 }
513
514 fn start_dialogue(&mut self, npc_id: String) {
516 let dialogue_state = DialogueState {
517 npc_id,
518 current_node: "start".to_string(),
519 history: Vec::new(),
520 };
521 self.state.phase = GamePhase::Dialogue(dialogue_state);
522 self.state.events.push(GameEvent::DialogueStarted);
523 }
524
525 fn handle_trigger_result(&mut self, result: super::runtime::TriggerResult) {
527 match result {
528 super::runtime::TriggerResult::ForcedSwitch(alter_id) => {
529 self.state.events.push(GameEvent::ForcedSwitch(alter_id));
530 }
531 super::runtime::TriggerResult::Activation(alters) => {
532 for alter_id in alters {
533 self.state.events.push(GameEvent::AlterActivated(alter_id));
534 }
535 }
536 super::runtime::TriggerResult::Dissociation => {
537 self.state.system.dissociation += 0.2;
538 self.state.events.push(GameEvent::DissociationSpike);
539 }
540 super::runtime::TriggerResult::NoResponse => {}
541 }
542 }
543
544 fn handle_event(&mut self, event: GameEvent) {
546 match event {
549 GameEvent::AlterSwitched(id) => {
550 }
552 GameEvent::CombatStarted => {
553 }
555 GameEvent::TraumaProcessed(id) => {
556 self.state.processed_traumas.push(id);
557 }
558 _ => {}
559 }
560 }
561
562 fn find_protector(&self) -> Option<String> {
567 self.state.system.alters.values()
568 .find(|a| a.abilities.contains("protection") || a.abilities.contains("combat"))
569 .map(|a| a.id.clone())
570 }
571
572 fn check_trigger_zones(&mut self) {
573 for zone in &self.state.scene.trigger_zones {
575 if zone.contains(self.state.scene.player_position) {
576 let trigger = Trigger {
577 id: zone.trigger_id.clone(),
578 name: zone.name.clone(),
579 category: TriggerCategory::Environmental,
580 intensity: zone.intensity,
581 context: HashMap::new(),
582 };
583 self.state.system.active_triggers.push(trigger);
584 }
585 }
586 }
587
588 fn check_enemy_encounter(&self) -> Option<Enemy> {
589 None }
592
593 fn reveal_fractured_content(&mut self) {
594 }
596
597 fn handle_shattered_reality(&mut self) {
598 if rand_float() < 0.01 {
600 self.state.system.dissociation += 0.05;
601 }
602 }
603
604 fn update_inner_world(&mut self, _delta: Duration) {
605 }
607
608 fn show_examination(&mut self, _target: &Interactable) {}
609 fn pick_up_item(&mut self, item: Item) {
610 self.state.inventory.push(item);
611 }
612 fn use_object(&mut self, _target: &Interactable) {}
613 fn transition_scene(&mut self, _scene_id: String) {}
614 fn update_interactables(&mut self) {}
615 fn open_menu(&mut self, _menu: MenuType) {}
616 fn use_ability(&mut self, _ability_id: &str) {}
617 fn toggle_pause(&mut self) {
618 if self.state.phase == GamePhase::Paused {
619 self.state.phase = GamePhase::Exploration;
620 } else {
621 self.state.phase = GamePhase::Paused;
622 }
623 }
624 fn select_dialogue_choice(&mut self, _choice: usize) {}
625 fn execute_combat_action(&mut self, _action: CombatAction) {}
626 fn end_cutscene(&mut self) {
627 self.state.phase = GamePhase::Exploration;
628 }
629}
630
631#[derive(Debug, Clone)]
637pub enum PlayerInput {
638 Move(Direction),
639 Interact,
640 OpenMenu(MenuType),
641 SwitchAlter(String),
642 UseAbility(String),
643 Ground,
644 Pause,
645 DialogueChoice(usize),
646 CombatAction(CombatAction),
647}
648
649#[derive(Debug, Clone, Copy)]
651pub enum Direction {
652 Up,
653 Down,
654 Left,
655 Right,
656}
657
658#[derive(Debug, Clone)]
660pub enum MenuType {
661 System,
662 Inventory,
663 Alters,
664 Map,
665}
666
667#[derive(Debug, Clone)]
669pub enum CombatAction {
670 Attack(usize),
671 Defend,
672 UseItem(usize),
673 Switch(String),
674 Flee,
675}
676
677#[derive(Debug, Clone)]
679pub enum GameEvent {
680 AlterSwitched(String),
681 AlterActivated(String),
682 ForcedSwitch(String),
683 SwitchResisted(f32),
684 SwitchFailed(String),
685 CombatStarted,
686 CombatVictory,
687 CombatDefeat,
688 CombatFled,
689 EmergencyRetreat,
690 DialogueStarted,
691 DialogueEnded,
692 GroundingSuccess,
693 GroundingFailed,
694 DissociationSpike,
695 RealityShift(RealityLayer),
696 TraumaProcessed(String),
697 ItemAcquired(String),
698 AbilityUnlocked(String),
699}
700
701#[derive(Debug, Clone, PartialEq)]
703pub struct DialogueState {
704 pub npc_id: String,
705 pub current_node: String,
706 pub history: Vec<String>,
707}
708
709#[derive(Debug, Clone, Default)]
711pub struct Scene {
712 pub id: String,
713 pub name: String,
714 pub safety_level: f32,
715 pub entities: Vec<SceneEntity>,
716 pub trigger_zones: Vec<TriggerZone>,
717 pub interactables: Vec<Interactable>,
718 pub player_position: (f32, f32),
719 pub active_cutscene: Option<Cutscene>,
720}
721
722impl Scene {
723 fn move_player(&mut self, direction: Direction) {
724 let speed = 5.0;
725 match direction {
726 Direction::Up => self.player_position.1 -= speed,
727 Direction::Down => self.player_position.1 += speed,
728 Direction::Left => self.player_position.0 -= speed,
729 Direction::Right => self.player_position.0 += speed,
730 }
731 }
732
733 fn check_transition(&self) -> Option<String> {
734 None
735 }
736
737 fn nearest_interactable(&self) -> Option<&Interactable> {
738 None
739 }
740}
741
742#[derive(Debug, Clone)]
744pub struct SceneEntity {
745 pub id: String,
746 pub position: (f32, f32),
747 pub entity_type: String,
748}
749
750impl SceneEntity {
751 fn update(&mut self, _delta: Duration) {}
752}
753
754#[derive(Debug, Clone)]
756pub struct TriggerZone {
757 pub name: String,
758 pub trigger_id: String,
759 pub bounds: ((f32, f32), (f32, f32)),
760 pub intensity: f32,
761}
762
763impl TriggerZone {
764 fn contains(&self, pos: (f32, f32)) -> bool {
765 pos.0 >= self.bounds.0.0 && pos.0 <= self.bounds.1.0 &&
766 pos.1 >= self.bounds.0.1 && pos.1 <= self.bounds.1.1
767 }
768}
769
770#[derive(Debug, Clone)]
772pub struct Interactable {
773 pub id: String,
774 pub position: (f32, f32),
775 pub interaction_type: InteractionType,
776}
777
778#[derive(Debug, Clone)]
780pub enum InteractionType {
781 Examine,
782 Talk(String),
783 PickUp(Item),
784 Use,
785 Enter(String),
786}
787
788#[derive(Debug, Clone)]
790pub struct Item {
791 pub id: String,
792 pub name: String,
793 pub description: String,
794 pub item_type: ItemType,
795}
796
797#[derive(Debug, Clone)]
799pub enum ItemType {
800 Key,
801 Consumable,
802 Document,
803 Memento,
804}
805
806#[derive(Debug, Clone)]
808pub struct Cutscene {
809 pub id: String,
810 pub duration: Duration,
811 pub elapsed: Duration,
812}
813
814impl Cutscene {
815 fn advance(&mut self, delta: Duration) {
816 self.elapsed += delta;
817 }
818
819 fn is_complete(&self) -> bool {
820 self.elapsed >= self.duration
821 }
822}
823
824#[derive(Debug, Clone)]
826pub struct SaveData {
827 pub system: PluralSystem,
828 pub scene: Scene,
829 pub game_time: u64,
830 pub flags: HashMap<String, FlagValue>,
831 pub inventory: Vec<Item>,
832 pub unlocked_abilities: Vec<String>,
833 pub processed_traumas: Vec<String>,
834}
835
836#[derive(Debug, Clone)]
838pub enum FlagValue {
839 Bool(bool),
840 Int(i32),
841 String(String),
842}
843
844fn create_host_alter() -> Alter {
849 use std::collections::HashSet;
850 Alter {
851 id: "host".to_string(),
852 name: "Host".to_string(),
853 category: AlterCategory::Council,
854 state: AlterPresenceState::Fronting,
855 anima: AnimaState::default(),
856 base_arousal: 0.0,
857 base_dominance: 0.0,
858 time_since_front: 0,
859 triggers: vec![],
860 abilities: HashSet::from(["perception".to_string()]),
861 preferred_reality: RealityLayer::Grounded,
862 memory_access: MemoryAccess::Partial(vec!["recent".to_string()]),
863 }
864}
865
866fn create_protector_alter() -> Alter {
867 use std::collections::HashSet;
868 Alter {
869 id: "protector".to_string(),
870 name: "Protector".to_string(),
871 category: AlterCategory::Council,
872 state: AlterPresenceState::Dormant,
873 anima: AnimaState::new(0.0, 0.5, 0.7),
874 base_arousal: 0.5,
875 base_dominance: 0.7,
876 time_since_front: 1000,
877 triggers: vec!["threat".to_string(), "danger".to_string()],
878 abilities: HashSet::from(["combat".to_string(), "protection".to_string()]),
879 preferred_reality: RealityLayer::Grounded,
880 memory_access: MemoryAccess::Full,
881 }
882}
883
884fn rand_float() -> f32 {
886 0.5
888}
889
890#[cfg(test)]
895mod tests {
896 use super::*;
897
898 #[test]
899 fn test_game_state_new() {
900 let state = GameState::new();
901 assert!(state.system.alters.contains_key("host"));
902 assert!(state.system.alters.contains_key("protector"));
903 assert_eq!(state.phase, GamePhase::Exploration);
904 }
905
906 #[test]
907 fn test_game_loop_creation() {
908 let game_loop = GameLoop::new();
909 assert!(game_loop.running);
910 assert_eq!(game_loop.target_ups, 60);
911 }
912
913 #[test]
914 fn test_alter_switch_in_game() {
915 let mut game_loop = GameLoop::new();
916 game_loop.request_alter_switch("protector");
917
918 assert!(!game_loop.state.events.is_empty());
920 }
921
922 #[test]
923 fn test_grounding_mechanic() {
924 let mut game_loop = GameLoop::new();
925 game_loop.state.system.dissociation = 0.5;
926 game_loop.state.system.stability = 0.8;
927
928 game_loop.attempt_grounding();
929
930 assert!(!game_loop.state.events.is_empty());
932 }
933}