summaryrefslogblamecommitdiff
path: root/src/emap/script.c
blob: 9e37f7ac82148ff96b8a1dd2bd4cf7d5c0946699 (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/malloc.h"
#include "common/mmo.h"
#include "common/utils.h"
#include "common/socket.h"
#include "common/strlib.h"
#include "common/timer.h"
#include "map/chat.h"
#include "map/chrif.h"
#include "map/clif.h"
#include "map/npc.h"
#include "map/pc.h"
#include "map/script.h"
#include "map/quest.h"
#include "map/unit.h"

#include "emap/script.h"
#include "emap/clif.h"
#include "emap/lang.h"
#include "emap/scriptdefines.h"
#include "emap/send.h"
#include "emap/data/bgd.h"
#include "emap/data/mapd.h"
#include "emap/data/npcd.h"
#include "emap/data/session.h"
#include "emap/struct/bgdext.h"
#include "emap/struct/mapdext.h"
#include "emap/struct/npcdext.h"
#include "emap/struct/sessionext.h"
#include "emap/utils/formatutils.h"

#define getExt() \
    TBL_NPC *nd = map->id2nd(st->oid); \
    if (!nd) \
        return; \
    struct NpcdExt *ext = npcd_get(nd); \
    if (!ext) \
        return

#define getExtRet(r) \
    TBL_NPC *nd = map->id2nd(st->oid); \
    if (!nd) \
        return r; \
    struct NpcdExt *ext = npcd_get(nd); \
    if (!ext) \
        return r

void eset_reg_npcscope_num(struct script_state* st, struct reg_db *n, int64 *num, const char* name, int *val)
{
    if (!strcmp(name, ".lang"))
    {
        getExt();
        ext->language = *val;
        hookStop();
    }
    else if (!strcmp(name, ".sex"))
    {
        getND();
        clif->clearunit_area(&nd->bl, CLR_OUTSIGHT);
        nd->vd->sex = *val;
        clif->spawn(&nd->bl);
        hookStop();
    }
    else if (!strcmp(name, ".distance"))
    {
        getND();
        nd->area_size = *val;
        hookStop();
    }
    else if (!strcmp(name, ".dir"))
    {
        getND();
        int newdir = *val;

        if (newdir < 0)
            newdir = 0;
        else if (newdir > 7)
            newdir = 7;

        nd->dir = newdir;
        npc->enable(nd->name, 1);
        hookStop();
    }
    else if (!strcmp(name, ".x"))
    {
        ShowWarning("you cant assign '.x'\n");
        script->reportsrc(st);
        hookStop();
    }
    else if (!strcmp(name, ".y"))
    {
        ShowWarning("you cant assign '.y'.\n");
        script->reportsrc(st);
        hookStop();
    }
    else if (!strcmp(name, ".class"))
    {
        getND();
        int class_ = *val;
        if (nd->class_ != class_)
            npc->setclass(nd, class_);
        hookStop();
    }
    else if (!strcmp(name, ".speed"))
    {
        getND();
        unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit
        nd->speed = *val;
        nd->ud->state.speed_changed = 1;
        hookStop();
    }
    else if (!strcmp(name, ".chat"))
    {
        ShowWarning("you cant assign '.chat'.\n");
        script->reportsrc(st);
        hookStop();
    }
    else if (!strcmp(name, ".sit"))
    {
        getND();
        nd->vd->dead_sit = (*val) ? 2 : 0;
        clif->sitting(&nd->bl);
        hookStop();
    }
    else if (!strcmp(name, ".stand"))
    {
        getND();
        nd->vd->dead_sit = (*val) ? 0 : 2;
        clif->sitting(&nd->bl);
        hookStop();
    }
    else if (!strcmp(name, ".walkmask"))
    {
        getExt();
        ext->walkMask = *val;
        hookStop();
    }
}

int eget_val_npcscope_num(struct script_state* st, struct reg_db *n, struct script_data* data)
{
    const char *name = reference_getname(data);
    if (!strcmp(name, ".lang"))
    {
        getExtRet(0);
        hookStop();
        return ext->language;
    }
    else if (!strcmp(name, ".sex"))
    {
        getNDReturn(0);
        hookStop();
        return nd->vd->sex;
    }
    else if (!strcmp(name, ".distance"))
    {
        getNDReturn(0);
        hookStop();
        return nd->area_size;
    }
    else if (!strcmp(name, ".dir"))
    {
        getNDReturn(0);
        hookStop();
        return nd->dir;
    }
    else if (!strcmp(name, ".x"))
    {
        getNDReturn(0);
        hookStop();
        return nd->bl.x;
    }
    else if (!strcmp(name, ".y"))
    {
        getNDReturn(0);
        hookStop();
        return nd->bl.y;
    }
    else if (!strcmp(name, ".class"))
    {
        getNDReturn(0);
        hookStop();
        return nd->class_;
    }
    else if (!strcmp(name, ".speed"))
    {
        getNDReturn(0);
        hookStop();
        return nd->speed;
    }
    else if (!strcmp(name, ".chat"))
    {
        getNDReturn(0);
        hookStop();
        return nd->chat_id;
    }
    else if (!strcmp(name, ".sit"))
    {
        getNDReturn(0);
        hookStop();
        return nd->vd->dead_sit == 2 ? 1 : 0;
    }
    else if (!strcmp(name, ".stand"))
    {
        getNDReturn(0);
        hookStop();
        return nd->vd->dead_sit == 0 ? 1 : 0;
    }
    else if (!strcmp(name, ".walkmask"))
    {
        getExtRet(0);
        hookStop();
        return ext->walkMask;
    }
    return 0;
}

void eset_reg_npcscope_str(struct script_state* st, struct reg_db *n, int64 *num, const char* name, const char *str)
{
    if (!strcmp(name, ".map$"))
    {
        ShowWarning("you cant assign '.map$'.\n");
        script->reportsrc(st);
        hookStop();
    }
    else if (!strcmp(name, ".name$"))
    {
        getND();
        npc->setdisplayname(nd, str);
//      not working because cant sent brodcast with translated npc name. need add for_each function for this.
//        clif->clearunit_area(&nd->bl, CLR_OUTSIGHT);
//        safestrncpy(nd->name, str, sizeof(nd->name));
//        clif->spawn(&nd->bl);
        hookStop();
    }
    else if (!strcmp(name, ".extname$"))
    {
        ShowWarning("you cant assign '.extname$'.\n");
        script->reportsrc(st);
        hookStop();
    }
}

char *eget_val_npcscope_str(struct script_state* st, struct reg_db *n, struct script_data* data)
{
    const char *name = reference_getname(data);
    if (!strcmp(name, ".map$"))
    {
        getNDReturn(0);
        hookStop();
        return map->list[nd->bl.m].name;
    }
    else if (!strcmp(name, ".name$"))
    {
        getNDReturn(0);
        hookStop();
        return nd->name;
    }
    else if (!strcmp(name, ".extname$"))
    {
        getNDReturn(0);
        hookStop();
        return nd->exname;
    }
    return NULL;
}

uint32 MakeDWord(uint16 word0, uint16 word1)
{
    return ((uint32)(word0)) | ((uint32)(word1 << 0x10));
}

BUILDIN(l)
{
    format_sub(st, 1);
    return true;
}

BUILDIN(lg)
{
    format_sub(st, 2);
    return true;
}

BUILDIN(setCamNpc)
{
    getSD();
    TBL_NPC *nd = NULL;

    int x = 0;
    int y = 0;

    if (script_hasdata(st, 2))
    {
        nd = npc->name2id (script_getstr(st, 2));
    }
    else
    {
        if (!st->oid)
        {
            ShowWarning("npc not attached\n");
            script->reportsrc(st);
            return false;
        }

        nd = (TBL_NPC *) map->id2bl (st->oid);
    }
    if (!nd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }
    if (sd->bl.m != nd->bl.m)
    {
        ShowWarning("npc and player located in other maps\n");
        script->reportsrc(st);
        return false;
    }

    if (script_hasdata(st, 3) && script_hasdata(st, 4))
    {
        x = script_getnum(st, 3);
        y = script_getnum(st, 4);
    }

    send_npccommand2(sd, st->oid, 2, nd->bl.id, x, y);

    return true;
}

BUILDIN(setCam)
{
    send_npccommand2(script->rid2sd (st), st->oid, 2, 0,
        script_getnum(st, 2), script_getnum(st, 3));

    return true;
}

BUILDIN(moveCam)
{
    send_npccommand2(script->rid2sd (st), st->oid, 4, 0,
        script_getnum(st, 2), script_getnum(st, 3));

    return true;
}

BUILDIN(restoreCam)
{
    getSD();
    send_npccommand(sd, st->oid, 3);
    return true;
}

BUILDIN(npcTalk3)
{
    const char *str;
    char *msg;
    TBL_NPC *nd = NULL;

    getSD();

    if (script_hasdata(st, 3))
    {
        nd = npc->name2id (script_getstr(st, 2));
        str = script_getstr(st, 3);
    }
    else
    {
        nd = (TBL_NPC *) map->id2bl (st->oid);
        str = script_getstr(st, 2);
    }

    if (!nd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }

    if (!str)
    {
        ShowWarning("error in string\n");
        script->reportsrc(st);
        return false;
    }

    if (sd)
        msg = (char*)lang_pctrans (nd->name, sd);
    else
        msg = nd->name;

    if (!msg)
    {
        ShowWarning("error in string\n");
        script->reportsrc(st);
        return false;
    }
    if (strlen(str) + strlen(msg) > 450)
    {
        ShowWarning("text message too big\n");
        script->reportsrc(st);
        return false;
    }

    if (nd)
    {
        char message[500];
        char name[500];
        strcpy (name, msg);
        strtok(name, "#");
        strcpy (message, name);
        strcat (message, " : ");
        strcat (message, str);
        send_local_message (sd->fd, &(nd->bl), message);
    }

    return true;
}

BUILDIN(closeDialog)
{
    getSD();
    send_npccommand(sd, st->oid, 5);
    return true;
}

BUILDIN(shop)
{
    getSD();
    TBL_NPC *nd = npc->name2id (script_getstr(st, 2));
    if (!nd)
    {
        ShowWarning("shop npc not found\n");
        script->reportsrc(st);
        return false;
    }

    st->state = sd->state.dialog == 1 ? CLOSE : END;
    clif->scriptclose(sd, st->oid);

    clif->npcbuysell (sd, nd->bl.id);
    return true;
}

BUILDIN(getItemLink)
{
    struct item_data *i_data;
    char *item_name;
    int  item_id = 0;

    if (script_isstringtype(st, 2))
    {
        i_data = itemdb->search_name (script_getstr(st, 2));
    }
    else
    {
        item_id = script_getnum (st, 2);
        i_data = itemdb->search (item_id);
    }

    item_name = (char *) aCalloc (100, sizeof (char));
    TBL_PC *sd = script->rid2sd(st);

    if (sd)
    {
        int version = 0;
        struct SessionExt *data = session_get_bysd(sd);
        if (data)
            version = data->clientVersion;

        if (i_data && version >= 7)
            sprintf(item_name, "[@@%u|@@]", (unsigned)i_data->nameid);
        else if (i_data)
            sprintf(item_name, "[@@%u|%s@@]", (unsigned)i_data->nameid, lang_pctrans (i_data->jname, sd));
        else if (item_id > 0)
            sprintf(item_name, "[@@%u|Unknown Item@@]", (unsigned)item_id);
        else
            sprintf(item_name, "[Unknown Item]");
    }
    else
    {
        if (i_data)
            sprintf(item_name, "[%s]", lang_pctrans (i_data->jname, sd));
        else
            sprintf(item_name, "[Unknown Item]");
    }

    script_pushstr(st, item_name);

    return true;
}

BUILDIN(requestLang)
{
    getSD();
    struct script_data* data;
    int64 uid;
    const char* name;

    data = script_getdata(st, 2);
    if (!data_isreference(data))
    {
        ShowError("script:requestlang: not a variable\n");
        script->reportsrc(st);
        st->state = END;
        return false;
    }
    uid = reference_getuid(data);
    name = reference_getname(data);

    if (is_string_variable(name))
    {
        ShowError("script:requestlang: not a variable\n");
        script->reportsrc(st);
        return false;
    }

    if (!sd->state.menu_or_input)
    {
        // first invocation, display npc input box
        sd->state.menu_or_input = 1;
        st->state = RERUNLINE;

        // send lang request
        send_npccommand(sd, st->oid, 0);
        clif->scriptinputstr(sd, st->oid);
    }
    else
    {
        // take received text/value and store it in the designated variable
        sd->state.menu_or_input = 0;

        int lng = -1;
        if (*sd->npc_str)
            lng = lang_getId(sd->npc_str);
        script->set_reg(st, sd, uid, name, (void*)h64BPTRSIZE(lng), script_getref(st,2));
        st->state = RUN;
    }
    return true;
}

BUILDIN(requestItem)
{
    getSD();
    struct script_data* data;
    int64 uid;
    const char* name;

    data = script_getdata(st, 2);
    if (!data_isreference(data))
    {
        ShowError("script:requestitem: not a variable\n");
        script->reportsrc(st);
        st->state = END;
        return false;
    }
    uid = reference_getuid(data);
    name = reference_getname(data);

    if (is_string_variable(name))
    {
        ShowError("script:requestitem: not a variable\n");
        script->reportsrc(st);
        return false;
    }

    if (!sd->state.menu_or_input)
    {
        // first invocation, display npc input box
        sd->state.menu_or_input = 1;
        st->state = RERUNLINE;

        // send item request
        send_npccommand(sd, st->oid, 10);
    }
    else
    {
        // take received text/value and store it in the designated variable
        sd->state.menu_or_input = 0;

        int item = 0;

        if (!sd->npc_str)
        {
            ShowWarning("npc string not found\n");
            script->reportsrc(st);
            return false;
        }

        if (sscanf (sd->npc_str, "%5d", &item) < 1)
        {
            ShowWarning("input data is not item id\n");
            script->reportsrc(st);
            return false;
        }

        script->set_reg(st, sd, uid, name, (void*)h64BPTRSIZE(item), script_getref(st,2));
        st->state = RUN;
    }
    return true;
}

BUILDIN(requestItems)
{
    getSD();
    struct script_data* data;
    int64 uid;
    const char* name;

    data = script_getdata(st, 2);
    if (!data_isreference(data))
    {
        ShowError("script:requestitem: not a variable\n");
        script->reportsrc(st);
        st->state = END;
        return false;
    }
    uid = reference_getuid(data);
    name = reference_getname(data);

    if (!is_string_variable(name))
    {
        ShowWarning("parameter is not variable\n");
        script->reportsrc(st);
        return false;
    }

    int count = 1;

    if (script_hasdata(st, 3))
    {
        count = script_getnum(st, 3);
        if (count < 0)
            count = 1;
    }

    if (!sd->state.menu_or_input)
    {
        // first invocation, display npc input box
        sd->state.menu_or_input = 1;
        st->state = RERUNLINE;

        // send item request with limit count
        send_npccommand2(sd, st->oid, 10, count, 0, 0);
    }
    else
    {
        // take received text/value and store it in the designated variable
        sd->state.menu_or_input = 0;

        if (!sd->npc_str)
        {
            ShowWarning("npc string not found\n");
            script->reportsrc(st);
            return false;
        }

        script->set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st, 2));
        st->state = RUN;
    }
    return true;
}

