§
    ²Ð½jÓ4  ã                  ó�   — d Z ddlmZ ddlZddlmZmZmZmZm	Z	m
Z
 e
rddlmZ ddlmZmZ ddlmZmZmZmZ  G d„ d	¦  «        ZdS )
zÓ
Level progression service for FreeExile.
Authoritative engine managing experience distribution, piecewise level transitions,
level gap efficiency decay, and tiered hybrid death penalties (PoE2 2026 Standards).
é    )ÚannotationsN)ÚAnyÚCallableÚDictÚListÚOptionalÚTYPE_CHECKING)ÚGameDesignMatrixService)ÚLevelExpBenchmarkÚcalculate_piecewise_exp_curve)ÚDeathPenaltyResultÚExpAwardResultÚLevelUpEventÚPlayerProgressionStatec                  óÖ   — e Zd ZdZ	 	 dIdJd	„ZdKd„ZdLd„ZdMd„ZdNd„ZdOd„Z	dPdQd„Z
	 	 	 	 	 dRdSd!„ZdTd"„ZdUd%„ZdVd'„Z	 	 dIdddd(œdWd1„ZdXd3„ZdYd4„ZdZd7„Zd[d;„Zd\d>„Zd]dB„Zd^dD„Zd_dH„ZdS )`ÚLevelProgressionServicez@Authoritative service for level progression and death penalties.NÚmatrix_serviceú!Optional[GameDesignMatrixService]Údb_pathúOptional[str]ÚreturnÚNonec                ór   — || _         || _        t          ¦   «         | _        i | _        g | _        g | _        d S )N)Ú_matrix_serviceÚ_db_pathr   Ú_benchmarksÚ_playersÚ_level_up_listenersÚ_death_listeners)Úselfr   r   s      ú?C:\Projects\FreeExile\server\world\level_progression_service.pyÚ__init__z LevelProgressionService.__init__   s>   € ð
  .ˆÔØˆŒÝ9VÑ9XÔ9XˆÔØ;=ˆŒØIKˆÔ ØLNˆÔÐÐó    ÚlevelÚintúOptional[LevelExpBenchmark]c                ó6   — | j                              |¦  «        S )z6Retrieves mathematical benchmark for a specific level.©r   Úget©r    r$   s     r!   Úget_benchmarkz%LevelProgressionService.get_benchmark(   s   € àÔ×#Ò# EÑ*Ô*Ð*r#   r   c                ó`   — | j                              |¦  «        p| j         |dk    rdnd         S )z=Retrieves canonical mathematical benchmark for level (1-100).éd   é   r(   r*   s     r!   Úget_canonical_benchmarkz/LevelProgressionService.get_canonical_benchmark,   s3   € àÔ×#Ò# EÑ*Ô*ÐZ¨dÔ.>ÀeÈsÂlÀl¸s¸sÐXYÔ.ZÐZr#   c                óh   — |dk     s|dk    rdS | j                              |¦  «        }|r|j        ndS )z=Returns experience needed to advance from level to level + 1.r.   r-   r   )r   r)   Úexp_to_next_level)r    r$   Úbenchs      r!   Úget_delta_expz%LevelProgressionService.get_delta_exp0   sA   € à�1Š9ˆ9˜ š˜Ø�1ØÔ ×$Ò$ UÑ+Ô+ˆØ*/Ð6ˆuÔ&Ð&°QÐ6r#   Úfloatc                ób   — |dk    s|dk    rdS |dk    rdS |dk    rdS |dk    rd	S |d
k    rdndS )z8Returns tiered percentage death penalty ratio for level.é<   r-   g        éP   çš™™™™™©?éY   gš™™™™™¹?éb   g333333Ã?éc   g      Ð?© r*   s     r!   Úget_death_penalty_ratioz/LevelProgressionService.get_death_penalty_ratio7   sT   € à�BŠ;ˆ;˜% 3š,˜,Ø�3Ø�BŠ;ˆ;Ø�4Ø�BŠ;ˆ;Ø�4Ø�BŠ;ˆ;Ø�4Ø ’{�{ˆtˆt¨Ð+r#   Úplayer_levelÚmonster_levelc                óì   — ||z
  }t          |¦  «        dk    rdS |dk    r*|dz
  }t          dt          j        d|z  ¦  «        ¦  «        S | dz
  }t          dt          j        d|z  ¦  «        ¦  «        S )zLCalculates experience yield multiplier based on character-monster level gap.é   ç      ð?g{®Gáz„?g333333ã¿r8   gš™™™™™Ù¿)ÚabsÚmaxÚmathÚexp)r    r>   r?   ÚgapÚdeltas        r!   Úcalculate_level_gap_multiplierz6LevelProgressionService.calculate_level_gap_multiplierC   sy   € à˜]Ñ*ˆÝˆs‰8Œ8�qŠ=ˆ=Ø�3Ø�Š7ˆ7Ø˜!‘GˆEÝ�t�TœX e¨e¡mÑ4Ô4Ñ5Ô5Ð5Ø�˜‘
