§
    ƒ½»jO8  ã            
       ó˜  — d Z ddlZddlmc mZ ddlZddlZddl	Z	ej
                             de	j
                             e	j
                             e	j
                             e¦  «        d¦  «        ¦  «        ¦  «         ddlmZmZmZmZmZmZmZmZ ddlmZ ddlmZ  G d„ dej        ¦  «        Zed	k    r ej         ¦   «          dS dS )
aý  
Unit tests for FreeExile Damage & Defense Calculation Engine (PoE2 Spirit).
Verifies:
1. Character offensive & defensive layers (Armour non-linear DR, Evasion entropy, Resistances, Ward/ES).
2. Monster scaling across levels and ranks (Common, Magic, Rare, Boss).
3. Boss Stagger/Poise system (threshold knock-down, damage amplification window).
4. Boss multi-phase enrage triggers (Phase 1, Phase 2 Frenzy, Phase 3 Savage Enrage).
5. Ailment application (Shock damage taken amplification, Ignite/Bleed DoT).
é    Nz../../server)ÚDamageChannelÚCombatantTypeÚAilmentTypeÚ	BossPhaseÚDamageVectorÚDamagePacketÚDefenseSheetÚBossStaggerState)ÚDamageCalculator)ÚMonsterScalingEnginec                   óP   — e Zd Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z	d„ Z
d	„ Zd
„ Zd„ ZdS )ÚTestDamageAndDefenseCalculatorc                 óR   — t          ¦   «         | _        t          ¦   «         | _        d S )N)r   Úcalcr   Úscaling)Úselfs    úFC:\Projects\FreeExile\tests\unit\test_damage_and_defense_calculator.pyÚsetUpz$TestDamageAndDefenseCalculator.setUp    s    € Ý$Ñ&Ô&ˆŒ	Ý+Ñ-Ô-ˆŒˆˆó    c                 óâ   — d}d}d}d}|}t          d¦  «        D ](}| j                             |||¦  «        \  }}|r|dz  }Œ)|                      |d¦  «         |                      |d¦  «         dS )	zQTests that the entropy pool guarantees expected hit frequency without streak RNG.ç     @�@é2   r   é
   é   é   é	   N)Úranger   Úroll_accuracy_vs_evasionÚassertGreaterEqualÚassertLessEqual)r   ÚaccuracyÚevasionÚentropyÚhitsÚcurrent_entropyÚ_Úis_hits           r   Ú/test_evasion_entropy_deterministic_distributionzNTestDamageAndDefenseCalculator.test_evasion_entropy_deterministic_distribution$   s–   € àˆØˆØˆð ˆØ!ˆÝ�r‘”ð 	ð 	ˆAØ&*¤i×&HÒ&HÈÐSZÐ\kÑ&lÔ&lÑ#ˆF�OØð Ø˜‘	�øà×Ò  aÑ(Ô(Ð(Ø×Ò˜T 1Ñ%Ô%Ð%Ð%Ð%r   c                 óH  — d}d}| j                              ||¦  «        }|                      |dd¬¦  «         d}| j                              ||¦  «        }|                      |dd¬¦  «         d}| j                              ||¦  «        }|                      |dd	¬¦  «         d
S )zÇ
        Tests Path of Exile non-linear Armour formula:
        DR = Armour / (Armour + 5 * Damage), capped at 90%.
        Small hits are heavily reduced; massive hits punch through armour!
        ç     ˆ³@ç      Y@gÍÌÌÌÌÌì?é   ©Úplacesr   g      à?g-!ôlVÅ?é   N)r   Úcalculate_armour_mitigationÚassertAlmostEqual)r   ÚarmourÚ	small_hitÚmitigated_smallÚmed_hitÚmitigated_medÚslam_hitÚmitigated_slams           r   Ú test_armour_nonlinear_mitigationz?TestDamageAndDefenseCalculator.test_armour_nonlinear_mitigation6   sº   € ð ˆð ˆ	Øœ)×?Ò?ÀÈ	ÑRÔRˆØ×Ò˜°¸QÐÑ?Ô?Ð?ð ˆØœ	×=Ò=¸fÀgÑNÔNˆØ×Ò˜}¨d¸1ÐÑ=Ô=Ð=ð ˆØœ×>Ò>¸vÀxÑPÔPˆØ×Ò˜~¨v¸aÐÑ@Ô@Ð@Ð@Ð@r   c                 óJ  — | j                              dddd¬¦  «        }|                      |dd¬¦  «         | j                              dddd	¬¦  «        }|                      |d
