Rework in line with PkmnLib for moving towards Result
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2023-06-22 16:40:51 +02:00
parent 2df5feab26
commit 5e0bd632b9
58 changed files with 1901 additions and 1345 deletions

View File

@@ -1,12 +1,13 @@
pub use crate::script;
pub use alloc::boxed::Box;
pub use alloc::rc::Rc;
pub use alloc::vec::Vec;
pub use atomic_float::AtomicF32;
pub use core::any::Any;
pub use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU32, Ordering};
pub use pkmn_lib_interface::app_interface::list::ImmutableList;
pub use pkmn_lib_interface::app_interface::{
get_volatile_as, BattleSide, DamageSource, DynamicLibrary, EffectParameter, ExecutingMove,
Gender, MoveCategory, MoveData, Party, Pokemon, Statistic, StringKey, TurnChoice,
};
pub use pkmn_lib_interface::handling::{Script, ScriptCapabilities, ScriptOwner};
pub use pkmn_lib_interface::PkmnResult;

View File

@@ -1,4 +1,5 @@
use crate::common_usings::*;
use pkmn_lib_interface::PkmnResult;
script!(Acrobatics, "acrobatics");
@@ -21,14 +22,15 @@ impl Script for Acrobatics {
_target: Pokemon,
_hit: u8,
base_power: &mut u8,
) {
if mv.user().held_item().is_none() {
) -> PkmnResult<()> {
if mv.user().held_item()?.is_none() {
if *base_power >= 128_u8 {
*base_power = 255
} else {
*base_power *= 2;
}
}
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -47,11 +49,11 @@ mod tests {
mv.expect_user().returning(move || {
let mut user = MockPokemon::new();
user.expect_held_item().returning(move || {
if has_held_item {
Ok(if has_held_item {
Some(Rc::new(MockItem::new()))
} else {
None
}
})
});
Rc::new(user)
});
@@ -64,7 +66,9 @@ mod tests {
let script = Acrobatics::new();
let mut base_power = 50u8;
script.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power);
script
.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power)
.unwrap();
assert_eq!(100, base_power);
}
@@ -74,7 +78,9 @@ mod tests {
let script = Acrobatics::new();
let mut base_power = 200u8;
script.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power);
script
.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power)
.unwrap();
assert_eq!(255, base_power);
}
@@ -84,7 +90,9 @@ mod tests {
let script = Acrobatics::new();
let mut base_power = 50u8;
script.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power);
script
.change_base_power(mv, Rc::new(MockPokemon::new()), 0, &mut base_power)
.unwrap();
assert_eq!(50, base_power);
}
}

View File

@@ -1,5 +1,6 @@
use crate::common_usings::*;
use core::mem::transmute;
use pkmn_lib_interface::PkmnResult;
pub struct Acupressure {}
@@ -22,14 +23,15 @@ impl Script for Acupressure {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) -> PkmnResult<()> {
if target.equals(&mv.user()) {
mv.get_hit_data(&target, hit).fail();
return;
mv.get_hit_data(&target, hit)?.fail()?;
return Ok(());
}
let rand_stat: Statistic =
unsafe { transmute(target.battle().unwrap().random().get_between(1, 6) as u8) };
target.change_stat_boost(rand_stat, 2, false);
unsafe { transmute(target.battle()?.unwrap().random().get_between(1, 6)? as u8) };
target.change_stat_boost(rand_stat, 2, false)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -56,13 +58,13 @@ mod tests {
mv.expect_user().returning_st(move || user.clone());
let mut hit_data = MockHitData::new();
hit_data.expect_fail().once();
hit_data.expect_fail().returning(|| Ok(())).once();
let hit_data = Rc::new(hit_data);
mv.expect_get_hit_data()
.returning_st(move |_, _| hit_data.clone());
.returning_st(move |_, _| Ok(hit_data.clone()));
let script = Acupressure::new();
script.on_secondary_effect(Rc::new(mv), u, 0);
script.on_secondary_effect(Rc::new(mv), u, 0).unwrap();
}
#[test]
@@ -76,11 +78,11 @@ mod tests {
random.expect_get_between().returning_st(|low, high| {
assert_eq!(1, low);
assert_eq!(6, high);
1
Ok(1)
});
Rc::new(random)
});
Some(Rc::new(battle))
Ok(Some(Rc::new(battle)))
});
user.expect_change_stat_boost()
.once()
@@ -88,7 +90,7 @@ mod tests {
assert_eq!(Statistic::Attack, stat);
assert_eq!(2, amount);
assert!(!self_inflicted);
true
Ok(true)
});
let user = Rc::new(user);
@@ -98,6 +100,6 @@ mod tests {
mv.expect_user().returning_st(move || user.clone());
let script = Acupressure::new();
script.on_secondary_effect(Rc::new(mv), u, 0);
script.on_secondary_effect(Rc::new(mv), u, 0).unwrap();
}
}