BUILDIN(requestItemIndex)
{
    getSessionData(client);
    struct script_data* data;
    int64 uid;
    const char* name;

    data = script_getdata(st, 2);
    if (!data_isreference(data))
    {
        ShowError("script:requestitem: not a variable\n");
        script->reportsrc(st);
        st->state = END;
        return false;
    }
    uid = reference_getuid(data);
    name = reference_getname(data);

    if (is_string_variable(name))
    {
        ShowError("script:requestitemindex: not a variable\n");
        script->reportsrc(st);
        return false;
    }

    if (!sd->state.menu_or_input)
    {
        // first invocation, display npc input box
        sd->state.menu_or_input = 1;
        st->state = RERUNLINE;

        // send item request
        if (client || client->clientVersion >= 11)
            send_npccommand(sd, st->oid, 11);
        else
            clif->scriptinputstr(sd, st->oid);
    }
    else
    {
        // take received text/value and store it in the designated variable
        sd->state.menu_or_input = 0;

        int item = 0;

        if (!sd->npc_str)
        {
            ShowWarning("npc string not found\n");
            script->reportsrc(st);
            return false;
        }

        if (sscanf (sd->npc_str, "%5d", &item) < 1)
        {
            ShowWarning("input data is not item id\n");
            script->reportsrc(st);
            return false;
        }

        script->set_reg(st, sd, uid, name, (void*)h64BPTRSIZE(item), script_getref(st,2));
        st->state = RUN;
    }
    return true;
}

