player.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. extern crate rltk;
  2. use rltk::{Point, Rltk, VirtualKeyCode};
  3. extern crate specs;
  4. use super::{CombatStats, Map, Player, Position, RunState, State, Viewshed, WantsToMelee};
  5. use specs::prelude::*;
  6. use std::cmp::{max, min};
  7. pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
  8. let mut positions = ecs.write_storage::<Position>();
  9. let players = ecs.read_storage::<Player>();
  10. let mut viewsheds = ecs.write_storage::<Viewshed>();
  11. let entities = ecs.entities();
  12. let combat_stats = ecs.read_storage::<CombatStats>();
  13. let map = ecs.fetch::<Map>();
  14. let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
  15. for (entity, _player, pos, viewshed) in
  16. (&entities, &players, &mut positions, &mut viewsheds).join()
  17. {
  18. let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
  19. for potential_target in map.tile_content[destination_idx].iter() {
  20. let target = combat_stats.get(*potential_target);
  21. if let Some(_target) = target {
  22. wants_to_melee
  23. .insert(
  24. entity,
  25. WantsToMelee {
  26. target: *potential_target,
  27. },
  28. )
  29. .expect("Add target failed");
  30. return;
  31. }
  32. }
  33. if !map.blocked[destination_idx] {
  34. pos.x = min(79, max(0, pos.x + delta_x));
  35. pos.y = min(49, max(0, pos.y + delta_y));
  36. viewshed.dirty = true;
  37. let mut ppos = ecs.write_resource::<Point>();
  38. ppos.x = pos.x;
  39. ppos.y = pos.y;
  40. }
  41. }
  42. }
  43. fn clamp<T: PartialOrd>(low: T, high: T, val: T) -> T {
  44. if val < low {
  45. low
  46. } else if val > high {
  47. high
  48. } else {
  49. val
  50. }
  51. }
  52. #[derive(Component)]
  53. pub enum GameEvent {
  54. MoveTo {
  55. destination: usize,
  56. tgt_x: i32,
  57. tgt_y: i32,
  58. },
  59. Attack { target: Entity },
  60. }
  61. pub struct HandleGameEvent;
  62. impl<'a> System<'a> for HandleGameEvent {
  63. type SystemData = (
  64. Entities<'a>,
  65. WriteStorage<'a, GameEvent>,
  66. WriteStorage<'a, WantsToMelee>,
  67. WriteStorage<'a, Position>,
  68. WriteStorage<'a, Viewshed>,
  69. WriteExpect<'a, Point>,
  70. ReadExpect<'a, Map>,
  71. );
  72. fn run(&mut self, (entities, mut game_events, mut melee, mut pos, mut viewshed, mut point, map): Self::SystemData) {
  73. for (e, event) in (&entities, &game_events).join() {
  74. match event {
  75. GameEvent::Attack { target } => {
  76. melee.insert(e, WantsToMelee { target: *target })
  77. .expect("Unable to add melee intention for player");
  78. }
  79. GameEvent::MoveTo { destination, tgt_x, tgt_y } => {
  80. if !map.blocked[*destination] {
  81. viewshed.get_mut(e).expect("Unable to find viewshed").dirty = true;
  82. pos.get_mut(e).expect("Unable to find position").x = *tgt_x;
  83. pos.get_mut(e).expect("Unable to find position").y = *tgt_y;
  84. point.x = clamp(0, 79, *tgt_x);
  85. point.y = clamp(0, 49, *tgt_y);
  86. }
  87. }
  88. }
  89. }
  90. game_events.clear();
  91. }
  92. }
  93. pub struct HandleInputEvent;
  94. impl<'a> System<'a> for HandleInputEvent {
  95. type SystemData = (
  96. WriteStorage<'a, InputEvent>,
  97. ReadExpect<'a, Map>,
  98. Entities<'a>,
  99. ReadStorage<'a, Position>,
  100. ReadStorage<'a, CombatStats>,
  101. WriteStorage<'a, GameEvent>,
  102. );
  103. fn run(&mut self, (mut event, map, entities, position, stats, mut game_events): Self::SystemData) {
  104. for (e, ev, pos) in (&entities, &event, &position).join() {
  105. match *ev {
  106. InputEvent::PlayerMovement { delta_x, delta_y } => {
  107. // depending on what's in the target cell, we
  108. // either want to attack or move
  109. let tgt_x = pos.x + delta_x;
  110. let tgt_y = pos.y + delta_y;
  111. let destination = map.xy_idx(tgt_x, tgt_y);
  112. if let Some(target) = map.get_attackable(destination, &stats) {
  113. game_events.insert(e, GameEvent::Attack { target })
  114. .expect("Unable to add GameEvent");
  115. } else {
  116. game_events.insert(e, GameEvent::MoveTo { destination, tgt_x, tgt_y })
  117. .expect("Unable to add GameEvent");
  118. }
  119. }
  120. }
  121. }
  122. event.clear();
  123. }
  124. }
  125. /// This type represents all the "low-level" actions we have available
  126. /// to us, corresponding directly to the set of actions we have bound
  127. /// on the keys
  128. #[derive(Component)]
  129. pub enum InputEvent {
  130. // attempt to move the player some amount in the given direction
  131. // (delta_x and delta_y should be -1, 0, or 1)
  132. PlayerMovement {
  133. delta_x: i32,
  134. delta_y: i32,
  135. },
  136. }
  137. pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
  138. let player = *gs.ecs.fetch::<Entity>();
  139. let mut input = gs.ecs.write_storage::<InputEvent>();
  140. // Player movement
  141. match ctx.key {
  142. None => return RunState::AwaitingInput, // Nothing happened
  143. Some(key) => match key {
  144. VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H =>
  145. input.insert(player, InputEvent::PlayerMovement { delta_x: -1, delta_y: 0 }),
  146. VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L =>
  147. input.insert(player, InputEvent::PlayerMovement { delta_x: 1, delta_y: 0 }),
  148. VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K =>
  149. input.insert(player, InputEvent::PlayerMovement { delta_x: 0, delta_y: -1 }),
  150. VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J =>
  151. input.insert(player, InputEvent::PlayerMovement { delta_x: 0, delta_y: 1 }),
  152. // Diagonals
  153. VirtualKeyCode::Numpad9 | VirtualKeyCode::U =>
  154. input.insert(player, InputEvent::PlayerMovement { delta_x: 1, delta_y: -1 }),
  155. VirtualKeyCode::Numpad7 | VirtualKeyCode::Y =>
  156. input.insert(player, InputEvent::PlayerMovement { delta_x: -1, delta_y: -1 }),
  157. VirtualKeyCode::Numpad3 | VirtualKeyCode::N =>
  158. input.insert(player, InputEvent::PlayerMovement { delta_x: 1, delta_y: 1 }),
  159. VirtualKeyCode::Numpad1 | VirtualKeyCode::B =>
  160. input.insert(player, InputEvent::PlayerMovement { delta_x: -1, delta_y: 1 }),
  161. _ => return RunState::AwaitingInput,
  162. },
  163. }.expect("Unable to insert player movement");
  164. RunState::PlayerTurn
  165. }