View File

@@ -1,4 +1,5 @@
use crate::common_usings::*;
use pkmn_lib_interface::PkmnResult;
pub struct AfterYou {}
@@ -21,15 +22,16 @@ impl Script for AfterYou {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) -> PkmnResult<()> {
if !target
.battle()
.battle()?
.unwrap()
.choice_queue()
.move_pokemon_choice_next(&target)
.move_pokemon_choice_next(&target)?
{
mv.get_hit_data(&target, hit).fail()
mv.get_hit_data(&target, hit)?.fail()?
}
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -53,7 +55,7 @@ mod tests {
choice_queue
.expect_move_pokemon_choice_next()
.once()
.return_const(true);
.returning_st(move |_| Ok(true));
Rc::new(choice_queue)
});
@@ -62,11 +64,13 @@ mod tests {
target
.expect_battle()
.once()
.return_once_st(move || Some(battle));
.return_once_st(move || Ok(Some(battle)));
let target = Rc::new(target);
let script = AfterYou::new();
script.on_secondary_effect(Rc::new(MockExecutingMove::new()), target, 0);
script
.on_secondary_effect(Rc::new(MockExecutingMove::new()), target, 0)
.unwrap();
}
#[test]
@@ -77,7 +81,7 @@ mod tests {
choice_queue
.expect_move_pokemon_choice_next()
.once()
.return_const(false);
.returning_st(move |_| Ok(false));
Rc::new(choice_queue)
});
@@ -86,18 +90,18 @@ mod tests {
target
.expect_battle()
.once()
.return_once_st(move || Some(battle));
.return_once_st(move || Ok(Some(battle)));
let target = Rc::new(target);
let mut mv = MockExecutingMove::new();
mv.expect_get_hit_data().once().returning_st(move |_, _| {
let mut hit = MockHitData::new();
hit.expect_fail().once();
Rc::new(hit)
hit.expect_fail().returning(|| Ok(())).once();
Ok(Rc::new(hit))
});
let mv = Rc::new(mv);
let script = AfterYou::new();
script.on_secondary_effect(mv, target, 0);
script.on_secondary_effect(mv, target, 0).unwrap();
}
}

View File

