a
    Ši�]  ã                   @   s0  d Z ddlmZmZmZmZ ddlmZmZ ddlZddl	Z	ddl
Z
ddddd	d
ddœddddd	dddœddddd	dddœddddd	dddœddddddd dœd!d"dd#ddd$dœd%d&dddd'd(dœd)d*d+ddd,d-dœd.d/d0d'd1d0d2dœd3d4d5d6d1d7d8dœd9d:d#d;d1d<d=dœgZd>d?„ Zeeeeef  d@œdAdB„Zdheeeeeeef  dCœdDdE„ZeeeedFœdGdH„Zeeeeef  d@œdIdJ„Zeeeef d@œdKdL„Zeeef eeef dMœdNdO„Zeeeeef dPœdQdR„ZedSœdTdU„ZdVdW„ ZdXdY„ ZdZd[„ Zd\d]„ Zd^d_„ Zd`da„ Zdbdc„ Zddde„ Zdfdg„ Z dS )iz£
Daily Quest System

Generates and tracks daily quests for player engagement.
Players receive 3 quests daily (easy/medium/hard) that award diamonds on completion.
é    )ÚDictÚAnyÚListÚOptional)ÚdateÚdatetimeNÚtalk_to_characterszTalk to 3 different charactersé   é
   Úeasyé	   Úmessage)ÚtypeÚdescÚrequiredÚrewardÚ
difficultyÚenergyÚiconÚbuy_itemzBuy an item from the storeé   é   ZcartÚattend_classzAttend 2 classesé   é   é   ÚbookÚ	socializezSocialize with friendszperson.2Ú
work_hourszWork for 6 hoursé   Úmediumé   Ú	briefcaseÚ
go_on_datezGo on a dateé   ÚheartÚcomplete_activitieszComplete 5 activitiesé   zcheckmark.circleÚstudyzStudy for 4 hoursé   é   z	book.fillÚspend_energyzSpend 50 energy on activitiesé2   ÚhardZboltÚ
earn_moneyz	Earn $500iô  é   é(   Z
dollarsignÚincrease_affinityz$Increase affinity by 20 points totalé#   é-   z
heart.fillc                  Ã   s¨   ddl m}  z^tD ]>}| d|d |d |d |d |d |d	 |d
 fƒI dH  qt dttƒ› d�¡ W n8 ty¢ } z tjd|› �dd� W Y d}~n
d}~0 0 dS )z›
    Insert quest template definitions into database.
    Called during server startup.
    Uses async database pool for proper connection management.
    r   ©Úexecute_querya·  INSERT INTO daily_quest_templates
                   (quest_type, description, progress_required, diamond_reward, difficulty, energy_cost, icon_name)
                   VALUES (%s, %s, %s, %s, %s, %s, %s) AS new_quest
                   ON DUPLICATE KEY UPDATE
                   description=new_quest.description,
                   progress_required=new_quest.progress_required,
                   diamond_reward=new_quest.diamond_rewardr   r   r   r   r   r   r   NzInitialized z daily quest templatesz$Error initializing quest templates: T©Úexc_info)Údatabase_asyncr5   ÚQUEST_TEMPLATESÚloggingÚinfoÚlenÚ	ExceptionÚerror)r5   ÚquestÚe© rA   ú7/var/www/lichun.app/lichun/ws/retention/daily_quests.pyÚinitialize_quest_templates/   s    ÿørC   )Ú	player_idÚreturnc                 Ã   sR  ddl m}m}m} t ¡ }�zê|d| |fƒI dH }|rf|d dkrft d| › d�¡ t| ƒI dH W S |dƒI dH }|sˆt 	d¡ g W S d	d
