summaryrefslogblamecommitdiff
path: root/src/emap/pc.c
blob: 1a7ff2232a51647f8dd3cdbe5b235b2406e37e93 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                         
                                            
 

                            



                   
                        
                          


                          
                         
                       
                    
                   
 

                               
                      
                    
                      
                        





                                   
 
                 
                  
 

                                     


                                     
                                                           
                   





                                   
 


                                       
 

                        




                                                       

                              




                                                       




                                                 
     


             

                                      
                       
       



                                          
                                                                     


                                
                       
       
                 
                                                                 
              
                                                          

     



                                                    
 
                        
                      

                                  
 

               
                   

               











                                                                          





















                                                                          

     



                                                          




                                        
                             

                             
 
                           





                                        
                       
       
                               
                                                                    


                                  

                                                   
 


                                            
 
                        


               


                        
                      
 















                                                                         



                                                            




                                          
                               

                               

                           


                  
 

                                       
 
                        













                                              
                
 
 
 
                                  













                                                                        
 


                                                 
 






                                        


                                                                 
                          





                                                         
                                                        

                                                             


                                                                 
                                                        

                                                                             
         


                     


                  
 


                                                 
 














                                                                      
 



                                                               





















                                                                        



                                                         
 
                                          


                  



                                                           
 
                                           

                  
 
                                                

                 
                                







                                         
 







                                                                         







                                      
 

                                                 
                                            

                                                                       











                                                             


                                                    



                                                                       
                                         





















                                                             




                                                                       





                                     
 



                                                               
 
          

               

                          
                                                                         


                                                                                            
                                                                    

                                
                                                              







                                                               
                         

                            
                                                              
                                    
         
                                                                       







                                             


                                  
                          
 
                                                     

                                                             
 
                                         
                        
                                           


                   
                 
     





                                            



                                                  
 
                                       




                                                        

                          
                                                                            



                  

                                                                               
 
                                             





                   















                                                                 





                                         


                                                                            







                                                        
                          
                                                                                


                  
 


                                                        
 
                                         




                                      











                                                        

                                                     

                                                               
 
                                               










                                                                           
 


                                                          
 



                                               





















































                                                                                                                                


                                                         
 



                                               





















                                                             


                                                                 

                                                                  
 

                                         






























                                                                                 




                                              






                                        
// Copyright (c) Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// Copyright (c) 2014 - 2015 Evol developers

#include "common/hercules.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common/HPMi.h"
#include "common/memmgr.h"
#include "common/mmo.h"
#include "common/socket.h"
#include "common/strlib.h"
#include "common/timer.h"
#include "map/itemdb.h"
#include "map/npc.h"
#include "map/pc.h"

#include "plugins/HPMHooking.h"

#include "emap/clif.h"
#include "emap/pc.h"
#include "emap/send.h"
#include "emap/script.h"
#include "emap/data/itemd.h"
#include "emap/data/mapd.h"
#include "emap/data/session.h"
#include "emap/struct/itemdext.h"
#include "emap/struct/mapdext.h"
#include "emap/struct/sessionext.h"

int langScriptId;
int mountScriptId;

int epc_readparam_pre(TBL_PC **sdPtr,
                      int *type)
{
    if (*type == Const_ClientVersion)
    {
        struct SessionExt *data = session_get_bysd(*sdPtr);
        hookStop();
        if (!data)
            return 0;
        return data->clientVersion;
    }
    return 0;
}

int epc_setregistry_pre(TBL_PC **sdPtr,
                        int64 *reg,
                        int *val)
{
    TBL_PC *sd = *sdPtr;

    if (*reg == langScriptId)
    {
        struct SessionExt *data = session_get_bysd(sd);
        if (!data)
            return 0;
        data->language = *val;
    }
    else if (*reg == mountScriptId)
    {
        struct SessionExt *data = session_get_bysd(sd);
        if (!data)
            return 0;
        if (data->mount != *val)
        {
            data->mount = *val;
            send_pc_info(&sd->bl, &sd->bl, SELF);
        }
    }

    return 0;
}

