summaryrefslogblamecommitdiff
path: root/npc/functions/util.txt
blob: 95087455e02909bca7b13b3ec62cf7a5c471921f (plain) (tree)
1
2
3
4
5
6
               
           
               


                    






                                                                                         
 





                                                                        









                                                                               

           
 



                                                                                     
                                      

               

                                                                          
 


                         


                                                   
                                  



                                    
                    





















                                                                                      










                                                                                       










                                                   


           




                                                         
                                                                                  








                                                                                                










                                                                                      

                                                                                 
                                        
                                 


















                                         
                                     
                                          

                    











                                       




                                     








                                       
                                    

                    
                       



                                           











                                                
                                        



                
                                                                                  
                                                    
                                         

                              
                                   
                               

                                               

                        

                                            





                                                    



                                                  

                                               











                                                



                                            



                                             



                                              



                                                    
                               
                                                  
                        
 



                                           



                                         





                                          

                        




                                                  
                         
 
 




 
                                   


                                      
                                                









                                               






                                                                           







                                              


                                                     
 



                                                 



                                            



                                                  
                               




                                              

                        



                                              
                                     
                         

 




 
                                     

                                    




                                        
                                        









                                       



                                       
                                        
                        
 




 
                                





                                         
                                       


                                          




                                                  
                                                   


                                                
                                                      


                                                 
                                                       

                        
                                   
                        
 
 































                                                  




 
                                









                                          




                                               
                                           

                        
                                   
                        

 




 
                                


















                                            
                                              






                                           
                                          









                                         



                                          
                                  
                         

 
            

     
                                        




                




                                                                 
                  

                                 
                         

     
                     

                                  
                         
                                 
                         
                                 
                         






                                  
                         
                                 
                         

     




                                  
                         
                                 
                         

     




                                                                          
                   
 
 















                                                      


                                                      
                                                





                                          














                                              





                                                                                                          
 





                                                                          
 







                                                                  
           
            
            


                               
                                                        




                                                 

                                                        
                                      
                                                               


                                         

                                      

                                                                    

                                 

                           

                                        

                                     
                 

 




                                           
                                             
                         
                                         
                                                       


                              
                                                     
                          

                                 
                           
                                    


                               


           



































                                                                 
                                      




                                         





                                                                     





                                     




                                                 

                                                                  






                                                                      
                    


                        
                            
                              


                                
                                                                 







                                                                                           


                                       
                    



                                                   



















                                                                          
 



                                                                                                
                             
                              
                                    

 

                                                                           
                                                           




                                                                         
                                             

                                                         



                









                                                                         
                                    













                                         




                                                    
























                                                                                   
                         
                                                                                  



                               








                                           
 
 


























                                                                                       





                                         

                                                           
                                                                        
 



                                           


           
                                  










                                         

                                                           
                 
                                                                              
 
                                  


                                  




                                         





                          

                                                           
                 
                                                                         
 
                                  

 






                                              










                                                                          





                                                                     


                                                 

                                   

                           
     







                                                                              
                           

                           

                               

                                                                          


                   



                                                                                                                   


                                  


                            





                                                                 
                







                                            
                         

                              
                                         


                                 
                                             
            
                                   





                     

                   

 
 



                                         
                                



                                                                                               
// TMW2 Script.
// Authors:
//    Jesusalva
// Description:
//    Util functions

/////////////////////////////////////////////////////////////////////////////////
// Delete item ID on inventories, storages, guild storages and carts. Also affects mails.
// WARNING, irreversible and dangerous!
// DelItemFromEveryPlayer( ID )
function	script	DelItemFromEveryPlayer	{
    if (getarg(0, -1) < 0)
        return;

    query_sql("DELETE FROM `inventory` WHERE `nameid`="+getarg(0));
    query_sql("DELETE FROM `cart_inventory` WHERE `nameid`="+getarg(0));
    query_sql("DELETE FROM `storage` WHERE `nameid`="+getarg(0));
    query_sql("DELETE FROM `guild_storage` WHERE `nameid`="+getarg(0));
    query_sql("DELETE FROM `rodex_items` WHERE `nameid`="+getarg(0));
    query_sql("DELETE FROM `auction` WHERE `nameid`="+getarg(0));
    debugmes "Deleting item %d", getarg(0);

    // Del items which SQL can't reach
    .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
    for (.@i = 0; .@i < .@c; .@i++) {
        .@am=countitem(getarg(0), .@players[.@i]);
        if (.@am) debugmes "DELETE %d items from ACC %d", .@am, .@players[.@i];
        if (.@am)
            delitem(getarg(0), .@am, .@players[.@i]);
    }
    return;
}