„ |D ƒ}dd
„ |D ƒ}dd
„ |D ƒ}	|r¾|r¾|	sìt 
dt|ƒ› dt|ƒ› dt|	ƒ› �¡ g W S t |¡t |¡t |	¡g}
g }|ƒ 4 I dH š¸}| ¡ 4 I dH šx}|
D ]Z}| d| |d |f¡I dH  | |j|d |d d|d |d |d |d ddœ	¡ �q2W d  ƒI dH  �q¼1 I dH �s²0    Y  W d  ƒI dH  �qê1 I dH �sà0    Y  t dt|
ƒ› d| › �¡ |W S  t�yL } z*tj
d| › d|› �dd� g W  Y d}~S d}~0 0 dS ) zì
    Assign 3 random quests to player each day (1 easy, 1 medium, 1 hard).
    Called at midnight or on first login of the day.

    Args:
        player_id: The player's ID

    Returns:
        List of assigned quest dictionaries
    r   )Ú	fetch_oneÚfetch_dict_allÚget_connectionz]SELECT COUNT(*) as count FROM player_daily_quests WHERE player_id = %s AND assigned_date = %sNúPlayer z already has quests for todayz#SELECT * FROM daily_quest_templatesz$No quest templates found in databasec                 S   s   g | ]}|d  dkr|‘qS )r   r   rA   ©Ú.0ÚqrA   rA   rB   Ú
<listcomp>l   ó    z)generate_daily_quests.<locals>.<listcomp>c                 S   s   g | ]}|d  dkr|‘qS )r   r    rA   rJ   rA   rA   rB   rM   m   rN   c                 S   s   g | ]}|d  dkr|‘qS )r   r-   rA   rJ   rA   rA   rB   rM   n   rN   z Missing quest templates - easy: z
, medium: z, hard: z¶INSERT INTO player_daily_quests
                           (player_id, quest_template_id, assigned_date, progress, completed)
                           VALUES (%s, %s, %s, 0, FALSE)ÚidÚ
quest_typeÚdescriptionÚprogress_requiredÚdiamond_rewardr   Ú	icon_nameF©	rO   rP   rQ   ÚprogressrR   rS   r   rT   Ú	completedz	Assigned z daily quests to player z)Error generating daily quests for player ú: Tr6   )r8   rF   rG   rH   r   Útodayr:   r;   Úget_active_questsÚwarningr>   r<   ÚrandomÚchoiceÚcursorÚexecuteÚappendÚ	lastrowidr=   )rD   rF   rG   rH   rY   ÚresultZ
all_questsZeasy_questsZmedium_questsZhard_questsÚselectedZassigned_questsÚconnr^   r?   r@   rA   rA   rB   Úgenerate_daily_questsK   s`    þ

(ýü
÷hre   )rD   rP   ÚamountrE   c           	      Ã   st  ddl m}m} t ¡ }�z|d| ||fƒI dH }|s<W dS t|d | |d ƒ}|d||d fƒI dH  t d	| › d
|› d|› d|d › �¡ ||d krît| |d |d ƒI dH  |d |d |d ||d |d |d |d dddœ
W S |d |d |d ||d |d |d |d ddœ	W S W nH t	�yn } z.tj
d| › d|› d|› �dd� W Y d}~dS d}~0 0 dS )ad  
    Update progress on quest of given type.
    Called after relevant player actions.

    Args:
        player_id: The player's ID
        quest_type: Type of quest to update (e.g., 'talk_to_characters')
        amount: Amount to increment progress (default 1)

    Returns:
        Updated quest dict if quest exists and was updated, None otherwise
    r   )Úfetch_dict_oner5   ah  SELECT pq.*, qt.quest_type, qt.description, qt.progress_required, qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               AND pq.completed = FALSE AND qt.quest_type = %sNrV   rR   z:UPDATE player_daily_quests SET progress = %s WHERE id = %srO   rI   z quest 'z' progress: ú/rS   rP   rQ   r   rT   T)
rO   rP   rQ   rV   rR   rS   r   rT   rW   Zjust_completedFrU   z)Error updating quest progress for player z	, quest 'z': r6   )r8   rg   r5   r   rY   Úminr:   r;   Úcomplete_questr=   r>   )	rD   rP   rf   rg   r5   rY   r?   Znew_progressr@   rA   rA   rB   Úupdate_quest_progressœ   sR    ú
	
þ
&ö÷ rk   )rD   Úquest_idr   rE   c              
   Ã   s´   ddl m} z\|d|| fƒI dH  ddlm} || d|› �|ƒI dH  t d| › d|› d	|› d
�¡ W dS  ty® } z.tjd|› d| › d|› �dd� W Y d}~dS d}~0 0 dS )zà
    Complete quest and award diamonds.

    Args:
        player_id: The player's ID
        quest_id: The quest record ID
        reward: Diamond reward amount

    Returns:
        True if successful, False otherwise
    r   r4   z†UPDATE player_daily_quests
               SET completed = TRUE, completed_date = NOW()
               WHERE id = %s AND player_id = %sN©Úaward_diamondsZdaily_quest_rI   z completed quest z
, awarded ú	 diamondsTzError completing quest z for player rX   r6   F)r8   r5   Úmonetization.diamond_economyrn   r:   r;   r=   r>   )rD   rl   r   r5   rn   r@   rA   rA   rB   rj   æ   s    ü
 rj   c                 Ã   sà   ddl m} t ¡ }z„|d| |fƒI dH }g }|D ]`}| |d |d |d |d |d	 |d
 |d |d t|d ƒ|d rˆ|d  ¡ nddœ