BUILDIN(requestItemsIndex)
{
    getSessionData(client);
    struct script_data* data;
    int64 uid;
    const char* name;

    data = script_getdata(st, 2);
    if (!data_isreference(data))
    {
        ShowError("script:requestitem: not a variable\n");
        script->reportsrc(st);
        st->state = END;
        return false;
    }
    uid = reference_getuid(data);
    name = reference_getname(data);

    if (!is_string_variable(name))
    {
        ShowWarning("parameter is not variable\n");
        script->reportsrc(st);
        return false;
    }

    int count = 1;

    if (script_hasdata(st, 3))
    {
        count = script_getnum(st, 3);
        if (count < 0)
            count = 1;
    }

    if (!sd->state.menu_or_input)
    {
        // first invocation, display npc input box
        sd->state.menu_or_input = 1;
        st->state = RERUNLINE;

        // send item request with limit count
        if (client || client->clientVersion >= 11)
            send_npccommand2(sd, st->oid, 11, count, 0, 0);
        else
            clif->scriptinputstr(sd, st->oid);
    }
    else
    {
        // take received text/value and store it in the designated variable
        sd->state.menu_or_input = 0;

        if (!sd->npc_str)
        {
            ShowWarning("npc string not found\n");
            script->reportsrc(st);
            return false;
        }

        script->set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st, 2));
        st->state = RUN;
    }
    return true;
}