#define equipPos(mask, field, lookf) \
    if (pos & (mask)) \
    { \
        if (id) \
            sd->status.field = id->look; \
        else \
            sd->status.field = 0; \
        eclif_changelook2(&sd->bl, lookf, sd->status.field, id, n); \
    }

#define equipPos2(mask, lookf) \
    if (pos & (mask)) \
    { \
        if (id) \
            eclif_changelook2(&sd->bl, lookf, id->look, id, n); \
        else \
            eclif_changelook2(&sd->bl, lookf, 0, id, n); \
    }

void epc_equipitem_pos_pre(TBL_PC **sdPtr,
                           struct item_data **idPtr,
                           int *nPtr,
                           int *posPtr)
{
    const int n = *nPtr;
    int pos = *posPtr;
    TBL_PC *sd = *sdPtr;
    struct item_data *id = *idPtr;

    hookStop();

    if (!id || !sd)
        return;

    if (pos & (EQP_HAND_R|EQP_SHADOW_WEAPON))
    {
        if(id)
        {
            sd->weapontype1 = id->look;
        }
        else
        {
            sd->weapontype1 = 0;
        }
        pc->calcweapontype(sd);
        eclif_changelook2(&sd->bl, LOOK_WEAPON, sd->status.weapon, id, n);
    }
    if (pos & (EQP_HAND_L|EQP_SHADOW_SHIELD))
    {
        if (id)
        {
            if(id->type == IT_WEAPON)
            {
                sd->status.shield = 0;
                sd->weapontype2 = id->look;
            }
            else if (id->type == IT_ARMOR)
            {
                sd->status.shield = id->look;
                sd->weapontype2 = 0;
            }
        }
        else
        {
            sd->status.shield = sd->weapontype2 = 0;
        }
        pc->calcweapontype(sd);
        eclif_changelook2(&sd->bl, LOOK_SHIELD, sd->status.shield, id, n);
    }

    equipPos(EQP_HEAD_LOW, head_bottom, LOOK_HEAD_BOTTOM);
    equipPos(EQP_HEAD_TOP, head_top, LOOK_HEAD_TOP);
    equipPos(EQP_HEAD_MID, head_mid, LOOK_HEAD_MID);
    equipPos(EQP_GARMENT, robe, LOOK_ROBE);
    equipPos2(EQP_SHOES, LOOK_SHOES);
    equipPos2(EQP_COSTUME_HEAD_TOP, 13);
    equipPos2(EQP_COSTUME_HEAD_MID, 14);
    equipPos2(EQP_COSTUME_HEAD_LOW, 15);
    equipPos2(EQP_COSTUME_GARMENT, 16);
    equipPos2(EQP_ARMOR, 17);
    equipPos2(EQP_ACC_R, 18);
    equipPos2(EQP_ACC_L, 19);

    //skipping SHADOW slots
}

#undef equipPos
#undef equipPos2

#define unequipPos(mask, field, lookf) \
    if (pos & (mask)) \
    { \
        sd->status.field = 0; \
        eclif_changelook2(&sd->bl, lookf, sd->status.field, 0, n); \
    }

#define unequipPos2(mask, lookf) \
    if (pos & (mask)) \
        eclif_changelook2(&sd->bl, lookf, 0, 0, n);

void epc_unequipitem_pos_pre(TBL_PC **sdPtr,
                             int *nPtr,
                             int *posPtr)
{
    TBL_PC *sd = *sdPtr;
    if (!sd)
        return;

    hookStop();

    const int n = *nPtr;
    int pos = *posPtr;

    if (pos & EQP_HAND_R)
    {
        sd->weapontype1 = 0;
        sd->status.weapon = sd->weapontype2;
        pc->calcweapontype(sd);
        eclif_changelook2(&sd->bl, LOOK_WEAPON, sd->status.weapon, 0, n);
        if (!battle->bc->dancing_weaponswitch_fix)
            status_change_end(&sd->bl, SC_DANCING, INVALID_TIMER);
    }
    if (pos & EQP_HAND_L)
    {
        sd->status.shield = sd->weapontype2 = 0;
        pc->calcweapontype(sd);
        eclif_changelook2(&sd->bl, LOOK_SHIELD, sd->status.shield, 0, n);
    }

    unequipPos(EQP_HEAD_LOW, head_bottom, LOOK_HEAD_BOTTOM);
    unequipPos(EQP_HEAD_TOP, head_top, LOOK_HEAD_TOP);
    unequipPos(EQP_HEAD_MID, head_mid, LOOK_HEAD_MID);
    unequipPos(EQP_GARMENT, robe, LOOK_ROBE);
    unequipPos2(EQP_SHOES, LOOK_SHOES);
    unequipPos2(EQP_COSTUME_HEAD_TOP, 13);
    unequipPos2(EQP_COSTUME_HEAD_MID, 14);
    unequipPos2(EQP_COSTUME_HEAD_LOW, 15);
    unequipPos2(EQP_COSTUME_GARMENT, 16);
    unequipPos2(EQP_ARMOR, 17);
    unequipPos2(EQP_ACC_R, 18);
    unequipPos2(EQP_ACC_L, 19);
    //skipping SHADOW slots
}