ˆÝ�4�œ %¨%¡-Ñ0Ô0Ñ1Ô1Ð1r#   r.   Ú	player_idÚstrÚdefault_levelr   c                ó&  — || j         v r| j         |         S t          dt          d|¦  «        ¦  «        }|                      |¦  «        }t	          ||d|dk    rdn|                      |¦  «        |j        |j        ddd¬¦	  «	        }|| j         |<   |S )zNRetrieves existing progression state or registers new player at default level.r.   r-   r   ©	rJ   r$   Úcurrent_expr1   Úcumulative_expÚlifetime_expÚunspent_talent_pointsÚtotal_talent_pointsÚdeaths_count)r   rD   Úminr/   r   r3   rP   )r    rJ   rL   r$   r2   Ústates         r!   Úget_player_statez(LevelProgressionService.get_player_stateN   sª   € à˜œÐ%Ð%Ø”= Ô+Ð+Ý�A•s˜3 Ñ.Ô.Ñ/Ô/ˆØ×,Ò,¨UÑ3Ô3ˆÝ&ØØØØ#(¨C¢< <˜a˜a°T×5GÒ5GÈÑ5NÔ5NØ Ô/ØÔ-Ø"#Ø !Øð

ñ 

ô 

ˆð $)ˆŒ�iÑ Øˆr#   r   rO   rR   rS   rT   c                ó.  — t          dt          d|¦  «        ¦  «        }|                      |¦  «        }|j        |z   }	|                      |¦  «        }
|dk    rt          |j        |	¦  «        }	d\  }
}t          ||||
|	|	|||¬¦	  «	        }|| j        |<   |S )zOExplicitly sets or updates player state (for test setup and state restoration).r.   r-   ©r   r   rN   )rD   rU   r/   rP   r3   r   r   )r    rJ   r$   rO   rR   rS   rT   Úvalid_levelr2   Úcum_expÚexp_nextrV   s               r!   Úset_player_statez(LevelProgressionService.set_player_stateb   s·   € õ ˜!�S  e™_œ_Ñ-Ô-ˆØ×,Ò,¨[Ñ9Ô9ˆØÔ&¨Ñ4ˆØ×%Ò% kÑ2Ô2ˆØ˜#ÒÐÝ˜%Ô.°Ñ8Ô8ˆGØ$(Ñ!ˆH�kÝ&ØØØ#Ø&Ø"Ø Ø"7Ø 3Ø%ð

ñ 

ô 

ˆð $)ˆŒ�iÑ Øˆr#   c                ó,   — |                       |¦  «        S )z6Returns authoritative progression snapshot for player.)rW   )r    rJ   s     r!   Úget_level_infoz&LevelProgressionService.get_level_info�   s   € à×$Ò$ YÑ/Ô/Ð/r#   ÚcallbackúCallable[[LevelUpEvent], None]c                ó:   — | j                              |¦  «         dS )z;Registers listener invoked when player character levels up.N)r   Úappend©r    r`   s     r!   Úadd_level_up_listenerz-LevelProgressionService.add_level_up_listener…   s   € àÔ ×'Ò'¨Ñ1Ô1Ð1Ð1Ð1r#   ú$Callable[[DeathPenaltyResult], None]c                ó:   — | j                              |¦  «         dS )z<Registers listener invoked when death penalty is calculated.N)r   rc   rd   s     r!   Úadd_death_penalty_listenerz2LevelProgressionService.add_death_penalty_listener‰   s   € àÔ×$Ò$ XÑ.Ô.Ð.Ð.Ð.r#   )Úbase_expÚ
zone_levelr>   Úarg3úOptional[int]Úarg4ri   rj   Úkwargsr   r   c          	     ó®  — |�|dk     rd}|                       |||||¦  «        \  }	}
t          d|	¦  «        }	|                      |||
|¦  «        }|j        dk    r|                      ||	|¦  «        S |                      |j        |¦  «        }t          dt          t          j        |	|z  ¦  «        ¦  «        ¦  «        }|  	                    |||	||¦  «        S )zUCalculates and awards monster defeat experience with gap decay and level transitions.Nr   r-   )
Ú_resolve_award_argsrD   Ú_ensure_player_for_awardr$   Ú_build_level_100_award_resultrI   r%   rE   ÚfloorÚ_apply_exp_gain)r    rJ   r?   rk   rm   ri   rj   r>   rn   Úraw_expÚz_lvlÚplayerÚgap_multÚawardeds                 r!   Úaward_monster_expz)LevelProgressionService.award_monster_exp�   sÛ   € ð Ð H¨q¢L LØˆHØ×1Ò1°$¸¸hÈ
ÐTaÑbÔb‰ˆ�Ý�a˜‘/”/ˆØ×.Ò.¨y¸,ÈÈ}Ñ]Ô]ˆàŒ<˜3ÒÐØ×5Ò5°f¸gÀ}ÑUÔUÐUà×6Ò6°v´|À]ÑSÔSˆÝ�a��TœZ¨°(Ñ(:Ñ;Ô;Ñ<Ô<Ñ=Ô=ˆØ×#Ò# F¨G°W¸hÈÑVÔVÐVr#   útuple[int, Optional[int]]c                ó”   — ||}}|€	|�|}|€|}n|€|�|}|€ |                       |¦  «        }|r|j        nd}t          d|¦  «        |fS )zMResolves polymorphic positional and keyword parameters for award_monster_exp.Né   r   )r+   Úmonster_benchmark_exprD   )	r    rk   rm   ri   rj   r?   Úresolved_baseÚresolved_zoner2   s	            r!   rp   z+LevelProgressionService._resolve_award_args§   sy   € ð (0°�}ˆØÐ  TÐ%5Ø ˆMØÐ$Ø $�øØÐ" tÐ'7Ø ˆMØÐ Ø×&Ò& }Ñ5Ô5ˆEØ;@ÐH˜EÔ7Ð7ÀbˆMÝ�1�mÑ$Ô$ mÐ3Ð3r#   c                óˆ   — || j         vr-|�|n|�|dk    r||k    r|nd}|                      ||¬¦  «        S | j         |         S )zFRetrieves or initializes player progression state for award operation.Nr.   )rL   )r   rW   )r    rJ   r>   rj   r?   Ú
init_levels         r!   rq   z0LevelProgressionService._ensure_player_for_award¼   sh   € ð ˜DœMÐ)Ð)Ø)5Ð)A˜˜Ø)Ð5¸*Àqº.¸.ÈZÐ[hÒMhÐMh�
�
Ðopð ð ×(Ò(¨À*Ð(ÑMÔMÐMØŒ}˜YÔ'Ð'r#   rw   ru   c                ób   — t          d||                      d|¦  «        d|z
  dddddd¬¦
  «
        S )zEConstructs zero-exp award result for characters at max cap level 100.r   r-   F©
Úexp_awardedru   Úgap_multiplierÚ	level_gapÚprevious_levelÚ	new_levelÚ
leveled_upÚlevels_gainedrO   r1   )r   rI   )r    rw   ru   r?   s       r!   rr   z5LevelProgressionService._build_level_100_award_resultË   sN   € õ ØØØ×>Ò>¸sÀMÑRÔRØ˜MÑ)ØØØØØØð
ñ 
ô 
ð 	
r#   Ústart_levelÚinitial_expútuple[int, int, int, int]c                óÔ   — ||d}}}|                       |¦  «        }|dk     r6||k    r0||z  }|dz  }|dz  }|                       |¦  «        }|dk     r||k    °0|dk    rd\  }}}||||fS )zHResolves level-up thresholds and returns (level, exp, exp_next, gained).r   r-   r.   )r-   r   r   )r3   )r    rŒ   r�   ÚlvlrF   Úgainedr\   s          r!   Ú_compute_level_advancementz2LevelProgressionService._compute_level_advancementÜ   sœ   € ð '¨°Q�&ˆSˆØ×%Ò% cÑ*Ô*ˆØ�CŠiˆi˜C 8šO˜OØ�8‰OˆCØ�1‰HˆCØ�a‰KˆFØ×)Ò)¨#Ñ.Ô.ˆHð	 �CŠiˆi˜C 8šO˜Oð
 �#Š:ˆ:Ø!*ÑˆC��hØ�C˜ 6Ð)Ð)r#   r…   r†   c                ó&  — |j         }|                      ||j        |z   ¦  «        \  }}}	}