BUILDIN(setq)
{
    int i;
    getSD();

    int quest_id = script_getnum(st, 2);
    int quest_value = script_getnum(st, 3);

    if (quest->check(sd, quest_id, HAVEQUEST) < 0)
        quest->add(sd, quest_id);
    ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
    if (i == sd->num_quests)
    {
        ShowError("Quest with id=%d not found\n", quest_id);
        script->reportsrc(st);
        return false;
    }

    sd->quest_log[i].count[0] = quest_value;
    sd->save_quest = true;
    if (map->save_settings & 64)
        chrif->save(sd,0);

    eclif_quest_add(sd, &sd->quest_log[i]);
    return true;
}

BUILDIN(getq)
{
    int i;
    getSDReturn(0);

    int quest_id = script_getnum(st, 2);
    if (quest->check(sd, quest_id, HAVEQUEST) < 0)
    {
        script_pushint(st, 0);
        return true;
    }
    ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
    if (i == sd->num_quests)
    {
        script_pushint(st, 0);
        return true;
    }
    script_pushint(st, sd->quest_log[i].count[0]);
    return true;
}

BUILDIN(setNpcDir)
{
    int newdir;
    TBL_NPC *nd = 0;

    if (script_hasdata(st, 3))
    {
        nd = npc->name2id (script_getstr(st, 2));
        newdir = script_getnum(st, 3);
    }
    else if (script_hasdata(st, 2))
    {
        if (!st->oid)
        {
            ShowWarning("npc not found\n");
            script->reportsrc(st);
            return false;
        }

        nd = (TBL_NPC *) map->id2bl (st->oid);
        newdir = script_getnum(st, 2);
    }
    if (!nd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }

    if (newdir < 0)
        newdir = 0;
    else if (newdir > 7)
        newdir = 7;

    nd->dir = newdir;
    npc->enable (nd->name, 1);

    return true;
}

BUILDIN(rif)
{
    const char *str = 0;
    if (script_getnum(st, 2))
    {
        str = script_getstr(st, 3);
        if (str)
            script_pushstr(st, aStrdup(str));
        else
            script_pushconststr(st, (char *)"");
    }
    else if (script_hasdata(st, 4))
    {
        str = script_getstr(st, 4);
        if (str)
            script_pushstr(st, aStrdup(str));
        else
            script_pushconststr(st, (char *)"");
    }
    else
    {
        script_pushconststr(st, (char *)"");
    }

    return true;
}