#undef unequipPos
#undef unequipPos2

bool epc_can_attack_pre(TBL_PC **sdPtr,
                        int *target_id)
{
    TBL_PC *sd = *sdPtr;
    if (!sd)
        return false;

    struct MapdExt *data = mapd_get(sd->bl.m);
    if (!data)
        return true;
    if (data->flag.nopve)
    {
        if (map->id2md(*target_id))
        {
            hookStop();
            return false;
        }
    }
    return true;
}


void epc_validate_levels_pre(void)
{
    int i;
    for (i = 0; i < 7; i++) {
        if (!pc->db_checkid(i)) continue;
        if (i == JOB_WEDDING || i == JOB_XMAS || i == JOB_SUMMER)
            continue; //Classes that do not need exp tables.
        int j = pc->class2idx(i);
        if (!pc->max_level[j][0])
            ShowWarning("Class %d does not has a base exp table.\n", i);
        if (!pc->max_level[j][1])
            ShowWarning("Class %d does not has a job exp table.\n", i);
    }
    hookStop();
}

int epc_isequip_post(int retVal,
                     struct map_session_data *sd,
                     int n)
{
    if (retVal)
    {
        if (!sd)
            return 0;

        if (n < 0 || n >= MAX_INVENTORY)
            return 0;

        struct ItemdExt *data = itemd_get(sd->inventory_data[n]);
        if (!data)
            return retVal;

        if (sd->battle_status.str < data->requiredStr ||
            sd->battle_status.agi < data->requiredAgi ||
            sd->battle_status.vit < data->requiredVit ||
            sd->battle_status.int_ < data->requiredInt ||
            sd->battle_status.dex < data->requiredDex ||
            sd->battle_status.luk < data->requiredLuk ||
            sd->battle_status.max_hp < data->requiredMaxHp ||
            sd->battle_status.max_sp < data->requiredMaxSp ||
            sd->battle_status.batk < data->requiredAtk ||
            sd->battle_status.matk_min < data->requiredMAtkMin ||
            sd->battle_status.matk_max < data->requiredMAtkMax ||
            sd->battle_status.def < data->requiredDef ||
            sd->battle_status.mdef < data->requiredMDef ||
            (data->requiredSkill && !pc->checkskill(sd, data->requiredSkill))
        )
        {
            return 0;
        }
    }
    return retVal;
}

int epc_useitem_post(int retVal,
                     struct map_session_data *sd,
                     int n)
{
    if (!sd)
        return retVal;

    if (n < 0 || n >= MAX_INVENTORY)
        return retVal;

    struct ItemdExt *data = itemd_get(sd->inventory_data[n]);
    if (!data)
        return retVal;

    const int effect = retVal ? data->useEffect : data->useFailEffect;
    if (effect != -1)
        clif->specialeffect(&sd->bl, effect, AREA);
    return retVal;
}

static void equippost_effect(struct map_session_data *const sd,
                             const int n,
                             const bool retVal,
                             const bool equip)
{
    if (!sd)
        return;

    if (n < 0 || n >= MAX_INVENTORY)
        return;

    struct ItemdExt *data = itemd_get(sd->inventory_data[n]);
    if (!data)
        return;

    int effect;
    if (equip)
        effect = retVal ? data->useEffect : data->useFailEffect;
    else
        effect = retVal ? data->unequipEffect : data->unequipFailEffect;

    if (effect != -1)
        clif->specialeffect(&sd->bl, effect, AREA);
    return;
}