|j        |z   }|j        |z   }|dk    r?|                      d¦  «        j        }t          ||¦  «        t          ||¦  «        }}d\  }	}t          |j        |||	|||j	        |
z   |j
        |
z   |j        ¬¦	  «	        | j        |j        <   |
dk    r|                      |j        |||
¦  «         t          |||t          ||z
  ¦  «        |||
dk    |
||	¬¦
  «
        S )zQApplies awarded experience, processes single/multi level-ups, and updates player.r-   rY   rN   r   r„   )r$   r’   rO   rP   rQ   r/   rU   r   rJ   rR   rS   rT   r   Ú_dispatch_level_upr   rC   )r    rw   r…   ru   r†   r?   Ú
prev_levelÚcurr_lvlÚcurr_expr\   r‘   r[   Úlife_expÚmax_cums                 r!   rt   z'LevelProgressionService._apply_exp_gainë   sU  € ð ”\ˆ
Ø/3×/NÒ/NØ˜Ô*¨[Ñ8ñ0
ô 0
Ñ,ˆ�(˜H fð Ô'¨+Ñ5ˆØÔ&¨Ñ4ˆØ�sŠ?ˆ?Ø×2Ò2°3Ñ7Ô7ÔFˆGÝ # G¨WÑ 5Ô 5µs¸7ÀHÑ7MÔ7M�XˆGØ!%ÑˆH�hå*@ØÔ&ØØ Ø&Ø"Ø!Ø"(Ô">ÀÑ"GØ &Ô :¸VÑ CØÔ,ð
+
ñ 
+
ô 
+
ˆŒ�fÔ&Ñ'ð �AŠ:ˆ:Ø×#Ò# FÔ$4°jÀ(ÈFÑSÔSÐSåØ#ØØ)Ý˜* }Ñ4Ñ5Ô5Ø%ØØ ’zØ Ø Ø&ð
ñ 
ô 
ð 	
r#   Ú	old_levelr‰   r‹   c                óv   — d|z  d|z  d|z  dœ}t          |||||¬¦  «        }| j        D ]} ||¦  «         ŒdS )z=Dispatches level-up notification to all registered listeners.g      @rB   g      <@)Ústat_pointsÚpassive_pointsÚhp)rJ   rš   r‰   Útalent_points_awardedÚstats_gainedN)r   r   )r    rJ   rš   r‰   r‹   ÚstatsÚeventÚlisteners           r!   r”   z*LevelProgressionService._dispatch_level_up  sz   € ð
  Ñ.Ø! MÑ1Ø˜Ñ&ð
ð 
ˆõ
 ØØØØ"/Øð
ñ 
ô 
ˆð Ô0ð 	ð 	ˆHØˆH�U‰OŒOˆOˆOð	ð 	r#   r   c                ó0  — |                       |¦  «        }|                      |j        ¦  «        }|                      |j        ¦  «        }t	          t          j        ||z  ¦  «        ¦  «        }t          |j        |¦  «        }t          d|j        |z
  ¦  «        }t          |j        |j        ||j        |j        |z
  |j        |j        |j        |j        dz   ¬¦	  «	        | j        |<   |                      ||||¦  «        }| j        D ]}	 |	|¦  «         Œ|S )zFCalculates and applies tiered death penalty with zero-floor safe rule.r   r.   rN   )rW   r=   r$   r3   r%   rE   rs   rU   rO   rD   r   rJ   r1   rP   rQ   rR   rS   rT   r   Ú_build_death_resultr   )