BUILDIN(countItemColor)
{
    int nameid, i;
    int count = 0;
    struct item_data* id = NULL;
    getSD();

    if (script_isstringtype(st, 2))
    {
        // item name
        id = itemdb->search_name(script_getstr(st, 2));
    }
    else
    {
        // item id
        id = itemdb->exists(script_getnum(st, 2));
    }

    if (id == NULL)
    {
        ShowError("buildin_countitem: Invalid item '%s'.\n", script_getstr(st,2));  // returns string, regardless of what it was
        script->reportsrc(st);
        script_pushint(st,0);
        return false;
    }

    nameid = id->nameid;

    for(i = 0; i < MAX_INVENTORY; i++)
    {
        if(sd->status.inventory[i].nameid == nameid)
            count += sd->status.inventory[i].amount;
    }

    script_pushint(st, count);
    return true;
}

BUILDIN(miscEffect)
{
    int type = script_getnum(st, 2);
    struct block_list *bl = NULL;

    if (script_hasdata(st, 3))
    {
        if (script_isstring(st, 3))
        {
            TBL_PC *sd = map->nick2sd(script_getstr(st, 3));
            if (sd)
                bl = &sd->bl;
        }
        else if (script_isint(st, 3))
        {
            bl = map->id2bl(script_getnum(st, 3));
        }
    }

    if (!bl)
    {
        TBL_PC *sd = script->rid2sd (st);
        if (sd)
            bl = &sd->bl;
    }
    if (bl)
        clif->specialeffect(bl, type, AREA);
    return true;
}

BUILDIN(setMapMask)
{
    const char *const mapName = script_getstr(st, 2);
    if (!mapName)
    {
        ShowWarning("invalid map name\n");
        script->reportsrc(st);
        return false;
    }
    const int m = map->mapname2mapid(mapName);
    if (m < 0)
    {
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }
    getMapData(m);

    const int val = script_getnum(st, 3);
    const unsigned int old = mapData->mask;
    mapData->mask = val;
    if (old != mapData->mask)
        send_mapmask_brodcast(m, mapData->mask);
    return true;
}

BUILDIN(getMapMask)
{
    const char *const mapName = script_getstr(st, 2);
    if (!mapName)
    {
        script_pushint(st, 0);
        ShowWarning("invalid map name\n");
        script->reportsrc(st);
        return false;
    }
    const int m = map->mapname2mapid(mapName);
    if (m < 0)
    {
        script_pushint(st, 0);
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }
    getMapDataReturn(m, 0);
    script_pushint(st, mapData->mask);
    return true;
}

BUILDIN(addMapMask)
{
    const char *const mapName = script_getstr(st, 2);
    if (!mapName)
    {
        ShowWarning("invalid map name\n");
        script->reportsrc(st);
        return false;
    }
    const int m = map->mapname2mapid(mapName);
    if (m < 0)
    {
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }
    getMapData(m);
    const int val = script_getnum(st, 3);
    const unsigned int old = mapData->mask;
    mapData->mask |= val;
    if (old != mapData->mask)
        send_mapmask_brodcast(m, mapData->mask);

    return true;
}

BUILDIN(removeMapMask)
{
    const char *const mapName = script_getstr(st, 2);
    if (!mapName)
    {
        ShowWarning("invalid map name\n");
        script->reportsrc(st);
        return true;
    }
    const int m = map->mapname2mapid(mapName);
    if (m < 0)
    {
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }
    getMapData(m);
    const int val = script_getnum(st, 3);
    const unsigned int old = mapData->mask;
    mapData->mask |= val;
    mapData->mask ^= val;
    if (old != mapData->mask)
        send_mapmask_brodcast(m, mapData->mask);
    return true;
}

BUILDIN(setNpcSex)
{
    TBL_NPC *nd = NULL;
    int sex = 0;
    if (script_hasdata(st, 3))
    {
        nd = npc->name2id (script_getstr(st, 2));
        sex = script_getnum(st, 3);
    }

    if (!nd || !nd->vd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }

    clif->clearunit_area(&nd->bl, CLR_OUTSIGHT);
    nd->vd->sex = sex;
    clif->spawn(&nd->bl);
    return true;
}

BUILDIN(showAvatar)
{
    int id = 0;
    if (script_hasdata(st, 2))
        id = script_getnum(st, 2);

    send_npccommand2(script->rid2sd (st), st->oid, 6, id, 0, 0);
    return true;
}

BUILDIN(setAvatarDir)
{
    int newdir = script_getnum(st, 2);

    if (newdir < 0)
        newdir = 0;
    else if (newdir > 7)
        newdir = 7;

    send_npccommand2(script->rid2sd (st), st->oid, 7, newdir, 0, 0);
    return true;
}

BUILDIN(setAvatarAction)
{
    send_npccommand2(script->rid2sd (st), st->oid, 8, script_getnum(st, 2), 0, 0);
    return true;
}

BUILDIN(clear)
{
    send_npccommand(script->rid2sd (st), st->oid, 9);
    return true;
}

BUILDIN(changeMusic)
{
    const char *const mapName = script_getstr(st, 2);
    const char *const music = script_getstr(st, 3);
    if (!music)
    {
        ShowWarning("invalid music file\n");
        script->reportsrc(st);
        return false;
    }
    if (!mapName)
    {
        ShowWarning("invalid map file\n");
        script->reportsrc(st);
        return false;
    }
    const int m = map->mapname2mapid(mapName);
    if (m < 0)
    {
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }

    send_changemusic_brodcast(m, music);
    return true;
}

