use crate::common_usings::*; use alloc::vec::Vec; 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 { let mut possible_moves = Vec::new(); // Iterate over every mon in the party 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()) } } } } 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(); let party = battle.find_party_for_pokemon(&user); if party.is_none() { choice.fail(); return; } let party = party.unwrap().party(); let possible_moves = Self::get_party_moves(&party, &user); if possible_moves.is_empty() { 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 } } #[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, 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()) } }