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
|
||||
|
||||
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
|
||||
|
||||
clean:
|
||||
clean: $(call print-help, clean, Removes save file and compilation artifacts)
|
||||
rm -f savegame.json
|
||||
cargo clean
|
||||
|
||||
check:
|
||||
check: $(call print-help, check, Check code syntax)
|
||||
cargo check
|
||||
|
||||
lint:
|
||||
lint: $(call print-help, lint, Check code syntax and style)
|
||||
cargo clippy
|
||||
|
||||
fmt:
|
||||
fmt: $(call print-help, fmt, Runs formatter on code)
|
||||
cargo +nightly fmt
|
||||
|
||||
fix: fmt
|
||||
cargo fix --allow-dirty --allow-staged
|
||||
|
||||
docs:
|
||||
docs: $(call print-help, docs, Generates code docs)
|
||||
cargo doc
|
||||
|
||||
open-docs:
|
||||
cargo doc --open
|
||||
|
||||
.phony: run-pi clean check run fmt fix lint docs open-docs
|
||||
.phony: run-pi clean check run fmt fix lint 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 ::specs::prelude::*;
|
||||
use rltk::{console, DistanceAlg, Point, RandomNumberGenerator};
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::components::{
|
||||
Chasing, Faction, MyTurn, Name, Position, SpecialAbilities, SpellTemplate, Viewshed,
|
||||
WantsToApproach, WantsToCastSpell, WantsToFlee,
|
||||
Chasing, Equipped, Faction, MyTurn, Name, Position, SpecialAbilities, SpellTemplate, Viewshed,
|
||||
WantsToApproach, WantsToCastSpell, WantsToFlee, WantsToShoot, Weapon,
|
||||
};
|
||||
use crate::raws::{self, find_spell_entity_by_name, Reaction, RAWS};
|
||||
use crate::{spatial, Map};
|
||||
@ -28,10 +28,12 @@ impl<'a> System<'a> for VisibleAI {
|
||||
WriteStorage<'a, WantsToCastSpell>,
|
||||
ReadStorage<'a, Name>,
|
||||
ReadStorage<'a, SpellTemplate>,
|
||||
ReadStorage<'a, Equipped>,
|
||||
ReadStorage<'a, Weapon>,
|
||||
WriteStorage<'a, WantsToShoot>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
use ::rltk::{DistanceAlg, Point};
|
||||
let (
|
||||
turns,
|
||||
factions,
|
||||
@ -48,6 +50,9 @@ impl<'a> System<'a> for VisibleAI {
|
||||
mut casting,
|
||||
names,
|
||||
spells,
|
||||
equipped,
|
||||
weapons,
|
||||
mut wants_shoot,
|
||||
) = data;
|
||||
|
||||
for (entity, _turn, my_faction, pos, viewshed) in
|
||||
@ -68,7 +73,6 @@ impl<'a> System<'a> for VisibleAI {
|
||||
for reaction in reactions.iter() {
|
||||
match reaction.1 {
|
||||
Reaction::Attack => {
|
||||
if let Some(abilities) = abilities.get(entity) {
|
||||
let range = DistanceAlg::Pythagoras.distance2d(
|
||||
Point::from(*pos),
|
||||
Point::new(
|
||||
@ -76,6 +80,7 @@ impl<'a> System<'a> for VisibleAI {
|
||||
reaction.0 as i32 / map.width,
|
||||
),
|
||||
);
|
||||
if let Some(abilities) = abilities.get(entity) {
|
||||
for ability in abilities.abilities.iter() {
|
||||
if range >= ability.min_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 {
|
||||
want_approach
|
||||
.insert(
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Handle rendering of the viewport
|
||||
use ::rltk::{Point, Rltk};
|
||||
use ::specs::prelude::*;
|
||||
use rltk::{Point, Rltk};
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::components::{Hidden, Position, Renderable, Target, TileSize};
|
||||
use crate::map::tile_glyph;
|
||||
@ -104,8 +104,8 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
||||
&& entity_screen_y < map_height
|
||||
{
|
||||
ctx.set(
|
||||
entity_screen_x + 1,
|
||||
entity_screen_y + 1,
|
||||
entity_screen_x,
|
||||
entity_screen_y,
|
||||
render.fg,
|
||||
render.bg,
|
||||
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_y = pos.y - min_y;
|
||||
ctx.set(
|
||||
entity_screen_x,
|
||||
entity_screen_y + 1,
|
||||
entity_screen_x - 1,
|
||||
entity_screen_y,
|
||||
colors::RED,
|
||||
colors::YELLOW,
|
||||
::rltk::to_cp437('['),
|
||||
);
|
||||
ctx.set(
|
||||
entity_screen_x + 2,
|
||||
entity_screen_y + 1,
|
||||
entity_screen_x + 1,
|
||||
entity_screen_y,
|
||||
colors::RED,
|
||||
colors::YELLOW,
|
||||
::rltk::to_cp437(']'),
|
||||
|
208
src/player.rs
208
src/player.rs
@ -1,17 +1,131 @@
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use ::rltk::{DistanceAlg, Point, Rltk, VirtualKeyCode};
|
||||
use ::specs::prelude::*;
|
||||
use rltk::{DistanceAlg, Point, Rltk, VirtualKeyCode};
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::components::{
|
||||
Attributes, BlocksTile, BlocksVisibility, Door, EntityMoved, Equipped, Faction, HungerClock,
|
||||
HungerState, Item, Player, Pools, Position, Renderable, Target, Vendor, Viewshed, WantsToMelee,
|
||||
WantsToPickupItem,
|
||||
HungerState, Item, Name, Player, Pools, Position, Renderable, Target, Vendor, Viewshed,
|
||||
WantsToMelee, WantsToPickupItem, WantsToShoot,
|
||||
};
|
||||
use crate::game_log::GameLog;
|
||||
use crate::raws::{self, Reaction, RAWS};
|
||||
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 {
|
||||
let mut positions = ecs.write_storage::<Position>();
|
||||
let players = ecs.read_storage::<Player>();
|
||||
@ -348,91 +462,6 @@ fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState {
|
||||
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 {
|
||||
// Hotkeys
|
||||
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;
|
||||
}
|
||||
VirtualKeyCode::F => return fire_on_target(&mut gs.ecs),
|
||||
|
||||
_ => return RunState::AwaitingInput,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user