@@ -1,12 +1,13 @@
use crate::common_usings::*;
use alloc::vec::Vec;
use pkmn_lib_interface::PkmnResult;
script!(Assist, "assist");
impl Assist {
/// Gets all the learned moves from the entire party, except the ones from the user, and the ones
/// that are not allowed to be copied.
fn get_party_moves(party: &Party, user: &Pokemon) -> Vec<MoveData> {
fn get_party_moves(party: &Party, user: &Pokemon) -> PkmnResult<Vec<MoveData>> {
let mut possible_moves = Vec::new();
// Iterate over every mon in the party
for mon in party.into_iter().flatten() {
@@ -16,7 +17,7 @@ impl Assist {
}
// Iterate over all moves. We make the assumption of 4 moves.
for move_index in 0..4 {
let mv = mon.get_learned_move(move_index);
let mv = mon.get_learned_move(move_index)?;
if let Some(mv) = mv {
// Make sure we can copy the move, otherwise add it as possible move.
if crate::utils::copyable_moves::can_copy_move(&mv.move_data()) {
@@ -25,7 +26,7 @@ impl Assist {
}
}
}
possible_moves
Ok(possible_moves)
}
}
@@ -42,24 +43,25 @@ impl Script for Assist {
&[ScriptCapabilities::ChangeMove]
}
fn change_move(&self, choice: TurnChoice, move_name: &mut StringKey) {
fn change_move(&self, choice: TurnChoice, move_name: &mut StringKey) -> PkmnResult<()> {
let user = choice.user();
let battle = user.battle().unwrap();
let party = battle.find_party_for_pokemon(&user);
let battle = user.battle()?.unwrap();
let party = battle.find_party_for_pokemon(&user)?;
if party.is_none() {
choice.fail();
return;
choice.fail()?;
return Ok(());
}
let party = party.unwrap().party();
let possible_moves = Self::get_party_moves(&party, &user);
let possible_moves = Self::get_party_moves(&party, &user)?;
if possible_moves.is_empty() {
choice.fail();
return;
choice.fail()?;
return Ok(());
}
let random = battle.random().get_max(possible_moves.len() as i32);
let random = battle.random().get_max(possible_moves.len() as i32)?;
*move_name = possible_moves[random as usize].name();
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -93,9 +95,9 @@ mod tests {
.returning_st(move || moves.get(index).unwrap().clone());
Rc::new(move_data)
});
Some(Rc::new(learned_move))
Ok(Some(Rc::new(learned_move)))
} else {
None
Ok(None)
}
});
mon.expect_equals().return_const(equals_user);
@@ -105,16 +107,19 @@ mod tests {
#[test]
fn get_party_moves_returns_moves_from_party_length_1() {
let mut party = MockParty::new();
party.expect_length().once().return_const(1usize);
party
.expect_length()
.once()
.returning_st(move || Ok(1usize));
party
.expect_get_pokemon()
.times(1)
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["tackle".into()], false)));
.returning_st(move |_| Ok(Some(mock_pokemon_with_moves(vec!["tackle".into()], false))));
let party: Party = Rc::new(party);
let user: Pokemon = Rc::new(MockPokemon::new());
let moves = Assist::get_party_moves(&party, &user);
let moves = Assist::get_party_moves(&party, &user).unwrap();
assert_eq!(1, moves.len());
assert_eq!(moves[0].name(), "tackle".into())
}
@@ -122,35 +127,38 @@ mod tests {
#[test]
fn get_party_moves_returns_moves_from_party_length_7() {
let mut party = MockParty::new();
party.expect_length().once().return_const(3usize);
party
.expect_length()
.once()
.returning_st(move || Ok(3usize));
party
.expect_get_pokemon()
.times(3)
.returning_st(move |index| {
if index == 0 {
Some(mock_pokemon_with_moves(
Ok(Some(mock_pokemon_with_moves(
vec!["move1".into(), "move2".into(), "move3".into()],
false,
))
)))
} else if index == 1 {
Some(mock_pokemon_with_moves(
Ok(Some(mock_pokemon_with_moves(
vec!["move1".into(), "move4".into()],
false,
))
)))
} else if index == 2 {
Some(mock_pokemon_with_moves(
Ok(Some(mock_pokemon_with_moves(
vec!["move2".into(), "move5".into()],
false,
))
)))
} else {
None
Ok(None)
}
});
let party: Party = Rc::new(party);
let user: Pokemon = Rc::new(MockPokemon::new());
let moves = Assist::get_party_moves(&party, &user);
let moves = Assist::get_party_moves(&party, &user).unwrap();
assert_eq!(7, moves.len());
assert_eq!(moves[0].name(), "move1".into());
assert_eq!(moves[1].name(), "move2".into());
@@ -164,12 +172,15 @@ mod tests {
#[test]
fn get_party_moves_ignores_user() {
let mut party = MockParty::new();
party.expect_length().once().return_const(3usize);
party
.expect_length()
.once()
.returning_st(move || Ok(3usize));
party
.expect_get_pokemon()
.times(3)
.returning_st(move |index| {
if index == 0 {
Ok(if index == 0 {
Some(mock_pokemon_with_moves(
vec!["move1".into(), "move2".into(), "move3".into()],
false,
@@ -186,13 +197,13 @@ mod tests {
))
} else {
None
}
})
});
let party: Party = Rc::new(party);
let user: Pokemon = Rc::new(MockPokemon::new());
let moves = Assist::get_party_moves(&party, &user);
let moves = Assist::get_party_moves(&party, &user).unwrap();
assert_eq!(5, moves.len());
assert_eq!(moves[0].name(), "move1".into());
assert_eq!(moves[1].name(), "move2".into());
@@ -204,26 +215,31 @@ mod tests {
#[test]
fn get_party_moves_ignores_non_copyable_move() {
let mut party = MockParty::new();
party.expect_length().once().return_const(1usize);
party
.expect_get_pokemon()
.times(1)
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["chatter".into()], false)));
.expect_length()
.once()
.returning_st(move || Ok(1usize));
party.expect_get_pokemon().times(1).returning_st(move |_| {
Ok(Some(mock_pokemon_with_moves(vec!["chatter".into()], false)))
});
let party: Party = Rc::new(party);
let user: Pokemon = Rc::new(MockPokemon::new());
let moves = Assist::get_party_moves(&party, &user);
let moves = Assist::get_party_moves(&party, &user).unwrap();
assert_eq!(0, moves.len());
}
#[test]
fn if_moves_found_assist_uses_that_move() {
let mut party = MockParty::new();
party.expect_length().once().return_const(1usize);
party
.expect_length()
.once()
.returning_st(move || Ok(1usize));
party
.expect_get_pokemon()
.times(1)
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["tackle".into()], false)));
.returning_st(move |_| Ok(Some(mock_pokemon_with_moves(vec!["tackle".into()], false))));
let party: Party = Rc::new(party);
@@ -243,16 +259,16 @@ mod tests {
let mut p = MockBattleParty::new();
p.expect_party().returning_st(move || party.clone());
Some(Rc::new(p))
Ok(Some(Rc::new(p)))
});
battle.expect_random().returning_st(|| {
let mut random = MockBattleRandom::new();
random.expect_get_max().return_const(0);
random.expect_get_max().returning_st(move |_| Ok(0));
Rc::new(random)
});
Some(Rc::new(battle))
Ok(Some(Rc::new(battle)))
});
Rc::new(user)
@@ -270,7 +286,7 @@ mod tests {
let mut move_name: StringKey = "".into();
let script = Assist::new();
script.change_move(choice_data, &mut move_name);
script.change_move(choice_data, &mut move_name).unwrap();
assert_eq!(move_name, "tackle".into())
}
}