BUILDIN(setNpcDialogTitle)
{
    const char *const name = script_getstr(st, 2);
    if (!name)
    {
        ShowWarning("invalid window title\n");
        script->reportsrc(st);
        return false;
    }
    TBL_PC *sd = script->rid2sd (st);
    if (!sd)
    {
        ShowWarning("player not attached\n");
        script->reportsrc(st);
        return false;
    }

    send_changenpc_title(sd, st->oid, name);
    return true;
}

BUILDIN(getMapName)
{
    TBL_PC *sd = script->rid2sd(st);
    if (!sd)
    {
        script_pushstr(st, aStrdup(""));
        ShowWarning("player not attached\n");
        script->reportsrc(st);
        return false;
    }
    if (sd->bl.m == -1)
    {
        script_pushstr(st, aStrdup(""));
        ShowWarning("invalid map\n");
        script->reportsrc(st);
        return false;
    }
    script_pushstr(st, aStrdup(map->list[sd->bl.m].name));
    return true;
}

BUILDIN(unequipById)
{
    int nameid = 0;
    int i;
    struct item_data *item_data;
    TBL_PC *sd = script->rid2sd(st);

    if (sd == NULL)
    {
        ShowWarning("player not attached\n");
        script->reportsrc(st);
        return false;
    }

    nameid = script_getnum(st, 2);
    if((item_data = itemdb->exists(nameid)) == NULL)
    {
        ShowWarning("item %d not found\n", nameid);
        script->reportsrc(st);
        return false;
    }
    for (i = 0; i < EQI_MAX; i++)
    {
        const int idx = sd->equip_index[i];
        if (idx >= 0)
        {
            if (sd->status.inventory[idx].nameid == nameid)
                pc->unequipitem(sd, idx, 1 | 2);
        }
    }
    return true;
}

BUILDIN(isPcDead)
{
    TBL_PC *sd = script->rid2sd(st);

    if (sd == NULL)
    {
        ShowWarning("player not attached\n");
        script->reportsrc(st);
        return false;
    }

    script_pushint(st, pc_isdead(sd) ? 1 : 0);
    return true;
}

static int areatimer_sub(struct block_list *bl, va_list ap)
{
    int tick;
    char *event;
    TBL_PC *sd;

    tick = va_arg(ap, int);
    event = va_arg(ap, char*);

    sd = (TBL_PC *)bl;
    if (!pc->addeventtimer(sd, tick, event))
    {
        if (sd)
            ShowWarning("buildin_addtimer: Event timer is full, can't add new event timer. (cid:%d timer:%s)\n", sd->status.char_id, event);
    }
    return 0;
}

BUILDIN(areaTimer)
{
    const char *const mapname = script_getstr(st, 2);
    const int x1 = script_getnum(st, 3);
    const int y1 = script_getnum(st, 4);
    const int x2 = script_getnum(st, 5);
    const int y2 = script_getnum(st, 6);
    const int time = script_getnum(st, 7);
    const char *const eventName = script_getstr(st, 8);
    int m;

    if ((m = map->mapname2mapid(mapname)) < 0)
    {
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }

    map->foreachinarea(areatimer_sub, m, x1, y1, x2, y2, BL_PC, time, eventName);

    return true;
}

static int buildin_getareadropitem_sub_del(struct block_list *bl, va_list ap)
{
    if (!bl)
        return 0;

    const int item = va_arg(ap, int);
    int *const amount = va_arg(ap, int *);
    TBL_ITEM *drop = (TBL_ITEM *)bl;

    if (drop->item_data.nameid == item)
    {
        (*amount) += drop->item_data.amount;
        map->clearflooritem(&drop->bl);
    }

    return 0;
}

BUILDIN(getAreaDropItem)
{
    const char *const str = script_getstr(st, 2);
    int16 m;
    int x0 = script_getnum(st, 3);
    int y0 = script_getnum(st, 4);
    int x1 = script_getnum(st, 5);
    int y1 = script_getnum(st, 6);
    int item;
    int amount = 0;

    if (script_isstringtype(st, 7))
    {
        const char *name = script_getstr(st, 7);
        struct item_data *item_data = itemdb->search_name(name);
        item = UNKNOWN_ITEM_ID;
        if (item_data)
            item = item_data->nameid;
    }
    else
    {
        item = script_getnum(st, 7);
    }

    if ((m = map->mapname2mapid(str)) < 0)
    {
        script_pushint(st, -1);
        ShowWarning("map not found\n");
        script->reportsrc(st);
        return false;
    }

    if (script_hasdata(st, 8) && script_getnum(st, 8))
    {
        map->foreachinarea(buildin_getareadropitem_sub_del,
            m, x0, y0, x1, y1, BL_ITEM, item, &amount);
    }
    else
    {
        map->foreachinarea(script->buildin_getareadropitem_sub,
            m, x0, y0, x1, y1, BL_ITEM, item, &amount);
    }
    script_pushint(st, amount);
    return true;
}

enum setmount_type
{
    SETMOUNT_TYPE_NONE         = 0,
    SETMOUNT_TYPE_PECO         = 1,
    SETMOUNT_TYPE_WUG          = 2,
    SETMOUNT_TYPE_MADO         = 3,
    SETMOUNT_TYPE_DRAGON_GREEN = 4,
    SETMOUNT_TYPE_DRAGON_BROWN = 5,
    SETMOUNT_TYPE_DRAGON_GRAY  = 6,
    SETMOUNT_TYPE_DRAGON_BLUE  = 7,
    SETMOUNT_TYPE_DRAGON_RED   = 8,
    SETMOUNT_TYPE_MAX,
    SETMOUNT_TYPE_DRAGON       = SETMOUNT_TYPE_DRAGON_GREEN,
};

