§
    žv¼j´7  ã                  ó¬   — d Z ddlmZ ddlZddlmZmZmZmZm	Z	 ddl
mZmZmZmZmZmZmZ ddlmZmZmZmZmZ ddlmZ dd	lmZ  G d
„ d¦  «        ZdS )a  
FreeExile Inventory, Personal Stash & Premium Merchant Stash Service.
Authoritative management of character bags, multi-tab storage, and MegaShop-gated merchant stalls.
Follows Engineering Standards 2026: Soft cap <= 350 lines, Zero-trust server authority.
é    )ÚannotationsN)ÚDictÚListÚOptionalÚTupleÚAnyé   )ÚItemTypeÚEquipmentSlotÚInventoryItemÚCharacterInventoryÚPersonalStashÚMerchantStashTabÚPlayerStorageContainer)Útry_merge_stackÚfind_first_empty_slotÚis_equipment_compatibleÚcreate_split_itemÚ create_default_storage_container)Úget_default_item_dimensions)ÚMegaShopServicec                  ó¼   — e Zd ZdZdZ	 	 d;d<d
„Zd=d„Zd>d„Zd?d„Zd@d„Z		 dAdBd„Z
	 dCdDd!„ZdEd%„ZdFd)„Z	 dAdGd+„Z	 dAdHd.„Z	 dAdId/„ZdJd1„ZdKd2„ZdLd6„ZdMd9„Z	 dAdNd:„ZdS )OÚInventoryServicezcAuthoritative service governing Character Inventory, Multi-Tab Stashes, and Premium Merchant Stash.Úitem_premium_merchant_stash_tabNÚshop_serviceúOptional[MegaShopService]Úconsignment_vaultúOptional[Any]ÚreturnÚNonec                óL   — |pt          ¦   «         | _        || _        i | _        d S ©N)r   r   r   Ú_containers)Úselfr   r   s      ú;C:\Projects\FreeExile\server\inventory\inventory_service.pyÚ__init__zInventoryService.__init__$   s-   € ð
 )Ð=­OÑ,=Ô,=ˆÔØ!2ˆÔØJLˆÔÐÐó    Ú
account_idÚstrÚcharacter_idr   c                ó^  — ||f}|| j         vrt          ||¦  «        | j         |<   | j         |         }|j        j        sn| j        rg| j                             |¦  «        }|j                             | j        d¦  «        dk    r)d|j        _        t          j	        ¦   «         |j        _
        |S )Nr   T)r#   r   Úmerchant_stashÚis_unlockedr   Ú
get_playerÚproduct_purchase_countsÚgetÚ!PREMIUM_MERCHANT_STASH_PRODUCT_IDÚtimeÚunlocked_at)r$   r(   r*   ÚkeyÚ	containerÚplayers         r%   Úget_or_create_character_storagez0InventoryService.get_or_create_character_storage-   s¶   € ð ˜<Ð(ˆØ�dÔ&Ð&Ð&Ý$DÀZÐQ]Ñ$^Ô$^ˆDÔ˜SÑ!àÔ$ SÔ)ˆ	ØÔ'Ô3ð 	C¸Ô8Ið 	CØÔ&×1Ò1°*Ñ=Ô=ˆFØÔ-×1Ò1°$Ô2XÐZ[Ñ\Ô\Ð_`Ò`Ð`Ø7;�	Ô(Ô4Ý7;´y±{´{�	Ô(Ô4àÐr'   r   c                ó8   — |                       ||¦  «        j        S r"   )r7   Ú	inventory©r$   r(   r*   s      r%   Úget_character_inventoryz(InventoryService.get_character_inventory=   s   € Ø×3Ò3°JÀÑMÔMÔWÐWr'   r   c                ó8   — |                       ||¦  «        j        S r"   )r7   Ústashr:   s      r%   Úget_personal_stashz#InventoryService.get_personal_stash@   s   € Ø×3Ò3°JÀÑMÔMÔSÐSr'   r   c                ó8   — |                       ||¦  «        j        S r"   )r7   r,   r:   s      r%   Úget_merchant_stashz#InventoryService.get_merchant_stashC   s   € Ø×3Ò3°JÀÑMÔMÔ\Ð\r'   Úitemr   Úpreferred_slotúOptional[int]úTuple[bool, str, Optional[int]]c                ó  — |                       ||¦  «        }|j        dk    r]|j        dk    rR|j        t          j        t          j        fv r3t          |j        |j        ¦  «        }|j        |j        c|_        |_        t          |j
        |¦  «        \  }}|r|�dd|j        › d|› �|fS |�0d|cxk    r|j        k     rn n||j
        vr||j
        |<   dd|› �|fS t          |j
        |j        ¦  «        }	|	�||j
        |	<   dd|	› �|	fS dS )Nr	   TzStacked z into slot r   zItem placed into slot )FzInventory bag is fullN)r;   ÚwidthÚheightÚ	item_typer
   ÚWEAPONÚARMORr   Úitem_idr   ÚslotsÚstack_countÚcapacityr   )
