Added chapter 2.9
This commit is contained in:
parent
bda34abfa3
commit
3caf253441
@ -1,4 +1,4 @@
|
|||||||
use rltk::RGB;
|
use rltk::{Point, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use specs_derive::*;
|
use specs_derive::*;
|
||||||
|
|
||||||
@ -88,8 +88,9 @@ pub struct WantsToPickupItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone)]
|
#[derive(Component, Debug, Clone)]
|
||||||
pub struct WantsToDrinkPotion {
|
pub struct WantsToUseItem {
|
||||||
pub potion: Entity,
|
pub item: Entity,
|
||||||
|
pub target: Option<Point>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone)]
|
#[derive(Component, Debug, Clone)]
|
||||||
@ -97,4 +98,30 @@ pub struct WantsToDropItem {
|
|||||||
pub item: Entity,
|
pub item: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct Consumable {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct ProvidesHealing {
|
||||||
|
pub heal_amount: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct Ranged {
|
||||||
|
pub range: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct InflictsDamage {
|
||||||
|
pub damage: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct AreaOfEffect {
|
||||||
|
pub radius: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct Confusion {
|
||||||
|
pub turns: i32,
|
||||||
|
}
|
||||||
|
146
src/gui.rs
146
src/gui.rs
@ -1,14 +1,14 @@
|
|||||||
use rltk::{BTerm, Point, VirtualKeyCode};
|
|
||||||
use rltk::RGB;
|
|
||||||
use rltk::Rltk;
|
use rltk::Rltk;
|
||||||
|
use rltk::RGB;
|
||||||
|
use rltk::{BTerm, Point, VirtualKeyCode};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::{CombatStats, InBackpack, State};
|
|
||||||
use crate::gamelog::GameLog;
|
use crate::gamelog::GameLog;
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
use crate::Name;
|
use crate::Name;
|
||||||
use crate::Player;
|
use crate::Player;
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
|
use crate::{CombatStats, InBackpack, State, Viewshed};
|
||||||
|
|
||||||
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||||
ctx.draw_box(
|
ctx.draw_box(
|
||||||
@ -230,18 +230,19 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||||||
|
|
||||||
match ctx.key {
|
match ctx.key {
|
||||||
None => (ItemMenuResult::NoResponse, None),
|
None => (ItemMenuResult::NoResponse, None),
|
||||||
Some(key) => {
|
Some(key) => match key {
|
||||||
match key {
|
VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None),
|
||||||
VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None) }
|
|
||||||
_ => {
|
_ => {
|
||||||
let selection = rltk::letter_to_option(key);
|
let selection = rltk::letter_to_option(key);
|
||||||
if selection > -1 && selection < count as i32 {
|
if selection > -1 && selection < count as i32 {
|
||||||
return (ItemMenuResult::Selected, Some(equippable[selection as usize]));
|
return (
|
||||||
|
ItemMenuResult::Selected,
|
||||||
|
Some(equippable[selection as usize]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(ItemMenuResult::NoResponse, None)
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,20 +252,62 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||||||
let backpack = gs.ecs.read_storage::<InBackpack>();
|
let backpack = gs.ecs.read_storage::<InBackpack>();
|
||||||
let entities = gs.ecs.entities();
|
let entities = gs.ecs.entities();
|
||||||
|
|
||||||
let inventory = (&backpack, &names).join().filter(|item| item.0.owner == *player_entity);
|
let inventory = (&backpack, &names)
|
||||||
|
.join()
|
||||||
|
.filter(|item| item.0.owner == *player_entity);
|
||||||
let count = inventory.count();
|
let count = inventory.count();
|
||||||
|
|
||||||
let mut y = (25 - (count / 2)) as i32;
|
let mut y = (25 - (count / 2)) as i32;
|
||||||
ctx.draw_box(15, y - 2, 31, (count + 3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK));
|
ctx.draw_box(
|
||||||
ctx.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Drop Which Item?");
|
15,
|
||||||
ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESCAPE to cancel");
|
y - 2,
|
||||||
|
31,
|
||||||
|
(count + 3) as i32,
|
||||||
|
RGB::named(rltk::WHITE),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
);
|
||||||
|
ctx.print_color(
|
||||||
|
18,
|
||||||
|
y - 2,
|
||||||
|
RGB::named(rltk::YELLOW),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
"Drop Which Item?",
|
||||||
|
);
|
||||||
|
ctx.print_color(
|
||||||
|
18,
|
||||||
|
y + count as i32 + 1,
|
||||||
|
RGB::named(rltk::YELLOW),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
"ESCAPE to cancel",
|
||||||
|
);
|
||||||
|
|
||||||
let mut equippable: Vec<Entity> = Vec::new();
|
let mut equippable: Vec<Entity> = Vec::new();
|
||||||
let mut j = 0;
|
let mut j = 0;
|
||||||
for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) {
|
for (entity, _pack, name) in (&entities, &backpack, &names)
|
||||||
ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437('('));
|
.join()
|
||||||
ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
|
.filter(|item| item.1.owner == *player_entity)
|
||||||
ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437(')'));
|
{
|
||||||
|
ctx.set(
|
||||||
|
17,
|
||||||
|
y,
|
||||||
|
RGB::named(rltk::WHITE),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
rltk::to_cp437('('),
|
||||||
|
);
|
||||||
|
ctx.set(
|
||||||
|
18,
|
||||||
|
y,
|
||||||
|
RGB::named(rltk::YELLOW),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
97 + j as rltk::FontCharType,
|
||||||
|
);
|
||||||
|
ctx.set(
|
||||||
|
19,
|
||||||
|
y,
|
||||||
|
RGB::named(rltk::WHITE),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
rltk::to_cp437(')'),
|
||||||
|
);
|
||||||
|
|
||||||
ctx.print(21, y, &name.name.to_string());
|
ctx.print(21, y, &name.name.to_string());
|
||||||
equippable.push(entity);
|
equippable.push(entity);
|
||||||
@ -274,17 +317,74 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||||||
|
|
||||||
match ctx.key {
|
match ctx.key {
|
||||||
None => (ItemMenuResult::NoResponse, None),
|
None => (ItemMenuResult::NoResponse, None),
|
||||||
Some(key) => {
|
Some(key) => match key {
|
||||||
match key {
|
VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None),
|
||||||
VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None) }
|
|
||||||
_ => {
|
_ => {
|
||||||
let selection = rltk::letter_to_option(key);
|
let selection = rltk::letter_to_option(key);
|
||||||
if selection > -1 && selection < count as i32 {
|
if selection > -1 && selection < count as i32 {
|
||||||
return (ItemMenuResult::Selected, Some(equippable[selection as usize]));
|
return (
|
||||||
|
ItemMenuResult::Selected,
|
||||||
|
Some(equippable[selection as usize]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(ItemMenuResult::NoResponse, None)
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ranged_target(
|
||||||
|
gs: &mut State,
|
||||||
|
ctx: &mut Rltk,
|
||||||
|
range: i32,
|
||||||
|
) -> (ItemMenuResult, Option<Point>) {
|
||||||
|
let player_entity = gs.ecs.fetch::<Entity>();
|
||||||
|
let player_pos = gs.ecs.fetch::<Point>();
|
||||||
|
let viewshed = gs.ecs.read_storage::<Viewshed>();
|
||||||
|
|
||||||
|
ctx.print_color(
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
RGB::named(rltk::YELLOW),
|
||||||
|
RGB::named(rltk::BLACK),
|
||||||
|
"Select Target:",
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut available_cells = Vec::new();
|
||||||
|
let visible = viewshed.get(*player_entity);
|
||||||
|
if let Some(visible) = visible {
|
||||||
|
for idx in visible.visible_tiles.iter() {
|
||||||
|
let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, *idx);
|
||||||
|
if distance <= range as f32 {
|
||||||
|
ctx.set_bg(idx.x, idx.y, RGB::named(rltk::BLUE));
|
||||||
|
available_cells.push(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (ItemMenuResult::Cancel, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouse_pos = ctx.mouse_pos();
|
||||||
|
let mut valid_target = false;
|
||||||
|
for idx in available_cells.iter() {
|
||||||
|
if idx.x == mouse_pos.0 && idx.y == mouse_pos.1 {
|
||||||
|
valid_target = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if valid_target {
|
||||||
|
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::CYAN));
|
||||||
|
if ctx.left_click {
|
||||||
|
return (
|
||||||
|
ItemMenuResult::Selected,
|
||||||
|
Some(Point::new(mouse_pos.0, mouse_pos.1)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::RED));
|
||||||
|
if ctx.left_click {
|
||||||
|
return (ItemMenuResult::Cancel, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(ItemMenuResult::NoResponse, None)
|
||||||
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use rltk::Rltk;
|
use rltk::Rltk;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::{CombatStats, InBackpack, Name, Position, Potion, State, WantsToDrinkPotion, WantsToDropItem, WantsToPickupItem};
|
use crate::{AreaOfEffect, CombatStats, Confusion, Consumable, InBackpack, InflictsDamage, Map, Name, Position, Potion, ProvidesHealing, Ranged, State, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToUseItem};
|
||||||
use crate::gamelog::GameLog;
|
use crate::gamelog::GameLog;
|
||||||
use crate::gui::ItemMenuResult;
|
use crate::gui::ItemMenuResult;
|
||||||
|
use crate::spawner::{confusion_scroll, player};
|
||||||
|
|
||||||
pub struct ItemCollectionSystem {}
|
pub struct ItemCollectionSystem {}
|
||||||
|
|
||||||
@ -44,37 +45,136 @@ impl<'a> System<'a> for ItemCollectionSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PotionUseSystem {}
|
pub struct ItemUseSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for PotionUseSystem {
|
impl<'a> System<'a> for ItemUseSystem {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadExpect<'a, Entity>,
|
ReadExpect<'a, Entity>,
|
||||||
WriteExpect<'a, GameLog>,
|
WriteExpect<'a, GameLog>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteStorage<'a, WantsToDrinkPotion>,
|
WriteStorage<'a, WantsToUseItem>,
|
||||||
ReadStorage<'a, Name>,
|
ReadStorage<'a, Name>,
|
||||||
ReadStorage<'a, Potion>,
|
ReadStorage<'a, Consumable>,
|
||||||
WriteStorage<'a, CombatStats>
|
WriteStorage<'a, CombatStats>,
|
||||||
|
ReadStorage<'a, ProvidesHealing>,
|
||||||
|
ReadStorage<'a, InflictsDamage>,
|
||||||
|
ReadStorage<'a, Ranged>,
|
||||||
|
ReadExpect<'a, Map>,
|
||||||
|
WriteStorage<'a, SufferDamage>,
|
||||||
|
ReadStorage<'a, AreaOfEffect>,
|
||||||
|
WriteStorage<'a, Confusion>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (player_entity, mut gamelog, entities, mut wants_drink, names, potions, mut combat_stats) = data;
|
let (
|
||||||
|
player_entity,
|
||||||
|
mut game_log,
|
||||||
|
entities,
|
||||||
|
mut wants_use,
|
||||||
|
names,
|
||||||
|
consumables,
|
||||||
|
mut combat_stats,
|
||||||
|
healing,
|
||||||
|
inflicts_damage,
|
||||||
|
ranged,
|
||||||
|
map,
|
||||||
|
mut suffer_damage,
|
||||||
|
aoe,
|
||||||
|
mut confused
|
||||||
|
) = data;
|
||||||
|
|
||||||
for (entity, drink, stats) in (&entities, &wants_drink, &mut combat_stats).join() {
|
for (entity, use_item) in (&entities, &wants_use).join() {
|
||||||
let potion = potions.get(drink.potion);
|
let mut used_item = true;
|
||||||
match potion {
|
|
||||||
None => {}
|
let mut targets: Vec<Entity> = Vec::new();
|
||||||
Some(potion) => {
|
match use_item.target {
|
||||||
stats.hp = i32::min(stats.max_hp, stats.hp + potion.heal_amount);
|
None => { targets.push(*player_entity) }
|
||||||
|
Some(target) => {
|
||||||
|
let area_effect = aoe.get(use_item.item);
|
||||||
|
match area_effect {
|
||||||
|
None => {
|
||||||
|
let idx = map.xy_idx(target.x, target.y);
|
||||||
|
for mob in map.tile_content[idx].iter() {
|
||||||
|
targets.push(*mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(area_effect) => {
|
||||||
|
let mut black_tiles = rltk::field_of_view(target, area_effect.radius, &*map);
|
||||||
|
black_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1);
|
||||||
|
for tile_idx in black_tiles.iter() {
|
||||||
|
let idx = map.xy_idx(tile_idx.x, tile_idx.y);
|
||||||
|
for mob in map.tile_content[idx].iter() {
|
||||||
|
targets.push(*mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item_damages) = inflicts_damage.get(use_item.item) {
|
||||||
|
used_item = false;
|
||||||
|
for mob in targets.iter() {
|
||||||
|
SufferDamage::new_damage(&mut suffer_damage, *mob, item_damages.damage);
|
||||||
if entity == *player_entity {
|
if entity == *player_entity {
|
||||||
gamelog.entries.push(format!("You drink the {}, healing {} hp.", names.get(drink.potion).unwrap().name, potion.heal_amount));
|
let mob_name = names.get(*mob).unwrap();
|
||||||
|
let item_name = names.get(use_item.item).unwrap();
|
||||||
|
game_log.entries.push(format!("You use {} on {}, inflicting {} hp.", item_name.name, mob_name.name, item_damages.damage));
|
||||||
}
|
}
|
||||||
entities.delete(drink.potion).expect("Delete failed");
|
|
||||||
|
used_item = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item_heals) = healing.get(use_item.item) {
|
||||||
|
used_item = false;
|
||||||
|
for target in targets.iter() {
|
||||||
|
let stats = combat_stats.get_mut(*target);
|
||||||
|
if let Some(stats) = stats {
|
||||||
|
stats.hp = i32::min(stats.max_hp, stats.hp + item_heals.heal_amount);
|
||||||
|
if entity == *player_entity {
|
||||||
|
game_log.entries.push(format!(
|
||||||
|
"You drink the {}, healing {} hp.",
|
||||||
|
names.get(use_item.item).unwrap().name,
|
||||||
|
item_heals.heal_amount
|
||||||
|
));
|
||||||
|
}
|
||||||
|
used_item = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wants_drink.clear();
|
let mut add_confusion = Vec::new();
|
||||||
|
{
|
||||||
|
if let Some(caused_confusion) = confused.get(use_item.item) {
|
||||||
|
used_item = false;
|
||||||
|
for mob in targets.iter() {
|
||||||
|
add_confusion.push((*mob, caused_confusion.turns));
|
||||||
|
if entity == *player_entity {
|
||||||
|
let mob_name = names.get(*mob).unwrap();
|
||||||
|
let item_name = names.get(use_item.item).unwrap();
|
||||||
|
game_log.entries.push(format!("You use {} on {}, confusing them.", item_name.name, mob_name.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for mob in add_confusion.iter() {
|
||||||
|
confused.insert(mob.0, Confusion { turns: mob.1 }).expect("Unable to insert status");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if used_item {
|
||||||
|
let consumable = consumables.get(use_item.item);
|
||||||
|
match consumable {
|
||||||
|
None => {}
|
||||||
|
Some(_) => {
|
||||||
|
entities.delete(use_item.item).expect("Delete failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wants_use.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,11 +188,19 @@ impl<'a> System<'a> for ItemDropSystem {
|
|||||||
WriteStorage<'a, WantsToDropItem>,
|
WriteStorage<'a, WantsToDropItem>,
|
||||||
ReadStorage<'a, Name>,
|
ReadStorage<'a, Name>,
|
||||||
WriteStorage<'a, Position>,
|
WriteStorage<'a, Position>,
|
||||||
WriteStorage<'a, InBackpack>
|
WriteStorage<'a, InBackpack>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (player_entity, mut gamelog, entities, mut wants_drop, names, mut positions, mut backpack) = data;
|
let (
|
||||||
|
player_entity,
|
||||||
|
mut game_log,
|
||||||
|
entities,
|
||||||
|
mut wants_drop,
|
||||||
|
names,
|
||||||
|
mut positions,
|
||||||
|
mut backpack,
|
||||||
|
) = data;
|
||||||
|
|
||||||
for (entity, to_drop) in (&entities, &wants_drop).join() {
|
for (entity, to_drop) in (&entities, &wants_drop).join() {
|
||||||
let mut dropper_pos: Position = Position { x: 0, y: 0 };
|
let mut dropper_pos: Position = Position { x: 0, y: 0 };
|
||||||
@ -101,16 +209,25 @@ impl<'a> System<'a> for ItemDropSystem {
|
|||||||
dropper_pos.x = dropped_pos.x;
|
dropper_pos.x = dropped_pos.x;
|
||||||
dropper_pos.y = dropped_pos.y;
|
dropper_pos.y = dropped_pos.y;
|
||||||
}
|
}
|
||||||
positions.insert(to_drop.item, Position { x: dropper_pos.x, y: dropper_pos.y }).expect("Unable to insert position");
|
positions
|
||||||
|
.insert(
|
||||||
|
to_drop.item,
|
||||||
|
Position {
|
||||||
|
x: dropper_pos.x,
|
||||||
|
y: dropper_pos.y,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Unable to insert position");
|
||||||
backpack.remove(to_drop.item);
|
backpack.remove(to_drop.item);
|
||||||
|
|
||||||
if entity == *player_entity {
|
if entity == *player_entity {
|
||||||
gamelog.entries.push(format!("You drop the {}.", names.get(to_drop.item).unwrap().name));
|
game_log.entries.push(format!(
|
||||||
|
"You drop the {}.",
|
||||||
|
names.get(to_drop.item).unwrap().name
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wants_drop.clear();
|
wants_drop.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
51
src/main.rs
51
src/main.rs
@ -12,7 +12,7 @@ use monster_ai_system::*;
|
|||||||
use player::*;
|
use player::*;
|
||||||
use visibility_system::*;
|
use visibility_system::*;
|
||||||
|
|
||||||
use crate::inventory_system::{ItemCollectionSystem, ItemDropSystem, PotionUseSystem};
|
use crate::inventory_system::{ItemCollectionSystem, ItemDropSystem, ItemUseSystem};
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
mod damage_system;
|
mod damage_system;
|
||||||
@ -36,6 +36,7 @@ pub enum RunState {
|
|||||||
MonsterTurn,
|
MonsterTurn,
|
||||||
ShowInventory,
|
ShowInventory,
|
||||||
ShowDropItem,
|
ShowDropItem,
|
||||||
|
ShowTargeting { range: i32, item: Entity },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -62,7 +63,7 @@ impl State {
|
|||||||
let mut inventory = ItemCollectionSystem {};
|
let mut inventory = ItemCollectionSystem {};
|
||||||
inventory.run_now(&self.ecs);
|
inventory.run_now(&self.ecs);
|
||||||
|
|
||||||
let mut potions = PotionUseSystem {};
|
let mut potions = ItemUseSystem {};
|
||||||
potions.run_now(&self.ecs);
|
potions.run_now(&self.ecs);
|
||||||
|
|
||||||
let mut drop_items = ItemDropSystem {};
|
let mut drop_items = ItemDropSystem {};
|
||||||
@ -122,12 +123,26 @@ impl GameState for State {
|
|||||||
gui::ItemMenuResult::NoResponse => {}
|
gui::ItemMenuResult::NoResponse => {}
|
||||||
gui::ItemMenuResult::Selected => {
|
gui::ItemMenuResult::Selected => {
|
||||||
let item_entity = result.1.unwrap();
|
let item_entity = result.1.unwrap();
|
||||||
let mut intent = self.ecs.write_storage::<WantsToDrinkPotion>();
|
let is_ranged = self.ecs.read_storage::<Ranged>();
|
||||||
intent.insert(*self.ecs.fetch::<Entity>(), WantsToDrinkPotion { potion: item_entity }).expect("Unable to insert intent");
|
let is_item_ranged = is_ranged.get(item_entity);
|
||||||
|
if let Some(is_item_ranged) = is_item_ranged {
|
||||||
|
newrunstate = RunState::ShowTargeting {
|
||||||
|
range: is_item_ranged.range,
|
||||||
|
item: item_entity,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||||
|
intent
|
||||||
|
.insert(
|
||||||
|
*self.ecs.fetch::<Entity>(),
|
||||||
|
WantsToUseItem { item: item_entity, target: None },
|
||||||
|
)
|
||||||
|
.expect("Unable to insert intent");
|
||||||
newrunstate = RunState::PlayerTurn;
|
newrunstate = RunState::PlayerTurn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RunState::ShowDropItem => {
|
RunState::ShowDropItem => {
|
||||||
let result = gui::drop_item_menu(self, ctx);
|
let result = gui::drop_item_menu(self, ctx);
|
||||||
match result.0 {
|
match result.0 {
|
||||||
@ -136,7 +151,24 @@ impl GameState for State {
|
|||||||
gui::ItemMenuResult::Selected => {
|
gui::ItemMenuResult::Selected => {
|
||||||
let item_entity = result.1.unwrap();
|
let item_entity = result.1.unwrap();
|
||||||
let mut intent = self.ecs.write_storage::<WantsToDropItem>();
|
let mut intent = self.ecs.write_storage::<WantsToDropItem>();
|
||||||
intent.insert(*self.ecs.fetch::<Entity>(), WantsToDropItem { item: item_entity }).expect("Unable to insert intent");
|
intent
|
||||||
|
.insert(
|
||||||
|
*self.ecs.fetch::<Entity>(),
|
||||||
|
WantsToDropItem { item: item_entity },
|
||||||
|
)
|
||||||
|
.expect("Unable to insert intent");
|
||||||
|
newrunstate = RunState::PlayerTurn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RunState::ShowTargeting { range, item } => {
|
||||||
|
let result = gui::ranged_target(self, ctx, range);
|
||||||
|
match result.0 {
|
||||||
|
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||||
|
gui::ItemMenuResult::NoResponse => {}
|
||||||
|
gui::ItemMenuResult::Selected => {
|
||||||
|
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||||
|
intent.insert(*self.ecs.fetch::<Entity>(), WantsToUseItem { item, target: result.1 }).expect("Unable to insert intent");
|
||||||
newrunstate = RunState::PlayerTurn;
|
newrunstate = RunState::PlayerTurn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,12 +209,17 @@ fn main() -> rltk::BError {
|
|||||||
gs.ecs.register::<CombatStats>();
|
gs.ecs.register::<CombatStats>();
|
||||||
gs.ecs.register::<WantsToMelee>();
|
gs.ecs.register::<WantsToMelee>();
|
||||||
gs.ecs.register::<SufferDamage>();
|
gs.ecs.register::<SufferDamage>();
|
||||||
gs.ecs.register::<Potion>();
|
|
||||||
gs.ecs.register::<Item>();
|
gs.ecs.register::<Item>();
|
||||||
gs.ecs.register::<InBackpack>();
|
gs.ecs.register::<InBackpack>();
|
||||||
gs.ecs.register::<WantsToPickupItem>();
|
gs.ecs.register::<WantsToPickupItem>();
|
||||||
gs.ecs.register::<WantsToDrinkPotion>();
|
gs.ecs.register::<WantsToUseItem>();
|
||||||
gs.ecs.register::<WantsToDropItem>();
|
gs.ecs.register::<WantsToDropItem>();
|
||||||
|
gs.ecs.register::<Consumable>();
|
||||||
|
gs.ecs.register::<ProvidesHealing>();
|
||||||
|
gs.ecs.register::<Ranged>();
|
||||||
|
gs.ecs.register::<InflictsDamage>();
|
||||||
|
gs.ecs.register::<AreaOfEffect>();
|
||||||
|
gs.ecs.register::<Confusion>();
|
||||||
let map = Map::new_map_rooms_and_corridors();
|
let map = Map::new_map_rooms_and_corridors();
|
||||||
|
|
||||||
let (player_x, player_y) = map.rooms[0].center();
|
let (player_x, player_y) = map.rooms[0].center();
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
use rltk::{console, Point};
|
use rltk::{console, Point};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{components::{Monster, Position, Viewshed, WantsToMelee}, Confusion, map::Map, RunState};
|
||||||
components::{Monster, Position, Viewshed, WantsToMelee},
|
|
||||||
map::Map,
|
|
||||||
RunState,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct MonsterAI {}
|
pub struct MonsterAI {}
|
||||||
|
|
||||||
@ -21,6 +17,7 @@ impl<'a> System<'a> for MonsterAI {
|
|||||||
ReadStorage<'a, Monster>,
|
ReadStorage<'a, Monster>,
|
||||||
WriteStorage<'a, Position>,
|
WriteStorage<'a, Position>,
|
||||||
WriteStorage<'a, WantsToMelee>,
|
WriteStorage<'a, WantsToMelee>,
|
||||||
|
WriteStorage<'a, Confusion>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
@ -34,6 +31,7 @@ impl<'a> System<'a> for MonsterAI {
|
|||||||
monster,
|
monster,
|
||||||
mut position,
|
mut position,
|
||||||
mut wants_to_melee,
|
mut wants_to_melee,
|
||||||
|
mut confused,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
if *runstate != RunState::MonsterTurn {
|
if *runstate != RunState::MonsterTurn {
|
||||||
@ -43,6 +41,20 @@ impl<'a> System<'a> for MonsterAI {
|
|||||||
for (entity, mut viewshed, _monster, mut pos) in
|
for (entity, mut viewshed, _monster, mut pos) in
|
||||||
(&entities, &mut viewshed, &monster, &mut position).join()
|
(&entities, &mut viewshed, &monster, &mut position).join()
|
||||||
{
|
{
|
||||||
|
let mut can_act = true;
|
||||||
|
|
||||||
|
if let Some(i_am_confused) = confused.get_mut(entity) {
|
||||||
|
i_am_confused.turns -= 1;
|
||||||
|
if i_am_confused.turns < 1 {
|
||||||
|
confused.remove(entity);
|
||||||
|
}
|
||||||
|
can_act = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !can_act {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let distance =
|
let distance =
|
||||||
rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos);
|
rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos);
|
||||||
if distance < 1.5 {
|
if distance < 1.5 {
|
||||||
|
@ -3,12 +3,12 @@ use std::cmp::{max, min};
|
|||||||
use rltk::{console, Point, Rltk, VirtualKeyCode};
|
use rltk::{console, Point, Rltk, VirtualKeyCode};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
use crate::gamelog::GameLog;
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{CombatStats, Player, Position, Viewshed, WantsToMelee},
|
components::{CombatStats, Player, Position, Viewshed, WantsToMelee},
|
||||||
Item,
|
map::{Map, TileType},
|
||||||
map::{Map, TileType}, RunState, State, WantsToPickupItem,
|
Item, RunState, State, WantsToPickupItem,
|
||||||
};
|
};
|
||||||
use crate::gamelog::GameLog;
|
|
||||||
|
|
||||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
||||||
let mut positions = ecs.write_storage::<Position>();
|
let mut positions = ecs.write_storage::<Position>();
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use rltk::{FontCharType, RandomNumberGenerator, RGB};
|
use rltk::{FontCharType, RandomNumberGenerator, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, InflictsDamage, Item, MAP_WIDTH, MAX_ITEMS, MAX_MONSTER, Monster, Name, Player, Position, Potion, ProvidesHealing, Ranged, Renderable, Viewshed};
|
||||||
BlocksTile, CombatStats, Item, MAP_WIDTH, MAX_ITEMS, MAX_MONSTER, Monster, Name, Player, Position,
|
|
||||||
Potion, Renderable, Viewshed,
|
|
||||||
};
|
|
||||||
use crate::rect::Rect;
|
use crate::rect::Rect;
|
||||||
|
|
||||||
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
||||||
@ -130,7 +127,7 @@ pub fn spawn_room(ecs: &mut World, room: &Rect) {
|
|||||||
for idx in item_spawn_points.iter() {
|
for idx in item_spawn_points.iter() {
|
||||||
let x = *idx % MAP_WIDTH;
|
let x = *idx % MAP_WIDTH;
|
||||||
let y = *idx / MAP_WIDTH;
|
let y = *idx / MAP_WIDTH;
|
||||||
health_potion(ecs, x as i32, y as i32);
|
random_item(ecs, x as i32, y as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +144,80 @@ pub fn health_potion(ecs: &mut World, x: i32, y: i32) {
|
|||||||
name: "Health Potion".to_string(),
|
name: "Health Potion".to_string(),
|
||||||
})
|
})
|
||||||
.with(Item {})
|
.with(Item {})
|
||||||
.with(Potion { heal_amount: 8 })
|
.with(Consumable {})
|
||||||
|
.with(ProvidesHealing { heal_amount: 8 })
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position { x, y })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: rltk::to_cp437(')'),
|
||||||
|
fg: RGB::named(rltk::CYAN),
|
||||||
|
bg: RGB::named(rltk::BLACK),
|
||||||
|
render_order: 2,
|
||||||
|
})
|
||||||
|
.with(Name {
|
||||||
|
name: "Magic Missile Scroll".to_string(),
|
||||||
|
})
|
||||||
|
.with(Item {})
|
||||||
|
.with(Consumable {})
|
||||||
|
.with(Ranged { range: 6 })
|
||||||
|
.with(InflictsDamage { damage: 8 })
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position { x, y })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: rltk::to_cp437(')'),
|
||||||
|
fg: RGB::named(rltk::CYAN),
|
||||||
|
bg: RGB::named(rltk::BLACK),
|
||||||
|
render_order: 2,
|
||||||
|
})
|
||||||
|
.with(Name {
|
||||||
|
name: "Fireball Scroll".to_string(),
|
||||||
|
})
|
||||||
|
.with(Item {})
|
||||||
|
.with(Consumable {})
|
||||||
|
.with(Ranged { range: 6 })
|
||||||
|
.with(InflictsDamage { damage: 20 })
|
||||||
|
.with(AreaOfEffect { radius: 3 })
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position { x, y })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: rltk::to_cp437(')'),
|
||||||
|
fg: RGB::named(rltk::CYAN),
|
||||||
|
bg: RGB::named(rltk::BLACK),
|
||||||
|
render_order: 2,
|
||||||
|
})
|
||||||
|
.with(Name {
|
||||||
|
name: "Confusion Scroll".to_string(),
|
||||||
|
})
|
||||||
|
.with(Item {})
|
||||||
|
.with(Consumable {})
|
||||||
|
.with(Ranged { range: 6 })
|
||||||
|
.with(Confusion { turns: 4 })
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn random_item(ecs: &mut World, x: i32, y: i32) {
|
||||||
|
let roll: i32;
|
||||||
|
{
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
roll = rng.roll_dice(1, 4);
|
||||||
|
}
|
||||||
|
match roll {
|
||||||
|
1 => health_potion(ecs, x, y),
|
||||||
|
2 => fireball_scroll(ecs, x, y),
|
||||||
|
3 => confusion_scroll(ecs, x, y),
|
||||||
|
_ => magic_missile_scroll(ecs, x, y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user