BUILDIN(setMount)
{
    int flag = SETMOUNT_TYPE_NONE;
    TBL_PC* sd = script->rid2sd(st);

    if (sd == NULL)
        return true;  // no player attached, report source

    if (script_hasdata(st, 2))
        flag = script_getnum(st, 2);

    // Sanity checks and auto-detection
    if (flag >= SETMOUNT_TYPE_DRAGON_GREEN && flag <= SETMOUNT_TYPE_DRAGON_RED)
    {
        if (pc->checkskill(sd, RK_DRAGONTRAINING))
        {
            // Rune Knight (Dragon)
            unsigned int option;
            option = ( flag == SETMOUNT_TYPE_DRAGON_GREEN ? OPTION_DRAGON1 :
                       flag == SETMOUNT_TYPE_DRAGON_BROWN ? OPTION_DRAGON2 :
                       flag == SETMOUNT_TYPE_DRAGON_GRAY ? OPTION_DRAGON3 :
                       flag == SETMOUNT_TYPE_DRAGON_BLUE ? OPTION_DRAGON4 :
                       flag == SETMOUNT_TYPE_DRAGON_RED ? OPTION_DRAGON5 :
                       OPTION_DRAGON1); // default value
            pc->setridingdragon(sd, option);
        }
    }
    else if (flag == SETMOUNT_TYPE_WUG)
    {
        // Ranger (Warg)
        if (pc->checkskill(sd, RA_WUGRIDER))
            pc->setridingwug(sd, true);
    }
    else if (flag == SETMOUNT_TYPE_MADO)
    {
        // Mechanic (Mado Gear)
        if (pc->checkskill(sd, NC_MADOLICENCE))
            pc->setmadogear(sd, true);
    }
    else if (flag == SETMOUNT_TYPE_PECO)
    {
        // Knight / Crusader (Peco Peco)
        if (pc->checkskill(sd, KN_RIDING))
            pc->setridingpeco(sd, true);
    }
    else if (flag == SETMOUNT_TYPE_NONE && pc_hasmount(sd))
    {
        if (pc_isridingdragon(sd))
        {
            pc->setridingdragon(sd, 0);
        }
        if (pc_isridingwug(sd))
        {
            pc->setridingwug(sd, false);
        }
        if (pc_ismadogear(sd))
        {
            pc->setmadogear(sd, false);
        }
        if (pc_isridingpeco(sd))
        {
            pc->setridingpeco(sd, false);
        }
    }

    return true;
}

BUILDIN(clientCommand)
{
    getSD();

    const char *const command = script_getstr(st, 2);
    if (!command)
    {
        ShowWarning("invalid client command\n");
        script->reportsrc(st);
        return false;
    }
    send_client_command(sd, command);
    return true;
}

BUILDIN(isUnitWalking)
{
    int id = 0;
    if (script_hasdata(st, 2))
        id = script_getnum(st, 2);
    else
        id = st->oid;
    struct block_list *bl = map->id2bl(id);
    if (!bl)
    {
        ShowWarning("invalid unit id\n");
        script->reportsrc(st);
        script_pushint(st, 0);
        return false;
    }
    struct unit_data *ud = unit->bl2ud(bl);
    if (!ud)
    {
        ShowWarning("invalid unit data\n");
        script->reportsrc(st);
        script_pushint(st, 0);
        return false;
    }
    script_pushint(st, ud->walktimer != INVALID_TIMER);
    return true;
}

BUILDIN(failedRefIndex)
{
    getSD()
    getInventoryIndex(2)

    if (sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0)
        return false;

    sd->status.inventory[n].refine = 0;
    if (sd->status.inventory[n].equip)
        pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE);
    clif->refine(sd->fd, 1, n, sd->status.inventory[n].refine);
    pc->delitem(sd, n, 1, 0, DELITEM_FAILREFINE, LOG_TYPE_SCRIPT);
    clif->misceffect(&sd->bl, 2);
    return true;
}

BUILDIN(downRefIndex)
{
    getSD()
    getInventoryIndex(2)

    if (sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0)
        return false;

    const int down = script_getnum(st, 3);

    logs->pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[n], sd->inventory_data[n]);

    if (sd->status.inventory[n].equip)
        pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE);
    sd->status.inventory[n].refine -= down;
    sd->status.inventory[n].refine = cap_value(sd->status.inventory[n].refine, 0, MAX_REFINE);

    clif->refine(sd->fd, 2, n, sd->status.inventory[n].refine);
    clif->delitem(sd, n, 1, DELITEM_MATERIALCHANGE);
    logs->pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[n], sd->inventory_data[n]);
    clif->additem(sd, n, 1, 0);
    clif->misceffect(&sd->bl, 2);
    return true;
}

