diff --git a/src/c_interface/static_data/item.rs b/src/c_interface/static_data/item.rs index eca9d1e..97447eb 100644 --- a/src/c_interface/static_data/item.rs +++ b/src/c_interface/static_data/item.rs @@ -29,12 +29,12 @@ ffi! { let v = slice::from_raw_parts(flags, flags_length as usize).to_vec(); let mut flags_map = HashSet::new(); for flag in v { - flags_map.insert(CStr::from_ptr(flag).to_str().unwrap().to_string()); + flags_map.insert(CStr::from_ptr(flag).to_str().unwrap().into()); } let handle = ItemHandle::alloc(ItemC { inner: thread_bound::DeferredCleanup::new(Item::new( - CStr::from_ptr(name).to_str().unwrap(), + &CStr::from_ptr(name).to_str().unwrap().into(), std::mem::transmute(category), std::mem::transmute(battle_category), price, diff --git a/src/dynamic_data/event_hooks/event_hook.rs b/src/dynamic_data/event_hooks/event_hook.rs index 03ef36e..66eb5e5 100644 --- a/src/dynamic_data/event_hooks/event_hook.rs +++ b/src/dynamic_data/event_hooks/event_hook.rs @@ -1,18 +1,20 @@ +use crate::dynamic_data::models::damage_source::DamageSource; use crate::dynamic_data::models::pokemon::Pokemon; +use crate::static_data::species_data::form::Form; +use crate::static_data::species_data::species::Species; use std::fmt::{Debug, Formatter}; -use std::sync::{Arc, RwLock}; #[derive(Default)] pub struct EventHook { evt_hook_function: Vec)>, } -impl EventHook { +impl<'a> EventHook { pub fn register_listener(&mut self, func: fn(&Box<&Event>)) { self.evt_hook_function.push(func); } - pub fn trigger(&self, evt: Event) { + pub fn trigger<'b>(&self, evt: Event<'a, 'b>) { let b = Box::new(&evt); for f in &self.evt_hook_function { f(&b); @@ -27,15 +29,33 @@ impl Debug for EventHook { } #[derive(Debug)] -pub enum Event<'a> { +pub enum Event<'a, 'b> { Switch { side_index: u8, index: u8, - pokemon: Option>>>, + pokemon: Option<&'a Pokemon<'b>>, }, Swap { side_index: u8, index_a: u8, index_b: u8, }, + SpeciesChange { + pokemon: &'a Pokemon<'b>, + species: &'a Species<'b>, + form: &'a Form<'b>, + }, + FormChange { + pokemon: &'a Pokemon<'b>, + form: &'a Form<'b>, + }, + Damage { + pokemon: &'a Pokemon<'b>, + source: DamageSource, + original_health: u32, + new_health: u32, + }, + Faint { + pokemon: &'a Pokemon<'b>, + }, } diff --git a/src/dynamic_data/flow/choice_queue.rs b/src/dynamic_data/flow/choice_queue.rs index 7b0bb0a..d758db9 100644 --- a/src/dynamic_data/flow/choice_queue.rs +++ b/src/dynamic_data/flow/choice_queue.rs @@ -1,2 +1,32 @@ +use crate::dynamic_data::models::pokemon::Pokemon; +use std::sync::Arc; + #[derive(Debug)] -pub struct ChoiceQueue {} +pub struct ChoiceQueue { + queue: Vec>, + current: usize, +} + +impl ChoiceQueue { + pub fn new(queue: Vec>) -> Self { + Self { queue, current: 0 } + } + + pub fn dequeue(&mut self) -> &Arc { + let c = &self.queue[self.current]; + self.current += 1; + c + } + + pub fn peek(&mut self) -> &Arc { + &self.queue[self.current] + } + + pub fn has_next(&self) -> bool { + self.current < self.queue.len() + } + + pub fn move_pokemon_choice_next(&mut self, _pokemon: &Pokemon) { + todo!() + } +} diff --git a/src/dynamic_data/libraries/dynamic_library.rs b/src/dynamic_data/libraries/dynamic_library.rs index 71a1fb8..0ca7e54 100644 --- a/src/dynamic_data/libraries/dynamic_library.rs +++ b/src/dynamic_data/libraries/dynamic_library.rs @@ -1,8 +1,10 @@ use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator; use crate::dynamic_data::libraries::script_resolver::ScriptCategory; +use crate::dynamic_data::script_handling::item_script::ItemScript; use crate::dynamic_data::script_handling::script::Script; +use crate::static_data::items::item::Item; use crate::static_data::libraries::static_data::StaticData; -use crate::PkmnResult; +use crate::{PkmnResult, StringKey}; #[derive(Debug)] pub struct DynamicLibrary<'a> { @@ -21,8 +23,12 @@ impl<'a> DynamicLibrary<'a> { pub fn load_script( &self, _category: ScriptCategory, - _key: &str, - ) -> PkmnResult> { + _key: &StringKey, + ) -> PkmnResult>> { + todo!() + } + + pub fn load_item_script(&self, _key: &Item) -> PkmnResult>> { todo!() } } diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index a3f97ad..fb084c7 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -9,7 +9,7 @@ use crate::dynamic_data::models::battle_side::BattleSide; use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; -use crate::{PkmnResult, ScriptCategory}; +use crate::{PkmnResult, ScriptCategory, StringKey}; use std::sync::{Arc, RwLock}; #[derive(Debug)] @@ -21,7 +21,7 @@ pub struct Battle<'a> { pokemon_per_side: u8, sides: Vec>, random: BattleRandom, - choice_queue: ChoiceQueue, + current_turn_queue: Option, has_ended: bool, result: BattleResult, event_hook: EventHook, @@ -54,7 +54,7 @@ impl<'a> Battle<'a> { pokemon_per_side, sides, random, - choice_queue: ChoiceQueue {}, + current_turn_queue: None, has_ended: false, result: BattleResult::Inconclusive, event_hook: Default::default(), @@ -89,6 +89,10 @@ impl<'a> Battle<'a> { pub fn sides(&self) -> &Vec> { &self.sides } + pub fn sides_mut(&mut self) -> &mut Vec> { + &mut self.sides + } + pub fn random(&self) -> &BattleRandom { &self.random } @@ -110,12 +114,51 @@ impl<'a> Battle<'a> { pub fn last_turn_time(&self) -> i64 { self.last_turn_time } - pub fn choice_queue(&self) -> &ChoiceQueue { - &self.choice_queue + pub fn current_turn_queue(&self) -> &Option { + &self.current_turn_queue } - pub fn can_slot_be_filled(&self) -> bool { - todo!() + pub fn can_slot_be_filled(&self, side: u8, index: u8) -> bool { + for party in &self.parties { + if party.is_responsible_for_index(side, index) && party.has_pokemon_not_in_field() { + return true; + } + } + false + } + + pub fn validate_battle_state(&mut self) { + if self.has_ended { + return; + } + let mut surviving_side_exists = false; + let mut winning_side = None; + for (side_index, side) in self.sides.iter().enumerate() { + // If any side has fled, the battle end. + if side.has_fled() { + self.result = BattleResult::Inconclusive; + self.has_ended = true; + return; + } + // If the side is not defeated + if !side.is_defeated() { + // More than 1 surviving side. Battle is not ended + if surviving_side_exists { + return; + } + surviving_side_exists = true; + winning_side = Some(side_index as u8); + } + } + // Everyone died :( + if !surviving_side_exists { + self.result = BattleResult::Inconclusive; + } + // Someone survived, they won! + else { + self.result = BattleResult::Conclusive(winning_side.unwrap()); + } + self.has_ended = true; } } @@ -124,7 +167,7 @@ impl<'a> VolatileScripts<'a> for Battle<'a> { &self.volatile } - fn load_volatile_script(&self, key: &str) -> PkmnResult> { + fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.library.load_script(ScriptCategory::Battle, key) } } diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 16a663f..7302f1b 100644 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -7,7 +7,8 @@ use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; use crate::dynamic_data::script_handling::ScriptSource; -use crate::{script_hook, PkmnResult}; +use crate::{script_hook, PkmnResult, StringKey}; +use std::ops::Deref; use std::sync::{Arc, RwLock, Weak}; #[derive(Debug)] @@ -83,7 +84,7 @@ impl<'a> BattleSide<'a> { /// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are /// empty, but can't be filled by any party anymore. pub fn all_slots_filled(&self) -> bool { - for pokemon in &self.pokemon { + for (i, pokemon) in self.pokemon.iter().enumerate() { if (!pokemon.is_none() || !pokemon.as_ref().unwrap().read().unwrap().is_usable()) && self .battle @@ -91,7 +92,7 @@ impl<'a> BattleSide<'a> { .unwrap() .read() .unwrap() - .can_slot_be_filled() + .can_slot_be_filled(self.index, i as u8) { return false; } @@ -146,7 +147,7 @@ impl<'a> BattleSide<'a> { battle.event_hook().trigger(Event::Switch { side_index: self.index, index, - pokemon: Some(pokemon_mutex.clone()), + pokemon: Some(&pokemon), }); script_hook!(on_switch_in, pokemon, &pokemon); } else { @@ -169,10 +170,10 @@ impl<'a> BattleSide<'a> { false } - pub fn mark_slot_as_unfillable(&mut self, pokemon: Arc>) { + pub fn mark_slot_as_unfillable(&mut self, pokemon: &Pokemon<'a>) { for (i, slot) in self.pokemon.iter().enumerate() { if let Some(p) = slot { - if p.read().unwrap().unique_identifier() == pokemon.unique_identifier() { + if p.read().unwrap().deref() as *const Pokemon == pokemon as *const Pokemon { self.fillable_slots[i] = false; return; } @@ -266,7 +267,7 @@ impl<'a> VolatileScripts<'a> for BattleSide<'a> { &self.volatile_scripts } - fn load_volatile_script(&self, key: &str) -> PkmnResult> { + fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.battle .upgrade() .unwrap() diff --git a/src/dynamic_data/models/damage_source.rs b/src/dynamic_data/models/damage_source.rs new file mode 100644 index 0000000..e5cbd91 --- /dev/null +++ b/src/dynamic_data/models/damage_source.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy)] +pub enum DamageSource { + AttackDamage = 0, + Misc = 1, +} diff --git a/src/dynamic_data/models/mod.rs b/src/dynamic_data/models/mod.rs index 4de1ad5..a1d1342 100644 --- a/src/dynamic_data/models/mod.rs +++ b/src/dynamic_data/models/mod.rs @@ -3,6 +3,7 @@ pub mod battle_party; pub mod battle_random; pub mod battle_result; pub mod battle_side; +pub mod damage_source; pub mod learned_move; pub mod pokemon; pub mod pokemon_party; diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 2a18726..231284c 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -1,6 +1,8 @@ use crate::defines::{LevelInt, MAX_MOVES}; +use crate::dynamic_data::event_hooks::event_hook::Event; use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary; use crate::dynamic_data::models::battle::Battle; +use crate::dynamic_data::models::damage_source::DamageSource; use crate::dynamic_data::models::learned_move::LearnedMove; use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; @@ -8,6 +10,7 @@ use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; use crate::dynamic_data::script_handling::ScriptSource; use crate::static_data::items::item::Item; use crate::static_data::natures::Nature; +use crate::static_data::species_data::ability::Ability; use crate::static_data::species_data::ability_index::AbilityIndex; use crate::static_data::species_data::form::Form; use crate::static_data::species_data::gender::Gender; @@ -15,7 +18,7 @@ use crate::static_data::species_data::species::Species; use crate::static_data::statistic_set::{ClampedStatisticSet, StatisticSet}; use crate::static_data::statistics::Statistic; use crate::utils::random::Random; -use crate::{PkmnResult, ScriptCategory}; +use crate::{script_hook, PkmnResult, ScriptCategory, StringKey}; use std::sync::{Arc, RwLock, Weak}; #[derive(Debug)] @@ -73,6 +76,7 @@ pub struct Pokemon<'a> { ability_index: AbilityIndex, is_ability_overridden: bool, + override_ability: Option, battle_data: Option>, @@ -80,8 +84,10 @@ pub struct Pokemon<'a> { allowed_experience: bool, types: Vec, + is_egg: bool, + is_caught: bool, - ability_script: Option>, + ability_script: Option>>, status_script: Option>, volatile: Arc>, } @@ -96,7 +102,7 @@ impl<'a> Pokemon<'a> { unique_identifier: u32, gender: Gender, coloring: u8, - nature: String, + nature: &StringKey, ) -> Pokemon<'a> { // Calculate experience from the level for the specified growth rate. let experience = library @@ -135,16 +141,18 @@ impl<'a> Pokemon<'a> { nickname: None, ability_index: ability, is_ability_overridden: false, + override_ability: None, battle_data: None, moves: [None, None, None, None], allowed_experience: false, types: form.types().to_vec(), + is_egg: false, + is_caught: false, ability_script: None, status_script: None, volatile: Default::default(), }; pokemon.recalculate_flat_stats(); - pokemon } @@ -190,6 +198,35 @@ impl<'a> Pokemon<'a> { pub fn held_item(&self) -> Option<&'a Item> { self.held_item } + pub fn has_held_item(&self, name: &StringKey) -> bool { + // Only true if we have an item, and the item name is the same as the requested item. + if let Some(v) = self.held_item { + return v.name() == name; + } + false + } + pub fn set_held_item(&mut self, item: &'a Item) { + self.held_item = Some(item); + } + pub fn remove_held_item(&mut self) { + self.held_item = None; + } + pub fn consume_held_item(&mut self) -> bool { + if self.held_item.is_none() { + return false; + } + let script = self + .library + .load_item_script(self.held_item.unwrap()) + .unwrap(); + if script.is_none() { + return false; + } + + // TODO: the entire item use part. + todo!(); + } + pub fn current_health(&self) -> u32 { self.current_health } @@ -249,9 +286,19 @@ impl<'a> Pokemon<'a> { pub fn is_ability_overriden(&self) -> bool { self.is_ability_overridden } - pub fn active_ability(&self) -> &Option> { + pub fn active_ability(&self) -> &Ability { + if self.is_ability_overridden { + if let Some(v) = &self.override_ability { + return v; + } + } + self.form.get_ability(self.ability_index) + } + + pub fn ability_script(&self) -> &Option>> { &self.ability_script } + pub fn seen_opponents(&self) -> Option<&Vec>>>> { if let Some(data) = &self.battle_data { Some(&data.seen_opponents) @@ -281,6 +328,7 @@ impl<'a> Pokemon<'a> { // If the pokemon is genderless, but it's new species is not, we want to set its gender if self.gender != Gender::Genderless && species.gender_rate() < 0.0 { + // If we're in battle, use the battle random for predictability if self.battle_data.is_some() { let battle_data = self.battle_data.as_mut().unwrap(); self.gender = species.get_random_gender( @@ -296,6 +344,7 @@ impl<'a> Pokemon<'a> { .unwrap(), ); } else { + // If we're not in battle, just use a new random. self.gender = species.get_random_gender(&mut Random::default()); } } @@ -303,11 +352,78 @@ impl<'a> Pokemon<'a> { else if species.gender_rate() < 0.0 && self.gender != Gender::Genderless { self.gender = Gender::Genderless; } - // TODO: Battle Event trigger + if let Some(battle_data) = &self.battle_data { + if let Some(battle) = battle_data.battle.upgrade() { + battle + .read() + .unwrap() + .event_hook() + .trigger(Event::SpeciesChange { + pokemon: self, + species, + form, + }) + } + } + } + + pub fn change_form(&mut self, form: &'a Form) { + if std::ptr::eq(self.form, form) { + return; + } + self.form = form; + + self.types.clear(); + for t in form.types() { + self.types.push(*t); + } + self.weight = form.weight(); + self.height = form.height(); + + let ability_script = self + .library + .load_script(ScriptCategory::Ability, self.active_ability().name()) + .unwrap(); + if let Some(ability_script) = ability_script { + self.ability_script = Some(Arc::new(ability_script)); + // Ensure the ability script gets initialized with the parameters for the ability. + self.ability_script() + .as_ref() + .unwrap() + .on_initialize(self.active_ability().parameters()) + } else { + self.ability_script = None; + } + let old_health = self.max_health(); + self.recalculate_flat_stats(); + let diff_health = (self.max_health() - old_health) as i32; + if self.current_health == 0 && (self.current_health as i32) < -diff_health { + self.current_health = 0; + } else { + self.current_health = self.current_health() + diff_health as u32; + } + // TODO: consider form specific attacks? + + if let Some(battle_data) = &self.battle_data { + if let Some(battle) = battle_data.battle.upgrade() { + battle + .read() + .unwrap() + .event_hook() + .trigger(Event::FormChange { + pokemon: self, + form, + }) + } + } } pub fn is_usable(&self) -> bool { - todo!() + !self.is_caught && !self.is_egg && !self.is_fainted() + } + + pub fn is_fainted(&self) -> bool { + self.current_health == 0 } pub fn set_battle_data(&mut self, battle: Weak>>, battle_side_index: u8) { @@ -360,6 +476,67 @@ impl<'a> Pokemon<'a> { battle_data.seen_opponents.push(pokemon); } } + + pub fn damage(&mut self, mut damage: u32, source: DamageSource) { + if damage > self.current_health { + damage = self.current_health; + } + if damage == 0 { + return; + } + let new_health = self.current_health() - damage; + if let Some(battle_data) = &self.battle_data { + if let Some(battle) = battle_data.battle.upgrade() { + battle.read().unwrap().event_hook().trigger(Event::Damage { + pokemon: self, + source, + original_health: self.current_health(), + new_health, + }); + // TODO: register history + script_hook!( + on_damage, + self, + self, + source, + self.current_health, + new_health + ); + } + } + self.current_health = new_health; + if self.is_fainted() && damage > 0 { + self.on_faint(source); + } + } + + pub fn on_faint(&self, source: DamageSource) { + if let Some(battle_data) = &self.battle_data { + if let Some(battle) = battle_data.battle.upgrade() { + battle + .read() + .unwrap() + .event_hook() + .trigger(Event::Faint { pokemon: self }); + script_hook!(on_faint, self, self, source); + script_hook!(on_remove, self,); + } + // TODO: Experience gain + + if let Some(battle) = battle_data.battle.upgrade() { + if !battle + .read() + .unwrap() + .can_slot_be_filled(battle_data.battle_side_index, battle_data.index) + { + let mut battle = battle.write().unwrap(); + let side = &mut battle.sides_mut()[battle_data.battle_side_index as usize]; + side.mark_slot_as_unfillable(self); + } + battle.write().unwrap().validate_battle_state(); + } + } + } } impl<'a> ScriptSource for Pokemon<'a> { @@ -373,7 +550,7 @@ impl<'a> VolatileScripts<'a> for Pokemon<'a> { &self.volatile } - fn load_volatile_script(&self, key: &str) -> PkmnResult> { + fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.library.load_script(ScriptCategory::Pokemon, key) } } @@ -389,8 +566,8 @@ pub mod test { #[test] fn construct_pokemon() { let lib = dynamic_library::test::build(); - let species = lib.static_data().species().get("foo").unwrap(); - let form = species.get_form("default").unwrap(); + let species = lib.static_data().species().get(&"foo".into()).unwrap(); + let form = species.get_form(&"default".into()).unwrap(); let pokemon = Pokemon::new( &lib, @@ -404,9 +581,9 @@ pub mod test { 0, Gender::Male, 0, - "test_nature".to_string(), + &"test_nature".into(), ); - assert_eq!(pokemon.species.name(), "foo"); - assert_eq!(pokemon.form.name(), "default"); + assert_eq!(pokemon.species.name(), &"foo".into()); + assert_eq!(pokemon.form.name(), &"default".into()); } } diff --git a/src/dynamic_data/script_handling/item_script.rs b/src/dynamic_data/script_handling/item_script.rs new file mode 100644 index 0000000..50fc844 --- /dev/null +++ b/src/dynamic_data/script_handling/item_script.rs @@ -0,0 +1 @@ +pub trait ItemScript {} diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index eefc1f8..5b96edc 100644 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -2,6 +2,7 @@ use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; use std::sync::Weak; +pub mod item_script; pub mod script; pub mod script_set; pub mod volatile_scripts; diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 26f3b05..0f7dcb4 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -1,8 +1,11 @@ +use crate::dynamic_data::models::damage_source::DamageSource; use crate::dynamic_data::models::pokemon::Pokemon; +use crate::static_data::moves::secondary_effect::EffectParameter; +use crate::StringKey; use std::fmt::{Debug, Formatter}; pub trait Script { - fn name(&self) -> &str; + fn name(&self) -> &StringKey; fn is_suppressed(&self) -> bool { self.get_suppressed_count() > 0 @@ -14,7 +17,7 @@ pub trait Script { // FIXME: add missing parameters fn stack(&self); fn on_remove(&self); - fn on_initialize(&self); + fn on_initialize(&self, pars: &Vec); fn on_before_turn(&self); fn change_speed(&self); fn change_priority(&self); @@ -52,9 +55,9 @@ pub trait Script { fn prevent_self_run_away(&self); fn prevent_opponent_run_away(&self); fn on_end_turn(&self); - fn on_damage(&self); - fn on_faint(&self); - fn on_switch_in<'b>(&self, pokemon: &'b Pokemon); + fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32); + fn on_faint(&self, pokemon: &Pokemon, source: DamageSource); + fn on_switch_in(&self, pokemon: &Pokemon); fn on_after_held_item_consume(&self); } diff --git a/src/dynamic_data/script_handling/script_set.rs b/src/dynamic_data/script_handling/script_set.rs index e09a4bb..ef7d562 100644 --- a/src/dynamic_data/script_handling/script_set.rs +++ b/src/dynamic_data/script_handling/script_set.rs @@ -1,11 +1,11 @@ use crate::dynamic_data::script_handling::script::Script; -use crate::PkmnResult; +use crate::{PkmnResult, StringKey}; use indexmap::IndexMap; use std::sync::Arc; #[derive(Debug, Default)] pub struct ScriptSet { - scripts: IndexMap>>, + scripts: IndexMap>>, } impl ScriptSet { @@ -15,33 +15,37 @@ impl ScriptSet { return existing.clone(); } let arc = Arc::new(script); - self.scripts.insert(arc.name().to_string(), arc.clone()); + self.scripts.insert(arc.name().clone(), arc.clone()); self.scripts.last().unwrap().1.clone() } pub fn stack_or_add<'b, F>( &mut self, - key: &str, + key: &StringKey, instantiation: &'b F, - ) -> PkmnResult>> + ) -> PkmnResult>>> where - F: Fn() -> PkmnResult>, + F: Fn() -> PkmnResult>>, { if let Some(existing) = self.scripts.get(key) { existing.stack(); - return Ok(existing.clone()); + return Ok(Some(existing.clone())); } let script = instantiation()?; - let arc = Arc::new(script); - self.scripts.insert(arc.name().to_string(), arc.clone()); - Ok(self.scripts.last().unwrap().1.clone()) + if let Some(script) = script { + let arc = Arc::new(script); + self.scripts.insert(arc.name().clone(), arc.clone()); + Ok(Some(self.scripts.last().unwrap().1.clone())) + } else { + Ok(None) + } } - pub fn get(&self, key: &str) -> Option<&Arc>> { + pub fn get(&self, key: &StringKey) -> Option<&Arc>> { self.scripts.get(key) } - pub fn remove(&mut self, key: &str) { + pub fn remove(&mut self, key: &StringKey) { let value = self.scripts.shift_remove(key); if let Some(script) = value { script.on_remove(); @@ -55,7 +59,7 @@ impl ScriptSet { self.scripts.clear(); } - pub fn has(&self, key: &str) -> bool { + pub fn has(&self, key: &StringKey) -> bool { self.scripts.contains_key(key) } diff --git a/src/dynamic_data/script_handling/volatile_scripts.rs b/src/dynamic_data/script_handling/volatile_scripts.rs index fe57fbd..d79e1f5 100644 --- a/src/dynamic_data/script_handling/volatile_scripts.rs +++ b/src/dynamic_data/script_handling/volatile_scripts.rs @@ -1,30 +1,30 @@ use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; -use crate::PkmnResult; +use crate::{PkmnResult, StringKey}; use std::sync::{Arc, RwLock}; pub trait VolatileScripts<'a> { fn volatile_scripts(&self) -> &Arc>; - fn load_volatile_script(&self, key: &str) -> PkmnResult>; + fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>>; - fn has_volatile_script(&self, key: &str) -> bool { + fn has_volatile_script(&self, key: &StringKey) -> bool { self.volatile_scripts().read().unwrap().has(key) } - fn get_volatile_script(&self, key: &str) -> Option>> { + fn get_volatile_script(&self, key: &StringKey) -> Option>> { let scripts = self.volatile_scripts().read().unwrap(); let s = scripts.get(key); s.cloned() } - fn add_volatile_script(&mut self, key: &str) -> PkmnResult>> { + fn add_volatile_script(&mut self, key: &StringKey) -> PkmnResult>>> { self.volatile_scripts() .write() .unwrap() .stack_or_add(key, &|| self.load_volatile_script(key)) } - fn remove_volatile_script(&mut self, key: &str) { + fn remove_volatile_script(&mut self, key: &StringKey) { self.volatile_scripts().write().unwrap().remove(key) } } diff --git a/src/lib.rs b/src/lib.rs index dae56da..f1af36d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,11 @@ #![feature(bench_black_box)] #![feature(let_chains)] -#[cfg(feature = "c_interface")] -#[macro_use] extern crate lazy_static; use crate::dynamic_data::libraries::script_resolver::ScriptCategory; +#[cfg(feature = "c_interface")] mod c_interface; pub mod defines; @@ -26,4 +25,6 @@ pub enum PokemonError { MiscError, } -pub type PkmnResult = std::result::Result; +pub type PkmnResult = Result; + +pub use utils::*; diff --git a/src/static_data/items/item.rs b/src/static_data/items/item.rs index cbaa428..220084d 100644 --- a/src/static_data/items/item.rs +++ b/src/static_data/items/item.rs @@ -1,25 +1,26 @@ use super::item_category::{BattleItemCategory, ItemCategory}; +use crate::StringKey; use std::collections::HashSet; #[derive(Debug)] pub struct Item { - name: String, + name: StringKey, category: ItemCategory, battle_category: BattleItemCategory, price: i32, - flags: HashSet, + flags: HashSet, } impl Item { pub fn new( - name: &str, + name: &StringKey, category: ItemCategory, battle_category: BattleItemCategory, price: i32, - flags: HashSet, + flags: HashSet, ) -> Item { Item { - name: name.to_string(), + name: name.clone(), category, battle_category, price, @@ -27,7 +28,7 @@ impl Item { } } - pub fn name(&self) -> &str { + pub fn name(&self) -> &StringKey { &self.name } pub fn category(&self) -> ItemCategory { @@ -39,11 +40,11 @@ impl Item { pub fn price(&self) -> i32 { self.price } - pub fn flags(&self) -> &HashSet { + pub fn flags(&self) -> &HashSet { &self.flags } - pub fn has_flag(&self, key: &str) -> bool { + pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } } diff --git a/src/static_data/items/item_category.rs b/src/static_data/items/item_category.rs index d0007a5..8c0be59 100644 --- a/src/static_data/items/item_category.rs +++ b/src/static_data/items/item_category.rs @@ -6,7 +6,7 @@ pub enum ItemCategory { Medicine, Berry, TMHM, - FormeChanger, + FormChanger, KeyItem, Mail, } diff --git a/src/static_data/libraries/ability_library.rs b/src/static_data/libraries/ability_library.rs new file mode 100644 index 0000000..36475c8 --- /dev/null +++ b/src/static_data/libraries/ability_library.rs @@ -0,0 +1,56 @@ +use crate::static_data::libraries::data_library::DataLibrary; +use crate::static_data::species_data::ability::Ability; +use crate::StringKey; +use hashbrown::HashMap; + +#[derive(Debug)] +pub struct AbilityLibrary { + map: HashMap>, + list: Vec, +} + +impl AbilityLibrary { + pub fn new(capacity: usize) -> AbilityLibrary { + AbilityLibrary { + map: HashMap::with_capacity(capacity), + list: Vec::with_capacity(capacity), + } + } +} + +impl DataLibrary<'_, Box> for AbilityLibrary { + fn map(&self) -> &HashMap> { + &self.map + } + + fn list_values(&self) -> &Vec { + &self.list + } + + fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { + (&mut self.map, &mut self.list) + } +} + +#[cfg(test)] +pub mod tests { + use crate::static_data::libraries::ability_library::AbilityLibrary; + use crate::static_data::libraries::data_library::DataLibrary; + use crate::static_data::species_data::ability::Ability; + use crate::StringKey; + + pub fn build() -> AbilityLibrary { + let mut lib = AbilityLibrary::new(1); + lib.add( + &StringKey::new("test_ability"), + Box::new(Ability::new( + &"test_ability".into(), + &"test_ability".into(), + Vec::new(), + )), + ); + // Drops borrow as mut + + lib + } +} diff --git a/src/static_data/libraries/data_library.rs b/src/static_data/libraries/data_library.rs index 6fe1d3e..61724ed 100644 --- a/src/static_data/libraries/data_library.rs +++ b/src/static_data/libraries/data_library.rs @@ -1,29 +1,30 @@ use crate::utils::random::Random; +use crate::StringKey; use hashbrown::HashMap; pub trait DataLibrary<'a, T: 'a> { - fn map(&self) -> &HashMap; - fn list_values(&self) -> &Vec; - fn get_modify(&mut self) -> (&mut HashMap, &mut Vec); + fn map(&self) -> &HashMap; + fn list_values(&self) -> &Vec; + fn get_modify(&mut self) -> (&mut HashMap, &mut Vec); - fn add(&mut self, key: &str, value: T) { + fn add(&mut self, key: &StringKey, value: T) { let modifies = self.get_modify(); - modifies.0.insert(key.to_string(), value); - modifies.1.push(key.to_string()); + modifies.0.insert(key.clone(), value); + modifies.1.push(key.clone()); } - fn remove(&mut self, key: &str) { + fn remove(&mut self, key: &StringKey) { let modifies = self.get_modify(); - let index = modifies.1.iter().position(|r| *r == key).unwrap(); + let index = modifies.1.iter().position(|r| r == key).unwrap(); modifies.0.remove(key); modifies.1.remove(index); } - fn get(&self, key: &str) -> Option<&T> { + fn get(&self, key: &StringKey) -> Option<&T> { self.map().get(key) } - fn get_mut(&mut self, key: &str) -> Option<&mut T> { + fn get_mut(&mut self, key: &StringKey) -> Option<&mut T> { self.get_modify().0.get_mut(key) } diff --git a/src/static_data/libraries/growth_rate_library.rs b/src/static_data/libraries/growth_rate_library.rs index e025b1c..161e3d9 100644 --- a/src/static_data/libraries/growth_rate_library.rs +++ b/src/static_data/libraries/growth_rate_library.rs @@ -1,11 +1,12 @@ use crate::defines::LevelInt; use crate::static_data::growth_rates::growth_rate::GrowthRate; +use crate::StringKey; use hashbrown::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; pub struct GrowthRateLibrary { - growth_rates: HashMap>, + growth_rates: HashMap>, } impl GrowthRateLibrary { @@ -15,14 +16,14 @@ impl GrowthRateLibrary { } } - pub fn calculate_level(&self, growth_rate: &str, experience: u32) -> LevelInt { + pub fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt { self.growth_rates[growth_rate].calculate_level(experience) } - pub fn calculate_experience(&self, growth_rate: &str, level: LevelInt) -> u32 { + pub fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> u32 { self.growth_rates[growth_rate].calculate_experience(level) } - pub fn add_growth_rate(&mut self, key: &str, value: Box) { - self.growth_rates.insert(key.to_string(), value); + pub fn add_growth_rate(&mut self, key: &StringKey, value: Box) { + self.growth_rates.insert(key.clone(), value); } } @@ -43,7 +44,7 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; w.add_growth_rate( - "test_growthrate", + &"test_growthrate".into(), Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])), ); // Drops borrow as mut @@ -54,14 +55,14 @@ pub mod tests { #[test] fn add_growth_rate_to_library_and_calculate_level() { let lib = build(); - assert_eq!(lib.calculate_level("test_growthrate", 3), 1); - assert_eq!(lib.calculate_level("test_growthrate", 50), 3); + assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3), 1); + assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50), 3); } #[test] fn add_growth_rate_to_library_and_calculate_experience() { let lib = build(); - assert_eq!(lib.calculate_experience("test_growthrate", 1), 0); - assert_eq!(lib.calculate_experience("test_growthrate", 3), 10); + assert_eq!(lib.calculate_experience(&"test_growthrate".into(), 1), 0); + assert_eq!(lib.calculate_experience(&"test_growthrate".into(), 3), 10); } } diff --git a/src/static_data/libraries/item_library.rs b/src/static_data/libraries/item_library.rs index c1fc5ef..bfca852 100644 --- a/src/static_data/libraries/item_library.rs +++ b/src/static_data/libraries/item_library.rs @@ -1,11 +1,12 @@ use crate::static_data::items::item::Item; use crate::static_data::libraries::data_library::DataLibrary; +use crate::StringKey; use hashbrown::HashMap; #[derive(Debug)] pub struct ItemLibrary { - map: HashMap>, - list: Vec, + map: HashMap>, + list: Vec, } impl ItemLibrary { @@ -18,15 +19,15 @@ impl ItemLibrary { } impl DataLibrary<'_, Box> for ItemLibrary { - fn map(&self) -> &HashMap> { + fn map(&self) -> &HashMap> { &self.map } - fn list_values(&self) -> &Vec { + fn list_values(&self) -> &Vec { &self.list } - fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { + fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { (&mut self.map, &mut self.list) } } @@ -41,7 +42,7 @@ pub mod tests { fn build_item() -> Item { Item::new( - "foo", + &"foo".into(), ItemCategory::MiscItem, BattleItemCategory::MiscBattleItem, 100, @@ -54,7 +55,7 @@ pub mod tests { let m = build_item(); // Borrow as mut so we can insert let w = &mut lib; - w.add("foo", Box::from(m)); + w.add(&"foo".into(), Box::from(m)); // Drops borrow as mut lib diff --git a/src/static_data/libraries/mod.rs b/src/static_data/libraries/mod.rs index 192b4a4..695211d 100644 --- a/src/static_data/libraries/mod.rs +++ b/src/static_data/libraries/mod.rs @@ -1,3 +1,4 @@ +pub mod ability_library; pub mod data_library; pub mod growth_rate_library; pub mod item_library; diff --git a/src/static_data/libraries/move_library.rs b/src/static_data/libraries/move_library.rs index e60d0b4..ec7149a 100644 --- a/src/static_data/libraries/move_library.rs +++ b/src/static_data/libraries/move_library.rs @@ -1,11 +1,12 @@ use crate::static_data::libraries::data_library::DataLibrary; use crate::static_data::moves::move_data::MoveData; +use crate::StringKey; use hashbrown::HashMap; #[derive(Debug)] pub struct MoveLibrary { - map: HashMap, - list: Vec, + map: HashMap, + list: Vec, } impl MoveLibrary { @@ -18,15 +19,15 @@ impl MoveLibrary { } impl DataLibrary<'_, MoveData> for MoveLibrary { - fn map(&self) -> &HashMap { + fn map(&self) -> &HashMap { &self.map } - fn list_values(&self) -> &Vec { + fn list_values(&self) -> &Vec { &self.list } - fn get_modify(&mut self) -> (&mut HashMap, &mut Vec) { + fn get_modify(&mut self) -> (&mut HashMap, &mut Vec) { (&mut self.map, &mut self.list) } } @@ -37,11 +38,12 @@ pub mod tests { use crate::static_data::libraries::move_library::MoveLibrary; use crate::static_data::moves::move_data::{MoveCategory, MoveData, MoveTarget}; use crate::static_data::moves::secondary_effect::SecondaryEffect; + use crate::StringKey; use std::collections::HashSet; fn build_move() -> MoveData { MoveData::new( - "foo", + &"foo".into(), 0, MoveCategory::Physical, 100, @@ -59,7 +61,7 @@ pub mod tests { let m = build_move(); // Borrow as mut so we can insert let w = &mut lib; - w.add("foo", m); + w.add(&StringKey::new("foo"), m); // Drops borrow as mut lib diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index 5534008..033215a 100644 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -1,11 +1,12 @@ use crate::static_data::libraries::data_library::DataLibrary; use crate::static_data::species_data::species::Species; +use crate::StringKey; use hashbrown::HashMap; #[derive(Debug)] pub struct SpeciesLibrary<'a> { - map: HashMap>>, - list: Vec, + map: HashMap>>, + list: Vec, } impl<'a> SpeciesLibrary<'a> { @@ -18,15 +19,20 @@ impl<'a> SpeciesLibrary<'a> { } impl<'a> DataLibrary<'a, Box>> for SpeciesLibrary<'a> { - fn map(&self) -> &HashMap>> { + fn map(&self) -> &HashMap>> { &self.map } - fn list_values(&self) -> &Vec { + fn list_values(&self) -> &Vec { &self.list } - fn get_modify(&mut self) -> (&mut HashMap>>, &mut Vec) { + fn get_modify( + &mut self, + ) -> ( + &mut HashMap>>, + &mut Vec, + ) { (&mut self.map, &mut self.list) } } @@ -44,12 +50,12 @@ pub mod tests { fn build_species<'a>() -> Species<'a> { Species::new( 0, - "foo", + &"foo".into(), 0.5, - "test_growthrate", + &"test_growthrate".into(), 0, Form::new( - "default", + &"default".into(), 0.0, 0.0, 0, @@ -69,7 +75,7 @@ pub mod tests { let species = build_species(); // Borrow as mut so we can insert let w = &mut lib; - w.add("foo", Box::from(species)); + w.add(&"foo".into(), Box::from(species)); // Drops borrow as mut lib @@ -81,10 +87,10 @@ pub mod tests { // Borrow as read so we can read let r = &lib; - let mon = r.get("foo"); + let mon = r.get(&"foo".into()); assert!(mon.is_some()); assert_eq!(mon.unwrap().id(), 0_u16); - assert_eq!(mon.unwrap().as_ref().name(), "foo"); + assert_eq!(mon.unwrap().as_ref().name(), &"foo".into()); assert_eq!(r.len(), 1); } @@ -92,11 +98,11 @@ pub mod tests { fn add_species_to_library_then_remove() { let mut lib = build(); - lib.remove("foo"); + lib.remove(&"foo".into()); // Borrow as read so we can read let r = &lib; - let mon = r.get("foo"); + let mon = r.get(&"foo".into()); assert!(mon.is_none()); assert_eq!(r.len(), 0); } diff --git a/src/static_data/libraries/static_data.rs b/src/static_data/libraries/static_data.rs index 4adcefe..e326585 100644 --- a/src/static_data/libraries/static_data.rs +++ b/src/static_data/libraries/static_data.rs @@ -1,3 +1,4 @@ +use crate::static_data::libraries::ability_library::AbilityLibrary; use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary; use crate::static_data::libraries::item_library::ItemLibrary; use crate::static_data::libraries::library_settings::LibrarySettings; @@ -15,6 +16,7 @@ pub struct StaticData<'a> { growth_rates: GrowthRateLibrary, types: TypeLibrary, natures: NatureLibrary, + abilities: AbilityLibrary, } impl<'a> StaticData<'a> { @@ -26,6 +28,7 @@ impl<'a> StaticData<'a> { growth_rates: GrowthRateLibrary, types: TypeLibrary, natures: NatureLibrary, + abilities: AbilityLibrary, ) -> Self { Self { settings, @@ -35,6 +38,7 @@ impl<'a> StaticData<'a> { growth_rates, types, natures, + abilities, } } @@ -59,6 +63,9 @@ impl<'a> StaticData<'a> { pub fn natures(&self) -> &NatureLibrary { &self.natures } + pub fn abilities(&self) -> &AbilityLibrary { + &self.abilities + } } #[cfg(test)] @@ -66,7 +73,8 @@ pub mod test { use crate::static_data::libraries::library_settings::LibrarySettings; use crate::static_data::libraries::static_data::StaticData; use crate::static_data::libraries::{ - growth_rate_library, item_library, move_library, species_library, type_library, + ability_library, growth_rate_library, item_library, move_library, species_library, + type_library, }; use crate::static_data::natures; @@ -79,6 +87,7 @@ pub mod test { growth_rates: growth_rate_library::tests::build(), types: type_library::tests::build(), natures: natures::tests::build(), + abilities: ability_library::tests::build(), } } } diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index 05d37d7..999c260 100644 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -1,8 +1,9 @@ +use crate::StringKey; use hashbrown::HashMap; #[derive(Debug)] pub struct TypeLibrary { - types: HashMap, + types: HashMap, effectiveness: Vec>, } @@ -14,7 +15,7 @@ impl TypeLibrary { } } - pub fn get_type_id(&self, key: &str) -> u8 { + pub fn get_type_id(&self, key: &StringKey) -> u8 { self.types[key] } @@ -30,9 +31,9 @@ impl TypeLibrary { e } - pub fn register_type(&mut self, name: &str) -> u8 { + pub fn register_type(&mut self, name: &StringKey) -> u8 { let id = self.types.len() as u8; - self.types.insert(name.to_string(), id); + self.types.insert(name.clone(), id); self.effectiveness.resize((id + 1) as usize, vec![]); for effectiveness in &mut self.effectiveness { effectiveness.resize((id + 1) as usize, 1.0) @@ -55,8 +56,8 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type("foo"); - w.register_type("bar"); + w.register_type(&"foo".into()); + w.register_type(&"bar".into()); // Drops borrow as mut w.set_effectiveness(0, 1, 0.5); @@ -71,14 +72,14 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type("foo"); - w.register_type("bar"); + w.register_type(&"foo".into()); + w.register_type(&"bar".into()); // Drops borrow as mut // Borrow as read so we can read let r = &lib; - assert_eq!(r.get_type_id("foo"), 0); - assert_eq!(r.get_type_id("bar"), 1); + assert_eq!(r.get_type_id(&"foo".into()), 0); + assert_eq!(r.get_type_id(&"bar".into()), 1); } #[test] @@ -87,8 +88,8 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type("foo"); - w.register_type("bar"); + w.register_type(&"foo".into()); + w.register_type(&"bar".into()); w.set_effectiveness(0, 1, 0.5); w.set_effectiveness(1, 0, 2.0); // Drops borrow as mut @@ -105,8 +106,8 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type("foo"); - w.register_type("bar"); + w.register_type(&"foo".into()); + w.register_type(&"bar".into()); w.set_effectiveness(0, 1, 0.5); w.set_effectiveness(1, 0, 2.0); // Drops borrow as mut diff --git a/src/static_data/moves/move_data.rs b/src/static_data/moves/move_data.rs index a210588..487cf56 100644 --- a/src/static_data/moves/move_data.rs +++ b/src/static_data/moves/move_data.rs @@ -1,4 +1,5 @@ use self::super::secondary_effect::SecondaryEffect; +use crate::StringKey; use std::collections::HashSet; #[derive(PartialEq, Debug)] @@ -30,7 +31,7 @@ pub enum MoveTarget { #[derive(PartialEq, Debug)] pub struct MoveData { - name: String, + name: StringKey, move_type: u8, category: MoveCategory, base_power: u8, @@ -44,7 +45,7 @@ pub struct MoveData { impl MoveData { pub fn new( - name: &str, + name: &StringKey, move_type: u8, category: MoveCategory, base_power: u8, @@ -56,7 +57,7 @@ impl MoveData { flags: HashSet, ) -> MoveData { MoveData { - name: name.to_string(), + name: name.clone(), move_type, category, base_power, diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs index af627e3..9d3f942 100644 --- a/src/static_data/natures.rs +++ b/src/static_data/natures.rs @@ -1,4 +1,5 @@ use crate::static_data::statistics::Statistic; +use crate::StringKey; use hashbrown::HashMap; #[derive(Debug)] @@ -45,7 +46,7 @@ impl Nature { #[derive(Debug)] pub struct NatureLibrary { - map: HashMap, + map: HashMap, } impl NatureLibrary { @@ -55,20 +56,20 @@ impl NatureLibrary { } } - pub fn load_nature(&mut self, name: &str, nature: Nature) { - self.map.insert(name.to_string(), nature); + pub fn load_nature(&mut self, name: StringKey, nature: Nature) { + self.map.insert(name, nature); } - pub fn get_nature(&self, key: &str) -> Option<&Nature> { + pub fn get_nature(&self, key: &StringKey) -> Option<&Nature> { self.map.get(key) } - pub fn get_nature_name(&self, nature: &Nature) -> String { + pub fn get_nature_name(&self, nature: &Nature) -> StringKey { for kv in &self.map { // As natures can't be copied, and should always be the same reference as the value // in the map, we just compare by reference. if (kv.1 as *const Nature) == (nature as *const Nature) { - return kv.0.to_string(); + return kv.0.clone(); } } panic!("No name was found for the given nature. This should never happen."); @@ -84,7 +85,7 @@ pub mod tests { let mut lib = NatureLibrary::new(2); lib.load_nature( - "test_nature", + "test_nature".into(), Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); @@ -95,14 +96,14 @@ pub mod tests { fn create_nature_library_insert_and_retrieve() { let mut lib = NatureLibrary::new(2); lib.load_nature( - "foo", + "foo".into(), Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); lib.load_nature( - "bar", + "bar".into(), Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), ); - let n1 = lib.get_nature("foo").expect("Nature was not found"); + let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found"); assert_eq!(n1.increase_stat, Statistic::HP); assert_eq!(n1.decrease_stat, Statistic::Attack); assert_eq!(n1.increase_modifier, 1.1); @@ -113,19 +114,19 @@ pub mod tests { fn create_nature_library_insert_and_get_name() { let mut lib = NatureLibrary::new(2); lib.load_nature( - "foo", + "foo".into(), Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); lib.load_nature( - "bar", + "bar".into(), Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), ); - let n1 = lib.get_nature("foo").expect("Nature was not found"); + let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found"); let name = lib.get_nature_name(n1); - assert_eq!(name, "foo"); - let n2 = lib.get_nature("bar").expect("Nature was not found"); + assert_eq!(name, "foo".into()); + let n2 = lib.get_nature(&"bar".into()).expect("Nature was not found"); let name2 = lib.get_nature_name(n2); - assert_eq!(name2, "bar"); + assert_eq!(name2, "bar".into()); } } diff --git a/src/static_data/species_data/ability.rs b/src/static_data/species_data/ability.rs new file mode 100644 index 0000000..70328e6 --- /dev/null +++ b/src/static_data/species_data/ability.rs @@ -0,0 +1,29 @@ +use crate::static_data::moves::secondary_effect::EffectParameter; +use crate::StringKey; + +#[derive(Debug)] +pub struct Ability { + name: StringKey, + effect: StringKey, + parameters: Vec, +} + +impl Ability { + pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec) -> Self { + Self { + name: name.clone(), + effect: effect.clone(), + parameters, + } + } + + pub fn name(&self) -> &StringKey { + &self.name + } + pub fn effect(&self) -> &StringKey { + &self.effect + } + pub fn parameters(&self) -> &Vec { + &self.parameters + } +} diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index c8beaff..b8c23be 100644 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -1,39 +1,42 @@ use self::super::learnable_moves::LearnableMoves; +use crate::static_data::species_data::ability::Ability; use crate::static_data::species_data::ability_index::AbilityIndex; use crate::static_data::statistic_set::StatisticSet; use crate::static_data::statistics::Statistic; use crate::utils::random::Random; +use crate::StringKey; use hashbrown::HashSet; +use std::ops::Deref; #[derive(Debug)] pub struct Form<'a> { - name: String, + name: StringKey, height: f32, weight: f32, base_experience: u32, types: Vec, base_stats: StatisticSet, - abilities: Vec, - hidden_abilities: Vec, + abilities: Vec<&'a Ability>, + hidden_abilities: Vec<&'a Ability>, moves: LearnableMoves<'a>, - flags: HashSet, + flags: HashSet, } impl<'a> Form<'a> { pub fn new( - name: &str, + name: &StringKey, height: f32, weight: f32, base_experience: u32, types: Vec, base_stats: StatisticSet, - abilities: Vec, - hidden_abilities: Vec, + abilities: Vec<&'a Ability>, + hidden_abilities: Vec<&'a Ability>, moves: LearnableMoves<'a>, - flags: HashSet, + flags: HashSet, ) -> Form<'a> { Form { - name: name.to_string(), + name: name.clone(), height, weight, base_experience, @@ -46,7 +49,7 @@ impl<'a> Form<'a> { } } - pub fn name(&self) -> &str { + pub fn name(&self) -> &StringKey { &self.name } pub fn height(&self) -> f32 { @@ -64,16 +67,16 @@ impl<'a> Form<'a> { pub fn base_stats(&self) -> StatisticSet { self.base_stats } - pub fn abilities(&self) -> &Vec { + pub fn abilities(&self) -> &Vec<&'a Ability> { &self.abilities } - pub fn hidden_abilities(&self) -> &Vec { + pub fn hidden_abilities(&self) -> &Vec<&'a Ability> { &self.hidden_abilities } pub fn moves(&self) -> &LearnableMoves<'a> { &self.moves } - pub fn flags(&self) -> &HashSet { + pub fn flags(&self) -> &HashSet { &self.flags } @@ -85,9 +88,9 @@ impl<'a> Form<'a> { self.base_stats.get_stat(stat) } - pub fn find_ability_index(&self, ability: &str) -> Option { + pub fn find_ability_index(&self, ability: &Ability) -> Option { for (index, a) in self.abilities.iter().enumerate() { - if a == ability { + if std::ptr::eq(a.deref(), ability as *const Ability) { return Some(AbilityIndex { hidden: false, index: index as u8, @@ -95,7 +98,7 @@ impl<'a> Form<'a> { } } for (index, a) in self.hidden_abilities.iter().enumerate() { - if a == ability { + if std::ptr::eq(a.deref(), ability as *const Ability) { return Some(AbilityIndex { hidden: true, index: index as u8, @@ -105,15 +108,23 @@ impl<'a> Form<'a> { None } - pub fn get_random_ability(&self, rand: &mut Random) -> &String { - &self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize] + pub fn get_ability(&self, index: AbilityIndex) -> &Ability { + if index.hidden { + self.hidden_abilities[index.index as usize] + } else { + self.abilities[index.index as usize] + } } - pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &String { - &self.hidden_abilities + + pub fn get_random_ability(&self, rand: &mut Random) -> &Ability { + self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize] + } + pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &Ability { + self.hidden_abilities [rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize] } - pub fn has_flag(&self, key: &str) -> bool { + pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } } diff --git a/src/static_data/species_data/learnable_moves.rs b/src/static_data/species_data/learnable_moves.rs index 72dc34b..1106738 100644 --- a/src/static_data/species_data/learnable_moves.rs +++ b/src/static_data/species_data/learnable_moves.rs @@ -46,7 +46,7 @@ mod tests { #[test] fn adds_level_moves() { let move1 = MoveData::new( - "foo", + &"foo".into(), 0, MoveCategory::Physical, 0, @@ -58,7 +58,7 @@ mod tests { Default::default(), ); let move2 = MoveData::new( - "bar", + &"bar".into(), 0, MoveCategory::Physical, 0, @@ -83,7 +83,7 @@ mod tests { #[test] fn adds_two_same_moves_at_different_level() { let move1 = MoveData::new( - "foo", + &"foo".into(), 0, MoveCategory::Physical, 0, diff --git a/src/static_data/species_data/mod.rs b/src/static_data/species_data/mod.rs index b9dc147..4444e1a 100644 --- a/src/static_data/species_data/mod.rs +++ b/src/static_data/species_data/mod.rs @@ -3,3 +3,4 @@ pub mod form; pub mod gender; pub mod learnable_moves; pub mod species; +pub mod ability; diff --git a/src/static_data/species_data/species.rs b/src/static_data/species_data/species.rs index 2313a39..d018917 100644 --- a/src/static_data/species_data/species.rs +++ b/src/static_data/species_data/species.rs @@ -1,36 +1,40 @@ use self::super::form::Form; use crate::static_data::species_data::gender::Gender; use crate::utils::random::Random; +use crate::StringKey; use hashbrown::{HashMap, HashSet}; #[derive(Debug)] pub struct Species<'a> { id: u16, - name: String, + name: StringKey, gender_rate: f32, - growth_rate: String, + growth_rate: StringKey, capture_rate: u8, - forms: HashMap>, - flags: HashSet, + forms: HashMap>, + flags: HashSet, +} +lazy_static::lazy_static! { + static ref DEFAULT_KEY: StringKey = StringKey::new("default"); } impl<'a> Species<'a> { pub fn new( id: u16, - name: &str, + name: &StringKey, gender_rate: f32, - growth_rate: &str, + growth_rate: &StringKey, capture_rate: u8, default_form: Form<'a>, - flags: HashSet, + flags: HashSet, ) -> Species<'a> { let mut forms = HashMap::with_capacity(1); - forms.insert("default".to_string(), default_form); + forms.insert_unique_unchecked(DEFAULT_KEY.clone(), default_form); Species { id, - name: name.to_string(), + name: name.clone(), gender_rate, - growth_rate: growth_rate.to_string(), + growth_rate: growth_rate.clone(), capture_rate, forms, flags, @@ -39,33 +43,37 @@ impl<'a> Species<'a> { pub fn id(&self) -> u16 { self.id } - pub fn name(&self) -> &str { + pub fn name(&self) -> &StringKey { &self.name } pub fn gender_rate(&self) -> f32 { self.gender_rate } - pub fn growth_rate(&self) -> &str { + pub fn growth_rate(&self) -> &StringKey { &self.growth_rate } pub fn capture_rate(&self) -> u8 { self.capture_rate } - pub fn forms(&self) -> &HashMap> { + pub fn forms(&self) -> &HashMap> { &self.forms } - pub fn flags(&self) -> &HashSet { + pub fn flags(&self) -> &HashSet { &self.flags } - pub fn add_form(&mut self, id: String, form: Form<'a>) { + pub fn add_form(&mut self, id: StringKey, form: Form<'a>) { self.forms.insert(id, form); } - pub fn get_form(&self, id: &str) -> Option<&Form> { + pub fn get_form(&self, id: &StringKey) -> Option<&Form> { self.forms.get(id) } + pub fn get_default_form(&self) -> &Form { + self.forms.get(&DEFAULT_KEY).unwrap() + } + pub fn get_random_gender(&self, rand: &mut Random) -> Gender { if self.gender_rate < 0.0 { Gender::Genderless @@ -76,7 +84,7 @@ impl<'a> Species<'a> { } } - pub fn has_flag(&self, key: &str) -> bool { + pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7bcbfe0..d8e60a6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1 +1,5 @@ pub mod random; +pub mod string_key; + +pub use random::Random; +pub use string_key::StringKey; diff --git a/src/utils/string_key.rs b/src/utils/string_key.rs new file mode 100644 index 0000000..4ff16d9 --- /dev/null +++ b/src/utils/string_key.rs @@ -0,0 +1,111 @@ +use std::hash::{Hash, Hasher}; +use std::sync::Arc; + +/// StringKey is an immutable string that is used for indexing of hashmaps or equality a lot. +/// By reference counting the string instead of copying, and caching the hash, we can get some +/// free speed out of it. Note that StringKeys also compare case insensitive, so that for example +/// `charmander` == `Charmander`. +#[derive(Clone, Debug)] +pub struct StringKey { + str: Arc, + hash: u32, +} + +impl StringKey { + pub const fn get_hash_const(s: &[u8; N]) -> u32 { + let mut crc: u32 = 0xffffffff; + + let mut i: usize = 0; + while i < N { + crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s[i]) as u32)) & 0xff) as usize]; + i += 1; + } + crc ^ 0xffffffff + } + + pub fn get_hash(s: &str) -> u32 { + let mut crc: u32 = 0xffffffff; + for byte in s.bytes() { + crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(byte) as u32)) & 0xff) as usize]; + } + crc ^ 0xffffffff + } + + pub fn new(s: &str) -> Self { + let hash = StringKey::get_hash(s); + Self { + str: Arc::new(s.to_string()), + hash, + } + } + + pub fn str(&self) -> &str { + self.str.as_str() + } + + pub fn hash(&self) -> u32 { + self.hash + } +} + +impl PartialEq for StringKey { + fn eq(&self, other: &Self) -> bool { + self.hash == other.hash + } +} + +impl Eq for StringKey {} + +impl Hash for StringKey { + fn hash(&self, state: &mut H) { + self.hash.hash(state); + } +} + +impl From<&str> for StringKey { + fn from(s: &str) -> Self { + StringKey::new(s) + } +} + +const fn to_lower(c: u8) -> u8 { + if c >= b'A' && c <= b'Z' { + return c + (b'a' - b'A'); + } + c +} + +const CRC_TABLE: &[u32] = &[ + 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, +];