2023-01-28 12:59:54 +00:00
|
|
|
use crate::common_usings::*;
|
2022-09-09 18:09:56 +00:00
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
|
|
|
script!(Assist, "assist");
|
|
|
|
|
|
|
|
impl Assist {
|
2023-01-07 12:32:14 +00:00
|
|
|
/// 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.
|
2022-09-09 18:09:56 +00:00
|
|
|
fn get_party_moves(party: &Party, user: &Pokemon) -> Vec<MoveData> {
|
|
|
|
let mut possible_moves = Vec::new();
|
|
|
|
// Iterate over every mon in the party
|
2023-01-07 12:32:14 +00:00
|
|
|
for mon in party.into_iter().flatten() {
|
|
|
|
// Ignore moves from the user
|
|
|
|
if mon.equals(user) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// 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);
|
|
|
|
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()) {
|
|
|
|
possible_moves.push(mv.move_data())
|
2022-09-09 18:09:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
possible_moves
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Script for Assist {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self {}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_name(&self) -> &'static str {
|
|
|
|
Self::get_const_name()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_capabilities(&self) -> &[ScriptCapabilities] {
|
|
|
|
&[ScriptCapabilities::ChangeMove]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn change_move(&self, choice: TurnChoice, move_name: &mut StringKey) {
|
|
|
|
let user = choice.user();
|
|
|
|
let battle = user.battle().unwrap();
|
2023-01-07 12:32:14 +00:00
|
|
|
let party = battle.find_party_for_pokemon(&user);
|
|
|
|
if party.is_none() {
|
|
|
|
choice.fail();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let party = party.unwrap().party();
|
2022-09-09 18:09:56 +00:00
|
|
|
let possible_moves = Self::get_party_moves(&party, &user);
|
|
|
|
|
2023-01-06 13:25:29 +00:00
|
|
|
if possible_moves.is_empty() {
|
2022-09-09 18:09:56 +00:00
|
|
|
choice.fail();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let random = battle.random().get_max(possible_moves.len() as i32);
|
|
|
|
*move_name = possible_moves[random as usize].name();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
2023-01-07 12:32:14 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use alloc::boxed::Box;
|
|
|
|
use alloc::rc::Rc;
|
|
|
|
use alloc::vec;
|
|
|
|
use pkmn_lib_interface::app_interface::{
|
|
|
|
MockBaseTurnChoiceData, MockBattle, MockBattleParty, MockBattleRandom, MockLearnedMove,
|
|
|
|
MockMoveData, MockMoveTurnChoiceData, MockParty, MockPokemon,
|
|
|
|
};
|
|
|
|
|
|
|
|
fn mock_pokemon_with_moves(moves: Vec<StringKey>, equals_user: bool) -> Pokemon {
|
|
|
|
let mut mon = MockPokemon::new();
|
|
|
|
let moves = Rc::new(moves);
|
|
|
|
mon.expect_get_learned_move().returning_st(move |index| {
|
|
|
|
let moves = moves.clone();
|
|
|
|
if index < moves.len() {
|
|
|
|
let mut learned_move = MockLearnedMove::new();
|
|
|
|
learned_move.expect_move_data().returning_st(move || {
|
|
|
|
let mut move_data = MockMoveData::new();
|
|
|
|
let moves = moves.clone();
|
|
|
|
move_data
|
|
|
|
.expect_name()
|
|
|
|
.returning_st(move || moves.get(index).unwrap().clone());
|
|
|
|
Rc::new(move_data)
|
|
|
|
});
|
|
|
|
Some(Rc::new(learned_move))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
mon.expect_equals().return_const(equals_user);
|
|
|
|
Rc::new(mon)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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_get_pokemon()
|
|
|
|
.times(1)
|
|
|
|
.returning_st(move |_| 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);
|
|
|
|
assert_eq!(1, moves.len());
|
|
|
|
assert_eq!(moves[0].name(), "tackle".into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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_get_pokemon()
|
|
|
|
.times(3)
|
|
|
|
.returning_st(move |index| {
|
|
|
|
if index == 0 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move1".into(), "move2".into(), "move3".into()],
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else if index == 1 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move1".into(), "move4".into()],
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else if index == 2 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move2".into(), "move5".into()],
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let party: Party = Rc::new(party);
|
|
|
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
|
|
|
|
|
|
|
let moves = Assist::get_party_moves(&party, &user);
|
|
|
|
assert_eq!(7, moves.len());
|
|
|
|
assert_eq!(moves[0].name(), "move1".into());
|
|
|
|
assert_eq!(moves[1].name(), "move2".into());
|
|
|
|
assert_eq!(moves[2].name(), "move3".into());
|
|
|
|
assert_eq!(moves[3].name(), "move1".into());
|
|
|
|
assert_eq!(moves[4].name(), "move4".into());
|
|
|
|
assert_eq!(moves[5].name(), "move2".into());
|
|
|
|
assert_eq!(moves[6].name(), "move5".into());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn get_party_moves_ignores_user() {
|
|
|
|
let mut party = MockParty::new();
|
|
|
|
party.expect_length().once().return_const(3usize);
|
|
|
|
party
|
|
|
|
.expect_get_pokemon()
|
|
|
|
.times(3)
|
|
|
|
.returning_st(move |index| {
|
|
|
|
if index == 0 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move1".into(), "move2".into(), "move3".into()],
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else if index == 1 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move1".into(), "move4".into()],
|
|
|
|
true,
|
|
|
|
))
|
|
|
|
} else if index == 2 {
|
|
|
|
Some(mock_pokemon_with_moves(
|
|
|
|
vec!["move2".into(), "move5".into()],
|
|
|
|
false,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let party: Party = Rc::new(party);
|
|
|
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
|
|
|
|
|
|
|
let moves = Assist::get_party_moves(&party, &user);
|
|
|
|
assert_eq!(5, moves.len());
|
|
|
|
assert_eq!(moves[0].name(), "move1".into());
|
|
|
|
assert_eq!(moves[1].name(), "move2".into());
|
|
|
|
assert_eq!(moves[2].name(), "move3".into());
|
|
|
|
assert_eq!(moves[3].name(), "move2".into());
|
|
|
|
assert_eq!(moves[4].name(), "move5".into());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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)));
|
|
|
|
let party: Party = Rc::new(party);
|
|
|
|
let user: Pokemon = Rc::new(MockPokemon::new());
|
|
|
|
|
|
|
|
let moves = Assist::get_party_moves(&party, &user);
|
|
|
|
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_get_pokemon()
|
|
|
|
.times(1)
|
|
|
|
.returning_st(move |_| Some(mock_pokemon_with_moves(vec!["tackle".into()], false)));
|
|
|
|
|
|
|
|
let party: Party = Rc::new(party);
|
|
|
|
|
|
|
|
let mut base_turn_choice = MockBaseTurnChoiceData::new();
|
|
|
|
|
|
|
|
base_turn_choice.expect_user().once().returning_st(move || {
|
|
|
|
let party = party.clone();
|
|
|
|
let mut user = MockPokemon::new();
|
|
|
|
user.expect_battle().once().returning_st(move || {
|
|
|
|
let party = party.clone();
|
|
|
|
let mut battle = MockBattle::new();
|
|
|
|
battle
|
|
|
|
.expect_find_party_for_pokemon()
|
|
|
|
.once()
|
|
|
|
.returning_st(move |_| {
|
|
|
|
let party = party.clone();
|
|
|
|
let mut p = MockBattleParty::new();
|
|
|
|
p.expect_party().returning_st(move || party.clone());
|
|
|
|
|
|
|
|
Some(Rc::new(p))
|
|
|
|
});
|
|
|
|
|
|
|
|
battle.expect_random().returning_st(|| {
|
|
|
|
let mut random = MockBattleRandom::new();
|
|
|
|
random.expect_get_max().return_const(0);
|
|
|
|
Rc::new(random)
|
|
|
|
});
|
|
|
|
|
|
|
|
Some(Rc::new(battle))
|
|
|
|
});
|
|
|
|
|
|
|
|
Rc::new(user)
|
|
|
|
});
|
|
|
|
base_turn_choice.expect_fail().never();
|
|
|
|
|
|
|
|
let base_turn_choice = Rc::new(base_turn_choice);
|
|
|
|
|
|
|
|
let mut move_turn_choice = MockMoveTurnChoiceData::new();
|
|
|
|
move_turn_choice
|
|
|
|
.expect_base()
|
|
|
|
.returning_st(move || base_turn_choice.clone());
|
|
|
|
|
|
|
|
let choice_data = TurnChoice::Move(Box::new(move_turn_choice));
|
|
|
|
|
|
|
|
let mut move_name: StringKey = "".into();
|
|
|
|
let script = Assist::new();
|
|
|
|
script.change_move(choice_data, &mut move_name);
|
|
|
|
assert_eq!(move_name, "tackle".into())
|
|
|
|
}
|
|
|
|
}
|