2021-12-23 13:07:50 -05:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2021-12-23 11:38:37 -05:00
|
|
|
|
|
|
|
use specs::prelude::*;
|
|
|
|
|
|
|
|
use crate::components::*;
|
2021-12-23 13:07:50 -05:00
|
|
|
use crate::random_table::RandomTable;
|
2021-12-23 12:31:03 -05:00
|
|
|
use crate::raws::Raws;
|
2021-12-23 11:38:37 -05:00
|
|
|
|
|
|
|
pub enum SpawnType {
|
|
|
|
AtPosition { x: i32, y: i32 },
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RawMaster {
|
|
|
|
raws: Raws,
|
|
|
|
item_index: HashMap<String, usize>,
|
2021-12-23 12:31:03 -05:00
|
|
|
mob_index: HashMap<String, usize>,
|
2021-12-23 12:48:09 -05:00
|
|
|
prop_index: HashMap<String, usize>,
|
2021-12-23 11:38:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RawMaster {
|
|
|
|
pub fn empty() -> RawMaster {
|
|
|
|
RawMaster {
|
2021-12-23 12:31:03 -05:00
|
|
|
raws: Raws {
|
|
|
|
items: Vec::new(),
|
|
|
|
mobs: Vec::new(),
|
2021-12-23 12:48:09 -05:00
|
|
|
props: Vec::new(),
|
2021-12-23 13:07:50 -05:00
|
|
|
spawn_table: Vec::new(),
|
2021-12-23 12:31:03 -05:00
|
|
|
},
|
2021-12-23 11:38:37 -05:00
|
|
|
item_index: HashMap::new(),
|
2021-12-23 12:31:03 -05:00
|
|
|
mob_index: HashMap::new(),
|
2021-12-23 12:48:09 -05:00
|
|
|
prop_index: HashMap::new(),
|
2021-12-23 11:38:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load(&mut self, raws: Raws) {
|
|
|
|
self.raws = raws;
|
|
|
|
self.item_index = HashMap::new();
|
2021-12-23 13:07:50 -05:00
|
|
|
let mut used_names: HashSet<String> = HashSet::new();
|
2021-12-23 11:38:37 -05:00
|
|
|
for (i, item) in self.raws.items.iter().enumerate() {
|
2021-12-23 13:07:50 -05:00
|
|
|
if used_names.contains(&item.name) {
|
|
|
|
rltk::console::log(format!(
|
|
|
|
"WARNING - duplicate item name in raws [{}]",
|
|
|
|
item.name
|
|
|
|
));
|
|
|
|
}
|
2021-12-23 11:38:37 -05:00
|
|
|
self.item_index.insert(item.name.clone(), i);
|
2021-12-23 13:07:50 -05:00
|
|
|
used_names.insert(item.name.clone());
|
2021-12-23 11:38:37 -05:00
|
|
|
}
|
2021-12-23 12:31:03 -05:00
|
|
|
for (i, mob) in self.raws.mobs.iter().enumerate() {
|
2021-12-23 13:07:50 -05:00
|
|
|
if used_names.contains(&mob.name) {
|
|
|
|
rltk::console::log(format!(
|
|
|
|
"WARNING - duplicate mob name in raws [{}]",
|
|
|
|
mob.name
|
|
|
|
));
|
|
|
|
}
|
2021-12-23 12:31:03 -05:00
|
|
|
self.mob_index.insert(mob.name.clone(), i);
|
2021-12-23 13:07:50 -05:00
|
|
|
used_names.insert(mob.name.clone());
|
2021-12-23 12:31:03 -05:00
|
|
|
}
|
2021-12-23 12:48:09 -05:00
|
|
|
for (i, prop) in self.raws.props.iter().enumerate() {
|
2021-12-23 13:07:50 -05:00
|
|
|
if used_names.contains(&prop.name) {
|
|
|
|
rltk::console::log(format!(
|
|
|
|
"WARNING - duplicate prop name in raws [{}]",
|
|
|
|
prop.name
|
|
|
|
));
|
|
|
|
}
|
2021-12-23 12:48:09 -05:00
|
|
|
self.prop_index.insert(prop.name.clone(), i);
|
2021-12-23 13:07:50 -05:00
|
|
|
used_names.insert(prop.name.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
for spawn in self.raws.spawn_table.iter() {
|
|
|
|
if !used_names.contains(&spawn.name) {
|
|
|
|
rltk::console::log(format!(
|
|
|
|
"WARNING - Spawn tables references unspecified entity {}",
|
|
|
|
spawn.name
|
|
|
|
));
|
|
|
|
}
|
2021-12-23 12:48:09 -05:00
|
|
|
}
|
2021-12-23 12:31:03 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn spawn_position(pos: SpawnType, new_entity: EntityBuilder) -> EntityBuilder {
|
|
|
|
let mut eb = new_entity;
|
|
|
|
|
|
|
|
// Spawn in the specified location
|
|
|
|
match pos {
|
|
|
|
SpawnType::AtPosition { x, y } => {
|
|
|
|
eb = eb.with(Position { x, y });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
eb
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_renderable_component(
|
|
|
|
renderable: &super::item_structs::Renderable,
|
|
|
|
) -> crate::components::Renderable {
|
|
|
|
crate::components::Renderable {
|
|
|
|
glyph: rltk::to_cp437(renderable.glyph.chars().next().unwrap()),
|
|
|
|
fg: rltk::RGB::from_hex(&renderable.fg).expect("Invalid RGB"),
|
|
|
|
bg: rltk::RGB::from_hex(&renderable.bg).expect("Invalid RGB"),
|
|
|
|
render_order: renderable.order,
|
2021-12-23 11:38:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn spawn_named_item(
|
|
|
|
raws: &RawMaster,
|
|
|
|
new_entity: EntityBuilder,
|
|
|
|
key: &str,
|
|
|
|
pos: SpawnType,
|
|
|
|
) -> Option<Entity> {
|
|
|
|
if raws.item_index.contains_key(key) {
|
|
|
|
let item_template = &raws.raws.items[raws.item_index[key]];
|
|
|
|
|
|
|
|
let mut eb = new_entity;
|
|
|
|
|
|
|
|
// Spawn in the specified location
|
2021-12-23 12:31:03 -05:00
|
|
|
eb = spawn_position(pos, eb);
|
2021-12-23 11:38:37 -05:00
|
|
|
|
|
|
|
// Renderable
|
|
|
|
if let Some(renderable) = &item_template.renderable {
|
2021-12-23 12:31:03 -05:00
|
|
|
eb = eb.with(get_renderable_component(renderable));
|
2021-12-23 11:38:37 -05:00
|
|
|
}
|
|
|
|
|
2021-12-23 12:05:56 -05:00
|
|
|
eb = eb.with(Name::from(&item_template.name));
|
|
|
|
|
|
|
|
eb = eb.with(Item {});
|
2021-12-23 11:38:37 -05:00
|
|
|
|
|
|
|
if let Some(consumable) = &item_template.consumable {
|
|
|
|
eb = eb.with(Consumable {});
|
|
|
|
for effect in consumable.effects.iter() {
|
|
|
|
let effect_name = effect.0.as_str();
|
|
|
|
match effect_name {
|
|
|
|
"provides_healing" => {
|
|
|
|
eb = eb.with(ProvidesHealing {
|
|
|
|
heal_amount: effect.1.parse::<i32>().unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"ranged" => {
|
|
|
|
eb = eb.with(Ranged {
|
|
|
|
range: effect.1.parse::<i32>().unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"damage" => {
|
|
|
|
eb = eb.with(InflictsDamage {
|
|
|
|
damage: effect.1.parse::<i32>().unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"area_of_effect" => {
|
|
|
|
eb = eb.with(AreaOfEffect {
|
|
|
|
radius: effect.1.parse::<i32>().unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"confusion" => {
|
|
|
|
eb = eb.with(Confusion {
|
|
|
|
turns: effect.1.parse::<i32>().unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"magic_mapping" => eb = eb.with(MagicMapper {}),
|
2021-12-23 12:05:56 -05:00
|
|
|
"food" => eb = eb.with(ProvidesFood {}),
|
2021-12-23 11:38:37 -05:00
|
|
|
_ => {
|
|
|
|
rltk::console::log(format!(
|
|
|
|
"Warning: consumable effect {} not implemented.",
|
|
|
|
effect_name
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-23 12:05:56 -05:00
|
|
|
if let Some(weapon) = &item_template.weapon {
|
|
|
|
eb = eb.with(Equippable {
|
|
|
|
slot: EquipmentSlot::Melee,
|
|
|
|
});
|
|
|
|
eb = eb.with(MeleePowerBonus {
|
|
|
|
power: weapon.power_bonus,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(shield) = &item_template.shield {
|
|
|
|
eb = eb.with(Equippable {
|
|
|
|
slot: EquipmentSlot::Shield,
|
|
|
|
});
|
|
|
|
eb = eb.with(DefenseBonus {
|
|
|
|
defense: shield.defense_bonus,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-12-23 11:38:37 -05:00
|
|
|
return Some(eb.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2021-12-23 12:31:03 -05:00
|
|
|
|
|
|
|
pub fn spawn_named_mob(
|
|
|
|
raws: &RawMaster,
|
|
|
|
new_entity: EntityBuilder,
|
|
|
|
key: &str,
|
|
|
|
pos: SpawnType,
|
|
|
|
) -> Option<Entity> {
|
|
|
|
if raws.mob_index.contains_key(key) {
|
|
|
|
let mob_template = &raws.raws.mobs[raws.mob_index[key]];
|
|
|
|
|
|
|
|
let mut eb = new_entity;
|
|
|
|
|
|
|
|
// Spawn in the specified location
|
|
|
|
eb = spawn_position(pos, eb);
|
|
|
|
|
|
|
|
// Renderable
|
|
|
|
if let Some(renderable) = &mob_template.renderable {
|
|
|
|
eb = eb.with(get_renderable_component(renderable));
|
|
|
|
}
|
|
|
|
|
|
|
|
eb = eb.with(Name::from(&mob_template.name));
|
|
|
|
|
|
|
|
eb = eb.with(Monster {});
|
|
|
|
if mob_template.blocks_tile {
|
|
|
|
eb = eb.with(BlocksTile {});
|
|
|
|
}
|
|
|
|
|
|
|
|
eb = eb.with(CombatStats {
|
|
|
|
max_hp: mob_template.stats.max_hp,
|
|
|
|
hp: mob_template.stats.hp,
|
|
|
|
power: mob_template.stats.power,
|
|
|
|
defense: mob_template.stats.defense,
|
|
|
|
});
|
|
|
|
|
|
|
|
eb = eb.with(Viewshed {
|
|
|
|
visible_tiles: Vec::new(),
|
|
|
|
range: mob_template.vision_range,
|
|
|
|
dirty: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
return Some(eb.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2021-12-23 12:48:09 -05:00
|
|
|
pub fn spawn_named_prop(
|
|
|
|
raws: &RawMaster,
|
|
|
|
new_entity: EntityBuilder,
|
|
|
|
key: &str,
|
|
|
|
pos: SpawnType,
|
|
|
|
) -> Option<Entity> {
|
|
|
|
if raws.prop_index.contains_key(key) {
|
|
|
|
let prop_template = &raws.raws.props[raws.prop_index[key]];
|
|
|
|
|
|
|
|
let mut eb = new_entity;
|
|
|
|
|
|
|
|
// Spawn in the specified location
|
|
|
|
eb = spawn_position(pos, eb);
|
|
|
|
|
|
|
|
// Renderable
|
|
|
|
if let Some(renderable) = &prop_template.renderable {
|
|
|
|
eb = eb.with(get_renderable_component(renderable));
|
|
|
|
}
|
|
|
|
|
|
|
|
eb = eb.with(Name::from(&prop_template.name));
|
|
|
|
|
|
|
|
if let Some(hidden) = prop_template.hidden {
|
|
|
|
if hidden {
|
|
|
|
eb = eb.with(Hidden {})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(blocks_tile) = prop_template.blocks_tile {
|
|
|
|
if blocks_tile {
|
|
|
|
eb = eb.with(BlocksTile {})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(blocks_visibility) = prop_template.blocks_visibility {
|
|
|
|
if blocks_visibility {
|
|
|
|
eb = eb.with(BlocksVisibility {})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(door_open) = prop_template.door_open {
|
|
|
|
eb = eb.with(Door { open: door_open });
|
|
|
|
}
|
|
|
|
if let Some(entry_trigger) = &prop_template.entry_trigger {
|
|
|
|
eb = eb.with(EntryTrigger {});
|
|
|
|
for effect in entry_trigger.effects.iter() {
|
|
|
|
match effect.0.as_str() {
|
|
|
|
"damage" => {
|
|
|
|
eb = eb.with(InflictsDamage {
|
|
|
|
damage: effect.1.parse::<i32>().unwrap(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
"single_activation" => eb = eb.with(SingleActivation {}),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Some(eb.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2021-12-23 12:31:03 -05:00
|
|
|
pub fn spawn_named_entity(
|
|
|
|
raws: &RawMaster,
|
|
|
|
new_entity: EntityBuilder,
|
|
|
|
key: &str,
|
|
|
|
pos: SpawnType,
|
|
|
|
) -> Option<Entity> {
|
|
|
|
if raws.item_index.contains_key(key) {
|
|
|
|
return spawn_named_item(raws, new_entity, key, pos);
|
|
|
|
} else if raws.mob_index.contains_key(key) {
|
|
|
|
return spawn_named_mob(raws, new_entity, key, pos);
|
2021-12-23 12:48:09 -05:00
|
|
|
} else if raws.prop_index.contains_key(key) {
|
|
|
|
return spawn_named_prop(raws, new_entity, key, pos);
|
2021-12-23 12:31:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2021-12-23 13:07:50 -05:00
|
|
|
|
|
|
|
pub fn get_spawn_table_for_depth(raws: &RawMaster, depth: i32) -> RandomTable {
|
|
|
|
use super::SpawnTableEntry;
|
|
|
|
|
|
|
|
let available_options: Vec<&SpawnTableEntry> = raws
|
|
|
|
.raws
|
|
|
|
.spawn_table
|
|
|
|
.iter()
|
|
|
|
.filter(|a| depth >= a.min_depth && depth <= a.max_depth)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut rt = RandomTable::new();
|
|
|
|
for e in available_options.iter() {
|
|
|
|
let mut weight = e.weight;
|
|
|
|
if e.add_map_depth_to_weight.is_some() {
|
|
|
|
weight += depth;
|
|
|
|
}
|
|
|
|
rt = rt.add(e.name.clone(), weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
rt
|
|
|
|
}
|