// Delete an acc_reg entry from all players. Full arrays only. Affect num and str db.
// WARNING, irreversible and dangerous!
// DelAccRegFromEveryPlayer( KEY )
function	script	DelAccRegFromEveryPlayer	{
    if (getarg(0, "error") == "error")
        return;

    query_sql("DELETE FROM `acc_reg_num_db` WHERE `key`='"+getarg(0)+"'");
    query_sql("DELETE FROM `acc_reg_str_db` WHERE `key`='"+getarg(0)+"'");

    if (playerattached())
        detachrid();

    // Del variables which SQL can't reach
    .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
    for (.@i = 0; .@i < .@c; .@i++) {
        attachrid(.@players[.@i]);
        if (compare(getarg(0), "$"))
            setd(getarg(0), "");
        else
            setd(getarg(0), 0);
        detachrid();
    }
    return;
}

// Delete an char_reg entry from all players. Full arrays only. Affect num and str db.
// WARNING, irreversible and dangerous!
// DelChrRegFromEveryPlayer( KEY )
function	script	DelChrRegFromEveryPlayer	{
    if (getarg(0, "error") == "error")
        return;

    query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='"+getarg(0)+"'");
    query_sql("DELETE FROM `char_reg_str_db` WHERE `key`='"+getarg(0)+"'");

    // Del variables which SQL can't reach
    .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
    for (.@i = 0; .@i < .@c; .@i++) {
        if (compare(getarg(0), "$"))
            setd(getarg(0), "");
        else
            setd(getarg(0), 0);
    }
    return;
}

// Delete a quest entry from all players. This includes all counters. Use with caution.
// WARNING, irreversible and dangerous!
// DelQuestFromEveryPlayer( ID )
function	script	DelQuestFromEveryPlayer	{
    if (getarg(0, -1) < 0)
        return;

    query_sql("DELETE FROM `quest` WHERE `quest_id`="+getarg(0));

    // Del quests which SQL can't reach
    .@a=playerattached();
    if (.@a) detachrid();
    .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
    for (.@i = 0; .@i < .@c; .@i++) {
        attachrid(.@players[.@i]);
        setq(getarg(0), 0, 0, 0, 0);
        detachrid();
    }
    if (.@a) attachrid(.@a);
    return;
}

