1 use rltk::{VirtualKeyCode, Rltk, Point};
2 use specs::prelude::*;
3 use super::{Position, Player, WantsToMelee,
4 Map, State, Viewshed,
5 RunState, CombatStats, WantsToPickupItem,
6 GameLog, Item, TileType,
7 Monster};
8 use std::cmp::{min, max};
9
10 fn get_item(ecs: &mut World) {
11 let player_pos = ecs.fetch::<Point>();
12 let player_entity = ecs.fetch::<Entity>();
13 let entities = ecs.entities();
14 let items = ecs.read_storage::<Item>();
15 let positions = ecs.read_storage::<Position>();
16 let mut gamelog = ecs.fetch_mut::<GameLog>();
17
18 let mut target_item : Option<Entity> = None;
19 for (item_entity, _item, position) in (&entities, &items, &positions).join() {
20 if position.x == player_pos.x && position.y == player_pos.y {
21 target_item = Some(item_entity);
22 }
23 }
24
25 match target_item {
26 None => gamelog.entries.push("There is nothing here to pick up.".to_string()),
27 Some(item) => {
28 let mut pickup = ecs.write_storage::<WantsToPickupItem>();
29 pickup.insert(*player_entity, WantsToPickupItem{ collected_by: *player_entity, item }).expect("Unable to insert want to pickup");
30 }
31 }
32 }
33
34 pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
35 let mut positions = ecs.write_storage::<Position>();
36 let players = ecs.write_storage::<Player>();
37 let mut viewsheds = ecs.write_storage::<Viewshed>();
38 let combat_stats = ecs.read_storage::<CombatStats>();
39 let map = ecs.fetch::<Map>();
40 let entities = ecs.entities();
41 let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
42
43 for (entity, _player, pos, viewshed) in (&entities, &players, &mut positions, &mut viewsheds).join() {
44 if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return; }
45 let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
46
47 for potential_target in map.tile_content[destination_idx].iter() {
48 let target = combat_stats.get(*potential_target);
49 if let Some(_target) = target {
50 wants_to_melee.insert(entity, WantsToMelee{ target: *potential_target }).expect("Add target failed");
51 return;
52 }
53 }
54
55 if !map.blocked[destination_idx] {
56 pos.x = min(79 , max(0, pos.x + delta_x));
57 pos.y = min(49, max(0, pos.y + delta_y));
58
59 let mut ppos = ecs.write_resource::<Point>();
60 ppos.x = pos.x;
61 ppos.y = pos.y;
62
63 viewshed.dirty = true;
64 }
65 }
66 }
67
68 pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
69 // Player movement
70 match ctx.key {
71 None => { return RunState::AwaitingInput } // Nothing happened
72 Some(key) => match key {
73 VirtualKeyCode::Left |
74 VirtualKeyCode::Numpad4 |
75 VirtualKeyCode::A |
76 VirtualKeyCode::H => try_move_player(-1, 0, &mut gs.ecs),
77
78 VirtualKeyCode::Right |
79 VirtualKeyCode::Numpad6 |
80 VirtualKeyCode::D |
81 VirtualKeyCode::L => try_move_player(1, 0, &mut gs.ecs),
82
83 VirtualKeyCode::Up |
84 VirtualKeyCode::Numpad8 |
85 VirtualKeyCode::W |
86 VirtualKeyCode::K => try_move_player(0, -1, &mut gs.ecs),
87
88 VirtualKeyCode::Down |
89 VirtualKeyCode::Numpad2 |
90 VirtualKeyCode::S |
91 VirtualKeyCode::J => try_move_player(0, 1, &mut gs.ecs),
92
93 // Diagonals
94 VirtualKeyCode::Numpad9 |
95 VirtualKeyCode::E |
96 VirtualKeyCode::Y => try_move_player(1, -1, &mut gs.ecs),
97
98 VirtualKeyCode::Numpad7 |
99 VirtualKeyCode::Q |
100 VirtualKeyCode::U => try_move_player(-1, -1, &mut gs.ecs),
101
102 VirtualKeyCode::Numpad3 |
103 VirtualKeyCode::C |
104 VirtualKeyCode::N => try_move_player(1, 1, &mut gs.ecs),
105
106 VirtualKeyCode::Numpad1 |
107 VirtualKeyCode::Z |
108 VirtualKeyCode::B => try_move_player(-1, 1, &mut gs.ecs),
109
110 VirtualKeyCode::G => get_item(&mut gs.ecs),
111 VirtualKeyCode::I => return RunState::ShowInventory,
112 VirtualKeyCode::X => return RunState::ShowDropItem,
113
114 VirtualKeyCode::Period => {
115 if try_next_level(&mut gs.ecs) {
116 return RunState::NextLevel;
117 }
118 }
119
120 VirtualKeyCode::R => return RunState::ShowRemoveItem,
121
122 // Skip Turn
123 VirtualKeyCode::Numpad5 => return skip_turn(&mut gs.ecs),
124 VirtualKeyCode::Space => return skip_turn(&mut gs.ecs),
125
126 // Save and Quit
127 VirtualKeyCode::Escape => return RunState::SaveGame,
128
129 _ => { return RunState::AwaitingInput }
130 },
131 }
132 RunState::PlayerTurn
133 }
134
135 fn skip_turn(ecs: &mut World) -> RunState {
136 let player_entity = ecs.fetch::<Entity>();
137 let viewshed_components = ecs.read_storage::<Viewshed>();
138 let monsters = ecs.read_storage::<Monster>();
139
140 let worldmap_resource = ecs.fetch::<Map>();
141
142 let mut can_heal = true;
143 let viewshed = viewshed_components.get(*player_entity).unwrap();
144 for tile in viewshed.visible_tiles.iter() {
145 let idx = worldmap_resource.xy_idx(tile.x, tile.y);
146 for entity_id in worldmap_resource.tile_content[idx].iter() {
147 let mob = monsters.get(*entity_id);
148 match mob {
149 None => {}
150 Some(_) => { can_heal = false; }
151 }
152 }
153 }
154
155 if can_heal {
156 let mut health_components = ecs.write_storage::<CombatStats>();
157 let player_hp = health_components.get_mut(*player_entity).unwrap();
158 player_hp.hp = i32::min(player_hp.hp + 1, player_hp.max_hp);
159 }
160
161 RunState::PlayerTurn
162 }
163
164 pub fn try_next_level(ecs: &mut World) -> bool {
165 let player_pos = ecs.fetch::<Point>();
166 let map = ecs.fetch::<Map>();
167 let player_idx = map.xy_idx(player_pos.x, player_pos.y);
168 if map.tiles[player_idx] == TileType::DownStairs {
169 true
170 } else {
171 let mut gamelog = ecs.fetch_mut::<GameLog>();
172 gamelog.entries.push("There is no way down from here.".to_string());
173 false
174 }
175 }