// 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++) {
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;
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;
// 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) >= 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/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;
// Jeremy Quest (+1 rep)
if (getq(FrostiaQuest_Jeremy) >= 2)
.@nr=.@nr+1;
// FROSTIA Magical Forumula
.@nr=.@nr*100/5;
// 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";
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;
// 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;
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;
}
// 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);
.@fm$=escape_sql(getarg(1));
query_sql("INSERT INTO `api_export` (`type`, `data`) VALUES ('"+.@cde+"', \""+.@fm$+"\")");
return;
}