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}