int epc_equipitem_post(int retVal,
                       struct map_session_data *sd,
                       int n,
                       int data __attribute__ ((unused)))
{
    equippost_effect(sd, n, retVal, true);
    return retVal;
}

int epc_unequipitem_post(int retVal,
                         struct map_session_data *sd,
                         int n,
                         int data __attribute__ ((unused)))
{
    equippost_effect(sd, n, retVal, false);
    return retVal;
}

int epc_check_job_name_pre(const char **namePtr)
{
    int val = -1;
    const char *name = *namePtr;
    if (script->get_constant(name, &val))
    {
        hookStop();
        return val;
    }
    hookStop();
    return -1;
}

int epc_setnewpc_post(int retVal,
                      struct map_session_data *sd,
                      int account_id __attribute__ ((unused)),
                      int char_id __attribute__ ((unused)),
                      int login_id1 __attribute__ ((unused)),
                      unsigned int client_tick  __attribute__ ((unused)),
                      int sex __attribute__ ((unused)),
                      int fd __attribute__ ((unused)))
{
    if (sd)
    {
        sd->battle_status.speed = 150;
        sd->base_status.speed = 150;
    }
    return retVal;
}

int epc_additem_post(int retVal,
                     struct map_session_data *sd,
                     struct item *item_data,
                     int amount __attribute__ ((unused)),
                     e_log_pick_type log_type __attribute__ ((unused)))
{
    if (!retVal)
    {
        struct ItemdExt *data = itemd_get_by_item(item_data);
        if (data && data->charmItem)
            status_calc_pc(sd, SCO_NONE);
    }
    return retVal;
}

static bool calcPc = false;

int epc_delitem_pre(struct map_session_data **sdPtr,
                    int *nPtr,
                    int *amountPtr,
                    int *typePtr __attribute__ ((unused)),
                    short *reasonPtr __attribute__ ((unused)),
                    e_log_pick_type *log_type __attribute__ ((unused)))
{
    struct map_session_data *sd = *sdPtr;
    if (!sd)
        return 1;
    const int n = *nPtr;
    const int amount = *amountPtr;

    if (sd->status.inventory[n].nameid == 0 ||
        amount <= 0 ||
        sd->status.inventory[n].amount < amount ||
        sd->inventory_data[n] == NULL)
    {
        return 1;
    }

    struct ItemdExt *data = itemd_get(sd->inventory_data[n]);
    if (data && data->charmItem)
        calcPc = true;

    return 0;
}

int epc_delitem_post(int retVal,
                     struct map_session_data *sd,
                     int n __attribute__ ((unused)),
                     int amount __attribute__ ((unused)),
                     int type __attribute__ ((unused)),
                     short reason __attribute__ ((unused)),
                     e_log_pick_type log_type __attribute__ ((unused)))
{
    if (!retVal && calcPc && sd)
        status_calc_pc(sd, SCO_NONE);
    calcPc = false;
    return retVal;
}

bool epc_can_insert_card_into_post(bool retVal,
                                   struct map_session_data* sd,
                                   int idx_card,
                                   int idx_equip)
{
    int f;
    if (retVal)
    {
        if (!sd)
            return retVal;
        struct ItemdExt *data = itemd_get(sd->inventory_data[idx_equip]);
        if (!data || !data->allowedCards[0].id) // allow cards if AllowedCards list is empty
            return retVal;

        const int newCardId = sd->status.inventory[idx_card].nameid;
        int cardAmountLimit = 0;

        for (f = 0; f < 100 && data->allowedCards[f].id; f ++)
        {
            if (data->allowedCards[f].id == newCardId)
            {
                cardAmountLimit = data->allowedCards[f].amount;
                break;
            }
        }
        if (!cardAmountLimit)
            return false;

        int cardsAmount = 0;
        const int slots = sd->inventory_data[idx_equip]->slot;
        for (f = 0; f < slots; f ++)
        {
            const int cardId = sd->status.inventory[idx_equip].card[f];
            if (cardId == newCardId)
                cardsAmount ++;
        }
        return cardAmountLimit > cardsAmount;
    }
    return retVal;
}

