summaryrefslogtreecommitdiff
path: root/npc/functions
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2021-04-10 03:00:20 -0300
committerJesusaves <cpntb1@ymail.com>2021-04-10 03:00:20 -0300
commitba1e827b6b4c17c35a163e6b55be8c122de632b8 (patch)
tree819f93d0ffee3697e336471710afb9681f0b8d86 /npc/functions
parent6e7f3113c0faad9edd4367d100ba9dd77e8d3130 (diff)
downloadserverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.gz
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.bz2
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.xz
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.zip
Add several convenience functions. Fix some bugs regarding misuse of readparam()
Diffstat (limited to 'npc/functions')
-rw-r--r--npc/functions/array.txt464
-rw-r--r--npc/functions/asklanguage.txt72
-rw-r--r--npc/functions/filters.txt126
-rw-r--r--npc/functions/goodbye.txt152
-rw-r--r--npc/functions/inc_sc_bonus.txt72
-rw-r--r--npc/functions/input.txt110
-rw-r--r--npc/functions/inventoryplace.txt36
-rw-r--r--npc/functions/main.txt37
-rw-r--r--npc/functions/math.txt123
-rw-r--r--npc/functions/npcmove.txt142
-rw-r--r--npc/functions/npcmovegraph.txt489
-rw-r--r--npc/functions/permissions.txt35
-rw-r--r--npc/functions/random-talk.txt207
-rw-r--r--npc/functions/string.txt211
-rwxr-xr-xnpc/functions/time.txt256
-rw-r--r--npc/functions/timer.txt89
16 files changed, 2444 insertions, 177 deletions
diff --git a/npc/functions/array.txt b/npc/functions/array.txt
new file mode 100644
index 00000000..d433abd7
--- /dev/null
+++ b/npc/functions/array.txt
@@ -0,0 +1,464 @@
+// Evol Script
+// Author: Gumi
+
+// array_pad(<array>, <size>, <value>)
+// prepend or append <value> until the array is of <size> size
+// returns the amount added on success, or false (0) if nothing changed
+
+function script array_pad {
+ .@index = getarrayindex(getarg(0)); // passed index
+ .@count = getarraysize(getarg(0)) - .@index; // actual size
+ .@size = getarg(1); // desired size
+ .@absolute = (.@size >= 0 ? .@size : -(.@size)); // |size|
+ .@delta = .@absolute - .@count; // amount to fill
+
+ if (.@absolute <= .@count) {
+ return false; // nothing to do
+ }
+
+ if (.@size < 0) {
+ copyarray(getelementofarray(getarg(0), .@index + .@delta), getarg(0), .@count); // shift to the right
+ cleararray(getarg(0), getarg(2), .@delta); // prepend
+ } else {
+ cleararray(getelementofarray(getarg(0), .@index + .@count), getarg(2), .@delta); // append
+ }
+
+ return .@delta;
+}
+
+
+
+// array_replace(<array>, <needle>, <replace>{, <neq>})
+// replace every occurence of <needle> with <replace>
+// returns the number of replaced elements
+
+function script array_replace {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(3, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ set(getelementofarray(getarg(0), .@i), getarg(2));
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_find(<array>, <needle>{, <neq>})
+// return the index of the first occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_find {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_rfind(<array>, <needle>{, <neq>})
+// return the index of the last occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_rfind {
+ .@min = getarrayindex(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = (getarraysize(getarg(0)) - 1); .@i >= .@min; --.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_exists(<array>, <needle>{, <neq>})
+// return true or false accordingly if <needle> is found in <array>
+
+function script array_exists {
+ return array_find(getarg(0), getarg(1), getarg(2, false)) > -1;
+}
+
+
+
+// array_count(<array>, <needle>{, <neq>})
+// counts the number of occurrence of <needle> in the <array>
+
+function script array_count {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_entries(<array>)
+// returns the number of non-empty entries
+
+function script array_entries {
+ if (isstr(getarg(0)) == 1) {
+ return array_count(getarg(0), "", true);
+ }
+ return array_count(getarg(0), 0, true);
+}
+
+
+
+// array_remove(<array>, <needle>{, <neq>})
+// removes every occurrence of <needle> in the <array> while shifting left
+
+function script array_remove {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_reverse(<array>)
+// reverses the array
+
+function script array_reverse {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = .@index; .@i < ((.@size + .@index) / 2); ++.@i) {
+ swap(getelementofarray(getarg(0), .@i), getelementofarray(getarg(0), .@size + .@index - 1 - .@i)); // a <> b
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_sum(<array>)
+// return the sum of every element of the array
+
+function script array_sum {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@sum += getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@sum;
+}
+
+
+
+// array_difference(<array>)
+// return the difference of every element of the array
+
+function script array_difference {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@diff -= getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@diff;
+}
+
+
+
+// array_shift(<array>)
+// returns the first element of the array and removes it, while shifting left
+
+function script array_shift {
+ if (isstr(getarg(0)) == 1) {
+ .@val$ = getarg(0);
+ } else {
+ .@int = true;
+ .@val = getarg(0);
+ }
+
+ deletearray(getarg(0), 1); // shift left
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// array_unshift(<array>, <value>)
+// adds <value> to the start of the array, while shifting right
+// returns the new size
+
+function script array_unshift {
+ .@size = getarraysize(getarg(0)) + 1;
+ array_pad(getarg(0), -(.@size - getarrayindex(getarg(0))), getarg(1));
+ return .@size;
+}
+
+
+
+// array_pop(<array>)
+// returns the last element of the array and removes it
+
+function script array_pop {
+ .@last = getarraysize(getarg(0)) - 1;
+
+ if (isstr(getelementofarray(getarg(0), .@last)) == 1) {
+ .@val$ = getelementofarray(getarg(0), .@last);
+ } else {
+ .@int = true;
+ .@val = getelementofarray(getarg(0), .@last);
+ }
+
+ deletearray(getelementofarray(getarg(0), .@last), 1);
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// TODO: Rename to array_append >.<
+// array_push(<array>, <value>)
+// adds <value> to the end of the array
+// returns the new size
+
+function script array_push {
+ .@size = getarraysize(getarg(0));
+ set(getelementofarray(getarg(0), .@size), getarg(1));
+ return .@size + 1;
+}
+
+
+
+// array_shuffle(<array>)
+// shuffles the array
+
+function script array_shuffle {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0)) - .@index;
+ freeloop(true);
+
+ if (isstr(getarg(0)) == 1) {
+ copyarray(.@tmp$[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp$[rand(.@size)]));
+ }
+ } else {
+ copyarray(.@tmp[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp[rand(.@size)]));
+ }
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_unique(<array>{, <threshold>})
+// allows entries to appear up to <threshold> in the array
+
+function script array_unique {
+ .@size = getarraysize(getarg(0));
+ .@max = getarg(1, 1);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@count = 1;
+ for (.@e = .@i + 1; .@e < .@size; ++.@e) {
+ if (getelementofarray(getarg(0), .@i) == getelementofarray(getarg(0), .@e)) {
+ if (++.@count >= .@max) {
+ deletearray(getelementofarray(getarg(0), .@e), 1);
+ ++.@removed; // increase counter
+ --.@size; // reduce size
+ --.@e; // step back
+ }
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@removed;
+}
+
+
+
+// array_diff(<array1>, <array2>{, <array>...}, <array>)
+// compares array1 against one or more other arrays and fills the last array
+// with the values in array1 that are not present in any of the other arrays
+// returns the number of entries not matching
+
+function script array_diff {
+ .@size = getarraysize(getarg(0));
+ .@index = getarrayindex(getarg(0));
+ freeloop(true);
+
+ for (.@a = 1; .@a < (getargcount() - 1); ++.@a) {
+ for (.@i = .@index; .@i < .@size; ++.@i) {
+ if (!array_exists(getarg(.@a), getelementofarray(getarg(0), .@i))) {
+ array_push(getarg(getargcount() - 1), getelementofarray(getarg(0), .@i));
+ ++.@count;
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_filter(<array>, "<function>")
+// filters the array using a callback function
+
+function script array_filter {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@eq = callfunc(getarg(1), getelementofarray(getarg(0), .@i)) != false;
+ if ((.@neq && .@eq) || (!(.@neq) && !(.@eq))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+// array_highest(<array>)
+// Returns the index of the highest value in <array>
+// NOTE: Array must be an INT array!
+
+function script array_highest {
+ .@size = getarraysize(getarg(0));
+ .@win=0;
+ .@idx=0;
+ .@dw=false;
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if (getelementofarray(getarg(0), .@i) > .@win) {
+ .@win=getelementofarray(getarg(0), .@i);
+ .@idx=.@i;
+ if (.@dw) {
+ deletearray .@draw;
+ .@dw=false;
+ }
+ } else if (getelementofarray(getarg(0), .@i) == .@win) {
+ if (!.@dw)
+ array_push(.@draw, .@idx);
+ array_push(.@draw, .@i);
+ .@dw=true;
+ }
+ }
+
+ // Will we return .@idx or do we need to draw a loot?
+ freeloop(false);
+ if (.@dw)
+ return any_of(.@draw);
+ else
+ return .@idx;
+}
+
+// relative_array_random(<array: 0, {[value, probability]..}>)
+// returns a random entry from the array, by relative probability
+// the first key of the array should be 0 and every entries are a tuple
+// of [value, probability]
+
+function script relative_array_random {
+ .@is_str = getdatatype(getarg(0)) & DATATYPE_STR;
+ .@total_prob = getelementofarray(getarg(0), 0);
+ .@initial_index = getarrayindex(getarg(0));
+ .@initial_index = .@initial_index ? .@initial_index : 1;
+ freeloop(true);
+
+ if (.@total_prob < 1 || getarg(1, false))
+ {
+ // first calculation, or forced re-calculation
+ .@total_prob = 0;
+ .@size = getarraysize(getarg(0));
+
+ for (.@i = .@initial_index + 1; .@i < .@size; .@i += 2) {
+ if (.@is_str) {
+ .@total_prob += max(1, atoi(getelementofarray(getarg(0), .@i)));
+ } else {
+ .@total_prob += max(1, getelementofarray(getarg(0), .@i));
+ }
+ }
+
+ // we cache on the first key
+ set(getelementofarray(getarg(0), 0), .@total_prob);
+ }
+
+ .@target_sum = rand(0, .@total_prob);
+
+ for (.@i = .@initial_index; .@sum < .@target_sum; .@i += 2) {
+ if (.@is_str) {
+ .@sum += atoi(getelementofarray(getarg(0), .@i + 1));
+ } else {
+ .@sum += getelementofarray(getarg(0), .@i + 1);
+ }
+
+ if (.@sum >= .@target_sum) {
+ break;
+ }
+ }
+
+ freeloop(false);
+ return getelementofarray(getarg(0), .@i);
+}
+
diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt
new file mode 100644
index 00000000..32e0f7bc
--- /dev/null
+++ b/npc/functions/asklanguage.txt
@@ -0,0 +1,72 @@
+// TMW2 script
+// Evol functions.
+// Author:
+// Reid, Jesusalva
+// Description:
+// Function setting the player language
+
+function script languagecode {
+ switch (Lang) {
+ case LANG_PTBR:
+ return "pt_BR";
+ case LANG_FR:
+ return "fr";
+ case LANG_DE:
+ return "de";
+ case LANG_ES:
+ return "es";
+ default:
+ return "en";
+ }
+}
+
+function script asklanguage {
+
+ dispbottom col("We need help with translations. [@@help://translate|Learn more@@]", 1);
+ switch (getarg(0, LANG_IN_SHIP))
+ {
+ case LANG_ON_SEA:
+ setarray .@messages$[0], "I hear you... (English)", // English
+ "Eu te ouço... (Português)", // Portuguese
+ "Je vous entends... (Français)", // French
+ "Ich höre euch... (Deutsch)", // German
+ "Te oigo... (Español)"; // Spanish
+ break;
+ case LANG_IN_SHIP:
+ setarray .@messages$[0], "I speak English.", // English
+ "Eu falo Português.", // Portuguese
+ "Je parle français.", // French
+ "Ich spreche Deutsch.", // German
+ "Hablo Español."; // Spanish
+ break;
+ default:
+ return;
+ }
+
+ setarray .@flags$[0], "flags/en",
+ "flags/pt_BR",
+ "flags/fr",
+ "flags/de",
+ "flags/es";
+
+ .@menustr$ = "";
+ .@separator$ = ":";
+
+ for (.@i = 0; .@i <= MAX_LANG; .@i++)
+ {
+ if (.@i == MAX_LANG) {
+ .@separator$ = "";
+ }
+ .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$;
+ }
+
+ select(.@menustr$);
+
+ .@lang = @menu - 1;
+
+ if (.@lang >= 0 || .@lang <= MAX_LANG) {
+ Lang = .@lang;
+ }
+
+ return;
+}
diff --git a/npc/functions/filters.txt b/npc/functions/filters.txt
new file mode 100644
index 00000000..01e3a856
--- /dev/null
+++ b/npc/functions/filters.txt
@@ -0,0 +1,126 @@
+// TMW2 scripts.
+// Authors:
+// Jesusalva
+// Description:
+// Several filters
+
+// filter_always( id )
+function script filter_always {
+ return true;
+}
+
+// filter_onlyme( id )
+function script filter_onlyme {
+ return (getarg(0) == getcharid(3));
+}
+
+// filter_notme( id )
+function script filter_notme {
+ return (getarg(0) != getcharid(3));
+}
+
+// filter_sameguild( id )
+function script filter_sameguild {
+ if (getcharid(2) < 1)
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameguildnotyou( id )
+function script filter_sameguildnotyou {
+ if (getcharid(2) < 1)
+ return false;
+ if (getarg(0) == getcharid(3))
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameparty( id )
+function script filter_sameparty {
+ if (getcharid(1) < 1 && getarg(0) != getcharid(3))
+ return false;
+ return (strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+}
+
+// filter_sameguildorparty( id )
+function script filter_sameguildorparty {
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_sameguildorpartynotyou( id )
+function script filter_sameguildorpartynotyou {
+ if (getarg(0) == getcharid(3))
+ return false;
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_hostile( id )
+function script filter_hostile {
+ //.@type=getunitdata(getarg(0), UDT_TYPE);
+ .@type=getunittype(getarg(0));
+ .@chkid=getarg(0);
+
+ // Players outside PVP
+ if (.@type == UNITTYPE_PC) {
+ getmapxy(.@m$, .@x, .@y, .@type, .@chkid);
+ if (!ispvpmap(.@m$))
+ return false;
+ // FIXME: We already have !(filter_sameguildorparty())
+ // We might be over-processing this
+ // Honor party flag
+ if (!getmapflag(.@mapa$, mf_pvp_noparty) &&
+ getcharid(1) == getcharid(1, strcharinfo(0, "", .@chkid)))
+ return false;
+ // Honor guild flag
+ if (!getmapflag(.@mapa$, mf_pvp_noguild) &&
+ getcharid(2) == getcharid(2, strcharinfo(0, "", .@chkid)))
+ return false;
+ }
+
+ // Monsters
+ if (.@type == UNITTYPE_MOB)
+ return true;
+
+ // NPCs
+ if (.@type == UNITTYPE_NPC)
+ return false;
+
+ // Homunculus
+ if (.@type == UNITTYPE_HOM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Pets
+ if (.@type == UNITTYPE_PET)
+ .@chkid=getunitdata(getarg(0), UDT_MASTERAID);
+
+ // Mercenaries
+ if (.@type == UNITTYPE_MER)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Elementals
+ if (.@type == UNITTYPE_ELEM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ //debugmes "filter_hostile: Filtering %d (original %d) (BL %d)", .@chkid, getarg(0), .@type;
+ // Players (and slaves)
+ return !(filter_sameguildorparty(.@chkid));
+}
+
+// filter_friendly( id )
+function script filter_friendly {
+ return !(filter_hostile(getarg(0)));
+}
+
+// filter_notboss( id )
+function script filter_notboss {
+ return (!(getunitdata(getarg(0), UDT_MODE) & MD_BOSS));
+}
+
diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt
new file mode 100644
index 00000000..b5214618
--- /dev/null
+++ b/npc/functions/goodbye.txt
@@ -0,0 +1,152 @@
+// Evol functions.
+// Authors:
+// gumi
+// Reid
+// Description:
+// script terminator functions
+
+
+
+// goodbye_msg
+// Tell a random goodbye sentence.
+// Variables:
+// .@rand = Random number between the number of "goodbye" choice.
+
+function script goodbye_msg {
+ setarray .byemsg$[0],
+ l("See you!"),
+ l("See you later!"),
+ l("See you soon!"),
+ l("Bye!"),
+ l("Farewell."),
+ l("Bye then!"),
+ l("Goodbye."),
+ l("Bye for now."),
+ l("Talk to you soon!"),
+ l("Talk to you later!"),
+ l("Have a good day!"),
+ l("Cheers!"),
+ l("Take care!");
+
+ return any_of(.byemsg$);
+}
+
+
+
+// cwarp
+// Closes the dialog, then warps the player.
+// You almost always want to use this instead of `warp`.
+// usage:
+// cwarp;
+// cwarp x, y;
+// cwarp map, x, y;
+
+function script cwarp {
+ .@map$ = getarg(0, "");
+ .@x = getarg(1, 0);
+ .@y = getarg(2, 0);
+
+ if (getargcount() > 0 && getargcount() < 3)
+ {
+ .@npc$ = strnpcinfo(0);
+ .@map$ = getvariableofnpc(.map$, .@npc$);
+ .@x = getarg(0);
+ .@y = getarg(1);
+ }
+
+ getmapxy .@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC; // get char location
+
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (getargcount() < 1)
+ {
+ warp .@pc_map$, .@pc_x, .@pc_y; // no arguments, just refresh
+ close;
+ }
+
+ if (.@map$ == .@pc_map$)
+ {
+ if (.@pc_x == .@x && .@pc_y == .@y)
+ {
+ close; // same location, don't move
+ }
+
+ else
+ {
+ slide .@x, .@y; // same map, slide instead of full warp
+ close;
+ }
+ }
+
+ warp .@map$, .@x, .@y; // different map, warp to given location
+ close;
+}
+
+
+
+// cshop
+// closes the dialog, then opens a shop
+// if no npc is given, calls "#<npc> $"
+
+function script cshop {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ shop getarg(0, "#" + strnpcinfo(0) + " $");
+ //close; => the shop buildin already sends close, and is a terminator itself
+}
+
+
+
+// cstorage
+// closes the dialog, then opens storage
+
+function script cstorage {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ openstorage;
+ close;
+}
+
+
+
+// bye
+// closes the dialog without waiting for the player to press close
+// can also display an emote
+
+function script bye {
+ .@emote = getarg(0, -1);
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ close;
+}
+
+
+
+// goodbye
+// same as bye, but also displays a canned message
+// can also display an emote
+
+function script goodbye {
+ npctalkonce(goodbye_msg());
+ bye getarg(0, -1);
+}
+
+
+
+// goodbye2
+// Waits for the player to press close, displays a canned message,
+// ends execution.
+// Can also display an emote
+
+function script goodbye2 {
+ .@emote = getarg(0, -1);
+
+ close2;
+ npctalkonce(goodbye_msg());
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ end;
+}
diff --git a/npc/functions/inc_sc_bonus.txt b/npc/functions/inc_sc_bonus.txt
new file mode 100644
index 00000000..8e6b6e5b
--- /dev/null
+++ b/npc/functions/inc_sc_bonus.txt
@@ -0,0 +1,72 @@
+// TMW-2 Script.
+// Author:
+// Jesusalva
+// Description:
+// Applies effects for INC_* (STR doesn't exist)
+// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE SC_FURY
+// Doesn't works: SC_STRUP
+// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE
+/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken)
+// PS. SC_FURY causes crit rate to increase
+//
+// Variables:
+// .@delay Second of buffing
+// .@type SC_*
+// .@min Min amount of type
+// .@max Max amount of type (optional)
+
+// SC_Bonus(delay, SC, min{, max})
+function script SC_Bonus {
+ .@delay=getarg(0);
+ .@type=getarg(1);
+ .@min=getarg(2);
+ .@max=getarg(3, .@min);
+ if (.@delay <= 0)
+ return false;
+
+ // Get the bonus value
+ if (.@min != .@max)
+ .@bonus=rand2(.@min, .@max);
+ else
+ .@bonus=.@min;
+
+ // Remaining time and effect conversion
+ .@v=getstatus(.@type, 1);
+ .@t=getstatus(.@type, 5);
+
+ // Convert remaining time to seconds, rounded down
+ if (.@t > 1000)
+ .@t=.@t/1000;
+ else
+ .@t=0;
+
+ // If there was effect previously, get ponderate average
+ if (.@v > 0)
+ .@v=ponderate_avg(.@bonus, .@delay, .@v, .@t);
+ else
+ .@v=.@bonus;
+
+ // Update time value to ms and to stack
+ .@t+=.@delay;
+ .@t*=1000;
+
+ // Debug print if needed
+ if (debug || $@GM_OVERRIDE)
+ debugmes "Effect %d (+%d percent) for %d ms", .@type, .@bonus, .@t;
+
+ // Restart the bonus
+ sc_end .@type;
+ sc_start .@type,.@t,.@v;
+ return true;
+}
+
+- script inc_sc_bonus -1,{
+OnUse:
+ SC_Bonus(@delay, @type, @min, @max);
+ @delay=0;
+ @type=0;
+ @min=0;
+ @max=0;
+ end;
+}
+
diff --git a/npc/functions/input.txt b/npc/functions/input.txt
new file mode 100644
index 00000000..0a510b74
--- /dev/null
+++ b/npc/functions/input.txt
@@ -0,0 +1,110 @@
+// Evol functions.
+// Author:
+// 4144
+// Jesusalva
+// Description:
+// Input utility functions
+// Variables:
+// none
+
+function script menuint {
+ deletearray .@vals;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ .@vals[.@cnt] = -1;
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return -1;
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
+function script menustr {
+ deletearray .@vals$;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals$[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return "";
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals$))
+ return "";
+
+ @menuret$ = .@vals$[@menu];
+ return @menuret$;
+}
+
+// menuint2(<array>)
+function script menuint2 {
+ .@menustr$="";
+
+ if (!(getdatatype(getarg(0)) & DATATYPE_VAR))
+ Exception("Inadequate argument type - Must be var", RB_DEFAULT|RB_ISFATAL);
+
+ copyarray(.@ar$, getarg(0), getarraysize(getarg(0)));
+
+ if (getarraysize(.@ar$) % 2 != 0)
+ Exception("Invalid array size: "+getarraysize(.@ar$), RB_DEFAULT|RB_ISFATAL);
+
+ freeloop(true);
+ for (.@f=0; .@f < getarraysize(.@ar$); .@f++) {
+ // String vs Int
+ if (.@f % 2 == 0) {
+ .@menustr$+=.@ar$[.@f]+":";
+ } else {
+ array_push(.@vals, atoi(.@ar$[.@f]));
+ }
+ }
+ freeloop(false);
+
+ // Do the request
+ // We have: .@vals and .@menustr$
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ //debugmes "Option %d", @menu;
+ //debugmes "Array size %d", getarraysize(.@vals);
+
+ if (@menu == 255)
+ return -1;
+
+ @menu-=1;
+ if (@menu < 0 || @menu > getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt
new file mode 100644
index 00000000..76cdad21
--- /dev/null
+++ b/npc/functions/inventoryplace.txt
@@ -0,0 +1,36 @@
+// Evol functions.
+// Authors:
+// Qwerty Dragon
+// Reid
+// Description:
+// Check if the player have enough place on his inventory to accept new items with arguments:
+// getarg(even numbers) item ID,
+// getarg(odd numbers) number of items,
+
+function script inventoryplace {
+
+ .@argc = getargcount();
+
+ if (.@argc % 2 != 0)
+ {
+ Exception("inventoryplace: Wrong argument count.", RB_SPEECH|RB_ISFATAL|RB_PLEASEREPORT|RB_DEBUGMES);
+ }
+
+ for (.@i = .@j = 0; .@i < .@argc; .@i += 2)
+ {
+ setarray .@item[.@j], getarg(.@i);
+ setarray .@amount[.@j], getarg(.@i + 1);
+ ++.@j;
+ }
+
+ if (!checkweight2(.@item, .@amount))
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("It looks like you can't carry anything else for now."),
+ l("You should come back when you have some free space.");
+
+ close;
+ }
+
+ return true;
+}
diff --git a/npc/functions/main.txt b/npc/functions/main.txt
index a3e222e8..eac02526 100644
--- a/npc/functions/main.txt
+++ b/npc/functions/main.txt
@@ -307,6 +307,13 @@ function script die {
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
/////////////////////////////////////////////
@@ -602,33 +609,7 @@ function script registercmd {
return;
}
-//////////////////////////////////////////////////////////////////////
-// maptimer("<map>", <tick>, "<npc>::<event>")
-function script maptimer {
- .@c = getunits(BL_PC, .@players, false, getarg(0));
- for (.@i = 0; .@i < .@c; .@i++) {
- addtimer(getarg(1), getarg(2), .@players[.@i]);
- }
- return .@i;
-}
-
-// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
-function script areatimer {
- // Legacy
- if (getargcount() > 7)
- .@ox=1;
- // Variables
- .@m$=getarg(.@ox); .@ox+=1;
- .@x1=getarg(.@ox); .@ox+=1;
- .@y1=getarg(.@ox); .@ox+=1;
- .@x2=getarg(.@ox); .@ox+=1;
- .@y2=getarg(.@ox); .@ox+=1;
- .@tk=getarg(.@ox); .@ox+=1;
- .@e$=getarg(.@ox); .@ox+=1;
- .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2);
- for (.@i = 0; .@i < .@c; .@i++) {
- addtimer(.@tk, .@e$, .@players[.@i]);
- }
- return .@i;
+function script iscollision {
+ return checknpccell(getarg(0), getarg(1), getarg(2), cell_chkpass);
}
diff --git a/npc/functions/math.txt b/npc/functions/math.txt
new file mode 100644
index 00000000..9c93fbb7
--- /dev/null
+++ b/npc/functions/math.txt
@@ -0,0 +1,123 @@
+// Evol functions.
+// Authors:
+// 4144
+// Reid
+// Description:
+// Math functions
+
+
+// abs(<int>)
+// returns the absolute value of the passed integer
+
+function script abs {
+ .@n = getarg(0);
+ return .@n >= 0 ? .@n : -.@n;
+}
+
+
+
+// lognbaselvl({<multiplicator>{, <min value>}})
+// returns BaseLevel * logn (BaseLevel * alpha).
+
+function script lognbaselvl {
+ .@alpha = getarg(0, 1);
+ .@min = getarg(1, 1);
+ .@ret = 0;
+ .@pc_level = BaseLevel * .@alpha;
+
+ while (.@pc_level >>= 1)
+ {
+ ++.@ret;
+ }
+ .@ret *= BaseLevel;
+
+ if (.@ret <= .@min)
+ {
+ .@ret = .@min;
+ }
+
+ return .@ret;
+}
+
+// log2(<int>)
+// returns the log base 2 of the passed integer, up to 20 (2**20=1.048.576) (round down always)
+
+function script log2 {
+ .@v=abs(getarg(0));
+ .@ok=0;
+ .@i=0;
+ if (.@v < 1)
+ return -1;
+
+ freeloop(true);
+ while (!.@ok) {
+ // exact match
+ if (2**.@i == .@v) {
+ .@ok=1;
+ // inexact match, or limit exceeded
+ } else if (2**.@i >= .@v || .@i > 20) {
+ .@ok=1;
+ .@i-=1; // round down
+ // not yet
+ } else {
+ .@i+=1;
+ }
+ }
+ freeloop(false);
+
+ return .@i;
+}
+
+
+// result is: lower < target <= higher
+// is_between ( lower, higher, target)
+function script is_between {
+ .@val=getarg(2);
+ .@min=getarg(0);
+ .@max=getarg(1);
+ return (.@min < .@val && .@val <= .@max);
+}
+
+
+// forces the equation: lower <= target <= higher.
+// Note it still works if higher and target values are swapped.
+// limit ( lower, target, higher)
+function script limit {
+ return max(getarg(0), min(getarg(1), getarg(2)));
+}
+
+
+// result is the ponderate average.
+// ponderate_avg ( arg1, sub1, arg2, sub2)
+function script ponderate_avg {
+ .@a1=getarg(0);
+ .@s1=getarg(1);
+ .@a2=getarg(2);
+ .@s2=getarg(3);
+
+ .@h1=.@a1*.@s1;
+ .@h2=.@a2*.@s2;
+ .@dd=.@s1+.@s2;
+
+ return (.@h1+.@h2)/.@dd;
+}
+
+// bitmask_count(<int>)
+// returns the number of bits set in <int> (max. 4096)
+
+function script bitmask_count {
+ .@n = getarg(0); // Number evaluated
+ .@p=0; // Bits set/unset
+ .@s=0; // Stack and Check
+ .@m=0; // Memory
+
+ // Loop only as needed
+ while (.@s < .@n) {
+ .@s=2**.@m;
+ if (.@n & .@s)
+ .@p++;
+ .@m++;
+ }
+ return .@p;
+}
+
diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt
new file mode 100644
index 00000000..612ab036
--- /dev/null
+++ b/npc/functions/npcmove.txt
@@ -0,0 +1,142 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Moving npc utility functions
+// Variables:
+// none
+
+function script initpath {
+ deletearray getvariableofnpc(.movepathcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathy, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathx, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 3)
+ {
+ set getvariableofnpc(.movepathcmd$[.@cnt], strnpcinfo(3)), getarg(.@f);
+ set getvariableofnpc(.movepathx[.@cnt], strnpcinfo(3)), getarg(.@f + 1);
+ set getvariableofnpc(.movepathy[.@cnt], strnpcinfo(3)), getarg(.@f + 2);
+ .@cnt ++;
+ }
+ //debugmes "array size: " + str(getarraysize(getvariableofnpc(.movepath, strnpcinfo(3))));
+ return;
+}
+
+function script domoveaction {
+ //debugmes "domoveaction: " + str(getvariableofnpc(.movepos, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return;
+ //debugmes "walking";
+ .@cmd$ = getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+ //debugmes "cmd: " + .@cmd$;
+
+ if (.@cmd$ == "move")
+ {
+ npcwalkto getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "dir")
+ {
+ setnpcdir getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "emote")
+ {
+ unitemote getnpcid(), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "class")
+ {
+ .class = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "warp")
+ {
+ movenpc strnpcinfo(3), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "goto")
+ {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 0;
+ }
+ else if (.@cmd$ == "rmove")
+ {
+ getmapxy(.@mapName$, .@x, .@y, 1);
+ npcwalkto .@x + getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), .@y + getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "speed")
+ {
+ .speed = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$ == "stand")
+ {
+ npcstand;
+ }
+ return 1;
+}
+
+function script movetonextpos {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait --;
+ //debugmes "wait";
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+ .@true = 1;
+ while (.@true)
+ {
+ .@true = 0;
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ //debugmes "movetonextpos: " + str(.@pos);
+ .@res = domoveaction(.@pos);
+ if (.@res == 1 || .@res == 2)
+ {
+ .@pos++;
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))))
+ .@pos = 0;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ if (.@res == 0 || .@res == 2)
+ {
+ .@true = 1;
+ }
+ }
+ return;
+}
+
+function script initialmove {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), 0;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ movetonextpos;
+ return;
+}
+
+function script getmovecmd {
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return "";
+ return getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+}
+
+function script domovestep {
+ if (isunitwalking())
+ {
+ initnpctimer;
+ end;
+ }
+ movetonextpos;
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt
new file mode 100644
index 00000000..0877b748
--- /dev/null
+++ b/npc/functions/npcmovegraph.txt
@@ -0,0 +1,489 @@
+// Evol functions.
+// Author:
+// Travolta
+// Description:
+// Moving npc utility functions (graph-based)
+// Variables:
+// none
+
+function script initmovegraph {
+ deletearray getvariableofnpc(.movegraphcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphlabels$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphweight, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphflags, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x2, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y2, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ set getvariableofnpc(.movegraphlabels$[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_x1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ if (!isstr(getarg(.@f, "label")))
+ {
+ set getvariableofnpc(.movepos_x2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ }
+ .@cnt ++;
+ }
+ return;
+}
+
+function script findmovegraphlabel {
+ if (!getargcount())
+ {
+ debugmes "findmovegraphlabel: no argument";
+ return -1;
+ }
+ if (!isstr(getarg(0)))
+ {
+ debugmes "findmovegraphlabel: need string argument";
+ return -1;
+ }
+
+ .@arg$ = getarg(0);
+ for (.@i = 0; .@i < getarraysize(getvariableofnpc(.movegraphlabels$, strnpcinfo(3))); .@i++)
+ {
+ if (getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) == .@arg$)
+ return .@i;
+ }
+
+ npcdebug "findmovegraphlabel: label not found: " + getarg(0);
+ return -1;
+}
+
+/* setmovegraphcmd(fromPositionLabel,toPositionLabel[,moveChanceWeight[,moveFlags]],postCommand, ...);
+ * This function manipulates NPC moving graph. Before calling it, make sure
+ * `initmovegraph' was called. The function accepts 3-5 parameters (many times):
+ * fromPositionLabel, toPositionLabel -- starting and ending position of NPC move
+ * moveChanceWeight -- positive integer, represents the chance of moving in given direction. (optional)
+ * moveFlags -- if .mg_flags & moveFlags != 0, move is possible. (optional)
+ * postCommand -- either "moveon" (start moving to next location straight after arriving from
+ * fromPositionLabel to toPositionLabel) or a semicolon-separated set of commands
+ * ("wait 3", "emote 5" etc, see `execmovecmd') that will be executed after arrival.
+ * The commands don't have to end with ";moveon", it's executed in the end by default.
+ */
+function script setmovegraphcmd {
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ .@from = findmovegraphlabel(getarg(.@f++));
+ .@to = findmovegraphlabel(getarg(.@f++));
+ .@weight = 1;
+ if (!isstr(getarg(.@f)))
+ .@weight = getarg(.@f++);
+ .@flags = 0xffff;
+ if (!isstr(getarg(.@f)))
+ .@flags = getarg(.@f++);
+ .@cmd$ = getarg(.@f++);
+ .@index = .@from * .@size + .@to; // emulation of 2d array
+ set getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)), .@cmd$;
+ set getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)), .@weight;
+ set getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)), .@flags;
+ }
+ return;
+}
+
+function script execmovecmd {
+
+ explode(.@cmd$, getarg(0), " ");
+
+ if (.@cmd$[0] == "moveon")
+ {
+ return 0;
+ }
+ else if (.@cmd$[0] == "dir")
+ {
+ .dir = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$[0] == "stand")
+ {
+ npcstand;
+ }
+ else if (.@cmd$[0] == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), atoi(.@cmd$[1]);
+ return 1;
+ }
+ else if (.@cmd$[0] == "emote")
+ {
+ unitemote getnpcid(), atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "class")
+ {
+ .class = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "warp")
+ {
+ .@pos = -1;
+ .@map$ = "";
+ .@pos_idx = 1;
+ if (getarraysize(.@cmd$) == 3)
+ {
+ .@map$ = .@cmd$[1];
+ .@pos_idx = 2;
+ }
+ .@pos = findmovegraphlabel(.@cmd$[.@pos_idx]);
+ if (.@pos != -1)
+ {
+ .@x = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ if (getstrlen(.@map$) > 0)
+ unitwarp getnpcid(), .@map$, .@x, .@y;
+ else
+ movenpc strnpcinfo(3), .@x, .@y;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ else
+ {
+ debugmes "execmovecmd: unknown WARP destination label: " + .@cmd$[1];
+ }
+ }
+ else if (.@cmd$[0] == "call")
+ {
+ switch (getarraysize(.@cmd$))
+ {
+ case 1:
+ debugmes "execmovecmd: CALL command needs some parameters";
+ return 0;
+ case 2:
+ return callfunc(.@cmd$[1]);
+ break;
+ case 3:
+ return callfunc(.@cmd$[1], .@cmd$[2]);
+ case 4:
+ default:
+ return callfunc(.@cmd$[1], .@cmd$[2], .@cmd$[3]);
+ }
+ }
+ else if (.@cmd$[0] == "speed")
+ {
+ .speed = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "say")
+ {
+ deletearray .@cmd$[0], 1;
+ .@msg$=implode(.@cmd$, " ");
+ if (.@msg$ != "" && .@msg$ != " ")
+ npctalk .@msg$;
+ else
+ debugmes "Invalid message passed to execmovecmd/npctalk";
+ }
+ else if (.@cmd$[0] == "debugmes")
+ {
+ deletearray .@cmd$[0], 1;
+ debugmes implode(.@cmd$, " ");
+ }
+ else if (.@cmd$[0] == "flags")
+ {
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), axtoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "flags_0")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags &= ~axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else if (.@cmd$[0] == "flags_1")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags |= axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else
+ {
+ debugmes "Unknown move graph cmd: " + .@cmd$[0];
+ }
+ return 0;
+}
+
+function script getnextmovecmd {
+ .@cmds$ = getvariableofnpc(.nextcmd$, strnpcinfo(3));
+ .@firstCmd$ = .@cmds$;
+ .@restCmd$ = "moveon";
+ .@index = strpos(.@cmds$, ";");
+ if (.@index >= 0)
+ {
+ .@firstCmd$ = substr(.@cmds$, 0, .@index - 1);
+ .@restCmd$ = substr(.@cmds$, .@index + 1, getstrlen(.@cmds$) - 1);
+ }
+ // npcdebug "firstCmd = " + .@firstCmd$ + " restCmd = " + .@restCmd$;
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@restCmd$;
+ return strip(.@firstCmd$);
+}
+
+// getrandompoint(x1,y1,x2,y2)
+// -- Get a random walkable point within a map rectangle
+// x1, y1 -- top-left corner of rectangle
+// x2, y2 -- bottom-right corner of rectangle
+// Returns 0 on success and -1 on error;
+// Since we cannot return multiple values, the random
+// coordinates are stored in NPC variables .move__rand_x, .move__rand_y
+function script getrandompoint {
+ if (getargcount() < 4)
+ {
+ debugmes "error: getrandompoint(x1, y1, x2, y2) takes 4 arguments";
+ return -1;
+ }
+
+ .@max_pokes = 10;
+ .@x1 = getarg(0);
+ .@y1 = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+ .@rx = -1; .@ry = -1;
+
+ getmapxy(.@map$, .@cx, .@cy, 1); // npc location
+
+ // let's try max_pokes random cells
+ for (.@poke = 0; .@poke < .@max_pokes; .@poke++)
+ {
+ .@rx = rand(.@x1, .@x2);
+ .@ry = rand(.@y1, .@y2);
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ }
+
+ // we check each cell from random middle point to the end
+ for (;.@rx <= .@x2; .@rx++)
+ {
+ for (;.@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ .@ry = .@y1;
+ }
+
+ // we check the rectangle from beginning to end
+ for (.@rx = .@x1; .@rx <= .@x2; .@rx++)
+ for (.@ry = .@y1; .@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+
+ // finally, if we don't find anything
+ debugmes "error: getrandompoint: cannot find walkable cell in rectangle [(" + .@x1 + "," + .@y1 + ") , (" + .@x2 + "," + .@y2 + ")]";
+ return -1;
+
+L_Found:
+ set getvariableofnpc(.move__rand_x, strnpcinfo(3)), .@rx;
+ set getvariableofnpc(.move__rand_y, strnpcinfo(3)), .@ry;
+ return 0;
+}
+
+// wrapper function for npcwalkto. It can accept 4 parameters.
+// If #3 and #4 params are set, the walkto location is chosen
+// from rectangle (x1,y1,x2,y2).
+// It sets the npc variables .move_target_x, .move_target_y
+// that are used to resume NPC walking
+// Returns 1 if walking is possible, 0 otherwise;
+function script mg_npcwalkto {
+ if (getargcount() < 2)
+ {
+ debugmes "usage: mg_npcwalkto(x1,y1[,x2,y2])";
+ return -1;
+ }
+
+ .@x = getarg(0);
+ .@y = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+
+ if (getargcount() >= 4 && .@x2 > 0 && .@y2 > 0)
+ if (!getrandompoint(.@x, .@y, .@x2, .@y2))
+ {
+ .@x = getvariableofnpc(.move__rand_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move__rand_y, strnpcinfo(3));
+ }
+ else
+ return 0;
+
+ if (npcwalkto(.@x, .@y))
+ {
+ set getvariableofnpc(.move_target_x, strnpcinfo(3)), .@x;
+ set getvariableofnpc(.move_target_y, strnpcinfo(3)), .@y;
+ return 1;
+ }
+ return 0;
+}
+
+function script movetonextpoint {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait--;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+
+ .@nextcmd$ = "";
+ while (.@nextcmd$ != "moveon")
+ {
+ .@nextcmd$ = getnextmovecmd();
+ npcdebug " " + .@nextcmd$;
+ if (execmovecmd(.@nextcmd$))
+ return;
+ }
+
+ // choose a random path from all possible paths
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ .@curr_flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@cur = 0;
+ .@weight_sum = 0;
+ // .@dbg$ = getvariableofnpc(.movegraphlabels$[.@pos], strnpcinfo(3)) + ": ";
+
+ for (.@i = 0; .@i < .@size; .@i++)
+ {
+ .@index = .@pos * .@size + .@i;
+ .@cmd$ = getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3));
+ .@flags = getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3));
+ if (.@cmd$ != "" &&
+ .@curr_flags & .@flags)
+ {
+ .@nextpos[.@cur] = .@i;
+ .@weights[.@cur] = getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3));
+ // .@dbg$ += getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) + "=" + .@weights[.@cur] + " ";
+ .@weight_sum += .@weights[.@cur];
+ .@cur++;
+ }
+ }
+ // npcdebug .@dbg$;
+
+ if (!.@weight_sum)
+ {
+ npcdebug("error: cannot pick next walk point. flags=" +
+ getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ return;
+ }
+
+ .@pick_tries = 0;
+L_TryPick:
+ // pick a random number based on weight_sum
+ .@rnd = rand(.@weight_sum);
+ .@k = -1; .@weight_sum = 0;
+ while (.@rnd >= .@weight_sum)
+ {
+ .@k++;
+ .@weight_sum += .@weights[.@k];
+ }
+
+ .@next_idx = .@nextpos[.@k];
+ .@next_x1 = getvariableofnpc(.movepos_x1[.@next_idx], strnpcinfo(3));
+ .@next_y1 = getvariableofnpc(.movepos_y1[.@next_idx], strnpcinfo(3));
+ .@next_x2 = getvariableofnpc(.movepos_x2[.@next_idx], strnpcinfo(3));
+ .@next_y2 = getvariableofnpc(.movepos_y2[.@next_idx], strnpcinfo(3));
+
+ if (!mg_npcwalkto(.@next_x1, .@next_y1, .@next_x2, .@next_y2))
+ {
+ if (.@pick_tries < 10)
+ {
+ .@pick_tries++;
+ goto L_TryPick;
+ }
+
+ // move to a nearby position
+ .@x1 = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y1 = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ .@x2 = getvariableofnpc(.movepos_x2[.@pos], strnpcinfo(3));
+ .@y2 = getvariableofnpc(.movepos_y2[.@pos], strnpcinfo(3));
+ mg_npcwalkto(.@x1, .@y1, .@x2, .@y2);
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), "wait 1";
+
+ return;
+ }
+
+ if (getvariableofnpc(.debug, strnpcinfo(3)))
+ {
+ getmapxy(.@map$, .@cx, .@cy, 1);
+ .@dist = distance(.@cx, .@cy, .@next_x1, .@next_y1);
+ npcdebug("moving to " + getvariableofnpc(.movegraphlabels$[.@next_idx], strnpcinfo(3)) +
+ " ("+ getvariableofnpc(.move_target_x, strnpcinfo(3)) +
+ "," + getvariableofnpc(.move_target_y, strnpcinfo(3)) +
+ ") [distance=" + .@dist +
+ "] flags=" + getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ }
+
+ .@nextcmd$ = getvariableofnpc(.movegraphcmd$[.@pos * .@size + .@next_idx], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@next_idx;
+ return;
+}
+
+// initial actions for npc when using move graphs.
+// function can accept 2 arguments:
+// 1: action sequence, for example "speed 200; dir 4". Default is "moveon"
+// 2: start point label. Default is #0 from move graph labels
+function script firstmove {
+ .@nextcmd$ = getarg(0, "moveon");
+ .@initpos = findmovegraphlabel(getarg(1, ""));
+ if (.@initpos < 0) .@initpos = 0;
+
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@initpos;
+ movenpc strnpcinfo(3), getvariableofnpc(.movepos_x1[.@initpos], strnpcinfo(3)),
+ getvariableofnpc(.movepos_y1[.@initpos], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), 0xffff;
+ movetonextpoint;
+ return;
+}
+
+function script npc_pausemove {
+ stopnpctimer;
+ .@move_after = 0;
+
+ if (isunitwalking())
+ {
+ .@move_after = 1;
+ npcwalkto .x, .y;
+ npcstop;
+ }
+ set getvariableofnpc(.move_after_pause, strnpcinfo(3)), .@move_after;
+
+ return 0;
+}
+
+function script npc_resumemove {
+ startnpctimer;
+
+ if (getvariableofnpc(.move_after_pause, strnpcinfo(3)))
+ {
+ .@x = getvariableofnpc(.move_target_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move_target_y, strnpcinfo(3));
+ npcwalkto .@x, .@y;
+ }
+
+ return 0;
+}
+
+// npc_turntoxy(x,y)
+// turn npc toward an object at position (x,y)
+function script npc_turntoxy {
+ .@target_x = getarg(0);
+ .@target_y = getarg(1);
+ .@dx = abs(.@target_x - .x);
+ .@dy = abs(.@target_y - .y);
+
+ if (.@dx > .@dy)
+ .dir = .@target_x >= .x ? 6 : 2;
+ else
+ .dir = .@target_y >= .y ? 0 : 4;
+
+ return 0;
+}
+
+function script dographmovestep {
+ if (!isunitwalking())
+ {
+ movetonextpoint;
+ }
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt
new file mode 100644
index 00000000..96e69fde
--- /dev/null
+++ b/npc/functions/permissions.txt
@@ -0,0 +1,35 @@
+// Evol scripts.
+// Author:
+// gumi
+// Description:
+// checks player permissions
+// ** admins are implicitly everything
+
+// administrator
+function script is_admin {
+ return has_permission(PERM_USE_ALL_COMMANDS, getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// any staff member
+function script is_trusted {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ has_permission("show_client_version", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// developer
+function script is_dev {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ has_permission(PERM_RECEIVE_REQUESTS, getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// event coordinator
+function script is_evtc {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ can_use_command("@monster", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// game master
+function script is_gm {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ can_use_command("@jail", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
diff --git a/npc/functions/random-talk.txt b/npc/functions/random-talk.txt
new file mode 100644
index 00000000..e6b6bee8
--- /dev/null
+++ b/npc/functions/random-talk.txt
@@ -0,0 +1,207 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Random dialog for various random NPCs.
+
+// Functions:
+// hello
+// moubootalk
+// villagertalk
+// sailortalk
+// legiontalk
+// asleep
+
+// Evol authors (some strings and code):
+// Reid
+// Akko Teru
+// Qwerty Dragon
+
+function script hello {
+
+ switch (rand2(3)) {
+ case 0:
+ npctalkonce(l("Heya!"));
+ break;
+ case 1:
+ npctalkonce(l("Hi."));
+ break;
+ case 2:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas!"));
+ else
+ npctalkonce(l("Nice day to you."));
+ break;
+ }
+
+ return;
+}
+
+function script moubootalk {
+ switch (rand2(4)) {
+ case 0:
+ npctalkonce(l("Moooooo!"));
+ break;
+ case 1:
+ npctalkonce(l("Moo!"));
+ break;
+ case 2:
+ npctalkonce(l("Moooooooooooo!"));
+ break;
+ case 3:
+ npctalkonce(l("Moooo!"));
+ break;
+ }
+ return;
+}
+
+function script sailortalk {
+
+ .@rand = rand2(8);
+ if (.@rand == 0) goodbye;
+ if (.@rand == 1) npctalkonce(l("Arr, I'm bored!"));
+ if (.@rand == 2) npctalkonce(l("Hey! Good to hear from you!"));
+ if (.@rand == 3) npctalkonce(l("Yarr arr!"));
+ if (.@rand == 4) {
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, arr yarr!!"));
+ else {
+ speech(
+ l("A sunny and hot day,"),
+ l("a quiet place,"),
+ l("a ground!"),
+ l("What else do you need?"));
+ }
+ close;
+ }
+ if (.@rand == 5) npctalkonce(l("A-hoy matey!"));
+ if (.@rand == 6) npctalkonce(l("Arr!"));
+ if (.@rand == 7) npctalkonce(l("Howdy?"));
+
+ // just to be sure
+ closedialog;
+ close;
+ end;
+}
+
+function script villagertalk {
+
+ function darn_or_smile {
+ .@darn = rand(42);
+
+ if (.@darn < 26) {
+ emotion E_JOY;
+ hello;
+ } else if (.@darn > 26) {
+ emotion E_LOOKAWAY;
+ goodbye;
+ } else {
+ npctalkonce(l("Stop it!"));
+ }
+ return;
+ }
+
+ switch (rand2(4)) {
+ case 0:
+ darn_or_smile();
+ break;
+ case 1:
+ npctalkonce(l("It is a sunny day, don't you think?"));
+ break;
+ case 2:
+ npctalkonce(l("Go fly a kite."));
+ break;
+ case 3:
+ npctalkonce(l("I just want to live my life in peace."));
+ break;
+ default:
+ emotion E_HAPPY;
+ break;
+ }
+
+ return;
+}
+
+function script legiontalk {
+ switch (rand2(15)) {
+ case 0:
+ npctalkonce(l("Do I look like a tree? I feel like one."));
+ //speech(
+ // l("Do you feel too weak even to do damage to this areas wishy-washy wildlife?"),
+ // l("Then concentrate your anger upon the trees hereabouts, you will gain experience whilst leveling your sword skill on them."),
+ // l("Oh, and a fruit may even fall for you if you are lucky! But stay alert to pick up your drops."));
+ //close;
+ break;
+ case 1:
+ npctalkonce(l("I'm a little busy right now."));
+ break;
+ case 2:
+ npctalkonce(l("Not in the mood to chat."));
+ break;
+ case 3:
+ npctalkonce(l("My breath smells bad."));
+ break;
+ case 4:
+ npctalkonce(l("Don't distract me, I have to stay alert."));
+ break;
+ case 5:
+ npctalkonce(l("Give me some space."));
+ break;
+ case 6:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, adventurer."));
+ else
+ npctalkonce(l("Can you please go away?"));
+ break;
+ case 7:
+ npctalkonce(l("Can't talk right now, I'm on patrol duty."));
+ break;
+ case 8:
+ npctalkonce(l("What're you looking at?!"));
+ break;
+ case 9:
+ npctalkonce(l("I can't stay here and talk all day. I have a job to do."));
+ break;
+ case 10:
+ npctalkonce(l("Keep moving pal."));
+ break;
+ case 11:
+ npctalkonce(l("So you think you're tough? A warrior must also be loyal and patient."));
+ break;
+ case 12:
+ emotion E_LOOKAWAY;
+ break;
+ case 13:
+ npctalkonce(l("Practice! There are no secrets to becoming a warrior."));
+ break;
+ case 14:
+ npctalkonce(l("There is no honor in fighting a weak opponent."));
+ break;
+ }
+
+ return;
+}
+
+function script asleep {
+ switch(rand2(5)) {
+ case 0: npctalkonce(l("Zzzzzzzzz...")); break;
+ case 1: npctalkonce(l("Rrrr... Pchhhh...")); break;
+ case 2: npctalkonce(l("Ggrmm... Grmmmm...")); break;
+ case 3: npctalkonce(l("Hm... Shhhh...")); break;
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
+
+function script studenttalk {
+ switch(rand2(6)) {
+ case 0: npctalkonce(l("I want to sleep...")); break;
+ case 1: npctalkonce(l("I have homework to do...")); break;
+ case 2: npctalkonce(l("I need to finish studying for my test...")); break;
+ case 3: npctalkonce(l("Ah, the Professors will get mad at me again...")); break;
+ case 4: npctalkonce(l("I'm a little busy right now.")); break;
+
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
diff --git a/npc/functions/string.txt b/npc/functions/string.txt
new file mode 100644
index 00000000..2a38d90d
--- /dev/null
+++ b/npc/functions/string.txt
@@ -0,0 +1,211 @@
+// Evol Script
+// Author: Gumi
+
+// safe string manipulation functions
+// ** does not require PCRE
+
+
+// str(<int>)
+// returns whatever is passed, converted to string
+
+function script str {
+ return "" + getarg(0);
+}
+
+
+
+// startswith("<string>", "<search>")
+// returns true if <string> begins with <search>
+
+function script startswith {
+ return substr(getarg(0), 0, getstrlen(getarg(1)) - 1) == getarg(1);
+}
+
+
+
+// endswith("<string>", "<search>")
+// returns true if <string> ends with <search>
+
+function script endswith {
+ .@t = getstrlen(getarg(0)); // total length
+ .@n = getstrlen(getarg(1)); // substring length
+ return substr(getarg(0), .@t - .@n, .@t - 1) == getarg(1);
+}
+
+
+
+// capitalize("<string>")
+// returns <string> with its first letter capitalized
+
+function script capitalize {
+ return setchar(getarg(0), strtoupper(charat(getarg(0), 0)), 0);
+}
+
+
+
+// titlecase("<string>" {, "<delimiter>" {, <camel>}})
+// returns <string> with the first letter of each word capitalized
+// if <camel> is true, the string is joined in a camelCase fashion
+
+function script titlecase {
+ .@delimiter$ = getarg(1, " ");
+ .@c = getarg(2, 0);
+ explode(.@words$, getarg(0), .@delimiter$);
+
+ for (.@i = (.@c ? 1 : 0); .@i < 255; ++.@i)
+ {
+ if (.@words$[.@i] == "")
+ {
+ break;
+ }
+
+ .@words$[.@i] = setchar(.@words$[.@i], strtoupper(charat(.@words$[.@i], 0)), 0);
+ }
+
+ return implode(.@words$, (.@c ? "" : .@delimiter$));
+}
+
+
+
+// camelcase("<string" {, "<delimiter>"})
+
+function script camelcase {
+ return titlecase(getarg(0), getarg(1, " "), true);
+}
+
+
+
+// zfill("<string>" {, <width> {, "<padding>"}})
+// returns <string> padded to the left with <padding> up to width
+
+function script zfill {
+ .@str$ = getarg(0);
+ .@width = getarg(1, 8);
+ .@padding$ = getarg(2, "0");
+
+ for (.@s = getstrlen(.@str$); .@s < .@width; ++.@s)
+ {
+ .@str$ = .@padding$ + .@str$;
+ }
+
+ return .@str$;
+}
+
+
+
+// format_number(<integer> {, "<separator>"})
+// formats a number properly
+
+function script format_number {
+ .@number$ = str(getarg(0));
+ .@len = getstrlen(.@number$);
+ .@separator$ = getarg(1, ",");
+
+ if (getargcount() < 2 && playerattached()) {
+ // get from user language
+ switch (Lang) {
+ case LANG_FR: .@separator$ = " "; break; // French
+ case LANG_DE: .@separator$ = "."; break; // Germanic
+ case LANG_PTBR: .@separator$ = "."; break; // Brazilian
+ default: .@separator$ = ","; // English (default)
+ }
+ }
+
+ for (.@i = .@len - 3; .@i > 0; .@i -= 3) {
+ .@number$ = insertchar(.@number$, .@separator$, .@i);
+ }
+
+ return .@number$;
+}
+
+
+
+// fnum(<integer>)
+// alias for format_number
+
+function script fnum {
+ return format_number(getarg(0));
+}
+
+
+
+// strip("<string>")
+// removes spaces at the start and end
+
+function script strip {
+ .@s$ = getarg(0);
+ if (.@s$ == "") {
+ return "";
+ }
+ .@start = 0;
+ .@end = getstrlen(.@s$) - 1;
+ for (.@i = .@start; .@i < .@end; .@i++)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@start++;
+ }
+ }
+ for (.@i = .@end; .@i >= .@start; .@i--)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@end--;
+ }
+ }
+ //debugmes "STRIP.DEBUG MODE ENABLED BY JESUSALVA. PASSING SUBSTRING PARAMS";
+ //debugmes "String \""+.@s$+"\" from "+str(.@start)+" to "+str(.@end);
+ return substr(.@s$, .@start, .@end);
+}
+
+
+
+// reverse("<string>")
+// returns <string> reversed
+
+function script reverse {
+ .@str$ = getarg(0);
+ .@len = getstrlen(.@str$);
+
+ for (.@i = 0; .@i < (.@len / 2); ++.@i) {
+ .@tmp$ = charat(.@str$, .@i);
+ .@str$ = setchar(.@str$, charat(.@str$, (.@len - 1 - .@i)), .@i); // a <= b
+ .@str$ = setchar(.@str$, .@tmp$, (.@len - 1 - .@i)); // b <= a
+ }
+
+ return .@str$;
+}
+
+
+
+// repeat("<string>", <multiplier>)
+// repeats <string> many times and returns it
+
+function script repeat {
+ .@mul = getarg(1);
+
+ for (.@i = 0; .@i < .@mul; ++.@i) {
+ .@str$ += getarg(0);
+ }
+
+ return .@str$;
+}
+
+
+
+// shuffle("<string>")
+// returns <string> shuffled
+
+function script shuffle {
+ .@str$ = getarg(0);
+
+ for (.@len = getstrlen(.@str$); .@len > 0; --.@len) {
+ .@rnd = rand(.@len);
+ .@out$ += charat(.@str$, .@rnd);
+ .@str$ = delchar(.@str$, .@rnd);
+ }
+
+ return .@out$;
+}
diff --git a/npc/functions/time.txt b/npc/functions/time.txt
index 21b94ac5..e6e4c70a 100755
--- a/npc/functions/time.txt
+++ b/npc/functions/time.txt
@@ -1,159 +1,117 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+function script now {
+ return gettimetick(2);
+}
-function script time_stamp {
- // local variables
- // if there is reasonable demand, these might be exported
- // (that is what the builtin is likely to do)
- @ts_year = gettime(7);
- @ts_month = gettime(6);
- @ts_mday = gettime(5);
- //set @ts_wday, gettime(4);
- @ts_hour = gettime(3);
- @ts_minute = gettime(2);
- @ts_second = gettime(1);
-
- // locals used to generate leading zeroes
- @ts_month_pad$ = "";
- @ts_mday_pad$ = "";
- @ts_hour_pad$ = "";
- @ts_minute_pad$ = "";
- @ts_second_pad$ = "";
-
- if (@ts_month < 10)
- @ts_month_pad$ = "0";
- if (@ts_mday < 10)
- @ts_mday_pad$ = "0";
- if (@ts_hour < 10)
- @ts_hour_pad$ = "0";
- if (@ts_minute < 10)
- @ts_minute_pad$ = "0";
- if (@ts_second < 10)
- @ts_second_pad$ = "0";
-
- @ts_date$ = @ts_year + "-" + @ts_month_pad$ + @ts_month + "-" + @ts_mday_pad$ + @ts_mday;
- @ts_time$ = @ts_hour_pad$ + @ts_hour + ":" + @ts_minute_pad$ + @ts_minute + ":" +@ts_second_pad$ + @ts_second;
-
- // cleanup
- @ts_year = 0;
- @ts_month = 0;
- @ts_mday = 0;
- @ts_hour = 0;
- @ts_minute = 0;
- @ts_second = 0;
- @ts_month_pad$ = "";
- @ts_mday_pad$ = "";
- @ts_hour_pad$ = "";
- @ts_minute_pad$ = "";
- @ts_second_pad$ = "";
-
- return;
+// Returns current time. A SQL update superseeded this.
+// santime( )
+function script santime {
+ return gettimetick(2);
}
+function script time_from_ms {
+ return now() + (getarg(0) / 1000);
+}
+function script time_from_seconds {
+ return now() + getarg(0);
+}
+function script time_from_minutes {
+ return now() + (getarg(0) * 60);
+}
+function script time_from_hours {
+ return now() + (getarg(0) * 3600);
+}
+
+function script time_from_days {
+ return now() + (getarg(0) * 86400);
+}
-function script HumanTime {
- @time$ = "now";
- if(@seconds) set @ms, @ms + (@seconds * 1000);
- if(@minutes) set @ms, @ms + (@minutes * 60000);
- if(@days) set @ms, @ms + (@days * 1440000);
- if(@ms < 1000) goto L_Millis; // under 1 second we have nothing to count
- @seconds = @ms / 1000;
- @ms = @ms % 1000;
- if(@seconds < 60) goto L_Seconds;
- @minutes = @seconds / 60;
- @seconds = @seconds % 60;
- if(@minutes < 60) goto L_Minutes;
- @hours = @minutes / 60;
- @minutes = @minutes % 60;
- if(@hours < 24) goto L_Hours;
- @days = @hours / 24;
- @hours = @hours % 24;
- if(@days) goto L_Days;
- goto L_Clean;
-
-L_Millis:
- @time$ = @ms + "ms";
- return;
-
-L_Seconds:
- @unit$ = "second";
- if(@seconds > 1) set @unit$, "seconds";
- @unit2$ = "millisecond";
- if(@ms > 1) set @unit2$, "milliseconds";
- @time$ = @seconds + " " + @unit$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit2$;
- goto L_Clean;
-
-L_Minutes:
- @unit$ = "minute";
- if(@minutes > 1) set @unit$, "minutes";
- @unit2$ = "second";
- if(@seconds > 1) set @unit2$, "seconds";
- @unit3$ = "millisecond";
- if(@ms > 1) set @unit3$, "milliseconds";
- @time$ = @minutes + " " + @unit$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit2$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit3$;
- goto L_Clean;
-
-L_Hours:
- @unit$ = "hour";
- if(@hours > 1) set @unit$, "hours";
- @unit2$ = "minute";
- if(@minutes > 1) set @unit2$, "minutes";
- @unit3$ = "second";
- if(@seconds > 1) set @unit3$, "seconds";
- @unit4$ = "millisecond";
- if(@ms > 1) set @unit4$, "milliseconds";
- @time$ = @hours + " " + @unit$;
- @separator$ = " and ";
- if(@seconds || @ms) set @separator$, ", ";
- if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit2$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$;
- goto L_Clean;
-
-L_Days:
- @unit$ = "day";
- if(@hours > 1) set @unit$, "days";
- @unit2$ = "hour";
- if(@hours > 1) set @unit2$, "hours";
- @unit3$ = "minute";
- if(@minutes > 1) set @unit3$, "minutes";
- @unit4$ = "second";
- if(@seconds > 1) set @unit4$, "seconds";
- @unit5$ = "millisecond";
- if(@ms > 1) set @unit5$, "milliseconds";
- @time$ = @days + " " + @unit$;
- @separator$ = " and ";
- if(@minutes || @seconds || @ms) set @separator$, ", ";
- if(@hours) set @time$, @time$ + @separator$ + @hours + " " + @unit2$;
- @separator$ = " and ";
- if(@seconds || @ms) set @separator$, ", ";
- if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit3$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$;
- goto L_Clean;
-
-L_Clean:
- @unit$ = "";
- @unit2$ = "";
- @unit3$ = "";
- @unit4$ = "";
- @unit5$ = "";
- @seconds = 0;
- @minutes = 0;
- @hours = 0;
- @days = 0;
- @separator$ = "";
- return;
+// FuzzyTime(<unix timestamp>{, <options>{, <precision>}})
+// gives time in a human-readable format
+//
+// <options> is bitmasked:
+// 1 do not show "ago" when in past
+// 2 do not show "in" when in the future
+// 4 show "from now" instead of "in" when in the future
+//
+// <precision> is the number of units to show,
+// by default uses two (eg. 2m30s or 1h20m).
+// Use '99' for max precision
+
+function script FuzzyTime {
+ .@future = getarg(0, now());
+ .@options = getarg(1, 3);
+ .@precision = getarg(2, 2);
+ .@diff = (.@future - now());
+
+ // check if in the past, or in the future
+ if (.@diff < 0) {
+ .@diff *= -1;
+ .@past = true;
+ }
+
+ .@diff = max(1, .@diff);
+
+ if (.@diff >= 31536000) {
+ .@years = (.@diff / 31536000);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 31536000));
+ .@ret$ += sprintf("%d %s", .@years, (.@years > 1 ? "years" : "year"));
+ }
+
+ if (.@diff >= 86400) {
+ .@days = (.@diff / 86400);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 86400));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : " and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@days, (.@days > 1 ? "days" : "day"));
+ }
+
+ if (.@diff >= 3600) {
+ .@hours = (.@diff / 3600);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 3600));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@hours, (.@hours > 1 ? "hours" : "hour"));
+ }
+
+ if (.@diff >= 60) {
+ .@minutes = (.@diff / 60);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 60));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@minutes, (.@minutes > 1 ? "minutes" : "minute"));
+ }
+
+ if (.@diff >= 1) {
+ if (++.@s > 1) {
+ .@ret$ += (.@s >= 3 ? ", " : " ") + "and ";
+ }
+
+ .@ret$ += sprintf("%d %s", .@diff, (.@diff > 1 ? "seconds" : "second"));
+ }
+
+ if (.@past && !(.@options & 1)) {
+ .@ret$ += " ago";
+ }
+
+ if (!(.@past) && !(.@options & 2)) {
+ .@ret$ = ((.@options & 4) ? sprintf("%s from now", .@ret$) : sprintf("in %s", .@ret$));
+ }
+
+ return .@ret$;
}
diff --git a/npc/functions/timer.txt b/npc/functions/timer.txt
new file mode 100644
index 00000000..27e09f13
--- /dev/null
+++ b/npc/functions/timer.txt
@@ -0,0 +1,89 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+
+// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer {
+ // Legacy
+ if (getargcount() > 7)
+ .@ox=1;
+ // Variables
+ .@m$=getarg(.@ox); .@ox+=1;
+ .@x1=getarg(.@ox); .@ox+=1;
+ .@y1=getarg(.@ox); .@ox+=1;
+ .@x2=getarg(.@ox); .@ox+=1;
+ .@y2=getarg(.@ox); .@ox+=1;
+ .@tk=getarg(.@ox); .@ox+=1;
+ .@e$=getarg(.@ox); .@ox+=1;
+ .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(.@tk, .@e$, .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areadeltimer("<map>", <x1>, <y1>, <x2>, <y2>, "<npc>::<event>")
+function script areadeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(5), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areatimer2("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(6), .@players[.@i]);
+ addtimer(getarg(5), getarg(6), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// addtimer2(<tick>, "<npc>::<event>")
+function script addtimer2 {
+ deltimer(getarg(1));
+ addtimer(getarg(0), getarg(1));
+ return;
+}
+
+
+// maptimer("<map>", <tick>, "<npc>::<event>")
+function script maptimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// Same as maptimer() but deletes any previously running timer
+// maptimer2("<map>", <tick>, "<npc>::<event>")
+function script maptimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(2), .@players[.@i]);
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// mapdeltimer("<map>", "<npc>::<event>")
+function script mapdeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(1), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// partytimer("<map>", <tick>, "<npc>::<event>", partyid)
+function script partytimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ if (getcharid(2, strcharinfo(0,"",.@players[.@i]) ) == getarg(3))
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+