// TMW2 Script // Evol functions. // Authors: // 4144 // Travolta // gumi // Jesusalva // Description: // Built-in essential functions. function script menuimage { return getarg(0) + "|" + getarg(1); } function script dnext { if (@dnext >= GSET_LONGMENU_DENSITY) { @dnext=0; next; } else { @dnext+=1; } return; } function script menuaction { return "[" + getarg(0) + "]"; } function script setq1 { // Quest, val1 , val2 , val3 , time setq getarg(0), getarg(1), getq2(getarg(0)), getq3(getarg(0)), getqtime(getarg(0)); return; } function script setq2 { // Quest, val1 , val2 , val3 , time setq getarg(0), getq(getarg(0)), getarg(1), getq3(getarg(0)), getqtime(getarg(0)); return; } function script setq3 { // Quest, val1 , val2 , val3 , time setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getarg(1), getqtime(getarg(0)); return; } function script setqtime { // Quest, val1 , val2 , val3 , time setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getq3(getarg(0)), getarg(1); return; } function script mesn { if (getargcount() > 0) { .@s$ = "[" + getarg(0) + "]"; } else { .@s$ = "[" + strnpcinfo(1) + "]"; } mes .@s$; return; } function script mesq { mes "\"" + getarg(0)+ "\""; return; } function script g { return Sex == 0 ? getarg(0) : getarg(1); } function script b { return "##B" + getarg(0) + "##b"; } function script col { .@color = getarg(1,9); if (.@color < 0) .@color = 0; if (.@color > 9) .@color = 9; return "##" + .@color + getarg(0) + "##0"; } function script adddefaultskills { if (getskilllv(NV_BASIC) < 7) { skill NV_BASIC, 7, 0; } if (getskilllv(TMW2_FAKESKILL) < 1) { skill TMW2_FAKESKILL, 1, 0; } return; } function script addremovemapmask { setmapmask getarg(0), (getmapmask(getarg(0)) | (getarg(1) + getarg(2))) ^ getarg(2); return; } function script mesc { mes col(getarg(0),getarg(1,9)); return; } function script get_race { .@g=getarg(0, Class); return l($@allraces$[.@g]); } // tutmes (message, {header=Tutorial, headerfirst=True}) function script tutmes { .@header$=getarg(1, l("TUTORIAL")); .@showheader=getarg(2, true); .@tcol=9; // Tutorial color code if (TUTORIAL) { dnext; if (.@showheader) { mesf(".:: %s ::.", .@header$); mesc getarg(0), .@tcol; } else { mesc .@header$+": "+getarg(0), .@tcol; } } return; } // Function to show narrator text. Accepts string args. // If first arg is a number N, then it represents bit flags. // Bit flags : // 0x1 -- blank line at beginning // 0x2 -- blank line at the end // 0x4 -- use last "next;" // 0x8 -- don't use first "mesn;" function script narrator { .@start = 0; .@argc = getargcount(); .@flags = 0; if (.@argc > 1 && !isstr(getarg(0))) { .@start = 1; .@flags = getarg(0); } if (.@flags & 0x1) mes ""; if (!(.@flags & 0x8)) mesn l("Narrator"); for (.@i = .@start; .@i < .@argc; .@i++) { mes col(getarg(.@i), 9); if (.@i < .@argc - 1) next; } if (.@flags & 0x4) next; else if (.@flags & 0x2) mes ""; return; } // Function to show NPC speech. Accepts string args. // If first arg is a number N, then it represents bit flags. // Bit flags : // 0x1 -- blank line at beginning // 0x2 -- blank line at the end // 0x4 -- use last "next;" // 0x8 -- don't use first "mesn;" function script speech { .@start = 0; .@argc = getargcount(); .@flags = 0; if (.@argc > 1 && !isstr(getarg(0))) { .@start = 1; .@flags = getarg(0); } if (.@flags & 0x1) mes ""; if (!(.@flags & 0x8)) mesn; for (.@i = .@start; .@i < .@argc; .@i++) { mesq getarg(.@i); if (.@i < .@argc - 1) next; } if (.@flags & 0x4) next; else if (.@flags & 0x2) mes ""; return; } // Show debug message if .debug variable of NPC is set to 1 function script npcdebug { if (getvariableofnpc(.debug, strnpcinfo(3))) debugmes strnpcinfo(3) + ": " + getarg(0); return; } function script askyesno { return select(menuaction(l("Yes")), menuaction(l("No"))); } // Argument: // 0 Quest variable // 1 Current value // 2 Next value function script compareandsetq { if (getq(getarg(0)) == getarg(1)) { setq getarg(0), getarg(2); return true; } return false; } // Use a delay to prevent spams from NPC that display text without the // use of (a) close/next function(s). // Argument: // 0 Text to display // 1 Lock delay (default = 1) // 2 Message function: (default = 0) // 0 = npctalk3 // 1 = npctalk // 2 = message // TODO: Use temp player var, because NPC var affect other players function script npctalkonce { // lock mechanism switch (getarg(2, 0)) { case 1: if (gettimetick(2) <= getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE))) return false; set(getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE)), gettimetick(2) + getarg(1, 1)); break; default: if (gettimetick(2) <= @NPC_TALK_LOCK[getnpcid()]) return false; @NPC_TALK_LOCK[getnpcid()] = gettimetick(2) + getarg(1, 1); } // talk mechanism switch (getarg(2, 0)) { case 0: npctalk3(getarg(0)); break; case 1: npctalk(getarg(0)); break; case 2: message(strcharinfo(0), getarg(0)); } return true; } // Randomizer functions ///////////////////////////////////////////// // pseudo-fix randomness // rand2( min, max ) function script rand2 { if (getargcount() == 2) { .@min=getarg(0)*100; .@max=getarg(1)*100+99; } else { .@min=0; .@max=getarg(0)*100-1; } return rand(.@min, .@max)/100; } // returns one argument randomly // any( {, ...} ) function script any { return getarg(rand2(getargcount())); } // returns any member of the array // any_of( ) function script any_of { return getelementofarray(getarg(0), getarrayindex(getarg(0)) + rand2(getarraysize(getarg(0)) - getarrayindex(getarg(0)))); } function script die { // Distinguish scripted deaths for perma-death mode if ($HARDCORE) @grace=true; // Wait script execution to finish addtimer 10, "#GlobalHandler::OnDie"; return; } // Returns if a map is on PVP Mode or Not // ispvpmap( {mapid} ) function script ispvpmap { .@mapa$=getarg(0, getmapname()); return (getmapflag(.@mapa$, mf_pvp) || getmapflag(.@mapa$, mf_pvp_noparty) || getmapflag(.@mapa$, mf_pvpnoguild)); } // TMW2 Custom Functions ///////////////////////////////////////////// // Function meant to be used by Main Storyline Quest // msObjective ( condition , message ) function script msObjective { if (getarg(0)) mesc getarg(1), 2; else mesc getarg(1), 9; return; } function script getmap { if (getmapxy(.@mapName$, .@xpos, .@ypos, getarg(0,0)) != 0) return false; // TODO: Maybe use getmapname() instead of getmapxy? return .@mapName$; } // isin( map, x1, y1, {[x2, y2][radius]} ) function script isin { if (getmapxy(.@mapName$, .@xpos, .@ypos, 0) != 0) return false; if (.@mapName$ != getarg(0)) return false; if (getarg(4,-1) < 0) { // Radius Based if (.@xpos >= getarg(1)-getarg(3) && .@xpos <= getarg(1)+getarg(3) && .@ypos >= getarg(2)-getarg(3) && .@ypos <= getarg(2)+getarg(3)) return true; } else { // Coordinate based if (.@xpos >= getarg(1) && .@xpos <= getarg(3) && .@ypos >= getarg(2) && .@ypos <= getarg(4)) return true; } return false; } // Clear output of getinventorylist() // delinventorylist() function script delinventorylist { deletearray @inventorylist_id; deletearray @inventorylist_amount; deletearray @inventorylist_equip; deletearray @inventorylist_refine; deletearray @inventorylist_identify; deletearray @inventorylist_attribute; deletearray @inventorylist_card1; deletearray @inventorylist_card2; deletearray @inventorylist_card3; deletearray @inventorylist_card4; deletearray @inventorylist_expire; deletearray @inventorylist_bound; @inventorylist_count=0; return; } // Get some acc id, even if offline // ( Name ) function script gf_accid { .@nb = query_sql("SELECT `account_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value); return .@value[0]; } // Get some char id, even if offline // ( Name ) function script gf_charnameid { .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value); return .@value[0]; } // Get some char name from char ID, even if offline // ( Name ) function script gf_charname { .@nb = query_sql("SELECT `name` FROM `char` WHERE `char_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$); return .@value$[0]; } // Get some char ID from account ID, even if offline // ( Name ) function script gf_charid { .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `account_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$); return .@value$[0]; } // Request pincode and validate it. Use any non-4-digits code to cancel. Failure will dc you. // Returns 1 if pin check is OK. function script validatepin { if (#FIRST_TIME < 2) { mesc l("ERROR: You must set a PinCode to make use of this function."), 1; return 0; } mesc l("Please insert your pincode."), 1; mesc l("WARNING: If you insert wrong pincode, you'll be disconnected."); mesc l("Use @@ to cancel.", "##B-1##b"); mes ""; input .@pin$; if (getstrlen(.@pin$) != 4) return 0; query_sql("SELECT userid FROM `login` WHERE account_id="+escape_sql(getcharid(3))+" AND pincode='"+escape_sql(.@pin$)+"' LIMIT 2", .@value$); if (getarraysize(.@value$) != 1) { atcommand "@kick "+strcharinfo(0); return 0; } // Enforce some cooldown to prevent an eventual exploit/abuse sleep2(rand2(150, 400)); mesc l("Thanks, @@. We just wanted to be sure it was you.", .@value$[0]); mes ""; return true; } // Something went wrong and must be reported (named after raise Exception in python) // Exception( BugID, {Flags{, Return Code}} ) function script Exception { // Fill variable .@msg$=getarg(0); .@gf=getarg(1,RB_DEFAULT); if (.@gf & RB_DISPBOTTOM) dispbottom("ERROR: "+.@msg$); if (.@gf & RB_DEBUGMES) debugmes("[Warning] "+.@msg$); if (.@gf & RB_SPEECH) mesc("ERROR, REPORT ME! "+.@msg$, 1); if (.@gf & RB_IRCBROADCAST) channelmes("#world", "Error in script: "+.@msg$); if (.@gf & RB_GLOBALANNOUNCE) announce("Error in script: "+.@msg$, bc_all); if (.@gf & RB_PLEASEREPORT) { if (.@gf & RB_DISPBOTTOM) dispbottom("Please take a screenshot and report this bug, explaining how it happened."); if (.@gf & RB_SPEECH) mesc("Please take a screenshot and report this bug, explaining how it happened."), 1; } if (.@gf & RB_ISFATAL) { if (.@gf & RB_DISPBOTTOM) dispbottom("This error is fatal, we stop execution."); if (.@gf & RB_DEBUGMES) debugmes("[Error] The error is fatal."); if (.@gf & RB_SPEECH) { mesc l("This error is fatal, we stop execution."), 1; close; } end; } return getarg(2, 0); } // mescordialog(text, color, {dialog=1}) function script mescordialog { if (getarg(2, true)) mesc getarg(0), getarg(1); else dispbottom col(getarg(0), getarg(1)); return; } // Delayed healing. Takes 3~5 seconds. Variates with Vit up to +100%. // The vit can have an additional 20% bonus as well. function script itheal { .@bas=getarg(0); .@vit=readbattleparam(getcharid(3), UDT_VIT); .@vit=cap_value(.@vit-1, 0, 100); if (getargcount() > 2) .@tim=getarg(3); else .@tim=rand2(3,5); .@min=.@bas*(100+.@vit)/100; .@max=.@bas*(100+.@vit*120/100)/100; // Now divide the HP values by the time .@min=max(1, .@min/.@tim); .@max=max(1, .@max/.@tim); callfunc("SC_Bonus", .@tim, SC_S_LIFEPOTION, .@min, .@max); if (getarg(1,0) > 0) heal 0, getarg(1, 0); return; } // 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$; } // 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$); } // 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; } // Linking functions ///////////////////////////////////////////// function script getquestlink { return "[@@q" + getarg(0) + "|@@]"; } function script getmonsterlink { return "[@@m" + getarg(0) + "|@@]"; } function script getpetlink { return "[@@p" + getarg(0) + "|@@]"; } function script getmercenarylink { return "[@@M" + getarg(0) + "|@@]"; } function script gethomunculuslink { return "[@@h" + getarg(0) + "|@@]"; } // Legacy functions ///////////////////////////////////////////// function script mapexit { debugmes "TRYING TO MAPEXIT IS DEPRECATED"; return; } function script destroy { disablenpc strnpcinfo(0); return; } function script npcaction { debugmes "Deprecated unitaction (did you mean npcsit; or whatever?)"; .@a=getarg(0, 0); if (.@a == 9) clear; return; } function script gmlog { logmes(getarg(0), LOGMES_ATCOMMAND); return; } function script getx { getmapxy(.@m$, .@x, .@y, 0); return .@x; } function script gety { getmapxy(.@m$, .@x, .@y, 0); return .@y; } function script getnpcx { return .x; } function script getnpcy { return .y; } function script title { setnpcdialogtitle getarg(0); return; } function script camera { if (getarg(0, "") != "") setcamnpc getarg(0); else restorecam; return; } function script mapmask { sendmapmask getarg(0); return; } function script getmask { return 1; //getmapmask(getmapname()); // TODO: Return original map masks } // isat( map, x, y ) function script isat { return isin(getarg(0), getarg(1), getarg(2), 0); } function script if_then_else { return (getarg(0) ? getarg(1) : getarg(2)); } function script misceffect { // or SELF + something return specialeffect(getarg(0), AREA, getarg(1, strnpcinfo(0))); } function script selfeffect { // or SELF + something return specialeffect(getarg(0), SELF, getarg(1, strnpcinfo(0))); } function script fakenpcname { if (getargcount() > 2) setnpcdisplay(getarg(0), getarg(1), getarg(2)); else setnpcdisplay(getarg(0), getarg(1)); return; } function script npcwarp { if (getargcount() > 2) .@id=getnpcid(getarg(3)); else .@id=getnpcid(); getmapxy(.@m$, .@x, .@y, UNITTYPE_NPC, strnpcinfo(0, "", .@id)); unitwarp(.@id, .@m$, getarg(0), getarg(1)); return; } function script get { return getvariableofnpc(getarg(0), getarg(1)); } function script sc_check { return getstatus(getarg(0), getarg(1, 0)); } function script wgm { charcommand("@request "+getarg(0)); return; } function script registercmd { // Remove "@" from command start .@cmd$=getarg(1); if (charat(.@cmd$, 0) == "@") delchar(.@cmd$, 0); bindatcmd getarg(0), .@cmd$, getarg(2, 0); return; } function script iscollision { return checknpccell(getarg(0), getarg(1), getarg(2), cell_chkpass); } function script readparam2 { return readbattleparam(getcharid(3), getarg(0)); } function script updateskill { skill getarg(0), getarg(1), 0; return; } function script npctalk2 { npctalk getarg(1), getarg(0); return; } function script learnskill { if (getskilllv(getarg(0)) < getarg(1, 1)) skill getarg(0), getarg(1, 1), 0; return; } function script spawndummy { // spawndummy(map, x, y, ID{, name{, event}}) .@m$=getarg(0); .@x=getarg(1); .@y=getarg(2); .@id=getarg(3); .@n$=getarg(4, strmobinfo(1, .@id)); .@e$=getarg(5, ""); // Create monster, with optional event if (.@e$ == "") .@u=monster(.@m$, .@x, .@y, .@n$, .@id, 1); else { .@u=monster(.@m$, .@x, .@y, .@n$, .@id, 1, .@e$); debugmes "SPAWNDUMMY %s", .@e$; } // Reset unit data for script use setunitdata(.@u, UDT_LEVEL, 1); setunitdata(.@u, UDT_HP, 1); setunitdata(.@u, UDT_MAXHP, 1); setunitdata(.@u, UDT_SP, 1); setunitdata(.@u, UDT_MAXSP, 1); setunitdata(.@u, UDT_MODE, 0); // Stoic mode setunitdata(.@u, UDT_ATKRANGE, 1); setunitdata(.@u, UDT_ATKMIN, 1); setunitdata(.@u, UDT_ATKMAX, 1); setunitdata(.@u, UDT_MATKMIN, 1); setunitdata(.@u, UDT_MATKMAX, 1); setunitdata(.@u, UDT_DEF, 1); setunitdata(.@u, UDT_MDEF, 1); setunitdata(.@u, UDT_HIT, 1); setunitdata(.@u, UDT_FLEE, 1); setunitdata(.@u, UDT_CRIT, 1); setunitdata(.@u, UDT_LOOKDIR, DOWN); return .@u; } ///////////////////////////////////////////////////////////////////////////////// // Do not run this outside startup // 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; }