r    rJ   rw   ÚratiorH   Únominal_lossÚexp_lostÚnew_expÚresultr£   s
             r!   Úapply_death_penaltyz+LevelProgressionService.apply_death_penalty.  s  € à×&Ò& yÑ1Ô1ˆØ×,Ò,¨V¬\Ñ:Ô:ˆØ×"Ò" 6¤<Ñ0Ô0ˆÝ�4œ: e¨e¡mÑ4Ô4Ñ5Ô5ˆÝ�vÔ)¨<Ñ8Ô8ˆÝ�a˜Ô+¨lÑ:Ñ;Ô;ˆå#9ØÔ&Ø”,ØØ$Ô6Ø!Ô0°8Ñ;ØÔ,Ø"(Ô">Ø &Ô :ØÔ,¨qÑ0ð
$
ñ 
$
ô 
$
ˆŒ�iÑ ð ×)Ò)¨&°(¸EÀ7ÑKÔKˆØÔ-ð 	ð 	ˆHØˆH�VÑÔÐÐØˆr#   r¨   r¦   r©   c           	     óL   — t          |j        |j        |||j        |d¬¦  «        S )zAConstructs DeathPenaltyResult with safe floor and no de-leveling.F)rJ   r$   r¨   Úpenalty_ratioÚcurrent_exp_beforeÚcurrent_exp_afterÚ
de_leveled)r   rJ   r$   rO   )r    rw   r¨   r¦   r©   s        r!   r¥   z+LevelProgressionService._build_death_resultH  s8   € õ "ØÔ&Ø”,ØØØ%Ô1Ø%Øð
ñ 
ô 
ð 	
r#   )NN)r   r   r   r   r   r   )r$   r%   r   r&   )r$   r%   r   r   )r$   r%   r   r%   )r$   r%   r   r4   )r>   r%   r?   r%   r   r4   )r.   )rJ   rK   rL   r%   r   r   )r.   r   r   r   r   )rJ   rK   r$   r%   rO   r%   rR   r%   rS   r%   rT   r%   r   r   )rJ   rK   r   r   )r`   ra   r   r   )r`   rf   r   r   )rJ   rK   r?   r%   rk   rl   rm   rl   ri   rl   rj   rl   r>   rl   rn   r   r   r   )rk   rl   rm   rl   ri   rl   rj   rl   r?   r%   r   r{   )
rJ   rK   r>   rl   rj   rl   r?   r%   r   r   )rw   r   ru   r%   r?   r%   r   r   )rŒ   r%   r�   r%   r   rŽ   )rw   r   r…   r%   ru   r%   r†   r4   r?   r%   r   r   )
rJ   rK   rš   r%   r‰   r%   r‹   r%   r   r   )rJ   rK   r   r   )
rw   r   r¨   r%   r¦   r4   r©   r%   r   r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r"   r+   r/   r3   r=   rI   rW   r]   r_   re   rh   rz   rp   rq   rr   r’   rt   r”   r«   r¥   r<   r#   r!   r   r      s  € € € € € ØJÐJð =AØ!%ð
Oð 
Oð 
Oð 
Oð 
Oð+ð +ð +ð +ð[ð [ð [ð [ð7ð 7ð 7ð 7ð
,ð 
,ð 
,ð 
,ð	2ð 	2ð 	2ð 	2ðð ð ð ð ð. ØØ%&Ø#$Øðð ð ð ð ð>0ð 0ð 0ð 0ð2ð 2ð 2ð 2ð/ð /ð /ð /ð #Ø"ðWð #'Ø$(Ø&*ðWð Wð Wð Wð Wð Wð44ð 4ð 4ð 4ð*(ð (ð (ð (ð
ð 
ð 
ð 
ð"*ð *ð *ð *ð.
ð .
ð .
ð .
ð`ð ð ð ð&ð ð ð ð4
ð 
ð 
ð 
ð 
ð 
r#   r   )r´   Ú
__future__r   rE   Útypingr   r   r   r   r   r	   Ú'server.world.game_design_matrix_servicer
   Ú$server.world.level_progression_curver   r   Ú$server.world.level_progression_typesr   r   r   r   r   r<   r#   r!   ú<module>rº      s  ððð ð #Ð "Ð "Ð "Ð "Ð "Ø €€€Ø EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ EÐ Eàð PØOÐOÐOÐOÐOÐOðð ð ð ð ð ð ð ðð ð ð ð ð ð ð ð ð ð ð ð{
ð {
ð {
ð {
ð {
ñ {
ô {
ð {
ð {
ð {
r#   