// 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)); consolewarn "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++) { attachrid(.@players[.@i]); if (compare(getarg(0), "$")) setd(getarg(0), ""); else setd(getarg(0), 0); detachrid(); } 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; consoleinfo("* 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; consoleinfo("* 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; // Naem Quest (+1 rep) if (getq(MineQuest_Naem) >= 3) .@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 (6 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; //// Order's Request (+1 rep) if (getq(LoFQuest_EPISODE) >= 13) .@nr=.@nr+1; // George Quest (+1 rep) if (getq(LoFQuest_George) >= 5) .@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/6; // 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 Henry Quest (+1 rep) if (getq(NivalisQuest_Henry) >= 2) .@nr=.@nr+1; // NIVALIS Magical Forumula .@nr=.@nr*100/7; // 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; // Fisherman Quest (crossover) (+1 rep) if (getq(HurnscaldQuest_ForestBow) >= 2) .@nr=.@nr+1; // Alvasus Quest (weekly) (+1 rep) if (getq(HalinarzoQuest_Alvasus) >= 1) .@nr=.@nr+1; // Sawis Quest (+1 rep) if (getq(HalinarzoQuest_Sawis) >= 2) .@nr=.@nr+1; // Mirio Quest (+1 rep) if (getq(HalinarzoQuest_Speed) >= 1) .@nr=.@nr+1; // HALINARZO Magical Forumula .@nr=.@nr*100/8; // Frostia Quests (3 points) } else if (getarg(0) == "Frostia") { // 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; // Jeremy Quest (+1 rep) if (getq(FrostiaQuest_Jeremy) >= 2) .@nr=.@nr+1; // Erlan Quest (+1 rep) if (getq(FrostiaQuest_Erlan) >= 2) .@nr=.@nr+1; // Rydel Quest (+1 rep) if (getq(FrostiaQuest_Rydel) >= 2) .@nr=.@nr+1; // Meriel Quest (+1 rep) if (getq(FrostiaQuest_Meriel) >= 2) .@nr=.@nr+1; // Taenya Quest (+1 rep) if (getq(FrostiaQuest_Taenya) >= 2) .@nr=.@nr+1; // Talindra Quest (+1 rep) if (getq(FrostiaQuest_Talindra) >= 2) .@nr=.@nr+1; // Gaelira Quest (+1 rep) if (getq(FrostiaQuest_Gaelira) >= 2) .@nr=.@nr+1; // Saevel Quest (+1 rep) if (getq(FrostiaQuest_Saevel) >= 2) .@nr=.@nr+1; // Jhon H's Quest (+1 rep) if (getq(FrostiaQuest_JhonH) >= 1) .@nr=.@nr+1; // FROSTIA Magical Forumula .@nr=.@nr*100/11; // 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; // Fortress Town Quests (1 point/special) } else if (getarg(0) == "Fortress") { // Who am I? (+2 rep) if (getq(General_Narrator) >= 22) .@nr=.@nr+2; // Cadis: Great Slime Hunt (+1 rep) if (getq(FortressQuest_SlimeHunter) >= 2) .@nr=.@nr+1; // Cadis: Great Over100 Hunt (+1 rep) if (getq(FortressQuest_Over100) >= 2) .@nr=.@nr+1; // Cadis: Great Ranged Hunt (+1 rep) if (getq(FortressQuest_RangedHunt) >= 2) .@nr=.@nr+1; // FORTRESS TOWN Magical Formula .@nr=.@nr*100/5; // 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; if (getarg(0) == "Artis") return 60000; } // From Tulimshar if (LOCATION$ == "Tulim") { if (getarg(0) == "Candor") return 22000; if (getarg(0) == "Hurns") return 28000; if (getarg(0) == "Nival") return 52000; if (getarg(0) == "Artis") return 60000; if (getarg(0) == "Tulim") return 12000; // Script-Controlled } // 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; } // From Artis if (LOCATION$ == "Artis") { if (getarg(0) == "Tulim") return 10000; if (getarg(0) == "Candor") return 10000; } // Error consolewarn "ERROR, INVALID LOCATION AND DESTINATION"; consolewarn("%s -> %s", 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 you are a legendary weapon holder // islegendary( {strcharinfo} ) function script islegendary { .@you$ = getarg(0, strcharinfo(0)); // TODO: countitem(Lightbringer) => Because rent time? return (.@you$ == $LIGHT_HOLDER$ || .@you$ == $AEGIS_HOLDER$ || .@you$ == $TYRAN_HOLDER$ || .@you$ == $RUNES_HOLDER$ || .@you$ == $DEMUR_HOLDER$); } // 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", "Tower", "Raid", "Olympics"; 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(, , ) 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=7; .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; // Not enough AP if (MAGIC_RP < .@ap) { return false; } // Payment if (countitem(.@it) < .@am) delitem ScholarshipBadge, 1; else delitem .@it, .@am; // Level up sk_lvup(.@sk, .@msp); MAGIC_RP-=.@ap; 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]*getskilllv(.@ski)*100); // 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; next; return false; } } // Max level reached if (getskilllv(.@ski) >= .@mlv) { mesc l("You've reached the maximum level for this skill."), 1; next; return true; } // Skill level check if (getskilllv(.@ski)) { .@learn$=l("Upgrading"); // New MSP ruleset if (getskilllv(.@ski) > 5) .@msp = 1; else .@msp = 0; } else if (.@msp <= 1) { // 1 MSP skills take no Research Points .@ap=0; } // Discount from usage [Mathias] .@ap=max(0, .@ap-skillInvoke[.@ski]); mesc l("%s %s will require:", .@learn$, getskillname(.@ski)); mes l("* %d/%d MSP (Magic Skill Points)", sk_points(), .@msp); mes l("* %s/%s RP (Research Points)", fnum(MAGIC_RP), fnum(.@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, .@ap); } // 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"); } } function script academicrank { switch (getarg(0, ACADEMIC_RANK)) { case 8: return l("Grand Master"); // Reserved for GM Team case 7: return l("Sage"); case 6: return l("Ph.D"); case 5: return l("Doctor"); case 4: return l("Master"); case 3: return l("Bachelor"); case 2: return l("Technician"); case 1: return l("Student"); case 0: return l("Layman"); default: return l("Banned from Academy"); } } // 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. FIXME - use (gettime(YEAR)-1970)*12 + gettime(MONTH)) .@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$ == "ARTIS") return TP_ARTIS; if (.@tw$ == "CANDOR") return TP_CANDOR; if (.@tw$ == "LILIT") return TP_LILIT; // TODO: Change this to use npc/config/location.txt instead if (.@tw$ == "FROSTIA") return TP_FROST; 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, report=True ) function script MapToLoc { // Fill variable .@v$=getarg(0); // Error code if (playerattached()) .@err=RB_DEFAULT; else .@err=RB_DEBUGMES; // Validate variable, see npc/config/location.txt first .@lx=array_find($@LOCMASTER_MAP$, .@v$); if (.@lx < 0) { if (getarg(1, true)) return Exception("Invalid map passed to MapToLoc: "+.@v$, .@err); else return ""; } 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 consolewarn("[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*2; } // 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; // This does not belong here, but... if (getq(LoFQuest_EPISODE) == 13) { sc_start SC_POISON, 86400000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK; } return .@BONUS; } // Frostia Util // frally( ) function script frally { return (Class == Elven); // or something } // Easter Egg // RegEasterEgg(EE_CODE, {CoinsAmount=3}) function script RegEasterEgg { .@code=getarg(0); .@coin=getarg(1,3); .@q=getq2(General_EasterEggs); if (!(.@q & .@code)) { setq1 General_EasterEggs, 1; setq2 General_EasterEggs, .@q|.@code; dispbottom l("For finding an Easter Egg, you got Strange Coins!"); getitem StrangeCoin, .@coin; setq3 General_EasterEggs, bitmask_count(.@q|.@code); } return; } // Makes a monster aggro // set_aggro( monster{, mode=MD_AGGRESSIVE} ) function script set_aggro { .@m=getarg(0); .@x=getarg(1, MD_AGGRESSIVE); .@op=getunitdata(.@m, UDT_MODE); .@op=.@op|.@x; setunitdata(.@m, UDT_MODE, .@op); return; } function script immortal { .@u=getarg(0); setunitdata(.@u, UDT_HP, 2147483647); setunitdata(.@u, UDT_MAXHP, 2147483647); return; } // 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$); } // Same as numdate() but SQL format. // sqldate({day variation, month variation}) function script sqldate { .@d=gettime(GETTIME_DAYOFMONTH)+getarg(0, 0); .@m=gettime(GETTIME_MONTH)+getarg(1, 0); .@y=gettime(GETTIME_YEAR); // Overflow prevention if (.@d <= 0) { .@d=1; } while (.@m > 12) { .@y+=1; .@m-=12; } while (.@m < 1) { .@y-=1; .@m+=12; } .@strdate$=sprintf("%04d-%02d-%02d %02d:%02d:%02d", .@y, .@m, .@d, gettime(GETTIME_HOUR), gettime(GETTIME_MINUTE), gettime(GETTIME_SECOND)); return .@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); if (!apicall(.@cde, getarg(1))) { debugmes "[API] Fallback in use."; .@fm$=escape_sql(getarg(1)); query_sql("INSERT INTO `api_export` (`type`, `data`) VALUES ('"+.@cde+"', \""+.@fm$+"\")"); } return; }