Finish implmentation of ranged weapons, completing section 5.28
This commit is contained in:
parent
89f6470ec0
commit
7e3ceab2a2
21
Makefile
21
Makefile
@ -1,29 +1,28 @@
|
|||||||
run:
|
include help-system.mk
|
||||||
|
|
||||||
|
run: $(call print-help, run, Runs the binary in develop mode)
|
||||||
cargo run
|
cargo run
|
||||||
|
|
||||||
run-pi:
|
run-pi: $(call print-help, run-pi, Sets appropriate flags so that the game runs on a Raspberry Pi)
|
||||||
MESA_GL_VERSION_OVERRIDE=3.0 MESA_GLSL_VERSION_OVERRIDE=330 cargo run
|
MESA_GL_VERSION_OVERRIDE=3.0 MESA_GLSL_VERSION_OVERRIDE=330 cargo run
|
||||||
|
|
||||||
clean:
|
clean: $(call print-help, clean, Removes save file and compilation artifacts)
|
||||||
rm -f savegame.json
|
rm -f savegame.json
|
||||||
cargo clean
|
cargo clean
|
||||||
|
|
||||||
check:
|
check: $(call print-help, check, Check code syntax)
|
||||||
cargo check
|
cargo check
|
||||||
|
|
||||||
lint:
|
lint: $(call print-help, lint, Check code syntax and style)
|
||||||
cargo clippy
|
cargo clippy
|
||||||
|
|
||||||
fmt:
|
fmt: $(call print-help, fmt, Runs formatter on code)
|
||||||
cargo +nightly fmt
|
cargo +nightly fmt
|
||||||
|
|
||||||
fix: fmt
|
fix: fmt
|
||||||
cargo fix --allow-dirty --allow-staged
|
cargo fix --allow-dirty --allow-staged
|
||||||
|
|
||||||
docs:
|
docs: $(call print-help, docs, Generates code docs)
|
||||||
cargo doc
|
cargo doc
|
||||||
|
|
||||||
open-docs:
|
.phony: run-pi clean check run fmt fix lint docs
|
||||||
cargo doc --open
|
|
||||||
|
|
||||||
.phony: run-pi clean check run fmt fix lint docs open-docs
|
|
19
help-system.mk
Normal file
19
help-system.mk
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
help:
|
||||||
|
@echo $(if $(need-help),,Type \'$(MAKE)$(dash-f) help\' to get help)
|
||||||
|
|
||||||
|
need-help := $(filter help,$(MAKECMDGOALS))
|
||||||
|
|
||||||
|
define print-help
|
||||||
|
$(if $(need-help),$(warning $1 -- $2))
|
||||||
|
endef
|
||||||
|
|
||||||
|
define last-element
|
||||||
|
$(word $(words $1),$1)
|
||||||
|
endef
|
||||||
|
|
||||||
|
this-makefile := $(call last-element,$(MAKEFILE_LIST))
|
||||||
|
other-makefiles := $(filter-out $(this-makefile),$(MAKEFILE_LIST))
|
||||||
|
parent-makefile := $(call last-element,$(other-makefiles))
|
||||||
|
|
||||||
|
dash-f := $(if $(filter-out Makefile makefile GNUmakefile,\
|
||||||
|
$(parent-makefile)), -f $(parent-makefile))
|
@ -1,9 +1,9 @@
|
|||||||
use ::rltk::RandomNumberGenerator;
|
use rltk::{console, DistanceAlg, Point, RandomNumberGenerator};
|
||||||
use ::specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Chasing, Faction, MyTurn, Name, Position, SpecialAbilities, SpellTemplate, Viewshed,
|
Chasing, Equipped, Faction, MyTurn, Name, Position, SpecialAbilities, SpellTemplate, Viewshed,
|
||||||
WantsToApproach, WantsToCastSpell, WantsToFlee,
|
WantsToApproach, WantsToCastSpell, WantsToFlee, WantsToShoot, Weapon,
|
||||||
};
|
};
|
||||||
use crate::raws::{self, find_spell_entity_by_name, Reaction, RAWS};
|
use crate::raws::{self, find_spell_entity_by_name, Reaction, RAWS};
|
||||||
use crate::{spatial, Map};
|
use crate::{spatial, Map};
|
||||||
@ -28,10 +28,12 @@ impl<'a> System<'a> for VisibleAI {
|
|||||||
WriteStorage<'a, WantsToCastSpell>,
|
WriteStorage<'a, WantsToCastSpell>,
|
||||||
ReadStorage<'a, Name>,
|
ReadStorage<'a, Name>,
|
||||||
ReadStorage<'a, SpellTemplate>,
|
ReadStorage<'a, SpellTemplate>,
|
||||||
|
ReadStorage<'a, Equipped>,
|
||||||
|
ReadStorage<'a, Weapon>,
|
||||||
|
WriteStorage<'a, WantsToShoot>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
use ::rltk::{DistanceAlg, Point};
|
|
||||||
let (
|
let (
|
||||||
turns,
|
turns,
|
||||||
factions,
|
factions,
|
||||||
@ -48,6 +50,9 @@ impl<'a> System<'a> for VisibleAI {
|
|||||||
mut casting,
|
mut casting,
|
||||||
names,
|
names,
|
||||||
spells,
|
spells,
|
||||||
|
equipped,
|
||||||
|
weapons,
|
||||||
|
mut wants_shoot,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
for (entity, _turn, my_faction, pos, viewshed) in
|
for (entity, _turn, my_faction, pos, viewshed) in
|
||||||
@ -68,14 +73,14 @@ impl<'a> System<'a> for VisibleAI {
|
|||||||
for reaction in reactions.iter() {
|
for reaction in reactions.iter() {
|
||||||
match reaction.1 {
|
match reaction.1 {
|
||||||
Reaction::Attack => {
|
Reaction::Attack => {
|
||||||
|
let range = DistanceAlg::Pythagoras.distance2d(
|
||||||
|
Point::from(*pos),
|
||||||
|
Point::new(
|
||||||
|
reaction.0 as i32 % map.width,
|
||||||
|
reaction.0 as i32 / map.width,
|
||||||
|
),
|
||||||
|
);
|
||||||
if let Some(abilities) = abilities.get(entity) {
|
if let Some(abilities) = abilities.get(entity) {
|
||||||
let range = DistanceAlg::Pythagoras.distance2d(
|
|
||||||
Point::from(*pos),
|
|
||||||
Point::new(
|
|
||||||
reaction.0 as i32 % map.width,
|
|
||||||
reaction.0 as i32 / map.width,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for ability in abilities.abilities.iter() {
|
for ability in abilities.abilities.iter() {
|
||||||
if range >= ability.min_range
|
if range >= ability.min_range
|
||||||
&& range <= ability.range
|
&& range <= ability.range
|
||||||
@ -104,6 +109,29 @@ impl<'a> System<'a> for VisibleAI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !done {
|
||||||
|
for (weapon, equip) in (&weapons, &equipped).join() {
|
||||||
|
if let Some(wrange) = weapon.range {
|
||||||
|
if equip.owner == entity {
|
||||||
|
console::log(format!(
|
||||||
|
"Owner found. Ranges: {}/{}",
|
||||||
|
wrange, range
|
||||||
|
));
|
||||||
|
if wrange >= range as i32 {
|
||||||
|
console::log("Inserting shoot");
|
||||||
|
wants_shoot
|
||||||
|
.insert(
|
||||||
|
entity,
|
||||||
|
WantsToShoot { target: reaction.2 },
|
||||||
|
)
|
||||||
|
.expect("Unable to insert intent to shoot");
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !done {
|
if !done {
|
||||||
want_approach
|
want_approach
|
||||||
.insert(
|
.insert(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Handle rendering of the viewport
|
//! Handle rendering of the viewport
|
||||||
use ::rltk::{Point, Rltk};
|
use rltk::{Point, Rltk};
|
||||||
use ::specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Hidden, Position, Renderable, Target, TileSize};
|
use crate::components::{Hidden, Position, Renderable, Target, TileSize};
|
||||||
use crate::map::tile_glyph;
|
use crate::map::tile_glyph;
|
||||||
@ -104,8 +104,8 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
|||||||
&& entity_screen_y < map_height
|
&& entity_screen_y < map_height
|
||||||
{
|
{
|
||||||
ctx.set(
|
ctx.set(
|
||||||
entity_screen_x + 1,
|
entity_screen_x,
|
||||||
entity_screen_y + 1,
|
entity_screen_y,
|
||||||
render.fg,
|
render.fg,
|
||||||
render.bg,
|
render.bg,
|
||||||
render.glyph,
|
render.glyph,
|
||||||
@ -118,15 +118,15 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
|||||||
let entity_screen_x = pos.x - min_x;
|
let entity_screen_x = pos.x - min_x;
|
||||||
let entity_screen_y = pos.y - min_y;
|
let entity_screen_y = pos.y - min_y;
|
||||||
ctx.set(
|
ctx.set(
|
||||||
entity_screen_x,
|
entity_screen_x - 1,
|
||||||
entity_screen_y + 1,
|
entity_screen_y,
|
||||||
colors::RED,
|
colors::RED,
|
||||||
colors::YELLOW,
|
colors::YELLOW,
|
||||||
::rltk::to_cp437('['),
|
::rltk::to_cp437('['),
|
||||||
);
|
);
|
||||||
ctx.set(
|
ctx.set(
|
||||||
entity_screen_x + 2,
|
entity_screen_x + 1,
|
||||||
entity_screen_y + 1,
|
entity_screen_y,
|
||||||
colors::RED,
|
colors::RED,
|
||||||
colors::YELLOW,
|
colors::YELLOW,
|
||||||
::rltk::to_cp437(']'),
|
::rltk::to_cp437(']'),
|
||||||
|
208
src/player.rs
208
src/player.rs
@ -1,17 +1,131 @@
|
|||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
use ::rltk::{DistanceAlg, Point, Rltk, VirtualKeyCode};
|
use rltk::{DistanceAlg, Point, Rltk, VirtualKeyCode};
|
||||||
use ::specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved, Equipped, Faction, HungerClock,
|
Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved, Equipped, Faction, HungerClock,
|
||||||
HungerState, Item, Player, Pools, Position, Renderable, Target, Vendor, Viewshed, WantsToMelee,
|
HungerState, Item, Name, Player, Pools, Position, Renderable, Target, Vendor, Viewshed,
|
||||||
WantsToPickupItem,
|
WantsToMelee, WantsToPickupItem, WantsToShoot,
|
||||||
};
|
};
|
||||||
use crate::game_log::GameLog;
|
use crate::game_log::GameLog;
|
||||||
use crate::raws::{self, Reaction, RAWS};
|
use crate::raws::{self, Reaction, RAWS};
|
||||||
use crate::{spatial, Map, RunState, State, TileType, VendorMode, WantsToCastSpell, Weapon};
|
use crate::{spatial, Map, RunState, State, TileType, VendorMode, WantsToCastSpell, Weapon};
|
||||||
|
|
||||||
|
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;
|
||||||
|
let mut log = ecs.fetch_mut::<GameLog>();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
log.entries.push(format!("You fire at {}", name.name));
|
||||||
|
}
|
||||||
|
shoot_store
|
||||||
|
.insert(*player_entity, WantsToShoot { target })
|
||||||
|
.expect("Insert Fail");
|
||||||
|
|
||||||
|
RunState::Ticking
|
||||||
|
} else {
|
||||||
|
log.entries
|
||||||
|
.push("You don't have a target selected!".to_string());
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
|
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
|
||||||
let mut positions = ecs.write_storage::<Position>();
|
let mut positions = ecs.write_storage::<Position>();
|
||||||
let players = ecs.read_storage::<Player>();
|
let players = ecs.read_storage::<Player>();
|
||||||
@ -348,91 +462,6 @@ fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState {
|
|||||||
RunState::Ticking
|
RunState::Ticking
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
// Hotkeys
|
// Hotkeys
|
||||||
if (ctx.shift || ctx.control) && ctx.key.is_some() {
|
if (ctx.shift || ctx.control) && ctx.key.is_some() {
|
||||||
@ -545,6 +574,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||||||
|
|
||||||
return RunState::AwaitingInput;
|
return RunState::AwaitingInput;
|
||||||
}
|
}
|
||||||
|
VirtualKeyCode::F => return fire_on_target(&mut gs.ecs),
|
||||||
|
|
||||||
_ => return RunState::AwaitingInput,
|
_ => return RunState::AwaitingInput,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user