§
    ð¿jR  ã                  ód   — 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
 ddlmZ  G d„ d¦  «        ZdS )	aú  
FreeExile Party Scaling Calculator.
Pure mathematical engine implementing Path of Exile 2 party scaling:
- Nearby radius spatial membership (15.0m, zone, instance)
- EXP sharing pool (+30%/player, (Lvl+10)^2.71 weighting, level gap decay)
- Item & Currency Quantity (+50%/player) and Rarity (+30%/player) multipliers
- Dynamic monster HP, Armour, Resistance (+2%/player cap 75%), and Poise scaling.

Follows Engineering Standards 2026: Soft cap <= 350 lines, strict typing, zero allocations in hot paths.
é    )ÚannotationsN)ÚAnyÚDictÚListÚOptionalÚTupleÚUnioné   )ÚPartyMemberStatec                  ó´   — e Zd ZU dZdZded<   eefd(d„¦   «         Zed)d„¦   «         Ze	 d*d+d„¦   «         Z	ed,d„¦   «         Z
e	 	 	 d-ddddœd.d'„¦   «         ZdS )/ÚPartyScalingCalculatorzBAuthoritative mathematical oracle for PoE2 party scaling formulas.g      .@ÚfloatÚNEARBY_RADIUS_DEFAULTÚmembersúList[PartyMemberState]Úref_wxÚref_wyÚref_zoneÚstrÚref_instÚradiusÚreturnc           
     ó>  — g }| D ]—}|j         sŒ
|j        |k    s|j        |k    rŒ!t          |dt          |dd¦  «        ¦  «        }t          |dt          |dd¦  «        ¦  «        }	t	          j        ||z
  |	|z
  ¦  «        }
|
|k    r|                     |¦  «         Œ˜|S )z©
        Filters party members that are within the 15.0m nearby radius.
        Requires exact match for zone_id, instance_id, and euclidean distance <= radius.
        ÚwxÚxç        ÚwyÚy)Ú	is_onlineÚzone_idÚinstance_idÚgetattrÚmathÚhypotÚappend)r   r   r   r   r   r   ÚnearbyÚmÚm_xÚm_yÚdists              ú>C:\Projects\FreeExile\server\party\party_scaling_calculator.pyÚcalculate_nearby_membersz/PartyScalingCalculator.calculate_nearby_members   sµ   € ð *,ˆØð 		!ð 		!ˆAØ”;ð ØØŒy˜HÒ$Ð$¨¬¸Ò(AÐ(AØÝ˜!˜T¥7¨1¨c°3Ñ#7Ô#7Ñ8Ô8ˆCÝ˜!˜T¥7¨1¨c°3Ñ#7Ô#7Ñ8Ô8ˆCÝ”:˜c F™l¨C°&©LÑ9Ô9ˆDØ�vŠ~ˆ~Ø—’˜aÑ Ô Ð øØˆó    Úplayer_levelÚintÚ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 )z�
        Calculates experience yield multiplier based on character-monster level gap.
        Matches LevelProgressionService authoritative formula.
        é   ç      ð?g{®Gáz„?g333333ã¿gš™™™™™©?gš™™™™™Ù¿)ÚabsÚmaxr#   Úexp)r.   r0   ÚgapÚdeltas       r+   Úcalculate_level_gap_multiplierz5PartyScalingCalculator.calculate_level_gap_multiplier2   s{   € ð ˜]Ñ*ˆÝˆ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-   NÚbase_expúOptional[int]úDict[str, int]c                ón  ‡	‡
‡‡— t          |¦  «        Š	‰	dk    s| dk    ri S dd‰	dz
  z  z   }t          t          j        | |z  ¦  «        ¦  «        Š
i Š|D ]4}t          j        t          |j        dz   ¦  «        d¦  «        ‰|j        <   Œ5t          ‰ 	                    ¦   «         ¦  «        Š‰dk    rˆ	ˆ
fd„|D ¦   «         }nˆ
ˆˆfd	„|D ¦   «         }|�bi }|D ][}||j                 }t                               |j        |¦  «        }t          t          j        ||z  ¦  «        ¦  «        ||j        <   Œ\|S |S )
a  
        Distributes monster experience among nearby party members:
        - Total Pool: BaseEXP * (1.0 + 0.30 * (N - 1))
        - Share Weight: W_i = (Level_i + 10)^2.71
        - Optional: Applies personal level gap penalty if monster_level provided.
        r   r3   ç333333Ó?r
   é
   g®Gáz®@r   c                ó&   •— i | ]}|j         ‰‰z  “ŒS © )Ú	player_id)Ú.0r'   ÚnÚ
