
    Ii5                    *   S r SSKJrJr  SSKJr  SSKrSSKJrJr  SSK	J
r
  SS	KJrJrJrJr  SS
KJr  SSKJrJr  SSKJr  SSKJrJr  SSKJr  SSKJr  SSKJrJ r J!r!J"r"J#r#J$r$J%r%J&r&  SSK'J(r(  SSK)J*r*  SSK+J,r,  Sr-\\
 " S S\5      5       5       r.g)z
Mile by Mile Game Implementation for PlayPalace v11.

A racing card game based on Mille Bornes. Players race to reach a target distance
while playing hazards on opponents and defending with safeties.
    )	dataclassfield)datetimeN   )GamePlayer)register_game   )Action	ActionSet	MenuInput
Visibility)	BotHelper)
GameResultPlayerResult)RoundTransitionTimer)TeamTeamManagerLocalization)KeybindState   )CardDeckCardType
HazardType
RemedyType
SafetyTypeHAZARD_TO_SAFETYSAFETY_TO_HAZARD)MileByMileOptionsMileByMilePlayer)	RaceState   c                   	  ^  \ rS rSr% Sr\" \S9r\\   \	S'   \" \
S9r\
\	S'   \" \S9r\\	S'   \" \S9r\\   \	S'   \" \S9r\\   \	S'   \" \S9r\\   \	S	'   S
r\\	S'   Sr\S-  \	S'   Sr\S-  \	S'   Sr\S-  \	S'   S
r\\	S'   Sr\\	S'   S
r\\	S'   U 4S jrSU 4S jjr\S\4S j5       r\S\4S j5       r \S\4S j5       r!\S\4S j5       r"\S\4S j5       r#SS\S\S\$S\4S jjr%SS  jr&S!\S\S-  4S" jr'S#\S\S-  4S$ jr(S!\S\4S% jr)S\$4S& jr*S\4S' jr+S!\S\4S( jr,S!\S)\SS4S* jr-S!\S)\SS4S+ jr.S, r/S#\S\04S- jr1S#\S\04U 4S. jjr2SU 4S/ jjr3S#\SS4S0 jr4S#\5S\S-  4S1 jr6S#\5S\74S2 jr8S#\5S\74S3 jr9S#\5S\74S4 jr:S#\5S\S-  4S5 jr;S#\5S\74S6 jr<S#\5S\S-  4S7 jr=S#\5S\74S8 jr>S#\5S\S-  4S9 jr?S#\5S\74S: jr@S#\5S;\S\4S< jrAS#\SS4S= jrBSS> jrCS#\S?\S\$4S@ jrD SS#\S?\SA\S\4SB jjrESC\S?\SA\S\4SD jrFSC\S?\SA\S\4SE jrGSC\SA\S\S-  4SF jrHSG\SA\S\4SH jrISI\SA\S\4SJ jrJSI\SA\S\4SK jrKS?\SA\S\4SL jrLSC\S?\S\$4SM jrMS#\S?\S\$4SN jrNSO\SP\SQ\S\$4SR jrOSC\S?\S\$4SS jrPS#\SO\S\\   4ST jrQS#\5S;\SS4SU jrRS#\5S;\SS4SV jrSS#\5S;\SS4SW jrTS#\5S\\   4SX jrUS#\5S\\   S\S-  4SY jrVS#\5SS4SZ jrWS#\5S;\SS4S[ jrX SS#\S\\S?\S]\S-  SS4
S^ jjrYS#\S\\S?\SS4S_ jrZ SS#\S\\S?\S`\S-  SS4
Sa jjr[S#\SS4Sb jr\Sc\\   S`\S-  S\S-  4Sd jr]S#\Se\Sf\Sg\^Sh\S?\S\$S-  4Si jr_S#\S?\Sg\^Sh\SS4
Sj jr`S#\S?\Sg\^Sh\SS4
Sk jraSg\^SS4Sl jrbS#\S\\S?\SS4Sm jrc SS#\S\\S?\Sn\$SS4
So jjrdS#\S\\S?\SS4Sp jreS#\S\\S?\SS4Sq jrfS#\S\S-  4Sr jrgSSs jrhS\\   4U 4St jjriSSu jrjSSv jrkSSw jrlSSx jrmSSy jrnSSz jroS{\S-  SS4S| jrpS\S-  4S} jrqS~\SS4S jrrS\s4S jrtS\sSA\S\\   4S jruS\S\S-  4S jrvSQ\S\S\SS4S jrwSQ\S\SS4S jrxS#\S\SS4S jryS\S?\SS4S jrzS#\S?\Sn\$SS4S jr{SU 4S jjr|S#\S\S-  4S jr}S#\S\S-  4S jr~S#\S?\SC\S\S\$S\4S jrS#\S?\S\S\$S\4
S jrS#\S?\SC\S\4S jrS?\SC\S\S\$S\4
S jrS#\S?\SC\S\4S jrS?\SC\S\4S jrSrU =r$ )MileByMileGame(   a  
Mile by Mile - A racing card game based on Mille Bornes.

Players race to reach a target distance by playing distance cards.
Hazards slow opponents, remedies fix problems, and safeties provide
permanent protection. First team to reach the winning score wins.
)default_factoryplayersoptionsdeckdiscard_pileprotections_pilerace_statesr   current_raceNrace_winner_team_indexdirty_trick_window_teamdirty_trick_window_hazarddirty_trick_window_ticksidleround_timer_stateround_timer_ticksc                 @   > [         TU ]  5         [        U SS9U l        g)zInitialize runtime state.      $@delay_secondsN)super__post_init__r   _round_timerself	__class__s    ;c:\Users\dbart\PlayPalace11\server\games\milebymile\game.pyr=   MileByMileGame.__post_init__G   s    0TJ    returnc                 @   > [         TU ]  5         [        U SS9U l        g)z3Rebuild non-serialized state after deserialization.r9   r:   N)r<   rebuild_runtime_stater   r>   r?   s    rB   rG   $MileByMileGame.rebuild_runtime_stateL   s    %'0TJrD   c                     g)NzMile by Mile clss    rB   get_nameMileByMileGame.get_nameQ   s    rD   c                     g)N
milebymilerJ   rK   s    rB   get_typeMileByMileGame.get_typeU   s    rD   c                     g)Nzcategory-card-gamesrJ   rK   s    rB   get_categoryMileByMileGame.get_categoryY   s    $rD   c                     g)Nr   rJ   rK   s    rB   get_min_playersMileByMileGame.get_min_players]       rD   c                     g)N	   rJ   rK   s    rB   get_max_playersMileByMileGame.get_max_playersa   rY   rD   	player_idnameis_botc                     [        XUS9$ )zCreate a new player.)idr_   r`   r"   )r@   r^   r_   r`   s       rB   create_playerMileByMileGame.create_playere   s    9GGrD   c                 N   U R                  5       nU Vs/ s H  o"R                  PM     nnU R                  R                  nSU;   d  [	        S U 5       5      (       a  [
        R                  " U5      nX@R                  l        U R                  R                  U5        U HB  nU R                  R                  UR                  5      nU(       d  M1  UR                  Ul        MD     U R                  R                   Vs/ s H  n[        5       PM     snU l        gs  snf s  snf )zSet up teams using TeamManager. c              3   P   #    U  H  oS :w  d  M
  UR                  5       v   M     g7f)vN)isupper).0cs     rB   	<genexpr>.MileByMileGame._setup_teams.<locals>.<genexpr>u   s     "N	1#X;199;;	s   	&&N)get_active_playersr_   r+   	team_modeanyr   parse_display_to_team_mode_team_managersetup_teamsget_teamindex
team_indexteamsr$   r/   )r@   active_playerspplayer_namesro   playerteam_s           rB   _setup_teamsMileByMileGame._setup_teamsm   s    002(6717 LL**	)s"N	"NNN#>>yII'0$&&|4 %F%%..v{{;Dt$(JJ! % 261C1C1I1IJ1IAIK1IJ# 8" Ks   D D"rv   c                 h    SUs=::  a  [        U R                  5      :  a  O  gU R                  U   $ g)z'Get the race state for a team by index.r   N)lenr/   r@   rv   s     rB   get_race_stateMileByMileGame.get_race_state   s3    
2S!1!122 ##J//rD   r{   c                 8    U R                  UR                  5      $ )z'Get the race state for a player's team.)r   rv   r@   r{   s     rB   get_player_race_state$MileByMileGame.get_player_race_state   s    ""6#4#455rD   c                     U[        U R                  R                  5      :  a2  U R                  R                  U R                  R                  U   5      $ SUS-    3$ )z%Get display name for a team by index.Team r   )r   rr   rw   get_team_namer   s     rB   r   MileByMileGame.get_team_name   sU    D..4455%%33D4F4F4L4LZ4XYYzA~&''rD   c                 4    U R                   R                  S:H  $ )z$Check if game is in individual mode.
individual)r+   ro   r@   s    rB   is_individual_mode!MileByMileGame.is_individual_mode   s    ||%%55rD   c                 @    [        U R                  R                  5      $ )zGet the number of teams.)r   rr   rw   r   s    rB   get_num_teamsMileByMileGame.get_num_teams   s    4%%++,,rD   c                     U[        U R                  R                  5      :  a#  U R                  R                  U   R                  $ g)zGet total score for a team.r   r   rr   rw   total_scorer   s     rB   get_team_scoreMileByMileGame.get_team_score   s;    D..4455%%++J7CCCrD   pointsc                     U[        U R                  R                  5      :  a-  U R                  R                  U   =R                  U-  sl        gg)zAdd points to a team's score.Nr   r@   rv   r   s      rB   add_team_scoreMileByMileGame.add_team_score   sB    D..4455$$Z0<<F< 6rD   c                     U[        U R                  R                  5      :  a  X R                  R                  U   l        gg)zSet the round score for a team.N)r   rr   rw   round_scorer   s      rB   set_team_round_score#MileByMileGame.set_team_round_score   s7    D..4455?E$$Z0< 6rD   c              #   R   #    [        U R                  5       H
  u  pX4v   M     g7f)z,Iterate over (team_index, race_state) pairs.N)	enumerater/   )r@   i