// Transforms an item in something else.
// ReplaceItemFromEveryPlayer( OldID, NewID )
function	script	ReplaceItemFromEveryPlayer	{
    if (getarg(0, -1) < 0)
        return;
    debugmes("* Server update: %d item was replaced by %d", getarg(0), getarg(1));
    query_sql("UPDATE `inventory` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    query_sql("UPDATE `cart_inventory` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    query_sql("UPDATE `storage` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    query_sql("UPDATE `guild_storage` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    query_sql("UPDATE `rodex_items` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    query_sql("UPDATE `auction` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0));
    return;
}

// Replaces a skill with another ID.
// ReplaceSkillFromEveryPlayer( OldID, NewID )
function	script	ReplaceSkillFromEveryPlayer	{
    if (getarg(0, -1) < 0)
        return;
    debugmes("* Server update: skill %d was replaced by %d", getarg(0), getarg(1));
    // If new ID already exists, it will skip
    query_sql("UPDATE IGNORE `skill` SET `id`='"+getarg(1)+"' WHERE `id`="+getarg(0));
    return;
}


/////////////////////////////////////////////////////////////////////////////////
// Returns Nard reputation for discounts
// Currently ranges from 0 to 16.
function	script	nard_reputation	{
    .@nr=0; // Base reputation

    // Valon Quest (+1 rep)
    if (getq(CandorQuest_Trainer) >= 14)
        .@nr=.@nr+1;

    // Zegas Quest (+1 rep)
    if (getq(CandorQuest_Barrel) >= 4)
        .@nr=.@nr+1;

    // Hide And Seek Quest (+1 rep)
    if (getq(CandorQuest_HAS) >= 4)
        .@nr=.@nr+1;

    // Sailors Quest (+1 rep)
    if (getq(CandorQuest_Sailors) >= 3)
        .@nr=.@nr+1;

    // Sailors Quest, part 2 (+1 rep)
    if (getq(CandorQuest_SailorCure) >= 1)
        .@nr=.@nr+1;

    // Vincent Quest (+1 rep)
    if (getq(CandorQuest_Vincent) >= 2)
        .@nr=.@nr+1;

    // Tolchi Quest (+1 rep)
    if (getq(CandorQuest_Tolchi) >= 4)
        .@nr=.@nr+1;

    // Maya Quest (+1 rep)
    if (getq(CandorQuest_Maya) >= 4)
        .@nr=.@nr+1;

    // Rosen Quest (+1 rep)
    if (getq(CandorQuest_Rosen) >= 3)
        .@nr=.@nr+1;

    // Ship Crew Quests
    // Dan Quest (+1 rep)
    if (getq(ShipQuests_Dan) >= 3)
        .@nr=.@nr+1;

    // Chef Gado Quest (+1 rep)
    if (getq(ShipQuests_ChefGado) >= 2)
        .@nr=.@nr+1;

    // Peter Quest (+1 rep)
    if (getq(ShipQuests_Peter) >= 7)
        .@nr=.@nr+1;

    // Tulimshar Quests
    // Swezanne Quest (+1 rep)
    if (getq(TulimsharQuest_Swezanne) >= 1)
        .@nr=.@nr+1;

    // Sailors Quest (+1 rep)
    if (getq(TulimsharQuest_Sailors) >= 2)
        .@nr=.@nr+1;

    // Hasan Quest (+1 rep)
    if (getq(TulimsharQuest_Hasan) >= 5)
        .@nr=.@nr+1;

    // Dausen Quest (+1 rep)
    if (getq(TulimsharQuest_WaterForGuard) >= 3)
        .@nr=.@nr+1;

    //debugmes "Reputation: "+str(.@nr);
    return .@nr;

}

// Returns reputation based on quests completion for discounts. Returns 0~100 int.
// Takes one argument (PC_DEST$). Grep for "getarg".
function	script	reputation	{
    .@nr=0; // Base reputation

    // Tulimshar Quests (16 points)
    if (getarg(0) == "Tulim") {
        // Eugene Quest (+1 rep)
        if (getq(TulimsharQuests_Fishman) >= 2)
            .@nr=.@nr+1;

        // Sarah Quest (+1 rep)
        if (getq(TulimsharQuest_Sarah) >= 1)
            .@nr=.@nr+1;

        // Dausen Quest (+1 rep)
        if (getq(TulimsharQuest_WaterForGuard) >= 3)
            .@nr=.@nr+1;

        // Dausen Quest II (+1 rep)
        if (getq(TulimsharQuest_MobTutorial) >= 8)
            .@nr=.@nr+1;

        // Swezanne Quest (+1 rep)
        if (getq(TulimsharQuest_Swezanne) >= 1)
            .@nr=.@nr+1;

        // Silvia Quest (+1 rep)
        if (getq(TulimsharQuest_Lifestone) >= 2)
            .@nr=.@nr+1;

        /*
        // Eisten Quest (+1 rep)
        if (getq(TulimsharQuest_Eistein) >= 6)
            .@nr=.@nr+1;
        */

        // Hasan Quest (+1 rep)
        if (getq(TulimsharQuest_Hasan) >= 5)
            .@nr=.@nr+1;

        // Devoir Quest (+1 rep)
        if (getq(TulimsharQuest_Devoir) >= 1)
            .@nr=.@nr+1;

        // Sailors Quest (+1 rep)
        if (getq(TulimsharQuest_Sailors) >= 2)
            .@nr=.@nr+1;

        // Zarkor Quest (+1 rep)
        if (getq(TulimsharQuest_DarkInvocator) >= 7)
            .@nr=.@nr+1;

        // Anwar Quest (+1 rep)
        if (getq(TulimsharQuest_AnwarField) >= 10)
            .@nr=.@nr+1;

        // Neko Quest (+1 rep)
        if (getq(TulimsharQuest_Neko) >= 2)
            .@nr=.@nr+1;

        // Tycoon Quest (+1 rep)
        if (getq(MineQuest_Tycoon) >= 15)
            .@nr=.@nr+1;

        // Dracoula Quest (+1 rep)
        if (getq(MineQuest_Dracoula) >= 1)
            .@nr=.@nr+1;

        // Caelum Quest (+1 rep)
        if (getq(MineQuest_Caelum) >= 2)
            .@nr=.@nr+1;

        // Veteran Officer Quest (+1 rep)
        if (getq(TulimsharQuest_WoodenSword) >= 2)
            .@nr=.@nr+1;

        // TULIMSHAR Magical Forumula
        .@nr=.@nr*100/16;







    // Hurnscald Quests (11 points)
    } else if (getarg(0) == "Hurns") {

        // Alan Quest (+1 rep)
        if (getq(HurnscaldQuest_ForestBow) >= 2)
            .@nr=.@nr+1;

        // Gwendolyn Quest (+1 rep)
        if (getq(HurnscaldQuest_HarkEye) >= 6)
            .@nr=.@nr+1;

        // Celestia Quest (+1 rep)
        if (getq(HurnscaldQuest_TeaParty) >= 2)
            .@nr=.@nr+1;

        /*
        // Yeti King Quest (+1 rep)
        // Please note that if you challenge it again and lose, it'll reset
        if (getq(HurnscaldQuest_Celestia) == 6)
            .@nr=.@nr+1;
        */

        // Farmers Quest (+1 rep)
        if (getq(HurnscaldQuest_Farmers) >= 5)
            .@nr=.@nr+1;

        // Helena Quest (+1 rep)
        if (getq(HurnscaldQuest_Bandits) >= 8)
            .@nr=.@nr+1;

        // Injuried Mouboo Quest (+1 rep)
        if (getq(HurnscaldQuest_InjuriedMouboo) >= 2)
            .@nr=.@nr+1;

        // Blood Donor Quest (+1 rep)
        if (getq(HurnscaldQuest_BloodDonor) >= 1)
            .@nr=.@nr+1;

        // Woody Quest (+1 rep)
        if (getq(HurnscaldQuest_Woody) >= 5)
            .@nr=.@nr+1;

        // Lieutenant Quest (+1 rep)
        if (getq(HurnscaldQuest_Lieutenant) >= 10)
            .@nr=.@nr+1;

        // Thorn Quest (+1 rep)
        if (getq(HurnscaldQuest_Thorn) >= 1)
            .@nr=.@nr+1;

        // Blossom Quest (+1 rep)
        if (getq(HurnscaldQuest_Blossom) >= 1)
            .@nr=.@nr+1;

        // LOF Bot Quest (+1 rep)
        if (getq(HurnscaldQuest_LOFPass) >= 3)
            .@nr=.@nr+1;

        // HURNSCALD Magical Forumula
        .@nr=.@nr*100/12;







    // Land Of Fire Quests (5 points)
    } else if (getarg(0) == "LoF") {

        // The EPISODE
        //// Tea For Two (+1 rep)
        if (getq(LoFQuest_EPISODE) >= 2)
            .@nr=.@nr+1;
        //// Early Christmas (+1 rep)
        if (getq(LoFQuest_EPISODE) >= 6)
            .@nr=.@nr+1;

        // George Quest (+1 rep)
        if (getq(LoFQuest_George) >= 3)
            .@nr=.@nr+1;

        // Fairy Quest (+1 rep)
        if (getq(LoFQuest_Fairy) >= 3)
            .@nr=.@nr+1;

        // Pet Detective Quest (+1 rep)
        if (getq(LoFQuest_Pets) >= 1)
            .@nr=.@nr+1;

        // LAND OF FIRE Magical Forumula
        .@nr=.@nr*100/5;






    // Nivalis Quests (6 points)
    } else if (getarg(0) == "Nival") {

        // Nivalis Well Quest (+1 rep)
        if (getq(NivalisQuest_Well) >= 2)
            .@nr=.@nr+1;

        // Nivalis Cindy Quest (+1 rep)
        if (getq(NivalisQuest_Cindy) >= 5)
            .@nr=.@nr+1;

        // Blue Sage: Investigation Quest (+1 rep)
        if (getq(NivalisQuest_BlueSage) >= 12)
            .@nr=.@nr+1;

        // Blue Sage: Slime Hunting Quest (+1 rep)
        if (getq(NivalisQuest_BlueSageSlimes) >= 2)
            .@nr=.@nr+1;

        // Blue Sage: Page Makers Quest (+1 rep)
        if (getq(NivalisQuest_BlueSagePagemaker) >= 1)
            .@nr=.@nr+1;

        // Blue Sage: Page Finders Quest (+1 rep)
        if (getq(NivalisQuest_BlueSagePagefinder) >= 1)
            .@nr=.@nr+1;

        // NIVALIS Magical Forumula
        .@nr=.@nr*100/6;







    // Halinarzo Quests (5 points)
    } else if (getarg(0) == "Halin") {

        // Foxhound Famine Quest (+1 rep)
        if (getq(HalinarzoQuest_Foxhound) >= 6)
            .@nr=.@nr+1;

        // Charles Quest (+1 rep)
        if (getq(HalinarzoQuest_TraderKing) >= 2)
            .@nr=.@nr+1;

        // Joaquim & Yumi Quest (+1 rep)
        if (getq(HalinarzoQuest_SickWife) >= 5)
            .@nr=.@nr+1;

        // Life Delight Quest (+1 rep)
        if (getq(HalinarzoQuest_LifeDelight) >= 2)
            .@nr=.@nr+1;

        // Sawis Quest (+1 rep)
        if (getq(HalinarzoQuest_Sawis) >= 2)
            .@nr=.@nr+1;

        // HALINARZO Magical Forumula
        .@nr=.@nr*100/5;







    // Frostia Quests (3 points)
    } else if (getarg(0) == "Frostia") {

        // Rescue Yeti Quest (+1 rep)
        if (getq(NivalisQuest_Well) >= 2)
            .@nr=.@nr+1;

        // Rescue Cindy Quest (+1 rep)
        if (getq(NivalisQuest_Cindy) >= 5)
            .@nr=.@nr+1;

        // Homunculus Quest (+1 rep)
        if (getq(FrostiaQuest_Homunculus) >= 4)
            .@nr=.@nr+1;

        // AFK Cap Quest (+1 rep)
        if (getq(FrostiaQuest_AFKCap) >= 2)
            .@nr=.@nr+1;

        // FROSTIA Magical Forumula
        .@nr=.@nr*100/4;







    // Candor Quests (10 points)
    } else if (getarg(0) == "Candor") {

        // Valon Quest (+1 rep)
        if (getq(CandorQuest_Trainer) >= 14)
            .@nr=.@nr+1;

        // Zegas Quest (+1 rep)
        if (getq(CandorQuest_Barrel) >= 4)
            .@nr=.@nr+1;

        // Hide And Seek Quest (+1 rep)
        if (getq(CandorQuest_HAS) >= 4)
            .@nr=.@nr+1;

        // Sailors Quest (+1 rep)
        if (getq(CandorQuest_Sailors) >= 3)
            .@nr=.@nr+1;

        // Sailors Quest, part 2 (+1 rep)
        if (getq(CandorQuest_SailorCure) >= 1)
            .@nr=.@nr+1;

        // Vincent Quest (+1 rep)
        if (getq(CandorQuest_Vincent) >= 2)
            .@nr=.@nr+1;

        // Tolchi Quest (+1 rep)
        if (getq(CandorQuest_Tolchi) >= 3)
            .@nr=.@nr+1;

        // Maya Quest (+1 rep)
        if (getq(CandorQuest_Maya) >= 4)
            .@nr=.@nr+1;

        // Rosen Quest (+1 rep)
        if (getq(CandorQuest_Rosen) >= 3)
            .@nr=.@nr+1;

        // Marggo Quest (+1 rep)
        if (getq(CandorQuest_Marggo) >= 1)
            .@nr=.@nr+1;

        // CANDOR Magical Forumula
        .@nr=.@nr*100/10;


    // Final
    }

    //debugmes "Reputation: "+str(.@nr);
    return .@nr;

}


// Returns time for ship travel.
// Can be modified by a factor.
function	script	nard_time	{
    // Estimates time to move by ship from LOCATION$ to getarg(0)

    // From Candor
    if (LOCATION$ == "Candor") {
        if (getarg(0) == "Tulim")
            return 22000;

    }
    // From Tulimshar
    if (LOCATION$ == "Tulim") {
        if (getarg(0) == "Candor")
            return 22000;
        if (getarg(0) == "Hurns")
            return 28000;
        if (getarg(0) == "Nival")
            return 52000;

    }
    // From Hurnscald
    if (LOCATION$ == "Hurns") {
        if (getarg(0) == "Candor")
            return 22000;
        if (getarg(0) == "Tulim")
            return 28000;
        if (getarg(0) == "Nival")
            return 28000;

    }
    // From Nivalis
    if (LOCATION$ == "Nival") {
        if (getarg(0) == "Candor")
            return 46000;
        if (getarg(0) == "Tulim")
            return 52000;
        if (getarg(0) == "Hurns")
            return 28000;

    }

    // Error
    debugmes "ERROR, INVALID LOCATION AND DESTINATION";
    debugmes l("@@ -> @@", LOCATION$, getarg(0));
    dispbottom l("An error on your travel time happened. Please report.");
    return INT_MAX;
}

// alignment() → 1 if Good, -1 if Evil, 0 if Neutral
function	script	alignment	{
    .@m=getq(HurnscaldQuest_InjuriedMouboo);

    // Mouboo was slain: EVIL
    if (.@m >= 9)
        return -1;

    // Mouboo was saved and Sagratha rescued: GOOD
    if (SAGRATHA_FRIENDSHIP >= 2)
        return 1;

    // N/A: NEUTRAL
    return 0;
}

// Returns if an event is a ranked Aurora Event or not
// (Had to be moved from functions/aurora.txt)
function	script	FYEventUsesRanking	{
    setarray .@av$, "Expo", "Fishing", "Mining";
    if (array_find(.@av$, $EVENT$) >= 0) {
        return true;
    }
    return false;
}

// Determines if player is still in range.
// eg.
// if (reachable(.x, .y, .distance)) {
function	script	reachable	{
    .@x=getarg(0);
    .@y=getarg(1);
    .@z=getarg(2);
    getmapxy(.@mp$, .@xp, .@yp, 0);

    if (distance(.@x, .@y, .@xp, .@yp) <= .@z)
        return 1;
    else
        return 0;
}

// Determines if party exp sharing is enabled
// ( Party ID )
function	script	party_expon	{
    .@nb = query_sql("SELECT exp FROM `party` WHERE party_id="+escape_sql(getarg(0))+" LIMIT 2", .@value);
    return .@value[0];
}

// Special rif for books
// rif2(<menu_id>, <condition>, <text>)
function	script	rif2	{
    return rif( getarg(1)   ,rif(@menu == getarg(0), "► ") + getarg(2));
}


// Prepare Mana Stone
// mstone( lvl )
function	script	mstone	{
    // Fill variable
    .@v=getarg(0);

    // Determine how much stats you need, this is based on players
    // and change based on $Global Variables
    .int=5;
    .lvl=15;
    .jlv=10;

    return (
            MAGIC_LVL == .@v &&
            readparam2(bInt) >= $MANA_BINT+(.int*.@v) &&
            BaseLevel >= $MANA_BLVL+(.lvl*.@v) &&
            JobLevel >= $MANA_JLVL+(.jlv*.@v) &&
            readparam(Sp) == readparam(MaxSp));
}

// MAGIC_PTS → Amount of used Magic Skill Points
// sk_maxpoints() → Max Magic Skill Points you may use
// Returns how many points you can use
// Current maximum as of 2020-06-21: (pratic) 30 ~ 43 (theoric)
function	script	sk_maxpoints	{
    // 2 points per magic level
    .@val=(MAGIC_LVL)*2;
    // 1 point every twice magic level
    .@val+=(MAGIC_LVL/2);
    // Excluding first 15, 1 point every 12 job levels (Up to JL 75)
    .@val+=min(5, ((JobLevel-15)/12));
    // 1 point per being a player
    .@val+=1;
    // 2 points per Rebirth
    .@val+=(REBIRTH*2);
    // 1 point per skill permit level
    .@val+=getskilllv(TMW2_SKILLPERMIT);
    // Sacrificing the Mouboo: +1 MSP
    .@val+=(alignment() < 0 ? 1 : 0);
    return .@val;
}

// Returns how many points you can allocate
function	script	sk_points	{
    return sk_maxpoints()-MAGIC_PTS;
}

// Returns true if a skill can be leveled up.
// sk_canlvup( {cost=1} )
function	script	sk_canlvup	{
    return ((MAGIC_PTS+getarg(0,1)) <= sk_maxpoints());
}

// Level up a skill in 1 level
// TODO: Return the point if leveling about Max Level
// sk_lvup( sk{, cost=0} )
function	script	sk_lvup	{
    .@lvl=getskilllv(getarg(0));
    getexp 0, 50*(.@lvl+1);
    addtoskill(getarg(0),.@lvl+1,0);
    if (getarg(1,0)) {
        MAGIC_PTS+=getarg(1,0);
    }
    return;
}

// LEGACY Magic School Learning Interface
// mlearn( skill, MAX_LV, MSP cost, item, amount{, AP cost} )
// returns false if cheater
function	script	mlearn	{
    .@sk=getarg(0);
    .@ff=getarg(1);
    .@msp=getarg(2);
    .@it=getarg(3);
    .@am=getarg(4);
    .@ap=getarg(5, 0);
    // Max level reached
    if (getskilllv(.@sk) >= .@ff) {
        return true;
    }
    // Not enough items
    if (countitem(.@it) < .@am && !(countitem(ScholarshipBadge)))
        return false;
    // Not enough MSP
    if (!sk_canlvup(.@msp))
        return false;
    // TODO: Not enough AP
    //if (MAGIC_AP < .@ap) {
    //    return false;
    //}

    // Payment
    if (countitem(.@it) < .@am)
        delitem ScholarshipBadge, 1;
    else
        delitem .@it, .@am;

    // Level up
    sk_lvup(.@sk, .@msp);
    return true;
}

// NEW Magic School Learning Interface
// learn_magic(Skill)
function	script	learn_magic	{
    .@ski=getarg(0);
    .@learn$=l("Learning");

    // Check if skill is valid
    .@mlv=$@MSK_MAXLV[.@ski];
    if (.@mlv < 1) {
        return Exception("ERROR: The skill "+.@ski+" is not valid!");
    }

    // Load a few temporary variables
    .@pre=$@MSK_PREREQ[.@ski];
    .@it=$@MSK_ITEM[.@ski];
    .@am=$@MSK_AMOUNT[.@ski];
    .@msp=$@MSK_MSPCOST[.@ski];
    .@ap=$@MSK_COST[.@ski];

    // Pre-requisite check
    if (.@pre) {
        if (getskilllv(.@pre) < 1) {
            mesc l("Pre-requisites not met!"), 1;
            mesc l("The following skill is needed: %s%s (Lv. %d)",
                   "##9", getskillname(.@pre), 1), 1;
            return false;
        }
    }

    // Max level reached
    if (getskilllv(.@ski) >= .@mlv) {
        mesc l("You've reached the maximum level for this skill."), 1;
        return true;
    }

    // Skill level check
    if (getskilllv(.@ski)) {
        .@msp=50; // FIXME NYI
        .@learn$=l("Upgrading");
    }

    mesc l("%s %s will require:", .@learn$, getskillname(.@ski));
    mes l("* %d/%d MSP (Magic Skill Points)", sk_points(), .@msp);
    //mes l("* %d/%d AP (???)", MAGIC_AP, .@ap);
    if (countitem(.@it) < .@am) {
        mesc l("~~%d/%d %s~~", countitem(.@it), .@am, getitemlink(.@it)), 8;
        mes l("* %d/%d %s", countitem(ScholarshipBadge), 1, getitemlink(ScholarshipBadge));
    } else {
        mes l("* %d/%d %s", countitem(.@it), .@am, getitemlink(.@it));
    }
    mes "";
    mesc l("Really learn this skill?");
    if (askyesno() == ASK_NO)
        return true;

    return mlearn(.@ski, .@mlv, .@msp, .@it, .@am);
}

// transcheck( {item 1, amount 1}, {item 2, amount 2}... )
// returns true upon success
function	script	transcheck	{
    if (getargcount() < 2 || getargcount() % 2 != 0)
        return Exception("Faulty learning skill command invoked - error");

    // Count items
    for (.@i=0;.@i < getargcount(); .@i++) {
        if (countitem(getarg(.@i)) < getarg(.@i+1))
            return false;
        .@i++;
    }

    // Delete Items
    for (.@i=0;.@i < getargcount(); .@i++) {
        delitem getarg(.@i), getarg(.@i+1);
        .@i++;
    }
    return true;
}

// Returns a value defining your current magic control (affects success ratio, higher is better)
// A value of '5' means perfect control, and a value of '0' means overwhelm.
// abizit()
function	script	abizit	{
    if (!MAGIC_LVL) return 0;
    .@base=((MAGIC_LVL*2)**3);
    return min(MAGIC_EXP/.@base, 5);
}

// anyloot( {item 1, amount 1, chance 1}, {item 2, amount 2, chance 2}... )
// Give chance (standard 1~10000 roll) to obtain item, capped at amount.
// TODO: Fill an array, then inventoryplace() and getitem()
function	script	anyloot	{
    if (getargcount() < 3 || getargcount() % 3 != 0)
        return Exception("Faulty anyloot skill command invoked - error");

    // Get Items
    for (.@i=0;.@i < getargcount(); .@i+=3) {
        if (rand2(10000) < getarg(.@i+2))
            getitem getarg(.@i), rand2(1, getarg(.@i+1));
    }
    return true;
}


// Returns, based on a 1-5 range, the title for both thief and merc ranks
// thiefrank() / mercrank()
function	script	thiefrank	{
    switch (THIEF_RANK) {
        case 5: return l("Bandit Lord");
        case 4: return l("Assassin");
        case 3: return l("Rogue");
        case 2: return l("Bandit");
        case 1: return l("Thief");
        case 0: return l("Citizen");
        default: return l("Error");
    }
}
function	script	mercrank	{
    switch (MERC_RANK) {
        case 5: return l("Constable");
        case 4: return l("Guardian");
        case 3: return l("Merchant");
        case 2: return l("Trader");
        case 1: return l("Fair Person");
        default: return l("Error");
    }
}

// alias to readbattleparam(getcharid(3), ?? )
function	script	battleparam	{
    return readbattleparam(getcharid(3), getarg(0));
}

// gettimeparam(GETTIME_X)
//   Returns the number of seconds/minutes/hours/days/months/years since 01/01/1970
function	script	gettimeparam	{
    .@p=getarg(0, GETTIME_MINUTE);

    // Seconds
    .@t=gettimetick(2);
    if (.@p == GETTIME_SECOND)
        return .@t;

    // Minutes (default)
    .@t=.@t/60;
    if (.@p == GETTIME_MINUTE)
        return .@t;

    // Hours
    .@t=.@t/60;
    if (.@p == GETTIME_HOUR)
        return .@t;

    // Days
    .@t=.@t/24;
    if (.@p == GETTIME_DAYOFMONTH)
        return .@t;

    // Weeks (estimative)
    .@a=.@t+4; // 01/01/1970 was a Thursday. So this will make it float at sunday.
    .@a=.@a/7;
    if (.@p == GETTIME_WEEKDAY)
        return .@a;

    // Months (estimative)
    .@t=.@t/30;
    if (.@p == GETTIME_MONTH)
        return .@t;

    // Years (estimative, unused, fallback)
    .@t=.@t/12;
    return .@t;
}


// Convert LOC (uppercase) to a TP variable
// POL_LocToTP( {TOWNCODE} )
function	script	POL_LocToTP	{
    .@tw$=strtoupper(getarg(0, LOCATION$));

    if (.@tw$ == "TULIM")
        return TP_TULIM;

    if (.@tw$ == "HALIN")
        return TP_HALIN;

    if (.@tw$ == "HURNS")
        return TP_HURNS;

    if (.@tw$ == "LOF")
        return TP_LOF;

    if (.@tw$ == "NIVAL")
        return TP_NIVAL;

    if (.@tw$ == "FROSTIA")
        return TP_FROSTIA;

    return Exception("Invalid town requested / POL_LocToTP", RB_DEFAULT|RB_SPEECH, -1);
}


// Upon entering a town
// EnterTown( LocName )
function	script	EnterTown	{
    // Fill variable
    .@v$=getarg(0);

    // Validade variable, see npc/config/location.txt first
    if (array_find($@LOCMASTER_LOC$, .@v$) < 0)
        return Exception("Invalid location passed to EnterTown: "+.@v$);

    // Do not save if you're exiled
    .@tpcode=POL_LocToTP(strtoupper(.@v$));
    if (!(#EXILED & .@tpcode))
        LOCATION$=.@v$;
    return;
}

// Convert map name to location id
// LocToMap( LocName )
function	script	LocToMap	{
    // Fill variable
    .@v$=getarg(0);

    // Error code
    if (playerattached())
        .@err=RB_DEFAULT;
    else
        .@err=RB_DEBUGMES;

    // Validade variable, see npc/config/location.txt first
    .@lx=array_find($@LOCMASTER_LOC$, .@v$);
    if (.@lx < 0)
        return Exception("Invalid location passed to LocToMap: "+.@v$, .@err);

    return $@LOCMASTER_MAP$[.@lx];
}

// Convert map name to location id
// MapToLoc( MapName )
function	script	MapToLoc	{
    // Fill variable
    .@v$=getarg(0);

    // Error code
    if (playerattached())
        .@err=RB_DEFAULT;
    else
        .@err=RB_DEBUGMES;

    // Validade variable, see npc/config/location.txt first
    .@lx=array_find($@LOCMASTER_MAP$, .@v$);
    if (.@lx < 0)
        return Exception("Invalid map passed to MapToLoc: "+.@v$, .@err);

    return $@LOCMASTER_LOC$[.@lx];
}

// Gets the location code for TP code
function	script	TPToLoc	{
    .@i=array_find($@LOCMASTER_TP, getarg(0));
    return $@LOCMASTER_MAP$[.@i];
    return;
}

// Warps home and updates LOCATION$
function	script	teleporthome	{
    warp "Save", 0, 0;
    .@i=array_find($@LOCMASTER_MAP$, getmap());
    if (.@i >= 0)
        EnterTown($@LOCMASTER_LOC$[.@i]);
    else
        debugmes("[ERROR] Invalid Town Map for Time Flask: %s", getmap());
    return;
}

// Returns TOP 3 Average Level
// TOP3AVERAGELVL( - )
function	script	TOP3AVERAGELVL	{
    return ($@hoblvl_value[0]+$@hoblvl_value[1]+$@hoblvl_value[2])/3;
}

// Grants newcomers exp boost. Returns bonus %
// NewcomerEXPDROPUP( - )
function	script	NewcomerEXPDROPUP	{
    // Event System Override
    if ($EVENT$ == "Anniversary") {
        if (BaseLevel < 10)
            BaseLevel=10;
    }
    // Newbies
    if (!REBIRTH) {
        .@AVG_LEVEL=($@hoblvl_value[0]+$@hoblvl_value[1]+$@hoblvl_value[2])/3;
        .@BONUS=min(50, .@AVG_LEVEL/2);
        .@BONUS-=BaseLevel;
        .@BONUS=max(1, .@BONUS);
    // Rebirth
    } else {
        .@BONUS=REBIRTH*10;
    }
    // Defaults to 24 hours
    sc_end SC_CASH_PLUSEXP;
    sc_end SC_CASH_RECEIVEITEM;
    sc_start SC_CASH_PLUSEXP, 86400000, (REBIRTH ? .@BONUS : .@BONUS*2/3);
    sc_start SC_CASH_RECEIVEITEM, 86400000, .@BONUS;
    return .@BONUS;
}

// Special function which makes a date as a number
// numdate( - )
function	script	numdate	{
    .@strdate$=sprintf("%04d%02d%02d", gettime(GETTIME_YEAR), gettime(GETTIME_MONTH), gettime(GETTIME_DAYOFMONTH));
    // Debug payload
    if ($@OVERRIDE_NUMDATE)
        return $@OVERRIDE_NUMDATE;
    return atoi(.@strdate$);
}

// json_encode( {varname, varvalue}, {varname 2, varvalue 2}... )
// returns string
function	script	json_encode	{
    if (getargcount() < 2 || getargcount() % 2 != 0)
        return Exception("json_encode arguments must be paired");

    .@json$="{";
    .@tab=true;

    // For arguments
    for (.@i=0;.@i < getargcount(); .@i++) {
        // Close previous item
        if (.@tab)
            .@tab=false;
        else
            .@json$+=",";

        // Input variable name
        .@json$+="\""+getarg(.@i)+"\": ";

        // Input variable value
        if (isstr(getarg(.@i+1)))
            .@json$+="\""+getarg(.@i+1)+"\"";
        else
            .@json$+=getarg(.@i+1);

        // Advance
        .@i++;
    }

    // Close the JSON
    .@json$+="}";
    return .@json$;
}


// api_send( code, data )
// sends to API
function	script	api_send	{
    .@cde=getarg(0);
    .@fm$=escape_sql(getarg(1));
    query_sql("INSERT INTO `api_export` (`type`, `data`) VALUES ('"+.@cde+"', \""+.@fm$+"\")");
    return;
}