total_pools     €€r+   ú
<dictcomp>z>PartyScalingCalculator.calculate_party_exp.<locals>.<dictcomp>Z   s!   ø€ ÐHÐHÐH¸1˜!œ+ z°Q¡ÐHÐHÐHr-   c           
     ó€   •— i | ]:}|j         t          t          j        ‰‰|j                  ‰z  z  ¦  «        ¦  «        “Œ;S rA   )rB   r/   r#   Úfloor)rC   r'   rE   Útotal_weightÚweightss     €€€r+   rF   z>PartyScalingCalculator.calculate_party_exp.<locals>.<dictcomp>\   sQ   ø€ ð ð ð àð ”�S¥¤¨J¸'À!Ä+Ô:NÐQ]Ñ:]Ñ,^Ñ!_Ô!_Ñ`Ô`ðð ð r-   )Úlenr/   r#   rH   Úpowr   ÚlevelrB   ÚsumÚvaluesr   r9   )r:   r   r0   Ú	pool_multr'   Ú
raw_sharesÚfinal_sharesÚrawÚgap_multrD   rE   rI   rJ   s            @@@@r+   Úcalculate_party_expz*PartyScalingCalculator.calculate_party_expA   s}  øøøø€ õ �‰LŒLˆØ�Š6ˆ6�X ’]�]ØˆIà˜$ ! a¡%™.Ñ(ˆ	Ý�œ H¨yÑ$8Ñ9Ô9Ñ:Ô:ˆ
à$&ˆØð 	Gð 	GˆAÝ#'¤8­E°!´'¸B±,Ñ,?Ô,?ÀÑ#FÔ#FˆG�A”KÑ Ð å˜7Ÿ>š>Ñ+Ô+Ñ,Ô,ˆØ˜3ÒÐØHÐHÐHÐHÐHÀÐHÑHÔHˆJˆJðð ð ð ð ð à ðñ ô ˆJð
 Ð$Ø+-ˆLØð Lð L�Ø  ¤Ô-�Ý1×PÒPÐQRÔQXÐZgÑhÔh�Ý,/µ´
¸3À¹>Ñ0JÔ0JÑ,KÔ,K�˜Qœ[Ñ)Ð)ØÐàÐr-   Únearby_countúTuple[float, float, float]c                ór   — | dk    rdS t          d| dz
  ¦  «        }dd|z  z   }dd|z  z   }dd|z  z   }|||fS )zæ
        Calculates (item_quantity, currency_quantity, rarity) multipliers:
        - Item Quantity: 1.0 + 0.50 * (N - 1)
        - Currency Quantity: 1.0 + 0.50 * (N - 1)
        - Rarity Multiplier: 1.0 + 0.30 * (N - 1)
        r   )r3   r3   r3   r
   r3   ç      à?r>   )r5   )rV   ÚextraÚitem_qtyÚcurr_qtyÚraritys        r+   Úcalculate_loot_multipliersz1PartyScalingCalculator.calculate_loot_multipliersk   s_   € ð ˜1ÒÐØ �=Ý�A�| aÑ'Ñ(Ô(ˆØ˜ ™Ñ%ˆØ˜ ™Ñ%ˆØ�t˜e‘|Ñ#ˆØ˜ 6Ð)Ð)r-   )Ú
