2022-02-01 15:41:08 -05:00
|
|
|
//! Player-related functionality
|
2021-12-10 20:16:48 -05:00
|
|
|
use std::cmp::{max, min};
|
|
|
|
|
2022-02-03 15:16:41 -05:00
|
|
|
use ::rltk::prelude::*;
|
|
|
|
use ::specs::prelude::*;
|
2021-12-10 20:16:48 -05:00
|
|
|
|
2021-11-09 15:50:42 -05:00
|
|
|
use crate::components::{
|
2022-01-31 11:25:36 -05:00
|
|
|
Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved, Equipped, Faction, HungerClock,
|
2022-01-31 15:00:00 -05:00
|
|
|
HungerState, Item, Name, Player, Pools, Position, Renderable, Target, Vendor, Viewshed,
|
|
|
|
WantsToMelee, WantsToPickupItem, WantsToShoot,
|
2021-11-03 15:11:19 -04:00
|
|
|
};
|
2022-01-12 10:45:13 -05:00
|
|
|
use crate::raws::{self, Reaction, RAWS};
|
2022-02-03 14:59:35 -05:00
|
|
|
use crate::rng::roll_dice;
|
2022-02-01 10:39:46 -05:00
|
|
|
use crate::{
|
|
|
|
colors, gamelog, spatial, Map, RunState, State, TileType, VendorMode, WantsToCastSpell, Weapon,
|
|
|
|
};
|
2021-10-22 10:05:06 -04:00
|
|
|
|
2022-01-31 15:00:00 -05:00
|
|
|
fn get_player_target_list(ecs: &mut World) -> Vec<(f32, Entity)> {
|
|
|
|
let mut possible_targets = Vec::new();
|
|
|
|
let viewsheds = ecs.read_storage::<Viewshed>();
|
|
|
|
let player_entity = ecs.fetch::<Entity>();
|
|
|
|
let equipped = ecs.read_storage::<Equipped>();
|
|
|
|
let weapon = ecs.read_storage::<Weapon>();
|
|
|
|
let map = ecs.fetch::<Map>();
|
|
|
|
let positions = ecs.read_storage::<Position>();
|
|
|
|
let factions = ecs.read_storage::<Faction>();
|
|
|
|
|
|
|
|
for (equipped, weapon) in (&equipped, &weapon).join() {
|
|
|
|
if equipped.owner == *player_entity {
|
|
|
|
if let Some(range) = weapon.range {
|
|
|
|
if let Some(vs) = viewsheds.get(*player_entity) {
|
|
|
|
let player_pos = positions.get(*player_entity).unwrap();
|
|
|
|
|
|
|
|
for tile_point in vs.visible_tiles.iter() {
|
|
|
|
let tile_idx = map.xy_idx(tile_point.x, tile_point.y);
|
|
|
|
let distance_to_target = DistanceAlg::Pythagoras
|
|
|
|
.distance2d(*tile_point, Point::from(*player_pos));
|
|
|
|
if distance_to_target < range as f32 {
|
|
|
|
spatial::for_each_tile_content(tile_idx, |possible_target| {
|
|
|
|
if possible_target != *player_entity
|
|
|
|
&& factions.get(possible_target).is_some()
|
|
|
|
{
|
|
|
|
possible_targets.push((distance_to_target, possible_target));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
possible_targets.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
|
|
|
|
|
|
|
possible_targets
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn end_turn_targeting(ecs: &mut World) {
|
|
|
|
let possible_targets = get_player_target_list(ecs);
|
|
|
|
let mut targets = ecs.write_storage::<Target>();
|
|
|
|
targets.clear();
|
|
|
|
|
|
|
|
if !possible_targets.is_empty() {
|
|
|
|
targets
|
|
|
|
.insert(possible_targets[0].1, Target {})
|
|
|
|
.expect("Failed to insert Target tag");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fire_on_target(ecs: &mut World) -> RunState {
|
|
|
|
let targets = ecs.write_storage::<Target>();
|
|
|
|
let entities = ecs.entities();
|
|
|
|
let mut current_target: Option<Entity> = None;
|
|
|
|
|
|
|
|
for (e, _t) in (&entities, &targets).join() {
|
|
|
|
current_target = Some(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(target) = current_target {
|
|
|
|
let player_entity = ecs.fetch::<Entity>();
|
|
|
|
let mut shoot_store = ecs.write_storage::<WantsToShoot>();
|
|
|
|
let names = ecs.read_storage::<Name>();
|
|
|
|
if let Some(name) = names.get(target) {
|
2022-02-01 10:39:46 -05:00
|
|
|
gamelog::line("You fire at")
|
|
|
|
.append_color(colors::CYAN, &name.name)
|
|
|
|
.log();
|
2022-01-31 15:00:00 -05:00
|
|
|
}
|
|
|
|
shoot_store
|
|
|
|
.insert(*player_entity, WantsToShoot { target })
|
|
|
|
.expect("Insert Fail");
|
|
|
|
|
|
|
|
RunState::Ticking
|
|
|
|
} else {
|
2022-02-01 10:39:46 -05:00
|
|
|
gamelog::log_line("You don't have a target selected!");
|
2022-01-31 15:00:00 -05:00
|
|
|
RunState::AwaitingInput
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cycle_target(ecs: &mut World) {
|
|
|
|
let possible_targets = get_player_target_list(ecs);
|
|
|
|
let mut targets = ecs.write_storage::<Target>();
|
|
|
|
let entities = ecs.entities();
|
|
|
|
let mut current_target: Option<Entity> = None;
|
|
|
|
|
|
|
|
for (e, _t) in (&entities, &targets).join() {
|
|
|
|
current_target = Some(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
targets.clear();
|
|
|
|
if let Some(current_target) = current_target {
|
|
|
|
if !possible_targets.len() > 1 {
|
|
|
|
let mut index = 0;
|
|
|
|
for (i, target) in possible_targets.iter().enumerate() {
|
|
|
|
if target.1 == current_target {
|
|
|
|
index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if index > possible_targets.len() - 2 {
|
|
|
|
targets
|
|
|
|
.insert(possible_targets[0].1, Target {})
|
|
|
|
.expect("Failed to insert Target tag");
|
|
|
|
} else {
|
|
|
|
targets
|
|
|
|
.insert(possible_targets[index + 1].1, Target {})
|
|
|
|
.expect("Failed to insert Target tag");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-06 13:37:23 -05:00
|
|
|
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
|
2021-10-22 10:05:06 -04:00
|
|
|
let mut positions = ecs.write_storage::<Position>();
|
2021-10-29 15:15:22 -04:00
|
|
|
let players = ecs.read_storage::<Player>();
|
2021-10-26 14:23:08 -04:00
|
|
|
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
2021-10-29 15:15:22 -04:00
|
|
|
let entities = ecs.entities();
|
2022-01-11 15:35:59 -05:00
|
|
|
let combat_stats = ecs.read_storage::<Attributes>();
|
2021-10-25 14:23:19 -04:00
|
|
|
let map = ecs.fetch::<Map>();
|
2021-10-29 15:15:22 -04:00
|
|
|
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
|
2021-11-29 16:00:07 -05:00
|
|
|
let mut entity_moved = ecs.write_storage::<EntityMoved>();
|
2021-12-17 14:23:36 -05:00
|
|
|
let mut doors = ecs.write_storage::<Door>();
|
|
|
|
let mut blocks_visibility = ecs.write_storage::<BlocksVisibility>();
|
|
|
|
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
|
|
|
|
let mut renderables = ecs.write_storage::<Renderable>();
|
2022-01-11 15:35:59 -05:00
|
|
|
let factions = ecs.read_storage::<Faction>();
|
2022-01-13 11:29:20 -05:00
|
|
|
let vendors = ecs.read_storage::<Vendor>();
|
2022-01-06 13:37:23 -05:00
|
|
|
let mut result = RunState::AwaitingInput;
|
2021-12-24 14:44:48 -05:00
|
|
|
|
|
|
|
let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new();
|
2021-10-22 10:05:06 -04:00
|
|
|
|
2021-10-29 15:15:22 -04:00
|
|
|
for (entity, _player, pos, viewshed) in
|
|
|
|
(&entities, &players, &mut positions, &mut viewsheds).join()
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2022-01-06 13:37:23 -05:00
|
|
|
return RunState::AwaitingInput;
|
2021-10-29 15:15:22 -04:00
|
|
|
}
|
2021-10-25 14:23:19 -04:00
|
|
|
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
|
2021-10-29 15:15:22 -04:00
|
|
|
|
2022-01-12 10:45:13 -05:00
|
|
|
result =
|
|
|
|
spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| {
|
2022-01-13 11:29:20 -05:00
|
|
|
if vendors.get(potential_target).is_some() {
|
|
|
|
return Some(RunState::ShowVendor {
|
|
|
|
vendor: potential_target,
|
|
|
|
mode: VendorMode::Sell,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-01-12 10:45:13 -05:00
|
|
|
let mut hostile = true;
|
|
|
|
if combat_stats.get(potential_target).is_some() {
|
|
|
|
if let Some(faction) = factions.get(potential_target) {
|
|
|
|
let reaction = crate::raws::faction_reaction(
|
|
|
|
&faction.name,
|
|
|
|
"Player",
|
|
|
|
&RAWS.lock().unwrap(),
|
|
|
|
);
|
|
|
|
if reaction != Reaction::Attack {
|
|
|
|
hostile = false;
|
|
|
|
}
|
2022-01-11 15:35:59 -05:00
|
|
|
}
|
|
|
|
}
|
2022-01-12 10:45:13 -05:00
|
|
|
if !hostile {
|
|
|
|
// Note that we want to move the bystander
|
|
|
|
swap_entities.push((potential_target, pos.x, pos.y));
|
|
|
|
|
|
|
|
// Move the player
|
|
|
|
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
|
|
|
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
|
|
|
entity_moved
|
|
|
|
.insert(entity, EntityMoved {})
|
|
|
|
.expect("Unable to insert marker");
|
|
|
|
|
|
|
|
viewshed.dirty = true;
|
|
|
|
let mut ppos = ecs.write_resource::<Point>();
|
|
|
|
ppos.x = pos.x;
|
|
|
|
ppos.y = pos.y;
|
|
|
|
|
|
|
|
return Some(RunState::Ticking);
|
|
|
|
} else if combat_stats.get(potential_target).is_some() {
|
2022-01-11 15:35:59 -05:00
|
|
|
wants_to_melee
|
|
|
|
.insert(
|
|
|
|
entity,
|
|
|
|
WantsToMelee {
|
2022-01-12 10:45:13 -05:00
|
|
|
target: potential_target,
|
2022-01-11 15:35:59 -05:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.expect("Add target failed");
|
|
|
|
|
2022-01-12 10:45:13 -05:00
|
|
|
return Some(RunState::Ticking);
|
2022-01-11 15:35:59 -05:00
|
|
|
}
|
2021-10-29 15:15:22 -04:00
|
|
|
|
2022-01-12 10:45:13 -05:00
|
|
|
if let Some(door) = doors.get_mut(potential_target) {
|
|
|
|
door.open = true;
|
|
|
|
blocks_visibility.remove(potential_target);
|
|
|
|
blocks_movement.remove(potential_target);
|
|
|
|
let glyph = renderables.get_mut(potential_target).unwrap();
|
|
|
|
glyph.glyph = rltk::to_cp437('/');
|
|
|
|
viewshed.dirty = true;
|
|
|
|
|
|
|
|
return Some(RunState::Ticking);
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
});
|
|
|
|
|
|
|
|
if !spatial::is_blocked(destination_idx) {
|
|
|
|
let old_idx = map.xy_idx(pos.x, pos.y);
|
2021-12-17 16:35:30 -05:00
|
|
|
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
|
|
|
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
2022-01-12 10:45:13 -05:00
|
|
|
let new_idx = map.xy_idx(pos.x, pos.y);
|
2021-11-29 16:00:07 -05:00
|
|
|
entity_moved
|
|
|
|
.insert(entity, EntityMoved {})
|
|
|
|
.expect("Failed to add EntityMoved flag to player");
|
2022-01-12 10:45:13 -05:00
|
|
|
spatial::move_entity(entity, old_idx, new_idx);
|
2021-10-26 14:23:08 -04:00
|
|
|
|
2021-10-29 15:15:22 -04:00
|
|
|
viewshed.dirty = true;
|
2021-10-26 15:43:59 -04:00
|
|
|
let mut ppos = ecs.write_resource::<Point>();
|
|
|
|
ppos.x = pos.x;
|
|
|
|
ppos.y = pos.y;
|
2022-01-11 15:35:59 -05:00
|
|
|
result = RunState::Ticking;
|
|
|
|
match map.tiles[destination_idx] {
|
|
|
|
TileType::DownStairs => result = RunState::NextLevel,
|
|
|
|
TileType::UpStairs => result = RunState::PreviousLevel,
|
|
|
|
_ => {}
|
2022-01-06 13:37:23 -05:00
|
|
|
}
|
2021-10-22 10:05:06 -04:00
|
|
|
}
|
|
|
|
}
|
2021-12-24 14:44:48 -05:00
|
|
|
|
|
|
|
for m in swap_entities.iter() {
|
2022-01-12 11:12:59 -05:00
|
|
|
if let Some(their_pos) = positions.get_mut(m.0) {
|
2022-01-12 10:45:13 -05:00
|
|
|
let old_idx = map.xy_idx(their_pos.x, their_pos.y);
|
2021-12-24 14:44:48 -05:00
|
|
|
their_pos.x = m.1;
|
|
|
|
their_pos.y = m.2;
|
2022-01-12 10:45:13 -05:00
|
|
|
let new_idx = map.xy_idx(their_pos.x, their_pos.y);
|
|
|
|
spatial::move_entity(m.0, old_idx, new_idx);
|
|
|
|
|
|
|
|
result = RunState::Ticking;
|
2021-12-24 14:44:48 -05:00
|
|
|
}
|
|
|
|
}
|
2022-01-06 13:37:23 -05:00
|
|
|
|
|
|
|
result
|
2021-10-22 10:05:06 -04:00
|
|
|
}
|
|
|
|
|
2021-11-15 13:55:31 -05:00
|
|
|
pub fn try_next_level(ecs: &mut World) -> bool {
|
|
|
|
let player_pos = ecs.fetch::<Point>();
|
|
|
|
let map = ecs.fetch::<Map>();
|
|
|
|
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
|
|
|
|
|
|
|
if map.tiles[player_idx] == TileType::DownStairs {
|
|
|
|
true
|
|
|
|
} else {
|
2022-02-01 10:39:46 -05:00
|
|
|
gamelog::log_line("There is no way down from here.");
|
2021-11-15 13:55:31 -05:00
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-06 09:13:40 -05:00
|
|
|
pub fn try_previous_level(ecs: &mut World) -> bool {
|
|
|
|
let player_pos = ecs.fetch::<Point>();
|
|
|
|
let map = ecs.fetch::<Map>();
|
|
|
|
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
|
|
|
|
|
|
|
if map.tiles[player_idx] == TileType::UpStairs {
|
|
|
|
true
|
|
|
|
} else {
|
2022-02-01 10:39:46 -05:00
|
|
|
gamelog::log_line("There is no way up from here.");
|
2022-01-06 09:13:40 -05:00
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 13:55:31 -05:00
|
|
|
fn get_item(ecs: &mut World) {
|
|
|
|
let player_pos = ecs.fetch::<Point>();
|
|
|
|
let player_entity = ecs.fetch::<Entity>();
|
|
|
|
let entities = ecs.entities();
|
|
|
|
let items = ecs.read_storage::<Item>();
|
|
|
|
let positions = ecs.read_storage::<Position>();
|
|
|
|
|
|
|
|
let mut target_item: Option<Entity> = None;
|
|
|
|
for (item_entity, _item, position) in (&entities, &items, &positions).join() {
|
|
|
|
if position.x == player_pos.x && position.y == player_pos.y {
|
|
|
|
target_item = Some(item_entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match target_item {
|
2022-02-01 10:39:46 -05:00
|
|
|
None => gamelog::log_line("There is nothing here to pick up."),
|
2021-11-15 13:55:31 -05:00
|
|
|
Some(item) => {
|
|
|
|
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
|
|
|
pickup
|
|
|
|
.insert(
|
|
|
|
*player_entity,
|
|
|
|
WantsToPickupItem {
|
|
|
|
collected_by: *player_entity,
|
|
|
|
item,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.expect("Unable to pick up item?!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn skip_turn(ecs: &mut World) -> RunState {
|
|
|
|
let player_entity = ecs.fetch::<Entity>();
|
|
|
|
let viewshed_components = ecs.read_storage::<Viewshed>();
|
2022-01-11 15:35:59 -05:00
|
|
|
let factions = ecs.read_storage::<Faction>();
|
2021-11-15 13:55:31 -05:00
|
|
|
|
|
|
|
let worldmap_resource = ecs.fetch::<Map>();
|
|
|
|
|
|
|
|
let mut can_heal = true;
|
|
|
|
let viewshed = viewshed_components.get(*player_entity).unwrap();
|
|
|
|
for tile in viewshed.visible_tiles.iter() {
|
|
|
|
let idx = worldmap_resource.xy_idx(tile.x, tile.y);
|
2022-01-12 10:45:13 -05:00
|
|
|
spatial::for_each_tile_content(idx, |entity_id| match factions.get(entity_id) {
|
|
|
|
None => {}
|
|
|
|
Some(faction) => {
|
|
|
|
let reaction =
|
|
|
|
raws::faction_reaction(&faction.name, "Player", &RAWS.lock().unwrap());
|
|
|
|
if reaction == Reaction::Attack {
|
|
|
|
can_heal = false;
|
2021-11-15 13:55:31 -05:00
|
|
|
}
|
|
|
|
}
|
2022-01-12 10:45:13 -05:00
|
|
|
});
|
2021-11-15 13:55:31 -05:00
|
|
|
}
|
|
|
|
|
2021-11-19 11:31:27 -05:00
|
|
|
// Don't allow healing when hungry or starving
|
|
|
|
let hunger_clocks = ecs.read_storage::<HungerClock>();
|
|
|
|
if let Some(hc) = hunger_clocks.get(*player_entity) {
|
|
|
|
match hc.state {
|
|
|
|
HungerState::Hungry => can_heal = false,
|
|
|
|
HungerState::Starving => can_heal = false,
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 13:55:31 -05:00
|
|
|
if can_heal {
|
2022-01-03 16:30:14 -05:00
|
|
|
let mut health_components = ecs.write_storage::<Pools>();
|
|
|
|
let pools = health_components.get_mut(*player_entity).unwrap();
|
|
|
|
pools.hit_points.current = i32::min(pools.hit_points.current + 1, pools.hit_points.max);
|
2022-01-25 11:42:02 -05:00
|
|
|
|
2022-02-03 14:59:35 -05:00
|
|
|
if roll_dice(1, 6) == 1 {
|
2022-01-25 11:42:02 -05:00
|
|
|
pools.mana.current = i32::min(pools.mana.current + 1, pools.mana.max);
|
|
|
|
}
|
2021-11-15 13:55:31 -05:00
|
|
|
}
|
|
|
|
|
2022-01-11 10:01:37 -05:00
|
|
|
RunState::Ticking
|
2021-11-15 13:55:31 -05:00
|
|
|
}
|
|
|
|
|
2022-01-04 13:54:57 -05:00
|
|
|
fn use_consumable_hotkey(gs: &mut State, key: i32) -> RunState {
|
|
|
|
use crate::components::{Consumable, InBackpack, Ranged, WantsToUseItem};
|
|
|
|
|
|
|
|
let consumables = gs.ecs.read_storage::<Consumable>();
|
|
|
|
let backpack = gs.ecs.read_storage::<InBackpack>();
|
|
|
|
let player_entity = gs.ecs.fetch::<Entity>();
|
|
|
|
let entities = gs.ecs.entities();
|
|
|
|
let mut carried_consumables = Vec::new();
|
|
|
|
|
|
|
|
for (entity, carried_by, _consumable) in (&entities, &backpack, &consumables).join() {
|
|
|
|
if carried_by.owner == *player_entity {
|
|
|
|
carried_consumables.push(entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key as usize) < carried_consumables.len() {
|
|
|
|
if let Some(ranged) = gs
|
|
|
|
.ecs
|
|
|
|
.read_storage::<Ranged>()
|
|
|
|
.get(carried_consumables[key as usize])
|
|
|
|
{
|
|
|
|
return RunState::ShowTargeting {
|
|
|
|
range: ranged.range,
|
|
|
|
item: carried_consumables[key as usize],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut intent = gs.ecs.write_storage::<WantsToUseItem>();
|
|
|
|
intent
|
|
|
|
.insert(
|
|
|
|
*player_entity,
|
|
|
|
WantsToUseItem {
|
|
|
|
item: carried_consumables[key as usize],
|
|
|
|
target: None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.expect("Unable to insert intent to use item.");
|
|
|
|
|
2022-01-11 10:01:37 -05:00
|
|
|
return RunState::Ticking;
|
2022-01-04 13:54:57 -05:00
|
|
|
}
|
|
|
|
|
2022-01-11 10:01:37 -05:00
|
|
|
RunState::Ticking
|
2022-01-04 13:54:57 -05:00
|
|
|
}
|
|
|
|
|
2022-01-25 09:58:30 -05:00
|
|
|
fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState {
|
|
|
|
use crate::components::{KnownSpells, Ranged};
|
|
|
|
use crate::raws::find_spell_entity;
|
|
|
|
|
|
|
|
let player_entity = gs.ecs.fetch::<Entity>();
|
|
|
|
let known_spells_storage = gs.ecs.read_storage::<KnownSpells>();
|
|
|
|
let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells;
|
|
|
|
|
|
|
|
if (key as usize) < known_spells.len() {
|
|
|
|
let pools = gs.ecs.read_storage::<Pools>();
|
|
|
|
let player_pools = pools.get(*player_entity).unwrap();
|
|
|
|
if player_pools.mana.current >= known_spells[key as usize].mana_cost {
|
|
|
|
if let Some(spell_entity) =
|
|
|
|
find_spell_entity(&gs.ecs, &known_spells[key as usize].display_name)
|
|
|
|
{
|
|
|
|
if let Some(ranged) = gs.ecs.read_storage::<Ranged>().get(spell_entity) {
|
|
|
|
return RunState::ShowTargeting {
|
|
|
|
range: ranged.range,
|
|
|
|
item: spell_entity,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut intent = gs.ecs.write_storage::<WantsToCastSpell>();
|
|
|
|
intent
|
|
|
|
.insert(
|
|
|
|
*player_entity,
|
|
|
|
WantsToCastSpell {
|
|
|
|
spell: spell_entity,
|
|
|
|
target: None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.expect("Unable to insert intent to cast spell");
|
|
|
|
|
|
|
|
return RunState::Ticking;
|
|
|
|
}
|
|
|
|
} else {
|
2022-02-01 10:39:46 -05:00
|
|
|
gamelog::log_line("You don't have enough mana to cast that!");
|
2022-01-25 09:58:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RunState::Ticking
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:15:32 -05:00
|
|
|
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|
|
|
// Hotkeys
|
|
|
|
if (ctx.shift || ctx.control) && ctx.key.is_some() {
|
|
|
|
let key: Option<i32> = match ctx.key.unwrap() {
|
2022-01-04 13:54:57 -05:00
|
|
|
VirtualKeyCode::Key1 => Some(1),
|
|
|
|
VirtualKeyCode::Key2 => Some(2),
|
|
|
|
VirtualKeyCode::Key3 => Some(3),
|
|
|
|
VirtualKeyCode::Key4 => Some(4),
|
|
|
|
VirtualKeyCode::Key5 => Some(5),
|
|
|
|
VirtualKeyCode::Key6 => Some(6),
|
|
|
|
VirtualKeyCode::Key7 => Some(7),
|
|
|
|
VirtualKeyCode::Key8 => Some(8),
|
|
|
|
VirtualKeyCode::Key9 => Some(9),
|
|
|
|
_ => None,
|
2022-01-25 11:15:32 -05:00
|
|
|
};
|
2022-01-25 09:58:30 -05:00
|
|
|
|
2022-01-04 13:54:57 -05:00
|
|
|
if let Some(key) = key {
|
2022-01-25 11:15:32 -05:00
|
|
|
if ctx.shift {
|
|
|
|
return use_consumable_hotkey(gs, key - 1);
|
|
|
|
} else if ctx.control {
|
|
|
|
return use_spell_hotkey(gs, key - 1);
|
|
|
|
}
|
2022-01-25 09:58:30 -05:00
|
|
|
}
|
|
|
|
}
|
2022-01-04 13:54:57 -05:00
|
|
|
|
2021-11-18 10:32:50 -05:00
|
|
|
// Mac OS is special when it comes to the numpad. Instead of reporting
|
|
|
|
// the keys as Numpad-specific numbers, it reports the number row scan
|
|
|
|
// codes. The quick fix is to match on both types of number scan codes.
|
2021-10-22 10:05:06 -04:00
|
|
|
match ctx.key {
|
2021-10-29 15:15:22 -04:00
|
|
|
None => return RunState::AwaitingInput, // Nothing happened
|
2021-10-22 10:05:06 -04:00
|
|
|
Some(key) => match key {
|
2021-11-03 15:11:19 -04:00
|
|
|
// Cardinal directions
|
2021-10-29 11:11:17 -04:00
|
|
|
VirtualKeyCode::Up
|
|
|
|
| VirtualKeyCode::Numpad8
|
2021-11-18 10:32:50 -05:00
|
|
|
| VirtualKeyCode::Key8
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::K => return try_move_player(0, -1, &mut gs.ecs),
|
2021-10-29 11:11:17 -04:00
|
|
|
|
|
|
|
VirtualKeyCode::Down
|
|
|
|
| VirtualKeyCode::Numpad2
|
2021-11-18 10:32:50 -05:00
|
|
|
| VirtualKeyCode::Key2
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::J => return try_move_player(0, 1, &mut gs.ecs),
|
2021-10-22 14:50:04 -04:00
|
|
|
|
2021-11-18 10:56:51 -05:00
|
|
|
VirtualKeyCode::Left
|
|
|
|
| VirtualKeyCode::Numpad4
|
|
|
|
| VirtualKeyCode::Key4
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::H => return try_move_player(-1, 0, &mut gs.ecs),
|
2021-11-18 10:48:14 -05:00
|
|
|
|
2021-11-18 10:56:51 -05:00
|
|
|
VirtualKeyCode::Right
|
|
|
|
| VirtualKeyCode::Numpad6
|
|
|
|
| VirtualKeyCode::Key6
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::L => return try_move_player(1, 0, &mut gs.ecs),
|
2021-11-18 10:56:51 -05:00
|
|
|
|
|
|
|
// Diagonals
|
2021-11-18 10:48:14 -05:00
|
|
|
VirtualKeyCode::Numpad7
|
2022-01-12 11:15:11 -05:00
|
|
|
| VirtualKeyCode::Home
|
2021-11-18 10:48:14 -05:00
|
|
|
| VirtualKeyCode::Key7
|
|
|
|
| VirtualKeyCode::U
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::Q => return try_move_player(-1, -1, &mut gs.ecs),
|
2021-11-18 10:48:14 -05:00
|
|
|
|
2021-11-18 10:56:51 -05:00
|
|
|
VirtualKeyCode::Numpad9
|
2022-01-12 11:15:11 -05:00
|
|
|
| VirtualKeyCode::PageUp
|
2021-11-18 10:56:51 -05:00
|
|
|
| VirtualKeyCode::Key9
|
|
|
|
| VirtualKeyCode::Y
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::W => return try_move_player(1, -1, &mut gs.ecs),
|
2021-11-18 10:48:14 -05:00
|
|
|
|
|
|
|
VirtualKeyCode::Numpad1
|
2022-01-12 11:15:11 -05:00
|
|
|
| VirtualKeyCode::End
|
2021-11-18 10:48:14 -05:00
|
|
|
| VirtualKeyCode::Key1
|
|
|
|
| VirtualKeyCode::B
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::A => return try_move_player(-1, 1, &mut gs.ecs),
|
2021-10-22 14:50:04 -04:00
|
|
|
|
2021-11-18 10:56:51 -05:00
|
|
|
VirtualKeyCode::Numpad3
|
2022-01-12 11:15:11 -05:00
|
|
|
| VirtualKeyCode::PageDown
|
2021-11-18 10:56:51 -05:00
|
|
|
| VirtualKeyCode::Key3
|
|
|
|
| VirtualKeyCode::N
|
2022-01-06 13:37:23 -05:00
|
|
|
| VirtualKeyCode::S => return try_move_player(1, 1, &mut gs.ecs),
|
2021-11-18 10:56:51 -05:00
|
|
|
|
2021-11-16 11:33:58 -05:00
|
|
|
// Skip Turn
|
2021-11-18 10:48:14 -05:00
|
|
|
VirtualKeyCode::Numpad5 | VirtualKeyCode::Key5 | VirtualKeyCode::Space => {
|
|
|
|
return skip_turn(&mut gs.ecs)
|
|
|
|
}
|
2021-11-08 13:58:40 -05:00
|
|
|
|
2021-11-09 15:50:42 -05:00
|
|
|
// Level changes
|
|
|
|
VirtualKeyCode::Period => {
|
|
|
|
if try_next_level(&mut gs.ecs) {
|
|
|
|
return RunState::NextLevel;
|
|
|
|
}
|
|
|
|
}
|
2022-01-06 09:13:40 -05:00
|
|
|
VirtualKeyCode::Comma => {
|
|
|
|
if try_previous_level(&mut gs.ecs) {
|
|
|
|
return RunState::PreviousLevel;
|
|
|
|
}
|
|
|
|
}
|
2021-11-09 15:50:42 -05:00
|
|
|
|
2021-11-16 11:33:58 -05:00
|
|
|
// Item management
|
|
|
|
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
|
|
|
VirtualKeyCode::I => return RunState::ShowInventory,
|
|
|
|
VirtualKeyCode::D => return RunState::ShowDropItem,
|
2021-11-15 11:32:09 -05:00
|
|
|
VirtualKeyCode::R => return RunState::ShowRemoveItem,
|
|
|
|
|
2021-11-16 11:33:58 -05:00
|
|
|
// Save and Quit
|
|
|
|
VirtualKeyCode::Escape => return RunState::SaveGame,
|
|
|
|
|
2022-01-07 09:39:47 -05:00
|
|
|
// Cheating!
|
|
|
|
VirtualKeyCode::Backslash => return RunState::ShowCheatMenu,
|
|
|
|
|
2022-01-31 11:25:36 -05:00
|
|
|
// Ranged
|
|
|
|
VirtualKeyCode::V => {
|
|
|
|
cycle_target(&mut gs.ecs);
|
|
|
|
|
|
|
|
return RunState::AwaitingInput;
|
|
|
|
}
|
2022-01-31 15:00:00 -05:00
|
|
|
VirtualKeyCode::F => return fire_on_target(&mut gs.ecs),
|
2022-01-31 11:25:36 -05:00
|
|
|
|
2021-10-29 15:15:22 -04:00
|
|
|
_ => return RunState::AwaitingInput,
|
2021-10-22 10:05:06 -04:00
|
|
|
},
|
|
|
|
}
|
2021-10-26 15:43:59 -04:00
|
|
|
|
2022-01-11 10:01:37 -05:00
|
|
|
RunState::Ticking
|
2021-10-25 15:26:39 -04:00
|
|
|
}
|