More documentation.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-07-01 17:07:22 +02:00
parent 03f5e3bb5a
commit 8f6ecdd4ad
35 changed files with 721 additions and 262 deletions

View File

@@ -3,10 +3,10 @@ use std::sync::Arc;
use parking_lot::RwLock;
use crate::dynamic_data::LearnedMove;
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::{LearnedMove, ScriptWrapper};
use crate::dynamic_data::{ScriptSource, ScriptSourceData};
/// The data on a turn choice that should be contained in every turn choice, regardless of type.
#[derive(Debug)]

View File

@@ -50,13 +50,22 @@ fn get_all_adjacent_opponent<'b, 'library>(
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, left as u8).as_ref().cloned(),
battle.get_pokemon(side, right).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), left as u8)
.as_ref()
.cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
battle.get_pokemon(get_opposite_side(side), right).as_ref().cloned(),
]
} else {
vec![
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, left as u8).as_ref().cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), left as u8)
.as_ref()
.cloned(),
]
};
}
@@ -65,6 +74,7 @@ fn get_all_adjacent_opponent<'b, 'library>(
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, right).as_ref().cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
battle.get_pokemon(get_opposite_side(side), right).as_ref().cloned(),
]
}

View File

@@ -246,14 +246,18 @@ impl<'own, 'library> Battle<'own, 'library> {
hit_data.set_damage(damage);
let mut accuracy = executing_move.use_move().accuracy();
script_hook!(
change_accuracy,
executing_move,
executing_move,
target,
hit_index,
&mut accuracy
);
// If the accuracy is 255, the move should always hit, and as such we should not allow
// modifying it.
if accuracy != 255 {
script_hook!(
change_accuracy,
executing_move,
executing_move,
target,
hit_index,
&mut accuracy
);
}
if accuracy < 100 && self.random().get_max(100) as u8 >= accuracy {
script_hook!(on_move_miss, target, executing_move, target);
self.event_hook().trigger(Event::Miss {

View File

@@ -40,7 +40,7 @@ impl<'library> Gen7MiscLibrary<'library> {
pub fn new() -> Self {
let struggle_data = Box::new(MoveData::new(
&StringKey::new("struggle"),
0,
0.into(),
MoveCategory::Physical,
50,
255,

View File

@@ -7,7 +7,6 @@ use parking_lot::RwLock;
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::{Event, EventHook};
use crate::dynamic_data::is_valid_target;
use crate::dynamic_data::models::battle_party::BattleParty;
use crate::dynamic_data::models::battle_random::BattleRandom;
use crate::dynamic_data::models::battle_result::BattleResult;
@@ -17,8 +16,9 @@ use crate::dynamic_data::ChoiceQueue;
use crate::dynamic_data::DynamicLibrary;
use crate::dynamic_data::Script;
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScripts;
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::VolatileScriptsOwner;
use crate::dynamic_data::{is_valid_target, ScriptWrapper};
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
use crate::{script_hook, PkmnResult, StringKey};
/// A pokemon battle, with any amount of sides and pokemon per side.
@@ -322,7 +322,7 @@ impl<'own, 'library> Battle<'own, 'library> {
}
}
impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> {
impl<'own, 'library> VolatileScriptsOwner<'own> for Battle<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.volatile_scripts
}

View File

@@ -13,7 +13,7 @@ use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::Script;
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScripts;
use crate::dynamic_data::VolatileScriptsOwner;
use crate::{script_hook, PkmnResult, StringKey};
/// A side on a battle.
@@ -296,7 +296,7 @@ impl<'own, 'library> BattleSide<'own, 'library> {
}
}
impl<'own, 'library> VolatileScripts<'own> for BattleSide<'own, 'library> {
impl<'own, 'library> VolatileScriptsOwner<'own> for BattleSide<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.volatile_scripts
}

View File

@@ -10,7 +10,7 @@ use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::TargetList;
use crate::static_data::MoveData;
use crate::static_data::{MoveData, TypeIdentifier};
use crate::{PkmnResult, PokemonError};
/// A hit data is the data for a single hit, on a single target.
@@ -25,7 +25,7 @@ pub struct HitData {
/// The actual damage of the hit.
damage: AtomicU32,
/// The type id of the type used for the hit.
move_type: AtomicU8,
move_type: Atomic<TypeIdentifier>,
/// Whether or not the hit has failed.
has_failed: AtomicBool,
}
@@ -48,7 +48,7 @@ impl HitData {
self.damage.load(Ordering::Relaxed)
}
/// The type id of the type used for the hit.
pub fn move_type(&self) -> u8 {
pub fn move_type(&self) -> TypeIdentifier {
self.move_type.load(Ordering::Relaxed)
}
/// Whether or not the hit has failed.
@@ -73,7 +73,7 @@ impl HitData {
self.damage.store(value, Ordering::SeqCst);
}
/// Sets the move type id of the hit.
pub fn set_move_type(&self, value: u8) {
pub fn set_move_type(&self, value: TypeIdentifier) {
self.move_type.store(value, Ordering::SeqCst);
}
/// Marks the hit as failed.

View File

@@ -2,21 +2,32 @@ use std::sync::atomic::{AtomicU8, Ordering};
use crate::static_data::MoveData;
/// A learned move is the data attached to a Pokemon for a move it has learned. It has information
/// such as the remaining amount of users, how it has been learned, etc.
#[derive(Debug)]
pub struct LearnedMove<'library> {
/// The immutable move information of the move.
move_data: &'library MoveData,
/// The maximal power points for this move.
max_pp: u8,
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
remaining_pp: AtomicU8,
/// The way the move was learned.
learn_method: MoveLearnMethod,
}
#[derive(Copy, Clone, Debug)]
/// The different ways a move can be learned.
#[derive(Copy, Clone, Debug, Default)]
pub enum MoveLearnMethod {
/// We do not know the learn method.
#[default]
Unknown = 0,
/// The move was learned through level up.
Level = 1,
}
impl<'a> LearnedMove<'a> {
/// Instantiate a new learned move.
pub fn new(move_data: &'a MoveData, learn_method: MoveLearnMethod) -> Self {
Self {
move_data,
@@ -26,36 +37,50 @@ impl<'a> LearnedMove<'a> {
}
}
/// The immutable move information of the move.
pub fn move_data(&self) -> &MoveData {
self.move_data
}
/// The maximal power points for this move.
pub fn max_pp(&self) -> u8 {
self.max_pp
}
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
pub fn remaining_pp(&self) -> u8 {
self.remaining_pp.load(Ordering::Relaxed)
}
/// The way the move was learned.
pub fn learn_method(&self) -> MoveLearnMethod {
self.learn_method
}
/// Try and reduce the PP by a certain amount. If the amount is higher than the current uses,
/// return false. Otherwise, reduce the PP, and return true.
pub fn try_use(&self, amount: u8) -> bool {
if amount > self.remaining_pp() {
return false;
}
self.remaining_pp.fetch_sub(amount, Ordering::SeqCst);
true
let res = self.remaining_pp.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
if amount > x {
None
} else {
Some(x - amount)
}
});
res.is_ok()
}
/// Set the remaining PP to the max amount of PP.
pub fn restore_all_uses(&self) {
self.remaining_pp.store(self.max_pp, Ordering::SeqCst);
}
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
pub fn restore_uses(&self, mut uses: u8) {
if self.remaining_pp() + uses > self.max_pp {
uses = self.remaining_pp() - uses;
}
self.remaining_pp.fetch_add(uses, Ordering::SeqCst);
self.remaining_pp
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
if x + uses > self.max_pp {
uses = self.max_pp - x;
}
Some(x)
})
.unwrap();
}
}

View File

@@ -11,8 +11,7 @@ use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod};
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScripts};
use crate::static_data::AbilityIndex;
use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner};
use crate::static_data::DataLibrary;
use crate::static_data::Form;
use crate::static_data::Gender;
@@ -20,95 +19,107 @@ use crate::static_data::Item;
use crate::static_data::Nature;
use crate::static_data::Species;
use crate::static_data::{Ability, Statistic};
use crate::static_data::{AbilityIndex, TypeIdentifier};
use crate::static_data::{ClampedStatisticSet, StatisticSet};
use crate::utils::Random;
use crate::{script_hook, PkmnResult, StringKey};
#[derive(Debug)]
pub struct PokemonBattleData<'pokemon, 'library> {
battle: *mut Battle<'pokemon, 'library>,
battle_side_index: AtomicU8,
index: AtomicU8,
on_battle_field: AtomicBool,
seen_opponents: RwLock<Vec<Weak<Pokemon<'pokemon, 'library>>>>,
}
impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> {
pub fn battle_mut(&mut self) -> Option<&mut Battle<'pokemon, 'library>> {
unsafe { self.battle.as_mut() }
}
pub fn battle(&self) -> Option<&Battle<'pokemon, 'library>> {
unsafe { self.battle.as_ref() }
}
pub fn battle_side_index(&self) -> u8 {
self.battle_side_index.load(Ordering::Relaxed)
}
pub fn index(&self) -> u8 {
self.index.load(Ordering::Relaxed)
}
pub fn on_battle_field(&self) -> bool {
self.on_battle_field.load(Ordering::Relaxed)
}
pub fn seen_opponents(&self) -> &RwLock<Vec<Weak<Pokemon<'pokemon, 'library>>>> {
&self.seen_opponents
}
}
/// An individual Pokemon as we know and love them.
#[derive(Debug)]
pub struct Pokemon<'own, 'library>
where
'own: 'library,
{
/// The library data of the Pokemon.
library: &'own DynamicLibrary,
/// The species of the Pokemon.
species: &'own Species,
/// The form of the Pokemon.
form: &'own Form,
/// An optional display species of the Pokemon. If this is set, the client should display this
/// species. An example of usage for this is the Illusion ability.
display_species: Option<&'own Species>,
/// An optional display form of the Pokemon. If this is set, the client should display this
// species. An example of usage for this is the Illusion ability.
display_form: Option<&'own Form>,
/// The current level of the Pokemon.
level: LevelInt,
/// The amount of experience of the Pokemon.
experience: AtomicU32,
/// A unique random number for this Pokemon.
unique_identifier: u32,
/// The gender of the Pokemon.
gender: Gender,
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
/// currently not used, and can be used for other implementations.
coloring: u8,
/// The held item of the Pokemon.
held_item: RwLock<Option<&'own Item>>,
/// The remaining health points of the Pokemon.
current_health: AtomicU32,
/// The weight of the Pokemon in kilograms.
weight: Atomic<f32>,
/// The height of the Pokemon in meters.
height: Atomic<f32>,
stat_boost: ClampedStatisticSet<AtomicI8, -6, 6>,
/// The stats of the Pokemon when disregarding any stat boosts.
flat_stats: StatisticSet<AtomicU32>,
/// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below
/// -6.
stat_boost: ClampedStatisticSet<AtomicI8, -6, 6>,
/// The stats of the Pokemon including the stat boosts
boosted_stats: StatisticSet<AtomicU32>,
/// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
individual_values: ClampedStatisticSet<AtomicU8, 0, 31>,
/// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
effort_values: ClampedStatisticSet<AtomicU8, 0, 252>,
/// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon.
nature: &'own Nature,
/// An optional nickname of the Pokemon.
nickname: Option<String>,
/// An index of the ability to find the actual ability on the form.
ability_index: AbilityIndex,
is_ability_overridden: bool,
/// An ability can be overriden to an arbitrary ability. This is for example used for the Mummy
/// ability.
override_ability: Option<Ability>,
/// If in battle, we have additional data.
battle_data: RwLock<Option<PokemonBattleData<'own, 'library>>>,
/// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots
/// are defined by None.
moves: RwLock<[Option<Arc<LearnedMove<'library>>>; MAX_MOVES]>,
/// Whether or not the Pokemon is allowed to gain experience.
allowed_experience: bool,
types: Vec<u8>,
/// The current types of the Pokemon.
types: Vec<TypeIdentifier>,
/// Whether or not this Pokemon is an egg.
is_egg: bool,
/// Whether or not this Pokemon was caught this battle.
is_caught: bool,
/// The script for the held item.
held_item_trigger_script: ScriptContainer,
/// The script for the ability.
ability_script: ScriptContainer,
/// The script for the status.
status_script: ScriptContainer,
/// The volatile status scripts of the Pokemon.
volatile: Arc<ScriptSet>,
/// Data required for the Pokemon to be a script source.
script_source_data: RwLock<ScriptSourceData>,
}
impl<'own, 'library> Pokemon<'own, 'library> {
/// Instantiates a new Pokemon.
pub fn new(
library: &'own DynamicLibrary,
species: &'own Species,
@@ -155,7 +166,6 @@ impl<'own, 'library> Pokemon<'own, 'library> {
nature,
nickname: None,
ability_index: ability,
is_ability_overridden: false,
override_ability: None,
battle_data: RwLock::new(None),
moves: RwLock::new([None, None, None, None]),
@@ -176,15 +186,19 @@ impl<'own, 'library> Pokemon<'own, 'library> {
pokemon
}
/// The library data of the Pokemon.
pub fn library(&self) -> &'own DynamicLibrary {
self.library
}
/// The species of the Pokemon.
pub fn species(&self) -> &'own Species {
self.species
}
/// The form of the Pokemon.
pub fn form(&self) -> &'own Form {
self.form
}
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
pub fn display_species(&self) -> &'own Species {
if let Some(v) = self.display_species {
v
@@ -192,6 +206,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
self.species
}
}
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
pub fn display_form(&self) -> &'own Form {
if let Some(v) = self.display_form {
v
@@ -199,25 +214,32 @@ impl<'own, 'library> Pokemon<'own, 'library> {
self.form
}
}
/// The current level of the Pokemon.
pub fn level(&self) -> LevelInt {
self.level
}
/// The amount of experience of the Pokemon.
pub fn experience(&self) -> u32 {
self.experience.load(Ordering::Relaxed)
}
/// A unique random number for this Pokemon.
pub fn unique_identifier(&self) -> u32 {
self.unique_identifier
}
/// The gender of the Pokemon.
pub fn gender(&self) -> Gender {
self.gender
}
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
/// currently not used, and can be used for other implementations.
pub fn coloring(&self) -> u8 {
self.coloring
}
/// Checks whether the Pokemon is holding an item,
pub fn held_item(&self) -> &RwLock<Option<&'own Item>> {
&self.held_item
}
/// Checks whether the Pokemon is holding a specific 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.read().deref() {
@@ -225,12 +247,15 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
false
}
/// Changes the held item of the Pokemon/
pub fn set_held_item(&self, item: &'own Item) -> Option<&'own Item> {
self.held_item.write().replace(item)
}
/// Removes the held item from the Pokemon.
pub fn remove_held_item(&self) -> Option<&'own Item> {
self.held_item.write().take()
}
/// Makes the Pokemon uses its held item.
pub fn consume_held_item(&self) -> bool {
if self.held_item.read().is_none() {
return false;
@@ -244,42 +269,53 @@ impl<'own, 'library> Pokemon<'own, 'library> {
todo!();
}
/// The remaining health points of the Pokemon.
pub fn current_health(&self) -> u32 {
self.current_health.load(Ordering::Relaxed)
}
/// The max health points of the Pokemon.
pub fn max_health(&self) -> u32 {
self.boosted_stats.hp()
}
/// The weight of the Pokemon in kilograms.
pub fn weight(&self) -> f32 {
self.weight.load(Ordering::Relaxed)
}
/// The height of the Pokemon in meters.
pub fn height(&self) -> f32 {
self.height.load(Ordering::Relaxed)
}
/// An optional nickname of the Pokemon.
pub fn nickname(&self) -> &Option<String> {
&self.nickname
}
/// An index of the ability to find the actual ability on the form.
pub fn real_ability(&self) -> &AbilityIndex {
&self.ability_index
}
pub fn types(&self) -> &Vec<u8> {
/// The current types of the Pokemon.
pub fn types(&self) -> &Vec<TypeIdentifier> {
&self.types
}
/// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots
/// are defined by None.
pub fn learned_moves(&self) -> &RwLock<[Option<Arc<LearnedMove<'library>>>; MAX_MOVES]> {
&self.moves
}
pub fn status(&self) -> &ScriptContainer {
&self.status_script
}
/// The stats of the Pokemon when disregarding any stat boosts.
pub fn flat_stats(&self) -> &StatisticSet<AtomicU32> {
&self.flat_stats
}
/// The stats of the Pokemon including the stat boosts
pub fn boosted_stats(&self) -> &StatisticSet<AtomicU32> {
&self.boosted_stats
}
/// Get the stat boosts for a specific stat.
pub fn stat_boost(&self, stat: Statistic) -> i8 {
self.stat_boost.get_stat(stat)
}
/// Change a boosted stat by a certain amount.
pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> bool {
let mut prevent = false;
script_hook!(
@@ -328,13 +364,16 @@ impl<'own, 'library> Pokemon<'own, 'library> {
return changed;
}
/// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
pub fn individual_values(&self) -> &ClampedStatisticSet<AtomicU8, 0, 31> {
&self.individual_values
}
/// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
pub fn effort_values(&self) -> &ClampedStatisticSet<AtomicU8, 0, 252> {
&self.effort_values
}
/// Gets the battle the battle is currently in.
pub fn get_battle(&self) -> Option<&Battle<'own, 'library>> {
let r = self.battle_data.read();
if let Some(data) = &r.deref() {
@@ -343,20 +382,24 @@ impl<'own, 'library> Pokemon<'own, 'library> {
None
}
}
/// Get the index of the side of the battle the Pokemon is in. Only returns a value if the Pokemon
/// is on the battlefield.
pub fn get_battle_side_index(&self) -> Option<u8> {
self.battle_data.read().as_ref().map(|data| data.battle_side_index())
}
/// Get the index of the slot on the side of the battle the Pokemon is in. Only returns a value
/// if the Pokemon is on the battlefield.
pub fn get_battle_index(&self) -> Option<u8> {
self.battle_data.read().as_ref().map(|data| data.index())
}
/// Returns whether something overrides the ability.
pub fn is_ability_overriden(&self) -> bool {
self.is_ability_overridden
self.override_ability.is_some()
}
/// Returns the currently active ability.
pub fn active_ability(&self) -> &Ability {
if self.is_ability_overridden {
if let Some(v) = &self.override_ability {
return v;
}
if let Some(v) = &self.override_ability {
return v;
}
self.library
.static_data()
@@ -365,33 +408,41 @@ impl<'own, 'library> Pokemon<'own, 'library> {
.unwrap()
}
/// The script for the status.
pub fn status(&self) -> &ScriptContainer {
&self.status_script
}
/// Returns the script for the currently active ability.
pub fn ability_script(&self) -> &ScriptContainer {
&self.ability_script
}
// pub fn seen_opponents(&self) -> &RwLock<Option<PokemonBattleData<'own, 'library>>> {
// &self.battle_data.read
// }
/// Whether or not the Pokemon is allowed to gain experience.
pub fn allowed_experience_gain(&self) -> bool {
self.allowed_experience
}
/// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon.
pub fn nature(&self) -> &'own Nature {
self.nature
}
/// Calculates the flat stats on the Pokemon.
pub fn recalculate_flat_stats(&self) {
self.library
.stat_calculator()
.calculate_flat_stats(self, &self.flat_stats);
self.recalculate_boosted_stats();
}
/// Calculates the boosted stats on the Pokemon.
pub fn recalculate_boosted_stats(&self) {
self.library
.stat_calculator()
.calculate_boosted_stats(self, &self.boosted_stats);
}
/// Change the species of the Pokemon.
pub fn change_species(&mut self, species: &'own Species, form: &'own Form) {
self.species = species;
self.form = form;
@@ -424,6 +475,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Change the form of the Pokemon.
pub fn change_form(&mut self, form: &'own Form) {
if std::ptr::eq(self.form, form) {
return;
@@ -474,14 +526,17 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Whether or not the Pokemon is useable in a battle.
pub fn is_usable(&self) -> bool {
!self.is_caught && !self.is_egg && !self.is_fainted()
}
/// Returns whether the Pokemon is fainted.
pub fn is_fainted(&self) -> bool {
self.current_health() == 0
}
/// Sets the current battle the Pokemon is in.
pub fn set_battle_data(&self, battle: *mut Battle<'own, 'library>, battle_side_index: u8) {
let mut w = self.battle_data.write();
if let Some(battle_data) = w.deref_mut() {
@@ -498,6 +553,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Sets whether or not the Pokemon is on the battlefield.
pub fn set_on_battlefield(&self, value: bool) {
let r = self.battle_data.read();
if let Some(data) = &mut r.deref() {
@@ -510,6 +566,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Sets the index of the slot of the side the Pokemon is on.
pub fn set_battle_index(&self, index: u8) {
let r = self.battle_data.read();
if let Some(data) = r.deref() {
@@ -517,10 +574,12 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Whether or not the Pokemon is on the battlefield.
pub fn is_on_battlefield(&self) -> bool {
self.battle_data.read().is_some_and(|a| a.on_battle_field())
}
/// Marks an opponent as seen, for use in experience gain.
pub fn mark_opponent_as_seen(&self, pokemon: Weak<Pokemon<'own, 'library>>) {
let r = self.battle_data.read();
if let Some(battle_data) = &r.deref() {
@@ -534,6 +593,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Damages the Pokemon by a certain amount of damage, from a specific damage source.
pub fn damage(&self, mut damage: u32, source: DamageSource) {
if damage > self.current_health() {
damage = self.current_health();
@@ -563,7 +623,8 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
pub fn on_faint(&self, source: DamageSource) {
/// Triggers when the Pokemon faints.
fn on_faint(&self, source: DamageSource) {
let r = self.battle_data.read();
if let Some(battle_data) = r.deref() {
if let Some(battle) = battle_data.battle() {
@@ -581,6 +642,7 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// Learn a move.
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) {
let mut learned_moves = self.learned_moves().write();
let move_pos = learned_moves.iter().position(|a| a.is_none());
@@ -592,6 +654,49 @@ impl<'own, 'library> Pokemon<'own, 'library> {
}
}
/// The data of the Pokemon related to being in a battle.
#[derive(Debug)]
pub struct PokemonBattleData<'pokemon, 'library> {
/// The battle data of the Pokemon
battle: *mut Battle<'pokemon, 'library>,
/// The index of the side of the Pokemon
battle_side_index: AtomicU8,
/// The index of the slot on the side of the Pokemon.
index: AtomicU8,
/// Whether or not the Pokemon is on the battlefield.
on_battle_field: AtomicBool,
/// A list of opponents the Pokemon has seen this battle.
seen_opponents: RwLock<Vec<Weak<Pokemon<'pokemon, 'library>>>>,
}
impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> {
/// The battle data of the Pokemon
pub fn battle_mut(&mut self) -> Option<&mut Battle<'pokemon, 'library>> {
unsafe { self.battle.as_mut() }
}
/// The battle data of the Pokemon
pub fn battle(&self) -> Option<&Battle<'pokemon, 'library>> {
unsafe { self.battle.as_ref() }
}
/// The index of the side of the Pokemon
pub fn battle_side_index(&self) -> u8 {
self.battle_side_index.load(Ordering::Relaxed)
}
/// The index of the slot on the side of the Pokemon.
pub fn index(&self) -> u8 {
self.index.load(Ordering::Relaxed)
}
/// Whether or not the Pokemon is on the battlefield.
pub fn on_battle_field(&self) -> bool {
self.on_battle_field.load(Ordering::Relaxed)
}
/// A list of opponents the Pokemon has seen this battle.
pub fn seen_opponents(&self) -> &RwLock<Vec<Weak<Pokemon<'pokemon, 'library>>>> {
&self.seen_opponents
}
}
impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> {
fn get_script_count(&self) -> usize {
let mut c = 3;
@@ -624,7 +729,7 @@ impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> {
}
}
impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> {
impl<'own, 'library> VolatileScriptsOwner<'own> for Pokemon<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.volatile
}

View File

@@ -3,30 +3,47 @@ use crate::dynamic_data::models::learned_move::MoveLearnMethod;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::{AbilityIndex, DataLibrary, Gender};
use crate::StringKey;
use crate::{Random, StringKey};
/// This allows for the easy chain building of a Pokemon.
pub struct PokemonBuilder<'own> {
/// The library of the Pokemon.
library: &'own DynamicLibrary,
/// The name of the species of the Pokemon.
species: StringKey,
/// The level of the Pokemon.
level: LevelInt,
/// The moves the Pokemon will know.
learned_moves: Vec<StringKey>,
/// A random seed used for any randomization done.
random_seed: Option<u128>,
}
impl<'own> PokemonBuilder<'own> {
/// Creates a new PokemonBuilder with a library, species, and level.
pub fn new(library: &'own DynamicLibrary, species: StringKey, level: LevelInt) -> Self {
Self {
library,
species,
level,
learned_moves: vec![],
random_seed: None,
}
}
/// Makes the Pokemon learn a move.
pub fn learn_move(mut self, learned_move: StringKey) -> Self {
self.learned_moves.push(learned_move);
self
}
/// Finally turn the builder into an actual Pokemon.
pub fn build(self) -> Pokemon<'own, 'own> {
let mut random = if let Some(seed) = self.random_seed {
Random::new(seed)
} else {
Random::default()
};
let species = self.library.static_data().species().get(&self.species).unwrap();
let form = species.get_default_form();
let p = Pokemon::new(
@@ -38,7 +55,7 @@ impl<'own> PokemonBuilder<'own> {
index: 0,
},
self.level,
0,
random.get_unsigned(),
Gender::Male,
0,
&"hardy".into(),

View File

@@ -1,12 +1,16 @@
use crate::dynamic_data::models::pokemon::Pokemon;
use std::sync::Arc;
use crate::dynamic_data::models::pokemon::Pokemon;
/// A list of Pokemon belonging to a trainer.
#[derive(Debug)]
pub struct PokemonParty<'pokemon, 'library> {
/// The underlying list of Pokemon.
pokemon: Vec<Option<Arc<Pokemon<'pokemon, 'library>>>>,
}
impl<'own, 'library> PokemonParty<'own, 'library> {
/// Instantiates a party with a set size.
pub fn new(size: usize) -> Self {
let mut pokemon = Vec::with_capacity(size);
for _i in 0..size {
@@ -15,10 +19,12 @@ impl<'own, 'library> PokemonParty<'own, 'library> {
Self { pokemon }
}
/// Instantiates a party with a list.
pub fn new_from_vec(pokemon: Vec<Option<Arc<Pokemon<'own, 'library>>>>) -> Self {
Self { pokemon }
}
/// Gets a Pokemon at an index in the party.
pub fn at(&self, index: usize) -> &Option<Arc<Pokemon<'own, 'library>>> {
let opt = self.pokemon.get(index);
if let Some(v) = opt {
@@ -28,10 +34,12 @@ impl<'own, 'library> PokemonParty<'own, 'library> {
}
}
/// Swaps two Pokemon in the party around.
pub fn switch(&mut self, a: usize, b: usize) {
self.pokemon.swap(a, b);
}
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
pub fn swap_into(
&mut self,
index: usize,
@@ -45,6 +53,7 @@ impl<'own, 'library> PokemonParty<'own, 'library> {
old
}
/// Whether or not the party still has Pokemon that can be used in battle.
pub fn has_usable_pokemon(&self) -> bool {
for pokemon in self.pokemon.iter().flatten() {
if pokemon.is_usable() {
@@ -54,14 +63,17 @@ impl<'own, 'library> PokemonParty<'own, 'library> {
false
}
/// Get the length of the underlying list of Pokemon.
pub fn length(&self) -> usize {
self.pokemon.len()
}
/// Gets the underlying list of Pokemon.
pub fn pokemon(&self) -> &Vec<Option<Arc<Pokemon<'own, 'library>>>> {
&self.pokemon
}
/// Makes sure there are no empty spots in the party anymore, leaving the length the same.
pub fn pack_party(&mut self) {
let mut first_empty = None;
let mut i = 0;

View File

@@ -1 +1,2 @@
/// The script functions that are relevant to item use.
pub trait ItemScript {}

View File

@@ -9,13 +9,15 @@ pub use script::*;
#[doc(inline)]
pub use script_set::*;
#[doc(inline)]
pub use volatile_scripts::*;
pub use volatile_scripts_owner::*;
mod item_script;
mod script;
mod script_set;
mod volatile_scripts;
mod volatile_scripts_owner;
/// This macro runs a script function on a given ScriptSource, and all its parents. It will ensure
/// to only run the script function if it is not suppressed, and can take any amount of parameters.
#[macro_export]
macro_rules! script_hook {
($hook_name: ident, $source: ident, $($parameters: expr),*) => {
@@ -33,6 +35,8 @@ macro_rules! script_hook {
};
}
/// This macro runs a script function on all scripts in a Vec of scripts. It will ensure it only
/// runs the script function if it is not suppressed, and can take any amount of parameters.
#[macro_export]
macro_rules! run_scripts {
($hook_name: ident, $source: ident, $($parameters: expr),*) => {
@@ -68,14 +72,20 @@ macro_rules! run_scripts {
};
}
/// The script source data is the basic data required for any script source.
#[derive(Default, Debug)]
pub struct ScriptSourceData {
/// Whether or not the data has been initialized yet.
is_initialized: bool,
/// A list that references all possible scripts on this source, and it's parents.
scripts: Vec<ScriptWrapper>,
}
/// A script source is a struct on which we can trigger scripts to be run from.
pub trait ScriptSource<'a> {
fn get_script_iterator(&self) -> ScriptAggregator {
/// Gets an iterator over all the scripts that are relevant to this script source. If the data
/// has not been initialised, it will do so here.
fn get_script_iterator(&self) -> ScriptIterator {
let lock = self.get_script_source_data();
if !lock.read().is_initialized {
let mut data = lock.write();
@@ -83,17 +93,28 @@ pub trait ScriptSource<'a> {
self.collect_scripts(&mut data.scripts);
data.is_initialized = true;
}
ScriptAggregator::new(&lock.read().scripts as *const Vec<ScriptWrapper>)
ScriptIterator::new(&lock.read().scripts as *const Vec<ScriptWrapper>)
}
/// The number of scripts that are expected to be relevant for this source. This generally is
/// the number of its own scripts + the number of scripts for any parents.
fn get_script_count(&self) -> usize;
/// Returns the underlying data required for us to be a script source.
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData>;
/// This should add all scripts belonging to this source to the scripts Vec, disregarding its
/// potential parents.
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>);
/// This should add all scripts that are relevant to the source the the scripts Vec, including
/// everything that belongs to its parents.
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>);
}
/// Enum to store both ScriptSets and sets in a single value.
#[derive(Debug)]
pub enum ScriptWrapper {
/// A reference to a single script.
Script(Weak<RwLock<Option<Arc<dyn Script>>>>),
/// A reference to a ScriptSet.
Set(Weak<ScriptSet>),
}
@@ -109,29 +130,31 @@ impl From<&Arc<ScriptSet>> for ScriptWrapper {
}
}
pub struct ScriptAggregator {
/// This struct allows for the iteration over scripts.
pub struct ScriptIterator {
/// A pointer to the vector of ScriptWrappers. This can be a pointer, as we know it remains valid
/// while we're using it.
scripts: *const Vec<ScriptWrapper>,
size: i32,
/// The current index in the scripts.
index: i32,
/// If we're currently inside a set, the current index inside the set.
set_index: i32,
}
impl ScriptAggregator {
impl ScriptIterator {
/// Instantiates an iterator.
pub fn new(scripts: *const Vec<ScriptWrapper>) -> Self {
unsafe {
let len = scripts.as_ref().unwrap().len();
Self {
scripts,
size: len as i32,
index: -1,
set_index: -1,
}
Self {
scripts,
index: -1,
set_index: -1,
}
}
/// Move to the next valid value in the scripts.
fn increment_to_next_value(&mut self) -> bool {
if self.index != -1 {
let wrapper = unsafe { &self.scripts.as_ref().unwrap()[self.index as usize] };
let wrapper = unsafe { &(*self.scripts)[self.index as usize] };
if let ScriptWrapper::Set(set) = wrapper {
if let Some(set) = set.upgrade() {
self.set_index += 1;
@@ -144,7 +167,8 @@ impl ScriptAggregator {
}
}
self.index += 1;
for index in self.index..self.size {
let len = (unsafe { &*self.scripts }).len() as i32;
for index in self.index..len {
self.index = index;
let wrapper = unsafe { &self.scripts.as_ref().unwrap()[self.index as usize] };
if let ScriptWrapper::Set(s) = wrapper {
@@ -166,6 +190,7 @@ impl ScriptAggregator {
false
}
/// Gets the next valid script. If none is found, returns None.
pub fn get_next(&mut self) -> Option<ScriptContainer> {
if !self.increment_to_next_value() {
return None;
@@ -183,6 +208,7 @@ impl ScriptAggregator {
}
}
/// Resets the iterator to the start.
pub fn reset(&mut self) {
self.index = -1;
self.set_index = -1;
@@ -260,7 +286,7 @@ mod tests {
fn script_aggregator_property_iterates_single_script() {
let script = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next() {
v.get().unwrap().read().as_ref().unwrap().on_initialize(&[]);
}
@@ -272,7 +298,7 @@ mod tests {
fn script_aggregator_property_iterates_single_script_with_resets() {
let script = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
@@ -293,7 +319,7 @@ mod tests {
ScriptWrapper::from(&script2),
ScriptWrapper::from(&script3),
];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next() {
v.get().unwrap().read().as_ref().unwrap().on_initialize(&[]);
}
@@ -315,7 +341,7 @@ mod tests {
ScriptWrapper::from(&script2),
ScriptWrapper::from(&script3),
];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
@@ -338,7 +364,7 @@ mod tests {
set.add(Arc::new(TestScript::new_with_name("test_c")));
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
@@ -367,7 +393,7 @@ mod tests {
set.add(Arc::new(TestScript::new_with_name("test_c")));
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
assert_eq!(
aggregator
.get_next()
@@ -407,7 +433,7 @@ mod tests {
set.add(Arc::new(TestScript::new_with_name("test_c")));
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
assert_eq!(
aggregator
.get_next()

View File

@@ -12,7 +12,7 @@ use crate::dynamic_data::Battle;
use crate::dynamic_data::DamageSource;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
use crate::static_data::EffectParameter;
use crate::static_data::{EffectParameter, TypeIdentifier};
use crate::static_data::{Item, Statistic};
use crate::StringKey;
@@ -105,7 +105,14 @@ pub trait Script: Send + Sync {
/// move, which include the scripts that are attached to the owner of the script.
fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>) {}
/// This function allows the script to change the actual type that is used for the move on a target.
fn change_move_type(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _move_type: &mut u8) {}
fn change_move_type(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_move_type: &mut TypeIdentifier,
) {
}
/// This function allows the script to change how effective a move is on a target.
fn change_effectiveness(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _effectiveness: &mut f32) {}
/// This function allows a script to block an outgoing move from being critical.

View File

@@ -1,16 +1,23 @@
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::{PkmnResult, StringKey};
use indexmap::IndexMap;
use parking_lot::RwLock;
use std::ops::Deref;
use std::sync::Arc;
use indexmap::IndexMap;
use parking_lot::RwLock;
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::{PkmnResult, StringKey};
/// A collection of unique scripts.
#[derive(Debug, Default)]
pub struct ScriptSet {
/// The scripts collection. This is an indexmap so we can iterate over them and always get the
/// scripts in the same order., while still allowing fast lookup.
scripts: RwLock<IndexMap<StringKey, ScriptContainer>>,
}
impl ScriptSet {
/// Adds a script to the set. If the script with that name already exists in this set, this
/// makes that script stack instead. The return value here is that script.
pub fn add(&self, script: Arc<dyn Script>) -> ScriptContainer {
if let Some(lock) = self.scripts.read().get(script.name()) {
if let Some(existing) = lock.get() {
@@ -27,6 +34,8 @@ impl ScriptSet {
self.scripts.read().last().unwrap().1.clone()
}
/// Adds a script with a name to the set. If the script with that name already exists in this
/// set, this makes that script stack instead. The return value here is that script.
pub fn stack_or_add<'b, F>(&self, key: &StringKey, instantiation: &'b F) -> PkmnResult<Option<ScriptContainer>>
where
F: Fn() -> PkmnResult<Option<Arc<dyn Script>>>,
@@ -51,10 +60,12 @@ impl ScriptSet {
}
}
/// Gets a script from the set using its unique name.
pub fn get(&self, key: &StringKey) -> Option<ScriptContainer> {
self.scripts.read().get(key).cloned()
}
/// Removes a script from the set using its unique name.
pub fn remove(&self, key: &StringKey) {
let value = self.scripts.write().shift_remove(key);
if let Some(script) = value {
@@ -66,6 +77,7 @@ impl ScriptSet {
}
}
/// Clears all scripts from the set.
pub fn clear(&self) {
for script in self.scripts.read().deref() {
if let Some(script) = script.1.get() {
@@ -77,18 +89,24 @@ impl ScriptSet {
self.scripts.write().clear();
}
/// Checks if the set has a script with the given name.
pub fn has(&self, key: &StringKey) -> bool {
self.scripts.read().contains_key(key)
}
/// Gets a script from the set at a specific index.
pub fn at(&self, index: usize) -> ScriptContainer {
self.scripts.read()[index].clone()
}
/// Gets the number of scripts in the set.
pub fn count(&self) -> usize {
self.scripts.read().len()
}
/// Get a vector of the scripts in this set. This copies the current scripts into a Vec, and
/// returns that. This allows modifying the scripts in the set, while still being able to iterate
/// over them.
pub(crate) fn get_owning_iterator(&self) -> Vec<ScriptContainer> {
let s = self.scripts.read();
let mut v = Vec::with_capacity(s.deref().len());

View File

@@ -1,25 +1,33 @@
use std::sync::Arc;
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::dynamic_data::script_handling::script_set::ScriptSet;
use crate::{PkmnResult, StringKey};
use std::sync::Arc;
pub trait VolatileScripts<'a> {
/// This trait adds a bunch of helper functions to deal with volatile scripts on a struct.
pub trait VolatileScriptsOwner<'a> {
/// Return the [`ScriptSet`] that are our volatile scripts.
fn volatile_scripts(&self) -> &Arc<ScriptSet>;
/// Loads a volatile script by name.
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>>;
/// Check if a volatile script with given name exists.
fn has_volatile_script(&self, key: &StringKey) -> bool {
self.volatile_scripts().has(key)
}
/// Gets a volatile script by name.
fn get_volatile_script(&self, key: &StringKey) -> Option<ScriptContainer> {
self.volatile_scripts().get(key)
}
/// Adds a volatile script by name.
fn add_volatile_script(&mut self, key: &StringKey) -> PkmnResult<Option<ScriptContainer>> {
self.volatile_scripts()
.stack_or_add(key, &|| self.load_volatile_script(key))
}
/// Removes a volatile script by name.
fn remove_volatile_script(&mut self, key: &StringKey) {
self.volatile_scripts().remove(key)
}