party_sizeÚbase_hpÚrankÚarg1ú Optional[Union[int, float, str]]Úarg2úOptional[Union[str, int]]Úparty_countr_   r`   úOptional[Union[int, float]]ra   úOptional[str]úDict[str, Any]c               ó¸  — d}d}d}t          | t          t          f¦  «        rCt          | ¦  «        }t          |t          ¦  «        r|}|�|}nFt          |t          ¦  «        r|}n.t          | t          ¦  «        r| }t          |t          ¦  «        r|}|�|}|�|}|�t          |¦  «        }|�|}t	          d|dz
  ¦  «        }	|                     ¦   «         }
d|
v r	dd|	z  z   }nd|
v r	dd|	z  z   }ndd	|	z  z   }dd
|	z  z   }t          dd|	z  ¦  «        }d}||||dœ}|�:t          t          ||z  ¦  «        ¦  «        }||d<   t          ||z  d¦  «        |d<   |S )zá
        Calculates scaled monster defenses:
        Supports both signatures:
          calculate_monster_scaling("COMMON", party_size=6)
          calculate_monster_scaling(base_hp=1000, rank="BOSS", party_count=6)
        NÚCOMMONr
   r   ÚBOSSr3   ÚRAREgffffffæ?rY   gš™™™™™¹?g     ÀR@g       @g333333Ã?)Úhp_multÚ
armor_multÚ	res_bonusÚpoise_ratioÚ	scaled_hpÚ	poise_max)Ú
isinstancer/   r   r   r5   ÚupperÚminÚround)rb   rd   rf   r_   r`   ra   Úresolved_base_hpÚresolved_rankÚresolved_sizerZ   Ú
rank_upperrn   ro   rp   rq   Úresrr   s                    r+   Úcalculate_monster_scalingz0PartyScalingCalculator.calculate_monster_scaling{   sÇ  € ð  -1ÐØ%ˆØˆå�d�S¥%˜LÑ)Ô)ð 	%Ý$ T™{œ{ÐÝ˜$¥Ñ$Ô$ð %Ø $�ØÐ&Ø +��Ý˜D¥#Ñ&Ô&ð %Ø $�øÝ˜�cÑ"Ô"ð 	%Ø ˆMÝ˜$¥Ñ$Ô$ð %Ø $�àÐ"Ø'ˆMØÐ!Ø&ˆMØÐÝ$ W™~œ~ÐØÐØ ˆMå�A�} qÑ(Ñ)Ô)ˆØ"×(Ò(Ñ*Ô*ˆ
à�ZÐÐØ˜D 5™LÑ(ˆGˆGØ�zÐ!Ð!Ø˜D 5™LÑ(ˆGˆGà˜D 5™LÑ(ˆGà˜4 %™<Ñ'ˆ
Ý˜˜c E™kÑ*Ô*ˆ	Øˆð Ø$Ø"Ø&ð	
ð 
ˆð Ð'Ý�EÐ"2°WÑ"<Ñ=Ô=Ñ>Ô>ˆIØ(ˆC�ÑÝ$ Y°Ñ%<¸aÑ@Ô@ˆC�Ñàˆ
r-   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r.   r/   r0   r/   r   r   )N)r:   r/   r   r   r0   r;   r   r<   )rV   r/   r   rW   )NNN)rb   rc   rd   re   rf   r;   r_   r;   r`   rg   ra   rh   r   ri   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ú__annotations__Ústaticmethodr,   r9   rU   r^   r}   rA   r-   r+   r   r      s  € € € € € € ØLÐLà#'ÐÐ'Ð'Ð'Ñ'àð .ðð ð ð ñ „\ðð2 ð2ð 2ð 2ñ „\ð2ð ð (,ð'ð 'ð 'ð 'ñ „\ð'ðR ð*ð *ð *ñ „\ð*ð à15Ø*.Ø%)ðCð
 %)Ø/3Ø"ðCð Cð Cð Cð Cñ „\ðCð Cð Cr-   r   )r�   Ú
__future__r   r#   Útypingr   r   r   r   r   r	   Úparty_typesr   r   rA   r-   r+   ú<module>r‡      sª   ðð	ð 	ð #Ð "Ð "Ð "Ð "Ð "Ø €€€Ø :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :Ð :à )Ð )Ð )Ð )Ð )Ð )ðlð lð lð lð lñ lô lð lð lð lr-   