View File

@@ -1,4 +1,5 @@
use crate::common_usings::*;
use pkmn_lib_interface::PkmnResult;
script!(Assurance, "assurance");
@@ -18,20 +19,22 @@ impl Script for Assurance {
]
}
fn on_before_turn(&self, choice: TurnChoice) {
fn on_before_turn(&self, choice: TurnChoice) -> PkmnResult<()> {
if let TurnChoice::Move(data) = &choice {
let side: BattleSide = choice
.user()
.battle()
.battle()?
.unwrap()
.sides()
.get(data.target_side() as u32)
.unwrap();
.get(data.target_side() as usize)
.unwrap()
.clone();
side.add_volatile(Box::new(AssuranceData {
for_position: data.target_index(),
has_hit: AtomicBool::new(false),
}));
}))?;
}
Ok(())
}
fn change_base_power(
@@ -40,15 +43,16 @@ impl Script for Assurance {
target: Pokemon,
_hit: u8,
base_power: &mut u8,
) {
) -> PkmnResult<()> {
if let Some(s) = get_volatile_as::<AssuranceData>(
target.battle_side().as_ref(),
target.battle_side()?.as_ref(),
AssuranceData::get_const_name(),
) {
)? {
if s.has_hit.load(Ordering::Relaxed) {
*base_power *= 2;
}
}
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -79,9 +83,10 @@ impl Script for AssuranceData {
&[ScriptCapabilities::OnEndTurn, ScriptCapabilities::OnDamage]
}
fn on_end_turn(&self) {
fn on_end_turn(&self) -> PkmnResult<()> {
let side = self.get_owner().unwrap().as_side();
side.remove_volatile(self);
side.remove_volatile(self)?;
Ok(())
}
fn on_damage(
@@ -90,10 +95,11 @@ impl Script for AssuranceData {
_source: DamageSource,
_old_health: u32,
_new_health: u32,
) {
if pokemon.battle_index() == self.for_position {
) -> PkmnResult<()> {
if pokemon.battle_index()? == self.for_position {
self.has_hit.store(true, Ordering::Relaxed);
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -1,5 +1,6 @@
use crate::common_usings::*;
use crate::pokemon::infatuated::Infatuated;
use pkmn_lib_interface::PkmnResult;
script!(Attract, "attract");
@@ -16,19 +17,22 @@ impl Script for Attract {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) {
let user_gender = mv.user().gender();
let target_gender = target.gender();
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) -> PkmnResult<()> {
let user_gender = mv.user().gender()?;
let target_gender = target.gender()?;
// If the move is used on a Pokémon that is the same gender as the user, it will fail
if target_gender == user_gender {
return mv.get_hit_data(&target, hit).fail();
mv.get_hit_data(&target, hit)?.fail()?;
return Ok(());
}
// It will also fail if used by or on a gender-unknown Pokémon
if user_gender == Gender::Genderless || target_gender == Gender::Genderless {
return mv.get_hit_data(&target, hit).fail();
mv.get_hit_data(&target, hit)?.fail()?;
return Ok(());
}
// If the target is the opposite gender of the Pokémon who launched the move, the target becomes infatuated
target.add_volatile_by_name(Infatuated::get_const_name());
target.add_volatile_by_name(Infatuated::get_const_name())?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -2,6 +2,7 @@ use crate::common_usings::*;
use crate::moves::light_screen::LightScreenEffect;
use crate::moves::reflect::ReflectEffect;
use crate::weather::hail::Hail;
use pkmn_lib_interface::PkmnResult;
script!(AuroraVeil, "aurora_veil");
@@ -18,21 +19,27 @@ impl Script for AuroraVeil {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) {
if target.battle().unwrap().has_weather(Hail::get_const_name()) {
return mv.get_hit_data(&target, hit).fail();
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) -> PkmnResult<()> {
if target
.battle()?
.unwrap()
.has_weather(Hail::get_const_name())?
{
mv.get_hit_data(&target, hit)?.fail()?;
return Ok(());
}
let binding = target.battle_side();
let binding = target.battle_side()?;
let script = binding
.add_volatile(Box::new(AuroraVeilEffect::new()))
.add_volatile(Box::new(AuroraVeilEffect::new()))?
.as_any()
.downcast_ref::<AuroraVeilEffect>()
.unwrap();
if mv.user().has_held_item("light_clay") {
if mv.user().has_held_item("light_clay")? {
script.turns.store(8, Ordering::SeqCst);
} else {
script.turns.store(5, Ordering::SeqCst);
}
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -66,30 +73,32 @@ impl Script for AuroraVeilEffect {
target: Pokemon,
hit: u8,
damage: &mut u32,
) {
if mv.get_hit_data(&target, hit).is_critical() {
return;
) -> PkmnResult<()> {
if mv.get_hit_data(&target, hit)?.is_critical()? {
return Ok(());
}
let side = self.get_owner().unwrap().as_side();
if side.has_volatile(ReflectEffect::get_const_name())
if side.has_volatile(ReflectEffect::get_const_name())?
&& mv.use_move().category() == MoveCategory::Physical
{
return;
return Ok(());
}
if side.has_volatile(LightScreenEffect::get_const_name())
if side.has_volatile(LightScreenEffect::get_const_name())?
&& mv.use_move().category() == MoveCategory::Special
{
return;
return Ok(());
}
let mut modifier = 2.0;
if target.battle().unwrap().pokemon_per_side() > 1 {
if target.battle()?.unwrap().pokemon_per_side()? > 1 {
modifier = 1.5
}
*damage = (*damage as f32 / modifier) as u32;
Ok(())
}
fn on_end_turn(&self) {
todo!()
fn on_end_turn(&self) -> PkmnResult<()> {
// TODO
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -1,4 +1,5 @@
use crate::common_usings::*;
use pkmn_lib_interface::PkmnResult;
script!(Automize, "automize");
@@ -15,18 +16,19 @@ impl Script for Automize {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) -> PkmnResult<()> {
let user = mv.user();
let stats = user.boosted_stats();
let original_speed = stats.speed();
let original_weight = user.weight();
user.change_stat_boost(Statistic::Speed, 2, true);
if user.boosted_stats().speed() != original_speed {
let original_speed = stats.speed()?;
let original_weight = user.weight()?;
user.change_stat_boost(Statistic::Speed, 2, true)?;
if user.boosted_stats().speed()? != original_speed {
user.set_weight(original_weight - 100.0);
if user.weight() != original_weight {
if user.weight()? != original_weight {
// TODO: Became nimble dialog.
}
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -1,4 +1,6 @@
use crate::common_usings::*;
use alloc::vec::Vec;
use pkmn_lib_interface::PkmnResult;
script!(
ChangeAllTargetStats,
@@ -27,22 +29,24 @@ impl Script for ChangeAllTargetStats {
fn on_initialize(
&self,
_library: DynamicLibrary,
parameters: Option<ImmutableList<Rc<EffectParameter>>>,
) {
parameters: Option<Vec<Rc<EffectParameter>>>,
) -> PkmnResult<()> {
self.amount.store(
parameters.unwrap().get(0).unwrap().as_int() as i8,
Ordering::SeqCst,
);
Ok(())
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, _hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, _hit: u8) -> PkmnResult<()> {
let user = mv.user();
let amount = self.amount.load(Ordering::SeqCst);
target.change_stat_boost(Statistic::Attack, amount, user.equals(&target));
target.change_stat_boost(Statistic::Defense, amount, user.equals(&target));
target.change_stat_boost(Statistic::SpecialAttack, amount, user.equals(&target));
target.change_stat_boost(Statistic::SpecialDefense, amount, user.equals(&target));
target.change_stat_boost(Statistic::Speed, amount, user.equals(&target));
target.change_stat_boost(Statistic::Attack, amount, user.equals(&target))?;
target.change_stat_boost(Statistic::Defense, amount, user.equals(&target))?;
target.change_stat_boost(Statistic::SpecialAttack, amount, user.equals(&target))?;
target.change_stat_boost(Statistic::SpecialDefense, amount, user.equals(&target))?;
target.change_stat_boost(Statistic::Speed, amount, user.equals(&target))?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -36,20 +36,22 @@ macro_rules! change_stat_effect {
fn on_initialize(
&self,
_library: DynamicLibrary,
parameters: Option<ImmutableList<Rc<EffectParameter>>>,
) {
parameters: Option<Vec<Rc<EffectParameter>>>,
) -> PkmnResult<()> {
self.amount.store(
parameters.unwrap().get(0).unwrap().as_int() as i8,
Ordering::SeqCst,
);
Ok(())
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, _hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, _hit: u8) -> PkmnResult<()> {
target.change_stat_boost(
Statistic::$stat,
self.amount.load(Ordering::SeqCst),
mv.user().equals(&target),
);
)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -15,21 +15,19 @@ impl Script for CurePartyStatus {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) {
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) -> PkmnResult<()> {
let user = mv.user();
user.clear_status();
let party = user.battle().unwrap().find_party_for_pokemon(&user);
let party = user.battle()?.unwrap().find_party_for_pokemon(&user)?;
if let Some(party) = party {
let p = party.party();
for index in 0..p.length() {
let mon = p.get_pokemon(index);
if let Some(mon) = mon {
if !mon.equals(&user) {
mon.clear_status();
}
for mon in p.as_ref().into_iter().flatten() {
if !mon.equals(&user) {
mon.clear_status();
}
}
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -23,22 +23,24 @@ impl Script for Drain {
fn on_initialize(
&self,
_library: DynamicLibrary,
parameters: Option<ImmutableList<Rc<EffectParameter>>>,
) {
parameters: Option<Vec<Rc<EffectParameter>>>,
) -> PkmnResult<()> {
self.heal_modifier.store(
parameters.unwrap().get(0).unwrap().as_float(),
Ordering::SeqCst,
);
Ok(())
}
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) {
let hit_data = mv.get_hit_data(&target, hit);
let damage = hit_data.damage();
fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) -> PkmnResult<()> {
let hit_data = mv.get_hit_data(&target, hit)?;
let damage = hit_data.damage()?;
let mut modifier = self.heal_modifier.load(Ordering::SeqCst);
if mv.user().has_held_item("big_root") {
if mv.user().has_held_item("big_root")? {
modifier *= 1.3;
}
mv.user().heal((damage as f32 * modifier) as u32, false);
mv.user().heal((damage as f32 * modifier) as u32, false)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -15,8 +15,14 @@ impl Script for Flinch {
&[ScriptCapabilities::OnSecondaryEffect]
}
fn on_secondary_effect(&self, _move: ExecutingMove, target: Pokemon, _hit: u8) {
target.add_volatile(Box::new(FlinchEffect::new()));
fn on_secondary_effect(
&self,
_move: ExecutingMove,
target: Pokemon,
_hit: u8,
) -> PkmnResult<()> {
target.add_volatile(Box::new(FlinchEffect::new()))?;
Ok(())
}
fn as_any(&self) -> &dyn Any {
@@ -39,9 +45,10 @@ impl Script for FlinchEffect {
&[ScriptCapabilities::PreventMove]
}
fn prevent_move(&self, mv: ExecutingMove, prevent: &mut bool) {
fn prevent_move(&self, mv: ExecutingMove, prevent: &mut bool) -> PkmnResult<()> {
*prevent = true;
mv.user().remove_volatile(self);
mv.user().remove_volatile(self)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -28,16 +28,17 @@ impl Script for HealEachEndOfTurn {
fn on_initialize(
&self,
_library: DynamicLibrary,
parameters: Option<ImmutableList<Rc<EffectParameter>>>,
) {
parameters: Option<Vec<Rc<EffectParameter>>>,
) -> PkmnResult<()> {
self.heal_percent.store(
parameters.unwrap().get(0).unwrap().as_float() / 100.0,
Ordering::SeqCst,
);
Ok(())
}
fn on_secondary_effect(&self, _mv: ExecutingMove, target: Pokemon, _hit: u8) {
let script = target.add_volatile_by_name("heal_each_end_of_turn_effect");
fn on_secondary_effect(&self, _mv: ExecutingMove, target: Pokemon, _hit: u8) -> PkmnResult<()> {
let script = target.add_volatile_by_name("heal_each_end_of_turn_effect")?;
let amount = self.heal_percent.load(Ordering::SeqCst);
script
.as_any()
@@ -45,6 +46,7 @@ impl Script for HealEachEndOfTurn {
.unwrap()
.heal_percent
.store(amount, Ordering::SeqCst);
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -21,17 +21,23 @@ impl Script for MultiHitMove {
&[ScriptCapabilities::ChangeNumberOfHits]
}
fn change_number_of_hits(&self, choice: TurnChoice, number_of_hits: &mut u8) {
fn change_number_of_hits(&self, choice: TurnChoice, number_of_hits: &mut u8) -> PkmnResult<()> {
// 35% chance that it will hit 2 times, a 35% chance it will hit 3 times, a 15% chance it
// will hit 4 times, and a 15% chance it will hit 5 times.
let rand_value = choice.user().battle().unwrap().random().get_between(0, 100);
let rand_value = choice
.user()
.battle()?
.unwrap()
.random()
.get_between(0, 100)?;
*number_of_hits = match rand_value {
0..=34 => 2,
35..=69 => 3,
70..=84 => 4,
85..=100 => 5,
_ => *number_of_hits,
}
};
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -20,12 +20,23 @@ impl Script for Struggle {
]
}
fn change_number_of_hits(&self, _choice: TurnChoice, number_of_hits: &mut u8) {
*number_of_hits = 1
fn change_number_of_hits(
&self,
_choice: TurnChoice,
number_of_hits: &mut u8,
) -> PkmnResult<()> {
*number_of_hits = 1;
Ok(())
}
fn is_invulnerable(&self, _move: ExecutingMove, _target: Pokemon, invulnerable: &mut bool) {
fn is_invulnerable(
&self,
_move: ExecutingMove,
_target: Pokemon,
invulnerable: &mut bool,
) -> PkmnResult<()> {
*invulnerable = false;
Ok(())
}
fn change_effectiveness(
@@ -34,16 +45,18 @@ impl Script for Struggle {
_target: Pokemon,
_hit: u8,
effectiveness: &mut f32,
) {
) -> PkmnResult<()> {
*effectiveness = 1.0;
Ok(())
}
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) {
let mut damage = mv.user().max_health() / 4;
fn on_secondary_effect(&self, mv: ExecutingMove, _target: Pokemon, _hit: u8) -> PkmnResult<()> {
let mut damage = mv.user().max_health()? / 4;
if damage == 0 {
damage = 1
}
mv.user().damage(damage, DamageSource::Struggle);
mv.user().damage(damage, DamageSource::Struggle)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -21,15 +21,16 @@ impl Script for HealEachEndOfTurnEffect {
&[ScriptCapabilities::OnEndTurn]
}
fn on_end_turn(&self) {
fn on_end_turn(&self) -> PkmnResult<()> {
if let Some(ScriptOwner::Pokemon(pokemon)) = self.get_owner() {
let mut amount =
pokemon.max_health() as f32 * self.heal_percent.load(Ordering::Relaxed);
if pokemon.has_held_item("big_root") {
pokemon.max_health()? as f32 * self.heal_percent.load(Ordering::Relaxed);
if pokemon.has_held_item("big_root")? {
amount *= 1.3;
}
pokemon.heal(amount as u32, false);
pokemon.heal(amount as u32, false)?;
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -15,10 +15,11 @@ impl Script for Infatuated {
&[ScriptCapabilities::PreventMove]
}
fn prevent_move(&self, mv: ExecutingMove, prevent: &mut bool) {
if mv.user().battle().unwrap().random().get_max(2) == 0 {
fn prevent_move(&self, mv: ExecutingMove, prevent: &mut bool) -> PkmnResult<()> {
if mv.user().battle()?.unwrap().random().get_max(2)? == 0 {
*prevent = true
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -4,6 +4,7 @@ use crate::pokemon::*;
use alloc::boxed::Box;
use pkmn_lib_interface::app_interface::{get_hash, StringKey};
use pkmn_lib_interface::handling::{Script, ScriptCategory};
use pkmn_lib_interface::println;
macro_rules! resolve_match {
(
@@ -24,10 +25,11 @@ macro_rules! resolve_match {
}
pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option<Box<dyn Script>> {
println!("Getting script {:?}", name);
match category {
ScriptCategory::Move => {
resolve_match! {
name.hash(),
name.hash().unwrap(),
acrobatics::Acrobatics,
acupressure::Acupressure,
after_you::AfterYou,
@@ -54,13 +56,13 @@ pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option<Box<dyn
ScriptCategory::Status => {}
ScriptCategory::Pokemon => {
resolve_match! {
name.hash(),
name.hash().unwrap(),
infatuated::Infatuated,
pokemon::heal_each_end_of_turn::HealEachEndOfTurnEffect,
}
}
ScriptCategory::Battle => {
resolve_match! {name.hash(), crate::util_scripts::ForceEffectTriggerScript,}
resolve_match! {name.hash().unwrap(), crate::util_scripts::ForceEffectTriggerScript,}
}
ScriptCategory::Side => {}
ScriptCategory::ItemBattleTrigger => {}

View File

@@ -27,9 +27,10 @@ impl Script for ForceEffectTriggerScript {
_target: Pokemon,
_hit: u8,
chance: &mut f32,
) {
) -> PkmnResult<()> {
// Set to 50_000% chance.
*chance = 50_000.0;
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@@ -5,7 +5,7 @@ macro_rules! non_copyable {
$mv:ident,
$($move_name:literal),+
) => {
match $mv.name().hash() {
match $mv.name().hash().unwrap() {
0
$(
| const { get_hash($move_name) }