r$   r(   r*   rA   rB   ÚinvÚdimÚmergedÚs_idxÚ
empty_slots
             r%   Úadd_item_to_inventoryz&InventoryService.add_item_to_inventoryH   sM  € ð ×*Ò*¨:°|ÑDÔDˆàŒ:˜Š?ˆ?˜tœ{¨aÒ/Ð/°D´NÅxÄÕX`ÔXfÐFgÐ4gÐ4gÝ-¨d¬l¸D¼NÑKÔKˆCØ&)¤i°´Ð#ˆDŒJ˜œå'¨¬	°4Ñ8Ô8‰ˆ�Øð 	P�eÐ'ØÐH DÔ$4ÐHÐHÀÐHÐHÈ%ÐOÐOàÐ%¨!¨~Ð*LÐ*LÒ*LÐ*LÀÄÒ*LÐ*LÐ*LÐ*LÐ*LÐQ_ÐgjÔgpÐQpÐQpØ(,ˆCŒI�nÑ%ØÐB°.ÐBÐBÀNÐRÐRå*¨3¬9°c´lÑCÔCˆ
ØÐ!Ø$(ˆCŒI�jÑ!ØÐ>°*Ð>Ð>À
ÐJÐJà3Ð3r'   r	   ÚslotÚintÚcountú)Tuple[bool, str, Optional[InventoryItem]]c                óô   — |                       ||¦  «        }||j        vr	dd|› d�d fS |j        |         }|j        |k    r)|xj        |z  c_        dd|› d|› �t          ||¦  «        fS |j        |= dd|› �|fS )NFzSlot ú	 is emptyTz
Extracted z items from slot zRemoved item from slot )r;   rL   rM   r   )r$   r(   r*   rU   rW   rO   rA   s          r%   Úremove_item_from_inventoryz+InventoryService.remove_item_from_inventoryd   s¶   € ð ×*Ò*¨:°|ÑDÔDˆØ�s”yÐ Ð ØÐ1 $Ð1Ð1Ð1°4Ð7Ð7ØŒy˜ŒˆØÔ˜eÒ#Ð#ØÐÔ Ñ%ÐÔØÐD eÐDÐD¸dÐDÐDÕFWÐX\Ð^cÑFdÔFdÐdÐdØŒI�dˆOØÐ5¨tÐ5Ð5°tÐ;Ð;r'   Ú	from_slotÚto_slotúTuple[bool, str]c                óL  — |                       ||¦  «        }||j        vrdd|› d�fS |dk     s||j        k    rdd|› d�fS ||k    rdS |j                             |¦  «        }||j        v r)|j        |         |j        |<   ||j        |<   dd	|› d
|› �fS ||j        |<   dd|› �fS )NFzSource slot rZ   r   zTarget slot ú out of range)TzNo-opTzSwapped slot z with zMoved item to slot )r;   rL   rN   Úpop)r$   r(   r*   r\   r]   rO   Úsrc_items          r%   Úmove_inventory_itemz$InventoryService.move_inventory_itemq   sô   € ð ×*Ò*¨:°|ÑDÔDˆØ˜CœIÐ%Ð%ØÐ=¨Ð=Ð=Ð=Ð=Ð=Ø�QŠ;ˆ;˜' S¤\Ò1Ð1ØÐ?¨Ð?Ð?Ð?Ð?Ð?Ø˜ÒÐØ �=à”9—=’= Ñ+Ô+ˆØ�c”iÐÐØ#&¤9¨WÔ#5ˆCŒI�iÑ Ø!)ˆCŒI�gÑØÐC¨ÐCÐC¸'ÐCÐCÐCÐCà%ˆŒ	�'ÑØÐ4¨7Ð4Ð4Ð4Ð4r'   Úinv_slotÚ
equip_slotr   c                óN  — |                       ||¦  «        }||j        vrdd|› d�fS |j        |         }t          |j        |¦  «        sdd|j        j        › d|j        › �fS |j        |= ||j        v r|j        |         |j        |<   ||j        |<   dd|j        › d|j        › �fS )	NFúInventory slot rZ   zCannot equip z into Tz	Equipped ú to )r;   rL   r   rH   ÚvalueÚ	equipmentÚname)r$   r(   r*   rd   re   rO   rA   s          r%   Ú
equip_itemzInventoryService.equip_item…   sÙ   € ð ×*Ò*¨:°|ÑDÔDˆØ˜3œ9Ð$Ð$ØÐ?¨HÐ?Ð?Ð?Ð?Ð?àŒy˜Ô"ˆÝ& t¤~°zÑBÔBð 	YØÐX¨$¬.Ô*>ÐXÐXÀjÔFVÐXÐXÐXÐXàŒI�hÐØ˜œÐ&Ð&Ø"%¤-°
Ô";ˆCŒI�hÑà$(ˆŒ�jÑ!ØÐB ¤ÐBÐB°
Ô0@ÐBÐBÐBÐBr'   Útarget_inv_slotc                ó   — |                       ||¦  «        }||j        vrdd|j        › �fS |j        |         }|}|�||j        v s||j        k    rt          |j        |j        ¦  «        }|€dS |j        |= ||j        |<   dd|j        › d|› �fS )NFzNo gear equipped in )Fz!Cannot unequip: inventory is fullTzUnequipped z into inventory slot )r;   rj   ri   rL   rN   r   rk   )r$   r(   r*   re   rm   rO   rA   Ú	dest_slots           r%   Úunequip_itemzInventoryService.unequip_item—   s¿   € ð ×*Ò*¨:°|ÑDÔDˆØ˜Sœ]Ð*Ð*ØÐC°Ô1AÐCÐCÐCÐCàŒ}˜ZÔ(ˆØ#ˆ	ØÐ 	¨S¬YÐ 6Ð 6¸)ÀsÄ|Ò:SÐ:SÝ-¨c¬i¸¼ÑFÔFˆIàÐØ=Ð=àŒM˜*Ð%Ø#ˆŒ	�)ÑØÐN 4¤9ÐNÐNÀ9ÐNÐNÐNÐNr'   Útab_idÚ
stash_slotc                ó¦  — |                       ||¦  «        }|                      ||¦  «        }||j        vrdd|› d�fS ||j        vrdd|› d�fS |j        |         }|j        |         }	|�||j        vr||j        k     r|nt          |j        |j        ¦  «        }
|
€dd|j        › d�fS |j        |= |	|j        |
<   dd|	j        › d	|j        › d
|
› �fS )NFrg   rZ   ú
Stash tab ú
 not foundz is fullTz
Deposited rh   z slot )r;   r>   rL   ÚtabsrN   r   rk   )r$   r(   r*   rd   rq   rr   rO   r=   ÚtabrA   ro   s              r%   Údeposit_to_stashz!InventoryService.deposit_to_stash°   sE  € ð ×*Ò*¨:°|ÑDÔDˆØ×'Ò'¨
°LÑAÔAˆà˜3œ9Ð$Ð$ØÐ?¨HÐ?Ð?Ð?Ð?Ð?Ø˜œÐ#Ð#ØÐ9 vÐ9Ð9Ð9Ð9Ð9àŒj˜Ô ˆØŒy˜Ô"ˆØ#-Ð#9¸jÐPSÔPYÐ>YÐ>YÐ^hÐknÔkwÒ^wÐ^w�J�Jõ  Tð  UXô  U^ð  `cô  `lñ  mô  mˆ	àÐØÐ9 s¤xÐ9Ð9Ð9Ð9Ð9àŒI�hÐØ#ˆŒ	�)ÑØÐL $¤)ÐLÐL°´ÐLÐLÀÐLÐLÐLÐLr'   c                ó€  — |                       ||¦  «        }|                      ||¦  «        }||j        vrdd|› d�fS |j        |         }||j        vrdd|› d�fS |j        |         }	|�||j        vr||j        k     r|nt          |j        |j        ¦  «        }
|
€dS |j        |= |	|j        |
<   dd|	j        › d	|
› �fS )
NFrt   ru   zStash slot rZ   )Fz"Cannot withdraw: inventory is fullTú	Withdrew z to inventory slot )r>   r;   rv   rL   rN   r   rk   )r$   r(   r*   rq   rr   rm   r=   rO   rw   rA   ro   s              r%   Úwithdraw_from_stashz$InventoryService.withdraw_from_stashÆ   s8  € ð ×'Ò'¨
°LÑAÔAˆØ×*Ò*¨:°|ÑDÔDˆà˜œÐ#Ð#ØÐ9 vÐ9Ð9Ð9Ð9Ð9ØŒj˜Ô ˆØ˜SœYÐ&Ð&ØÐ=¨
Ð=Ð=Ð=Ð=Ð=àŒy˜Ô$ˆØ(7Ð(CÈÐ_bÔ_hÐHhÐHhÐm|ð  @Cô  @Lò  nLð  nL�O�Oõ  Shð  ilô  irð  twô  t@ñ  SAô  SAˆ	àÐØ>Ð>àŒI�jÐ!Ø#ˆŒ	�)ÑØÐJ ¤ÐJÐJ¸yÐJÐJÐJÐJr'   Úboolc                ó.  — |                       ||¦  «        }|j        j        rdS | j        ri| j                             |¦  «        }|j                             | j        d¦  «        dk    r+d|j        _        t          j        ¦   «         |j        _	        dS dS )NTr   F)
r7   r,   r-   r   r.   r/   r0   r1   r2   r3   )r$   r(   r*   r5   r6   s        r%   Úis_merchant_stash_unlockedz+InventoryService.is_merchant_stash_unlockedÞ   s”   € Ø×8Ò8¸À\ÑRÔRˆ	ØÔ#Ô/ð 	Ø�4ØÔð 	ØÔ&×1Ò1°*Ñ=Ô=ˆFØÔ-×1Ò1°$Ô2XÐZ[Ñ\Ô\Ð_`Ò`Ð`Ø7;�	Ô(Ô4Ý7;´y±{´{�	Ô(Ô4Ø�tØˆur'   c                ó.  — |                       ||¦  «        rdS | j        sdS | j                             || j        ¬¦  «        }|j        sdd|j        › �fS |                      ||¦  «        }d|j        _        t          j	        ¦   «         |j        _
        dS )N)Tz&Merchant stash tab is already unlocked)FzMegaShop service unavailable)r(   Ú
product_idFzFailed to unlock: T)TzASuccessfully unlocked Premium Merchant Stash Tab with Gold Cores!)r~   r   Úpurchase_product_with_coinsr1   ÚsuccessÚerror_messager7   r,   r-   r2   r3   )r$   r(   r*   Úsettle_resultr5   s        r%   Ú unlock_merchant_stash_with_coinsz1InventoryService.unlock_merchant_stash_with_coinsê   s·   € ð ×*Ò*¨:°|ÑDÔDð 	BØAÐAàÔ ð 	9Ø8Ð8àÔ)×EÒEØ!ØÔ=ð Fñ 
ô 
ˆð Ô$ð 	MØÐL¨}Ô/JÐLÐLÐLÐLà×8Ò8¸À\ÑRÔRˆ	Ø/3ˆ	Ô Ô,Ý/3¬y©{¬{ˆ	Ô Ô,ØXÐXr'   Úmerchant_slotÚprice_currencyÚprice_amountc                ó:  — |                       ||¦  «        sdS |dk    rdS |                      ||¦  «        }|                      ||¦  «        }||j        vrdd|› d�fS |dk     s||j        k    rdd|› d�fS ||j        v rdd|› d	�fS |j                             |¦  «        }	||	_        ||	_        | j        rJt          | j        d
¦  «        r5	 | j         
                    ||	||¬¦  «        |	_        n# t          $ r Y nw xY w|	|j        |<   dd|	j        › d|› d|› �fS )N)FzlMerchant Stash Tab is locked. Purchase the Premium Merchant Stash Tab in MegaShop with Gold Cores to unlock.r   )Fz,Price amount must be strictly greater than 0Frg   rZ   úMerchant slot r`   z is already occupiedÚlist_item_for_sale)Ú	seller_idrA   r‡   rˆ   TzListed z in Merchant Stash for ú )r~   r;   r@   rL   rN   ra   Úasking_price_currencyÚasking_price_amountr   Úhasattrr‹   Ú
listing_idÚ	Exceptionrk   )
r$   r(   r*   rd   r†   r‡   rˆ   rO   Úmerchant_tabrA   s
             r%   Úplace_item_in_merchant_stashz-InventoryService.place_item_in_merchant_stashÿ   s¨  € ð ×.Ò.¨z¸<ÑHÔHð 	Ið Ið  Ià˜1ÒÐØHÐHà×*Ò*¨:°|ÑDÔDˆØ×.Ò.¨z¸<ÑHÔHˆà˜3œ9Ð$Ð$ØÐ?¨HÐ?Ð?Ð?Ð?Ð?Ø˜1ÒÐ °Ô1FÒ FÐ FØÐG¨=ÐGÐGÐGÐGÐGØ˜LÔ.Ð.Ð.ØÐN¨=ÐNÐNÐNÐNÐNàŒy�}Š}˜XÑ&Ô&ˆØ%3ˆÔ"Ø#/ˆÔ àÔ!ð 	¥g¨dÔ.DÐFZÑ&[Ô&[ð 	ðØ"&Ô"8×"KÒ"KØ(¨tÀNÐamð #Lñ #ô #�”�øõ ð ð ð Ø�ðøøøð -1ˆÔ˜=Ñ)ØÐ`˜tœyÐ`Ð`ÀÐ`Ð`ÐP^Ð`Ð`Ð`Ð`s   Ã#C1 Ã1