¡ q2|W S  tyÚ } z*tj	d| › d|› �dd� g W  Y d}~S d}~0 0 dS )z‹
    Get player's current daily quests.

    Args:
        player_id: The player's ID

    Returns:
        List of quest dictionaries
    r   )rG   a¯  SELECT pq.id, pq.progress, pq.completed, pq.completed_date,
                      qt.quest_type, qt.description, qt.progress_required,
                      qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               ORDER BY qt.difficulty DESCNrO   rP   rQ   rV   rR   rS   r   rT   rW   Úcompleted_date)
rO   rP   rQ   rV   rR   rS   r   rT   rW   rq   z'Error getting active quests for player rX   Tr6   )
r8   rG   r   rY   r`   ÚboolÚ	isoformatr=   r:   r>   )rD   rG   rY   Úquestsrb   r?   r@   rA   rA   rB   rZ   
  s2    
ø

ö
rZ   c              
   C   s^  d}d}�z0z¢t ƒ }|jdd�}| d| f¡ | ¡ }| d| f¡ | ¡ }|rX|d nd|rr|d rrt|d ƒnd|r€|d	 ndd
d„ |D ƒdœW W |r¢| ¡  |r®| ¡  S  t�y } zRtj	d| › d|› �dd� dddi dœW  Y d}~W |�r| ¡  |�r| ¡  S d}~0 0 W |�r,| ¡  |�rZ| ¡  n|�rJ| ¡  |�rX| ¡  0 dS )z’
    Get player's quest completion statistics.

    Args:
        player_id: The player's ID

    Returns:
        Dict with quest statistics
    NT©Ú
dictionaryaw  SELECT
                   COUNT(*) as total_completed,
                   SUM(qt.diamond_reward) as total_diamonds_earned,
                   COUNT(DISTINCT pq.assigned_date) as days_completed
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUEa$  SELECT qt.difficulty,
                      COUNT(*) as completed_count
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUE
               GROUP BY qt.difficultyÚtotal_completedr   Útotal_diamonds_earnedZdays_completedc                 S   s   i | ]}|d  |d “qS )r   Zcompleted_countrA   )rK   ÚitemrA   rA   rB   Ú
<dictcomp>h  rN   z(get_quest_statistics.<locals>.<dictcomp>)rw   rx   Zdays_with_questsÚby_difficultyz*Error getting quest statistics for player rX   r6   )
Úget_database_connectionr^   r_   ÚfetchoneÚfetchallÚintÚcloser=   r:   r>   )rD   rd   r^   Ústatsr{   r@   rA   rA   rB   Úget_quest_statistics:  sZ    
ø
ù	üìüõ
ýr‚   )r?   rE   c                 C   sà   ddl m } ddddœ}dddddddddddd	œ}|  d
d¡}| || |  dd¡d¡¡}|  dd¡ot|  d¡du}t| d ƒd| d v rž| d  d¡d n| d dd… | d | ¡ | d dddœ| d | d | d |dœ	S )zÈ
    Format quest data to match iOS DailyQuest model expectations.

    Args:
        quest: Raw quest dictionary from database

    Returns:
        Formatted quest dictionary matching iOS model
    r   )r   Ú
activitiesÚcareerZwealth)r   r    r-   ÚsocialÚ	education)r   r   r#   r1   r   r.   r   r   r(   r&   r+   rP   Ú r   r   rW   Frq   NrO   z - rQ   r/   rS   ©Údiamondsr   ÚmoneyrV   rR   )	rO   ÚnamerQ   Úcategoryr   rV   ÚtargetrW   Úclaimed)r   ÚgetÚstrÚsplitÚ
capitalize)r?   r   Zcategory_mapZtype_to_categoryrP   rŒ   rŽ   rA   rA   rB   Úformat_quest_for_client{  sB    ýõ
,ýór“   )rD   rl   rE   c              
   C   s(  d}d}�zú�zXt ƒ }|jdd�}| dt|ƒ| f¡ | ¡ }|sjddddœW W |r\| ¡  |rh| ¡  S |d sšdd	ddœW W |rŒ| ¡  |r˜| ¡  S |d
 rÊddddœW W |r¼| ¡  |rÈ| ¡  S | dt|ƒ| f¡ ddlm} || |d dƒ | ¡  t	 
d| › d|› d|d › d�¡ dd|d › d�|d dddœdœW W |�rX| ¡  |�rf| ¡  S  t�yä } zd|�r†| ¡  t	jd| › d|› d|› �dd� ddddœW  Y d}~W |�rÌ| ¡  |�rÚ| ¡  S d}~0 0 W |�rö| ¡  |�r$| ¡  n|�r| ¡  |�r"| ¡  0 dS )zÈ
    Claim reward for a completed quest.

    Args:
        player_id: The player's ID
        quest_id: The quest ID to claim

    Returns:
        Dictionary with success status and reward info
    NTru   zÌSELECT pq.*, qt.diamond_reward
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.id = %s AND pq.player_id = %sFzQuest not found)Úsuccessr   r   rW   zQuest not completed yetrq   zReward already claimedztUPDATE player_daily_quests
               SET completed_date = NOW()
               WHERE id = %s AND player_id = %sr   rm   rS   zDaily Quest RewardrI   z claimed quest z	 reward: ro   zClaimed z
 diamonds!rˆ   z'Error claiming quest reward for player z, quest rX   r6   zFailed to claim reward)r|   r^   r_   r   r}   r€   rp   rn   Úcommitr:   r;   r=   Úrollbackr>   )rD   rl   rd   r^   r?   rn   r@   rA   rA   rB   Úclaim_quest_rewardµ  s¢    
ûý:Ãý3Êý+Ò
ü"ýýè
 ýö
ýr—   )rD   c              
   C   sø   z´ddl m}m }m} t| ƒ}|s*t| ƒ}|r<dd„ |D ƒng }| ¡ }||dd� }| ||j ¡ ¡ 	¡ }	| ||j ¡ ¡ 	¡ }
|| d||	|
dœƒ t
 d	| › d
t|ƒ› d�¡ W n> tyò } z&t
jd| › d
|› �dd� W Y d}~n
d}~0 0 dS )zð
    WebSocket handler for daily quest check.
    Called when player connects to server or at midnight.

    Args:
        player_id: The player's ID
        send_to_client: Function to send messages to client (player_id, message_dict)
    r   )r   r   Ú	timedeltac                 S   s   g | ]}t |ƒ‘qS rA   )r“   rJ   rA   rA   rB   rM   &  rN   z,handle_daily_quest_check.<locals>.<listcomp>r   )ÚdaysZdailyQuestsStatus)r   rt   ZlastResetDateÚnextResetDatez"Sent daily quests state to player rX   z questsz,Error handling daily quest check for player Tr6   N)r   r   r˜   rZ   re   rY   Úcombineri   Útimers   r:   r;   r<   r=   r>   )rD   Úsend_to_clientr   r   r˜   Zactive_questsZformatted_questsrY   ÚtomorrowZ
last_resetÚ
next_resetr@   rA   rA   rB   Úhandle_daily_quest_check  s&    	ü r    c                   C   s   dS )aI  
    Example: Update quest when player talks to a character

    Integration point: ws/conversationEvents.py or wherever conversations are handled

    Add this after a successful conversation:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'talk_to_characters', 1)
    NrA   rA   rA   rA   rB   Ú example_integration_conversationC  s    
r¡   c                   C   s   dS )a¯  
    Example: Update quest when player works

    Integration point: ws/intradayActivity.py or work event handlers

    Add this after work hours are completed:
        from retention.daily_quests import update_quest_progress

        # Track work hours
        update_quest_progress(player_id, 'work_hours', hours_worked)

        # Track activity completion
        update_quest_progress(player_id, 'complete_activities', 1)
    NrA   rA   rA   rA   rB   Úexample_integration_workP  s    r¢   c                   C   s   dS )a_  
    Example: Update quest when player studies or attends class

    Integration point: Education/school event handlers

    Add this after attending class:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'attend_class', 1)
        update_quest_progress(player_id, 'study', hours_studied)
    NrA   rA   rA   rA   rB   Úexample_integration_studyb  s    r£   c                   C   s   dS )a  
    Example: Update quest when player makes a purchase

    Integration point: Store/purchase handlers

    Add this after successful purchase:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'buy_item', 1)
    NrA   rA   rA   rA   rB   Úexample_integration_shoppingp  s    
r¤   c                   C   s   dS )a  
    Example: Update quest when player goes on a date

    Integration point: Dating/relationship event handlers

    Add this when date activity starts:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'go_on_date', 1)
    NrA   rA   rA   rA   rB   Úexample_integration_dating}  s    
r¥   c                   C   s   dS )a!  
    Example: Update quest when player spends energy

    Integration point: Activity/energy deduction handlers

    Add this when energy is spent:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'spend_energy', energy_amount)
    NrA   rA   rA   rA   rB   Úexample_integration_energyŠ  s    
r¦   c                   C   s   dS )a  
    Example: Update quest when player earns money

    Integration point: Work/income event handlers

    Add this when money is earned:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'earn_money', amount_earned)
    NrA   rA   rA   rA   rB   Úexample_integration_money—  s    
r§   c                   C   s   dS )a.  
    Example: Update quest when affinity increases

    Integration point: Relationship/affinity update handlers

    Add this when affinity increases:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'increase_affinity', affinity_increase)
    NrA   rA   rA   rA   rB   Úexample_integration_affinity¤  s    
r¨   c                   C   s   dS )a  
    Example: Update quest when player socializes

    Integration point: Social activity handlers

    Add this when social activity completes:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'socialize', 1)
    NrA   rA   rA   rA   rB   Úexample_integration_socialize±  s    
r©   )r   )!Ú__doc__Útypingr   r   r   r   r   r   r:   r\   Úasyncior9   rC   r   r�   re   rk   rr   rj   rZ   r‚   r“   r—   r    r¡   r¢   r£   r¤   r¥   r¦   r§   r¨   r©   rA   rA   rA   rB   Ú<module>   sr   ÿÿÿÿÿÿÿÿÿÿÿæQ"J$0A :]1