BUILDIN(successRefIndex)
{
    getSD()
    getInventoryIndex(2)

    if (sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0)
        return false;

    const int up = script_getnum(st, 3);

    logs->pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[n],sd->inventory_data[n]);

    if (sd->status.inventory[n].refine >= MAX_REFINE)
        return true;

    sd->status.inventory[n].refine += up;
    sd->status.inventory[n].refine = cap_value( sd->status.inventory[n].refine, 0, MAX_REFINE);
    if (sd->status.inventory[n].equip)
        pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE);
    clif->refine(sd->fd, 0, n, sd->status.inventory[n].refine);
    clif->delitem(sd, n, 1, DELITEM_MATERIALCHANGE);
    logs->pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[n],sd->inventory_data[n]);
    clif->additem(sd, n, 1, 0);
    clif->misceffect(&sd->bl, 3);

    if (sd->status.inventory[n].refine == 10 &&
        sd->status.inventory[n].card[0] == CARD0_FORGE &&
        sd->status.char_id == (int)MakeDWord(sd->status.inventory[n].card[2], sd->status.inventory[n].card[3]))
    { // Fame point system [DracoRPG]
        switch (sd->inventory_data[n]->wlv)
        {
            case 1:
                pc->addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
                break;
            case 2:
                pc->addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
                break;
            case 3:
                pc->addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
                break;
        }
    }

    return true;
}

// return paramater type
// 0 - int
// 1 - string
// 2 - other
BUILDIN(isStr)
{
    if (script_isinttype(st, 2))
        script_pushint(st, 0);
    else if (script_isstringtype(st, 2))
        script_pushint(st, 1);
    else
        script_pushint(st, 2);
    return true;
}

BUILDIN(npcSit)
{
    TBL_NPC *nd = NULL;

    if (script_hasdata(st, 2))
    {
        nd = npc->name2id (script_getstr(st, 2));
    }
    else
    {
        if (!st->oid)
        {
            ShowWarning("npc not attached\n");
            script->reportsrc(st);
            return false;
        }

        nd = (TBL_NPC *) map->id2bl (st->oid);
    }
    if (!nd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }
    nd->vd->dead_sit = 2;
    clif->sitting(&nd->bl);
    return true;
}

BUILDIN(npcStand)
{
    TBL_NPC *nd = NULL;

    if (script_hasdata(st, 2))
    {
        nd = npc->name2id (script_getstr(st, 2));
    }
    else
    {
        if (!st->oid)
        {
            ShowWarning("npc not attached\n");
            script->reportsrc(st);
            return false;
        }

        nd = (TBL_NPC *) map->id2bl (st->oid);
    }
    if (!nd)
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }
    nd->vd->dead_sit = 0;
    clif->standing(&nd->bl);
    return true;
}

BUILDIN(npcWalkTo)
{
    struct npc_data *nd = (struct npc_data *)map->id2bl(st->oid);
    int x = 0, y = 0;

    x = script_getnum(st, 2);
    y = script_getnum(st, 3);

    if (nd)
    {
        unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit
        if (!nd->status.hp)
        {
            status_calc_npc(nd, SCO_FIRST);
        }
        else
        {
            status_calc_npc(nd, SCO_NONE);
        }
        nd->vd->dead_sit = 0;
        unit->walktoxy(&nd->bl,x,y,0);
    }
    else
    {
        ShowWarning("npc not found\n");
        script->reportsrc(st);
        return false;
    }

    return true;
}

BUILDIN(setBgTeam)
{
    int bgId = script_getnum(st, 2);
    int teamId = script_getnum(st, 3);

    struct battleground_data *bgd = bg->team_search(bgId);
    struct BgdExt *data = bgd_get(bgd);
    if (!data)
    {
        ShowWarning("bettle ground not found\n");
        script->reportsrc(st);
        return false;
    }

    data->teamId = teamId;
    return true;
}

// chatjoin chatId [,char [,password]]
BUILDIN(chatJoin)
{
    int chatId = script_getnum(st, 2);
    TBL_PC *sd = NULL;
    const char *password = "";
    if (script_hasdata(st, 4))
    {
        if (script_isstringtype(st, 3))
            sd = map->nick2sd(script_getstr(st, 3));
        if (script_isstringtype(st, 4))
            password = script_getstr(st, 4);
    }
    else if (script_hasdata(st, 3))
    {
        if (script_isstringtype(st, 3))
            sd = map->nick2sd(script_getstr(st, 3));
    }
    else
    {
        sd = script->rid2sd(st);
    }
    if (!sd)
    {
        ShowWarning("player not found\n");
        script->reportsrc(st);
        return false;
    }

    chat->join(sd, chatId, password);
    return true;
}

/// Retrieves the value of the specified flag of the specified cell.
///
/// checknpccell("<map name>",<x>,<y>,<type>) -> <bool>
///
/// @see cell_chk* constants in const.txt for the types
BUILDIN(checkNpcCell)
{
    int16 m = map->mapname2mapid(script_getstr(st, 2));
    int16 x = script_getnum(st, 3);
    int16 y = script_getnum(st, 4);
    cell_chk type = (cell_chk)script_getnum(st, 5);

    if (m == -1)
    {
        ShowWarning("checknpccell: Attempted to run on unexsitent map '%s', type %d, x/y %d,%d\n", script_getstr(st, 2), type, x, y);
        return true;
    }

    TBL_NPC *nd = map->id2nd(st->oid);
    struct block_list *bl = NULL;
    if (nd)
        bl = &nd->bl;

    script_pushint(st, map->getcell(m, bl, x, y, type));

    return true;
}