// temporary inv index and item id
static int tempN = 0;
static int tempId = 0;
static int tempAmount = 0;

int epc_dropitem_pre(struct map_session_data **sdPtr,
                     int *nPtr,
                     int *amountPtr __attribute__ ((unused)))
{
    struct map_session_data *sd = *sdPtr;
    const int n = *nPtr;
    if (!sd || n < 0 || n >= MAX_INVENTORY)
    {
        tempN = 0;
        tempId = 0;
        return 0;
    }

    tempN = n;
    tempId = sd->status.inventory[n].nameid;
    return 1;
}

int epc_dropitem_post(int retVal,
                      struct map_session_data *sd,
                      int n,
                      int amount)
{
    if (retVal && n == tempN && tempId)
    {
        struct item_data *item = itemdb->search(tempId);
        if (!item)
            return retVal;
        struct ItemdExt *data = itemd_get(item);
        if (!data)
            return retVal;
        script_run_item_amount_script(sd, data->dropScript, tempId, amount);
    }
    return retVal;
}

int epc_takeitem_pre(struct map_session_data **sdPtr  __attribute__ ((unused)),
                     struct flooritem_data **fitemPtr)
{
    struct flooritem_data *fitem = *fitemPtr;
    if (!fitem)
    {
        tempN = 0;
        tempId = 0;
        return 0;
    }

    struct ItemdExt *data = itemd_get_by_item(&fitem->item_data);
    if (!data)
    {
        tempN = -1;
        tempId = fitem->item_data.nameid;
        tempAmount = fitem->item_data.amount;
        return 1;
    }

    if (!data->allowPickup)
    {
        hookStop();
        return 0;
    }

    tempN = -1;
    tempId = fitem->item_data.nameid;
    tempAmount = fitem->item_data.amount;
    return 1;
}

int epc_takeitem_post(int retVal,
                      struct map_session_data *sd,
                      struct flooritem_data *fitem __attribute__ ((unused)))
{
    if (retVal && tempN == -1 && tempId)
    {
        struct item_data *item = itemdb->search(tempId);
        if (!item)
            return retVal;
        struct ItemdExt *data = itemd_get(item);
        if (!data)
            return retVal;
        script_run_item_amount_script(sd, data->takeScript, tempId, tempAmount);
    }
    return retVal;
}

int epc_insert_card_pre(struct map_session_data **sdPtr,
                        int *idx_card,
                        int *idx_equip)
{
    struct map_session_data *sd = *sdPtr;
    if (!sd ||
        *idx_equip < 0 ||
        *idx_equip >= MAX_INVENTORY ||
        *idx_card < 0 ||
        *idx_card >= MAX_INVENTORY)
    {
        tempN = 0;
        tempId = 0;
        tempAmount = 0;
        return 0;
    }
    tempN = *idx_equip;
    tempId = sd->status.inventory[*idx_equip].nameid;
    tempAmount = sd->status.inventory[*idx_card].nameid;
    return 1;
}

int epc_insert_card_post(int retVal,
                         struct map_session_data* sd,
                         int idx_card __attribute__ ((unused)),
                         int idx_equip)
{
    if (retVal && idx_equip == tempN && tempId)
    {
        struct item_data *item = itemdb->search(tempId);
        if (!item)
            return retVal;
        struct ItemdExt *data = itemd_get(item);
        if (!data)
            return retVal;
        script_run_card_script(sd, data->insertScript, tempId, tempAmount);
    }
    return retVal;
}

bool epc_can_Adopt_pre(struct map_session_data **p1_sdPtr,
                       struct map_session_data **p2_sdPtr,
                       struct map_session_data **b_sdPtr)
{
    struct map_session_data *p1_sd = *p1_sdPtr;
    struct map_session_data *p2_sd = *p2_sdPtr;
    struct map_session_data *b_sd = *b_sdPtr;

    hookStop();

