Finish implmentation of ranged weapons, completing section 5.28

This commit is contained in:
Timothy Warren 2022-01-31 15:00:00 -05:00
parent 89f6470ec0
commit 7e3ceab2a2
5 changed files with 196 additions and 120 deletions

View File

@ -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
View 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))

View File

@ -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,14 +73,14 @@ impl<'a> System<'a> for VisibleAI {
for reaction in reactions.iter() {
match reaction.1 {
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) {
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() {
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(

View File

@ -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(']'),

View File

@ -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,
},