C>Ã=C>Únew_currencyÚ
new_amountc                ó¼   — |                       ||¦  «        }||j        vrdd|› d�fS |dk    rdS |j        |         }|j        rdS ||_        ||_        dd|› d	|› �fS )
NFrŠ   rZ   r   )Fz'New price amount must be greater than 0)Fz0Item is currently locked in an active trade swapTzUpdated price to r�   )r@   rL   Ú	is_lockedrŽ   r�   )r$   r(   r*   r†   r•   r–   r“   rA   s           r%   Úupdate_merchant_item_pricez+InventoryService.update_merchant_item_price'  s—   € ð ×.Ò.¨z¸<ÑHÔHˆØ Ô 2Ð2Ð2ØÐC¨=ÐCÐCÐCÐCÐCØ˜Š?ˆ?ØCÐCàÔ! -Ô0ˆØŒ>ð 	MØLÐLà%1ˆÔ"Ø#-ˆÔ ØÐD¨ÐDÐD°lÐDÐDÐDÐDr'   c                ó|  — |                       ||¦  «        }|                      ||¦  «        }||j        vrdd|› d�fS |j        |         }|j        rdS |}|�||j        v s||j        k    rt          |j        |j        ¦  «        }|€dS |j        |= d |_        d|_        d |_        ||j        |<   dd|j	        › d	�fS )
NFrŠ   rZ   )Fz/Cannot withdraw: item is locked in 2PC checkout)Fz&Cannot withdraw: inventory bag is fullr   Trz   z  from Merchant Stash back to bag)
r@   r;   rL   r˜   rN   r   rŽ   r�   r‘   rk   )	r$   r(   r*   r†   rm   r“   rO   rA   ro   s	            r%   Úremove_item_from_merchant_stashz0InventoryService.remove_item_from_merchant_stash8  sú   € ð ×.Ò.¨z¸<ÑHÔHˆØ×*Ò*¨:°|ÑDÔDˆà Ô 2Ð2Ð2ØÐC¨=ÐCÐCÐCÐCÐCàÔ! -Ô0ˆØŒ>ð 	LØKÐKà#ˆ	ØÐ 	¨S¬YÐ 6Ð 6¸)ÀsÄ|Ò:SÐ:SÝ-¨c¬i¸¼ÑFÔFˆIàÐØBÐBàÔ˜}Ð-Ø%)ˆÔ"Ø#$ˆÔ ØˆŒØ#ˆŒ	�)ÑØÐL ¤ÐLÐLÐLÐLÐLr'   )NN)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"   )
r(   r)   r*   r)   rA   r   rB   rC   r   rD   )r	   )
r(   r)   r*   r)   rU   rV   rW   rV   r   rX   )
r(   r)   r*   r)   r\   rV   r]   rV   r   r^   )
r(   r)   r*   r)   rd   rV   re   r   r   r^   )
r(   r)   r*   r)   re   r   rm   rC   r   r^   )r(   r)   r*   r)   rd   rV   rq   r)   rr   rC   r   r^   )r(   r)   r*   r)   rq   r)   rr   rV   rm   rC   r   r^   )r(   r)   r*   r)   r   r|   )r(   r)   r*   r)   r   r^   )r(   r)   r*   r)   rd   rV   r†   rV   r‡   r)   rˆ   rV   r   r^   )r(   r)   r*   r)   r†   rV   r•   r)   r–   rV   r   r^   )
r(   r)   r*   r)   r†   rV   rm   rC   r   r^   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r1   r&   r7   r;   r>   r@   rT   r[   rc   rl   rp   rx   r{   r~   r…   r”   r™   r›   © r'   r%   r   r      sû  € € € € € ØmÐmà(IÐ%ð 37Ø+/ðMð Mð Mð Mð Mðð ð ð ð Xð Xð Xð XðTð Tð Tð Tð]ð ]ð ]ð ]ð )-ð4ð 4ð 4ð 4ð 4ð: KLð<ð <ð <ð <ð <ð5ð 5ð 5ð 5ð(Cð Cð Cð Cð. *.ðOð Oð Oð Oð Oð4 koðMð Mð Mð Mð Mð. rvðKð Kð Kð Kð Kð0
ð 
ð 
ð 
ðYð Yð Yð Yð*&að &að &að &aðPEð Eð Eð Eð, *.ðMð Mð Mð Mð Mð Mð Mr'   r   )rŸ   Ú
__future__r   r2   Útypingr   r   r   r   r   Úinventory_typesr
   r   r   r   r   r   r   Úinventory_helpersr   r   r   r   r   Úspatial_grid_typesr   Úshop.shop_servicer   r   r    r'   r%   ú<module>r§      sb  ððð ð #Ð "Ð "Ð "Ð "Ð "Ø €€€Ø 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ðð ð ð ð ð ð ð ð ð ð ð ð ð ð <Ð ;Ð ;Ð ;Ð ;Ð ;Ø -Ð -Ð -Ð -Ð -Ð -ðvMð vMð vMð vMð vMñ vMô vMð vMð vMð vMr'   