    if (!p1_sd || !p2_sd || !b_sd)
        return false;

    if (b_sd->status.father || b_sd->status.mother || b_sd->adopt_invite)
        return false; // already adopted baby / in adopt request

    if (!p1_sd->status.partner_id || !p1_sd->status.party_id || p1_sd->status.party_id != b_sd->status.party_id)
        return false; // You need to be married and in party with baby to adopt

    if (p1_sd->status.partner_id != p2_sd->status.char_id || p2_sd->status.partner_id != p1_sd->status.char_id)
        return false; // Not married, wrong married

    if (p2_sd->status.party_id != p1_sd->status.party_id)
        return false; // Both parents need to be in the same party

    // Parents need to have their ring equipped
//    if (!pc->isequipped(p1_sd, WEDDING_RING_M) && !pc->isequipped(p1_sd, WEDDING_RING_F))
//        return false;

//    if (!pc->isequipped(p2_sd, WEDDING_RING_M) && !pc->isequipped(p2_sd, WEDDING_RING_F))
//        return false;

    // Already adopted a baby
    if (p1_sd->status.child || p2_sd->status.child)
    {
        clif->adopt_reply(p1_sd, 0);
        hookStop();
        return false;
    }

    // Parents need at least lvl 70 to adopt
//    if (p1_sd->status.base_level < 70 || p2_sd->status.base_level < 70)
//    {
//        clif->adopt_reply(p1_sd, 1);
//        hookStop();
//        return false;
//    }

    if (b_sd->status.partner_id)
    {
        clif->adopt_reply(p1_sd, 2);
        hookStop();
        return false;
    }

//    if (!((b_sd->status.class_ >= JOB_NOVICE && b_sd->status.class_ <= JOB_THIEF) || b_sd->status.class_ == JOB_SUPER_NOVICE))
//        return false;

    hookStop();
    return true;
}

bool epc_adoption_pre(struct map_session_data **p1_sdPtr,
                      struct map_session_data **p2_sdPtr,
                      struct map_session_data **b_sdPtr)
{
    struct map_session_data *p1_sd = *p1_sdPtr;
    struct map_session_data *p2_sd = *p2_sdPtr;
    struct map_session_data *b_sd = *b_sdPtr;

    if (!pc->can_Adopt(p1_sd, p2_sd, b_sd))
    {
        hookStop();
        return false;
    }

    p1_sd->status.child = b_sd->status.char_id;
    p2_sd->status.child = b_sd->status.char_id;
    b_sd->status.father = p1_sd->status.char_id;
    b_sd->status.mother = p2_sd->status.char_id;

    // Baby Skills
    pc->skill(b_sd, WE_BABY, 1, SKILL_GRANT_PERMANENT);
    pc->skill(b_sd, WE_CALLPARENT, 1, SKILL_GRANT_PERMANENT);

    // Parents Skills
    pc->skill(p1_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT);
    pc->skill(p2_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT);

    hookStop();
    return true;
}

// copy from pc_process_chat_message
// exception only prevent call gm command if string start with ##
bool epc_process_chat_message_pre(struct map_session_data **sdPtr,
                                  const char **messagePtr)
{
    struct map_session_data *sd = *sdPtr;
    const char *message = *messagePtr;
    if (message && strlen(message) > 2 && message[0] == '#' && message[1] == '#')
    {
        // do nothing
    }
    else if (atcommand->exec(sd->fd, sd, message, true))
    {
        hookStop();
        return false;
    }

    if (!pc->can_talk(sd))
    {
        hookStop();
        return false;
    }

    if (battle->bc->min_chat_delay != 0)
    {
        if (DIFF_TICK(sd->cantalk_tick, timer->gettick()) > 0)
        {
            hookStop();
            return false;
        }
        sd->cantalk_tick = timer->gettick() + battle->bc->min_chat_delay;
    }

    pc->update_idle_time(sd, BCIDLE_CHAT);

    hookStop();
    return true;
}

int epc_dead_post(int retVal,
                  struct map_session_data *sd,
                  struct block_list *src)
{
    if (retVal > 0)
    {
        if (sd)
            send_pc_killed(sd->fd, src);
    }
    return retVal;
}