rust_warrior/engine/systems/
player.rs1use std::cmp;
4
5use crate::{
6 actions::{Action, Direction},
7 engine::world::World,
8 floor::Tile,
9 unit::UnitType,
10 Warrior,
11};
12
13pub fn player_system(world: &mut World) -> Vec<String> {
18 let mut events = Vec::new();
19
20 let (wx, wy) = world.warrior.position;
21 let (health, _) = world.warrior.hp;
22 let facing = world.warrior.facing.unwrap();
23
24 let x_min = cmp::max(wx - 3, 0);
27 let x_max = cmp::min(wx + 3, world.floor.width as i32 - 1);
28
29 let west: Vec<(i32, Tile)> = (x_min..wx)
31 .rev()
32 .map(|i| {
33 let unit = world.other_units.iter_mut().find(|unit| {
34 let (x, _) = unit.position;
35 x == i
36 });
37 match unit {
38 Some(unit) => (i, Tile::Unit(unit.unit_type)),
39 _ => (i, Tile::Empty),
40 }
41 })
42 .collect();
43
44 let east: Vec<(i32, Tile)> = ((wx + 1)..=x_max)
46 .map(|i| {
47 let unit = world.other_units.iter_mut().find(|unit| {
48 let (x, _) = unit.position;
49 x == i
50 });
51 match unit {
52 Some(unit) => (i, Tile::Unit(unit.unit_type)),
53 _ => (i, Tile::Empty),
54 }
55 })
56 .collect();
57
58 let (ahead, behind) = match facing {
59 Direction::Forward => (east, west),
60 Direction::Backward => (west, east),
61 };
62
63 let warrior = Warrior::new(
64 world.warrior_level,
65 ahead.clone().into_iter().map(|(_, t)| t).collect(),
67 behind.clone().into_iter().map(|(_, t)| t).collect(),
69 health,
70 facing,
71 );
72
73 world.player.play_turn(&warrior);
74
75 if let Some(action) = warrior.action() {
76 match action {
77 Action::Walk(direction) => {
78 let target_x = if facing == direction {
79 wx + 1
82 } else {
83 wx - 1
86 };
87
88 let other_unit = world.other_units.iter().find(|unit| {
89 let (x, _) = unit.position;
90 x == target_x
91 });
92
93 match other_unit {
94 Some(unit) => {
95 events.push(format!(
96 "{warrior} bumps into {enemy:?}",
97 warrior = &world.player_name,
98 enemy = unit.unit_type
99 ));
100 }
101 _ => {
102 events.push(format!(
103 "{warrior} walks {direction:?}",
104 warrior = &world.player_name,
105 direction = direction
106 ));
107 world.warrior.position = (target_x, wy);
108 }
109 }
110 }
111 Action::Attack(direction) => {
112 let target_x = if facing == direction {
113 wx + 1
116 } else {
117 wx - 1
120 };
121
122 let other_unit = world.other_units.iter_mut().enumerate().find(|(_, unit)| {
123 let (x, _) = unit.position;
124 x == target_x
125 });
126
127 match other_unit {
128 Some((i, enemy)) => {
129 events.push(format!(
130 "{warrior} attacks {direction:?} and hits {enemy:?}",
131 warrior = &world.player_name,
132 direction = direction,
133 enemy = enemy.unit_type
134 ));
135 let atk = match direction {
136 Direction::Forward => world.warrior.atk,
137 Direction::Backward => (world.warrior.atk as f32 / 2.0).ceil() as i32,
138 };
139 let (current, max) = enemy.hp;
140 let remaining = cmp::max(current - atk, 0);
141 events.push(format!(
142 "{enemy:?} takes {atk} damage, {remaining} HP left",
143 enemy = enemy.unit_type,
144 atk = atk,
145 remaining = remaining
146 ));
147 enemy.hp = (remaining, max);
148
149 if remaining == 0 {
150 events.push(format!("{:?} is dead!", enemy.unit_type));
151 world.remove_unit(i);
152 }
153 }
154 _ => {
155 events.push(format!(
156 "{warrior} attacks {direction:?} and hits nothing",
157 warrior = &world.player_name,
158 direction = direction
159 ));
160 }
161 }
162 }
163 Action::Rest => {
164 let (current, max) = world.warrior.hp;
165 if current < max {
166 let restored = if (current + 2) > max {
167 max - current
168 } else {
169 2
170 };
171 events.push(format!(
172 "{warrior} regains {restored} HP from resting! Now {remaining} HP left",
173 warrior = &world.player_name,
174 restored = restored,
175 remaining = current + restored
176 ));
177 world.warrior.hp = (current + restored, max);
178 } else {
179 events.push(format!(
180 "{} rests but is already at max HP",
181 &world.player_name
182 ));
183 };
184 }
185 Action::Rescue(direction) => {
186 let target_x = if facing == direction {
187 wx + 1
190 } else {
191 wx - 1
194 };
195
196 let other_unit = world.other_units.iter().enumerate().find(|(_, unit)| {
197 let (x, _) = unit.position;
198 x == target_x
199 });
200
201 match other_unit {
202 Some((i, captive)) if captive.unit_type == UnitType::Captive => {
203 events.push(format!(
204 "{warrior} frees {captive:?} from their bindings",
205 warrior = &world.player_name,
206 captive = captive.unit_type
207 ));
208 events.push(format!("{:?} escapes!", captive.unit_type));
209 world.remove_unit(i);
210 }
211 Some((_, enemy)) => {
212 events.push(format!(
213 "{warrior} leans {direction:?} to rescue {enemy:?}, but it is not a captive!",
214 warrior = &world.player_name,
215 direction = direction,
216 enemy = enemy.unit_type
217 ));
218 }
219 None => {
220 events.push(format!(
221 "{warrior} leans {direction:?} to rescue someone, but nobody is here",
222 warrior = &world.player_name,
223 direction = direction
224 ));
225 }
226 }
227 }
228 Action::Pivot(direction) => {
229 events.push(format!(
230 "{warrior} pivots to face {direction:?}",
231 warrior = &world.player_name,
232 direction = direction
233 ));
234 world.warrior.facing = Some(direction);
235 }
236 Action::Shoot(direction) => {
237 let target = world.other_units.iter_mut().enumerate().find(|(_, unit)| {
239 let (x, _) = unit.position;
240 match direction {
241 Direction::Forward => {
242 matches!(ahead.iter().find(|(_, tile)| *tile != Tile::Empty), Some((target_x, _)) if *target_x == x)
243 }
244 Direction::Backward => {
245 matches!(behind.iter().find(|(_, tile)| *tile != Tile::Empty), Some((target_x, _)) if *target_x == x)
246 }
247 }
248 });
249
250 match target {
251 Some((i, enemy)) => {
252 events.push(format!(
253 "{warrior} lets loose an arrow {direction:?} and hits {enemy:?}",
254 warrior = &world.player_name,
255 direction = direction,
256 enemy = enemy.unit_type
257 ));
258 let atk = (world.warrior.atk as f32 / 2.0).ceil() as i32;
259 let (current, max) = enemy.hp;
260 let remaining = cmp::max(current - atk, 0);
261 events.push(format!(
262 "{enemy:?} takes {atk} damage, {remaining} HP left",
263 enemy = enemy.unit_type,
264 atk = atk,
265 remaining = remaining
266 ));
267 enemy.hp = (remaining, max);
268
269 if remaining == 0 {
270 events.push(format!("{:?} is dead!", enemy.unit_type));
271 world.remove_unit(i);
272 }
273 }
274 _ => {
275 events.push(format!(
276 "{warrior} lets loose an arrow {direction:?} and hits nothing",
277 warrior = &world.player_name,
278 direction = direction
279 ));
280 }
281 }
282 }
283 }
284 }
285
286 for warning in warrior.warnings() {
287 events.push(warning);
288 }
289
290 events
291}