d¬¦  «         | j                              dddd¬¦  «        }|                      |dd¬¦  «         dS )zVTests that effective resistance is capped at 75% and penetration directly bypasses it.r   g333333ë?g      è?ç        )Ú
raw_damageÚ
resistanceÚmax_capÚpenetrationg     @o@r   r-   g      Ð?ç     @@gš™™™™™É¿g     À’@N)r   Úcalculate_resistance_damager1   )r   Úmitigated_dmgÚpen_dmgÚvuln_dmgs       r   Ú*test_resistance_mitigation_and_penetrationzITestDamageAndDefenseCalculator.test_resistance_mitigation_and_penetrationM   sÝ   € ð œ	×=Ò=ØØØØð	 >ñ 
ô 
ˆð 	×Ò˜}¨e¸AÐÑ>Ô>Ð>ð ”)×7Ò7ØØØØð	 8ñ 
ô 
ˆð 	×Ò˜w¨°aÐÑ8Ô8Ð8ð ”9×8Ò8ØØØØð	 9ñ 
ô 
ˆð 	×Ò˜x¨¸ÐÑ:Ô:Ð:Ð:Ð:r   c                 óî  — t          dt          j        dt          d¬¦  «        d¬¦  «        }t	          dt          j        dddd	d	d
d
¬¦	  «	        }| j                             ||d¬¦  «        \  }}|                      |j	        ¦  «         |  
                    |j        d	¦  «         |  
                    |j        d¦  «         |  
                    |j        d
¦  «         |  
                    |j        d¦  «         dS )z4Tests that Ward absorbs incoming damage before Life.r   r   g     Àr@©Úphysicalr*   ©Úattacker_idÚcombatant_typeÚattacker_levelÚdamage_vectorr!   é   r   g      i@r;   )	Údefender_idrK   Údefender_levelÚmax_lifeÚcurrent_lifeÚmax_wardÚcurrent_wardr2   Úevasion_ratingr   ©Úentropy_poolr+   g      Œ@N)r   r   ÚPLAYERr   r	   ÚMONSTER_COMMONr   Úexecute_hitÚ
assertTruer'   ÚassertEqualÚabsorbed_by_wardÚdamage_to_liferT   rR   ©r   ÚpacketÚdefenseÚresultr&   s        r   Ú"test_ward_energy_shield_absorptionzATestDamageAndDefenseCalculator.test_ward_energy_shield_absorptionj   s  € åØÝ(Ô/ØÝ&°Ð6Ñ6Ô6Øð
ñ 
ô 
ˆõ ØÝ(Ô7ØØØØØØØð

ñ 

ô 

ˆð ”I×)Ò)¨&°'ÈÐ)ÑJÔJ‰	ˆ�Ø�Š˜œÑ&Ô&Ð&Ø×Ò˜Ô0°%Ñ8Ô8Ð8Ø×Ò˜Ô.°Ñ6Ô6Ð6Ø×Ò˜Ô-¨sÑ3Ô3Ð3Ø×Ò˜Ô-¨uÑ5Ô5Ð5Ð5Ð5r   c                 ó°  — t          dt          j        dt          d¬¦  «        d¬¦  «        }t	          dt          j        dddd	¬
¦  «        }| j                             ||d¬¦  «        \  }}|                      |j	        ¦  «         |  
                    |j        ¦  «         |                      |j        d¦  «         |                      |j        d¦  «         dS )u=   Tests Huyá»…n áº¢nh Bá»™ i-frame grants 100% damage immunity.r   rN   r@   rG   g    €‡Ã@rI   r,   r   T)rO   rK   rP   rQ   rR   Úis_in_iframer   rV   r;   N)r   r   rY   r   r	   rX   r   rZ   ÚassertFalser'   r[   Ú	is_evadedr\   Úfinal_total_damagerR   r_   s        r   Útest_phantom_evasion_iframez:TestDamageAndDefenseCalculator.test_phantom_evasion_iframeˆ   sã   € åØÝ(Ô7ØÝ&°Ð6Ñ6Ô6Øð