race_states      rB   
iter_teamsMileByMileGame.iter_teams   s$     &t'7'78MA- 9   %'c                     [        SS9nUR                  [        SSSSSSS	95        UR                  [        S
SSSSSS	95        U$ )z(Create the turn action set for a player.turn)r_   dirty_trickPlay dirty trick_action_dirty_trick_is_dirty_trick_enabled_is_dirty_trick_hiddenF)rb   labelhandler
is_enabled	is_hiddenshow_in_actions_menu	junk_cardDiscard card_action_junk_card_is_junk_card_enabled_is_junk_card_hidden)r   addr   )r@   r{   
action_sets      rB   create_turn_action_set%MileByMileGame.create_turn_action_set   sd    F+

 	 (-42%*		
 	$+20%*		
 rD   c                   > [         TU ]  U5      n[        SSSSSS9nUR                  U5        UR                  UR
                  ;   a%  UR
                  R                  UR                  5        UR
                  R                  SUR                  5        [        SS	S
SSS9nUR                  U5        UR                  UR
                  ;   a%  UR
                  R                  UR                  5        UR
                  R                  SUR                  5        S H$  nUR                  U5      nU(       d  M  SUl	        M&     U$ )z;Create the standard action set with Mile by Mile overrides.check_statusCheck status_action_check_status_is_check_status_enabled_is_check_status_hidden)rb   r   r   r   r   r   check_status_detailedDetailed status_action_check_status_detailedr   )check_scorescheck_scores_detailedF)
r<   create_standard_action_setr   r   rb   _orderremoveinsert
get_actionr   )r@   r{   r   action	action_idexistingrA   s         rB   r   )MileByMileGame.create_standard_action_set   s   W7?
 *1/
 	v99
)))$$VYY/  FII.&#31/
 	v99
)))$$VYY/  FII.BI!,,Y7Hx05- C rD   c                   > [         TU ]  5         SU R                  ;   a  / U R                  S'   SU R                  ;   a  / U R                  S'   U R                  SSS/[        R
                  SS9  U R                  SSS/[        R
                  SS9  U R                  S	S
S/[        R
                  S9  U R                  SSS/[        R
                  S9  U R                  SSS/[        R
                  S9  g)z!Define all keybinds for the game.szshift+sr   r   T)stateinclude_spectatorsr   r   dr   r   )r   zshift+enterr   r   	backspaceN)r<   setup_keybinds	_keybindsdefine_keybindr   ACTIVEr?   s    rB   r   MileByMileGame.setup_keybinds   s     $.. "$DNN3&(*DNN9% 	%%# 	 	
 	$%%%# 	 	
 	C!3m_LL_L_` 	M>K=P\PcPcdK+lNaNabrD   c                 |   U R                  US5      nU(       d  g[        S[        S-   5       H[  nSU 3nUR                  U5      (       a  UR	                  U5        XBR
                  ;   d  M@  UR
                  R	                  U5        M]     [        UR                  S5       H  u  p5SU 3nU R                  X5      nSnUR                  [        R                  :X  a<  U(       a5  U R                  XR                  5      n[        U5      S:  a  [        SSSS	9nUR!                  [#        US
SSSSUSS95        M     g)z0Update card slot actions based on player's hand.r   Nr   r   
card_slot_zmilebymile-select-target_hazard_target_options_bot_select_hazard_target)promptr+   
bot_select _action_play_card_is_card_action_enabled_is_card_action_hidden_get_card_slot_labelF)rb   r   r   r   r   	get_labelinput_requestr   )get_action_setrange	HAND_SIZEr   r   r   r   hand_can_play_card	card_typer   HAZARD_get_valid_hazard_targetsvaluer   r   r   r   )	r@   r{   turn_setr   r   cardplayabler   targetss	            rB   _update_card_actions#MileByMileGame._update_card_actions  s%   &&vv6 q)a-(A$QC(I""9--	*OO+&&y1 ) !a0GA$QC(I**68H !M~~0X88Lw<!#$-9 8#>%M LL /864"/).	# 1rD   c                 &    U R                   S:w  a  gg)z(Check if check status action is enabled.playingaction-not-playingN)statusr   s     rB   r   'MileByMileGame._is_check_status_enabledN  s    ;;)#'rD   c                 "    [         R                  $ )z:Check status is always hidden (triggered by keybind only).r   HIDDENr   s     rB   r   &MileByMileGame._is_check_status_hiddenT         rD   c                 "    [         R                  $ )z6Hide base score actions (custom status replaces them).r   r   s     rB   _is_check_scores_hidden&MileByMileGame._is_check_scores_hiddenX  r  rD   c                 "    [         R                  $ )z?Hide base detailed score actions (custom status replaces them).r   r   s     rB    _is_check_scores_detailed_hidden/MileByMileGame._is_check_scores_detailed_hidden\  r  rD   c                 |    U R                   S:w  a  gUnU R                  c  gUR                  U R                  :w  a  gg)z'Check if dirty trick action is enabled.r   r   Nz milebymile-no-dirty-trick-windowzmilebymile-not-your-dirty-trick)r   r2   rv   )r@   r{   
mbm_players      rB   r   &MileByMileGame._is_dirty_trick_enabled`  s?    ;;)#''-
''/5  D$@$@@4rD   c                 "    [         R                  $ )z,Dirty trick is always hidden (keybind only).r   r   s     rB   r   %MileByMileGame._is_dirty_trick_hiddenk  r  rD   c                     U R                   S:w  a  gU R                  U:w  a  gU R                  R                  (       a  gg)z%Check if junk card action is enabled.r   r   zaction-not-your-turnmilebymile-between-racesN)r   current_playerr>   	is_activer   s     rB   r   $MileByMileGame._is_junk_card_enabledo  s7    ;;)#'&()&&-rD   c                 "    [         R                  $ )z*Junk card is always hidden (keybind only).r   r   s     rB   r   #MileByMileGame._is_junk_card_hiddeny  r  rD   c                 ^    U R                   S:w  a  gU R                  R                  (       a  gg)z"Check if card actions are enabled.r   r   r  N)r   r>   r  r   s     rB   r   &MileByMileGame._is_card_action_enabled}  s&    ;;)#'&&-rD   c                 b    U R                   S:w  a  [        R                  $ [        R                  $ )z%Card actions are visible during play.r   )r   r   r  VISIBLEr   s     rB   r   %MileByMileGame._is_card_action_hidden  s&    ;;)#$$$!!!rD   r   c                 h   [        U[        5      (       d  g [        UR                  S5      S   5      S-
  nUS:  d  U[        UR                  5      :  a  gUR                  U   nU R                  U5      nU(       a  UR                  OSnU R                  XF5      $ ! [        [
        4 a     gf = f)z)Get dynamic label for a card slot action.r   r}   r   r   en)
