@@ -33,6 +33,7 @@ class Pokemon:
3333 "_last_request" ,
3434 "_level" ,
3535 "_max_hp" ,
36+ "_mimic_move" ,
3637 "_forme_change_ability" ,
3738 "_moves" ,
3839 "_must_recharge" ,
@@ -125,6 +126,7 @@ def __init__(
125126 self ._temporary_ability : Optional [str ] = None
126127 self ._forme_change_ability : Optional [str ] = None
127128 self ._temporary_types : List [PokemonType ] = []
129+ self ._mimic_move : Optional [Move ] = None
128130
129131 if request_pokemon :
130132 self .update_from_request (request_pokemon )
@@ -155,6 +157,8 @@ def __str__(self) -> str:
155157 def _add_move (self , move_id : str ) -> Optional [Move ]:
156158 """Store the move if applicable."""
157159 id_ = Move .retrieve_id (move_id )
160+ if id_ in self .moves :
161+ return self .moves [id_ ]
158162 if not Move .should_be_stored (id_ , self ._data .gen ):
159163 return None
160164 if id_ not in self ._moves :
@@ -210,6 +214,8 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
210214 pkmn_request ["active" ] == self .active
211215 ), f"{ pkmn_request ['active' ]} != { self .active } \n request: { pkmn_request } "
212216 if self .item == "unknown_item" :
217+ # needed for ability initialization in start of game,
218+ # done anyway in update_from_request()
213219 self ._item = pkmn_request ["item" ]
214220 if self ._data .gen > 4 :
215221 assert pkmn_request ["item" ] == (
@@ -222,11 +228,18 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
222228 ), f"{ pkmn_request ['condition' ]} != { self .hp_status } \n request: { pkmn_request } "
223229 if self .base_species == "mew" :
224230 return
225- for move_request , move in zip (pkmn_request ["moves" ], self .moves .values ()):
226- assert Move .retrieve_id (move_request ) == Move .retrieve_id (
227- move .id
228- ), f"{ Move .retrieve_id (move_request )} != { Move .retrieve_id (move .id )} \n request: { pkmn_request } "
231+ if not (
232+ self ._mimic_move is not None
233+ and self ._mimic_move .id in [m .id for m in self ._moves .values ()]
234+ ):
235+ # Ensures no duplicate move due to mimic copying an already-known move
236+ for move_request , move in zip (pkmn_request ["moves" ], self .moves .values ()):
237+ assert Move .retrieve_id (move_request ) == Move .retrieve_id (
238+ move .id
239+ ), f"{ Move .retrieve_id (move_request )} != { Move .retrieve_id (move .id )} \n request: { pkmn_request } "
229240 if self .ability is None :
241+ # needed for ability initialization in start of game,
242+ # done anyway in update_from_request()
230243 self .ability = pkmn_request ["baseAbility" ]
231244 assert pkmn_request ["baseAbility" ] == (
232245 self .base_ability or ""
@@ -237,10 +250,7 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
237250 ), f"{ pkmn_request ['ability' ]} != { self .ability or '' } "
238251
239252 def check_move_consistency (self , active_request : Dict [str , Any ]):
240- if (
241- self .base_species in ["ditto" , "mew" ]
242- or "copycat" in [m .id for m in self .moves .values ()]
243- ):
253+ if self .base_species in ["ditto" , "mew" ]:
244254 return
245255 for move_request in active_request ["moves" ]:
246256 matches = [
@@ -252,7 +262,8 @@ def check_move_consistency(self, active_request: Dict[str, Any]):
252262 if not matches :
253263 continue
254264 move = matches [0 ]
255- if "pp" in move_request and self ._data .gen not in [7 , 8 ]:
265+ if "pp" in move_request and self ._data .gen not in [1 , 2 , 3 , 7 , 8 ]:
266+ # exclude early gens because of unreliable Showdown event messages
256267 # exclude gen 7 and 8 because of Z-move and Max Move PP untrackability
257268 assert (
258269 move_request ["pp" ] == move .current_pp
@@ -368,6 +379,7 @@ def faint(self):
368379 self ._current_hp = 0
369380 self ._status = Status .FNT
370381 self .temporary_ability = None
382+ self ._mimic_move = None
371383 self ._clear_effects ()
372384
373385 def forme_change (self , species : str ):
@@ -563,6 +575,7 @@ def switch_out(self, fields: Dict[Field, int]):
563575 self ._protect_counter = 0
564576 self .temporary_ability = None
565577 self ._temporary_types = []
578+ self ._mimic_move = None
566579
567580 if self ._status == Status .TOX :
568581 self ._status_counter = 0
@@ -603,7 +616,7 @@ def _update_from_pokedex(self, species: str, store_species: bool = True):
603616 self ._possible_abilities = [
604617 to_id_str (ability ) for ability in dex_entry ["abilities" ].values ()
605618 ]
606- if len (self ._possible_abilities ) == 1 :
619+ if len (self ._possible_abilities ) == 1 and self . _data . gen >= 3 :
607620 self .ability = self ._possible_abilities [0 ]
608621 else :
609622 self .forme_change_ability = None
@@ -955,6 +968,18 @@ def first_turn(self) -> bool:
955968 """
956969 return self ._first_turn
957970
971+ @property
972+ def forme_change_ability (self ) -> Optional [str ]:
973+ """
974+ :return: The pokemon's ability after changing forme. None if the pokemon hasn't changed forme.
975+ :rtype: str, optional
976+ """
977+ return self ._forme_change_ability
978+
979+ @forme_change_ability .setter
980+ def forme_change_ability (self , ability : Optional [str ]):
981+ self ._forme_change_ability = to_id_str (ability ) if ability is not None else None
982+
958983 @property
959984 def gender (self ) -> Optional [PokemonGender ]:
960985 """
@@ -1036,25 +1061,21 @@ def max_hp(self) -> int:
10361061 """
10371062 return self ._max_hp or 0
10381063
1039- @property
1040- def forme_change_ability (self ) -> Optional [str ]:
1041- """
1042- :return: The pokemon's ability after changing forme. None if the pokemon hasn't changed forme.
1043- :rtype: str, optional
1044- """
1045- return self ._forme_change_ability
1046-
1047- @forme_change_ability .setter
1048- def forme_change_ability (self , ability : Optional [str ]):
1049- self ._forme_change_ability = to_id_str (ability ) if ability is not None else None
1050-
10511064 @property
10521065 def moves (self ) -> Dict [str , Move ]:
10531066 """
10541067 :return: A dictionary of the pokemon's known moves.
10551068 :rtype: Dict[str, Move]
10561069 """
1057- return self ._moves
1070+ if self ._mimic_move is not None :
1071+ return dict (
1072+ [
1073+ (k , v ) if k != "mimic" else (self ._mimic_move .id , self ._mimic_move )
1074+ for k , v in self ._moves .items ()
1075+ ]
1076+ )
1077+ else :
1078+ return self ._moves
10581079
10591080 @property
10601081 def must_recharge (self ) -> bool :
0 commit comments