ñ 
ô 
ˆõ ØÝ(Ô/ØØØØð
ñ 
ô 
ˆð ”I×)Ò)¨&°'ÈÐ)ÑJÔJ‰	ˆ�Ø×Ò˜œÑ'Ô'Ð'Ø�Š˜Ô(Ñ)Ô)Ð)Ø×Ò˜Ô2°CÑ8Ô8Ð8Ø×Ò˜Ô-¨vÑ6Ô6Ð6Ð6Ð6r   c                 óÌ  — | j                              dt          j        ¬¦  «        }| j                              dt          j        ¬¦  «        }| j                              dt          j        ¬¦  «        }|                      |j        |j        dz  ¦  «         |                      |j        |j        dz  ¦  «         |                      |j        t          j
                 d¦  «         dS )zETests level scaling and rank multipliers (Common, Magic, Rare, Boss).r   ©ÚlevelÚrankr   r   é   gffffffÖ?N)r   Úcreate_monster_defenser   rY   ÚBOSSÚassertGreaterrQ   r   Úresistancesr   ÚFIRE)r   Úlvl1_commonÚlvl50_commonÚ
lvl50_bosss       r   Ú)test_monster_scaling_and_rank_multiplierszHTestDamageAndDefenseCalculator.test_monster_scaling_and_rank_multipliers    sÂ   € à”l×9Ò9ÀÍÔHdÐ9ÑeÔeˆØ”|×:Ò:ÀÍ-ÔJfÐ:ÑgÔgˆØ”\×8Ò8¸rÍÔHZÐ8Ñ[Ô[ˆ
à×Ò˜<Ô0°+Ô2FÈÑ2JÑKÔKÐKà×Ò 
Ô 3°\Ô5JÈRÑ5OÑPÔPÐPà×Ò 
Ô 6µ}Ô7IÔ JÈDÑQÔQÐQÐQÐQr   c           	      ó<  — | j                              dt          j        ¬¦  «        }| j                              |j        d¬¦  «        }t          dt          j        dt          d¬¦  «        d|j	        dz  ¬	¦  «        }| j
                             ||d
|¬¦  «        \  }}|                      |j        ¦  «         |                      |j        ¦  «         |                      |j        ¦  «         | j
                             ||||¬¦  «        \  }}|                      |j        ¦  «         |                      |j        ¦  «         |                      |j        ¦  «         |                      |j        d¦  «         t          dt          j        dt          d¬¦  «        d¬¦  «        }| j
                             ||||¬¦  «        \  }	}