isinstancer#   intsplit
ValueError
IndexErrorr   r   get_userlocale_get_localized_card_name)r@   r{   r   slotr   userr$  s          rB   r   #MileByMileGame._get_card_slot_label  s    &"233	ys+B/014D !8ts6;;//{{4 }}V$ $$,,T:: J' 		s    B B10B1c                 &    U R                  U5        g)z)Update dynamic card actions for a player.N)r   r   s     rB   _update_turn_actions#MileByMileGame._update_turn_actions  s    !!&)rD   c                 L    U R                    H  nU R                  U5        M     g)z$Update card actions for all players.N)r*   r*  r   s     rB   _update_all_turn_actions'MileByMileGame._update_all_turn_actions  s    llF%%f- #rD   r   c                 J   U R                  U5      nU(       d  gUR                  [        R                  :X  a  U R	                  X25      $ UR                  [        R
                  :X  a  U R                  X5      $ UR                  [        R                  :X  a  U R                  X25      $ UR                  [        R                  :X  a   UR                  UR                  5      (       + $ UR                  [        R                  :X  a!  UR                  S:X  a  UR                  (       + $ g)zCheck if a card can be played.Ffalse_virtue)r   r   r   DISTANCE_can_play_distancer   _can_play_hazardREMEDY_can_play_remedySAFETY
has_safetyr   SPECIAL	has_karmar@   r{   r   r   s       rB   r   MileByMileGame._can_play_card  s    //7
>>X...**:<<^^x.((66^^x.((::^^x.!,,TZZ888^^x///zz^+%////rD   r$  c                    SSK Jn  U R                  U5      nU(       d  UR                  " US5      $ UR                  [
        R                  :X  a  U R                  XRU5      $ UR                  [
        R                  :X  a  UR                  " US5      $ UR                  [
        R                  :X  a  U R                  XRU5      $ UR                  [
        R                  :X  a  UR                  " US5      $ UR                  [
        R                  :X  a#  UR                  S:X  a  UR                  " US5      $ UR                  " US5      $ )	z7Get a human-readable reason why a card can't be played.r
   r   zmilebymile-reason-not-on-teamzmilebymile-reason-no-targetszmilebymile-reason-has-safetyr0  zmilebymile-reason-has-karmamilebymile-reason-generic)messages.localizationr   r   getr   r   r1  _get_distance_unplayable_reasonr   r4  _get_remedy_unplayable_reasonr6  r8  r   )r@   r{   r   r$  r   r   s         rB   _get_unplayable_reason%MileByMileGame._get_unplayable_reason  s    	://7
##F,KLL>>X...77
&QQ>>X__,##F,JKK>>X__,55jOO>>X__,##F,JKK>>X---$**2N##F,IJJ(CDDrD   r   c                 T   SSK Jn  UR                  nUR                  5       (       dJ  UR	                  [
        R                  5      (       a  UR                  " US5      $ UR                  " US5      $ UR	                  [
        R                  5      (       a  US:  a  UR                  " US5      $ U R                  R                  (       aM  UR                  U-   U R                  R                  :  a&  UR                  " USU R                  R                  S9$ UR                  " US	5      $ )
Nr
   r   zmilebymile-reason-stoppedzmilebymile-reason-has-problem2   zmilebymile-reason-speed-limitz"milebymile-reason-exceeds-distancemilesr=  )r>  r   distancecan_play_distancehas_problemr   STOPr?  SPEED_LIMITr+   only_allow_perfect_crossingrG  round_distance)r@   r   r   r$  r   rH  s         rB   r@  .MileByMileGame._get_distance_unplayable_reason  s     	:==++--%%joo66#''0KLL##F,KLL!!*"8"899hm##F,KLL<<33(*T\\-H-HH#''8,,55 
 (CDDrD   c                    SSK Jn  UR                  nU[        R                  :X  a  UR
                  " US5      $ U[        R                  :X  a  UR                  [        R                  5      (       a  UR
                  " US5      $ UR                  [        R                  5      (       d  UR
                  " US5      $ U R                  X5      nU(       a  U$ U[        R                  :X  a  UR
                  " US5      $ U[        R                  :X  a  UR
                  " US5      $ U[        R                   :X  a  UR
                  " US5      $ UR
                  " US	5      $ )
Nr
   r   z milebymile-reason-no-speed-limitz"milebymile-reason-has-right-of-wayz milebymile-reason-already-movingzmilebymile-reason-has-gaszmilebymile-reason-tires-finezmilebymile-reason-no-accidentr=  )r>  r   r   r   END_OF_LIMITr?  ROLLr7  r   RIGHT_OF_WAYrJ  r   rK  _first_blocking_problemGASOLINE
SPARE_TIREREPAIRS)r@   r   r   r$  r   remedyproblems          rB   rA  ,MileByMileGame._get_remedy_unplayable_reason  s$    	:Z,,,##F,NOOZ__$$$Z%<%<==#''0TUU))*//::#''0RSS22:FGZ(((##F,GHHZ***##F,JKKZ'''##F,KLL(CDDrD   c                     SSK Jn  UR                   HL  nU[        R                  [        R
                  4;  d  M)  U R                  XB5      nUR                  " USUS9s  $    g )Nr
   r   z milebymile-reason-must-fix-first)rY  )r>  r   problemsr   rK  rL  _get_localized_problem_namer?  )r@   r   r$  r   rY  problem_names         rB   rT  &MileByMileGame._first_blocking_problem  s]    9!**Gz
0F0FGG#??P#''6(  + rD   rY  c           
         SSK Jn  [        R                  S[        R                  S[        R
                  S[        R                  S[        R                  S0nUR                  US5      nU(       a  UR                  " X%5      $ U$ )	z-Get localized name for a problem/hazard type.r
   r   milebymile-card-out-of-gasmilebymile-card-flat-tiremilebymile-card-accidentmilebymile-card-speed-limitmilebymile-card-stopr   )	r>  r   r   
OUT_OF_GAS	FLAT_TIREACCIDENTrL  rK  r?  )r@   rY  r$  r   key_mapkeys         rB   r]  *MileByMileGame._get_localized_problem_name  sr    9 !!#?  "=!;""$AOO3
 kk'2&03|,@@rD   safetyc                     SSK Jn  [        R                  S[        R                  S[        R
                  S[        R                  S0nUR                  US5      nU(       a  UR                  " X%5      $ U$ )z%Get localized name for a safety type.r
   r   milebymile-card-extra-tankmilebymile-card-puncture-proofmilebymile-card-driving-acemilebymile-card-right-of-wayr   )r>  r   r   
EXTRA_TANKPUNCTURE_PROOFDRIVING_ACErS  r?  )r@   rl  r$  r   ri  rj  s         rB   _get_localized_safety_name)MileByMileGame._get_localized_safety_name   sh    9 !!#?%%'G""$A##%C	
 kk&"%03|,??rD   c                     [         R                  S[         R                  S[         R                  S[         R                  S0nUR                  US5      nU(       a  [        R
                  " X$5      $ U$ )z,Get localized effect text for a safety card.z#milebymile-safety-effect-extra-tankz'milebymile-safety-effect-puncture-proofz$milebymile-safety-effect-driving-acez%milebymile-safety-effect-right-of-wayr   )r   rr  rs  rt  rS  r?  r   )r@   rl  r$  ri  rj  s        rB   _get_localized_safety_effect+MileByMileGame._get_localized_safety_effect-  se     !!#H%%'P""$J##%L	
 kk&"%03|,??rD   c                    SSK Jn  UR                  [        R                  :X  a  UR
                  " USUR                  S9$ [        R                  S[        R                  S[        R                  S[        R                  S[        R                  S	[        R                  S
[        R                  S[        R                   S[        R"                  S[        R$                  S[&        R(                  S[&        R*                  S[&        R,                  S[&        R.                  SSS0nUR                  UR                  S5      nU(       a  UR
                  " X%5      $ UR0                  $ )zGet localized name for a card.r
   r   zmilebymile-card-milesrF  ra  rb  rc  rd  re  zmilebymile-card-gasolinezmilebymile-card-spare-tirezmilebymile-card-repairszmilebymile-card-end-of-limitzmilebymile-card-green-lightrn  ro  rp  rq  r0  zmilebymile-card-false-virtuer   )r>  r   r   r   r1  r?  r   r   rf  rg  rh  rL  rK  r   rU  rV  rW  rQ  rR  r   rr  rs  rt  rS  r_   )r@   r   r$  r   ri  rj  s         rB   r%  'MileByMileGame._get_localized_card_name8  s   9>>X...##F,C4::VV !!#?  "=!;""$AOO3!;!!#? 9##%COO:!!#?%%'G""$A##%C:'
* kk$**b)03|,BBrD   c                 $   UR                  5       (       d  gUR                  nUR                  [        R                  5      (       a  US:  a  gU R
                  R                  (       a(  UR                  U-   U R
                  R                  :  a  gg)z'Check if team can play a distance card.FrE  T)	rI  rH  rJ  r   rL  r+   rM  rG  rN  )r@   r   r   rH  s       rB   r2  !MileByMileGame._can_play_distanceW  sq    ++--== !!*"8"899hm <<33(*T\\-H-HHrD   c                     U R                  U5      nU(       d  gU R                  5        H9  u  pEXAR                  :X  a  M  U R                  UR                  XS5      (       d  M9    g   g)z.Check if hazard can be played on any opponent.FT)r   r   rv   _can_play_hazard_on_teamr   )r@   r{   r   attacker_state
target_idxtarget_states         rB   r3  MileByMileGame._can_play_hazardi  sZ    33F;(,(9$J...,,TZZVV	 ):
 rD   hazardtargetattackerc                    [         R                  " U5      nU(       a  UR                  U5      (       a  gU R                  R                  (       a#  UR
                  (       d  UR
                  (       a  gU[        R                  :X  a  UR                  U5      (       + $ U R                  R                  (       a  UR                  U5      (       + $ UR                  5       (       + $ )z1Check if hazard can be played on a specific team.F)r   r?  r7  r+   
karma_ruler9  r   rL  rJ  allow_stacking_attackshas_any_problem)r@   r  r  r  blocking_safetys        rB   r  'MileByMileGame._can_play_hazard_on_teamv  s     +..v6v00AA <<""%%&*:*: Z+++))&111 ||22!--f555!11333rD   c                    UR                   nU[        R                  :X  a  UR                  [        R
                  5      $ U[        R                  :X  a  UR                  [        R                  5      (       a  gUR                  [        R                  5      (       d  gUR                   H)  nU[        R                  [        R
                  4;  d  M)    g   g[        R                  [        R                  [        R                  [        R                  [        R                   [        R"                  0nUR%                  U5      nU=(       a    UR                  U5      $ )zCheck if remedy can be played.FT)r   r   rQ  rJ  r   rL  rR  r7  r   rS  rK  r\  rU  rf  rV  rg  rW  rh  r?  )r@   r   r   rX  rY  remedy_to_hazardr  s          rB   r5  MileByMileGame._can_play_remedy  s   Z,,,))**@*@AAZ__$$$Z%<%<==))*//::%..:??J4J4J"KK  /  !6!6!!:#7#7
 3 3

 "%%f-8*0088rD   c                     U R                  U5      nU(       d  / $ / nU R                  5        H@  u  pVXQR                  :X  a  M  U R                  X&U5      (       d  M/  UR	                  U5        MB     U$ )z:Get list of team indices that can be targeted by a hazard.)r   r   rv   r  append)r@   r{   r  r  r   r  r  s          rB   r   (MileByMileGame._get_valid_hazard_targets  sh    33F;I(,(9$J...,,V>RRz*	 ):
 rD   c           
        ^ ^ T R                  U5      nU(       d  gSSKJn  UR                  mUR                  " TS5      nT R                  5        H  u  pgT R                  U5      nU[        T R                  R                  5      :  a  T R                  R                  U   OSn	U	(       a  U	R                  OSn
UR                  (       a'  SR                  UU 4S jUR                   5       5      nOUnUR                  (       a'  SR                  UU 4S jUR                   5       5      nOUnUR                  S	UU
UR                  UUS
9  M     g)zShow game status to player.Nr
   r   milebymile-noner   , c              3   H   >#    U  H  nTR                  UT5      v   M     g 7fNr]  rj   ry   r$  r@   s     rB   rl   6MileByMileGame._action_check_status.<locals>.<genexpr>  %      )I\AD44Q??I\   "c              3   H   >#    U  H  nTR                  UT5      v   M     g 7fr  ru  rj   r   r$  r@   s     rB   rl   r    %      )H[1D33Av>>H[r  zmilebymile-status)r_   r   rG  r\  safeties)r#  r>  r   r$  r?  r   r   r   rr   rw   r   r\  joinr  speak_lrG  )r@   r{   r   r'  r   none_strteam_idxr   r_   r|   scoreproblems_strsafeties_strr$  s   `            @rB   r   #MileByMileGame._action_check_status  s/   }}V$9##F,=>$(OO$5 H%%h/D
 c$"4"4":":;; ""((2 
 )-D$$!E""#yy )ISI\I\)    (""#yy )HRH[H[)    (LL# &&%%  1 %6rD   c                   ^ ^ T R                  U5      nU(       d  gSSKJn  UR                  mUR                  " TS5      n/ nT R                  5        GH  u  pxT R                  U5      n	U[        T R                  R                  5      :  a  T R                  R                  U   OSn
U
(       a  U
R                  OSnUR                  (       a'  SR                  UU 4S jUR                   5       5      nOUnUR                  (       a'  SR                  UU 4S jUR                   5       5      nOUnUR                  U	 S	U S
UR                   SU SU 3	5        GM
     T R!                  X5        g)z/Show detailed game status in a status box menu.Nr
   r   r  r   r  c              3   H   >#    U  H  nTR                  UT5      v   M     g 7fr  r  r  s     rB   rl   ?MileByMileGame._action_check_status_detailed.<locals>.<genexpr>  r  r  c              3   H   >#    U  H  nTR                  UT5      v   M     g 7fr  r  r  s     rB   rl   r  	  r  r  : z	 points, z miles, Problems: z, Safeties: )r#  r>  r   r$  r?  r   r   r   rr   rw   r   r\  r  r  r  rG  
status_box)r@   r{   r   r'  r   r  linesr  r   r_   r|   r  r  r  r$  s   `             @rB   r   ,MileByMileGame._action_check_status_detailed  sW   }}V$9##F,=>$(OO$5 H%%h/D
 c$"4"4":":;; ""((2 
 )-D$$!E ""#yy )ISI\I\)    ( ""#yy )HRH[H[)    ( LL&5':+;+;*<<N|n\hiuhvw9 %6@ 	&rD   c                 X   [        U[        5      (       d  gU R                  U5      nU(       a  U R                  UR                  :w  a  gU R
                  nU(       d  g[        R                  " U5      nU(       d  gSnSn[        UR                  5       H;  u  pU	R                  [        R                  :X  d  M%  U	R                  U:X  d  M7  U	nUn  O   U(       d*  U R                  U5      n
U
(       a  U
R                  S5        gU R!                  XUSS9  SU l        SU l        SU l        g)u*   Handle dirty trick (Coup Fourré) attempt.Nr  zmilebymile-no-matching-safetyTis_dirty_trickr   )r  r#   r   r2   rv   r3   r   r?  r   r   r   r   r6  r   r#  r  _play_safetyr4   )r@   r{   r   r   r  r  safety_card
card_indexr   r   r'  s              rB   r   "MileByMileGame._action_dirty_trick  s   &"233//7
T99V=N=NN// +..v6
 -GA~~0TZZ?5R"
	 . ==(D<= 	&k$O (,$)-&()%rD   c           
      B   [        U[        5      (       d  / $ U R                  R                  UR                  5      nU(       d  / $  [        UR                  S5      S   5      S-
  nUS:  d  U[        UR                  5      :  a  / $ UR                  U   nUR                  [        R                  :w  a  / $ U R                  XR                  5      n/ nU H  nU R                  U   nU R                   R"                  U   n	U R%                  5       (       a0  UR'                  U	R(                  S    SUR*                   S35        Mp  SR-                  U	R(                  5      n
UR'                  SUS-    S	U
 SUR*                   S35        M     U$ ! [         a    / s $ f = f)
z5Get list of valid hazard target names for menu input.r}   r  r   r    ( miles)r  r   r  )r  r#   _pending_actionsr?  rb   r  r   r!  r   r   r   r   r   r   r   r/   rr   rw   r   r  membersrG  r  )r@   r{   r   r&  r   target_indicesr+   r  r   r|   r  s              rB   r   %MileByMileGame._hazard_target_options>  sy   &"233I ))--fii8	I	ys+B/014D !8ts6;;//I{{4 >>X__,I77

K&H))(3J%%++H5D&&(($,,q/!2"Z5E5E4FgNO))DLL1x!|nBwir*BRBRASSZ[\ ' +  	I	s    F FFc                 
  ^  [        U[        5      (       d  gT R                  R                  UR                  5      nU(       d  g [        UR                  S5      S   5      S-
  nUS:  d  U[        UR                  5      :  a  gUR                  U   nUR                  [        R                  :w  a  gT R                  XR                  5      nU(       d  g[        UU 4S jS9nT R                   U   nT R"                  R$                  U   n	T R'                  5       (       a  U	R(                  S    SUR*                   S	3$ S
R-                  U	R(                  5      n
SUS-    SU
 SUR*                   S	3$ ! [         a     gf = f)z7Bot selects hazard target - picks team with most miles.Nr}   r  r   r   c                 6   > TR                   U    R                  $ r  )r/   rG  )r   r@   s    rB   <lambda>:MileByMileGame._bot_select_hazard_target.<locals>.<lambda>{  s    T5E5Ea5H5N5NrD   )rj  r  r  r  r   r  )r  r#   r  r?  rb   r  r   r!  r   r   r   r   r   r   r   maxr/   rr   rw   r   r  rG  r  )r@   r{   r+   r   r&  r   r  best_idxr   r|   r  s   `          rB   r   (MileByMileGame._bot_select_hazard_targeta  s`   &"233))--fii8		ys+B/014D !8ts6;;//{{4 >>X__,77

K ~+NO%%h/
!!''1""$$ll1o&b)9)9(:'BBii-G8a<.7)2j6F6F5GwOO/  		s    E5 5
FFc                    [        U[        5      (       d  gU R                  U:w  a  g[        U5      S:X  a  US   nSnO[        U5      S:X  a  Uu  pCOg [	        UR                  S5      S   5      S-
  nUS:  d  U[        UR                  5      :  a  gUR                  U   nU R                  X5      (       a  U R                  XXd5        gUR                  (       a  U R                  XU5        gU R                  U5      nU(       aH  U R                  XUR                  5      nU R                  XgR                  5      n	UR!                  SXS9  gg! [         a     gf = f)	zHandle playing a card from hand.

Can be called as:
- _action_play_card(player, action_id) - no input
- _action_play_card(player, input_value, action_id) - with menu input
Nr   r   r   r}   r  zmilebymile-cant-play)r   reason)r  r#   r  r   r  r   r!  r   r   
_play_cardr`   _discard_cardr#  rB  r$  r%  r  )
r@   r{   argsr   input_valuer&  r   r'  r  	card_names
             rB   r    MileByMileGame._action_play_card  sD    &"233 &( t9>QIKY!^%)"K	ys+B/014D !8ts6;;//{{4 v,,OOF$< }}""66}}V,!88t{{SF $ = =dKK PILL!7iLW !  		s    E 
EEc                    [        U[        5      (       d  gU R                  U:w  a  gU R                  U5      nUR                  nU(       a  UR                  S5      (       d*  U R                  U5      nU(       a  UR                  S5        g [        UR                  S5      S   5      S-
  nUS:  d  U[        UR                  5      :  a  gUR                  U   nU R                  R                  (       d@  U R                  X5      (       a*  U R                  U5      nU(       a  UR                  S5        gU R!                  XU5        g! [         a     gf = f)	zQHandle discarding the currently selected card (shift+enter or backspace keybind).Nr   zmilebymile-no-card-selectedr}   r  r   r   z milebymile-cant-discard-playable)r  r#   r  get_action_contextmenu_item_id
startswithr#  r  r  r   r!  r   r   r+   always_allow_discardingr   r  )r@   r{   r   contextr  r'  r&  r   s           rB   r    MileByMileGame._action_junk_card  s*   &"233 &( ))&1++<#:#:<#H#H==(D:;	|))#.r23a7D !8ts6;;//{{4  ||33""600}}V,LL!CD6.!  		s    E
 

EEr&  target_namec                    UR                   [        R                  :X  a  U R                  XU5        gUR                   [        R                  :X  a  U R                  XX45        gUR                   [        R                  :X  a  U R                  XU5        gUR                   [        R                  :X  a  U R                  XUSS9  gUR                   [        R                  :X  a  U R                  XU5        gg)zPlay a card from hand.Fr  N)r   r   r1  _play_distancer   _play_hazardr4  _play_remedyr6  r  r8  _play_special)r@   r{   r&  r   r  s        rB   r  MileByMileGame._play_card  s     >>X...d3^^x.fD>^^x.fD1^^x.fDG^^x///vT2 0rD   c                    U R                  U5      nU(       d  gUR                  nUR                  R                  U5        U=R                  U-  sl        US:X  a  SUl        U R                  S[        R                  " SS5       S35        SS	S	S	S	S
.nXV;   a1  [        R                  " SXe   5      nU R                  SU SU S35        U R                  5       (       a'  U R                  SUR                  UUR                  S9  O&U R                  SUR                  UUR                  S9  U R                  R                  U5        UR                  U R                  R                  :  a  UR                  U R                  R                  :X  ai  U R                  R                   (       dN  U R                  5       (       a  U R                  SUR                  S9  OkU R                  SUR"                  S-   S9  OMU R                  5       (       a  U R                  SUR                  S9  OU R                  SUR"                  S-   S9  U R                  S5        UR"                  U l        U R'                  5         g)zPlay a distance card.N   Tgame_cards/playr      .oggr   r
   )   rE  K   d   r  zgame_milebymile/rG  z$milebymile-plays-distance-individual)r{   rH  totalzmilebymile-plays-distance-teamz.milebymile-journey-complete-perfect-individualr{   z(milebymile-journey-complete-perfect-team)r|   z&milebymile-journey-complete-individualz milebymile-journey-complete-teamzgame_milebymile/winround.ogg)r   rH  r   poprG  used_200_mile
play_soundrandomrandintr   broadcast_lr_   r-   r  r+   rN  rM  rv   r1   	_end_turn)r@   r{   r&  r   r   rH  sound_variantsvariants           rB   r  MileByMileGame._play_distance  s8   //7
==H$s?'+J$ 	/&..A*>)?tDE  QAAA>%nnQ(@AGOO.xjgYdKL ""$$6{{! &&	   0{{! &&	   	  & t||:::  DLL$?$??@@**,,$$H%{{ % 
 $$BIZIZ]^I^ %  **,,$$%MV\VaVa$b$$%GfN_N_bcNc$dOO:;*0*;*;D'rD   target_selectionc                    U R                  U5      nU(       d  gU R                  XR                  5      nU(       d  U R                  U5        gU R	                  Xd5      nUc  gU R
                  U   nU R                  R                  U   n	UR                  R                  U5        U R                  XXXs5      n
U
c  gUR                  R                  U5        UR                  UR                  5        UR                  [        R                  :w  aC  UR!                  ["        R$                  5      (       d  UR                  [        R&                  5        U R)                  XX5        U
(       a  U R+                  XR,                  5        Xpl        UR                  U l        SU l        U R5                  U	5        U R7                  5         g)z"Play a hazard card on an opponent.N<   )r   r   r   _notify_no_hazard_targets_resolve_hazard_targetr/   rr   rw   r   r  _resolve_karma_attackbattle_piler  add_problemr   rL  r7  r   rS  rK  _announce_hazard_play_announce_attacker_shunnedrv   r2   r3   r4   _schedule_dirty_trick_botsr  )r@   r{   r&  r   r  r  r  r  r  target_teamattacker_shunneds              rB   r  MileByMileGame._play_hazard1  sx    33F;77

K**6200R
''
3((..z:55Lz
 # 	  ''-  , ::///**:+B+BCC((9""6I ++F4E4EF (2$)-&(*% 	''4rD   c                 X    U R                  U5      nU(       a  UR                  S5        g g )Nzmilebymile-no-valid-targets)r#  r  )r@   r{   r'  s      rB   r  (MileByMileGame._notify_no_hazard_targetsj  s%    }}V$LL67 rD   r  c                 "   U(       a  U R                  5       (       ai  SU;   a  UR                  S5      S   OUnU HF  nU R                  R                  U   nUR                  (       d  M/  UR                  S   U:X  d  MD  Us  $    g UR                  S5      (       aE   [        UR                  S5      S   R                  SS5      5      nU H  nUS-   U:X  d  M  Us  $    g g [        U5      S:X  a  US   $ US   $ ! [        [        4 a     g f = f)Nr  r   r   :r   r   )r   r   rr   rw   r  r  r  replacer!  r"  r   )r@   r  r  r_   idxr|   team_nums          rB   r  %MileByMileGame._resolve_hazard_targeto  s   
 &&(( // %**403) 
 *C--33C8D|||Q4(?"
 *  $..w77$#&'7'='=c'B1'E'M'MgWY'Z#[#1C"Qw(2'*
 $2
 ~!#!!$$a   '
3 $#$s   9C; C; C; ;DDr  r  r  r  c                    SnU R                   R                  (       a  UR                  (       a  UR                  (       a  SUl        SUl        U R                  S[        R
                  " SS5       S35        U R                  XXE5        U R                  XR                  U5        U R                  R                  U5        U R                  5         g UR                  (       a  UR                  (       d	  SUl        SnU$ )NFr  r   r  r  T)r+   r  r9  r  r  r  _announce_hazard_target_announce_karma_clashrv   r-   r  r  )r@   r{   r  r  r  r  r   r  s           rB   r  $MileByMileGame._resolve_karma_attack  s     !<<""''L,B,B+0().&/&..A2F1Gt LM,,V;S**63D3DjQ!!((. ''0F0F+0(#' rD   c           
         U R                  S[        R                  " SS5       S35        [        R                  S[        R                  " SS5       S3[        R
                  S[        R                  S[        R                  S	[        R                  S
0nUR                  U;   a  U R                  XRR                     5        U R                  XX45        g )Nr  r   r  r  zgame_milebymile/crashr   zgame_milebymile/outofgas.oggzgame_milebymile/flat.oggzgame_milebymile/stop.oggzgame_milebymile/speedlimit.ogg)r  r  r  r   rh  rf  rg  rK  rL  r   r  )r@   r{   r   r  r  hazard_soundss         rB   r  $MileByMileGame._announce_hazard_play  s     	/&..A*>)?tDE#819M8Nd!S!!#A  "<OO7""$D
 ::&OOM**56$$V;KrD   c                     U R                  5       (       a,  UR                  S   nU R                  SUUR                  US9  g U R                  SUUR                  US-   S9  g )Nr   z"milebymile-plays-hazard-individual)r{   r  zmilebymile-plays-hazard-teamr   )r{   r|   )r   r  _broadcast_card_messager_   )r@   r{   r   r  r  r  s         rB   r  &MileByMileGame._announce_hazard_target  so     ""$$%--a0K((4{{"	 )  ((.{{!^	 ) rD   c                     UR                    HZ  nU R                  U5      nU(       d  M  UR                  (       d  M0  [        R                  " U[
        R                  " SS5      S9  M\     g )N      ticks)r  _get_player_by_namer`   r   jolt_botr  r  )r@   r  member_namemembers       rB   r  )MileByMileGame._schedule_dirty_trick_bots  sJ    &..K--k:Fv&---""6B1GH /rD   c                    U R                  U5      nU(       d  gUR                  R                  U5        UR                  R	                  U5        UR
                  nU R                  S[        R                  " SS5       S35        U[        R                  :X  a2  UR                  [        R                  5        U R                  S5        GOEU[        R                  :X  aJ  UR                  [        R                  5        U R                  S[        R                  " SS5       S35        OU[        R                   :X  a1  UR                  [        R"                  5        U R                  S	5        OU[        R$                  :X  a1  UR                  [        R&                  5        U R                  S
5        O]U[        R(                  :X  aI  UR                  [        R*                  5        U R                  S[        R                  " SS5       S35        U R-                  SX1R.                  S9  U R0                  R	                  U5        U R3                  5         g)zPlay a remedy card.Nr  r   r  r  z!game_milebymile/speedlimitend.oggzgame_milebymile/greenlightr
   zgame_milebymile/gas.oggzgame_milebymile/sparetyre.oggzgame_milebymile/repairr   zmilebymile-plays-cardr  )r   r   r  r  r  r   r  r  r  r   rQ  remove_problemr   rL  rR  rK  rU  rf  rV  rg  rW  rh  r  r_   r-   r  )r@   r{   r&  r   r   rX  s         rB   r  MileByMileGame._play_remedy  s   //7
%%d+/&..A*>)?tDEZ,,,%%j&<&<=OO?@z&%%joo6OO819M8NdSTz***%%j&;&;<OO56z,,,%%j&:&:;OO;<z)))%%j&9&9:OO4V^^Aq5I4J$OP$$%<d;;$W  &rD   r  c                    U R                  U5      nU(       d  gUR                  R                  U5        UR                  UR                  5        U(       Ga#  U=R
                  S-  sl        U R                  UUSS9  U R                  S5        [        R                  " UR                  5      nU(       a  UR                  U5        UR                  [        R                  :X  a>  UR                  [        R                  5        UR                  [        R                  5        [!        UR"                  5      S:X  a=  [        R                  UR"                  ;   a  UR                  [        R                  5        GOWU R                  UUSS9  U R                  S[$        R&                  " SS5       S	35        [        R(                  S
[        R*                  S[$        R&                  " SS5       S	3[        R,                  S[        R                  S0nUR                  U;   a  U R                  XsR                     5        [        R                  " UR                  5      nU(       a  UR                  U5        UR                  [        R                  :X  a>  UR                  [        R                  5        UR                  [        R                  5        U R.                  R1                  U5        U R3                  U5      nU(       a_  UR                  R1                  U5        U R5                  U5      n	U	(       a,  U R7                  XR8                  5      n
U	R;                  SU
SS9  U R=                  U5        U R?                  U5        UR@                  (       a+  [B        RD                  " U[$        R&                  " SS5      S9  gg)zPlay a safety card.Nr   T)r{   r   r  zmention.oggFr  r  r  zgame_milebymile/drivingace.oggzgame_milebymile/extratankr   z!game_milebymile/punctureproof.oggzgame_milebymile/rightofway.oggmilebymile-you-drewtabler   buffer   r(   r  )#r   r   r  
add_safetyr   dirty_trick_count"_broadcast_safety_play_with_effectr  r    r?  r  r   rS  r   rL  rK  r   r\  r  r  rt  rr  rs  r.   r  
_draw_cardr#  r%  r$  r  r*  rebuild_player_menur`   r   r  )r@   r{   r&  r   r  r   r  safety_soundsnew_cardr'  r  s              rB   r  MileByMileGame._play_safety  s    //7
djj)((A-(33# 4 
 OOM* &))$**5F))&1zzZ444))**@*@A))*//: :&&'1,JDWDW1W))*//:33$ 4 
 OOofnnQ.B-C4HI &&(H%%)B6>>RSUVCWBXX\']))+N'')I	M zz]*jj 9: &))$**5F))&1zzZ444))**@*@A))*//: 	$$T* ??6*KKx(==(D 99(KKP	27S!!&)  ( ==vV^^B-CD rD   c                 z   U R                  U5      nU(       d  gUR                  R                  U5        UR                  S:X  aL  SUl        U R                  S[        R                  " SS5       S35        U R                  XR                  5        U R                  R                  U5        U R                  5         g)z#Play a special card (False Virtue).Nr0  Tr  r   r  r  )r   r   r  r   r9  r  r  r  _announce_false_virtuerv   r.   r  r  )r@   r{   r&  r   r   s        rB   r  MileByMileGame._play_specialJ  s    //7
::'#'J OOofnnQ.B-C4HI ''0A0AB$$T*rD   c                    UR                   R                  U5        UR                  [        R                  :X  a  U R
                  R                  U5        OU R                  R                  U5        U R                  SUR                  S9  U R                  S[        R                  " SS5       S35        U R                  5         g)zDiscard a card.zmilebymile-discardsr  zgame_cards/discardr   r
   r  N)r   r  r   r   r6  r.   r  r-   r  r_   r  r  r  r  )r@   r{   r&  r   s       rB   r  MileByMileGame._discard_card\  s     >>X__,!!((.$$T*.v{{C,V^^Aq-A,B$GHrD   c                 N   U R                   R                  5       (       a  U R                  (       a  U R                  R                  (       d  gU R                   R                  U R                  5        / U l        U R                   R                  5         U R                  S5        U R                  S[        R                  " SS5       S35        U R                  R                  S:X  a%  U R                   R                  UR                  5      $ U R                   R                  5       $ )zDraw a card for a player.Nzmilebymile-deck-reshuffledzgame_cards/shuffler   r
   r  zNo Duplicates)r,   is_emptyr-   r+   reshuffle_discard_pileadd_allshuffler  r  r  r  rig_gamedraw_non_duplicater   drawr   s     rB   r'  MileByMileGame._draw_cardn  s    99$$DLL,O,OIId//0 "DII9:OO011E0FdKL<<  O399//<<yy~~rD   c                     U R                  5       nU HU  n/ Ul        [        [        5       H8  nU R	                  U5      nU(       d  M  UR                  R                  U5        M:     MW     g)z"Deal initial hands to all players.N)rn   r   r   r   r'  r  )r@   rx   r{   r}   r   s        rB   _deal_initial_hands"MileByMileGame._deal_initial_hands~  sU    002$FFK9%v.4KK&&t, & %rD   c                 `  > [         TU ]  5       nU R                  5         U R                  U R                  R
                  5      nU(       a  UR                  U5        U R                  R                  (       a6  [        U R                  R                  5      nUS:  a  UR                  S5        U$ )z,Validate game configuration before starting.r
   z(milebymile-error-karma-needs-three-teams)r<   prestart_validater~   _validate_team_moder+   ro   r  r  r   rr   rw   )r@   errorsteam_mode_error	num_teamsrA   s       rB   r>   MileByMileGame.prestart_validate  s    *, 	 224<<3I3IJMM/* <<""D..445I1}HIrD   c                     U R                  5         SU l        SU l        SU l        U R	                  5       nU R                  U5        U R                  S5        U R                  S5        U R                  5         g)zCalled when the game starts.r   Tr   zgame_milebymile/music.oggzgame_milebymile/amloop.oggN)	r~   r   game_activer0   rn   set_turn_players
play_musicplay_ambience_start_race)r@   rx   s     rB   on_startMileByMileGame.on_start  sq     	 002n- 	3478 	rD   c                    U =R                   S-  sl         SU l        U R                   H  nUR                  5         M     U R                  R
                  S:X  a  SOSnU R                  R
                  S:X  a  SOSn[        5       U l        U R                  R                  UUU R                  R                  S9  U R                  R                  5         / U l        / U l        U R                  5         [        R                  " / SQ5      nU R!                  SU 35        U R#                  S	5        U R%                  5         U R'                  5         g)
zStart a new race.r   Nz
2x Attacksr   z2x Defenses)attack_multiplierdefense_multiplierinclude_karma_cards)zshuffle1.oggzshuffle2.oggzshuffle3.oggzgame_cards/zmilebymile-new-race)r0   r1   r/   resetr+   r6  r   r,   build_standard_deckr  r5  r-   r.   r;  r  choicer  r  reset_turn_order_start_turn)r@   r   attack_multdefense_multshuffle_sounds        rB   rI  MileByMileGame._start_race  s   Q&*# **J +  <<00L@aa LL11]BqF			%%)+ $ 7 7 	& 	

 			 " 	  " &VW+m_56./ 	rD   c                 n   U R                   nU(       a  [        U[        5      (       d  gU R                  U5      nU(       a  UR                  R                  U5        U R                  S[        R                  " SS5       S35        U R                  U5      nU(       a,  U R                  X#R                  5      nUR                  SUSS9  U R                  5         UR                  (       a*  [        R                   " U[        R                  " S	S
5      S9  U R#                  5         U R%                  5         g)zStart a player's turn.Nzgame_cards/drawr   r  r  r  r   r!  r#  rE  r  )r  r  r#   r'  r   r  r  r  r  r#  r%  r$  r  announce_turnr`   r   r  r-  rebuild_all_menus)r@   r{   r   r'  r  s        rB   rT  MileByMileGame._start_turn  s    $$Z0@AA v&KKt$OOofnnQ.B-C4HI==(D 99$L	27S 	==vV^^B-CD%%' rD   c                    U R                   R                  (       a  gU R                  b  U R                  5         gU R                  R                  5       (       aS  U R                  R                  (       d8  [        S U R                  5        5       5      nU(       a  U R                  5         g[        R                  " U [        R                  " SS5      S9  U R                  SS9  U R                  5         g)zEnd current player's turn.Nc              3   R   #    U  H  n[        UR                  5      S :H  v   M     g7fr   N)r   r   )rj   ry   s     rB   rl   +MileByMileGame._end_turn.<locals>.<genexpr>  s     P6OCK1,6Or      r  r  F)announce)r>   r  r1   	_end_racer,   r2  r+   r3  allrn   r   	jolt_botsr  r  advance_turnrT  )r@   	all_emptys     rB   r  MileByMileGame._end_turn  s     && &&2NN 99(K(KPd6M6M6OPPI  	Dr2(>?5)rD   c                    U R                   nUc;  SnU R                  5        H%  u  p4UR                  U:  d  M  UR                  nUnM'     U R                  S5        U R	                  U5        U R                  5       nUb  U R                  U5        gU R                  R                  5         U R                  5         U R                  5         g)z*End the current race and calculate scores.Nr  zmilebymile-race-complete)r1   r   rG  r  _calculate_race_scores_check_game_winner	_end_gamer>   startr-  r[  )r@   winning_team_idx	max_milesr  r   game_winners         rB   rc  MileByMileGame._end_race  s     (,'B'B#I(,(9$##i/ * 0 0I'/$ ):
 	34 	##$45 --/"NN;' ##%))+""$rD   c                 $    U R                  5         g)z6Called when round timer expires - start the next race.N)rI  r   s    rB   on_round_timer_ready#MileByMileGame.on_round_timer_ready#  s    rD   rn  c           
      h  ^ SSK Jn  U R                  5        GH  u  mn[        UR                  U R
                  R                  5      nUn/ nTU:H  nU(       Ga  UR                  U R
                  R                  :  a  US-  nUR                  SSS045        U R
                  R                  (       d>  UR                  U R
                  R                  :X  a  US-  nUR                  SSS045        UR                  (       d  US-  nUR                  S	SS045        [        U4S
 jU R                  5        5       5      (       a  US-  nUR                  SSS045        [        UR                  5      nUS:  a  US-  n	XY-  nUR                  SXS.45        US:X  a  US-  nUR                  SSS045        UR                  S:  a2  UR                  S-  n
XZ-  nUR                  SXR                  S.45        U R                  TU5        U R                  TU5        U R!                  T5      nU R"                   H  nU R%                  U5      nU(       d  M  UR&                  nUR(                  " USUS9/nU H)  u  nnUR                  UR(                  " UU40 UD65        M+     UR*                  " X5      nUR-                  SUUUS9  M     GM     U R/                  S5        [1        U R3                  5       5       H3  mU R!                  T5      nU R/                  SXR5                  T5      S9  M5     g)z#Calculate and announce race scores.r
   r   i  zmilebymile-from-tripr   r  zmilebymile-from-perfecti,  zmilebymile-from-safec              3   V   >#    U  H  u  pUT:w  d  M  UR                   S :H  v   M      g7fr_  rF  )rj   r   rsr  s      rB   rl   8MileByMileGame._calculate_race_scores.<locals>.<genexpr>C  s%     U2CqH}}rxx1}2Cs   ))i  zmilebymile-from-shutoutr   r  zmilebymile-from-safeties)r   countr  zmilebymile-from-all-safetieszmilebymile-from-dirty-trickszmilebymile-from-distancerF  zmilebymile-earned-points)r_   r  	breakdownzmilebymile-total-scoreszmilebymile-team-score)r_   r  N)r>  r   r   minrG  r+   rN  r  rM  r  rd  r   r  r%  r   r   r   r*   r#  r$  r?  format_list_andr  r  r   r   r   )r@   rn  r   r   
base_milesr  bonus_parts	is_winnersafety_countsafety_bonusdt_bonusr_   ry   r'  r$  bonus_descriptionsrj  paramsrz  r  s                      @rB   rj  %MileByMileGame._calculate_race_scores'  s.   9$(OO$5 HjZ--t||/J/JKJE24K $44IZ--1L1LL""$:XsO#LM ||??!''4<<+F+FF#**,ERU+WX "//SLE&&(>3'PQ U$//2CUUUSLE&&(AHc?'ST z223La+c1%""2#/G q ""$BXsO#TU ++a/%77#=!""6#+6R6RS %%h6%0 %%h/D\\}}Q' !$$V-GzZ&" $/KC&--l.>.>vs.Uf.UV $/ )88T	.'	   "{ %6h 	23d0023H%%h/D'd:M:Mh:W   4rD   c                 2   [        U R                  5       5       Hz  nU R                  U5      U R                  R                  :  d  M.  Un[        U R                  5       5       H+  nU R                  U5      U R                  U5      :  d  M)  UnM-     Us  $    g)z?Check if any team has won the game. Returns team index or None.N)r   r   r   r+   winning_score)r@   r  r  	other_idxs       rB   rk  !MileByMileGame._check_game_winner  s    d0023H""8,0J0JJ#!&t'9'9';!<I**958K8KH8UU#, "=   4 rD   
winner_idxc                 ~   U R                  S5        U R                  R                  U   nU R                  U5      nU R	                  5       (       a  U R                  SUR                  S   S9  O/SR                  UR                  5      nU R                  SUS-   US9  U R                  S	US
9  U R                  5         g)zEnd the game with a winner.zgame_pig/win.oggzmilebymile-wins-individualr   r  r  zmilebymile-wins-teamr   )r|   r  zmilebymile-final-score)r  N)	r  rr   rw   r   r   r  r  r  finish_game)r@   r  winner_teamwinner_scoremembers_strs        rB   rl  MileByMileGame._end_game  s    *+((..z:**:6""$$9+BUBUVWBXY))K$7$78K3*q.R]^1FrD   c                    [        U R                  5       5       Vs/ s H  oU R                  U5      4PM     nn[        US SS9n0 nU H  u  pVU R	                  U5      nXdU'   M     U(       a  US   OSu  pU R	                  U5      n
[        U R                  5       [        R                  " 5       R                  5       U R                  U R                  5        Vs/ s H8  n[        UR                  UR                  UR                  [!        USS5      S9PM:     snU
U	UU R"                  U R$                  R&                  U R$                  R(                  S	.S
9$ s  snf s  snf )z4Build the game result with MileByMile-specific data.c                     U S   $ )Nr   rJ   )ts    rB   r  2MileByMileGame.build_game_result.<locals>.<lambda>  s    1rD   T)rj  reverser   )r   r   is_virtual_botF)r^   player_namer`   r  )winner_namer  final_scoresrounds_playedtarget_scorero   )	game_type	timestampduration_ticksplayer_resultscustom_data)r   r   r   sortedr   r   rQ   r   now	isoformatsound_scheduler_tickrn   r   rb   r_   r`   getattrroundr+   rN  ro   )r@   r   team_scoressorted_teamsr  r  r  r_   r  r  r  ry   s               rB   build_game_result MileByMileGame.build_game_result  sQ    =B$BTBTBV<WX<Wq4..q12<WXk~tL +OH%%h/D!&  , 7C<? 
((4mmolln..044 002 3A dd !88#*1.>#F	 3  + , ,!% $ ; ;!\\33
 	
 Y s   E?Eresultc                    [         R                  " US5      /nUR                  R                  S0 5      n[        UR	                  5       S5       H7  u  nu  pg[         R                  " USUS9nUR                  U SU SU 35        M9     U$ )z*Format the end screen for MileByMile game.zgame-final-scoresr  r   zgame-points)ry  z. r  )r   r?  r  r   itemsr  )	r@   r  r$  r  r  r   r_   r  
points_strs	            rB   format_end_screen MileByMileGame.format_end_screen  s    !!&*=>?))--nbA ),*<*<*> BA}%))&-uMJLLA3bb56 !C rD   c                 R    U R                    H  nUR                  U:X  d  M  Us  $    g)zGet a player by name.N)r*   r_   )r@   r_   r{   s      rB   r  "MileByMileGame._get_player_by_name  s&    llF{{d" # rD   attacker_team_idxtarget_team_idxc                    U R                  5       (       a  U R                  R                  U   nUR                  S   nU R                   H}  nU R                  U5      nU(       d  M  Xa:X  a  UR                  SSS9  M4  UR                  U:X  a  UR                  SUR                  SS9  Ma  UR                  SUR                  USS9  M     gU R                   H}  nU R                  U5      nU(       d  M  UR                  U:X  a  UR                  S	SS9  M?  UR                  U:X  a  UR                  S
US-   SS9  Me  UR                  SUS-   US-   SS9  M     g)zGAnnounce when both attacker and target lose karma (attack neutralized).r   z!milebymile-karma-clash-you-targetr   r"  z#milebymile-karma-clash-you-attacker)r  r"  zmilebymile-karma-clash-others)r  r  r"  z milebymile-karma-clash-your-teamz"milebymile-karma-clash-target-teamr   r|   r"  z"milebymile-karma-clash-other-teamsN)	r   rr   rw   r  r*   r#  r  r_   rv   )r@   r  r  r  r  r  ry   r'  s           rB   r	  $MileByMileGame._announce_karma_clash  sT    ""$$,,22?CK%--a0K\\}}Q'=LL!DWLUVV{*LL=!)& !  LL7!)*&	 !  "( \\}}Q'<<#44LL!CGLT\\_4LL<.2& !  LL<!2Q!6.2&	 !  "rD   c                    U R                  5       (       a`  U R                   HO  nU R                  U5      nU(       d  M  X1:X  a  UR                  SSS9  M4  UR                  SUR                  SS9  MQ     g
U R                   HS  nU R                  U5      nU(       d  M  UR
                  U:X  a  UR                  SSS9  M?  UR                  SUS-   SS	9  MU     g
)z1Announce when attacker loses karma for attacking.zmilebymile-karma-shunned-your   r  zmilebymile-karma-shunned-otherr{   r"  z"milebymile-karma-shunned-your-teamz#milebymile-karma-shunned-other-teamr   r  Nr   r*   r#  r  r_   rv   )r@   r  r  ry   r'  s        rB   r  )MileByMileGame._announce_attacker_shunned  s     ""$$\\}}Q'=LL!?LPLL8'}}& !  " \\}}Q'<<#44LL!EgLVLL=.2& !  "rD   r  c                    U R                  5       (       a`  U R                   HO  nU R                  U5      nU(       d  M  X1:X  a  UR                  SSS9  M4  UR                  SUR                  SS9  MQ     g
U R                   HS  nU R                  U5      nU(       d  M  UR
                  U:X  a  UR                  SSS9  M?  UR                  SUS-   SS	9  MU     g
)z:Announce when a player plays False Virtue to regain karma.zmilebymile-false-virtue-your   r  zmilebymile-false-virtue-otherr  z!milebymile-false-virtue-your-teamz"milebymile-false-virtue-other-teamr   r  Nr  )r@   r{   r  ry   r'  s        rB   r-  %MileByMileGame._announce_false_virtue/  s    ""$$\\}}Q';LL!>wLOLL7%{{& !  " \\}}Q'<<8+LL!DWLULL<%\& !  "rD   message_keyc                     U R                    HN  nU R                  U5      nU(       d  M  U R                  X%R                  5      nUR                  " U4USS.UD6  MP     g)z>Broadcast a message with a localized card name to all players.r   r!  N)r*   r#  r%  r$  r  )r@   r  r   kwargsry   r'  r  s          rB   r  &MileByMileGame._broadcast_card_messageL  sP    A==#D55dKKHILLO9WOO rD   c          	      v   U R                    H  nU R                  U5      nU(       d  M  U R                  X%R                  5      nU R	                  UR
                  UR                  5      nXA:X  a  U(       a  SOSnUR                  XUSS9  M  U(       a  SOSnUR                  UUR                  UUSS9  M     g)	z?Broadcast a safety play with a short explanation of its effect.z+milebymile-you-play-dirty-trick-with-effectz&milebymile-you-play-safety-with-effectr   )r   effectr"  z/milebymile-player-plays-dirty-trick-with-effectz*milebymile-player-plays-safety-with-effect)r{   r   r  r"  N)r*   r#  r%  r$  rx  r   r  r_   )	r@   r{   r   r  ry   r'  r  r  rj  s	            rB   r&  1MileByMileGame._broadcast_safety_play_with_effectU  s     A==#D55dKKHI66tzz4;;OF{ & BA 
 SP & FE 
 !;;"!"  + rD   c                 ,  > [         TU ]  5         U R                  (       d  gU R                  R                  5         U R                  S:  a3  U =R                  S-  sl        U R                  S::  a  SU l        SU l        [        R                  " U 5        g)zCalled every tick.Nr   r   )r<   on_tickrE  r>   r4   r2   r3   r   r?   s    rB   r  MileByMileGame.on_tick~  sz     	!!# ((1,))Q.),,1/3,15.$rD   c                    U R                   R                  (       a  gU R                  b  UR                  U R                  :X  aw  U R                  nU(       a  [
        R                  " U5      OSnU(       aE  UR                   H5  nUR                  [        R                  :X  d  M#  UR                  U:X  d  M5    g   U R                  U:w  a  gU R                  U5      $ )zBot AI decision making.Nr   )r>   r  r2   rv   r3   r   r?  r   r   r   r6  r   r  _bot_choose_card)r@   r{   r  r  r   s        rB   	bot_thinkMileByMileGame.bot_think  s     && ''3  D$@$@@77BH"2"6"6v">d" &>>X__<A^#0 !,
 &( $$V,,rD   c                 :   UR                   (       d  gU R                  U5      nU(       d  gU R                  R                  nX2R                  -
  nUS:*  nSnSn[        UR                   5       H#  u  pU R                  XX$U5      n
X:  d  M  U
nUnM%     SUS-    3$ )zBot card selection logic.Nr  r   r  r   r   )r   r   r+   rN  rG  r   _bot_score_card)r@   r{   r   target_distancedistance_needed
is_endgame	best_slotbest_priorityr   r   prioritys              rB   r  MileByMileGame._bot_choose_card  s    {{//7
,,55),<,<<$+
 	 -GA++F*WabH' (		 . IM?++rD   r  r  c                    UR                   [        R                  :X  a  U R                  XXE5      $ UR                   [        R                  :X  a  U R                  XU5      $ UR                   [        R                  :X  a  U R                  X#XE5      $ UR                   [        R                  :X  a  U R                  XU5      $ UR                   [        R                  :X  a  U R                  X#5      $ g)z%Score a card for bot decision making.r  )r   r   r1  _bot_score_distance_cardr4  _bot_score_remedy_cardr6  _bot_score_safety_cardr   _bot_score_hazard_cardr8  _bot_score_special_card)r@   r{   r   r   r  r  s         rB   r  MileByMileGame._bot_score_card  s     >>X...00[[>>X__,..vZHH>>X__,..t]]>>X__,..vZHH>>X---//AArD   c                     U R                  X5      (       d  gUR                  nU(       a-  XS:X  a  gXS:  a  U R                  R                  (       a  ggSU-   $ SU-   $ )Nr  i  rE  i  i  )r   rH  r+   rM  )r@   r{   r   r  r  rH  s         rB   r  'MileByMileGame._bot_score_distance_card  sZ     ""600==*)<<;;(?"hrD   c                    UR                   [        R                  :X  aI  UR                  [        R
                  5      (       a%  UR                  [        R                  5      (       d  gUR                   [        R                  :X  a%  UR                  [        R                  5      (       a  gU R                  X5      (       a  gg)Ni  i
  i	     )r   r   rR  rJ  r   rK  r7  r   rS  rQ  rL  r   r:  s       rB   r  %MileByMileGame._bot_score_remedy_card  s     ::(Z-C-CJOO-T-T(()@)@AA::000Z5K5KJLbLb5c5cv,,rD   c                 b    UR                  UR                  5      (       a  gU(       a  US::  a  gg)NrE  r  i  i  )r7  r   )r@   r   r   r  r  s        rB   r  %MileByMileGame._bot_score_safety_card  s,       ,,/S0rD   c                    ^ ^ T R                  TU5      (       d  gT R                  R                  (       a9  UR                  (       a(  [	        UU 4S jTR
                   5       5      nU(       a  gg)Nr  c              3      >#    U  H;  nUR                   [        R                  :H  =(       a    TR                  TU5      v   M=     g 7fr  )r   r   r1  r   )rj   rk   r{   r@   s     rB   rl   8MileByMileGame._bot_score_hazard_card.<locals>.<genexpr>  s=      ($A x000ST5H5HQR5SS$s   AArE  i   )r   r+   r  r9  rp   r   )r@   r{   r   r   has_playable_distances   ``   rB   r  %MileByMileGame._bot_score_hazard_card  sW     ""6400<<""z';';$' (( %! %rD   c                 H    UR                   S:X  a  UR                  (       d  gg)Nr0  i  rE  )r   r9  )r@   r   r   s      rB   r  &MileByMileGame._bot_score_special_card  s    ::'
0D0DrD   )r>   r0   r,   r3   r2   r4   r-   rE  r.   r/   r1   r   )rE   N)F)r  r  )__name__
__module____qualname____firstlineno____doc__r   listr*   r#   __annotations__r!   r+   r   r,   r-   r   r.   r/   r$   r0   r  r1   r2   r3   strr4   r6   r7   r=   rG   classmethodrM   rQ   rT   rW   r\   boolrc   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r   r   r   r   r   r   r   r*  r-  r   rB  r@  rA  rT  r]  ru  rx  r%  r2  r3  r  r5  r   r   r   r   r   r   r   r   r  r  r  r  r  r   r  r  r  r  r  r  r  r  r'  r;  r>  rJ  rI  rT  r  rc  rs  rj  rk  rl  r   r  r  r  r	  r  r-  r  r&  r  r  r  r  r  r  r  r  r  __static_attributes____classcell__)rA   s   @rB   r'   r'   (   sc    ',D&AGT"#A!&7H!IGI t,D$,$T:L$t*:#(#>d4j>#(#>Ki>L#)-C$J- +/S4Z.,0sTz0$%c% $s#sK
K
       %S % %      Hs H# Ht HP` HK, T1A 6,< 6TAQ 6( ( (6D 6-s -  G Gc Gd G
Fs FC FD F
 -= ) @1A i B$cL-+; - -fv #* !f ! !!f ! !!v !* !	f 	t 	!V !
 !F sTz !6 !j !f t "V "
 ";6 ;c ;c ; *+; * *.%5 T d ( CGE&E.2E<?E	E.EE E 	E
 
E0EE E 	E
 
E6) S SSWZ A3 A A A@ @c @c @	@3 	@ 	@ 	@CT C3 C3 C>Y d t $'7 t  4s 4I 4QZ 4_c 4.99 9D 9T 9:0@ # RVWZR[ $*6 *c *d *X,'F ,'s ,'t ,'\&*& &*S &*T &*P!V !S	 !F"P "Pc "PsUYz "PH-X -X$ -X^&/ &/3 &/4 &/Z #'3 3 3 	3
 4Z3 
3&@%5 @S @ @QU @N (,7 7 7 	7
 *7 
7r80@ 8T 8
!S	! *! 
t	!<    "   	 
       
 8L L L 	L
 L 
L&   	
  
.Id It I#3 3 d t J  %OE OE OE 	OE
 OE 
OEb$4 C t PT $$4 C t PT $ !1  dTk   -49 (&!F!20%8]sTz ]d ]~
C$J 
C D  $
: $
L	
 	C 	DI 	 0@40G 0"0 0 	0
 
0d(=@	>-=  QU :P3 Pd PQU P# # #
 # 
#R &- 0 -S4Z -.,'7 ,C$J ,2   	
   
*   	
  
(   	
 
  	
  
   	
 
"D i C  rD   r'   )/r  dataclassesr   r   r   r  baser   r   registryr	   game_utils.actionsr   r   r   r   game_utils.bot_helperr   game_utils.game_resultr   r   game_utils.round_timerr   game_utils.teamsr   r   r>  r   server.core.ui.keybindsr   cardsr   r   r   r   r   r   r   r    r+   r!   r{   r#   r   r$   r   r'   rJ   rD   rB   <module>r     sz    )    $ J J . > : 1 1 0	 	 	 ' $  	 pT p  prD   