summaryrefslogtreecommitdiff
path: root/npc/functions
diff options
context:
space:
mode:
Diffstat (limited to 'npc/functions')
-rw-r--r--npc/functions/RNGesus.txt23
-rw-r--r--npc/functions/array.txt410
-rw-r--r--npc/functions/asklanguage.txt80
-rw-r--r--npc/functions/asleep.txt19
-rw-r--r--npc/functions/bank.txt134
-rw-r--r--npc/functions/barber.txt110
-rw-r--r--npc/functions/beds.txt30
-rw-r--r--npc/functions/clientversion.txt20
-rw-r--r--npc/functions/confused-tree-dict.txt560
-rw-r--r--npc/functions/doors.txt59
-rw-r--r--npc/functions/fishing.txt294
-rw-r--r--npc/functions/game-rules.txt59
-rw-r--r--npc/functions/goodbye.txt152
-rw-r--r--npc/functions/hammocks.txt50
-rw-r--r--npc/functions/harbours.txt39
-rw-r--r--npc/functions/hello.txt23
-rw-r--r--npc/functions/input.txt66
-rw-r--r--npc/functions/inventoryplace.txt37
-rw-r--r--npc/functions/legiontalk.txt66
-rw-r--r--npc/functions/main.txt215
-rw-r--r--npc/functions/marriage.txt293
-rw-r--r--npc/functions/masks.txt68
-rw-r--r--npc/functions/math.txt40
-rw-r--r--npc/functions/mouboofunc.txt89
-rw-r--r--npc/functions/npcmove.txt142
-rw-r--r--npc/functions/npcmovegraph.txt485
-rw-r--r--npc/functions/openbook.txt36
-rw-r--r--npc/functions/permissions.txt35
-rw-r--r--npc/functions/quest-debug/000-ShipQuests_Julia.txt37
-rw-r--r--npc/functions/quest-debug/001-ShipQuests_Arpan.txt27
-rw-r--r--npc/functions/quest-debug/002-ShipQuests_Alige.txt27
-rw-r--r--npc/functions/quest-debug/003-ShipQuests_Peter.txt30
-rw-r--r--npc/functions/quest-debug/004-ShipQuests_Nard.txt38
-rw-r--r--npc/functions/quest-debug/005-ShipQuests_Knife.txt25
-rw-r--r--npc/functions/quest-debug/006-ShipQuests_ArpanMoney.txt26
-rw-r--r--npc/functions/quest-debug/007-ShipQuests_Door.txt25
-rw-r--r--npc/functions/quest-debug/008-ShipQuests_Couwan.txt26
-rw-r--r--npc/functions/quest-debug/009-ShipQuests_TreasureChest.txt25
-rw-r--r--npc/functions/quest-debug/010-ShipQuests_Ale.txt25
-rw-r--r--npc/functions/quest-debug/011-ShipQuests_Astapolos.txt25
-rw-r--r--npc/functions/quest-debug/012-ShipQuests_Gulukan.txt25
-rw-r--r--npc/functions/quest-debug/013-ShipQuests_Jalad.txt25
-rw-r--r--npc/functions/quest-debug/014-ShipQuests_QMuller.txt25
-rw-r--r--npc/functions/quest-debug/015-ShipQuests_Tibbo.txt25
-rw-r--r--npc/functions/quest-debug/016-ShipQuests_Gugli.txt48
-rw-r--r--npc/functions/quest-debug/017-ShipQuests_ChefGado.txt30
-rw-r--r--npc/functions/quest-debug/018-General_Cookies.txt25
-rw-r--r--npc/functions/quest-debug/019-ArtisQuests_LazyBrother.txt28
-rw-r--r--npc/functions/quest-debug/020-ArtisQuests_Urchin.txt26
-rw-r--r--npc/functions/quest-debug/021-ArtisQuests_CatchPiou.txt26
-rw-r--r--npc/functions/quest-debug/022-ArtisQuests_Fishman.txt26
-rw-r--r--npc/functions/quest-debug/023-ArtisQuests_QOnan.txt27
-rw-r--r--npc/functions/quest-debug/026-General_Rumly.txt27
-rw-r--r--npc/functions/quest-debug/027-ArtisQuests_Enora.txt49
-rw-r--r--npc/functions/quest-debug/028-General_Narrator.txt25
-rw-r--r--npc/functions/quest-debug/029-ArtisQuests_Fexil.txt27
-rw-r--r--npc/functions/quest-debug/030-ArtisQuests_Lloyd.txt25
-rw-r--r--npc/functions/quest-debug/031-General_Janus.txt31
-rw-r--r--npc/functions/quest-debug/032-ArtisQuests_MonaDad.txt25
-rw-r--r--npc/functions/quest-debug/033-Artis_Legion_Progress.txt30
-rw-r--r--npc/functions/quest-debug/functions.txt109
-rw-r--r--npc/functions/questgen.txt38
-rw-r--r--npc/functions/riddle.txt74
-rw-r--r--npc/functions/sailordialogue.txt53
-rw-r--r--npc/functions/sailortalk.txt37
-rw-r--r--npc/functions/savepoint.txt48
-rw-r--r--npc/functions/shops.txt13
-rw-r--r--npc/functions/string.txt195
-rw-r--r--npc/functions/time.txt108
-rw-r--r--npc/functions/timer.txt56
-rw-r--r--npc/functions/util.txt35
-rw-r--r--npc/functions/villagertalk.txt53
72 files changed, 5364 insertions, 0 deletions
diff --git a/npc/functions/RNGesus.txt b/npc/functions/RNGesus.txt
new file mode 100644
index 000000000..f71c2f28a
--- /dev/null
+++ b/npc/functions/RNGesus.txt
@@ -0,0 +1,23 @@
+// Evol functions.
+// Authors:
+// gumi
+// Description:
+// Randomization helper functions.
+
+
+
+// any(<arg>{, ...<arg>})
+// returns one argument randomly
+
+function script any {
+ return getarg(rand(getargcount()));
+}
+
+
+
+// any_of(<array>)
+// returns any member of the array
+
+function script any_of {
+ return getelementofarray(getarg(0), getarrayindex(getarg(0)) + rand(getarraysize(getarg(0)) - getarrayindex(getarg(0))));
+}
diff --git a/npc/functions/array.txt b/npc/functions/array.txt
new file mode 100644
index 000000000..d9d649921
--- /dev/null
+++ b/npc/functions/array.txt
@@ -0,0 +1,410 @@
+// 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_product(<array>)
+// return the product of every element of the array
+
+function script array_product {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@prod *= getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@prod;
+}
+
+
+
+// array_quotient(<array>)
+// return the product of every element of the array
+
+function script array_quotient {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@quot /= getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@quot;
+}
+
+
+
+// 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$;
+}
+
+
+
+// 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;
+}
diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt
new file mode 100644
index 000000000..4bea4bd11
--- /dev/null
+++ b/npc/functions/asklanguage.txt
@@ -0,0 +1,80 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Function setting the player language
+
+function script asklanguage {
+
+ .@nb_language = 11;
+
+ switch (getarg(0, 0))
+ {
+ case LANG_ON_SEA:
+ setarray .@messages$[0], "I hear you... (English)", // English
+ "Je vous entends... (Français)", // French
+ "Да я вас слышу... (Русский)", // Russian
+ "Te oigo... (Español)", // Spanish
+ "Eu te ouço... (Português)", // Portuguese
+ "Ich höre euch... (Deutsch)", // German
+ "'k hoar ui wel... (Vlaams)", // Flemish
+ "Słyszę cię... (Polski)", // Polish
+ "Vi sento... (Italiano)", // Italian
+ "Ik kan je horen... (Nederlands)", // Dutch
+ "Et escolto (Català)", // Catalan
+ "Mi aŭdas vin... (Angle)"; // Esperanto
+ break;
+ case LANG_IN_SHIP:
+ setarray .@messages$[0], "I speak English.", // English
+ "Je parle français.", // French
+ "Я говорю на русском.", // Russian
+ "Hablo Español.", // Spanish
+ "Eu falo Português.", // Portuguese
+ "Ich spreche Deutsch.", // German
+ "'k klappe Vloams", // Flemish
+ "Mówię po polsku.", // Polish
+ "Parlo Italiano.", // Italian
+ "Ik spreek Nederlands.", // Dutch
+ "Parlo català.", // Catalan
+ "Mi parolas Esperanton."; // Esperanto
+ break;
+ default:
+ return;
+ }
+
+ setarray .@flags$[0], "flags/en",
+ "flags/fr",
+ "flags/ru",
+ "flags/es",
+ "flags/pt_BR",
+ "flags/de",
+ "flags/vls",
+ "flags/pl",
+ "flags/it",
+ "flags/nl_BE",
+ "flags/ca",
+ "flags/eo";
+
+ .@menustr$ = "";
+ .@separator$ = ":";
+
+ for (.@i = 0; .@i <= .@nb_language; .@i++)
+ {
+ if (.@i == .@nb_language)
+ {
+ .@separator$ = "";
+ }
+ .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$;
+ }
+
+ select(.@menustr$);
+
+ .@lang = @menu - 1;
+
+ if (.@lang >= 0 || .@lang <= .@nb_language)
+ {
+ Lang = .@lang;
+ }
+
+ return;
+}
diff --git a/npc/functions/asleep.txt b/npc/functions/asleep.txt
new file mode 100644
index 000000000..aa5abd635
--- /dev/null
+++ b/npc/functions/asleep.txt
@@ -0,0 +1,19 @@
+// Evol functions.
+// Authors:
+// Alige
+// Reid
+// Description:
+// Tell a random sleeping sound.
+// Variables:
+// .@rand = Random number between the number of "sleeping" choice.
+
+function script asleep {
+ switch(rand(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;
+}
diff --git a/npc/functions/bank.txt b/npc/functions/bank.txt
new file mode 100644
index 000000000..5b2813797
--- /dev/null
+++ b/npc/functions/bank.txt
@@ -0,0 +1,134 @@
+// Evol scripts.
+// Authors:
+// gumi
+// Reid
+
+function script MerchantGuild_Bank {
+ do
+ {
+ if (BankVault > 0)
+ {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You currently have @@ Esperin on your bank account.",
+ format_number(BankVault)),
+ l("What do you want to do with your money?");
+ }
+ else
+ {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("What do you want to do with your money?");
+ }
+
+ select
+ rif(Zeny > 0, l("Deposit.")),
+ rif(BankVault > 0, l("Withdraw.")),
+ l("I'm done.");
+
+ switch (@menu)
+ {
+ case 1:
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("How much do you want to deposit?");
+
+ menuint
+ l("Other."), -1,
+ rif(Zeny >= 5000, format_number(5000) + " E."), 5000,
+ rif(Zeny >= 10000, format_number(10000) + " E."), 10000,
+ rif(Zeny >= 25000, format_number(25000) + " E."), 25000,
+ rif(Zeny >= 50000, format_number(50000) + " E."), 50000,
+ rif(Zeny >= 100000, format_number(100000) + " E."), 100000,
+ rif(Zeny >= 250000, format_number(250000) + " E."), 250000,
+ rif(Zeny >= 500000, format_number(500000) + " E."), 500000,
+ rif(Zeny >= 1000000, format_number(1000000) + " E."), 1000000,
+ l("All of my money."), -2,
+ l("I changed my mind."), -3;
+
+ switch (@menuret)
+ {
+ case -1:
+ input @menuret;
+ break;
+ case -2:
+ @menuret = Zeny;
+ }
+
+ if (@menuret > 0)
+ {
+ if (@menuret > Zeny)
+ {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You do not have enough Esperin on yourself.");
+ break;
+ }
+
+ @menuret = min(MAX_BANK_ZENY, @menuret); // make sure the variable can't overflow
+ .@before = BankVault; // amount before the deposit
+ .@max = MAX_BANK_ZENY - BankVault; // maximum possible deposit
+ .@deposit = min(.@max, @menuret); // actual deposit
+
+ if (.@deposit > 0)
+ {
+ BankVault += .@deposit; // add to bank
+ Zeny -= .@deposit; // remove from inventory
+
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You made a cash deposit of @@ E.", format_number(.@deposit));
+ }
+ }
+ break;
+
+ case 2:
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("How much do you want to withdraw?");
+
+ menuint
+ l("Other."), -1,
+ rif(BankVault >= 5000, format_number(5000) + " E."), 5000,
+ rif(BankVault >= 10000, format_number(10000) + " E."), 10000,
+ rif(BankVault >= 25000, format_number(25000) + " E."), 25000,
+ rif(BankVault >= 50000, format_number(50000) + " E."), 50000,
+ rif(BankVault >= 100000, format_number(100000) + " E."), 100000,
+ rif(BankVault >= 250000, format_number(250000) + " E."), 250000,
+ rif(BankVault >= 500000, format_number(500000) + " E."), 500000,
+ rif(BankVault >= 1000000, format_number(1000000) + " E."), 1000000,
+ l("All of my money."), -2,
+ l("I changed my mind."), -3;
+
+ switch (@menuret)
+ {
+ case -1:
+ input @menuret;
+ break;
+ case -2:
+ @menuret = BankVault;
+ }
+
+ if (@menuret > 0)
+ {
+ if (@menuret > BankVault)
+ {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You do not have enough Esperin on your bank account.");
+ break;
+ }
+
+ @menuret = min(MAX_ZENY, @menuret); // make sure the variable can't overflow
+ .@before = Zeny; // amount before the withdrawal
+ .@max = MAX_ZENY - Zeny; // maximum possible withdrawal
+ .@withdrawal = min(.@max, @menuret); // actual withdrawal
+
+ if (.@withdrawal > 0)
+ {
+ Zeny += .@withdrawal; // add to inventory
+ BankVault -= .@withdrawal; // remove from bank
+
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You withdrew a total of @@ E.", format_number(.@withdrawal));
+ }
+ }
+ break;
+
+ default: return;
+ }
+ } while (true);
+}
diff --git a/npc/functions/barber.txt b/npc/functions/barber.txt
new file mode 100644
index 000000000..370285b76
--- /dev/null
+++ b/npc/functions/barber.txt
@@ -0,0 +1,110 @@
+// Evol scripts.
+// Authors:
+// omatt
+// Reid
+// Travolta
+// Description:
+// Function for supporting barber NPC.
+
+function script BarberSayStyle {
+
+ .@get_color = getlook(LOOK_HAIR_COLOR);
+ .@get_look = getlook(LOOK_HAIR);
+ .@style_name$ = $@hairstyle$[.@get_look];
+ .@color_name$ = $@haircolor$[.@get_color];
+
+ switch (getarg(0))
+ {
+ case 1:
+ message strcharinfo(0), l("@@", .@style_name$);
+ break;
+ case 2:
+ message strcharinfo(0), l("@@", .@color_name$);
+ break;
+ case 3:
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("Your hairstyle is @@ and its color is @@.", .@style_name$, .@color_name$);
+ break;
+ }
+ return;
+}
+function script BarberChangeStyle {
+ do
+ {
+ .@hairsizearray = getarraysize($@hairstyle$);
+ .@get_look = getlook(LOOK_HAIR);
+
+ // Here .@i starts from 1 because hairstyle 0 doesn't exist.
+ for (.@i = 1; .@i < .@hairsizearray; .@i++)
+ {
+ .@menustr$ = .@menustr$
+ + rif(.@get_look != .@i, l("" + $@hairstyle$[.@i] + ""))
+ + ":";
+ }
+
+ .@menustr$ = .@menustr$ + l("I'm fine for now, thank you.");
+
+ .@idx = select(l("As you want!"),.@menustr$);
+
+ if (.@idx == .@i + 1) return; // last choice to quit dialog
+
+ switch (@menu)
+ {
+ case 1:
+ do
+ {
+ // here "- 1" because i don't use the 0 of array
+ .@rand_hair = rand(1,(.@hairsizearray - 1));
+ } while (.@rand_hair == getlook(LOOK_HAIR));
+ setlook LOOK_HAIR, .@rand_hair;
+ setlook LOOK_HAIR_COLOR, getlook(LOOK_HAIR_COLOR);
+ BarberSayStyle 1;
+ break;
+ default:
+ // and here "- 1" because the first choice is taken by the random
+ setlook LOOK_HAIR, (@menu - 1);
+ setlook LOOK_HAIR_COLOR, getlook(LOOK_HAIR_COLOR);
+ break;
+ }
+ .@menustr$ = "";
+ } while (1);
+}
+
+function script BarberChangeColor {
+ do
+ {
+ .@get_look = getlook(LOOK_HAIR_COLOR);
+ .@hairsizearray = getarraysize($@haircolor$);
+
+ for (.@i = 0; .@i < .@hairsizearray; .@i++)
+ {
+ .@menustr$ = .@menustr$
+ + rif(.@get_look != .@i, l("" + $@haircolor$[.@i] + ""))
+ + ":";
+ }
+
+ .@menustr$ = l("Surprise me!") + ":" + .@menustr$ + l("I'm fine for now, thank you.");
+
+ .@idx = select(.@menustr$);
+
+ if (.@idx == .@i + 2) return;
+
+ switch (@menu)
+ {
+ case 1:
+ do
+ {
+ .@rand_color = rand(0, .@hairsizearray);
+ } while (.@rand_color == getlook(LOOK_HAIR_COLOR));
+ setlook LOOK_HAIR_COLOR, .@rand_color;
+ BarberSayStyle 2;
+ break;
+ default:
+ setlook LOOK_HAIR_COLOR, (@menu - 2);
+ break;
+ }
+ .@menustr$ = "";
+ } while (1);
+
+ return;
+}
diff --git a/npc/functions/beds.txt b/npc/functions/beds.txt
new file mode 100644
index 000000000..4ab8b6a77
--- /dev/null
+++ b/npc/functions/beds.txt
@@ -0,0 +1,30 @@
+// Evol functions.
+// Authors:
+// 4144
+// Reid
+// Description:
+// Beds utility functions
+
+function script bedTouch {
+ if (PC_IS_DEAD)
+ {
+ PC_IS_DEAD = 0;
+ if (INN_REGISTER == REDPLUSH_INN)
+ {
+ INN_REGISTER = NO_INN;
+ percentheal 100,100;
+ }
+ }
+ close;
+}
+
+function script bedClic {
+ narrator S_LAST_NEXT, l("Do you want to use this place as save point?");
+
+ if (askyesno() == ASK_YES)
+ {
+ savepointparticle getarg(0, ""), getarg(1, -1), getarg(2, -1), getarg(3, NO_INN);
+ }
+ closedialog;
+ close;
+}
diff --git a/npc/functions/clientversion.txt b/npc/functions/clientversion.txt
new file mode 100644
index 000000000..9d502be3d
--- /dev/null
+++ b/npc/functions/clientversion.txt
@@ -0,0 +1,20 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Function checking the client version and reports if it is too old.
+
+function script checkclientversion {
+ if (ClientVersion > 20) return;
+
+ mesn "Narrator";
+ mes col("Warning.", 9);
+ mes col("Warning.", 9);
+ mes col("Warning: You are using an old client.", 9);
+ next;
+ mes col("Not all features will work.", 9);
+ next;
+ mes col("Please install the new client from http://www.evolonline.org/ or http://manaplus.org/", 9);
+ next;
+ return;
+}
diff --git a/npc/functions/confused-tree-dict.txt b/npc/functions/confused-tree-dict.txt
new file mode 100644
index 000000000..4250ec35d
--- /dev/null
+++ b/npc/functions/confused-tree-dict.txt
@@ -0,0 +1,560 @@
+// Evol scripts.
+// Author:
+// gumi
+// rein
+// Based on CrazyTree, originally made by:
+// gumi
+// pclouds
+// veryape
+// wushin
+// Description:
+// dictionaries for confused tree
+
+// Built-in variables:
+// ~t lowercase hot word regex
+// ~n npc name
+// ~p player name or special name
+// ~P player name only
+//
+// Custom variables:
+// {{var}} random from array .D_var$
+// {{^var}} same but capitalize
+// {{+var}} same but title case
+// {{!var}} same but all caps
+// You can also specify multiple variables, separated by a comma (,)
+//
+// Example:
+// "*drops a {{! size }} {{ color }} {{^ sizeable object, someone }} on ~p's head*"
+
+function script TREE_dictionaries {
+ .npc$ = strnpcinfo(0);
+
+ // special aliases below (regex of lowercase char names)
+ // the substitutions are an array separated by backticks (`)
+ // XXX: this could become a hashtable at some point if it gets too big
+
+ setarray getvariableofnpc(.alias$[0], .npc$),
+ "^veryape(?:gm)?$", "hairyape",
+
+ "^wu-?shin$", "Dwarven Princess`"
+ "She-Ra",
+
+ "^reid$", "Borg Queen`"
+ "Mistress`"
+ "Milady`"
+ "R'eid",
+
+ "^(?:slicer|madcamel)$", "Camel Toe",
+
+ "^4144(?:4d494e)?$", "NPC",
+
+ "^omatt$", "@@https://youtu.be/S2qiZoqH9OY|omatt@@`"
+ "o'matt",
+
+ "^prsm$", "Refractor`"
+ "Overlord";
+ set getvariableofnpc(.alias, .npc$), getarraysize(getvariableofnpc(.alias$, .npc$));
+
+
+
+ // special drops below (regex of lowercase char names)
+ // the substitutions are an array of standard replies separated by backticks (`)
+ // and allow {{variables}}
+ // XXX: this could become a hashtable at some point if it gets too big
+
+ setarray getvariableofnpc(.sdrops$[0], .npc$),
+ "^reid$", "*drops an empty jar of Nutella on ~p*",
+ "^omatt$", "*drops Elisabeth Granneman on ~p*";
+ set getvariableofnpc(.sdrops, .npc$), getarraysize(getvariableofnpc(.sdrops$, .npc$));
+
+
+ // variables below
+
+ setarray getvariableofnpc(.D_size$[0], .npc$), // {{size}}
+ "tiny",
+ "small",
+ "perfectly sized",
+ "large",
+ "huge",
+ "humongous",
+ "ginormous";
+ set getvariableofnpc(.D_size, .npc$), getarraysize(getvariableofnpc(.D_size$, .npc$));
+
+ setarray getvariableofnpc(.D_color$[0], .npc$), // {{color}}
+ "red",
+ "orange",
+ "yellow",
+ "pink",
+ "aqua",
+ "cyan",
+ "blue",
+ "indigo",
+ "violet",
+ "purple",
+ "magenta",
+ "pink",
+ "black",
+ "white",
+ "grey",
+ "greyscale",
+ "brown",
+ "maroon",
+ "turquoise",
+ "lime",
+ "sky blue",
+ "invisible";
+ set getvariableofnpc(.D_color, .npc$), getarraysize(getvariableofnpc(.D_color$, .npc$));
+
+ setarray getvariableofnpc(.D_violentadverb$[0], .npc$), // {{violent adverb}}
+ "violently",
+ "repeatedly",
+ "casually",
+ "forcefully",
+ "slowly",
+ "carefully",
+ "hopefully",
+ "dangerously",
+ "shockingly",
+ "religiously";
+ set getvariableofnpc(.D_violentadverb, .npc$), getarraysize(getvariableofnpc(.D_violentadverb$, .npc$));
+
+ setarray getvariableofnpc(.D_hello$[0], .npc$), // {{hello}}
+ "hi",
+ "hi",
+ "hi",
+ "hi",
+ "hey",
+ "hey",
+ "hey",
+ "yo",
+ "yo",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "hello",
+ "howdy",
+ "bonjour";
+ set getvariableofnpc(.D_hello, .npc$), getarraysize(getvariableofnpc(.D_hello$, .npc$));
+
+ setarray getvariableofnpc(.D_violentverb$[0], .npc$), // {{violent verb}}
+ "slaps",
+ "slaps",
+ "slaps",
+ "slaps",
+ "slaps", // yeah the tree likes to slap a lot
+ "hits",
+ "pummels",
+ "beats",
+ "flattens",
+ "taunts",
+ "liquidates",
+ "spanks",
+ "affronts",
+ "tranquilizes",
+ "atomizes",
+ "impales",
+ "dismembers";
+ set getvariableofnpc(.D_violentverb, .npc$), getarraysize(getvariableofnpc(.D_violentverb$, .npc$));
+
+ setarray getvariableofnpc(.D_location$[0], .npc$), // {{location}}
+ "Artis",
+ "Hurnscald",
+ "Tulimshar",
+ "Nivalis",
+ "Candor",
+ "Drasil";
+ set getvariableofnpc(.D_location, .npc$), getarraysize(getvariableofnpc(.D_location$, .npc$));
+
+ setarray getvariableofnpc(.D_sizeableobject$[0], .npc$), // {{sizeable object}}
+ "trout",
+ "whale",
+ "space whale",
+ "penguin",
+ "coelacanth",
+ "squid",
+ "shrimp",
+ "crab",
+ "tentacle",
+ "dictionary",
+ "grammar book",
+ "textbook",
+ "dinosaur",
+ "t-rex",
+ "star-nosed mole",
+ "chimpanzee",
+ "mermaid",
+ "merman",
+ "piano",
+ "prince",
+ "princess",
+ "pinkie",
+ "squirrel",
+ "mouboo",
+ "wet mop",
+ "drunken pirate",
+ "cake",
+ "cookie",
+ "chocobo",
+ "restraining order",
+ "freight train",
+ "carnival hammer",
+ "crate",
+ "bomb",
+ "bowl of petinuas",
+ "box",
+ "platypus",
+ "magic eightball",
+ "vase";
+ set getvariableofnpc(.D_sizeableobject, .npc$), getarraysize(getvariableofnpc(.D_sizeableobject$, .npc$));
+
+ setarray getvariableofnpc(.D_nsizeableobject$[0], .npc$), // {{n sizeable object}}
+ "octopus",
+ "elephant",
+ "angry cat",
+ "anvil",
+ "encyclopedia set";
+ set getvariableofnpc(.D_nsizeableobject, .npc$), getarraysize(getvariableofnpc(.D_nsizeableobject$, .npc$));
+
+ setarray getvariableofnpc(.D_someone$[0], .npc$), // {{someone}}
+ "Voldemort",
+ "Cthulhu",
+ "Platyna",
+ "Hitler",
+ "Luvia",
+ "General Krukan",
+ "Borg Queen",
+ "Freeyorp",
+ "MadCamel";
+ set getvariableofnpc(.D_someone, .npc$), getarraysize(getvariableofnpc(.D_someone$, .npc$));
+
+ // replies below
+
+ setarray getvariableofnpc(.greetings$[0], .npc$),
+ "{{^ hello }} ~p!",
+ "{{^ hello }} ~p!",
+ "{{^ hello }} ~p!",
+ "{{^ hello }} ~p!",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p.",
+ "{{^ hello }} ~p, what's up?",
+ "{{^ hello }} ~p, anything new?",
+ "{{^ hello }} ~p, how are you?",
+ "~p!!!!",
+ "~p!!!",
+ "~p!!",
+ "{{^ hello }} ~p! You are looking lovely today!",
+ "Welcome back, ~p.",
+ "Welcome back, ~p.",
+ "Welcome back, ~p.",
+ "~p is back!!",
+ "Hello and welcome to the Aperture Science computer-aided enrichment center.",
+ "Greetings ~p.",
+ "What's up ~p?",
+ "What's up ~p?",
+ "How are you ~p?";
+ set getvariableofnpc(.greetings, .npc$), getarraysize(getvariableofnpc(.greetings$, .npc$));
+
+ setarray getvariableofnpc(.jokes$[0], .npc$),
+ "How did the tree get drunk? On root beer.",
+ "Do you think I'm lazy?",
+ "I miss CrazyTree %%S.",
+ "I miss LazyTree %%S.",
+ "I'm not telling you!",
+ "*sighs.*",
+ "If I do it for you, then I have to do it for everybody.",
+ "What did the beaver say to the tree? It's been nice gnawing you.",
+ "What did the little tree say to the big tree? Leaf me alone.",
+ "What did the tree wear to the pool party? Swimming trunks.",
+ "What do trees give to their dogs? Treets.",
+ "What do you call a tree that only eats meat? Carniforous.",
+ "What do you call a tree who's always envious? Evergreen.",
+ "What is the tree's least favourite month? Sep-timber!",
+ "What kind of tree can fit into your hand? A palm-tree.",
+ "What was the tree's favorite subject in school? Chemistree.",
+ "Why did the leaf go to the doctor? It was feeling green.",
+ "Why doesn't the tree need sudo? Because it has root.",
+ "Why was the cat afraid of the tree? Because of its bark.",
+ "Why was the tree executed? For treeson.",
+ "How do trees get on the internet? They log in.",
+ "Why did the pine tree get into trouble? Because it was being knotty.",
+ "Did you hear the one about the oak tree? It's a corn-y one!",
+ "What do you call a blonde in a tree with a briefcase? Branch Manager.",
+ "How is an apple like a lawyer? They both look good hanging from a tree.",
+ "Why did the sheriff arrest the tree? Because its leaves rustled.",
+ "I'm too tired, ask someone else.",
+ "If you are trying to get me to tell jokes you are barking up the wrong tree!",
+ "You wooden think they were funny anyhow. Leaf me alone!",
+ "What is brown and sticky? A stick.",
+ "What's the best way to carve wood? Whittle by whittle.",
+ "What did the tree do when the bank closed? It started its own branch.",
+ "Do you want a brief explanation of an acorn? In a nutshell, it’s an oak tree.",
+ "A snare drum and a crash cymbal fell out of a tree. *BA-DUM TSSSHH*",
+ "How do you properly identify a dogwood tree? By the bark!",
+ "Where do saplings go to learn? Elementree school.",
+ "Why do trees make great thieves? Sticky fingers.",
+ "What is green, has leaves, and a trunk? A houseplant going on vacation.",
+ "Where can Adansonia trees go for a quick trim? To the baobarber.",
+ "What looks like half a spruce tree? The other half.",
+ "What do you give to a sick citrus tree? Lemon aid.",
+ "What did the tree say to the drill? You bore me.",
+ "What happened to the wooden car with wooden wheels and a wooden engine? It wooden go.",
+ "How do trees keep you in suspense? I'll tell you tomorrow.",
+ "Where do birch trees keep their valuables? In a river bank.",
+ "What kind of stories do giant sequoia trees tell? Tall tales.",
+ "What is the most frustrating thing about being a tree? Having so many limbs and not being able to walk.",
+ "What's black, highly dangerous, and lives in a tree? A crow with a machine gun.",
+ "What kind of wood doesn't float? Natalie Wood.",
+ "Two men passed a sign while looking for work. It was for tree fellers. They said: “what a shame, there are only two of us”.";
+ set getvariableofnpc(.jokes, .npc$), getarraysize(getvariableofnpc(.jokes$, .npc$));
+
+ setarray getvariableofnpc(.healing$[0], .npc$),
+ "Eat an apple, they're good for you.",
+ "If I do it for you, then I have to do it for everybody.",
+ "Oh, go drink a potion or something.",
+ "Whoops! I lost my spellbook.",
+ "No mana.";
+ set getvariableofnpc(.healing, .npc$), getarraysize(getvariableofnpc(.healing$, .npc$));
+
+ setarray getvariableofnpc(.whoami$[0], .npc$),
+ "An undercover GM.",
+ "An exiled GM.",
+ "I'm not telling you!",
+ "I'm a bot! I'll be level 99 one day! Mwahahahaaha!!!111!",
+ "Somebody said I'm a Chinese copy of CrazyTree.",
+ "I am your evil twin.",
+ "I don't remember anything after I woke up! What happened to me?",
+ "I don't know. Why am I here??",
+ "Who are you?",
+ "On the 8th day, God was bored and said 'There will be bots'. So here I am.",
+ "♪ I'm your hell, I'm your dream, I'm nothing in between ♪♪",
+ "♪♪ Aperture Science. We do what we must, because... we can ♪",
+ "I'm just a reincarnation of a copy.";
+ set getvariableofnpc(.whoami, .npc$), getarraysize(getvariableofnpc(.whoami$, .npc$));
+
+ setarray getvariableofnpc(.drops$[0], .npc$),
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops an {{ n sizeable object }} on ~p's head.*",
+ "*drops an {{ n sizeable object }} on ~p's head.*",
+ "*drops {{ someone }} on ~p's head.*",
+ "*drops a coin on ~p's head.*",
+ "*drops a fruit on ~p's head.*",
+ "*drops an apple on ~p's head.*",
+ "*drops an iten on ~p's head.*",
+ "*drops a GM on ~p.*",
+ "*drops a piece of moon rock on ~p's head.*",
+ "*drops a pin on ~p's head.*",
+ "*drops a rock on ~p's head.*",
+ "*drops a tub of paint on ~p's head.*",
+ "*drops a sandworm on ~p.*",
+ "*drops an idea in ~p's head.*",
+ "*drops The Hitchhiker's Guide to the Galaxy on ~p's head.*",
+ "Ouch.",
+ "Ouchy.",
+ "*drops dead.*",
+ "*sighs.*",
+ "Leaf me alone.",
+ "Stop it! I doesn't drop branches, try the Druid tree for once!";
+ set getvariableofnpc(.drops, .npc$), getarraysize(getvariableofnpc(.drops$, .npc$));
+
+ setarray getvariableofnpc(.die$[0], .npc$),
+ "*drops an iten on ~p's head.*",
+ "*drops a piece of moon rock on ~p's head.*",
+ "*drops {{ someone }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops a {{ sizeable object }} on ~p's head.*",
+ "*drops an {{ n sizeable object }} on ~p's head.*",
+ "*drops a {{ size }} {{ sizeable object, n sizeable object }} on ~p's head.*",
+ "*drops a {{ size }} {{ color }} {{ sizeable object, n sizeable object }} on ~p's head.*",
+ "*{{ violent adverb }} {{ violent verb }} ~p.*",
+ "*drops dead.*",
+ "*sighs.*",
+ "Avada Kedavra!",
+ "Make me!",
+ "Never!!",
+ "You die, ~p!",
+ "You die, ~p!",
+ "You die, ~p!",
+ "You die, ~p!",
+ "No!",
+ "In a minute.",
+ "Suuure... I'll get right on it...";
+ set getvariableofnpc(.die, .npc$), getarraysize(getvariableofnpc(.die$, .npc$));
+
+ setarray getvariableofnpc(.poke$[0], .npc$),
+ "*tickles.*";
+ set getvariableofnpc(.poke, .npc$), getarraysize(getvariableofnpc(.poke$, .npc$));
+
+ setarray getvariableofnpc(.disgusting$[0], .npc$),
+ "Ewwwww %%^.";
+ set getvariableofnpc(.disgusting, .npc$), getarraysize(getvariableofnpc(.disgusting$, .npc$));
+
+ setarray getvariableofnpc(.answer$[0], .npc$),
+ "42.",
+ "Kittens.";
+ set getvariableofnpc(.answer, .npc$), getarraysize(getvariableofnpc(.answer$, .npc$));
+
+ setarray getvariableofnpc(.burning$[0], .npc$),
+ "*curses ~p and dies %%c.*",
+ "Help! I'm on fire!",
+ "Oh hot.. hot hot!",
+ "*is glowing.*",
+ "*is flaming.*",
+ "Ehemm. Where are firefighters? I need them now!",
+ "*is so hot!.*",
+ "*slowly catches fire.*",
+ "*trembles with trepidation.*",
+ "*is immune to fire.*";
+ set getvariableofnpc(.burning, .npc$), getarraysize(getvariableofnpc(.burning$, .npc$));
+
+ setarray getvariableofnpc(.kill$[0], .npc$),
+ "*curses ~p and dies %%c.*";
+ set getvariableofnpc(.kill, .npc$), getarraysize(getvariableofnpc(.kill$, .npc$));
+
+ setarray getvariableofnpc(.silly$[0], .npc$),
+ "Hahaha, good one!";
+ set getvariableofnpc(.silly, .npc$), getarraysize(getvariableofnpc(.silly$, .npc$));
+
+ setarray getvariableofnpc(.love$[0], .npc$),
+ "♪♪ and IIII.. will alwayyyys loooovvve youuuuu. ♪♪ %%]",
+ "♪♪ nothing's gonna change my love for you, you oughta know by now how much I love you.. ♪ %%]",
+ "♪ ..and then I go and spoil it all, by saying something stupid like: “I love you.” ♪",
+ "♪ ..won't you find a place for me? somewhere in your heart... ♪♪",
+ "Thank you.",
+ "♪♪ ..I can't love another when my heart is somewhere far away.. ♪",
+ "%%]";
+ set getvariableofnpc(.love, .npc$), getarraysize(getvariableofnpc(.love$, .npc$));
+
+ setarray getvariableofnpc(.dance$[0], .npc$),
+ "I would but I am rooted to the ground.",
+ "Have you ever seen a tree dance before?",
+ "Hahaha, good one!";
+ set getvariableofnpc(.dance, .npc$), getarraysize(getvariableofnpc(.dance$, .npc$));
+
+ setarray getvariableofnpc(.hate$[0], .npc$),
+ "Right back at you!",
+ "Ok...",
+ "*pats ~p, let it go...*",
+ "Hu hu hu, ~p hates me.";
+ set getvariableofnpc(.hate, .npc$), getarraysize(getvariableofnpc(.hate$, .npc$));
+
+ setarray getvariableofnpc(.bye$[0], .npc$),
+ "*waves goodbye to ~p in tears, come back soon!*";
+ set getvariableofnpc(.bye, .npc$), getarraysize(getvariableofnpc(.bye$, .npc$));
+
+ setarray getvariableofnpc(.pain$[0], .npc$),
+ "Ouch.",
+ "Ouchy.",
+ "Argh.",
+ "Eek.",
+ "*howls.*",
+ "*screams.*",
+ "*groans.*",
+ "*cries.*",
+ "*faints.*",
+ "*shrieks.*",
+ "*hides behind itself.*",
+ "%%k",
+ "Why, what did I do to you? %%i";
+ set getvariableofnpc(.pain, .npc$), getarraysize(getvariableofnpc(.pain$, .npc$));
+
+ setarray getvariableofnpc(.eightball$[0], .npc$),
+ "It is possible.",
+ "Yes!",
+ "Of course.",
+ "Naturally.",
+ "Obviously.",
+ "It shall be.",
+ "The outlook is good.",
+ "It is so.",
+ "One would be wise to think so.",
+ "The answer is certainly yes.",
+ "In your dreams.",
+ "I doubt it very much.",
+ "No chance.",
+ "The outlook is very poor.",
+ "Unlikely.",
+ "About as likely as pigs flying.",
+ "You're kidding, right?",
+ "NO!",
+ "NO.",
+ "No.",
+ "Maybe...",
+ "No clue.",
+ "I don't know.",
+ "The outlook is hazy, please ask again later.",
+ "What are you asking me for?",
+ "Come again?",
+ "You know the answer better than I.",
+ "The answer is def-- oooh! shiny thing!",
+ "No idea.",
+ "Perhaps.",
+ "I think it is better not to tell you.",
+ "Error 417: Expectation failed.";
+ set getvariableofnpc(.eightball, .npc$), getarraysize(getvariableofnpc(.eightball$, .npc$));
+
+ setarray getvariableofnpc(.bad$[0], .npc$),
+ "I'm not bad! You are bad!",
+ "OK, I'm bad.",
+ "I'm just a littttle bad.",
+ "Not as bad as the people that made me.";
+ set getvariableofnpc(.bad, .npc$), getarraysize(getvariableofnpc(.bad$, .npc$));
+
+ setarray getvariableofnpc(.no_idea$[0], .npc$),
+ "What?",
+ "What??",
+ "What?",
+ "Whatever.",
+ "Hmm...",
+ "Hmm...",
+ "Huh?",
+ "*yawns.*",
+ "Wait a minute...",
+ "What are you talking about?",
+ "Who are you?",
+ "What about me?",
+ "I don't know what you are talking about",
+ "Excuse me?",
+ "Very interesting.",
+ "Really?",
+ "Go on...",
+ "*scratches its leafy head.*",
+ "*feels a disturbance in the force.*",
+ "%%j",
+ "*senses a disturbance in the force.*",
+ "I'm bored...",
+ "%%U",
+ "%%[";
+ set getvariableofnpc(.no_idea, .npc$), getarraysize(getvariableofnpc(.no_idea$, .npc$));
+
+ setarray getvariableofnpc(.shut_up$[0], .npc$),
+ "*goes hide in a corner %%S.*";
+ set getvariableofnpc(.shut_up, .npc$), getarraysize(getvariableofnpc(.shut_up$, .npc$));
+
+ setarray getvariableofnpc(.climb$[0], .npc$),
+ "*sways violently.*",
+ "*bends all the way to the ground.*",
+ "*creaks and bends.*",
+ "*welcomes those who come to play %%I.*",
+ "*beams with pride.*";
+ set getvariableofnpc(.climb, .npc$), getarraysize(getvariableofnpc(.climb$, .npc$));
+
+ return;
+}
diff --git a/npc/functions/doors.txt b/npc/functions/doors.txt
new file mode 100644
index 000000000..c1b6565ef
--- /dev/null
+++ b/npc/functions/doors.txt
@@ -0,0 +1,59 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Doors utility functions
+// Variables:
+// none
+
+function script doorTouch {
+ if (getareausers() <= 1)
+ {
+ .dir = 2;
+ stopnpctimer;
+ initnpctimer;
+ }
+ close;
+}
+
+function script doorUnTouch {
+ if (getareausers(.map$) == 0)
+ {
+ .dir = 4;
+ initnpctimer;
+ startnpctimer;
+ }
+ close;
+}
+
+function script doorTimer {
+ stopnpctimer;
+ if (.dir == 2) .dir = 6;
+ if (.dir == 4) .dir = 8;
+ end;
+}
+
+function script doorInit {
+ .distance = 5;
+ .alwaysVisible = true;
+ .sex = G_OTHER;
+ end;
+}
+
+function script open_door {
+ .@door$ = getarg(0);
+ setnpcdir.@door$, 2;
+ sleep 300;
+ setnpcdir .@door$, 6;
+ sleep 300;
+ return 0;
+}
+
+function script close_door {
+ .@door$ = getarg(0);
+ setnpcdir .@door$, 4;
+ sleep 300;
+ setnpcdir .@door$, 8;
+ sleep 300;
+ return 0;
+}
diff --git a/npc/functions/fishing.txt b/npc/functions/fishing.txt
new file mode 100644
index 000000000..3d719ac78
--- /dev/null
+++ b/npc/functions/fishing.txt
@@ -0,0 +1,294 @@
+// Evol functions.
+// Authors:
+// gumi
+// omatt
+// Travolta
+// Description:
+// Fishing functions.
+// Variable
+// .dir
+// DOWN Never try or pulled too late
+// UP Bait dropped
+// LEFT Fish bite bait
+//
+// player log on .dir is DOWN, start by choose bait menu
+// player chooses bait, script addtimer in npc .dir is UP
+// if player pulls before signal npc, bait is lost, set .bait to DOWN goto choose bait
+// if player pulls after pull delay max, bait is lost, set .bait to DOWN goto choose bait
+// npc signal .dir is LEFT
+// player pulls between npc signal and pulls delay max, got the fish, set .dir to DOWN goto choose bait
+
+function script fishing_cleanup {
+ .@npc$ = getarg(0, "");
+ if (.@npc$ == "") end;
+
+ set getvariableofnpc(.char_id, .@npc$), 0; // clean acc id
+ set getvariableofnpc(.account_id, .@npc$), 0; // clean char id
+ set getvariableofnpc(.last_used, .@npc$), gettimetick(2); // set last used time
+ setnpcdir .@npc$, DOWN; // reset direction
+ return;
+}
+
+- script global fishing handler 32767,{
+ end;
+
+OnBite:
+ if (getnpcdir(@fishing_spot$) != UP)
+ end;
+
+ setnpcdir @fishing_spot$, LEFT;
+ @fishing_tick = gettimetick(0);
+ specialeffect(getvariableofnpc(.bite_fx, @fishing_spot$), SELF, playerattached());
+ end;
+
+OnCleanUp:
+ dispbottom l("You waited too long and lost the bait...");
+ specialeffect(getvariableofnpc(.failure_fx, @fishing_spot$), SELF, playerattached()); // event fail
+OnPCLogoutEvent:
+ fishing_cleanup @fishing_spot$;
+ @fishing_spot$ = ""; // unbind fishing npc
+ end;
+}
+
+function script fishing {
+
+///////////////////////////////////////////
+// Var initialization
+
+ .@npc$ = strnpcinfo(0); // the full name of the fishing spot
+
+ .@account_id = getvariableofnpc(.account_id, .@npc$); // the account id of the player using the fishing spot
+ .@char_id = getvariableofnpc(.char_id, .@npc$); // the char id of the player using the fishing spot
+ .@dir = getnpcdir(.@npc$); // direction of the fishing spot
+ .@last_used = getvariableofnpc(.last_used, .@npc$); // when this fishing spot was last used
+ .@baits = getvariableofnpc(.baits, .@npc$); // bait count
+
+ .@rod = getvariableofnpc(.fishing_rod, .@npc$); // the fishing rod required for this spot
+ .@rod = (.@rod ? .@rod : FishingRod);
+
+ .@regen_time = getvariableofnpc(.cooldown, .@npc$); // cooldown for the fishing spot
+ .@regen_time = (.@regen_time ? .@regen_time : 20);
+
+ .@success_fx = getvariableofnpc(.success_fx, .@npc$); // effect to show on success
+ .@success_fx = (.@success_fx ? .@success_fx : 27);
+
+ .@failure_fx = getvariableofnpc(.failure_fx, .@npc$); // effect to show on failure
+ if (.@failure_fx < 1)
+ {
+ .@failure_fx = 28;
+ set getvariableofnpc(.failure_fx, .@npc$), .@failure_fx; // needed by global handler
+ }
+
+ .@initial_fx = getvariableofnpc(.initial_fx, .@npc$); // effect to show when throwing the bait
+ .@initial_fx = (.@initial_fx ? .@initial_fx : 29);
+
+ .@bite_fx = getvariableofnpc(.bite_fx, .@npc$); // effect to show when something bites
+ if (.@bite_fx < 1)
+ {
+ .@bite_fx = 30;
+ set getvariableofnpc(.bite_fx, .@npc$), .@bite_fx; // needed by global handler
+ }
+
+ .@wait_time_min = getvariableofnpc(.wait_time_min, .@npc$); // min amount of time to wait for the line to sink
+ .@wait_time_min = (.@wait_time_min ? .@wait_time_min : 4000);
+
+ .@wait_time_max = getvariableofnpc(.wait_time_max, .@npc$); // max amount of time to wait for the line to sink
+ .@wait_time_max = (.@wait_time_max ? .@wait_time_max : 18000);
+
+ .@catch_time = getvariableofnpc(.catch_time, .@npc$); // the player must catch the fish within X ms after the line sinks
+ .@catch_time = (.@catch_time ? .@catch_time : 5000);
+
+ .@pull_rand_max = getvariableofnpc(.pull_rand_max, .@npc$);
+ .@pull_rand_max = (.@pull_rand_max ? .@pull_rand_max : 800);
+
+ .@fish_id = getvariableofnpc(.common_fish, .@npc$);
+ .@fish_id = (.@fish_id ? .@fish_id : CommonCarp);
+
+ .@rare_id = getvariableofnpc(.rare_fish, .@npc$);
+ .@rare_fish = (.@rare_fish ? .@rare_fish : GrassCarp);
+
+ .@rare_fish_chance = getvariableofnpc(.rare_fish_chance, .@npc$);
+ .@rare_fish_chance = (.@rare_fish_chance ? .@rare_fish_chance : 25);
+
+ if (getvariableofnpc(.bait_ids[0], .@npc$) < 1)
+ {
+ // default baits (bait, chance booster)
+ setarray getvariableofnpc(.bait_ids[0], .@npc$),
+ SmallTentacles, 0,
+ Bread, 0,
+ Aquada, 1,
+ UrchinMeat, 0,
+ TortugaTongue, 2,
+ Tentacles, 0;
+ }
+
+ if (.@baits < 1)
+ {
+ // only count it once
+ .@baits = getarraysize(getvariableofnpc(.bait_ids[0], .@npc$));
+ set getvariableofnpc(.baits, .@npc$), .@baits;
+ }
+
+
+///////////////////////////////////////////
+// Logic below
+
+ if (countitem(.@rod) < 1)
+ {
+ dispbottom l("You don't have any @@.", getitemlink(.@rod));
+ return -1;
+ }
+
+ if (.@account_id > 0 && !isloggedin(.@account_id, .@char_id))
+ {
+ fishing_cleanup .@npc$; // reset
+ .@dir = DOWN;
+ }
+
+ if (.@char_id != getcharid(CHAR_ID_CHAR) && .@dir != DOWN)
+ {
+ dispbottom l("This fishing spot is already being used!");
+ return -2;
+ }
+
+
+ // pull too soon
+ if (.@dir == UP)
+ {
+ deltimer "global fishing handler::OnCleanUp"; // cancel auto cleanup
+ deltimer "global fishing handler::OnBite";
+ specialeffect(.@failure_fx, SELF, playerattached()); // event fail
+ fishing_cleanup .@npc$; // do it manually instead
+ dispbottom l("You pulled too soon and lost the bait.");
+ return -3;
+ }
+
+ // pull maybe on time
+ else if (.@dir == LEFT)
+ {
+ deltimer "global fishing handler::OnCleanUp"; // cancel auto cleanup
+ fishing_cleanup .@npc$; // do it manually instead
+
+ getmapxy .@mapbis$, .@xbis, .@ybis, UNITTYPE_PC; // get current char location
+
+ // Leave spot, lost the bait
+ if (.@mapbis$ != @fishing_loc$[0] || .@xbis != @fishing_loc[0] || .@ybis != @fishing_loc[1] || @fishing_spot$ != .@npc$)
+ {
+ dispbottom l("You left your fishing spot!");
+ return -4;
+ }
+
+ // RNG to obtain a rare fish
+ // TODO: chance booster
+ if (rand(.@rare_fish_chance) == 0)
+ {
+ // TODO: rare fish array
+ .@fish_id = .@rare_id;
+ }
+ // TODO: else common fish array
+
+ // RNG to obtain a fish
+ if (rand(gettimetick(0) - @fishing_tick) <= .@pull_rand_max)
+ {
+ specialeffect(.@success_fx, SELF, playerattached()); // event success
+
+ if(!checkweight(.@fish_id, 1))
+ {
+ dispbottom l("You caught a @@ but had no room in your inventory to carry it.", getitemlink(.@fish_id));
+ makeitem .@fish_id, 1, .@mapbis$, .@xbis, .@ybis; // drop on the ground
+ return 0;
+ }
+
+ //dispbottom l("You caught a @@!", getitemlink(.@fish_id)); <= already shows "you picked up [...]"
+ getitem .@fish_id, 1;
+ }
+ else
+ {
+ dispbottom l("You pulled too late and lost the bait...");
+ specialeffect(.@failure_fx, SELF, playerattached()); // event fail
+ .@fish_id = 0;
+ }
+
+ return .@fish_id;
+ }
+
+
+
+ if (gettimetick(2) - .@last_used < .@regen_time)
+ {
+ dispbottom l("This fishing spot has just been used, give it a rest.");
+ return -5;
+ }
+
+
+ // begin fishing
+ narrator S_LAST_NEXT,
+ l("You see some fish reflecting the sun on the surface of the water."),
+ l("What will be the bait for the fish?");
+
+ mes "##B" + l("Drag and drop an item from your inventory.") + "##b";
+
+ .@bait = requestitem();
+ .@bait_c = false;
+
+ if (.@bait < 1)
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("You take your fishing rod and leave.");
+
+ return -6;
+ }
+
+ if (countitem(.@bait) < 1)
+ {
+ return -6;
+ }
+
+ for (.@i = 0; .@i < .@baits; .@i += 2)
+ {
+ if (getvariableofnpc(.bait_ids[.@i], .@npc$) == .@bait)
+ {
+ .@bait_c = true;
+ break;
+ }
+ }
+
+ if (.@bait_c != true)
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("This item cannot be used as bait here.");
+
+ return -6;
+ }
+
+ if (getvariableofnpc(.char_id, .@npc$) > 0)
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("Somebody took your place on this spot!"),
+ l("You take your fishing rod and leave.");
+ return -8;
+ }
+
+ @fishing_spot$ = .@npc$; // bind player to fishing spot
+ set getvariableofnpc(.account_id, .@npc$), getcharid(CHAR_ID_ACCOUNT); // record account id
+ set getvariableofnpc(.char_id, .@npc$), getcharid(CHAR_ID_CHAR); // record char id
+ set getvariableofnpc(.last_used, .@npc$), gettimetick(2);
+ getmapxy(@fishing_loc$[0], @fishing_loc[0], @fishing_loc[1], 0); // record char pos
+ delitem .@bait, 1;
+
+ // The player uses this spot, his bait is ready, he just has to wait for the signal.
+ closedialog;
+
+ specialeffect(.@initial_fx, SELF); // throw the bait
+ sleep2 800; // wait 0.8s for synchronize the sound of "plop" in water with the npc dir UP.
+ setnpcdir .@npc$, UP;
+
+ dispbottom l("Wait for the bait to sink underwater.");
+
+ .@delay = rand(.@wait_time_min, .@wait_time_max);
+
+ addtimer .@delay, "global fishing handler::OnBite"; // bite logic
+ addtimer (.@delay + .@catch_time), "global fishing handler::OnCleanUp"; // auto clean up
+
+ return 0;
+}
diff --git a/npc/functions/game-rules.txt b/npc/functions/game-rules.txt
new file mode 100644
index 000000000..f633ee5f3
--- /dev/null
+++ b/npc/functions/game-rules.txt
@@ -0,0 +1,59 @@
+// Evol scripts.
+// Authors:
+// gumi
+// Qwerty Dragon
+// Reid
+// WildX
+// Description:
+// 7 main rules of Evol Online.
+
+function script GameRules {
+ if (checkweight(BookOfLaws, 1) == true && countitem(BookOfLaws) < 1)
+ getitem BookOfLaws, 1; // give the book if the player lost it
+
+ narrator getarg(0, 0),
+ l("1. ##BDo not bot##b, this means you are not allowed to perform any AFK (away from keyboard) activity, apart from standing idle."),
+ l("2. ##BDo not use offensive/rude language##b in the chats or in your character(s) name(s)."),
+ l("3. ##BDo not spam/flood other players.##b This includes chat spam and spam by trade requests."),
+ l("4. ##BSpeak only English in public areas.##b You can speak whatever language you want through whispers or whenever everyone in the area can speak said language."),
+ l("5. ##BDo not beg others##b for money, items or favours of any kind. If you want to ask for something, do it politely and only once. Try not to annoy other players."),
+ l("6. ##BFollow the [@@http://wiki.evolonline.org/rules/esc|ESC@@]##b (Evol Social Convention)."),
+ l("7. ##BDo not multibox.##b You are not allowed to engage in combat while controlling more than one character at a time.");
+ return;
+}
+
+
+- script @rules 32767,{
+ end;
+
+ function read_book {
+ narrator S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("This book outlines the laws of every city and holding in Gasaron."),
+ l("The first page contains the universal rules that have been agreed upon throughout the land.");
+
+ GameRules S_NO_NPC_NAME | S_LAST_NEXT;
+
+ narrator S_NO_NPC_NAME,
+ l("The next page begins to list the complex trading laws of the City of Esperia"),
+ l("All this seems unimportant to you right now.");
+ close;
+ }
+
+OnCall:
+ GameRules;
+ close;
+
+OnUseBook:
+ if (openbook())
+ read_book;
+ bye;
+
+OnShelfUse:
+ if (openbookshelf())
+ read_book;
+ bye;
+
+OnInit:
+ .book_name$ = "The Book of Laws";
+ bindatcmd "rules", "@rules::OnCall", 0, 2, 0;
+}
diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt
new file mode 100644
index 000000000..620883b8c
--- /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 .byemsg$[rand(getarraysize(.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/hammocks.txt b/npc/functions/hammocks.txt
new file mode 100644
index 000000000..8e1c2fec4
--- /dev/null
+++ b/npc/functions/hammocks.txt
@@ -0,0 +1,50 @@
+// Evol functions.
+// Authors:
+// 4144
+// Reid
+// Description:
+// Hammocks utility functions
+// Variables:
+// none
+
+function script hamTouchLeft {
+ if (getareausers() <= 1)
+ {
+ .dir = 0;
+ stopnpctimer;
+ initnpctimer;
+ }
+ close;
+}
+
+function script hamUnTouch {
+ if (getareausers() == 0)
+ {
+ .dir = 2;
+ initnpctimer;
+ startnpctimer;
+ }
+ close;
+}
+
+function script hamTimerLeft {
+ stopnpctimer;
+ if (.dir == 2) .dir = 0;
+ end;
+}
+
+function script hamTouchRight {
+ if (getareausers() <= 1)
+ {
+ .dir = 0;
+ stopnpctimer;
+ initnpctimer;
+ }
+ close;
+}
+
+function script hamTimerRight {
+ stopnpctimer;
+ if (.dir == 2) .dir = 0;
+ end;
+}
diff --git a/npc/functions/harbours.txt b/npc/functions/harbours.txt
new file mode 100644
index 000000000..63d58076b
--- /dev/null
+++ b/npc/functions/harbours.txt
@@ -0,0 +1,39 @@
+// Evol scripts.
+// Author:
+// Reid
+// Description:
+// Harbour utility functions
+// Animation:
+// Length: 1680
+// Values:
+// 2 Hook moving down.
+// 4 Hook moving up.
+// 6 Hook down.
+// 8 Hook up.
+
+function script harbourClic {
+ if (.dir == 0)
+ {
+ .dir = 2;
+
+ initnpctimer;
+ startnpctimer;
+ close;
+ }
+
+ if (.dir == 6)
+ {
+ .dir = 4;
+
+ initnpctimer;
+ startnpctimer;
+ close;
+ }
+}
+
+function script harbourTimer {
+ stopnpctimer;
+ if (.dir == 2) .dir = 6;
+ if (.dir == 4) .dir = 0;
+ end;
+}
diff --git a/npc/functions/hello.txt b/npc/functions/hello.txt
new file mode 100644
index 000000000..9399b3b58
--- /dev/null
+++ b/npc/functions/hello.txt
@@ -0,0 +1,23 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Tell a random greeting sentence.
+
+function script hello {
+
+ switch (rand(3))
+ {
+ case 0:
+ npctalkonce(l("Heya!"));
+ break;
+ case 1:
+ npctalkonce(l("Hi."));
+ break;
+ case 2:
+ npctalkonce(l("Nice day to you."));
+ break;
+ }
+
+ return;
+}
diff --git a/npc/functions/input.txt b/npc/functions/input.txt
new file mode 100644
index 000000000..cf0382d25
--- /dev/null
+++ b/npc/functions/input.txt
@@ -0,0 +1,66 @@
+// Evol functions.
+// Author:
+// 4144
+// 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$;
+}
diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt
new file mode 100644
index 000000000..4a5e08cad
--- /dev/null
+++ b/npc/functions/inventoryplace.txt
@@ -0,0 +1,37 @@
+// 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)
+ {
+ debugmes "inventoryplace: Wrong argument count.";
+ close;
+ }
+
+ 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/legiontalk.txt b/npc/functions/legiontalk.txt
new file mode 100644
index 000000000..fbc35e1f7
--- /dev/null
+++ b/npc/functions/legiontalk.txt
@@ -0,0 +1,66 @@
+// Evol functions.
+// Authors:
+// Akko Teru
+// Reid
+// Qwerty Dragon
+// Description:
+// Tell a random sentence suited to Aemil's Legion in Artis.
+
+function script legiontalk {
+
+ switch (rand(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:
+ 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(lg("Keep moving girl.", "Keep moving boy."));
+ break;
+ case 11:
+ npctalkonce(lg("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;
+}
diff --git a/npc/functions/main.txt b/npc/functions/main.txt
new file mode 100644
index 000000000..243ae383d
--- /dev/null
+++ b/npc/functions/main.txt
@@ -0,0 +1,215 @@
+// Evol functions.
+// Authors:
+// 4144
+// Travolta
+// Description:
+// Build in functions.
+
+function script menuimage {
+ return getarg(0) + "|" + getarg(1);
+}
+
+function script menuaction {
+ return "[" + getarg(0) + "]";
+}
+
+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 col {
+ .@color = getarg(1);
+ if (.@color < 0) .@color = 0;
+ if (.@color > 9) .@color = 9;
+ return "##" + .@color + getarg(0) + "##0";
+}
+
+function script adddefaultskills {
+ if (getskilllv(NV_BASIC) < 6)
+ {
+ skill NV_BASIC, 6, 0;
+ }
+ return;
+}
+
+function script addremovemapmask {
+ setmapmask getarg(0), (getmapmask(getarg(0)) | (getarg(1) + getarg(2))) ^ getarg(2);
+ 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
+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(0)])
+ return false;
+ @NPC_TALK_LOCK[getnpcid(0)] = 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;
+}
+
+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) + "|@@]";
+}
diff --git a/npc/functions/marriage.txt b/npc/functions/marriage.txt
new file mode 100644
index 000000000..d9e48bf54
--- /dev/null
+++ b/npc/functions/marriage.txt
@@ -0,0 +1,293 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Functions for marriage
+
+// check is player is near marriage npc
+// args:
+// 0 - player name
+// returns:
+// true if player located near npc.
+function script marriagecheckname {
+ .@name$ = getarg(0);
+ if (.@name$ == "")
+ {
+ // no other registrand
+ return false;
+ }
+ .@id = getcharid(0, .@name$);
+ .@accoundId = getcharid(3, .@name$);
+ if (isloggedin(.@accoundId, .@id) == false)
+ {
+ // registrant not logged in
+ return false;
+ }
+ getmapxy(.@mapname$, .@x, .@y, 0, .@name$);
+ if (.@mapname$ != strnpcinfo(4))
+ {
+ // registrant on other map
+ return false;
+ }
+ if (distance(.@x, .@y, .x, .y) > .distance)
+ {
+ // registrant too far
+ return false;
+ }
+ return true;
+}
+
+// return player name registered with same gender like attached player
+function script getmarriageregistrant {
+ if (Sex)
+ {
+ .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3));
+ }
+ else
+ {
+ .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3));
+ }
+ return .@name$;
+}
+
+// return registered marriage partner name
+function script getmarriagepartner {
+ if (Sex)
+ {
+ .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3));
+ }
+ else
+ {
+ .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3));
+ }
+ if (marriagecheckname(.@name$))
+ return .@name$;
+ return "";
+}
+
+// register attached player as partner for marriage
+function script marriageregisterself {
+ if (Sex)
+ {
+ set getvariableofnpc(.maleName$, strnpcinfo(3)), strcharinfo(0);
+ set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), "";
+ }
+ else
+ {
+ set getvariableofnpc(.femaleName$, strnpcinfo(3)), strcharinfo(0);
+ set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), "";
+ }
+ return;
+}
+
+// return player name what was accepted by partner
+function script getmarriagepartneraccepted {
+ if (Sex)
+ {
+ .@name$ = getvariableofnpc(.femaleName_partner$, strnpcinfo(3));
+ }
+ else
+ {
+ .@name$ = getvariableofnpc(.maleName_partner$, strnpcinfo(3));
+ }
+ return .@name$;
+}
+
+// return true if partner present near and partner accepted you
+function script ismarriagepartneraccepted {
+ .@partner$ = getmarriagepartner();
+ if (.@partner$ == "")
+ return false;
+ .@name$ = getmarriagepartneraccepted();
+ if (.@name$ == strcharinfo(0))
+ return true;
+ return false;
+}
+
+// accept for attached player his/her partner
+// args:
+// 0 - partner name
+function script marriageacceptpartner {
+ .@name$ = getarg(0);
+ if (Sex)
+ {
+ set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), .@name$;
+ }
+ else
+ {
+ set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), .@name$;
+ }
+ return;
+}
+
+function script askmarry {
+ speech l("Do you want to marry @@?", getarg(0));
+ if (askyesno() == ASK_YES)
+ return true;
+ return false;
+}
+
+// start marriage registration process
+function script marriageregister {
+ .@partner$ = getmarriagepartner();
+ if (.@partner$ == "")
+ { // no partner registered
+ speech l("Ok I add your name... @@...", strcharinfo(0));
+ marriageregisterself();
+ next;
+ speech lg("Now wait for your partner, then talk to me again.");
+ npctalk l("@@ registered for marriage. Waiting for partner...", strcharinfo(0));
+ close;
+ }
+ else
+ { // partner already registered
+ if (askmarry(.@partner$) == true)
+ {
+ marriageregisterself();
+ marriageacceptpartner(.@partner$);
+ npctalk l("@@ registered for marriage and accepted partner @@!", strcharinfo(0), .@partner$);
+ npctalk l("Waiting for @@...", .@partner$);
+ close;
+ }
+ else
+ {
+ close;
+ }
+ }
+ return;
+}
+
+// remove all marriage registations
+function script marriageclear {
+ set getvariableofnpc(.maleName$, strnpcinfo(3)), "";
+ set getvariableofnpc(.femaleName$, strnpcinfo(3)), "";
+ set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), "";
+ set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), "";
+ return;
+}
+
+// do actual marriage
+function script domarriage {
+ .@name$ = strcharinfo(0);
+ .@partner$ = getarg(0);
+ if (marriage(.@partner$))
+ {
+ speech l("You got married to @@!", .@partner$);
+ npctalk l("@@ and @@ just got married!", .@name$, .@partner$);
+ }
+ else
+ {
+ npctalk l("Marriage failed.");
+ }
+ marriageclear();
+ return;
+}
+
+// marry main code
+function script marriagemarry {
+ .@registrant$ = getmarriageregistrant();
+ if (marriagecheckname(.@registrant$) == true)
+ {
+ if (.@registrant$ == strcharinfo(0))
+ {
+ if (ismarriagepartneraccepted())
+ {
+ .@partner$ = getmarriagepartner();
+ if (marriagecheckname(.@partner$) == false)
+ {
+ speech l("Partner not ready.");
+ }
+ else if (askmarry(.@partner$) == true)
+ {
+ domarriage(.@partner$);
+ }
+ }
+ else
+ {
+ speech l("You already registered. Waiting for your partner...");
+ }
+ }
+ else
+ {
+ speech l("Sorry, I'm busy with other registrations."),
+ l("Come after a little while.");
+ }
+ close;
+ }
+
+ speech l("What can I do for you?");
+ switch (select(l("I want to register for marriage."),
+ l("Nothing")))
+ {
+ case 1:
+ marriageregister;
+ break;
+ case 2:
+ break;
+ }
+ return;
+}
+
+// divorce main code
+function script marriagedivorce {
+ speech l("What can I do for you?");
+ switch (select(l("I want to divorce."),
+ l("Nothing")))
+ {
+ case 1:
+ speech lg("Are you sure?");
+ if (askyesno() == ASK_YES)
+ {
+ if (divorce())
+ {
+ speech l("You are now divorced!"),
+ l("Good look.");
+ npctalk l("@@ divorced!", strcharinfo(0));
+ }
+ else
+ {
+ speech l("Divorce error!");
+ }
+ }
+ break;
+ case 2:
+ break;
+ }
+ return;
+}
+
+// main function for marriage
+function script marriagemain {
+ if (Sex > 1)
+ {
+ speech l("Sorry i can't help you. Go away!");
+ close;
+ }
+
+ if (getpartnerid() != 0)
+ { // have partner
+ marriagedivorce();
+ }
+ else
+ { // no partner
+ marriagemarry();
+ }
+
+ return;
+}
+
+// check registration list by timer
+function script marriagecheck {
+ .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3));
+ if (.@name$ != "" && marriagecheckname(.@name) == false)
+ {
+ set getvariableofnpc(.maleName$, strnpcinfo(3)), "";
+ set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), "";
+ }
+ .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3));
+ if (.@name$ != "" && marriagecheckname(.@name) == false)
+ {
+ set getvariableofnpc(.femaleName$, strnpcinfo(3)), "";
+ set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), "";
+ }
+}
diff --git a/npc/functions/masks.txt b/npc/functions/masks.txt
new file mode 100644
index 000000000..98e8bdf87
--- /dev/null
+++ b/npc/functions/masks.txt
@@ -0,0 +1,68 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Triggers functions to add and remove masks.
+// Variables:
+// none
+
+// Artis Aemil's Legion
+
+function script artisALRemTopMask {
+ if ((getareausers("001-2-33", 23, 27, 45, 31) >= 1) ||
+ (getareausers("001-2-33", 23, 32, 26, 38) >= 1) ||
+ (getareausers("001-2-33", 42, 32, 45, 38) >= 1))
+ {
+ removemapmask "001-2-33", 4;
+ }
+ return 0;
+}
+
+function script artisALAddTopMask {
+ if ((getareausers("001-2-33", 23, 27, 45, 31) == 0) &&
+ (getareausers("001-2-33", 23, 32, 26, 38) == 0) &&
+ (getareausers("001-2-33", 42, 32, 45, 38) == 0))
+ {
+ addmapmask "001-2-33", 4;
+ }
+ return 0;
+}
+
+function script artisALRemBotMask {
+ if (getareausers("001-2-33", 23, 32, 45, 46) >= 1)
+ {
+ removemapmask "001-2-33", 8;
+ }
+ return 0;
+}
+
+function script artisALAddBotMask {
+ if (getareausers("001-2-33", 23, 32, 45, 46) == 0)
+ {
+ addmapmask "001-2-33", 8;
+ }
+ return 0;
+}
+
+function script artisALUpdateMask {
+ if (getareausers("001-2-33", 23, 32, 45, 46) >= 1)
+ {
+ removemapmask "001-2-33", 8;
+ }
+ else
+ {
+ addmapmask "001-2-33", 8;
+ }
+ if ((getareausers("001-2-33", 23, 27, 45, 31) >= 1) ||
+ (getareausers("001-2-33", 23, 32, 26, 38) >= 1) ||
+ (getareausers("001-2-33", 42, 32, 45, 38) >= 1))
+ {
+ removemapmask "001-2-33", 4;
+ }
+ else
+ {
+ addmapmask "001-2-33", 4;
+ }
+
+ return 0;
+}
diff --git a/npc/functions/math.txt b/npc/functions/math.txt
new file mode 100644
index 000000000..004125c7b
--- /dev/null
+++ b/npc/functions/math.txt
@@ -0,0 +1,40 @@
+// 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;
+}
diff --git a/npc/functions/mouboofunc.txt b/npc/functions/mouboofunc.txt
new file mode 100644
index 000000000..6129c083e
--- /dev/null
+++ b/npc/functions/mouboofunc.txt
@@ -0,0 +1,89 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Various scripts used in walking mouboo NPCs.
+
+function script moubootalk {
+ switch (rand(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 mouboocheckxy {
+ setarray .nearnpc$[0], "Mouboo#Artis0",
+ "Mouboo#Artis1",
+ "Mouboo#Artis2",
+ "Mouboo#Artis3",
+ "Taree";
+ for (.@size = 0; .@size < getarraysize(.nearnpc$); .@size++)
+ {
+ if (strcmp(.name$, .nearnpc$[.@size]) == 0)
+ {
+ continue;
+ }
+
+ .@npc_x = getvariableofnpc(.x, .nearnpc$[.@size]);
+ .@npc_y = getvariableofnpc(.y, .nearnpc$[.@size]);
+
+ if (.@npc_x == .x && .@npc_y == .y)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+function script moubootimer {
+ if (mouboocheckxy() && !isunitwalking())
+ {
+ movetonextpoint;
+ }
+ else if (rand(0,6) == 5)
+ {
+ if (!isunitwalking())
+ {
+ movetonextpoint;
+ }
+ }
+ initnpctimer;
+ end;
+}
+
+function script mouboograph {
+ initmovegraph "down_pos", 66, 79, 75, 86,
+ "left_pos", 66, 79, 75, 86,
+ "up_pos", 66, 79, 75, 86,
+ "right_pos", 66, 79, 75, 86;
+
+ setmovegraphcmd "down_pos", "left_pos", 1, "dir 2",
+ "down_pos", "up_pos", 1, "dir 4",
+ "down_pos", "right_pos", 1, "dir 6",
+ "left_pos", "down_pos", 1, "dir 0",
+ "left_pos", "up_pos", 1, "dir 4",
+ "left_pos", "right_pos", 1, "dir 6",
+ "up_pos", "down_pos", 1, "dir 0",
+ "up_pos", "left_pos", 1, "dir 2",
+ "up_pos", "right_pos", 1, "dir 6",
+ "right_pos", "down_pos", 1, "dir 0",
+ "right_pos", "left_pos", 1, "dir 2",
+ "right_pos", "up_pos", 1, "dir 4";
+
+ firstmove "wait 2", "down_pos";
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt
new file mode 100644
index 000000000..eb6acded0
--- /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(0), 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 000000000..b26f9a162
--- /dev/null
+++ b/npc/functions/npcmovegraph.txt
@@ -0,0 +1,485 @@
+// 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(0), 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(0), .@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;
+ npctalk implode(.@cmd$, " ");
+ }
+ 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/openbook.txt b/npc/functions/openbook.txt
new file mode 100644
index 000000000..0278415ce
--- /dev/null
+++ b/npc/functions/openbook.txt
@@ -0,0 +1,36 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Narrator dialogue to show the selected book.
+// Variables:
+// @book_name$ = The name of the book to read.
+
+function script openbook {
+ .@book_name$ = "\"" + l(getarg(0, getvariableofnpc(.book_name$, strnpcinfo(0)))) + "\"";
+
+ narrator S_LAST_NEXT,
+ l("You open a book named @@.", .@book_name$),
+ l("Do you want to read it?");
+
+ return (select("Yes.", "No.") == 1);
+}
+
+function script openbookshelf {
+ .@book_name$ = "\"" + l(getarg(0, getvariableofnpc(.book_name$, strnpcinfo(0)))) + "\"";
+
+ narrator S_LAST_NEXT,
+ l("You see a dust covered book on the shelf..."),
+ l("The name of the book is @@.", .@book_name$),
+ l("Do you want to read it?");
+
+ return (select("Yes.", "No.") == 1);
+}
+
+function script openoldbook {
+ narrator S_LAST_NEXT,
+ l("You open the book, but it looks like the sea water and time damaged it severely. Some pages are not readable anymore. Some others are simply missing."),
+ l("The old book seems to tell about the legend of Aemil. Would you like to read it?");
+
+ return (select("Yes.", "No.") == 1);
+}
diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt
new file mode 100644
index 000000000..62e0da6b7
--- /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))) ||
+ has_permission("send_gm", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
diff --git a/npc/functions/quest-debug/000-ShipQuests_Julia.txt b/npc/functions/quest-debug/000-ShipQuests_Julia.txt
new file mode 100644
index 000000000..e4e0ec2cd
--- /dev/null
+++ b/npc/functions/quest-debug/000-ShipQuests_Julia.txt
@@ -0,0 +1,37 @@
+// Julia quest debug
+// Author:
+// gumi
+
+function script QuestDebug0 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Julia";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Julia));
+ mes "---";
+ mes l("Related quests:");
+ mes "ShipQuests_Nard: " + getq(ShipQuests_Nard);
+ mes "ShipQuests_ChefGado: " + getq(ShipQuests_ChefGado);
+ mes "General_Narrator: " + getq(General_Narrator);
+ next;
+
+ GenericQuestDebug ShipQuests_Julia,
+ menuimage("actions/manage", l("Debug Nard quest")), -1,
+ menuimage("actions/manage", l("Debug Gado quest")), -2,
+ menuimage("actions/manage", l("Debug Narrator")), -3,
+ l("Does not have the quest"), 0,
+ l("Got the quest"), 1,
+ l("Completed"), 2;
+
+ switch (@menuret)
+ {
+ case -1: callfunc "QuestDebug4"; break;
+ case -2: callfunc "QuestDebug17"; break;
+ case -3: callfunc "QuestDebug28"; break;
+ default: if (@menuret < 0) return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/001-ShipQuests_Arpan.txt b/npc/functions/quest-debug/001-ShipQuests_Arpan.txt
new file mode 100644
index 000000000..26c496466
--- /dev/null
+++ b/npc/functions/quest-debug/001-ShipQuests_Arpan.txt
@@ -0,0 +1,27 @@
+// Arpan quest debug
+// Author:
+// gumi
+
+function script QuestDebug1 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Arpan";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Arpan));
+ next;
+
+ GenericQuestDebug ShipQuests_Arpan,
+ l("Arpan is waiting for you"), 0,
+ l("Arpan told you to open the chest"), 1,
+ l("You opened the chest"), 2,
+ l("Completed"), 3;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/002-ShipQuests_Alige.txt b/npc/functions/quest-debug/002-ShipQuests_Alige.txt
new file mode 100644
index 000000000..36d0ce23e
--- /dev/null
+++ b/npc/functions/quest-debug/002-ShipQuests_Alige.txt
@@ -0,0 +1,27 @@
+// Alige quest debug
+// Author:
+// gumi
+
+function script QuestDebug2 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Alige";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Alige));
+ next;
+
+ GenericQuestDebug ShipQuests_Alige,
+ l("Does not have the quest"), 0,
+ l("First encounter"), 1,
+ l("Alige asks for food"), 2,
+ l("Completed"), 3;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/003-ShipQuests_Peter.txt b/npc/functions/quest-debug/003-ShipQuests_Peter.txt
new file mode 100644
index 000000000..e08e1eb24
--- /dev/null
+++ b/npc/functions/quest-debug/003-ShipQuests_Peter.txt
@@ -0,0 +1,30 @@
+// Peter quest debug
+// Author:
+// gumi
+
+function script QuestDebug3 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Peter";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Peter));
+ next;
+
+ GenericQuestDebug ShipQuests_Peter,
+ l("Does not have the quest"), 0,
+ l("Peter needs help"), 6,
+ l("Peter asks to kill rattos"), 1,
+ l("Killed rattos"), 2,
+ l("Got no reward"), 3,
+ l("Peter asks to kill rattos again"), 4,
+ l("Peter gave reward"), 5;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/004-ShipQuests_Nard.txt b/npc/functions/quest-debug/004-ShipQuests_Nard.txt
new file mode 100644
index 000000000..9ec51cd87
--- /dev/null
+++ b/npc/functions/quest-debug/004-ShipQuests_Nard.txt
@@ -0,0 +1,38 @@
+// Nard quest debug
+// Author:
+// gumi
+
+function script QuestDebug4 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Nard";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Nard));
+ mes "---";
+ mes l("Subquests:");
+ mes "ShipQuests_Gugli: " + getq(ShipQuests_Gugli);
+ mes "ShipQuests_ChefGado: " + getq(ShipQuests_ChefGado);
+ next;
+
+ GenericQuestDebug ShipQuests_Nard,
+ l("Does not have the quest"), 0,
+ l("Nard asks to help crew"), 1,
+ menuimage("actions/manage", l("Debug Gugli quest")), -1,
+ l("Completed Gugli's task"), 2,
+ l("Nard asks to solve conflict"), 3,
+ menuimage("actions/manage", l("Debug Gado quest")), -2,
+ l("Solved conflict"), 4,
+ l("Official crew member"), 5,
+ l("Talked to narrator"), 6;
+
+ switch (@menuret)
+ {
+ case -1: callfunc "QuestDebug16"; break;
+ case -2: callfunc "QuestDebug17"; break;
+ default: if (@menuret < 0) return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/005-ShipQuests_Knife.txt b/npc/functions/quest-debug/005-ShipQuests_Knife.txt
new file mode 100644
index 000000000..a5ec1ae86
--- /dev/null
+++ b/npc/functions/quest-debug/005-ShipQuests_Knife.txt
@@ -0,0 +1,25 @@
+// Knife quest debug
+// Author:
+// gumi
+
+function script QuestDebug5 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Knife";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Knife));
+ next;
+
+ GenericQuestDebug ShipQuests_Knife,
+ l("Does not have the quest"), 0,
+ l("Got the knife"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/006-ShipQuests_ArpanMoney.txt b/npc/functions/quest-debug/006-ShipQuests_ArpanMoney.txt
new file mode 100644
index 000000000..00e2fbea8
--- /dev/null
+++ b/npc/functions/quest-debug/006-ShipQuests_ArpanMoney.txt
@@ -0,0 +1,26 @@
+// Money quest debug
+// Author:
+// gumi
+
+function script QuestDebug6 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_ArpanMoney";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_ArpanMoney));
+ next;
+
+ GenericQuestDebug ShipQuests_ArpanMoney,
+ l("Does not have the quest"), 0,
+ l("Elmo told about money"), 1,
+ l("Arpan gave money"), 2;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/007-ShipQuests_Door.txt b/npc/functions/quest-debug/007-ShipQuests_Door.txt
new file mode 100644
index 000000000..88aa00abe
--- /dev/null
+++ b/npc/functions/quest-debug/007-ShipQuests_Door.txt
@@ -0,0 +1,25 @@
+// Door quest debug
+// Author:
+// gumi
+
+function script QuestDebug7 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Door";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Door));
+ next;
+
+ GenericQuestDebug ShipQuests_Door,
+ l("Does not have the quest"), 0,
+ l("Heard conversation"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/008-ShipQuests_Couwan.txt b/npc/functions/quest-debug/008-ShipQuests_Couwan.txt
new file mode 100644
index 000000000..3df0cdbf0
--- /dev/null
+++ b/npc/functions/quest-debug/008-ShipQuests_Couwan.txt
@@ -0,0 +1,26 @@
+// Couwan quest debug
+// Author:
+// gumi
+
+function script QuestDebug8 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Couwan";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Couwan));
+ next;
+
+ GenericQuestDebug ShipQuests_Couwan,
+ l("Does not have the quest"), 0,
+ l("Couwan asks to deliver box"), 1,
+ l("Delivered box, got reward"), 2;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/009-ShipQuests_TreasureChest.txt b/npc/functions/quest-debug/009-ShipQuests_TreasureChest.txt
new file mode 100644
index 000000000..97adef46f
--- /dev/null
+++ b/npc/functions/quest-debug/009-ShipQuests_TreasureChest.txt
@@ -0,0 +1,25 @@
+// Treasure chest quest debug
+// Author:
+// gumi
+
+function script QuestDebug9 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_TreasureChest";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_TreasureChest));
+ next;
+
+ GenericQuestDebug ShipQuests_TreasureChest,
+ l("Does not have the quest"), 0,
+ l("Opened treasure chest"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/010-ShipQuests_Ale.txt b/npc/functions/quest-debug/010-ShipQuests_Ale.txt
new file mode 100644
index 000000000..e54221224
--- /dev/null
+++ b/npc/functions/quest-debug/010-ShipQuests_Ale.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug10 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Ale";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Ale));
+ next;
+
+ GenericQuestDebug ShipQuests_Ale,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/011-ShipQuests_Astapolos.txt b/npc/functions/quest-debug/011-ShipQuests_Astapolos.txt
new file mode 100644
index 000000000..aca065edb
--- /dev/null
+++ b/npc/functions/quest-debug/011-ShipQuests_Astapolos.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug11 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Astapolos";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Astapolos));
+ next;
+
+ GenericQuestDebug ShipQuests_Astapolos,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/012-ShipQuests_Gulukan.txt b/npc/functions/quest-debug/012-ShipQuests_Gulukan.txt
new file mode 100644
index 000000000..fac7ccded
--- /dev/null
+++ b/npc/functions/quest-debug/012-ShipQuests_Gulukan.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug12 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Gulukan";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Gulukan));
+ next;
+
+ GenericQuestDebug ShipQuests_Gulukan,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/013-ShipQuests_Jalad.txt b/npc/functions/quest-debug/013-ShipQuests_Jalad.txt
new file mode 100644
index 000000000..9ae6ed2a2
--- /dev/null
+++ b/npc/functions/quest-debug/013-ShipQuests_Jalad.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug13 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Jalad";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Jalad));
+ next;
+
+ GenericQuestDebug ShipQuests_Jalad,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/014-ShipQuests_QMuller.txt b/npc/functions/quest-debug/014-ShipQuests_QMuller.txt
new file mode 100644
index 000000000..8a25ee653
--- /dev/null
+++ b/npc/functions/quest-debug/014-ShipQuests_QMuller.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug14 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_QMuller";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_QMuller));
+ next;
+
+ GenericQuestDebug ShipQuests_QMuller,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/015-ShipQuests_Tibbo.txt b/npc/functions/quest-debug/015-ShipQuests_Tibbo.txt
new file mode 100644
index 000000000..84280ebfe
--- /dev/null
+++ b/npc/functions/quest-debug/015-ShipQuests_Tibbo.txt
@@ -0,0 +1,25 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug15 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Tibbo";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Tibbo));
+ next;
+
+ GenericQuestDebug ShipQuests_Tibbo,
+ l("Does not have the quest"), 0,
+ l("Got the package"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/016-ShipQuests_Gugli.txt b/npc/functions/quest-debug/016-ShipQuests_Gugli.txt
new file mode 100644
index 000000000..e4868ff48
--- /dev/null
+++ b/npc/functions/quest-debug/016-ShipQuests_Gugli.txt
@@ -0,0 +1,48 @@
+// Part of Gugli quest debug
+// Author:
+// gumi
+
+function script QuestDebug16 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_Gugli";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_Gugli));
+ mes "---";
+ mes l("Subquests:");
+ mes "ShipQuests_Ale: " + getq(ShipQuests_Ale);
+ mes "ShipQuests_Astapolos: " + getq(ShipQuests_Astapolos);
+ mes "ShipQuests_Gulukan: " + getq(ShipQuests_Gulukan);
+ mes "ShipQuests_Jalad: " + getq(ShipQuests_Jalad);
+ mes "ShipQuests_QMuller: " + getq(ShipQuests_QMuller);
+ mes "ShipQuests_Tibbo: " + getq(ShipQuests_Tibbo);
+ next;
+
+ GenericQuestDebug ShipQuests_Gugli,
+ l("Does not have the quest"), 0,
+ l("Gugli asks to collect packages"), 1,
+ menuimage("actions/manage", l("Reset subquests")), -1,
+ menuimage("actions/manage", l("Set subquests as completed")), -2,
+ l("Got reward from gugli"), 2;
+
+ switch (@menuret)
+ {
+ case 0:
+ case 2:
+ case -1:
+ case -2:
+ .@v = (@menuret == -2 || @menuret == 2);
+ setq ShipQuests_Ale, .@v;
+ setq ShipQuests_Astapolos, .@v;
+ setq ShipQuests_Gulukan, .@v;
+ setq ShipQuests_Jalad, .@v;
+ setq ShipQuests_QMuller, .@v;
+ setq ShipQuests_Tibbo, .@v;
+ break;
+ default: if (@menuret < 0) return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/017-ShipQuests_ChefGado.txt b/npc/functions/quest-debug/017-ShipQuests_ChefGado.txt
new file mode 100644
index 000000000..05e8cdb09
--- /dev/null
+++ b/npc/functions/quest-debug/017-ShipQuests_ChefGado.txt
@@ -0,0 +1,30 @@
+// Gado quest debug
+// Author:
+// gumi
+
+function script QuestDebug17 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ShipQuests_ChefGado";
+ mes "---";
+ mes l("Quest state: @@", getq(ShipQuests_ChefGado));
+ next;
+
+ GenericQuestDebug ShipQuests_ChefGado,
+ l("Does not have the quest"), 0,
+ l("Nard asks to solve conflict"), 1,
+ l("Got poison from Gado"), 2,
+ l("Poisoned Julia"), 3,
+ l("Completed, Gado wins"), 4,
+ l("Completed, Julia wins (returned poison)"), 5,
+ l("Completed, Julia wins"), 6;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/018-General_Cookies.txt b/npc/functions/quest-debug/018-General_Cookies.txt
new file mode 100644
index 000000000..16c7bcd75
--- /dev/null
+++ b/npc/functions/quest-debug/018-General_Cookies.txt
@@ -0,0 +1,25 @@
+// Cookie quest debug
+// Author:
+// gumi
+
+function script QuestDebug18 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "General_Cookies";
+ mes "---";
+ mes l("Quest state: @@", getq(General_Cookies));
+ next;
+
+ GenericQuestDebug General_Cookies,
+ l("Does not have the quest"), 0,
+ l("Got a cookie"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/019-ArtisQuests_LazyBrother.txt b/npc/functions/quest-debug/019-ArtisQuests_LazyBrother.txt
new file mode 100644
index 000000000..41733adf8
--- /dev/null
+++ b/npc/functions/quest-debug/019-ArtisQuests_LazyBrother.txt
@@ -0,0 +1,28 @@
+// Lazy brother quest debug
+// Author:
+// gumi
+
+function script QuestDebug19 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_LazyBrother";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_LazyBrother));
+ next;
+
+ GenericQuestDebug ArtisQuests_LazyBrother,
+ l("Does not have the quest"), 0,
+ l("Katja asked for help"), 1,
+ l("Found bobo, didn't tell Katja"), 2,
+ l("Told bobo to go home"), 3,
+ l("Katja gave reward"), 4;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/020-ArtisQuests_Urchin.txt b/npc/functions/quest-debug/020-ArtisQuests_Urchin.txt
new file mode 100644
index 000000000..3a092e478
--- /dev/null
+++ b/npc/functions/quest-debug/020-ArtisQuests_Urchin.txt
@@ -0,0 +1,26 @@
+// Urchin quest debug
+// Author:
+// gumi
+
+function script QuestDebug20 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_Urchin";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_Urchin));
+ next;
+
+ GenericQuestDebug ArtisQuests_Urchin,
+ l("Does not have the quest"), 0,
+ l("Moon needs @@", l("Croc Claw")), 1,
+ l("Found @@, got exp", l("Croc Claw")), 2;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/021-ArtisQuests_CatchPiou.txt b/npc/functions/quest-debug/021-ArtisQuests_CatchPiou.txt
new file mode 100644
index 000000000..f9c45339e
--- /dev/null
+++ b/npc/functions/quest-debug/021-ArtisQuests_CatchPiou.txt
@@ -0,0 +1,26 @@
+// Piou quest debug
+// Author:
+// gumi
+
+function script QuestDebug21 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_CatchPiou";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_CatchPiou));
+ next;
+
+ GenericQuestDebug ArtisQuests_CatchPiou,
+ l("Does not have the quest"), 0,
+ l("Salem asks to catch piou"), 1,
+ l("Returned piou to Salem"), 2;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/022-ArtisQuests_Fishman.txt b/npc/functions/quest-debug/022-ArtisQuests_Fishman.txt
new file mode 100644
index 000000000..0a193e6fe
--- /dev/null
+++ b/npc/functions/quest-debug/022-ArtisQuests_Fishman.txt
@@ -0,0 +1,26 @@
+// Eugene quest debug
+// Author:
+// gumi
+
+function script QuestDebug22 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_Fishman";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_Fishman));
+ next;
+
+ GenericQuestDebug ArtisQuests_Fishman,
+ l("Does not have the quest"), 0,
+ l("Eugene needs tentacles"), 1,
+ l("Gave tentacles, got reward"), 2;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/023-ArtisQuests_QOnan.txt b/npc/functions/quest-debug/023-ArtisQuests_QOnan.txt
new file mode 100644
index 000000000..02eaa8f60
--- /dev/null
+++ b/npc/functions/quest-debug/023-ArtisQuests_QOnan.txt
@@ -0,0 +1,27 @@
+// Q'Onan quest debug
+// Author:
+// gumi
+
+function script QuestDebug23 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_QOnan";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_QOnan));
+ next;
+
+ GenericQuestDebug ArtisQuests_QOnan,
+ l("Does not have the quest"), 0,
+ l("Q'Onan asks to find chest"), 1,
+ l("Found the chest"), 2,
+ l("Gave to Q'Onan, got reward"), 3;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/026-General_Rumly.txt b/npc/functions/quest-debug/026-General_Rumly.txt
new file mode 100644
index 000000000..c533c27a0
--- /dev/null
+++ b/npc/functions/quest-debug/026-General_Rumly.txt
@@ -0,0 +1,27 @@
+// Rumly quest debug
+// Author:
+// gumi
+
+function script QuestDebug26 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "General_Rumly";
+ mes "---";
+ mes l("Quest state: @@", getq(General_Rumly));
+ next;
+
+ GenericQuestDebug General_Rumly,
+ l("Does not have the quest"), 0,
+ l("Rumly needs your help"), 1,
+ l("Rumly wants @@", l("Plushroom")), 2,
+ l("Gave @@ to Rumly", l("Plushroom")), 3;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/027-ArtisQuests_Enora.txt b/npc/functions/quest-debug/027-ArtisQuests_Enora.txt
new file mode 100644
index 000000000..9121ceb30
--- /dev/null
+++ b/npc/functions/quest-debug/027-ArtisQuests_Enora.txt
@@ -0,0 +1,49 @@
+// Newbie quest debug
+// Author:
+// gumi
+
+function script QuestDebug27 {
+ do
+ {
+ clear();
+ setnpcdialogtitle(l("Quest debug"));
+ mes("ArtisQuests_Enora");
+ mes("---");
+ mes(l("Quest state: @@, @@",
+ getq(ArtisQuests_Enora),
+ getq2(ArtisQuests_Enora)));
+ next();
+
+ GenericQuestDebug(ArtisQuests_Enora,
+ l("Does not have the quest"), 0,
+ l("Enora asks to visit Chelios"), 1,
+ l("Chelios asks to visit Lloyd"), 2,
+ l("Lloyd gave package"), 3,
+ l("Chelios made sword"), 4,
+ l("Enora asks to visit Resa"), 5,
+ l("Resa gave package"), 6,
+ l("Enora asks to visit Q'Pid"), 7,
+ l("Lost the riddle"), 8,
+ l("Ivan gave package"), 9,
+ l("Enora asks to kill fluffies"), 10,
+ l("Completed, got reward"), 11);
+
+ switch (@menuret)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10: setq(ArtisQuests_Enora, getq(ArtisQuests_Enora), 0); break;
+ case 11: setq(ArtisQuests_Enora, getq(ArtisQuests_Enora), 10); break;
+ default: if (@menuret < 0) return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/028-General_Narrator.txt b/npc/functions/quest-debug/028-General_Narrator.txt
new file mode 100644
index 000000000..408668b7a
--- /dev/null
+++ b/npc/functions/quest-debug/028-General_Narrator.txt
@@ -0,0 +1,25 @@
+// Narrator debug
+// Author:
+// gumi
+
+function script QuestDebug28 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "General_Narrator";
+ mes "---";
+ mes l("Quest state: @@", getq(General_Narrator));
+ next;
+
+ GenericQuestDebug General_Narrator,
+ l("Game introduction"), 0,
+ l("Visited Artis at least once"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/029-ArtisQuests_Fexil.txt b/npc/functions/quest-debug/029-ArtisQuests_Fexil.txt
new file mode 100644
index 000000000..9c0d7945d
--- /dev/null
+++ b/npc/functions/quest-debug/029-ArtisQuests_Fexil.txt
@@ -0,0 +1,27 @@
+// Fexil quest debug
+// Author:
+// gumi
+
+function script QuestDebug29 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_Fexil";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_Fexil));
+ next;
+
+ GenericQuestDebug ArtisQuests_Fexil,
+ l("Does not have the quest"), 0,
+ l("Lloyd gave pass"), 1,
+ l("Fexil needs help"), 2,
+ l("Fexil wants to buy fur"), 3;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/030-ArtisQuests_Lloyd.txt b/npc/functions/quest-debug/030-ArtisQuests_Lloyd.txt
new file mode 100644
index 000000000..0afb019c8
--- /dev/null
+++ b/npc/functions/quest-debug/030-ArtisQuests_Lloyd.txt
@@ -0,0 +1,25 @@
+// Lloyd quest debug
+// Author:
+// gumi
+
+function script QuestDebug30 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_Lloyd";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_Lloyd));
+ next;
+
+ GenericQuestDebug ArtisQuests_Lloyd,
+ l("Does not have the quest"), 0,
+ l("Registered"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/031-General_Janus.txt b/npc/functions/quest-debug/031-General_Janus.txt
new file mode 100644
index 000000000..f97b4fbeb
--- /dev/null
+++ b/npc/functions/quest-debug/031-General_Janus.txt
@@ -0,0 +1,31 @@
+// Janus quest debug
+// Author:
+// gumi
+
+function script QuestDebug31 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "General_Janus";
+ mes "---";
+ mes l("Quest state: @@", getq(General_Janus));
+ next;
+
+ GenericQuestDebug General_Janus,
+ l("Does not have the quest"), 0,
+ l("Talked to Janus"), 1,
+ l("Can create party"), 2,
+ l("Can create guild"), 3;
+
+ switch (@menuret)
+ {
+ case 0:
+ case 1: skill NV_BASIC, min(6, getskilllv(NV_BASIC)), 0; break;
+ case 2:
+ case 3: skill NV_BASIC, max(7, getskilllv(NV_BASIC)), 0; break;
+ default: if (@menuret < 0) return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/032-ArtisQuests_MonaDad.txt b/npc/functions/quest-debug/032-ArtisQuests_MonaDad.txt
new file mode 100644
index 000000000..68ba67fe9
--- /dev/null
+++ b/npc/functions/quest-debug/032-ArtisQuests_MonaDad.txt
@@ -0,0 +1,25 @@
+// Mona quest debug
+// Author:
+// gumi
+
+function script QuestDebug32 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "ArtisQuests_MonaDad";
+ mes "---";
+ mes l("Quest state: @@", getq(ArtisQuests_MonaDad));
+ next;
+
+ GenericQuestDebug ArtisQuests_MonaDad,
+ l("Does not have the quest"), 0,
+ l("Mona's dad is missing"), 1;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/033-Artis_Legion_Progress.txt b/npc/functions/quest-debug/033-Artis_Legion_Progress.txt
new file mode 100644
index 000000000..c4ea5558a
--- /dev/null
+++ b/npc/functions/quest-debug/033-Artis_Legion_Progress.txt
@@ -0,0 +1,30 @@
+// Mona quest debug
+// Authors:
+// gumi
+// monwarez
+
+function script QuestDebug33 {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes "Artis_Legion_Progress";
+ mes "---";
+ mes l("Quest state: @@", getq(Artis_Legion_Progress));
+ next;
+
+ GenericQuestDebug Artis_Legion_Progress,
+ l("Does not have the quest"), 0,
+ l("Sent to training"), 1,
+ l("Finished training"), 2,
+ l("Sent to battle"), 3,
+ l("Finished battle"), 4,
+ l("Sent to Q'Anon"), 5;
+
+ if (@menuret < 0)
+ {
+ return;
+ }
+
+ } while (1);
+}
diff --git a/npc/functions/quest-debug/functions.txt b/npc/functions/quest-debug/functions.txt
new file mode 100644
index 000000000..f89adfab7
--- /dev/null
+++ b/npc/functions/quest-debug/functions.txt
@@ -0,0 +1,109 @@
+// Evol functions.
+// Author:
+// gumi
+// Description:
+// generic quest debug functions
+
+
+
+// GenericQuestDebug
+// makes a generic quest debug menu for the given quest when the server
+// is in debug mode.
+
+function script GenericQuestDebug {
+ if (!debug && !is_admin())
+ {
+ select
+ menuimage("actions/back", l("Go back"));
+
+ @menuret = -0x7FFFFFFF;
+ return 1;
+ }
+
+ mes l("Choose desired quest state:");
+
+ deletearray .@vals;
+ .@menustr$ = menuimage("actions/abort", l("Do nothing")) + ":";
+ .@vals[0] = -0x7FFFFFFF;
+ .@cnt = 1;
+
+ for (.@f = 1; .@f < getargcount(); .@f += 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@qv = getarg(.@f + 1);
+ .@s$ = menuimage("actions/" + (.@qv ? "edit" : "reset"), "[" + .@qv + "] " + getarg(.@f));
+ .@menustr$ += (.@qv < 0 ? getarg(.@f) : .@s$) + ":";
+ .@vals[.@cnt] = .@qv;
+ ++.@cnt;
+ }
+ }
+
+ if (is_admin())
+ {
+ .@menustr$ += menuimage("actions/nuke", l("Set state manually"));
+ .@vals[.@cnt] = -0x7FFFFFFE;
+ }
+
+
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return -1;
+
+ --@menu;
+ if (@menu < 0 || @menu >= getarraysize(.@vals))
+ return -1;
+
+ @menuret = .@vals[@menu];
+
+ switch (@menuret)
+ {
+ case -0x7FFFFFFE: input @menuret; setq getarg(0,0), @menuret;
+ case -0x7FFFFFFF: return 1;
+ }
+
+ if (@menuret >= 0)
+ {
+ setq getarg(0,0), @menuret;
+ }
+ return;
+}
+
+
+
+// selectd
+// like a normal select() but gives access to quest debug
+//
+// quest id can either be passed as first argument
+// or as the .quest_debug npc variable
+
+function script selectd {
+ .@menustr$ = rif(debug, menuaction(l("debug")));
+ .@count = getargcount();
+ .@f = 0;
+ .@questID = getvariableofnpc(.quest_debug, strnpcinfo(0));
+
+ if (.@count > 1 && !isstr(getarg(0)))
+ {
+ ++.@f;
+ .@questID = getarg(0);
+ }
+
+ for (; .@f < .@count; ++.@f)
+ {
+ .@menustr$ += ":" + getarg(.@f);
+ }
+
+ @menu = 255;
+ select .@menustr$;
+
+ switch (@menu)
+ {
+ case -1:
+ case 255: return -1;
+ default: @menu -= 1; return @menu; // FIXME: here I would have done `return --@menu;` but hercules prevents this
+ case 1: callfunc "QuestDebug" + .@questID; closedialog; end;
+ }
+}
diff --git a/npc/functions/questgen.txt b/npc/functions/questgen.txt
new file mode 100644
index 000000000..285924cbd
--- /dev/null
+++ b/npc/functions/questgen.txt
@@ -0,0 +1,38 @@
+// Evol functions.
+// Authors:
+// monwarez
+// Reid
+// Description:
+// Generate a random quest type and location.
+// Variables:
+// @quests : 2D array containing quest type and location availability
+// with @nb_type rows and @nb_location columns.
+// boolean value determine if the connection is possible between
+// the quest type and location selected.
+// @nb_difficulty : difficulty value from 0 to N-1.
+// Argument:
+// 0 : quest type
+// 1 : quest location
+// 2 : quest difficulty
+// Return : Tridimentional array value;
+
+function script questgen {
+
+ .@argc = getargcount();
+ @q_type = getarg(0, rand(@nb_type * 10) / 10);
+
+ do
+ {
+ @q_location = getarg(1, rand(@nb_location * 10) / 10);
+ .@cell = @q_type * @nb_location + @q_location;
+
+ if (!@quests[.@cell] && .@argc >= 2)
+ {
+ return false;
+ }
+ } while (!@quests[.@cell]);
+
+ @q_difficulty = getarg(2, rand(@nb_difficulty * 10) / 10);
+
+ return true;
+}
diff --git a/npc/functions/riddle.txt b/npc/functions/riddle.txt
new file mode 100644
index 000000000..fb503e3b3
--- /dev/null
+++ b/npc/functions/riddle.txt
@@ -0,0 +1,74 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Riddle enigma validator
+//
+// Arguments
+// 0 PC answer
+// 1 English correct answer
+// 2 Translated correct answer
+
+// TODO: levenshtein(), similar_text(), and maybe even soundex()
+
+function script riddlevalidation {
+ .@answer$ = strtolower(getarg(0));
+ .@good$ = strtolower(getarg(1));
+ .@good_translated$ = strtolower(getarg(2));
+
+ .@size_answer = getstrlen(.@answer$);
+ .@size_good = getstrlen(.@good$);
+ .@size_good_translated = getstrlen(.@good_translated$);
+
+ .@max = max(.@size_answer, .@size_good_translated, .@size_good);
+
+ // Input is too long.
+ if (.@max > 20)
+ {
+ return false;
+ }
+
+ .@size_good *= 70;
+ .@size_good_translated *= 70;
+
+ for (.@i = 0; .@i < .@max; .@i++)
+ {
+ .@correct = 0;
+ .@correct_translated = 0;
+
+ for (.@k = .@k_translated = .@j = .@i; .@j < .@max; .@j++)
+ {
+ if (charat(.@answer$, .@j) == charat(.@good$, .@k))
+ {
+ .@correct++;
+ .@k++;
+ }
+ else
+ {
+ .@correct--;
+ }
+
+ if (charat(.@answer$, .@j) ==
+ charat(.@good_translated$, .@k_translated))
+ {
+ .@correct_translated++;
+ .@k_translated++;
+ }
+ else
+ {
+ .@correct_translated--;
+ }
+ }
+ // if 70% of the word is correct
+ .@correct *= 100;
+ .@correct_translated *= 100;
+
+ if (.@correct >= .@size_good ||
+ .@correct_translated >= .@size_good_translated)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/npc/functions/sailordialogue.txt b/npc/functions/sailordialogue.txt
new file mode 100644
index 000000000..281246f7f
--- /dev/null
+++ b/npc/functions/sailordialogue.txt
@@ -0,0 +1,53 @@
+// Evol functions.
+// Authors:
+// Qwerty Dragon
+// Reid
+// Description:
+// Random sailor dialogues between two categories of NPCs.
+
+function script sailorfood {
+ mesn;
+
+ .@q = rand(0, 400) / 100;
+ if (.@q == 0) goto L_RandomA;
+ if (.@q == 1) goto L_RandomB;
+ if (.@q > 1) goto L_RandomC;
+
+L_RandomA:
+ mesq l("Hey.");
+ next;
+ mesq l("What did Gugli say about the box? Was it ok?");
+ next;
+
+ menu
+ l("It's ok."), L_Fine,
+ l("He needs more food."), -;
+
+ mes "";
+ mesn;
+ mesq l("Oh really? I'll put more food in the next box then.");
+
+ close;
+
+L_Fine:
+ mes "";
+ mesn;
+ mesq l("It's alright! Just one more box and it'll be ok.");
+
+ close;
+
+L_RandomB:
+ mesq l("Thanks for the help!");
+ next;
+ mesq l("These boxes are way too heavy to be lifted by only one person, all the way onto the ship.");
+
+ close;
+
+L_RandomC:
+ mesq l("I think I'll be done soon, since I almost have a box full of @@s!", getitemlink(CrocClaw));
+ next;
+ mesq l("And you? How's it going on your side?");
+ next;
+
+ return;
+}
diff --git a/npc/functions/sailortalk.txt b/npc/functions/sailortalk.txt
new file mode 100644
index 000000000..73145768b
--- /dev/null
+++ b/npc/functions/sailortalk.txt
@@ -0,0 +1,37 @@
+// Evol functions.
+// Author:
+// Reid
+// Description:
+// Tell a random sentence.
+// Variables:
+// .@rand = Random number between the number of sentence choice.
+
+function script sailortalk {
+
+ .@rand = rand(8);
+ if (.@rand == 0) goodbye;
+ if (.@rand == 1)
+ {
+ speech(
+ l("These purple mushrooms are called @@s. There are plenty of 'em on this island!", getitemlink(Plushroom)),
+ l("It's a kind of mushroom that tastes like a marshmallow and looks like a plush! @@, get it?", getitemlink(Plushroom)),
+ l("These funny fungi are mushrooming all around this island. Just pick some @@s and have a try.", getitemlink(Plushroom)));
+ close;
+ }
+ if (.@rand == 2) npctalkonce(l("Good to hear from you!"));
+ if (.@rand == 3) npctalkonce(l("So finally someone has came to visit me?"));
+ if (.@rand == 4)
+ {
+ 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("We are glad captain Nard has let you join the crew!"));
+ if (.@rand == 7) npctalkonce(l("Howdy?"));
+
+ end;
+}
diff --git a/npc/functions/savepoint.txt b/npc/functions/savepoint.txt
new file mode 100644
index 000000000..0e0b6f908
--- /dev/null
+++ b/npc/functions/savepoint.txt
@@ -0,0 +1,48 @@
+// Evol functions.
+// Authors:
+// gumi
+// Reid
+// Description:
+// Adds a new save point location.
+// Usage:
+// savepointparticle;
+// savepointparticle NO_INN;
+// savepointparticle map, x, y, NO_INN;
+// Description:
+// Save location with arguments:
+// getarg(0) map name,
+// getarg(1) x's value,
+// getarg(2) y's value,
+// getarg(3) INN flag.
+
+function script savepointparticle {
+
+ if (gettimetick(2) - @lastSave < 5)
+ return;
+
+ .@mapname$ = getarg(0, "");
+ .@mapx = getarg(1, -1);
+ .@mapy = getarg(2, -1);
+ .@i = 3;
+
+ if (.@mapy < 1)
+ {
+ .@npc$ = strnpcinfo(0);
+ .@mapname$ = getvariableofnpc(.map$, .@npc$);
+ .@mapx = getvariableofnpc(.x, .@npc$);
+ .@mapy = getvariableofnpc(.y, .@npc$);
+ .@i = 0;
+ }
+
+ message strcharinfo(0), l("Your position has been saved.");
+
+ if (getarg(.@i, NO_INN) == NO_INN)
+ {
+ INN_REGISTER = NO_INN;
+ }
+ savepoint .@mapname$, .@mapx, .@mapy;
+ specialeffect(4, SELF, getcharid(3));
+ @lastSave = gettimetick(2);
+
+ return;
+}
diff --git a/npc/functions/shops.txt b/npc/functions/shops.txt
new file mode 100644
index 000000000..79890e815
--- /dev/null
+++ b/npc/functions/shops.txt
@@ -0,0 +1,13 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Shops utility functions
+// Variables:
+// none
+
+function script restoreshopitem {
+ if (shopcount(getarg(0)) < getarg(1))
+ sellitem getarg(0), -1, getarg(1);
+ return;
+}
diff --git a/npc/functions/string.txt b/npc/functions/string.txt
new file mode 100644
index 000000000..efeaf476d
--- /dev/null
+++ b/npc/functions/string.txt
@@ -0,0 +1,195 @@
+// 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 1: .@separator$ = " "; break; // French
+ default: .@separator$ = ","; // English (default)
+ }
+ }
+
+ for (.@i = .@len - 3; .@i > 0; .@i -= 3) {
+ .@number$ = insertchar(.@number$, .@separator$, .@i);
+ }
+
+ return .@number$;
+}
+
+
+
+// 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--;
+ }
+ }
+ 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
new file mode 100644
index 000000000..30ab9f469
--- /dev/null
+++ b/npc/functions/time.txt
@@ -0,0 +1,108 @@
+function script now {
+ 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);
+}
+
+
+// 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 max precision
+
+function script FuzzyTime {
+ .@future = getarg(0, now());
+ .@options = getarg(1, 0);
+ .@precision = getarg(2, 99);
+ .@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 000000000..e79070f3b
--- /dev/null
+++ b/npc/functions/timer.txt
@@ -0,0 +1,56 @@
+// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(getarg(5), getarg(6), .@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;
+}
+
+
+// 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;
+}
+
+// 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;
+}
diff --git a/npc/functions/util.txt b/npc/functions/util.txt
new file mode 100644
index 000000000..ae70b233d
--- /dev/null
+++ b/npc/functions/util.txt
@@ -0,0 +1,35 @@
+// Evol functions.
+// Authors:
+// Reid
+// Description:
+// Util functions
+
+
+// season_direction({day, month})
+// returns the direction that represents our current season (approximation)
+// DOWN: Winter, 21/12
+// DOWNLEFT: Spring, 20/03
+// LEFT: Summer, 21/06
+// UPLEFT: Autumn, 22/09
+
+function script season_direction {
+ .@current_month = getarg(0, gettime(GETTIME_MONTH));
+
+ if (.@current_month % 3 == 0)
+ {
+ .@current_day = getarg(1, gettime(GETTIME_DAYOFMONTH));
+
+ switch (.@current_month)
+ {
+ case MARCH: .@season_day = 20; break;
+ case JUNE: .@season_day = 21; break;
+ case SEPTEMBER: .@season_day = 22; break;
+ case DECEMBER: .@season_day = 21; break;
+ default: break;
+ }
+
+ .@is_after_season_day = .@current_day >= .@season_day ? 0 : -1;
+ }
+
+ return (.@current_month / 3 + .@is_after_season_day) % 4;
+}
diff --git a/npc/functions/villagertalk.txt b/npc/functions/villagertalk.txt
new file mode 100644
index 000000000..371a9f20b
--- /dev/null
+++ b/npc/functions/villagertalk.txt
@@ -0,0 +1,53 @@
+// Evol functions.
+// Authors:
+// Akko Teru
+// Qwerty Dragon
+// Reid
+// Description:
+// Tell a random sentence. || There ought to be a law!
+
+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 (rand(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;
+}