Add initiative penalties to items

This commit is contained in:
Timothy Warren 2022-01-13 10:14:13 -05:00
parent 09de57f67f
commit 220b00c64c
11 changed files with 334 additions and 70 deletions

View File

@ -219,7 +219,10 @@
"effects": {
"provides_healing": "8"
}
}
},
"weight_lbs": 0.5,
"base_value": 50.0,
"vendor_category": "alchemy"
},
{
"name": "Magic Missile Scroll",
@ -234,7 +237,10 @@
"ranged": "6",
"damage": "20"
}
}
},
"weight_lbs": 0.5,
"base_value": 50.0,
"vendor_category": "alchemy"
},
{
"name": "Fireball Scroll",
@ -250,7 +256,10 @@
"damage": "20",
"area_of_effect": "3"
}
}
},
"weight_lbs": 0.5,
"base_value": 100.0,
"vendor_category": "alchemy"
},
{
"name": "Confusion Scroll",
@ -263,10 +272,12 @@
"consumable": {
"effects": {
"ranged": "6",
"damage": "20",
"confusion": "4"
}
}
},
"weight_lbs": 0.5,
"base_value": 75.0,
"vendor_category": "alchemy"
},
{
"name": "Magic Mapping Scroll",
@ -280,7 +291,10 @@
"effects": {
"magic_mapping": ""
}
}
},
"weight_lbs": 0.5,
"base_value": 50.0,
"vendor_category": "alchemy"
},
{
"name": "Rations",
@ -294,7 +308,10 @@
"effects": {
"food": ""
}
}
},
"weight_lbs": 2.0,
"base_value": 0.5,
"vendor_category": "food"
},
{
"name": "Meat",
@ -308,7 +325,10 @@
"effects": {
"food": ""
}
}
},
"weight_lbs": 2.0,
"base_value": 0.5,
"vendor_category": "food"
},
{
"name": "Hide",
@ -317,7 +337,9 @@
"fg": "#A52A2A",
"bg": "#000000",
"order": 2
}
},
"weight_lbs": 2.0,
"base_value": 5.0
},
{
"name": "Dried Sausage",
@ -331,7 +353,9 @@
"effects": {
"food": ""
}
}
},
"weight_lbs": 2.0,
"base_value": 0.5
},
{
"name": "Beer",
@ -345,7 +369,10 @@
"effects": {
"provides_healing": "4"
}
}
},
"weight_lbs": 2.0,
"base_value": 0.5,
"vendor_category": "food"
},
{
"name": "Rusty Longsword",
@ -360,7 +387,11 @@
"attribute": "Might",
"base_damage": "1d8-1",
"hit_bonus": -1
}
},
"weight_lbs": 3.0,
"base_value": 10.0,
"initiative_penalty": 2,
"vendor_category": "junk"
},
{
"name": "Dagger",
@ -375,7 +406,11 @@
"attribute": "Quickness",
"base_damage": "1d4",
"hit_bonus": 0
}
},
"weight_lbs": 1.0,
"base_value": 2.0,
"initiative_penalty": 0,
"vendor_category": "weapon"
},
{
"name": "Shortsword",
@ -390,7 +425,11 @@
"attribute": "Might",
"base_damage": "1d6",
"hit_bonus": 0
}
},
"weight_lbs": 2.0,
"base_value": 10.0,
"initiative_penalty": 1,
"vendor_category": "weapon"
},
{
"name": "Longsword",
@ -405,7 +444,11 @@
"attribute": "Might",
"base_damage": "1d8",
"hit_bonus": 0
}
},
"weight_lbs": 3.0,
"base_value": 15.0,
"initiative_penalty": 2,
"vendor_category": "weapon"
},
{
"name": "Battleaxe",
@ -418,9 +461,13 @@
"weapon": {
"range": "melee",
"attribute": "Might",
"base_damage": "1d8+1",
"base_damage": "1d8",
"hit_bonus": 0
}
},
"weight_lbs": 4.0,
"base_value": 10.0,
"initiative_penalty": 2,
"vendor_category": "weapon"
},
{
"name": "Shield",
@ -433,7 +480,11 @@
"wearable": {
"slot": "Shield",
"armor_class": 1.0
}
},
"weight_lbs": 5.0,
"base_value": 3.0,
"initiative_penalty": 0.5,
"vendor_category": "armor"
},
{
"name": "Tower Shield",
@ -446,7 +497,11 @@
"wearable": {
"slot": "Shield",
"armor_class": 2.0
}
},
"weight_lbs": 45.0,
"base_value": 30.0,
"initiative_penalty": 1.0,
"vendor_category": "armor"
},
{
"name": "Stained Tunic",
@ -459,7 +514,11 @@
"wearable": {
"slot": "Torso",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "junk"
},
{
"name": "Torn Trousers",
@ -472,7 +531,11 @@
"wearable": {
"slot": "Legs",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "junk"
},
{
"name": "Old Boots",
@ -485,7 +548,11 @@
"wearable": {
"slot": "Feet",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "junk"
},
{
"name": "Cudgel",
@ -500,7 +567,11 @@
"attribute": "Quickness",
"base_damage": "1d4",
"hit_bonus": 0
}
},
"weight_lbs": 2.0,
"base_value": 0.1,
"initiative_penalty": 2.0,
"vendor_category": "junk"
},
{
"name": "Cloth Tunic",
@ -513,7 +584,11 @@
"wearable": {
"slot": "Torso",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "clothes"
},
{
"name": "Cloth Pants",
@ -526,7 +601,11 @@
"wearable": {
"slot": "Legs",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "clothes"
},
{
"name": "Slippers",
@ -539,7 +618,11 @@
"wearable": {
"slot": "Feet",
"armor_class": 0.1
}
},
"weight_lbs": 1.0,
"base_value": 1.0,
"initiative_penalty": 0.1,
"vendor_category": "clothes"
},
{
"name": "Leather Armor",
@ -552,7 +635,11 @@
"wearable": {
"slot": "Torso",
"armor_class": 1.0
}
},
"weight_lbs": 15.0,
"base_value": 10.0,
"initiative_penalty": 0.5,
"vendor_category": "clothes"
},
{
"name": "Leather Boots",
@ -565,7 +652,11 @@
"wearable": {
"slot": "Feet",
"armor_class": 0.2
}
},
"weight_lbs": 2.0,
"base_value": 5.0,
"initiative_penalty": 0.25,
"vendor_category": "clothes"
}
],
"mobs": [
@ -592,7 +683,11 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6",
"vendor": [
"food"
]
},
{
"name": "Shady Salesman",
@ -612,7 +707,11 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6",
"vendor": [
"junk"
]
},
{
"name": "Patron",
@ -637,7 +736,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d4"
},
{
"name": "Priest",
@ -657,7 +757,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6"
},
{
"name": "Parishioner",
@ -682,7 +783,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d4"
},
{
"name": "Blacksmith",
@ -702,7 +804,12 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6",
"vendor": [
"armor",
"weapon"
]
},
{
"name": "Clothier",
@ -722,7 +829,11 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6",
"vendor": [
"clothes"
]
},
{
"name": "Alchemist",
@ -742,7 +853,11 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6",
"vendor": [
"alchemy"
]
},
{
"name": "Mom",
@ -768,7 +883,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6"
},
{
"name": "Peasant",
@ -791,7 +907,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d2"
},
{
"name": "Dock Worker",
@ -816,7 +933,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d2"
},
{
"name": "Fisher",
@ -841,7 +959,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d2"
},
{
"name": "Wannabe Pirate",
@ -866,7 +985,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "2d6"
},
{
"name": "Drunk",
@ -891,7 +1011,8 @@
"Cloth Pants",
"Slippers"
],
"faction": "Townsfolk"
"faction": "Townsfolk",
"gold": "1d2"
},
{
"name": "Rat",
@ -1046,7 +1167,8 @@
"range": 6,
"color": "#FFFF55"
},
"faction": "Bandits"
"faction": "Bandits",
"gold": "1d6"
},
{
"name": "Orc",
@ -1060,7 +1182,8 @@
"vision_range": 8,
"movement": "static",
"attributes": {},
"faction": "Cave Goblins"
"faction": "Cave Goblins",
"gold": "1d8"
},
{
"name": "Goblin",
@ -1074,7 +1197,8 @@
"vision_range": 8,
"movement": "static",
"attributes": {},
"faction": "Cave Goblins"
"faction": "Cave Goblins",
"gold": "1d6"
},
{
"name": "Kobold",
@ -1088,7 +1212,8 @@
"vision_range": 4,
"movement": "static",
"attributes": {},
"faction": "Cave Goblins"
"faction": "Cave Goblins",
"gold": "1d4"
},
{
"name": "Bat",

View File

@ -2,6 +2,7 @@ mod adjacent_ai_system;
mod approach_ai_system;
mod chase_ai_system;
mod default_move_system;
mod encumbrance_system;
mod flee_ai_system;
mod initiative_system;
mod quipping;
@ -12,6 +13,7 @@ pub use adjacent_ai_system::AdjacentAI;
pub use approach_ai_system::ApproachAI;
pub use chase_ai_system::ChaseAI;
pub use default_move_system::DefaultMoveAI;
pub use encumbrance_system::EncumbranceSystem;
pub use flee_ai_system::FleeAI;
pub use initiative_system::InitiativeSystem;
pub use quipping::QuipSystem;

View File

@ -0,0 +1,89 @@
use std::collections::HashMap;
use ::specs::prelude::*;
use crate::components::{Attributes, EquipmentChanged, Equipped, InBackpack, Item, Pools};
use crate::game_log::GameLog;
pub struct EncumbranceSystem {}
impl<'a> System<'a> for EncumbranceSystem {
#[allow(clippy::type_complexity)]
type SystemData = (
WriteStorage<'a, EquipmentChanged>,
Entities<'a>,
ReadStorage<'a, Item>,
ReadStorage<'a, InBackpack>,
ReadStorage<'a, Equipped>,
WriteStorage<'a, Pools>,
ReadStorage<'a, Attributes>,
ReadExpect<'a, Entity>,
WriteExpect<'a, GameLog>,
);
fn run(&mut self, data: Self::SystemData) {
let (
mut equip_dirty,
entities,
items,
backpacks,
wielded,
mut pools,
attributes,
player,
mut gamelog,
) = data;
if equip_dirty.is_empty() {
return;
}
// Build the map of who needs updating
let mut to_update: HashMap<Entity, (f32, f32)> = HashMap::new(); // (weight, initiative)
for (entity, _dirty) in (&entities, &equip_dirty).join() {
to_update.insert(entity, (0., 0.));
}
// Remove all dirty statements
equip_dirty.clear();
// Total up equipped items
for (item, equipped) in (&items, &wielded).join() {
if to_update.contains_key(&equipped.owner) {
let totals = to_update.get_mut(&equipped.owner).unwrap();
totals.0 += item.weight_lbs;
totals.1 += item.initiative_penalty;
}
}
// Total up carried items
for (item, carried) in (&items, &backpacks).join() {
if to_update.contains_key(&carried.owner) {
let totals = to_update.get_mut(&carried.owner).unwrap();
totals.0 += item.weight_lbs;
totals.1 += item.initiative_penalty;
}
}
// Apply the data to Pools
for (entity, (weight, initiative)) in to_update.iter() {
if let Some(pool) = pools.get_mut(*entity) {
pool.total_weight = *weight;
pool.total_initiative_penalty = *initiative;
if let Some(attr) = attributes.get(*entity) {
let carry_capacity_lbs = (attr.might.base + attr.might.modifiers) * 15;
if pool.total_weight as i32 > carry_capacity_lbs {
// Overburdened
pool.total_initiative_penalty += 4.0;
if *entity == *player {
gamelog.append(
"You are overburdened, and suffering an initiative penalty.",
);
}
}
}
}
}
}
}

View File

@ -1,5 +1,5 @@
mod enums;
mod simple;
mod tags;
use std::collections::HashMap;
@ -10,7 +10,7 @@ use ::specs::prelude::*;
use ::specs::saveload::{ConvertSaveload, Marker};
use ::specs_derive::*;
pub use enums::*;
pub use simple::*;
pub use tags::*;
use crate::gamesystem::attr_bonus;
@ -81,6 +81,13 @@ pub struct WantsToMelee {
pub target: Entity,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Item {
pub initiative_penalty: f32,
pub weight_lbs: f32,
pub base_value: f32,
}
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct SufferDamage {
pub amount: Vec<(i32, bool)>,
@ -250,12 +257,23 @@ pub struct Pool {
pub current: i32,
}
impl Pool {
pub fn new(default_value: i32) -> Pool {
Pool {
max: default_value,
current: default_value,
}
}
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Pools {
pub hit_points: Pool,
pub mana: Pool,
pub xp: i32,
pub level: i32,
pub total_weight: f32,
pub total_initiative_penalty: f32,
}
#[derive(Serialize, Deserialize, Clone)]

View File

@ -1,3 +1,5 @@
///! Simple Components that are mainly used for tagging behavior. None of these have
/// any properties
use ::serde::{Deserialize, Serialize};
use ::specs::prelude::*;
use ::specs_derive::*;
@ -5,9 +7,6 @@ use ::specs_derive::*;
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Player {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Item {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Consumable {}
@ -37,3 +36,6 @@ pub struct BlocksVisibility {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct MyTurn {}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct EquipmentChanged {}

View File

@ -17,11 +17,19 @@ impl<'a> System<'a> for ItemCollectionSystem {
WriteStorage<'a, Position>,
ReadStorage<'a, Name>,
WriteStorage<'a, InBackpack>,
WriteStorage<'a, EquipmentChanged>,
);
fn run(&mut self, data: Self::SystemData) {
let (player_entity, mut gamelog, mut wants_pickup, mut positions, names, mut backpack) =
data;
let (
player_entity,
mut gamelog,
mut wants_pickup,
mut positions,
names,
mut backpack,
mut dirty,
) = data;
for pickup in wants_pickup.join() {
positions.remove(pickup.item);
@ -33,6 +41,9 @@ impl<'a> System<'a> for ItemCollectionSystem {
},
)
.expect("Failed to add item to backpack");
dirty
.insert(pickup.collected_by, EquipmentChanged {})
.expect("Unable to insert equipment change");
if pickup.collected_by == *player_entity {
gamelog.append(format!(
@ -73,6 +84,7 @@ impl<'a> System<'a> for ItemUseSystem {
WriteStorage<'a, HungerClock>,
ReadStorage<'a, MagicMapper>,
WriteExpect<'a, RunState>,
WriteStorage<'a, EquipmentChanged>,
);
#[allow(clippy::cognitive_complexity)]
@ -100,9 +112,13 @@ impl<'a> System<'a> for ItemUseSystem {
mut hunger_clocks,
magic_mapper,
mut runstate,
mut dirty,
) = data;
for (entity, useitem) in (&entities, &wants_use).join() {
dirty
.insert(entity, EquipmentChanged {})
.expect("Unable to insert equipment change");
let mut used_item = true;
// Targeting
@ -364,6 +380,7 @@ impl<'a> System<'a> for ItemDropSystem {
ReadStorage<'a, Name>,
WriteStorage<'a, Position>,
WriteStorage<'a, InBackpack>,
WriteStorage<'a, EquipmentChanged>,
);
fn run(&mut self, data: Self::SystemData) {
@ -375,6 +392,7 @@ impl<'a> System<'a> for ItemDropSystem {
names,
mut positions,
mut backpack,
mut dirty,
) = data;
for (entity, to_drop) in (&entities, &wants_drop).join() {
@ -397,6 +415,10 @@ impl<'a> System<'a> for ItemDropSystem {
backpack.remove(to_drop.item);
dirty
.insert(entity, EquipmentChanged {})
.expect("Unable to insert equipment change");
if entity == *player_entity {
gamelog.append(format!(
"You drop the {}.",

View File

@ -113,6 +113,9 @@ impl State {
let mut vis = VisibilitySystem {};
vis.run_now(&self.ecs);
let mut encumbrance = ai::EncumbranceSystem {};
encumbrance.run_now(&self.ecs);
let mut initiative = ai::InitiativeSystem {};
initiative.run_now(&self.ecs);
@ -475,6 +478,7 @@ fn main() -> ::rltk::BError {
EntryTrigger,
Equippable,
Equipped,
EquipmentChanged,
Faction,
Hidden,
HungerClock,

View File

@ -9,6 +9,9 @@ pub struct Item {
pub consumable: Option<Consumable>,
pub weapon: Option<Weapon>,
pub wearable: Option<Wearable>,
pub initiative_penalty: Option<f32>,
pub weight_lbs: Option<f32>,
pub base_value: Option<f32>,
}
#[derive(Deserialize, Debug)]

View File

@ -234,7 +234,11 @@ pub fn spawn_named_item(
eb = eb.with(Name::from(&item_template.name));
eb = eb.with(Item {});
eb = eb.with(Item {
initiative_penalty: item_template.initiative_penalty.unwrap_or(0.),
weight_lbs: item_template.weight_lbs.unwrap_or(0.),
base_value: item_template.base_value.unwrap_or(0.),
});
if let Some(consumable) = &item_template.consumable {
eb = eb.with(Consumable {});
@ -396,14 +400,10 @@ pub fn spawn_named_mob(
let pools = Pools {
level: mob_level,
xp: 0,
hit_points: Pool {
current: mob_hp,
max: mob_hp,
},
mana: Pool {
current: mob_mana,
max: mob_mana,
},
hit_points: Pool::new(mob_hp),
mana: Pool::new(mob_mana),
total_weight: 0.,
total_initiative_penalty: 0.,
};
eb = eb.with(pools);

View File

@ -74,6 +74,7 @@ pub fn save_game(ecs: &mut World) {
EntryTrigger,
Equippable,
Equipped,
EquipmentChanged,
Faction,
Hidden,
HungerClock,
@ -181,6 +182,7 @@ pub fn load_game(ecs: &mut World) {
EntryTrigger,
Equippable,
Equipped,
EquipmentChanged,
Faction,
Hidden,
HungerClock,

View File

@ -39,16 +39,12 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
})
.with(Skills::new(1))
.with(Pools {
hit_points: Pool {
current: player_hp_at_level(11, 1),
max: player_hp_at_level(11, 1),
},
mana: Pool {
current: mana_at_level(11, 1),
max: mana_at_level(11, 1),
},
hit_points: Pool::new(player_hp_at_level(11, 1)),
mana: Pool::new(mana_at_level(11, 1)),
xp: 0,
level: 1,
total_weight: 0.,
total_initiative_penalty: 0.,
})
.with(LightSource {
color: RGB::from_f32(1.0, 1.0, 0.5),
@ -56,6 +52,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
})
.with(Initiative { current: 0 })
.with(Faction::from("Player"))
.with(EquipmentChanged {})
.marked::<SimpleMarker<SerializeMe>>()
.build();