levels, and ui changes
This commit is contained in:
parent
be2c8a35a5
commit
3dab5202f8
15 changed files with 337 additions and 104 deletions
|
|
@ -149,17 +149,22 @@ pub struct WantsToMelee {
|
|||
pub target: Entity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct GrantsXP {
|
||||
pub amount: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct SufferDamage {
|
||||
pub amount: Vec<i32>,
|
||||
pub amount: Vec<(i32, bool)>,
|
||||
}
|
||||
|
||||
impl SufferDamage {
|
||||
pub fn new_damage(store: &mut WriteStorage<SufferDamage>, victim: Entity, amount: i32) {
|
||||
pub fn new_damage(store: &mut WriteStorage<SufferDamage>, victim: Entity, amount: i32, from_player: bool) {
|
||||
if let Some(suffering) = store.get_mut(victim) {
|
||||
suffering.amount.push(amount);
|
||||
suffering.amount.push((amount, from_player));
|
||||
} else {
|
||||
let dmg = SufferDamage { amount: vec![amount] };
|
||||
let dmg = SufferDamage { amount: vec![(amount, from_player)] };
|
||||
store.insert(victim, dmg).expect("Unable to insert damage.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,109 @@
|
|||
use super::{gamelog, Entities, Item, Map, Name, Player, Pools, Position, RunState, SufferDamage};
|
||||
use super::{
|
||||
gamelog, Attributes, GrantsXP, Item, Map, Name, ParticleBuilder, Player, Pools, Position, RunState, SufferDamage,
|
||||
};
|
||||
use crate::gamesystem::{mana_per_level, player_hp_per_level};
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct DamageSystem {}
|
||||
|
||||
impl<'a> System<'a> for DamageSystem {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteStorage<'a, Pools>,
|
||||
WriteStorage<'a, SufferDamage>,
|
||||
WriteExpect<'a, Map>,
|
||||
ReadStorage<'a, Position>,
|
||||
WriteExpect<'a, Map>,
|
||||
Entities<'a>,
|
||||
ReadExpect<'a, Entity>,
|
||||
ReadStorage<'a, Attributes>,
|
||||
WriteExpect<'a, rltk::RandomNumberGenerator>,
|
||||
ReadStorage<'a, GrantsXP>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadExpect<'a, rltk::Point>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (mut stats, mut damage, mut map, positions, entities) = data;
|
||||
let (
|
||||
mut stats,
|
||||
mut damage,
|
||||
positions,
|
||||
mut map,
|
||||
entities,
|
||||
player,
|
||||
attributes,
|
||||
mut rng,
|
||||
xp_granters,
|
||||
mut particle_builder,
|
||||
player_pos,
|
||||
) = data;
|
||||
let mut xp_gain = 0;
|
||||
|
||||
for (entity, mut stats, damage) in (&entities, &mut stats, &damage).join() {
|
||||
stats.hit_points.current -= damage.amount.iter().sum::<i32>();
|
||||
let pos = positions.get(entity);
|
||||
if let Some(pos) = pos {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
map.bloodstains.insert(idx);
|
||||
for dmg in damage.amount.iter() {
|
||||
stats.hit_points.current -= dmg.0;
|
||||
let pos = positions.get(entity);
|
||||
if let Some(pos) = pos {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
map.bloodstains.insert(idx);
|
||||
}
|
||||
|
||||
if stats.hit_points.current < 1 && dmg.1 {
|
||||
let gives_xp = xp_granters.get(entity);
|
||||
if let Some(xp_value) = gives_xp {
|
||||
xp_gain += xp_value.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if xp_gain != 0 {
|
||||
let mut player_stats = stats.get_mut(*player).unwrap();
|
||||
let player_attributes = attributes.get(*player).unwrap();
|
||||
player_stats.xp += xp_gain;
|
||||
rltk::console::log(xp_gain);
|
||||
|
||||
let mut next_level_requirement = -1;
|
||||
if player_stats.level < 10 {
|
||||
next_level_requirement = 20 * 2_i32.pow(player_stats.level as u32 - 1);
|
||||
} else if player_stats.level < 20 {
|
||||
next_level_requirement = 10000 * 2_i32.pow(player_stats.level as u32 - 10);
|
||||
} else if player_stats.level < 30 {
|
||||
next_level_requirement = 10000000 * (player_stats.level - 19);
|
||||
}
|
||||
|
||||
if next_level_requirement != -1 && player_stats.xp >= next_level_requirement {
|
||||
// We've gone up a level!
|
||||
player_stats.level += 1;
|
||||
gamelog::Logger::new()
|
||||
.append("Welcome to experience level")
|
||||
.append(player_stats.level)
|
||||
.append(".")
|
||||
.log();
|
||||
for i in 0..5 {
|
||||
if player_pos.y - i > 1 {
|
||||
particle_builder.request(
|
||||
player_pos.x,
|
||||
player_pos.y - i,
|
||||
rltk::RGB::named(rltk::GOLD),
|
||||
rltk::RGB::named(rltk::BLACK),
|
||||
rltk::to_cp437('*'),
|
||||
200.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
let hp_gained = player_hp_per_level(
|
||||
&mut rng,
|
||||
player_attributes.constitution.base + player_attributes.constitution.modifiers,
|
||||
);
|
||||
player_stats.hit_points.max += hp_gained;
|
||||
player_stats.hit_points.current += hp_gained;
|
||||
|
||||
let mana_gained = mana_per_level(
|
||||
&mut rng,
|
||||
player_attributes.intelligence.base + player_attributes.intelligence.modifiers,
|
||||
);
|
||||
player_stats.mana.max += mana_gained;
|
||||
player_stats.mana.current += mana_gained;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ impl Logger {
|
|||
}
|
||||
|
||||
/// Appends text in RED to the current message logger.
|
||||
#[allow(dead_code)]
|
||||
pub fn damage(mut self, damage: i32) -> Self {
|
||||
self.fragments.push(LogFragment { colour: RGB::named(rltk::RED), text: format!("{} ", damage).to_string() });
|
||||
return self;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub fn player_hp_per_level(rng: &mut rltk::RandomNumberGenerator, constitution:
|
|||
return rng.roll_dice(1, 8) + attr_bonus(constitution);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn player_hp_at_level(rng: &mut rltk::RandomNumberGenerator, constitution: i32, level: i32) -> i32 {
|
||||
let mut total = 10 + attr_bonus(constitution);
|
||||
for _i in 0..level {
|
||||
|
|
|
|||
65
src/gui/letter_to_option.rs
Normal file
65
src/gui/letter_to_option.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use rltk::prelude::*;
|
||||
|
||||
pub fn letter_to_option(key: VirtualKeyCode, shift: bool) -> i32 {
|
||||
if shift {
|
||||
match key {
|
||||
VirtualKeyCode::A => 26,
|
||||
VirtualKeyCode::B => 27,
|
||||
VirtualKeyCode::C => 28,
|
||||
VirtualKeyCode::D => 29,
|
||||
VirtualKeyCode::E => 30,
|
||||
VirtualKeyCode::F => 31,
|
||||
VirtualKeyCode::G => 32,
|
||||
VirtualKeyCode::H => 33,
|
||||
VirtualKeyCode::I => 34,
|
||||
VirtualKeyCode::J => 35,
|
||||
VirtualKeyCode::K => 36,
|
||||
VirtualKeyCode::L => 37,
|
||||
VirtualKeyCode::M => 38,
|
||||
VirtualKeyCode::N => 39,
|
||||
VirtualKeyCode::O => 40,
|
||||
VirtualKeyCode::P => 41,
|
||||
VirtualKeyCode::Q => 42,
|
||||
VirtualKeyCode::R => 43,
|
||||
VirtualKeyCode::S => 44,
|
||||
VirtualKeyCode::T => 45,
|
||||
VirtualKeyCode::U => 46,
|
||||
VirtualKeyCode::V => 47,
|
||||
VirtualKeyCode::W => 48,
|
||||
VirtualKeyCode::X => 49,
|
||||
VirtualKeyCode::Y => 50,
|
||||
VirtualKeyCode::Z => 51,
|
||||
_ => -1,
|
||||
}
|
||||
} else {
|
||||
match key {
|
||||
VirtualKeyCode::A => 0,
|
||||
VirtualKeyCode::B => 1,
|
||||
VirtualKeyCode::C => 2,
|
||||
VirtualKeyCode::D => 3,
|
||||
VirtualKeyCode::E => 4,
|
||||
VirtualKeyCode::F => 5,
|
||||
VirtualKeyCode::G => 6,
|
||||
VirtualKeyCode::H => 7,
|
||||
VirtualKeyCode::I => 8,
|
||||
VirtualKeyCode::J => 9,
|
||||
VirtualKeyCode::K => 10,
|
||||
VirtualKeyCode::L => 11,
|
||||
VirtualKeyCode::M => 12,
|
||||
VirtualKeyCode::N => 13,
|
||||
VirtualKeyCode::O => 14,
|
||||
VirtualKeyCode::P => 15,
|
||||
VirtualKeyCode::Q => 16,
|
||||
VirtualKeyCode::R => 17,
|
||||
VirtualKeyCode::S => 18,
|
||||
VirtualKeyCode::T => 19,
|
||||
VirtualKeyCode::U => 20,
|
||||
VirtualKeyCode::V => 21,
|
||||
VirtualKeyCode::W => 22,
|
||||
VirtualKeyCode::X => 23,
|
||||
VirtualKeyCode::Y => 24,
|
||||
VirtualKeyCode::Z => 25,
|
||||
_ => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
158
src/gui/mod.rs
158
src/gui/mod.rs
|
|
@ -1,11 +1,12 @@
|
|||
use super::{
|
||||
camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, Consumable, Equippable, Equipped,
|
||||
Hidden, HungerClock, HungerState, InBackpack, Map, Name, Player, Point, Pools, Position, Prop, Renderable,
|
||||
RunState, Skill, Skills, State, Viewshed,
|
||||
camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, Equipped, Hidden, HungerClock,
|
||||
HungerState, InBackpack, Map, Name, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills,
|
||||
State, Viewshed,
|
||||
};
|
||||
use rltk::{Rltk, VirtualKeyCode, RGB};
|
||||
use specs::prelude::*;
|
||||
use std::collections::BTreeMap;
|
||||
mod letter_to_option;
|
||||
mod tooltip;
|
||||
|
||||
pub fn draw_lerping_bar(
|
||||
|
|
@ -92,18 +93,19 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
|||
format!("XP{}/{}", stats.level, stats.xp),
|
||||
);
|
||||
// Draw attributes
|
||||
ctx.print_color(36, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "STR");
|
||||
ctx.print_color(39, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.strength.base);
|
||||
ctx.print_color(43, 53, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "DEX");
|
||||
ctx.print_color(46, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.dexterity.base);
|
||||
ctx.print_color(50, 53, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "CON");
|
||||
ctx.print_color(53, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.constitution.base);
|
||||
ctx.print_color(36, 54, RGB::named(rltk::CYAN), RGB::named(rltk::BLACK), "INT");
|
||||
ctx.print_color(39, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.intelligence.base);
|
||||
ctx.print_color(43, 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "WIS");
|
||||
ctx.print_color(46, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.wisdom.base);
|
||||
ctx.print_color(50, 54, RGB::named(rltk::PURPLE), RGB::named(rltk::BLACK), "CHA");
|
||||
ctx.print_color(53, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.charisma.base);
|
||||
let x = 38;
|
||||
ctx.print_color(x, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "STR");
|
||||
ctx.print_color(x + 3, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.strength.base);
|
||||
ctx.print_color(x + 7, 53, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "DEX");
|
||||
ctx.print_color(x + 10, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.dexterity.base);
|
||||
ctx.print_color(x + 14, 53, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "CON");
|
||||
ctx.print_color(x + 17, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.constitution.base);
|
||||
ctx.print_color(x, 54, RGB::named(rltk::CYAN), RGB::named(rltk::BLACK), "INT");
|
||||
ctx.print_color(x + 3, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.intelligence.base);
|
||||
ctx.print_color(x + 7, 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "WIS");
|
||||
ctx.print_color(x + 10, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.wisdom.base);
|
||||
ctx.print_color(x + 14, 54, RGB::named(rltk::PURPLE), RGB::named(rltk::BLACK), "CHA");
|
||||
ctx.print_color(x + 17, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.charisma.base);
|
||||
// Draw hunger
|
||||
match hunger.state {
|
||||
HungerState::Satiated => {
|
||||
|
|
@ -123,14 +125,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
|||
|
||||
// Draw equipment
|
||||
let names = ecs.read_storage::<Name>();
|
||||
let backpack = ecs.read_storage::<InBackpack>();
|
||||
let equippables = ecs.read_storage::<Equippable>();
|
||||
let mut equipment: Vec<String> = Vec::new();
|
||||
for (_entity, _pack, name) in
|
||||
(&equippables, &backpack, &names).join().filter(|item| item.1.owner == *player_entity)
|
||||
{
|
||||
equipment.push(format!("- {}", &name.name));
|
||||
}
|
||||
for (_equipped, name) in (&equipped, &names).join().filter(|item| item.0.owner == *player_entity) {
|
||||
equipment.push(format!("- {} (worn)", &name.name));
|
||||
}
|
||||
|
|
@ -148,7 +143,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
|||
ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "Backpack");
|
||||
y += 1;
|
||||
let (player_inventory, _inventory_ids) = get_player_inventory(&ecs);
|
||||
y = print_options(player_inventory, 72, y, ctx);
|
||||
y = print_options(player_inventory, 72, y, ctx).0;
|
||||
|
||||
// Draw entities seen on screen
|
||||
let viewsheds = ecs.read_storage::<Viewshed>();
|
||||
|
|
@ -231,6 +226,7 @@ pub fn get_input_direction(
|
|||
match ctx.key {
|
||||
None => return RunState::ActionWithDirection { function },
|
||||
Some(key) => match key {
|
||||
VirtualKeyCode::Escape => return RunState::AwaitingInput,
|
||||
// Cardinals
|
||||
VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => return function(-1, 0, ecs),
|
||||
VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => return function(1, 0, ecs),
|
||||
|
|
@ -253,13 +249,19 @@ pub enum ItemMenuResult {
|
|||
Selected,
|
||||
}
|
||||
|
||||
pub fn print_options(inventory: BTreeMap<(String, String), i32>, mut x: i32, mut y: i32, ctx: &mut Rltk) -> i32 {
|
||||
pub fn print_options(inventory: BTreeMap<(String, String), i32>, mut x: i32, mut y: i32, ctx: &mut Rltk) -> (i32, i32) {
|
||||
let mut j = 0;
|
||||
let initial_x: i32 = x;
|
||||
let mut width: i32 = -1;
|
||||
for (name, item_count) in &inventory {
|
||||
x = initial_x;
|
||||
// Print the character required to access this item. i.e. (a)
|
||||
ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
|
||||
if j < 26 {
|
||||
ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
|
||||
} else {
|
||||
// If we somehow have more than 26, start using capitals
|
||||
ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 65 - 26 + j as rltk::FontCharType);
|
||||
}
|
||||
|
||||
x += 2;
|
||||
|
||||
|
|
@ -286,11 +288,33 @@ pub fn print_options(inventory: BTreeMap<(String, String), i32>, mut x: i32, mut
|
|||
}
|
||||
ctx.print(x, y, name.0.to_string());
|
||||
}
|
||||
let this_width = x - initial_x + name.0.len() as i32;
|
||||
width = if width > this_width { width } else { this_width };
|
||||
|
||||
y += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
return y;
|
||||
return (y, width);
|
||||
}
|
||||
|
||||
pub fn get_max_inventory_width(inventory: &BTreeMap<(String, String), i32>) -> i32 {
|
||||
let mut width: i32 = 0;
|
||||
for (name, count) in inventory {
|
||||
let mut this_width = name.0.len() as i32;
|
||||
if count < &1 {
|
||||
this_width += 4;
|
||||
if name.0.ends_with("s") {
|
||||
this_width += 3;
|
||||
} else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| name.0.starts_with(v)) {
|
||||
this_width += 1;
|
||||
}
|
||||
} else {
|
||||
this_width += 4;
|
||||
}
|
||||
width = if width > this_width { width } else { this_width };
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
pub fn show_help(ctx: &mut Rltk) -> YesNoResult {
|
||||
|
|
@ -362,19 +386,29 @@ pub fn get_player_inventory(ecs: &World) -> (BTreeMap<(String, String), i32>, BT
|
|||
pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option<Entity>) {
|
||||
let (player_inventory, inventory_ids) = get_player_inventory(&gs.ecs);
|
||||
let count = player_inventory.len();
|
||||
let y = (25 - (count / 2)) as i32;
|
||||
ctx.draw_box(15, y - 2, 45, (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), "Inventory");
|
||||
ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel");
|
||||
|
||||
print_options(player_inventory, 17, y, ctx);
|
||||
let (x_offset, y_offset) = (1, 10);
|
||||
|
||||
ctx.print_color(
|
||||
1 + x_offset,
|
||||
1 + y_offset,
|
||||
RGB::named(rltk::WHITE),
|
||||
RGB::named(rltk::BLACK),
|
||||
"Interact with what item? [aA-zZ][Esc.]",
|
||||
);
|
||||
|
||||
let x = 1 + x_offset;
|
||||
let y = 3 + y_offset;
|
||||
let width = get_max_inventory_width(&player_inventory);
|
||||
ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK));
|
||||
print_options(player_inventory, x + 1, y + 1, ctx);
|
||||
|
||||
match ctx.key {
|
||||
None => (ItemMenuResult::NoResponse, None),
|
||||
Some(key) => match key {
|
||||
VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None),
|
||||
_ => {
|
||||
let selection = rltk::letter_to_option(key);
|
||||
let selection = letter_to_option::letter_to_option(key, ctx.shift);
|
||||
if selection > -1 && selection < count as i32 {
|
||||
return (ItemMenuResult::Selected, Some(*inventory_ids.iter().nth(selection as usize).unwrap().1));
|
||||
}
|
||||
|
|
@ -387,12 +421,22 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||
pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option<Entity>) {
|
||||
let (player_inventory, inventory_ids) = get_player_inventory(&gs.ecs);
|
||||
let count = player_inventory.len();
|
||||
let y = (25 - (count / 2)) as i32;
|
||||
ctx.draw_box(15, y - 2, 45, (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 what?");
|
||||
ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel");
|
||||
|
||||
print_options(player_inventory, 17, y, ctx);
|
||||
let (x_offset, y_offset) = (1, 10);
|
||||
|
||||
ctx.print_color(
|
||||
1 + x_offset,
|
||||
1 + y_offset,
|
||||
RGB::named(rltk::WHITE),
|
||||
RGB::named(rltk::BLACK),
|
||||
"Drop what? [aA-zZ][Esc.]",
|
||||
);
|
||||
|
||||
let x = 1 + x_offset;
|
||||
let y = 3 + y_offset;
|
||||
let width = get_max_inventory_width(&player_inventory);
|
||||
ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK));
|
||||
print_options(player_inventory, x + 1, y + 1, ctx);
|
||||
|
||||
match ctx.key {
|
||||
None => (ItemMenuResult::NoResponse, None),
|
||||
|
|
@ -414,24 +458,38 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti
|
|||
let names = gs.ecs.read_storage::<Name>();
|
||||
let backpack = gs.ecs.read_storage::<Equipped>();
|
||||
let entities = gs.ecs.entities();
|
||||
|
||||
let inventory = (&backpack, &names).join().filter(|item| item.0.owner == *player_entity);
|
||||
let count = inventory.count();
|
||||
|
||||
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.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Remove what?");
|
||||
ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel");
|
||||
let (x_offset, y_offset) = (1, 10);
|
||||
|
||||
let mut equippable: Vec<Entity> = Vec::new();
|
||||
let mut j = 0;
|
||||
ctx.print_color(
|
||||
1 + x_offset,
|
||||
1 + y_offset,
|
||||
RGB::named(rltk::WHITE),
|
||||
RGB::named(rltk::BLACK),
|
||||
"Drop what? [aA-zZ][Esc.]",
|
||||
);
|
||||
|
||||
let mut equippable: Vec<(Entity, String)> = Vec::new();
|
||||
let mut width = 3;
|
||||
for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) {
|
||||
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(')'));
|
||||
let this_name = &name.name;
|
||||
let this_width = 3 + this_name.len();
|
||||
width = if width > this_width { width } else { this_width };
|
||||
equippable.push((entity, this_name.to_string()));
|
||||
}
|
||||
|
||||
ctx.print(21, y, &name.name.to_string());
|
||||
equippable.push(entity);
|
||||
let x = 1 + x_offset;
|
||||
let mut y = 3 + y_offset;
|
||||
|
||||
ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK));
|
||||
y += 1;
|
||||
|
||||
let mut j = 0;
|
||||
for (_, name) in &equippable {
|
||||
ctx.set(x + 1, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType);
|
||||
ctx.print(x + 3, y, name);
|
||||
y += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
|
@ -443,7 +501,7 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti
|
|||
_ => {
|
||||
let selection = rltk::letter_to_option(key);
|
||||
if selection > -1 && selection < count as i32 {
|
||||
return (ItemMenuResult::Selected, Some(equippable[selection as usize]));
|
||||
return (ItemMenuResult::Selected, Some(equippable[selection as usize].0));
|
||||
}
|
||||
(ItemMenuResult::NoResponse, None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ impl<'a> System<'a> for HungerSystem {
|
|||
}
|
||||
}
|
||||
HungerState::Fainting => {
|
||||
SufferDamage::new_damage(&mut inflict_damage, entity, 1);
|
||||
SufferDamage::new_damage(&mut inflict_damage, entity, 1, false);
|
||||
if entity == *player_entity {
|
||||
gamelog::Logger::new().colour(rltk::RED).append("You can't go on without food...").log();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,13 +298,10 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||
let entity_name = names.get(*mob).unwrap();
|
||||
match destructible {
|
||||
None => {
|
||||
SufferDamage::new_damage(&mut suffer_damage, *mob, damage.amount);
|
||||
SufferDamage::new_damage(&mut suffer_damage, *mob, damage.amount, true);
|
||||
if entity == *player_entity {
|
||||
damage_logger = damage_logger
|
||||
.append("The")
|
||||
.npc_name(&entity_name.name)
|
||||
.append("is hit!")
|
||||
.period();
|
||||
damage_logger =
|
||||
damage_logger.append("The").npc_name(&entity_name.name).append("is hit!");
|
||||
needs_damage_log = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -531,6 +531,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<WantsToMelee>();
|
||||
gs.ecs.register::<SufferDamage>();
|
||||
gs.ecs.register::<Item>();
|
||||
gs.ecs.register::<GrantsXP>();
|
||||
gs.ecs.register::<Equippable>();
|
||||
gs.ecs.register::<Equipped>();
|
||||
gs.ecs.register::<MeleeWeapon>();
|
||||
|
|
|
|||
|
|
@ -141,18 +141,22 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
armour_ac_bonus += ac.amount;
|
||||
}
|
||||
}
|
||||
let mut armour_class = bac - attribute_ac_bonus - skill_ac_bonus - armour_ac_bonus;
|
||||
let actual_armour_class = bac - attribute_ac_bonus - skill_ac_bonus - armour_ac_bonus;
|
||||
let mut armour_class_roll = actual_armour_class;
|
||||
|
||||
if armour_class < 0 {
|
||||
armour_class = rng.roll_dice(1, armour_class);
|
||||
if actual_armour_class < 0 {
|
||||
// Invert armour class so we can roll 1d(AC)
|
||||
armour_class_roll = rng.roll_dice(1, -actual_armour_class);
|
||||
// Invert result so it's a negative again
|
||||
armour_class_roll = -armour_class_roll;
|
||||
}
|
||||
|
||||
let target_number = 10 + armour_class + attacker_bonuses;
|
||||
let target_number = 10 + armour_class_roll + attacker_bonuses;
|
||||
|
||||
if COMBAT_LOGGING {
|
||||
rltk::console::log(format!(
|
||||
"ATTACKLOG: {} *{}* {}: rolled ({}) 1d20 vs. {} (10 + {}AC + {}to-hit)",
|
||||
&name.name, attack_verb, &target_name.name, d20, target_number, armour_class, attacker_bonuses
|
||||
&name.name, attack_verb, &target_name.name, d20, target_number, armour_class_roll, attacker_bonuses
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -160,9 +164,19 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
// Target hit!
|
||||
let base_damage = rng.roll_dice(weapon_info.damage_n_dice, weapon_info.damage_die_type);
|
||||
let skill_damage_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills);
|
||||
let attribute_damage_bonus = weapon_info.damage_bonus;
|
||||
let mut damage =
|
||||
i32::max(0, base_damage + attribute_damage_bonus + skill_damage_bonus + attribute_damage_bonus);
|
||||
let mut attribute_damage_bonus = weapon_info.damage_bonus;
|
||||
match weapon_info.attribute {
|
||||
WeaponAttribute::Dexterity => attribute_damage_bonus += attacker_attributes.dexterity.bonus,
|
||||
WeaponAttribute::Strength => attribute_damage_bonus += attacker_attributes.strength.bonus,
|
||||
WeaponAttribute::Finesse => {
|
||||
if attacker_attributes.dexterity.bonus > attacker_attributes.strength.bonus {
|
||||
attribute_damage_bonus += attacker_attributes.dexterity.bonus;
|
||||
} else {
|
||||
attribute_damage_bonus += attacker_attributes.strength.bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut damage = i32::max(0, base_damage + skill_damage_bonus + attribute_damage_bonus);
|
||||
|
||||
if COMBAT_LOGGING {
|
||||
rltk::console::log(format!(
|
||||
|
|
@ -177,8 +191,8 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
));
|
||||
}
|
||||
|
||||
if armour_class < 0 {
|
||||
let ac_damage_reduction = rng.roll_dice(1, armour_class);
|
||||
if actual_armour_class < 0 {
|
||||
let ac_damage_reduction = rng.roll_dice(1, -actual_armour_class);
|
||||
damage = i32::min(1, damage - ac_damage_reduction);
|
||||
if COMBAT_LOGGING {
|
||||
rltk::console::log(format!(
|
||||
|
|
@ -192,7 +206,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
if let Some(pos) = pos {
|
||||
particle_builder.damage_taken(pos.x, pos.y)
|
||||
}
|
||||
SufferDamage::new_damage(&mut inflict_damage, wants_melee.target, damage);
|
||||
SufferDamage::new_damage(&mut inflict_damage, wants_melee.target, damage, entity == *player_entity);
|
||||
if entity == *player_entity {
|
||||
gamelog::Logger::new() // You hit the <name>.
|
||||
.append("You hit the")
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
|||
if map.tile_content[destination_idx].len() == 0 {
|
||||
if rng.roll_dice(1, 20) == 20 {
|
||||
let mut suffer_damage = ecs.write_storage::<SufferDamage>();
|
||||
SufferDamage::new_damage(&mut suffer_damage, entity, 1);
|
||||
SufferDamage::new_damage(&mut suffer_damage, entity, 1, false);
|
||||
gamelog::Logger::new().append("Ouch! You kick the open air, and pull something.").log();
|
||||
break;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -275,13 +275,6 @@ pub fn spawn_named_mob(
|
|||
let mob_mana = mana_at_level(&mut rng, mob_int, mob_level);
|
||||
let mob_bac = if mob_template.bac.is_some() { mob_template.bac.unwrap() } else { 10 };
|
||||
|
||||
if SPAWN_LOGGING {
|
||||
rltk::console::log(format!(
|
||||
"SPAWNLOG: {} ({}HP, {}MANA, {}BAC) spawned at level {} ({}[base], {}[map difficulty], {}[player level])",
|
||||
&mob_template.name, mob_hp, mob_mana, mob_bac, mob_level, base_mob_level, map_difficulty, player_level
|
||||
));
|
||||
}
|
||||
|
||||
let pools = Pools {
|
||||
level: mob_level,
|
||||
xp: 0,
|
||||
|
|
@ -330,6 +323,32 @@ pub fn spawn_named_mob(
|
|||
eb = eb.with(natural);
|
||||
}
|
||||
|
||||
let mut xp_value = 1;
|
||||
xp_value += mob_level * mob_level;
|
||||
// if speed > 18, +5
|
||||
// if speed > 12, +3
|
||||
if mob_bac < 0 {
|
||||
xp_value += 14 + 2 * mob_bac;
|
||||
} else if mob_bac == 0 {
|
||||
xp_value += 7;
|
||||
} else if mob_bac == 1 {
|
||||
xp_value += 6;
|
||||
} else if mob_bac == 2 {
|
||||
xp_value += 5;
|
||||
}
|
||||
if mob_level > 9 {
|
||||
xp_value += 50;
|
||||
}
|
||||
|
||||
eb = eb.with(GrantsXP { amount: xp_value });
|
||||
|
||||
if SPAWN_LOGGING {
|
||||
rltk::console::log(format!(
|
||||
"SPAWNLOG: {} ({}HP, {}MANA, {}BAC) spawned at level {} ({}[base], {}[map difficulty], {}[player level]), worth {} XP",
|
||||
&mob_template.name, mob_hp, mob_mana, mob_bac, mob_level, base_mob_level, map_difficulty, player_level, xp_value
|
||||
));
|
||||
}
|
||||
|
||||
let new_mob = eb.build();
|
||||
|
||||
// Build entity, then check for anything they're wearing
|
||||
|
|
@ -338,6 +357,7 @@ pub fn spawn_named_mob(
|
|||
spawn_named_entity(raws, ecs, tag, SpawnType::Equipped { by: new_mob }, map_difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
return Some(new_mob);
|
||||
}
|
||||
None
|
||||
|
|
@ -421,7 +441,7 @@ pub fn table_by_name(raws: &RawMaster, key: &str, difficulty: i32) -> RandomTabl
|
|||
let available_options: Vec<&SpawnTableEntry> = spawn_table
|
||||
.table
|
||||
.iter()
|
||||
.filter(|entry| entry.difficulty >= lower_bound && entry.difficulty <= upper_bound)
|
||||
.filter(|entry| entry.difficulty > lower_bound && entry.difficulty <= upper_bound)
|
||||
.collect();
|
||||
|
||||
let mut rt = RandomTable::new();
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ pub fn save_game(ecs: &mut World) {
|
|||
EntryTrigger,
|
||||
Equippable,
|
||||
Equipped,
|
||||
GrantsXP,
|
||||
Hidden,
|
||||
HungerClock,
|
||||
InBackpack,
|
||||
|
|
@ -164,6 +165,7 @@ pub fn load_game(ecs: &mut World) {
|
|||
EntryTrigger,
|
||||
Equippable,
|
||||
Equipped,
|
||||
GrantsXP,
|
||||
Hidden,
|
||||
HungerClock,
|
||||
InBackpack,
|
||||
|
|
|
|||
|
|
@ -71,20 +71,6 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
|||
raws::SpawnType::Carried { by: player },
|
||||
0,
|
||||
);
|
||||
raws::spawn_named_entity(
|
||||
&raws::RAWS.lock().unwrap(),
|
||||
ecs,
|
||||
"scroll_magicmissile",
|
||||
raws::SpawnType::Carried { by: player },
|
||||
0,
|
||||
);
|
||||
raws::spawn_named_entity(
|
||||
&raws::RAWS.lock().unwrap(),
|
||||
ecs,
|
||||
"scroll_fireball",
|
||||
raws::SpawnType::Carried { by: player },
|
||||
0,
|
||||
);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ impl<'a> System<'a> for TriggerSystem {
|
|||
let damage = inflicts_damage.get(*entity_id);
|
||||
if let Some(damage) = damage {
|
||||
particle_builder.damage_taken(pos.x, pos.y);
|
||||
SufferDamage::new_damage(&mut inflict_damage, entity, damage.amount);
|
||||
SufferDamage::new_damage(&mut inflict_damage, entity, damage.amount, false);
|
||||
}
|
||||
|
||||
let confuses = confusion.get(*entity_id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue