A lot more documentation, some initial work on the script resolver.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-06-30 17:34:57 +02:00
parent 25e2a0dda1
commit 03f5e3bb5a
18 changed files with 450 additions and 210 deletions

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::history::HistoryHolder;
use crate::dynamic_data::is_valid_target;
use crate::dynamic_data::models::battle_party::BattleParty;
use crate::dynamic_data::models::battle_random::BattleRandom;
@@ -22,28 +21,43 @@ use crate::dynamic_data::VolatileScripts;
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::{script_hook, PkmnResult, StringKey};
/// A pokemon battle, with any amount of sides and pokemon per side.
#[derive(Debug)]
pub struct Battle<'own, 'library> {
/// The library the battle uses for handling.
library: &'own DynamicLibrary,
/// A list of all different parties in the battle.
parties: Vec<BattleParty<'own, 'library>>,
/// Whether or not Pokemon can flee from the battle.
can_flee: bool,
/// The number of sides in the battle. Typically 2.
number_of_sides: u8,
/// The number of Pokemon that can be on each side.
pokemon_per_side: u8,
/// A list of all sides in the battle.
sides: Vec<BattleSide<'own, 'library>>,
/// The RNG used for the battle.
random: BattleRandom,
/// A queue of the yet to be executed choices in a turn.
current_turn_queue: RwLock<Option<ChoiceQueue<'own, 'library>>>,
/// Whether or not the battle has ended.
has_ended: AtomicBool,
/// The eventual result of the battle. Inconclusive until the battle is ended.
result: Atomic<BattleResult>,
/// The handler to send all events to.
event_hook: EventHook,
history_holder: Box<HistoryHolder>,
/// The index of the current turn. 0 until all choices
current_turn: AtomicU32,
/// All the volatile scripts attached to a Pokemon
volatile_scripts: Arc<ScriptSet>,
/// The time the last turn took to run. Defaults to 0.
last_turn_time: Atomic<chrono::Duration>,
/// Data required for this script to be a script source.
script_source_data: RwLock<ScriptSourceData>,
}
impl<'own, 'library> Battle<'own, 'library> {
/// Initializes a new battle.
pub fn new(
library: &'own DynamicLibrary,
parties: Vec<BattleParty<'own, 'library>>,
@@ -52,6 +66,8 @@ impl<'own, 'library> Battle<'own, 'library> {
pokemon_per_side: u8,
random_seed: Option<u128>,
) -> Self {
// If no seed was passed, we use the current time as seed for the RNG, otherwise we use the
// seed.
let random = if let Some(seed) = random_seed {
BattleRandom::new_with_seed(seed)
} else {
@@ -74,7 +90,6 @@ impl<'own, 'library> Battle<'own, 'library> {
has_ended: AtomicBool::new(false),
result: Atomic::new(BattleResult::Inconclusive),
event_hook: Default::default(),
history_holder: Box::new(HistoryHolder {}),
current_turn: AtomicU32::new(0),
volatile_scripts: Default::default(),
last_turn_time: Atomic::new(chrono::Duration::zero()),
@@ -89,53 +104,64 @@ impl<'own, 'library> Battle<'own, 'library> {
battle
}
/// The library the battle uses for handling.
pub fn library(&self) -> &'own DynamicLibrary {
self.library
}
/// A list of all different parties in the battle.
pub fn parties(&self) -> &Vec<BattleParty<'own, 'library>> {
&self.parties
}
/// Whether or not Pokemon can flee from the battle.
pub fn can_flee(&self) -> bool {
self.can_flee
}
/// The number of sides in the battle. Typically 2.
pub fn number_of_sides(&self) -> u8 {
self.number_of_sides
}
/// The number of Pokemon that can be on each side.
pub fn pokemon_per_side(&self) -> u8 {
self.pokemon_per_side
}
/// A list of all sides in the battle.
pub fn sides(&self) -> &Vec<BattleSide<'own, 'library>> {
&self.sides
}
/// A mutable list of all sides in the battle.
pub fn sides_mut(&mut self) -> &mut Vec<BattleSide<'own, 'library>> {
&mut self.sides
}
/// The RNG used for the battle.
pub fn random(&self) -> &BattleRandom {
&self.random
}
/// Whether or not the battle has ended.
pub fn has_ended(&self) -> bool {
self.has_ended.load(Ordering::Relaxed)
}
/// The eventual result of the battle. Inconclusive until the battle is ended.
pub fn result(&self) -> BattleResult {
self.result.load(Ordering::Relaxed)
}
/// The handler to send all events to.
pub fn event_hook(&self) -> &EventHook {
&self.event_hook
}
pub fn history_holder(&self) -> &HistoryHolder {
self.history_holder.deref()
}
/// The index of the current turn. 0 until all choices
pub fn current_turn(&self) -> u32 {
self.current_turn.load(Ordering::Relaxed)
}
/// The time the last turn took to run. Defaults to 0.
pub fn last_turn_time(&self) -> chrono::Duration {
self.last_turn_time.load(Ordering::Relaxed)
}
/// A queue of the yet to be executed choices in a turn.
pub fn current_turn_queue(&self) -> &RwLock<Option<ChoiceQueue<'own, 'library>>> {
&self.current_turn_queue
}
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
pub fn get_pokemon(&self, side: u8, index: u8) -> Option<Arc<Pokemon<'own, 'library>>> {
let side = self.sides.get(side as usize);
side?;
@@ -145,6 +171,9 @@ impl<'own, 'library> Battle<'own, 'library> {
pokemon.unwrap().clone()
}
/// Returns whether a slot on the battlefield can still be filled. If no party is responsible
/// for that slot, or a party is responsible, but has no remaining Pokemon to throw out anymore,
/// this returns false.
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() {
@@ -154,7 +183,10 @@ impl<'own, 'library> Battle<'own, 'library> {
false
}
/// Validates whether the battle is still in a non-ended state. If the battle has ended, this
/// properly sets who has won etc.
pub fn validate_battle_state(&self) {
// If we've already ended, we dont need to run this.
if self.has_ended() {
return;
}
@@ -189,6 +221,7 @@ impl<'own, 'library> Battle<'own, 'library> {
self.has_ended.store(true, Ordering::SeqCst);
}
/// Checks whether a choice is actually possible.
pub fn can_use(&self, choice: &TurnChoice) -> bool {
// If the user is not usable, we obviously can;t use the choice.
if !choice.user().is_usable() {
@@ -211,6 +244,7 @@ impl<'own, 'library> Battle<'own, 'library> {
true
}
/// Try and set the choice for the battle. If the choice is not valid, this returns false.
pub fn try_set_choice(&mut self, choice: TurnChoice<'own, 'library>) -> PkmnResult<bool> {
if !self.can_use(&choice) {
return Ok(false);
@@ -227,6 +261,8 @@ impl<'own, 'library> Battle<'own, 'library> {
Ok(true)
}
/// Checks to see whether all Pokemon on the field have set their choices. If so, we then run
/// the turn.
fn check_choices_set_and_run(&self) -> PkmnResult<()> {
for side in &self.sides {
if !side.all_choices_set() {
@@ -292,7 +328,8 @@ impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> {
}
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.library.load_script(ScriptCategory::Battle, key)
self.library
.load_script((self as *const Self).cast(), ScriptCategory::Battle, key)
}
}

View File

@@ -1,20 +1,29 @@
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::models::pokemon_party::PokemonParty;
use std::sync::Arc;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::models::pokemon_party::PokemonParty;
/// A battle party is a wrapper around a party, with the indices for which the party is responsible
/// on the field attached.
#[derive(Debug)]
pub struct BattleParty<'own, 'library> {
/// The party the BattleParty is holding.
party: Arc<PokemonParty<'own, 'library>>,
/// The indices for which the party is responsible, in the format (side, index)
responsible_indices: Vec<(u8, u8)>,
}
impl<'own, 'library> BattleParty<'own, 'library> {
/// Initializes a battle party with the underlying party, and the indices the party is responsible
/// for.
pub fn new(party: Arc<PokemonParty<'own, 'library>>, responsible_indices: Vec<(u8, u8)>) -> Self {
Self {
party,
responsible_indices,
}
}
/// Checks whether the party is responsible for the given index.
pub fn is_responsible_for_index(&self, side: u8, index: u8) -> bool {
for responsible_index in &self.responsible_indices {
if responsible_index.0 == side && responsible_index.1 == index {
@@ -24,6 +33,7 @@ impl<'own, 'library> BattleParty<'own, 'library> {
false
}
/// Whether or not the party has non fainted Pokemon that could be thrown out into the field.
pub fn has_pokemon_not_in_field(&self) -> bool {
for pokemon in self.party.pokemon().iter().flatten() {
if pokemon.is_usable() && !pokemon.is_on_battlefield() {
@@ -33,6 +43,7 @@ impl<'own, 'library> BattleParty<'own, 'library> {
false
}
/// Gets a Pokemon at an index.
pub fn get_pokemon(&self, index: usize) -> &Option<Arc<Pokemon<'own, 'library>>> {
self.party.at(index)
}

View File

@@ -7,32 +7,43 @@ use crate::dynamic_data::script_handling::ScriptSource;
use crate::script_hook;
use crate::utils::Random;
/// The RNG for a battle.
#[derive(Default)]
pub struct BattleRandom {
/// The actual underlying RNG. This is in a mutex, so it is thread safe, and can be ran
/// predictably, with guaranteed the same outputs.
random: Mutex<Random>,
}
impl BattleRandom {
/// Initializes a new RNG with a given seed.
pub fn new_with_seed(seed: u128) -> Self {
BattleRandom {
random: Mutex::new(Random::new(seed)),
}
}
/// Returns the underlying random number generator.
pub fn get_rng(&self) -> &Mutex<Random> {
&self.random
}
/// Get a random 32 bit integer. Can be any value between min int and max int.
pub fn get(&self) -> i32 {
return self.get_rng().lock().unwrap().get();
}
/// Get a random 32 bit integer between 0 and max.
pub fn get_max(&self, max: i32) -> i32 {
return self.get_rng().lock().unwrap().get_max(max);
}
/// Get a random 32 bit integer between min and max.
pub fn get_between(&self, min: i32, max: i32) -> i32 {
return self.get_rng().lock().unwrap().get_between(min, max);
}
/// Gets whether or not a move triggers its secondary effect. This takes its chance, and
/// rolls whether it triggers. As a side effect this run scripts to allow modifying this random
/// chance.
pub fn effect_chance(
&self,
mut chance: f32,

View File

@@ -1,5 +1,9 @@
/// The result of a battle.
#[derive(Debug, Copy, Clone)]
pub enum BattleResult {
/// The battle has no winner. Either the battle has not ended, or everyone is dead, or one of
/// the parties has ran away.
Inconclusive,
/// The battle has a winner, with the inner value being the index of the side that has won.
Conclusive(u8),
}

View File

@@ -16,22 +16,35 @@ use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScripts;
use crate::{script_hook, PkmnResult, StringKey};
/// A side on a battle.
#[derive(Debug)]
pub struct BattleSide<'own, 'library> {
/// The index of the side on the battle.
index: u8,
/// The number of Pokemon that can be on the side.
pokemon_per_side: u8,
/// A list of pokemon currently on the battlefield.
pokemon: RwLock<Vec<Option<Arc<Pokemon<'own, 'library>>>>>,
/// The currently set choices for all Pokemon on the battlefield. Cleared when the turn starts.
choices: RwLock<Vec<Option<TurnChoice<'own, 'library>>>>,
/// The slots on the side that can still be filled. Once all slots are set to false, this side
/// has lost the battle.
fillable_slots: Vec<AtomicBool>,
/// The number of choices that are set.
choices_set: AtomicU8,
/// A reference to the battle we're part of.
battle: *mut Battle<'own, 'library>,
/// Whether or not this side has fled.
has_fled_battle: bool,
/// The volatile scripts that are attached to the side.
volatile_scripts: Arc<ScriptSet>,
/// Data required for this to be a script source.
script_source_data: RwLock<ScriptSourceData>,
}
impl<'own, 'library> BattleSide<'own, 'library> {
/// Instantiates a battle side.
pub fn new(index: u8, pokemon_per_side: u8) -> Self {
let mut pokemon = Vec::with_capacity(pokemon_per_side as usize);
let mut choices = Vec::with_capacity(pokemon_per_side as usize);
@@ -59,39 +72,50 @@ impl<'own, 'library> BattleSide<'own, 'library> {
}
}
/// Set the battle this side belongs to.
pub(crate) fn set_battle(&mut self, battle: *mut Battle<'own, 'library>) {
self.battle = battle;
}
/// The index of the side on the battle.
pub fn index(&self) -> u8 {
self.index
}
/// The number of Pokemon that can be on the side.
pub fn pokemon_per_side(&self) -> u8 {
self.pokemon_per_side
}
/// A list of pokemon currently on the battlefield.
pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<Option<Arc<Pokemon<'own, 'library>>>>> {
self.pokemon.read()
}
/// The currently set choices for all Pokemon on the battlefield. Cleared when the turn starts.
pub fn choices(&self) -> &RwLock<Vec<Option<TurnChoice<'own, 'library>>>> {
&self.choices
}
/// The slots on the side that can still be filled. Once all slots are set to false, this side
/// has lost the battle.
pub fn fillable_slots(&self) -> &Vec<AtomicBool> {
&self.fillable_slots
}
/// The number of choices that are set.
pub fn choices_set(&self) -> u8 {
self.choices_set.load(Ordering::SeqCst)
}
/// A reference to the battle we're part of.
pub fn battle(&self) -> &Battle<'own, 'library> {
unsafe { self.battle.as_ref().unwrap() }
}
/// Whether or not this side has fled.
pub fn has_fled_battle(&self) -> bool {
self.has_fled_battle
}
/// The volatile scripts that are attached to the side.
pub fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.volatile_scripts
}
/// Whether every Pokemon on this side has its choices
pub fn all_choices_set(&self) -> bool {
self.choices_set() == self.pokemon_per_side
}
@@ -110,7 +134,8 @@ impl<'own, 'library> BattleSide<'own, 'library> {
true
}
pub fn set_choice(&self, choice: TurnChoice<'own, 'library>) {
/// Sets a choice for a Pokemon on this side.
pub(crate) fn set_choice(&self, choice: TurnChoice<'own, 'library>) {
for (index, pokemon_slot) in self.pokemon.read().iter().enumerate() {
if let Some(pokemon) = pokemon_slot {
if std::ptr::eq(pokemon.deref(), choice.user().deref()) {
@@ -122,6 +147,7 @@ impl<'own, 'library> BattleSide<'own, 'library> {
}
}
/// Resets all choices on this side.
pub fn reset_choices(&self) {
let len = self.choices.read().len();
for i in 0..len {
@@ -129,10 +155,12 @@ impl<'own, 'library> BattleSide<'own, 'library> {
}
}
/// Forcibly removes a Pokemon from the field.
pub fn force_clear_pokemon(&mut self, index: u8) {
self.pokemon.write()[index as usize] = None;
}
/// Switches out a spot on the field for a different Pokemon.
pub fn set_pokemon(&self, index: u8, pokemon: Option<Arc<Pokemon<'own, 'library>>>) {
{
let old = &self.pokemon.read()[index as usize];
@@ -173,6 +201,7 @@ impl<'own, 'library> BattleSide<'own, 'library> {
}
}
/// Checks whether a Pokemon is on the field in this side.
pub fn is_pokemon_on_side(&self, pokemon: Arc<Pokemon<'own, 'library>>) -> bool {
for p in self.pokemon.read().iter().flatten() {
if std::ptr::eq(p.deref().deref(), pokemon.deref()) {
@@ -182,10 +211,13 @@ impl<'own, 'library> BattleSide<'own, 'library> {
false
}
pub fn mark_slot_as_unfillable(&self, index: u8) {
/// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore.
/// If this happens, the slot can not be used again.
pub(crate) fn mark_slot_as_unfillable(&self, index: u8) {
self.fillable_slots[index as usize].store(false, Ordering::SeqCst);
}
/// Checks whether a slot is unfillable or not.
pub fn is_slot_unfillable(&self, pokemon: Arc<Pokemon<'own, 'library>>) -> bool {
for (i, slot) in self.pokemon.read().iter().enumerate() {
if let Some(p) = slot {
@@ -197,6 +229,7 @@ impl<'own, 'library> BattleSide<'own, 'library> {
false
}
/// Checks whether the side has been defeated.
pub fn is_defeated(&self) -> bool {
for fillable_slot in &self.fillable_slots {
if fillable_slot.load(Ordering::Relaxed) {
@@ -206,19 +239,23 @@ impl<'own, 'library> BattleSide<'own, 'library> {
true
}
/// Checks whether the side has fled.
pub fn has_fled(&self) -> bool {
self.has_fled_battle
}
/// Mark the side as fled.
pub fn mark_as_fled(&mut self) {
self.has_fled_battle = true;
}
/// Gets a random Pokemon on the given side.
pub fn get_random_creature_index(&self) -> u8 {
// TODO: Consider adding parameter to only get index for available creatures.
self.battle().random().get_max(self.pokemon_per_side as i32) as u8
}
/// Swap two Pokemon on a single side around.
pub fn swap_positions(&mut self, a: u8, b: u8) -> bool {
// If out of range, don't allow swapping.
if a >= self.pokemon_per_side || b >= self.pokemon_per_side {
@@ -265,7 +302,9 @@ impl<'own, 'library> VolatileScripts<'own> for BattleSide<'own, 'library> {
}
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.battle().library().load_script(crate::ScriptCategory::Side, key)
self.battle()
.library()
.load_script((self as *const Self).cast(), crate::ScriptCategory::Side, key)
}
}

View File

@@ -1,5 +1,8 @@
/// A source of damage. This should be as unique as possible.
#[derive(Debug, Clone, Copy)]
pub enum DamageSource {
AttackDamage = 0,
/// The damage is done by a move.
MoveDamage = 0,
/// The damage is done by something else.
Misc = 1,
}

View File

@@ -13,69 +13,99 @@ use crate::dynamic_data::TargetList;
use crate::static_data::MoveData;
use crate::{PkmnResult, PokemonError};
/// A hit data is the data for a single hit, on a single target.
#[derive(Default, Debug)]
pub struct HitData {
/// Whether or not the hit is critical.
critical: AtomicBool,
/// The base power of the hit.
base_power: AtomicU8,
/// The type effectiveness of the hit.
effectiveness: Atomic<f32>,
/// The actual damage of the hit.
damage: AtomicU32,
/// The type id of the type used for the hit.
move_type: AtomicU8,
/// Whether or not the hit has failed.
has_failed: AtomicBool,
}
impl HitData {
/// Whether or not the hit is critical.
pub fn is_critical(&self) -> bool {
self.critical.load(Ordering::Relaxed)
}
/// The base power of the hit.
pub fn base_power(&self) -> u8 {
self.base_power.load(Ordering::Relaxed)
}
/// The type effectiveness of the hit.
pub fn effectiveness(&self) -> f32 {
self.effectiveness.load(Ordering::Relaxed)
}
/// The actual damage of the hit.
pub fn damage(&self) -> u32 {
self.damage.load(Ordering::Relaxed)
}
/// The type id of the type used for the hit.
pub fn move_type(&self) -> u8 {
self.move_type.load(Ordering::Relaxed)
}
/// Whether or not the hit has failed.
pub fn has_failed(&self) -> bool {
self.has_failed.load(Ordering::Relaxed)
}
/// Sets whether or not the hit is critical.
pub fn set_critical(&self, value: bool) {
self.critical.store(value, Ordering::SeqCst);
}
/// Sets the base power of the hit.
pub fn set_base_power(&self, value: u8) {
self.base_power.store(value, Ordering::SeqCst);
}
/// Sets the type effectiveness of the hit.
pub fn set_effectiveness(&self, value: f32) {
self.effectiveness.store(value, Ordering::SeqCst);
}
/// Sets the actual damage of the hit.
pub fn set_damage(&self, value: u32) {
self.damage.store(value, Ordering::SeqCst);
}
/// Sets the move type id of the hit.
pub fn set_move_type(&self, value: u8) {
self.move_type.store(value, Ordering::SeqCst);
}
/// Marks the hit as failed.
pub fn fail(&self) {
self.has_failed.store(true, Ordering::SeqCst);
}
}
/// An executing move is the data of the move for while it is executing.
#[derive(Debug)]
pub struct ExecutingMove<'own, 'battle, 'library> {
/// The number of hits this move has.
number_of_hits: u8,
/// A list of hits for this move. For multi target multi hit moves, this stores the hits linearly,
/// for example: (target1, hit1), (target1, hit2), (target2, hit1), (target2, hit2), etc.
hits: Vec<HitData>,
/// The user of the move.
user: Arc<Pokemon<'battle, 'library>>,
/// The move the user has actually chosen to do.
chosen_move: Arc<LearnedMove<'library>>,
/// The move that the user is actually going to do.
use_move: &'own MoveData,
/// The script of the move.
script: ScriptContainer,
/// The targets for this move.
targets: &'own TargetList<'battle, 'library>,
/// Data required for this to be a script source.
script_source_data: RwLock<ScriptSourceData>,
}
impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
/// Instantiates an executing move.
pub fn new(
targets: &'own TargetList<'battle, 'library>,
number_of_hits: u8,
@@ -100,26 +130,33 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
script_source_data: Default::default(),
}
}
/// The number of targets this move has.
pub fn target_count(&self) -> usize {
self.targets.len()
}
/// The number of hits this move has per target.
pub fn number_of_hits(&self) -> u8 {
self.number_of_hits
}
/// The user of the move.
pub fn user(&self) -> &Arc<Pokemon<'battle, 'library>> {
&self.user
}
/// The move the user has actually chosen to do.
pub fn chosen_move(&self) -> &Arc<LearnedMove<'library>> {
&self.chosen_move
}
/// The move that the user is actually going to do.
pub fn use_move(&self) -> &'own MoveData {
self.use_move
}
/// The script of the move.
pub fn script(&self) -> &ScriptContainer {
&self.script
}
/// Gets a hit data for a target, with a specific index.
pub fn get_hit_data<'func>(
&'func self,
for_target: &'func Arc<Pokemon<'battle, 'library>>,
@@ -136,6 +173,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
Err(PokemonError::InvalidTargetRequested)
}
/// Checks whether a Pokemon is a target for this move.
pub fn is_pokemon_target(&self, pokemon: &Arc<Pokemon<'battle, 'library>>) -> bool {
for target in self.targets.iter().flatten() {
if std::ptr::eq(target.deref().deref(), pokemon.deref().deref()) {
@@ -145,6 +183,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
false
}
/// Gets the index of the hits in this move where the hits for a specific target start.
pub(crate) fn get_index_of_target(&self, for_target: &Arc<Pokemon<'battle, 'library>>) -> PkmnResult<usize> {
for (index, target) in self.targets.iter().enumerate() {
if let Some(target) = target {
@@ -157,6 +196,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
Err(PokemonError::InvalidTargetRequested)
}
/// Gets a hit based on its raw index.
pub(crate) fn get_hit_from_raw_index(&self, index: usize) -> &HitData {
&self.hits[index]
}

View File

@@ -1,6 +1,7 @@
use crate::static_data::MoveData;
use std::sync::atomic::{AtomicU8, Ordering};
use crate::static_data::MoveData;
#[derive(Debug)]
pub struct LearnedMove<'library> {
move_data: &'library MoveData,
@@ -46,4 +47,15 @@ impl<'a> LearnedMove<'a> {
self.remaining_pp.fetch_sub(amount, Ordering::SeqCst);
true
}
pub fn restore_all_uses(&self) {
self.remaining_pp.store(self.max_pp, Ordering::SeqCst);
}
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);
}
}

View File

@@ -439,7 +439,11 @@ impl<'own, 'library> Pokemon<'own, 'library> {
let ability_script = self
.library
.load_script(ScriptCategory::Ability, self.active_ability().name())
.load_script(
(self as *const Self).cast(),
ScriptCategory::Ability,
self.active_ability().name(),
)
.unwrap();
if let Some(ability_script) = ability_script {
self.ability_script
@@ -626,14 +630,14 @@ impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> {
}
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.library.load_script(ScriptCategory::Pokemon, key)
self.library
.load_script((self as *const Self).cast(), ScriptCategory::Pokemon, key)
}
}
#[cfg(test)]
pub mod test {
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::AbilityIndex;
use crate::static_data::DataLibrary;
use crate::static_data::Gender;