underworld_core/
game.rs

1use crate::{
2    actions::{
3        Action, AttackNpc, CastSpellOnNpc, CastSpellOnPlayer, ExitRoom, InspectFixture, InspectNpc,
4        LookAtFixture, LookAtNpc, LootFixture, LootNpc, MovePlayerItem, OpenFixture,
5        OpenFixtureHiddenCompartment, UseItemOnPlayer,
6    },
7    components::{
8        games::GameState,
9        items::{
10            ConsumableEffectName, {packed_tags_for_item_type, ready_tag_for_item_type},
11        },
12        PlayerCharacter,
13    },
14    errors::Error,
15    events::Event,
16    handlers::{handle_action, HandledAction},
17};
18
19pub struct Game {
20    pub state: GameState,
21    pub player: PlayerCharacter,
22}
23
24impl Game {
25    pub fn handle_action(&mut self, action: &Action) -> Result<Vec<Event>, Error> {
26        let HandledAction {
27            events,
28            new_state,
29            new_player,
30        } = handle_action(action, &self.state, &self.player)?;
31        self.state = new_state;
32        self.player = new_player;
33
34        Ok(events)
35    }
36
37    pub fn current_actions(&self) -> Vec<Action> {
38        let fixture_actions = self
39            .state
40            .current_room()
41            .fixture_positions
42            .iter()
43            .flat_map(|fixture_position| Some(&fixture_position.fixture))
44            .flat_map(|fixture| {
45                let mut actions = vec![
46                    Action::LookAtFixture(LookAtFixture {
47                        fixture_id: fixture.id.to_string(),
48                    }),
49                    Action::InspectFixture(InspectFixture {
50                        fixture_id: fixture.id.to_string(),
51                        discover_hidden_compartment: true,
52                    }),
53                ];
54
55                if fixture.can_be_opened && !fixture.open {
56                    actions.push(Action::OpenFixture(OpenFixture {
57                        fixture_id: fixture.id.to_string(),
58                    }));
59                }
60
61                let knowledge = self.state.fixture_knowledge(&fixture.id);
62                if knowledge.knows_has_hidden_compartment
63                    && !fixture.hidden_compartment_open
64                    && fixture.has_hidden_compartment
65                {
66                    actions.push(Action::OpenFixtureHiddenCompartment(
67                        OpenFixtureHiddenCompartment {
68                            fixture_id: fixture.id.to_string(),
69                        },
70                    ));
71                }
72
73                let mut item_ids: Vec<String> = Vec::new();
74
75                if fixture.open {
76                    for fixture_item in fixture
77                        .items
78                        .iter()
79                        .filter(|fixture_item| fixture_item.is_inside)
80                    {
81                        item_ids.push(fixture_item.item.id.to_string());
82                    }
83                }
84
85                if fixture.hidden_compartment_open {
86                    for fixture_item in fixture
87                        .items
88                        .iter()
89                        .filter(|fixture_item| fixture_item.is_in_hidden_compartment)
90                    {
91                        item_ids.push(fixture_item.item.id.to_string());
92                    }
93                }
94
95                for fixture_item in fixture.items.iter().filter(|fixture_item| {
96                    !fixture_item.is_inside && !fixture_item.is_in_hidden_compartment
97                }) {
98                    item_ids.push(fixture_item.item.id.to_string());
99                }
100
101                if !item_ids.is_empty() {
102                    actions.push(Action::LootFixture(LootFixture {
103                        fixture_id: fixture.id.to_string(),
104                        item_ids,
105                    }))
106                }
107
108                actions
109            });
110
111        let npc_actions = self
112            .state
113            .current_room()
114            .npc_positions
115            .iter()
116            .map(|npc_position| &npc_position.npc)
117            .flat_map(|npc| {
118                let mut actions = vec![
119                    Action::LookAtNpc(LookAtNpc {
120                        npc_id: npc.id.to_string(),
121                    }),
122                    Action::InspectNpc(InspectNpc {
123                        npc_id: npc.id.to_string(),
124                        discover_health: true,
125                        discover_packed_items: true,
126                    }),
127                ];
128
129                if !npc.character.is_dead() {
130                    actions.push(Action::AttackNpc(AttackNpc {
131                        npc_id: npc.id.to_string(),
132                    }));
133
134                    for learned_spell in self.player.character.spell_memory.spells.iter() {
135                        actions.push(Action::CastSpellOnNpc(CastSpellOnNpc {
136                            spell_id: learned_spell.id.to_string(),
137                            npc_id: npc.id.to_string(),
138                        }));
139                    }
140                } else {
141                    let item_ids = npc
142                        .character
143                        .inventory
144                        .equipment
145                        .iter()
146                        .map(|character_item| character_item.item.id.to_string())
147                        .collect();
148
149                    actions.push(Action::LootNpc(LootNpc {
150                        npc_id: npc.id.to_string(),
151                        item_ids,
152                    }));
153                }
154
155                actions
156            });
157
158        let exit_actions = self.state.current_room_exits().into_iter().map(|id| {
159            Action::ExitRoom(ExitRoom {
160                exit_id: id.to_string(),
161            })
162        });
163
164        let spell_actions = self
165            .player
166            .character
167            .spell_memory
168            .spells
169            .iter()
170            .map(|learned_spell| {
171                Action::CastSpellOnPlayer(CastSpellOnPlayer {
172                    spell_id: learned_spell.id.to_string(),
173                })
174            });
175
176        let item_actions =
177            self.player
178                .character
179                .inventory
180                .equipment
181                .iter()
182                .flat_map(|character_item| {
183                    let mut actions: Vec<Action> = Vec::new();
184
185                    if character_item.is_consumable() {
186                        match &character_item.item.consumable {
187                            Some(consumable) => match &consumable.effect.name {
188                                ConsumableEffectName::LearnSpell
189                                | ConsumableEffectName::HealingGrog => {
190                                    actions.push(Action::UseItemOnPlayer(UseItemOnPlayer {
191                                        item_id: character_item.item.id.to_string(),
192                                    }));
193                                }
194                            },
195                            None => {}
196                        }
197                    } else if character_item.is_packed() {
198                        let location_tag = ready_tag_for_item_type(&character_item.item.item_type);
199                        actions.push(Action::MovePlayerItem(MovePlayerItem {
200                            item_id: character_item.item.id.to_string(),
201                            location_tag: Some(location_tag),
202                            put_at_the_ready: true,
203                        }));
204                    } else {
205                        for location_tag in
206                            packed_tags_for_item_type(&character_item.item.item_type).into_iter()
207                        {
208                            actions.push(Action::MovePlayerItem(MovePlayerItem {
209                                item_id: character_item.item.id.to_string(),
210                                location_tag: Some(location_tag),
211                                put_at_the_ready: false,
212                            }));
213                        }
214                    }
215
216                    actions
217                });
218
219        npc_actions
220            .chain(exit_actions)
221            .chain(fixture_actions)
222            .chain(spell_actions)
223            .chain(item_actions)
224            .collect()
225    }
226}