|                      |	j        ¦  «         dd|j        t(          j                 z
  z  }|dz  }|                      |	j        |d¬¦  «         dS )z±
        Tests PoE2 Boss Stagger:
        Heavy impact hits fill Stagger meter.
        When reaching 100%, Boss enters STAGGERED state and takes +30% amplified damage.
        é<   rk   ©rl   éc   r   rG   ç     jè@g333333ã?)rJ   rK   rL   rM   r!   Ústagger_impactr   )rW   Úboss_staggerr;   )ÚfirerI   ç      ð?gÍÌÌÌÌÌô?ç       @)ÚdeltaN)r   ro   r   rp   Úcreate_boss_stagger_staterQ   r   rX   r   Úmax_stagger_poiser   rZ   r[   r'   rf   Úboss_staggered_triggeredÚis_staggeredrq   Ústagger_duration_remaining_secrr   r   rs   r1   rh   )r   Úboss_defenser~   Úheavy_packetÚres1Úent1Úres2Úent2Úburst_packetÚ	res_burstr&   Úexpected_base_mitigatedÚexpected_staggereds                r   Ú test_boss_stagger_poise_mechanicz?TestDamageAndDefenseCalculator.test_boss_stagger_poise_mechanic¬   s  € ð ”|×:Ò:ÀÍ-ÔJ\Ð:Ñ]Ô]ˆØ”|×=Ò=¸lÔ>SÐ[]Ð=Ñ^Ô^ˆõ $ØÝ(Ô/ØÝ&°Ð7Ñ7Ô7ØØ'Ô9¸DÑ@ð
ñ 
ô 
ˆð ”Y×*Ò*¨<¸ÐTVÐeqÐ*ÑrÔr‰
ˆˆdØ�Š˜œÑ$Ô$Ð$Ø×Ò˜Ô6Ñ7Ô7Ð7Ø×Ò˜Ô2Ñ3Ô3Ð3ð ”Y×*Ò*¨<¸ÐTXÐgsÐ*ÑtÔt‰
ˆˆdØ�Š˜œÑ$Ô$Ð$Ø�Š˜Ô5Ñ6Ô6Ð6Ø�Š˜Ô1Ñ2Ô2Ð2Ø×Ò˜<ÔFÈÑLÔLÐLõ $ØÝ(Ô/ØÝ&¨FÐ3Ñ3Ô3Øð
ñ 
ô 
ˆð ”y×,Ò,¨\¸<ÐVZÐiuÐ,ÑvÔv‰ˆ	�1Ø�Š˜	Ô(Ñ)Ô)Ð)à"(¨C°,Ô2JÍ=ÔK]Ô2^Ñ,^Ñ"_ÐØ4°tÑ;ÐØ×Ò˜yÔ;Ð=OÐWZÐÑ[Ô[Ð[Ð[Ð[r   c                 ó.  — | j                              dd¬¦  «        }|                      |t          j        ¦  «         | j                              dd¬¦  «        }|                      |t          j        ¦  «         | j                              dd¬¦  «        }|                      |t          j        ¦  «         | j                              ddt          j        ¬¦  «        }|                      |j	         
                    t          j        d¦  «        d	¦  «         d
S )zVTests Boss escalating into Phase 2 (Frenzy) and Phase 3 (Savage Enrage) based on HP %.g     ˆÃ@)Ú
current_hpÚmax_hpr*   ç     @Ÿ@i	  éF   )Úboss_idrl   Úphaser;   gš™™™™™É?N)r   Úevaluate_boss_phaser\   r   ÚPHASE_1_NORMALÚPHASE_2_ENRAGEDÚPHASE_3_SAVAGE_FRENZYÚcreate_boss_attack_packetr   Úelemental_penetrationÚgetr   rs   )r   Úp1Úp2Úp3Úbase_packets        r   Útest_boss_enrage_phasesz6TestDamageAndDefenseCalculator.test_boss_enrage_phasesÛ   sø   € ð Œ\×-Ò-¸ÈÐ-ÑQÔQˆØ×Ò˜�YÔ5Ñ6Ô6Ð6ð Œ\×-Ò-¸ÈÐ-ÑPÔPˆØ×Ò˜�YÔ6Ñ7Ô7Ð7ð Œ\×-Ò-¸ÈÐ-ÑPÔPˆØ×Ò˜�YÔ<Ñ=Ô=Ð=ð ”l×<Ò<ÀSÐPRÕZcÔZyÐ<ÑzÔzˆà×Ò Ô A× EÒ EÅmÔFXÐZ]Ñ ^Ô ^Ð`dÑeÔeÐeÐeÐer   c           
      óx  — ddl m}m} ddlm}  |¦   «         }t          dt          j        ddd¬¦  «        } |ddt          j        d|j        |¬	¦  «        }| j	         
                    dt          j        ¬
¦  «        }| j	                             |j        d¬¦  «        } |ddt          j        d|j        ||t          j        ¬¦  «        }	|                     |¦  «         |                     |	¦  «         t%          dt          j        dt'          |j        dz  ¬¦  «        d¬¦  «        }
|                     dd|
d¬¦  «        }|                      |¦  «         |                      |j        ¦  «         |                      |	j        t          j        ¦  «         |                      |j        t          j        ¦  «         dS )z\Tests that CombatEngine handles full attack transactions and updates Boss Phase dynamically.r   ©ÚCombatEngineÚCombatEntity©ÚFiveElementsr   r   r–   )rO   rK   rP   rQ   rR   u   Kiáº¿m KhÃ¡ch VÃ´ Danh)Ú	entity_idÚnamerK   rl   Úelementra   rk   rz   r,   u   Má»™c YÃªu Ma HoÃ ng)r¬   r­   rK   rl   r®   ra   r~   Ú
boss_phasegÍÌÌÌÌÌÜ?rG   r|   rI   iè  )Úcurrent_timestamp_msN)Úcombat.combat_enginer¨   r©   Úworld.martial_matrixr«   r	   r   rX   ÚKIMr   ro   rp   rƒ   rQ   ÚMOCr   r›   Úregister_entityr   r   Úprocess_attackÚassertIsNotNoner[   r'   r\   r¯   rœ   Úboss_phase_transition)r   r¨   r©   r«   ÚengineÚplayer_defenseÚplayerrˆ   r~   ÚbossÚ
dmg_packetÚress               r   Ú6test_combat_engine_process_attack_and_phase_transitionzUTestDamageAndDefenseCalculator.test_combat_engine_process_attack_and_phase_transitionî   së  € àCÐCÐCÐCÐCÐCÐCÐCØ5Ð5Ð5Ð5Ð5Ð5à�‘”ˆõ &ØÝ(Ô/ØØØð
ñ 
ô 
ˆð �ØØ)Ý(Ô/ØØ Ô$Ø"ð
ñ 
ô 
ˆð ”|×:Ò:ÀÍ-ÔJ\Ð:Ñ]Ô]ˆØ”|×=Ò=¸lÔ>SÐ[]Ð=Ñ^Ô^ˆØˆ|ØØ'Ý(Ô-ØØ Ô$Ø Ø%Ý Ô/ð	
ñ 	
ô 	
ˆð 	×Ò˜vÑ&Ô&Ð&Ø×Ò˜tÑ$Ô$Ð$õ "ØÝ(Ô/ØÝ&°Ô0EÈÑ0LÐMÑMÔMØð
ñ 
ô 
ˆ
ð ×#Ò# A q¨*È4Ð#ÑPÔPˆØ×Ò˜SÑ!Ô!Ð!Ø�Š˜œ
Ñ#Ô#Ð#à×Ò˜œ­)Ô*CÑDÔDÐDØ×Ò˜Ô2µIÔ4MÑNÔNÐNÐNÐNr   c           
      ó¦  — ddl m}m} ddlm}  |¦   «         }t          dt          j        ddddd¬	¦  «        }t          dd
dd¬¦  «        } |ddt          j	        d|j
        ||d¬¦  «        }|                     |¦  «         |                     dd¬¦  «         |                      |j        d¦  «         |                      |j        d¦  «         |                      |j        ¦  «         |                     dd¬¦  «         |                      |j        ¦  «         |                      |j        d
¦  «         |                      |j        d¦  «         dS )zMTests that Ward recharges after 4s idle and staggered boss recovers after 4s.r   r§   rª   r   é   r   r@   r+   )rO   rK   rP   rQ   rR   rS   rT   r;   Tg      @)r„   Úcurrent_staggerr†   r‡   u   Huyáº¿t Ma TÆ°á»›ng)r¬   r­   rK   rl   r®   ra   r~   Úlast_hit_received_timestamp_msr�   iÐ  )Ú	delta_secr°   r€   iˆ  N)r±   r¨   r©   r²   r«   r	   r   rX   r
   rp   ÚTHOrµ   Úupdate_tickr\   rT   r‡   r[   r†   rf   rq   )r   r¨   r©   r«   r¹   ra   ÚstaggerÚentitys           r   Ú1test_combat_engine_ward_recharge_and_stagger_tickzPTestDamageAndDefenseCalculator.test_combat_engine_ward_recharge_and_stagger_tick'  s�  € àCÐCÐCÐCÐCÐCÐCÐCØ5Ð5Ð5Ð5Ð5Ð5à�‘”ˆÝØÝ(Ô/ØØØØØð
ñ 
ô 
ˆõ #Ø$ØØØ+.ð	
ñ 
ô 
ˆð �ØØ&Ý(Ô-ØØ Ô$ØØ Ø+,ð	
ñ 	
ô 	
ˆð 	×Ò˜vÑ&Ô&Ð&ð 	×Ò S¸tÐÑDÔDÐDØ×Ò˜Ô-¨uÑ5Ô5Ð5Ø×Ò˜Ô?ÀÑEÔEÐEØ�Š˜Ô,Ñ-Ô-Ð-ð 	×Ò S¸tÐÑDÔDÐDà×Ò˜Ô-Ñ.Ô.Ð.Ø×Ò˜Ô?ÀÑEÔEÐEà×Ò˜7Ô/°Ñ7Ô7Ð7Ð7Ð7r   N)Ú__name__Ú
__module__Ú__qualname__r   r(   r9   rE   rc   ri   rw   r’   r¥   r¿   rÉ   © r   r   r   r      sÊ   € € € € € ð.ð .ð .ð&ð &ð &ð$Að Að Að.;ð ;ð ;ð:6ð 6ð 6ð<7ð 7ð 7ð0
Rð 
Rð 
Rð-\ð -\ð -\ð^fð fð fð&7Oð 7Oð 7Oðr-8ð -8ð -8ð -8ð -8r   r   Ú__main__)!Ú__doc__ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arÚunittestÚsysÚosÚpathÚinsertÚabspathÚjoinÚdirnameÚ__file__Úcombat.combat_typesr   r   r   r   r   r   r	   r
   Úcombat.damage_calculatorr   Úcombat.monster_scalingr   ÚTestCaser   rÊ   ÚmainrÍ   r   r   ú<module>rä      sn  ððð ð € € € € € € € € € € € € €€€€Ø 
€
€
€
Ø 	€	€	€	à „‡‚��2”7—?’? 2¤7§<¢<°´·²ÀÑ0IÔ0IÈ>Ñ#ZÔ#ZÑ[Ô[Ñ \Ô \Ð \ð	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 6Ð 5Ð 5Ð 5Ð 5Ð 5Ø 7Ð 7Ð 7Ð 7Ð 7Ð 7ðu8ð u8ð u8ð u8ð u8 XÔ%6ñ u8ô u8ð u8ðp	 ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr   