summaryrefslogtreecommitdiff
path: root/npc/functions
diff options
context:
space:
mode:
Diffstat (limited to 'npc/functions')
-rw-r--r--npc/functions/DyeConfig.txt41
-rw-r--r--npc/functions/alchemy.txt91
-rw-r--r--npc/functions/array.txt464
-rw-r--r--npc/functions/asklanguage.txt72
-rw-r--r--npc/functions/banker.txt182
-rw-r--r--npc/functions/barber.txt223
-rw-r--r--npc/functions/bitwise.txt176
-rw-r--r--npc/functions/clear_vars.txt30
-rw-r--r--npc/functions/confused-tree-dict.txt515
-rw-r--r--npc/functions/dailyquest.txt164
-rw-r--r--npc/functions/default_npc_checks.txt137
-rw-r--r--npc/functions/dynamic_menu.txt289
-rw-r--r--npc/functions/evil_obelisk.txt96
-rw-r--r--npc/functions/ferry.txt135
-rw-r--r--npc/functions/filters.txt132
-rw-r--r--npc/functions/game_rules.txt22
-rw-r--r--npc/functions/ghost.txt36
-rw-r--r--npc/functions/global_event_handler.txt49
-rw-r--r--npc/functions/gm_island.txt71
-rw-r--r--npc/functions/goodbye.txt152
-rw-r--r--npc/functions/headstyles.txt46
-rw-r--r--npc/functions/inc_sc_bonus.txt105
-rw-r--r--npc/functions/inn.txt34
-rw-r--r--npc/functions/input.txt110
-rw-r--r--npc/functions/inventoryplace.txt36
-rw-r--r--npc/functions/location.txt128
-rw-r--r--npc/functions/lockpicking.txt87
-rw-r--r--npc/functions/magic.txt159
-rw-r--r--npc/functions/main.txt750
-rw-r--r--npc/functions/math.txt105
-rw-r--r--npc/functions/miriam.txt17
-rw-r--r--npc/functions/mob_points.txt83
-rw-r--r--npc/functions/motd.txt10
-rw-r--r--npc/functions/motdconfig.txt38
-rw-r--r--npc/functions/npcmove.txt142
-rw-r--r--npc/functions/npcmovegraph.txt489
-rw-r--r--npc/functions/permissions.txt36
-rw-r--r--npc/functions/process_equip.txt26
-rw-r--r--npc/functions/quests.txt67
-rw-r--r--npc/functions/quiz.txt92
-rw-r--r--npc/functions/random-talk.txt207
-rw-r--r--npc/functions/scoreboards.txt273
-rw-r--r--npc/functions/slot_machine.txt92
-rw-r--r--npc/functions/soul_menhir.txt60
-rw-r--r--npc/functions/stat_reset.txt49
-rw-r--r--npc/functions/string.txt211
-rw-r--r--npc/functions/time.txt117
-rw-r--r--npc/functions/timer.txt89
-rw-r--r--npc/functions/travelers.txt235
-rw-r--r--npc/functions/undead_debug.txt111
-rw-r--r--npc/functions/vault.txt114
-rw-r--r--npc/functions/water_bottle.txt43
-rw-r--r--npc/functions/weather.txt244
53 files changed, 7482 insertions, 0 deletions
diff --git a/npc/functions/DyeConfig.txt b/npc/functions/DyeConfig.txt
new file mode 100644
index 00000000..60ab1e75
--- /dev/null
+++ b/npc/functions/DyeConfig.txt
@@ -0,0 +1,41 @@
+009-2,32,105,0 script #DyeChecker NPC32767,{
+ end;
+OnInit:
+ /******************************
+ Config Starts Here
+ ******************************/
+ setarray $@DYE_color_names$, "Red", "Green", "Dark Blue", "Yellow", "Light blue", "Pink", "Black", "Orange", "Purple", "Dark Green";
+ setarray $@DYE_colors$, "Red", "Green", "DarkBlue", "Yellow", "LightBlue", "Pink", "Black", "Orange", "Purple", "DarkGreen";
+
+ setarray $@DYE_items$, "Beret", "CottonShirt", "CottonCloth", "VNeckSweater", "Turtleneck", "CottonShorts", "CottonTrousers", "CottonSkirt", "Miniskirt", "TankTop", "ShortTankTop", "SilkRobe", "CottonHeadband", "DesertHat", "CottonBoots", "CottonGloves", "RabbitEars", "WizardHat", "BowlerHat", "BowlerHatBrown", "FineDress", "Contributor", "SorcererRed", "SorcererGreen", "SorcererDBlue", "SorcererYellow", "SorcererLBlue", "SorcererPink", "SorcererBlack", "SorcererOrange", "SorcererPurple", "SorcererDGreen", "SorcererWhite";
+ setarray $@DYE_item_names$, "Beret", "Cotton Shirt", "Cotton Cloth", "V-Neck Sweater", "Turtleneck Sweater", "Cotton Shorts", "Cotton Trousers", "Cotton Skirt", "Miniskirt", "Tank Top", "Short Tank Top", "Silk Robe", "Cotton Headband", "Desert Hat", "Cotton Boots", "Cotton Gloves", "Rabbit Ears", "Wizard Hat", "Bowler Hat", "Bowler Hat (brown)", "Fine Dress", "Contributor Shirt", "Sorcerer Robe (Red)", "Sorcerer Robe (Green)", "Sorcerer Robe (Dark Blue)", "Sorcerer Robe (Yellow)", "Sorcerer Robe (Light Blue)", "Sorcerer Robe (Pink)", "Sorcerer Robe (Black)", "Sorcerer Robe (Orange)", "Sorcerer Robe (Purple)", "Sorcerer Robe (Dark Green)", "Sorcerer Robe (White)";
+ /******************************
+ Config Ends Here
+ ******************************/
+
+ $@w = 0;
+ freeloop 1; // do not check for infinity loop
+ callsub S_Array;
+ freeloop 0; // re-enable infinity loop check
+ $@w = 0;
+ $@c = 0;
+ end;
+
+S_Array:
+ if(getitemname($@DYE_items$[$@w]) == "null") goto L_Fail;
+ $@c = 0;
+ callsub S_Color;
+ $@w = $@w + 1;
+ if($@w < getarraysize($@DYE_items$)) goto S_Array;
+ return;
+
+L_Fail:
+ debugmes "Dye Fail: "+$@DYE_items$[$@w];
+ mapexit;
+
+S_Color:
+ if(getitemname($@DYE_colors$[$@c] + $@DYE_items$[$@w]) == "null") goto L_Fail;
+ $@c = $@c + 1;
+ if($@c < getarraysize($@DYE_colors$)) goto S_Color;
+ return;
+}
diff --git a/npc/functions/alchemy.txt b/npc/functions/alchemy.txt
new file mode 100644
index 00000000..ea77011e
--- /dev/null
+++ b/npc/functions/alchemy.txt
@@ -0,0 +1,91 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Alchemy System (oversimplified)
+
+// Usage: AlchemySystem ()
+// Returns true on success, false on failure
+function script AlchemySystem {
+ .success=false;
+
+ setskin "craft2";
+ .@var$ = requestcraft(2);
+ .@craft = initcraft(.@var$);
+ .@entry = findcraftentry(.@craft, CRAFT_ALCHEMY);
+ if (debug || $@GM_OVERRIDE) mes "found craft entry: " + .@entry;
+ if (.@entry < 0) {
+ .success=false;
+ } else {
+ // Determine how many units to make
+ // This code comes from Moubootaur Legends
+ // Where sponsors could make up to 25 units
+ // And you could configure a fixed number to
+ // avoid prompts...
+ // ...
+ // PS. This is not using freeloop()
+ // Max amount is limited for performance.
+ if (GSET_FIXED_ALCHEMY) {
+ .@m=limit(1, GSET_FIXED_ALCHEMY, 25);
+ } else {
+ .@max=(is_trusted() ? 25 : 10);
+ mesc l("How many to brew? (%d-%d)", 1, .@max);
+ input(.@m, 1, .@max);
+ }
+ // Alchemy loop
+ .@i=0;
+ while (.@i < .@m) {
+ .@s=validatecraft(.@craft);
+ // Could not validate (not enough resources)
+ if (!.@s) {
+ mesc l("Insufficient materials to continue."), 1;
+ if (.@i)
+ mesc l("Only %d/%d units were produced.", .@i, .@m), 1;
+ break;
+ }
+ .@s=usecraft(.@craft);
+ .@i++;
+ // Exploiting?!
+ if (!.@s)
+ break;
+ }
+ .success=true;
+ }
+ deletecraft .@craft;
+ setskin "";
+ return .success;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Build the various crafting tables on the world
+001-2,92,76,0 script #AlchemyTable000 NPC_NO_SPRITE,{
+ title l("Alchemy Lab");
+ mes l("This is an alchemy lab.");
+ mes l("With it, you can combine reagents together to create powerful potions.");
+ next;
+ mes b(l("Drag & drop 2 items from your inventory. All items used will be lost."));
+ if (AlchemySystem())
+ mesc l("Success!"), 3;
+ else
+ mesc l("That didn't work!"), 1;
+ close;
+
+OnInit:
+ .distance=2;
+ end;
+}
+
+// Tonori Region (0~99)
+001-2,92,89,0 duplicate(#AlchemyTable000) #AlchemyTable001 NPC_NO_SPRITE
+001-2,98,76,0 duplicate(#AlchemyTable000) #AlchemyTable002 NPC_NO_SPRITE
+001-2,98,89,0 duplicate(#AlchemyTable000) #AlchemyTable003 NPC_NO_SPRITE
+
+// Argaes Region (100~199)
+026-2,35,117,0 duplicate(#AlchemyTable000) #AlchemyTable100 NPC_NO_SPRITE
+
+// Candor Region (200~299)
+029-2,113,57,0 duplicate(#AlchemyTable000) #AlchemyTable200 NPC_NO_SPRITE
+
+
+
+
diff --git a/npc/functions/array.txt b/npc/functions/array.txt
new file mode 100644
index 00000000..d433abd7
--- /dev/null
+++ b/npc/functions/array.txt
@@ -0,0 +1,464 @@
+// Evol Script
+// Author: Gumi
+
+// array_pad(<array>, <size>, <value>)
+// prepend or append <value> until the array is of <size> size
+// returns the amount added on success, or false (0) if nothing changed
+
+function script array_pad {
+ .@index = getarrayindex(getarg(0)); // passed index
+ .@count = getarraysize(getarg(0)) - .@index; // actual size
+ .@size = getarg(1); // desired size
+ .@absolute = (.@size >= 0 ? .@size : -(.@size)); // |size|
+ .@delta = .@absolute - .@count; // amount to fill
+
+ if (.@absolute <= .@count) {
+ return false; // nothing to do
+ }
+
+ if (.@size < 0) {
+ copyarray(getelementofarray(getarg(0), .@index + .@delta), getarg(0), .@count); // shift to the right
+ cleararray(getarg(0), getarg(2), .@delta); // prepend
+ } else {
+ cleararray(getelementofarray(getarg(0), .@index + .@count), getarg(2), .@delta); // append
+ }
+
+ return .@delta;
+}
+
+
+
+// array_replace(<array>, <needle>, <replace>{, <neq>})
+// replace every occurence of <needle> with <replace>
+// returns the number of replaced elements
+
+function script array_replace {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(3, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ set(getelementofarray(getarg(0), .@i), getarg(2));
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_find(<array>, <needle>{, <neq>})
+// return the index of the first occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_find {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_rfind(<array>, <needle>{, <neq>})
+// return the index of the last occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_rfind {
+ .@min = getarrayindex(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = (getarraysize(getarg(0)) - 1); .@i >= .@min; --.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_exists(<array>, <needle>{, <neq>})
+// return true or false accordingly if <needle> is found in <array>
+
+function script array_exists {
+ return array_find(getarg(0), getarg(1), getarg(2, false)) > -1;
+}
+
+
+
+// array_count(<array>, <needle>{, <neq>})
+// counts the number of occurrence of <needle> in the <array>
+
+function script array_count {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_entries(<array>)
+// returns the number of non-empty entries
+
+function script array_entries {
+ if (isstr(getarg(0)) == 1) {
+ return array_count(getarg(0), "", true);
+ }
+ return array_count(getarg(0), 0, true);
+}
+
+
+
+// array_remove(<array>, <needle>{, <neq>})
+// removes every occurrence of <needle> in the <array> while shifting left
+
+function script array_remove {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_reverse(<array>)
+// reverses the array
+
+function script array_reverse {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = .@index; .@i < ((.@size + .@index) / 2); ++.@i) {
+ swap(getelementofarray(getarg(0), .@i), getelementofarray(getarg(0), .@size + .@index - 1 - .@i)); // a <> b
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_sum(<array>)
+// return the sum of every element of the array
+
+function script array_sum {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@sum += getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@sum;
+}
+
+
+
+// array_difference(<array>)
+// return the difference of every element of the array
+
+function script array_difference {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@diff -= getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@diff;
+}
+
+
+
+// array_shift(<array>)
+// returns the first element of the array and removes it, while shifting left
+
+function script array_shift {
+ if (isstr(getarg(0)) == 1) {
+ .@val$ = getarg(0);
+ } else {
+ .@int = true;
+ .@val = getarg(0);
+ }
+
+ deletearray(getarg(0), 1); // shift left
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// array_unshift(<array>, <value>)
+// adds <value> to the start of the array, while shifting right
+// returns the new size
+
+function script array_unshift {
+ .@size = getarraysize(getarg(0)) + 1;
+ array_pad(getarg(0), -(.@size - getarrayindex(getarg(0))), getarg(1));
+ return .@size;
+}
+
+
+
+// array_pop(<array>)
+// returns the last element of the array and removes it
+
+function script array_pop {
+ .@last = getarraysize(getarg(0)) - 1;
+
+ if (isstr(getelementofarray(getarg(0), .@last)) == 1) {
+ .@val$ = getelementofarray(getarg(0), .@last);
+ } else {
+ .@int = true;
+ .@val = getelementofarray(getarg(0), .@last);
+ }
+
+ deletearray(getelementofarray(getarg(0), .@last), 1);
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// TODO: Rename to array_append >.<
+// array_push(<array>, <value>)
+// adds <value> to the end of the array
+// returns the new size
+
+function script array_push {
+ .@size = getarraysize(getarg(0));
+ set(getelementofarray(getarg(0), .@size), getarg(1));
+ return .@size + 1;
+}
+
+
+
+// array_shuffle(<array>)
+// shuffles the array
+
+function script array_shuffle {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0)) - .@index;
+ freeloop(true);
+
+ if (isstr(getarg(0)) == 1) {
+ copyarray(.@tmp$[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp$[rand(.@size)]));
+ }
+ } else {
+ copyarray(.@tmp[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp[rand(.@size)]));
+ }
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_unique(<array>{, <threshold>})
+// allows entries to appear up to <threshold> in the array
+
+function script array_unique {
+ .@size = getarraysize(getarg(0));
+ .@max = getarg(1, 1);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@count = 1;
+ for (.@e = .@i + 1; .@e < .@size; ++.@e) {
+ if (getelementofarray(getarg(0), .@i) == getelementofarray(getarg(0), .@e)) {
+ if (++.@count >= .@max) {
+ deletearray(getelementofarray(getarg(0), .@e), 1);
+ ++.@removed; // increase counter
+ --.@size; // reduce size
+ --.@e; // step back
+ }
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@removed;
+}
+
+
+
+// array_diff(<array1>, <array2>{, <array>...}, <array>)
+// compares array1 against one or more other arrays and fills the last array
+// with the values in array1 that are not present in any of the other arrays
+// returns the number of entries not matching
+
+function script array_diff {
+ .@size = getarraysize(getarg(0));
+ .@index = getarrayindex(getarg(0));
+ freeloop(true);
+
+ for (.@a = 1; .@a < (getargcount() - 1); ++.@a) {
+ for (.@i = .@index; .@i < .@size; ++.@i) {
+ if (!array_exists(getarg(.@a), getelementofarray(getarg(0), .@i))) {
+ array_push(getarg(getargcount() - 1), getelementofarray(getarg(0), .@i));
+ ++.@count;
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_filter(<array>, "<function>")
+// filters the array using a callback function
+
+function script array_filter {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@eq = callfunc(getarg(1), getelementofarray(getarg(0), .@i)) != false;
+ if ((.@neq && .@eq) || (!(.@neq) && !(.@eq))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+// array_highest(<array>)
+// Returns the index of the highest value in <array>
+// NOTE: Array must be an INT array!
+
+function script array_highest {
+ .@size = getarraysize(getarg(0));
+ .@win=0;
+ .@idx=0;
+ .@dw=false;
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if (getelementofarray(getarg(0), .@i) > .@win) {
+ .@win=getelementofarray(getarg(0), .@i);
+ .@idx=.@i;
+ if (.@dw) {
+ deletearray .@draw;
+ .@dw=false;
+ }
+ } else if (getelementofarray(getarg(0), .@i) == .@win) {
+ if (!.@dw)
+ array_push(.@draw, .@idx);
+ array_push(.@draw, .@i);
+ .@dw=true;
+ }
+ }
+
+ // Will we return .@idx or do we need to draw a loot?
+ freeloop(false);
+ if (.@dw)
+ return any_of(.@draw);
+ else
+ return .@idx;
+}
+
+// relative_array_random(<array: 0, {[value, probability]..}>)
+// returns a random entry from the array, by relative probability
+// the first key of the array should be 0 and every entries are a tuple
+// of [value, probability]
+
+function script relative_array_random {
+ .@is_str = getdatatype(getarg(0)) & DATATYPE_STR;
+ .@total_prob = getelementofarray(getarg(0), 0);
+ .@initial_index = getarrayindex(getarg(0));
+ .@initial_index = .@initial_index ? .@initial_index : 1;
+ freeloop(true);
+
+ if (.@total_prob < 1 || getarg(1, false))
+ {
+ // first calculation, or forced re-calculation
+ .@total_prob = 0;
+ .@size = getarraysize(getarg(0));
+
+ for (.@i = .@initial_index + 1; .@i < .@size; .@i += 2) {
+ if (.@is_str) {
+ .@total_prob += max(1, atoi(getelementofarray(getarg(0), .@i)));
+ } else {
+ .@total_prob += max(1, getelementofarray(getarg(0), .@i));
+ }
+ }
+
+ // we cache on the first key
+ set(getelementofarray(getarg(0), 0), .@total_prob);
+ }
+
+ .@target_sum = rand(0, .@total_prob);
+
+ for (.@i = .@initial_index; .@sum < .@target_sum; .@i += 2) {
+ if (.@is_str) {
+ .@sum += atoi(getelementofarray(getarg(0), .@i + 1));
+ } else {
+ .@sum += getelementofarray(getarg(0), .@i + 1);
+ }
+
+ if (.@sum >= .@target_sum) {
+ break;
+ }
+ }
+
+ freeloop(false);
+ return getelementofarray(getarg(0), .@i);
+}
+
diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt
new file mode 100644
index 00000000..32e0f7bc
--- /dev/null
+++ b/npc/functions/asklanguage.txt
@@ -0,0 +1,72 @@
+// TMW2 script
+// Evol functions.
+// Author:
+// Reid, Jesusalva
+// Description:
+// Function setting the player language
+
+function script languagecode {
+ switch (Lang) {
+ case LANG_PTBR:
+ return "pt_BR";
+ case LANG_FR:
+ return "fr";
+ case LANG_DE:
+ return "de";
+ case LANG_ES:
+ return "es";
+ default:
+ return "en";
+ }
+}
+
+function script asklanguage {
+
+ dispbottom col("We need help with translations. [@@help://translate|Learn more@@]", 1);
+ switch (getarg(0, LANG_IN_SHIP))
+ {
+ case LANG_ON_SEA:
+ setarray .@messages$[0], "I hear you... (English)", // English
+ "Eu te ouço... (Português)", // Portuguese
+ "Je vous entends... (Français)", // French
+ "Ich höre euch... (Deutsch)", // German
+ "Te oigo... (Español)"; // Spanish
+ break;
+ case LANG_IN_SHIP:
+ setarray .@messages$[0], "I speak English.", // English
+ "Eu falo Português.", // Portuguese
+ "Je parle français.", // French
+ "Ich spreche Deutsch.", // German
+ "Hablo Español."; // Spanish
+ break;
+ default:
+ return;
+ }
+
+ setarray .@flags$[0], "flags/en",
+ "flags/pt_BR",
+ "flags/fr",
+ "flags/de",
+ "flags/es";
+
+ .@menustr$ = "";
+ .@separator$ = ":";
+
+ for (.@i = 0; .@i <= MAX_LANG; .@i++)
+ {
+ if (.@i == MAX_LANG) {
+ .@separator$ = "";
+ }
+ .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$;
+ }
+
+ select(.@menustr$);
+
+ .@lang = @menu - 1;
+
+ if (.@lang >= 0 || .@lang <= MAX_LANG) {
+ Lang = .@lang;
+ }
+
+ return;
+}
diff --git a/npc/functions/banker.txt b/npc/functions/banker.txt
new file mode 100644
index 00000000..5692d238
--- /dev/null
+++ b/npc/functions/banker.txt
@@ -0,0 +1,182 @@
+// Bank scripts
+function script Banking {
+ do
+ {
+ if (BankVault > 0) {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You currently have @@ GP 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 >= 1000, format_number(1000) + " GP."), 1000,
+ rif(Zeny >= 2500, format_number(2500) + " GP."), 2500,
+ rif(Zeny >= 5000, format_number(5000) + " GP."), 5000,
+ rif(Zeny >= 10000, format_number(10000) + " GP."), 10000,
+ rif(Zeny >= 25000, format_number(25000) + " GP."), 25000,
+ rif(Zeny >= 50000, format_number(50000) + " GP."), 50000,
+ rif(Zeny >= 100000, format_number(100000) + " GP."), 100000,
+ 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 Gold 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 @@ GP.", 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 >= 1000, format_number(1000) + " GP."), 1000,
+ rif(BankVault >= 2500, format_number(2500) + " GP."), 2500,
+ rif(BankVault >= 5000, format_number(5000) + " GP."), 5000,
+ rif(BankVault >= 10000, format_number(10000) + " GP."), 10000,
+ rif(BankVault >= 25000, format_number(25000) + " GP."), 25000,
+ rif(BankVault >= 50000, format_number(50000) + " GP."), 50000,
+ rif(BankVault >= 100000, format_number(100000) + " GP."), 100000,
+ 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 Gold Pieces 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 @@ GP.", format_number(.@withdrawal));
+ }
+ }
+ break;
+ default: return;
+ }
+ } while (true);
+}
+
+function script Banker {
+ mesn;
+ mes l("\"Welcome to the bank!");
+ mes l("How can I help you?\"");
+ next;
+ do
+ {
+ select
+ l("I would like to store some items."),
+ l("I would like to perform money transactions."),
+ l("Did I received any mail?"),
+ rif(getcharid(2) > 0, l("I would like to open Guild Storage.")),
+ l("Bye.");
+
+ switch (@menu) {
+ case 1:
+ closeclientdialog;
+ openstorage;
+ close;
+ break;
+ case 2:
+ Banking();
+ // Shader quest
+ if (BankVault >= 10000000 &&
+ BaseLevel >= 85 &&
+ getq(General_Banker) < 1) {
+ mesq l("Oh.");
+ next;
+ mesq l("It seems you managed to amass quite a fortune!");
+ next;
+ mesq l("Thank you for using our services. Please accept this little gift.");
+ setq General_Banker, 1;
+ getitem CashiersShade, 1;
+ next;
+ }
+ break;
+ case 3:
+ // NOTE: These values are HARDCODED, do not try changing it!
+ mesc l("Note: Transfering items on mail cost %s GP/item", fnum(2500));
+ mesc l("Money transference will have a %d %% fee as well.", 2);
+ next;
+ closeclientdialog;
+ openmail();
+ close;
+ break;
+ case 4:
+ if (getcharid(3) != getguildinfo(GUILDINFO_MASTER_CID, getcharid(2))) {
+ mesn;
+ mesq l("Only %s is authorized to use the Guild Storage.", getguildinfo(GUILDINFO_MASTER_NAME, getcharid(2)));
+ break;
+ } else {
+ if (guildopenstorage())
+ mesc l("Storage temporarily unavailable, someone else might be using it.");
+ }
+ }
+ if (@menu != 5) {
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT | S_NO_NPC_NAME,
+ l("Something else?");
+ }
+ } while (@menu != 5);
+ closeclientdialog;
+ goodbye;
+ close;
+}
diff --git a/npc/functions/barber.txt b/npc/functions/barber.txt
new file mode 100644
index 00000000..ee1687dc
--- /dev/null
+++ b/npc/functions/barber.txt
@@ -0,0 +1,223 @@
+// Evol scripts.
+// Authors:
+// omatt
+// Reid
+// Travolta
+// Jesusalva
+// Description:
+// Function for supporting barber NPC.
+
+// BarberSayStyle({what})
+// what: 1 = Style; 2 = Color; 3 = Style + Color in dialog
+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, 3))
+ {
+ case 1:
+ message strcharinfo(0), l("%s", .@style_name$);
+ break;
+ case 2:
+ message strcharinfo(0), l("%s", .@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;
+}
+
+// 3 = Abs (Real value is 1)
+// 2 = Boobs (Real value is 0)
+// 1 = Shirt (Real value is 3)
+function script BarberChangeBodyType {
+ mesn l("Note");
+ mes b(l("Changing your body type will send you back to the character selection screen."));
+ next();
+
+ mes l("Please select the desired body type:");
+ menuint(
+ rif(BodyType == BODYTYPE_3, "► ") + l("Body type %s", "A"), BODYTYPE_3,
+ rif(BodyType == BODYTYPE_2, "► ") + l("Body type %s", "B"), BODYTYPE_2,
+ rif(BodyType == BODYTYPE_1, "► ") + l("Body type %s", "C"), BODYTYPE_1);
+
+ if (BodyType == @menuret) {
+ return; // don't kick to char selection when not needed
+ }
+
+ // FIXME: when manaplus supports seamless changing for evol2, use a simple return;
+ closedialog();
+ close2();
+ BodyType = @menuret;
+ close;
+}
+
+function script BarberChangeRace {
+
+ mes l("What's your race?");
+ mesc l("WARNING: Changing race may have side effects.");
+ menuint
+ l("Talpan"), Talpan,
+ l("Tritan"), Tritan,
+ l("Ifriton"), Ifriton,
+ rif(is_dev(), l("Gispaan")), Gispaan,
+ rif(is_dev(), l("Sparron")), Sparron,
+ rif(countitem(SkeletonCharm), l("Undead")), Skellie;
+
+ mes "";
+
+ // Not needed
+ if (Class == @menuret)
+ return;
+
+ // Change race and we're done
+ //Class = @menuret;
+ jobchange(@menuret); // STUPID idea, but imposed by Hercules
+ return;
+}
+
+// Jack of all trades
+// Barber({intro=True})
+function script Barber {
+ if (getarg(0, true)) {
+ mesn;
+ mesq l("Hello.");
+ next;
+ }
+ mesq l("What would you like me to do?");
+ next;
+ do
+ {
+ select
+ l("What is my current hairstyle and hair color?"),
+ l("I'd like to get a different style."),
+ l("Can you do something with my color?"),
+ l("How about changing my body type?"),
+ rif(BaseLevel > 70, l("I would like to change my species.")),
+ l("I'm fine for now, thank you.");
+
+ switch (@menu)
+ {
+ case 1:
+ BarberSayStyle();
+ break;
+ case 2:
+ BarberChangeStyle();
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("Enjoy your new style."),
+ l("Anything else?");
+ break;
+ case 3:
+ BarberChangeColor();
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("I hope you like this color."),
+ l("Anything else?");
+ break;
+ case 4:
+ BarberChangeBodyType();
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("You look fantastic."),
+ l("Anything else?");
+ break;
+ case 5:
+ BarberChangeRace();
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("This service is provided with no warranties on regards of side effects."),
+ l("Anything else?");
+ break;
+ break;
+ case 6:
+ speech S_FIRST_BLANK_LINE | S_LAST_NEXT,
+ l("Feel free to come visit me another time.");
+ goodbye;
+ return;
+ }
+ } while (true);
+ return;
+}
+
diff --git a/npc/functions/bitwise.txt b/npc/functions/bitwise.txt
new file mode 100644
index 00000000..02360662
--- /dev/null
+++ b/npc/functions/bitwise.txt
@@ -0,0 +1,176 @@
+// The Mana World Script
+// Author: Gumi, Jesusalva
+/**
+ * Gets a bitmasked value in from an integer. If the shift is omitted, it will
+ * be deduced from the mask.
+ *
+ * @arg 0 - the variable
+ * @arg 1 - mask
+ * @arg 2 - shift */
+function script bitwise_get {
+ .@shift = getarg(2, 0);
+
+ if (getargcount() < 3) {
+ // guess the shift from the mask:
+ for (.@shift = 0; .@shift < 32; ++.@shift) {
+ if ((getarg(1) & (1 << .@shift)) != 0) {
+ break;
+ }
+ }
+ }
+
+ return (getarg(0) & getarg(1)) >> .@shift;
+}
+
+/**
+ * sets a bitmasked value in a variable
+ *
+ * @arg 0 - the target variable
+ * @arg 1 - mask
+ * @arg 2 - shift
+ * @arg 3 - new value
+ * @return a reference to the variable
+ */
+function script bitwise_set {
+ if (getargcount() < 4) {
+ // guess the shift from the mask:
+ for (.@shift = 0; .@shift < 32; ++.@shift) {
+ if ((getarg(1) & (1 << .@shift)) != 0) {
+ break;
+ }
+ }
+
+ return set(getarg(0), (getarg(0) & ~(getarg(1))) | (getarg(2, 0) << .@shift));
+ }
+
+ return set(getarg(0), (getarg(0) & ~(getarg(1))) | (getarg(3, 0) << getarg(2, 0)));
+}
+
+// bitmask_count(<int>)
+// returns the number of bits set in <int> (up to 4096?)
+function script bitmask_count {
+ .@n = getarg(0); // Number evaluated
+ .@p=0; // Bits set/unset
+ .@s=0; // Stack and Check
+ .@m=0; // Memory
+
+ // Loop only as needed
+ while (.@s < .@n) {
+ .@s=2**.@m;
+ if (.@n & .@s)
+ .@p++;
+ .@m++;
+ }
+ return .@p;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// A Nibble can go up to 15. There are 7 nibbles.
+// get_nibble(VAR, NIBBLEID)
+function script get_nibble {
+ .@v=getarg(0);
+ switch (getarg(1)) {
+ case 0:
+ .@s=0; .@m=0xF; break;
+ case 1:
+ .@s=4; .@m=0xF0; break;
+ case 2:
+ .@s=8; .@m=0xF00; break;
+ case 3:
+ .@s=12; .@m=0xF000; break;
+ case 4:
+ .@s=16; .@m=0xF0000; break;
+ case 5:
+ .@s=20; .@m=0xF00000; break;
+ case 6:
+ .@s=24; .@m=0xF000000; break;
+ default:
+ Exception("Invalid Nibble: "+getarg(1), RB_DEFAULT, .@v);
+ }
+
+ return bitwise_get(.@v, .@m, .@s);
+}
+
+// A Byte can go up to 255. There are 3 bytes. The forth can go up to 127.
+// get_nibble(VAR, BYTEID)
+function script get_byte {
+ .@v=getarg(0);
+ switch (getarg(1)) {
+ case 0:
+ .@s=0; .@m=0xFF; break;
+ case 1:
+ .@s=8; .@m=0xFF00; break;
+ case 2:
+ .@s=16; .@m=0xFF0000; break;
+ case 3:
+ .@s=24; .@m=0x7F000000; break;
+ default:
+ Exception("Invalid Byte: "+getarg(1), RB_DEFAULT, .@v);
+ }
+
+ return bitwise_get(.@v, .@m, .@s);
+}
+
+// A Bitword can go up to 65535 and is fixed in position to handle Soul EXP.
+// get_bitword(VAR)
+function script get_bitword {
+ .@v=getarg(0);
+
+ return bitwise_get(.@v, 0xFFFF, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// A Nibble can go up to 15. There are 7 nibbles.
+// set_nibble(VAR, NIBBLEID, VAL)
+function script set_nibble {
+ .@v=getarg(0);
+ switch (getarg(1)) {
+ case 0:
+ .@s=0; .@m=0xF; break;
+ case 1:
+ .@s=4; .@m=0xF0; break;
+ case 2:
+ .@s=8; .@m=0xF00; break;
+ case 3:
+ .@s=12; .@m=0xF000; break;
+ case 4:
+ .@s=16; .@m=0xF0000; break;
+ case 5:
+ .@s=20; .@m=0xF00000; break;
+ case 6:
+ .@s=24; .@m=0xF000000; break;
+ default:
+ Exception("Invalid SNibble: "+getarg(1), RB_DEFAULT);
+ }
+
+ return bitwise_set(getarg(0), .@m, .@s, getarg(2));
+}
+
+// A Byte can go up to 255. There are 3 bytes. The forth can go up to 127.
+// set_nibble(VAR, BYTEID, VAL)
+function script set_byte {
+ .@v=getarg(0);
+ switch (getarg(1)) {
+ case 0:
+ .@s=0; .@m=0xFF; break;
+ case 1:
+ .@s=8; .@m=0xFF00; break;
+ case 2:
+ .@s=16; .@m=0xFF0000; break;
+ case 3:
+ .@s=24; .@m=0x7F000000; break;
+ default:
+ Exception("Invalid SByte: "+getarg(1), RB_DEFAULT);
+ }
+
+ return bitwise_set(getarg(0), .@m, .@s, getarg(2));
+}
+
+// A Bitword can go up to 65535 and is fixed in position to handle Soul EXP.
+// set_bitword(VAR, VAL)
+function script set_bitword {
+ .@v=getarg(0);
+
+ return bitwise_set(getarg(0), 0xFFFF, 0, getarg(1));
+}
+
diff --git a/npc/functions/clear_vars.txt b/npc/functions/clear_vars.txt
new file mode 100644
index 00000000..82ac931f
--- /dev/null
+++ b/npc/functions/clear_vars.txt
@@ -0,0 +1,30 @@
+
+function script ClearVariables {
+ if (@login_event != 1) goto L_Deprecated;
+
+ // Remove old variables to new quest system
+ if (QL_VALON) {
+ setq CandorQuest_Valon, QL_VALON;
+ QL_VALON=0;
+ }
+ if (QL_HIDENSEEK) {
+ setq CandorQuest_HideNSeek, (QL_HIDENSEEK >= 64 ? 2 : 1), (QL_HIDENSEEK-1);
+ }
+
+ // Don't ask me what code below does
+ // ----------------------------------
+ /*
+ if (#BankAccount >= 0) return;
+ if (Zeny >= -#BankAccount) {
+ Zeny = Zeny + #BankAccount;
+ #BankAccount = 0;
+ }
+
+ // Partial fix
+ #BankAccount = #BankAccount + Zeny;
+ Zeny = 0;
+ */
+ return;
+}
+
+
diff --git a/npc/functions/confused-tree-dict.txt b/npc/functions/confused-tree-dict.txt
new file mode 100644
index 00000000..e1c0f555
--- /dev/null
+++ b/npc/functions/confused-tree-dict.txt
@@ -0,0 +1,515 @@
+// 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$", "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$[1], .npc$), // {{size}}
+ "tiny", 1,
+ "small", 1,
+ "perfectly sized", 1,
+ "large", 1,
+ "huge", 1,
+ "humongous", 1,
+ "ginormous", 1);
+ set(getvariableofnpc(.D_size, .npc$), getarraysize(getvariableofnpc(.D_size$, .npc$)));
+
+ setarray(getvariableofnpc(.D_color$[1], .npc$), // {{color}}
+ "red", 1,
+ "orange", 1,
+ "yellow", 1,
+ "pink", 1,
+ "aqua", 1,
+ "cyan", 1,
+ "blue", 1,
+ "indigo", 1,
+ "violet", 1,
+ "purple", 1,
+ "magenta", 1,
+ "pink", 1,
+ "black", 1,
+ "white", 1,
+ "grey", 1,
+ "greyscale", 1,
+ "brown", 1,
+ "maroon", 1,
+ "turquoise", 1,
+ "lime", 1,
+ "sky blue", 1,
+ "invisible", 1);
+ set(getvariableofnpc(.D_color, .npc$), getarraysize(getvariableofnpc(.D_color$, .npc$)));
+
+ setarray(getvariableofnpc(.D_violentadverb$[1], .npc$), // {{violent adverb}}
+ "violently", 1,
+ "repeatedly", 1,
+ "casually", 1,
+ "forcefully", 1,
+ "slowly", 1,
+ "carefully", 1,
+ "hopefully", 1,
+ "dangerously", 1,
+ "shockingly", 1,
+ "religiously", 1);
+ set(getvariableofnpc(.D_violentadverb, .npc$), getarraysize(getvariableofnpc(.D_violentadverb$, .npc$)));
+
+ setarray(getvariableofnpc(.D_hello$[1], .npc$), // {{hello}}
+ "hi", 4,
+ "hey", 3,
+ "yo", 2,
+ "hello", 10,
+ "howdy", 1,
+ "bonjour", 1);
+ set(getvariableofnpc(.D_hello, .npc$), getarraysize(getvariableofnpc(.D_hello$, .npc$)));
+
+ setarray(getvariableofnpc(.D_violentverb$[1], .npc$), // {{violent verb}}
+ "slaps", 5,
+ "hits", 1,
+ "pummels", 1,
+ "beats", 1,
+ "flattens", 1,
+ "taunts", 1,
+ "liquidates", 1,
+ "spanks", 1,
+ "affronts", 1,
+ "tranquilizes", 1,
+ "atomizes", 1,
+ "impales", 1,
+ "dismembers", 1);
+ set(getvariableofnpc(.D_violentverb, .npc$), getarraysize(getvariableofnpc(.D_violentverb$, .npc$)));
+
+ // FIXME: Is this even used anywhere?
+ setarray(getvariableofnpc(.D_location$[1], .npc$), // {{location}}
+ "Artis", 1,
+ "Hurnscald", 1,
+ "Tulimshar", 1,
+ "Nivalis", 1,
+ "Candor", 1,
+ "Drasil", 1);
+ set(getvariableofnpc(.D_location, .npc$), getarraysize(getvariableofnpc(.D_location$, .npc$)));
+
+ setarray(getvariableofnpc(.D_sizeableobject$[1], .npc$), // {{sizeable object}}
+ "trout", 1,
+ "whale", 1,
+ "space whale", 1,
+ "penguin", 1,
+ "coelacanth", 1,
+ "squid", 1,
+ "shrimp", 1,
+ "crab", 1,
+ "tentacle", 1,
+ "dictionary", 1,
+ "grammar book", 1,
+ "textbook", 1,
+ "dinosaur", 1,
+ "t-rex", 1,
+ "star-nosed mole", 1,
+ "chimpanzee", 1,
+ "mermaid", 1,
+ "merman", 1,
+ "piano", 1,
+ "prince", 1,
+ "princess", 1,
+ "pinkie", 1,
+ "squirrel", 1,
+ "mouboo", 1,
+ "wet mop", 1,
+ "drunken pirate", 1,
+ "cake", 1,
+ "cookie", 1,
+ "chocobo", 1,
+ "restraining order", 1,
+ "freight train", 1,
+ "carnival hammer", 1,
+ "crate", 1,
+ "bomb", 1,
+ "bowl of petinuas", 1,
+ "box", 1,
+ "platypus", 1,
+ "magic eightball", 1,
+ "vase", 1);
+ set(getvariableofnpc(.D_sizeableobject, .npc$), getarraysize(getvariableofnpc(.D_sizeableobject$, .npc$)));
+
+ setarray(getvariableofnpc(.D_nsizeableobject$[1], .npc$), // {{n sizeable object}}
+ "octopus", 1,
+ "elephant", 1,
+ "angry cat", 1,
+ "anvil", 1,
+ "encyclopedia set", 1);
+ set(getvariableofnpc(.D_nsizeableobject, .npc$), getarraysize(getvariableofnpc(.D_nsizeableobject$, .npc$)));
+
+ setarray(getvariableofnpc(.D_someone$[1], .npc$), // {{someone}}
+ "Voldemort", 1,
+ "Cthulhu", 1,
+ "Platyna", 1,
+ "Hitler", 1,
+ "Luvia", 1,
+ "General Krukan", 1,
+ "Borg Queen", 1,
+ "Freeyorp", 1,
+ "MadCamel", 1);
+ set(getvariableofnpc(.D_someone, .npc$), getarraysize(getvariableofnpc(.D_someone$, .npc$)));
+
+ // replies below
+
+ setarray(getvariableofnpc(.greetings$[1], .npc$),
+ "{{^ hello }} ~p!", 4,
+ "{{^ hello }} ~p.", 6,
+ "{{^ hello }} ~p, what's up?", 1,
+ "{{^ hello }} ~p, anything new?", 1,
+ "{{^ hello }} ~p, how are you?", 1,
+ "~p!!!!", 1,
+ "~p!!!", 1,
+ "~p!!", 1,
+ "{{^ hello }} ~p! You are looking lovely today!", 1,
+ "Welcome back, ~p.", 3,
+ "~p is back!!", 1,
+ "Hello and welcome to the Aperture Science computer-aided enrichment center.", 1,
+ "Greetings ~p.", 1,
+ "What's up ~p?", 2,
+ "How are you ~p?", 1);
+ set(getvariableofnpc(.greetings, .npc$), getarraysize(getvariableofnpc(.greetings$, .npc$)));
+
+ setarray(getvariableofnpc(.jokes$[1], .npc$),
+ "How did the tree get drunk? On root beer.", 1,
+ "Do you think I'm lazy?", 1,
+ "I miss CrazyTree %%S.", 1,
+ "I miss LazyTree %%S.", 1,
+ "I'm not telling you!", 1,
+ "*sighs.*", 1,
+ "If I do it for you, then I have to do it for everybody.", 1,
+ "What did the beaver say to the tree? It's been nice gnawing you.", 1,
+ "What did the little tree say to the big tree? Leaf me alone.", 1,
+ "What did the tree wear to the pool party? Swimming trunks.", 1,
+ "What do trees give to their dogs? Treets.", 1,
+ "What do you call a tree that only eats meat? Carniforous.", 1,
+ "What do you call a tree who's always envious? Evergreen.", 1,
+ "What is the tree's least favourite month? Sep-timber!", 1,
+ "What kind of tree can fit into your hand? A palm-tree.", 1,
+ "What was the tree's favorite subject in school? Chemistree.", 1,
+ "Why did the leaf go to the doctor? It was feeling green.", 1,
+ "Why doesn't the tree need sudo? Because it has root.", 1,
+ "Why was the cat afraid of the tree? Because of its bark.", 1,
+ "Why was the tree executed? For treeson.", 1,
+ "How do trees get on the internet? They log in.", 1,
+ "Why did the pine tree get into trouble? Because it was being knotty.", 1,
+ "Did you hear the one about the oak tree? It's a corn-y one!", 1,
+ "What do you call a blonde in a tree with a briefcase? Branch Manager.", 1,
+ "How is an apple like a lawyer? They both look good hanging from a tree.", 1,
+ "Why did the sheriff arrest the tree? Because its leaves rustled.", 1,
+ "I'm too tired, ask someone else.", 1,
+ "If you are trying to get me to tell jokes you are barking up the wrong tree!", 1,
+ "You wooden think they were funny anyhow. Leaf me alone!", 1,
+ "What is brown and sticky? A stick.", 1,
+ "What's the best way to carve wood? Whittle by whittle.", 1,
+ "What did the tree do when the bank closed? It started its own branch.", 1,
+ "Do you want a brief explanation of an acorn? In a nutshell, it’s an oak tree.", 1,
+ "A snare drum and a crash cymbal fell out of a tree. *BA-DUM TSSSHH*", 1,
+ "How do you properly identify a dogwood tree? By the bark!", 1,
+ "Where do saplings go to learn? Elementree school.", 1,
+ "Why do trees make great thieves? Sticky fingers.", 1,
+ "What is green, has leaves, and a trunk? A houseplant going on vacation.", 1,
+ "Where can Adansonia trees go for a quick trim? To the baobarber.", 1,
+ "What looks like half a spruce tree? The other half.", 1,
+ "What do you give to a sick citrus tree? Lemon aid.", 1,
+ "What did the tree say to the drill? You bore me.", 1,
+ "What happened to the wooden car with wooden wheels and a wooden engine? It wooden go.", 1,
+ "How do trees keep you in suspense? I'll tell you tomorrow.", 1,
+ "Where do birch trees keep their valuables? In a river bank.", 1,
+ "What kind of stories do giant sequoia trees tell? Tall tales.", 1,
+ "What is the most frustrating thing about being a tree? Having so many limbs and not being able to walk.", 1,
+ "What's black, highly dangerous, and lives in a tree? A crow with a machine gun.", 1,
+ "What kind of wood doesn't float? Natalie Wood.", 1,
+ "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”.", 1);
+ set(getvariableofnpc(.jokes, .npc$), getarraysize(getvariableofnpc(.jokes$, .npc$)));
+
+ setarray(getvariableofnpc(.healing$[1], .npc$),
+ "Eat an apple, they're good for you.", 1,
+ "If I do it for you, then I have to do it for everybody.", 1,
+ "Oh, go drink a potion or something.", 1,
+ "Whoops! I lost my spellbook.", 1,
+ "No mana.", 1);
+ set(getvariableofnpc(.healing, .npc$), getarraysize(getvariableofnpc(.healing$, .npc$)));
+
+ setarray(getvariableofnpc(.whoami$[1], .npc$),
+ "An undercover GM.", 1,
+ "An exiled GM.", 1,
+ "I'm not telling you!", 1,
+ "I'm a bot! I'll be level 99 one day! Mwahahahaaha!!!111!", 1,
+ "Somebody said I'm a Chinese copy of CrazyTree.", 1,
+ "I am your evil twin.", 1,
+ "I don't remember anything after I woke up! What happened to me?", 1,
+ "I don't know. Why am I here??", 1,
+ "Who are you?", 1,
+ "On the 8th day, God was bored and said 'There will be bots'. So here I am.", 1,
+ "♪ I'm your hell, I'm your dream, I'm nothing in between ♪♪", 1,
+ "♪♪ Aperture Science. We do what we must, because... we can ♪", 1,
+ "I'm just a reincarnation of a copy.", 1);
+ set(getvariableofnpc(.whoami, .npc$), getarraysize(getvariableofnpc(.whoami$, .npc$)));
+
+ setarray(getvariableofnpc(.drops$[1], .npc$),
+ "*drops a {{ sizeable object }} on ~p's head.*", 8,
+ "*drops an {{ n sizeable object }} on ~p's head.*", 2,
+ "*drops {{ someone }} on ~p's head.*", 1,
+ "*drops a coin on ~p's head.*", 1,
+ "*drops a fruit on ~p's head.*", 1,
+ "*drops an apple on ~p's head.*", 1,
+ "*drops an iten on ~p's head.*", 1,
+ "*drops a GM on ~p.*", 1,
+ "*drops a piece of moon rock on ~p's head.*", 1,
+ "*drops a pin on ~p's head.*", 1,
+ "*drops a rock on ~p's head.*", 1,
+ "*drops a tub of paint on ~p's head.*", 1,
+ "*drops a sandworm on ~p.*", 1,
+ "*drops an idea in ~p's head.*", 1,
+ "*drops The Hitchhiker's Guide to the Galaxy on ~p's head.*", 1,
+ "Ouch.", 1,
+ "Ouchy.", 1,
+ "*drops dead.*", 1,
+ "*sighs.*", 1,
+ "Leaf me alone.", 1,
+ "Stop it! I don't drop branches, try the Druid tree for once!", 1);
+ set(getvariableofnpc(.drops, .npc$), getarraysize(getvariableofnpc(.drops$, .npc$)));
+
+ setarray(getvariableofnpc(.die$[1], .npc$),
+ "*drops an iten on ~p's head.*", 1,
+ "*drops a piece of moon rock on ~p's head.*", 1,
+ "*drops {{ someone }} on ~p's head.*", 1,
+ "*drops a {{ sizeable object }} on ~p's head.*", 3,
+ "*drops an {{ n sizeable object }} on ~p's head.*", 1,
+ "*drops a {{ size }} {{ sizeable object, n sizeable object }} on ~p's head.*", 1,
+ "*drops a {{ size }} {{ color }} {{ sizeable object, n sizeable object }} on ~p's head.*", 1,
+ "*{{ violent adverb }} {{ violent verb }} ~p.*", 1,
+ "*drops dead.*", 1,
+ "*sighs.*", 1,
+ "Avada Kedavra!", 1,
+ "Make me!", 1,
+ "Never!!", 1,
+ "You die, ~p!", 4,
+ "No!", 1,
+ "In a minute.", 1,
+ "Suuure... I'll get right on it...", 1);
+ set(getvariableofnpc(.die, .npc$), getarraysize(getvariableofnpc(.die$, .npc$)));
+
+ setarray(getvariableofnpc(.poke$[1], .npc$),
+ "*tickles.*", 1);
+ set(getvariableofnpc(.poke, .npc$), getarraysize(getvariableofnpc(.poke$, .npc$)));
+
+ setarray(getvariableofnpc(.disgusting$[1], .npc$),
+ "Ewwwww %%^.", 1);
+ set(getvariableofnpc(.disgusting, .npc$), getarraysize(getvariableofnpc(.disgusting$, .npc$)));
+
+ setarray(getvariableofnpc(.answer$[1], .npc$),
+ "42.", 1,
+ "Kittens.", 1);
+ set(getvariableofnpc(.answer, .npc$), getarraysize(getvariableofnpc(.answer$, .npc$)));
+
+ setarray(getvariableofnpc(.burning$[1], .npc$),
+ "*curses ~p and dies %%c.*", 1,
+ "Help! I'm on fire!", 1,
+ "Oh hot.. hot hot!", 1,
+ "*is glowing.*", 1,
+ "*is flaming.*", 1,
+ "Ehemm. Where are firefighters? I need them now!", 1,
+ "*is so hot!.*", 1,
+ "*slowly catches fire.*", 1,
+ "*trembles with trepidation.*", 1,
+ "*is immune to fire.*", 1);
+ set(getvariableofnpc(.burning, .npc$), getarraysize(getvariableofnpc(.burning$, .npc$)));
+
+ setarray(getvariableofnpc(.kill$[1], .npc$),
+ "*curses ~p and dies %%c.*", 1);
+ set(getvariableofnpc(.kill, .npc$), getarraysize(getvariableofnpc(.kill$, .npc$)));
+
+ setarray(getvariableofnpc(.silly$[1], .npc$),
+ "Hahaha, good one!", 1);
+ set(getvariableofnpc(.silly, .npc$), getarraysize(getvariableofnpc(.silly$, .npc$)));
+
+ setarray(getvariableofnpc(.love$[1], .npc$),
+ "♪♪ and IIII.. will alwayyyys loooovvve youuuuu. ♪♪ %%]", 1,
+ "♪♪ nothing's gonna change my love for you, you oughta know by now how much I love you.. ♪ %%]", 1,
+ "♪ ..and then I go and spoil it all, by saying something stupid like: “I love you.” ♪", 1,
+ "♪ ..won't you find a place for me? somewhere in your heart... ♪♪", 1,
+ "Thank you.", 1,
+ "♪♪ ..I can't love another when my heart is somewhere far away.. ♪", 1,
+ "%%]", 1);
+ set(getvariableofnpc(.love, .npc$), getarraysize(getvariableofnpc(.love$, .npc$)));
+
+ setarray(getvariableofnpc(.dance$[1], .npc$),
+ "I would but I am rooted to the ground.", 1,
+ "Have you ever seen a tree dance before?", 1,
+ "Hahaha, good one!", 1);
+ set(getvariableofnpc(.dance, .npc$), getarraysize(getvariableofnpc(.dance$, .npc$)));
+
+ setarray(getvariableofnpc(.hate$[1], .npc$),
+ "Right back at you!", 1,
+ "Ok...", 1,
+ "*pats ~p, let it go...*", 1,
+ "Hu hu hu, ~p hates me.", 1);
+ set(getvariableofnpc(.hate, .npc$), getarraysize(getvariableofnpc(.hate$, .npc$)));
+
+ setarray(getvariableofnpc(.bye$[1], .npc$),
+ "*waves goodbye to ~p in tears, come back soon!*", 1);
+ set(getvariableofnpc(.bye, .npc$), getarraysize(getvariableofnpc(.bye$, .npc$)));
+
+ setarray(getvariableofnpc(.pain$[1], .npc$),
+ "Ouch.", 1,
+ "Ouchy.", 1,
+ "Argh.", 1,
+ "Eek.", 1,
+ "*howls.*", 1,
+ "*screams.*", 1,
+ "*groans.*", 1,
+ "*cries.*", 1,
+ "*faints.*", 1,
+ "*shrieks.*", 1,
+ "*hides behind itself.*", 1,
+ "%%k", 1,
+ "Why, what did I do to you? %%i", 1);
+ set(getvariableofnpc(.pain, .npc$), getarraysize(getvariableofnpc(.pain$, .npc$)));
+
+ setarray(getvariableofnpc(.eightball$[1], .npc$),
+ "It is possible.", 1,
+ "Yes!", 1,
+ "Of course.", 1,
+ "Naturally.", 1,
+ "Obviously.", 1,
+ "It shall be.", 1,
+ "The outlook is good.", 1,
+ "It is so.", 1,
+ "One would be wise to think so.", 1,
+ "The answer is certainly yes.", 1,
+ "In your dreams.", 1,
+ "I doubt it very much.", 1,
+ "No chance.", 1,
+ "The outlook is very poor.", 1,
+ "Unlikely.", 1,
+ "About as likely as pigs flying.", 1,
+ "You're kidding, right?", 1,
+ "NO!", 1,
+ "NO.", 1,
+ "No.", 1,
+ "Maybe...", 1,
+ "No clue.", 1,
+ "I don't know.", 1,
+ "The outlook is hazy, please ask again later.", 1,
+ "What are you asking me for?", 1,
+ "Come again?", 1,
+ "You know the answer better than I.", 1,
+ "The answer is def-- oooh! shiny thing!", 1,
+ "No idea.", 1,
+ "Perhaps.", 1,
+ "I think it is better not to tell you.", 1,
+ "Error 417: Expectation failed.", 1);
+ set(getvariableofnpc(.eightball, .npc$), getarraysize(getvariableofnpc(.eightball$, .npc$)));
+
+ setarray(getvariableofnpc(.bad$[1], .npc$),
+ "I'm not bad! You are bad!", 1,
+ "OK, I'm bad.", 1,
+ "I'm just a littttle bad.", 1,
+ "Not as bad as the people that made me.", 1);
+ set(getvariableofnpc(.bad, .npc$), getarraysize(getvariableofnpc(.bad$, .npc$)));
+
+ setarray(getvariableofnpc(.no_idea$[1], .npc$),
+ "What?", 2,
+ "What??", 1,
+ "Whatever.", 1,
+ "Hmm...", 2,
+ "Huh?", 1,
+ "*yawns.*", 1,
+ "Wait a minute...", 1,
+ "What are you talking about?", 1,
+ "Who are you?", 1,
+ "What about me?", 1,
+ "I don't know what you are talking about", 1,
+ "Excuse me?", 1,
+ "Very interesting.", 1,
+ "Really?", 1,
+ "Go on...", 1,
+ "*scratches its leafy head.*", 1,
+ "*feels a disturbance in the force.*", 1,
+ "%%j", 1,
+ "*senses a disturbance in the force.*", 1,
+ "I'm bored...", 1,
+ "%%U", 1,
+ "%%[", 1);
+ set(getvariableofnpc(.no_idea, .npc$), getarraysize(getvariableofnpc(.no_idea$, .npc$)));
+
+ setarray(getvariableofnpc(.shut_up$[1], .npc$),
+ "*goes hide in a corner %%S.*", 1);
+ set(getvariableofnpc(.shut_up, .npc$), getarraysize(getvariableofnpc(.shut_up$, .npc$)));
+
+ setarray(getvariableofnpc(.climb$[1], .npc$),
+ "*sways violently.*", 1,
+ "*bends all the way to the ground.*", 1,
+ "*creaks and bends.*", 1,
+ "*welcomes those who come to play %%I.*", 1,
+ "*beams with pride.*", 1);
+ set(getvariableofnpc(.climb, .npc$), getarraysize(getvariableofnpc(.climb$, .npc$)));
+
+ return;
+}
diff --git a/npc/functions/dailyquest.txt b/npc/functions/dailyquest.txt
new file mode 100644
index 00000000..6f39e59d
--- /dev/null
+++ b/npc/functions/dailyquest.txt
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+function script DailyQuestPointsFunc {
+ @dq_earliest = gettimetick(2) - 86400;
+ if (DailyQuestTime < @dq_earliest)
+ DailyQuestTime = @dq_earliest;
+
+ // how many whole daily quest points the player has earned
+ // we increment DailyQuestTime by the number of seconds in that many increments
+ @dq_increments = (gettimetick(2) - DailyQuestTime)*BaseLevel / 86400;
+ DailyQuestTime = DailyQuestTime + @dq_increments*86400/BaseLevel;
+
+ // player can't regenerate any quest points, but might have a bonus
+ if (DailyQuestPoints >= BaseLevel)
+ goto L_Bonus;
+
+ // normal recharging case - increment, but don't let it recharge more than a day's worth
+ DailyQuestPoints = DailyQuestPoints + @dq_increments;
+ if (DailyQuestPoints > BaseLevel)
+ DailyQuestPoints = BaseLevel;
+ // fallthrough to bonus, which *is* allowed to push DailyQuestPoints above BaseLevel
+ goto L_Bonus;
+
+L_Bonus:
+ DailyQuestPoints = DailyQuestPoints + DailyQuestBonus;
+ DailyQuestBonus = 0;
+
+ return;
+}
+
+function script DailyQuest {
+ callfunc "DailyQuestPointsFunc";
+
+ if (BaseLevel < @dq_level)
+ goto L_Low_Level;
+ if (DailyQuestPoints < @dq_cost)
+ goto L_Not_Enough_Points;
+
+ mes "\"If you bring me " + @dq_count + " " + @dq_friendly_name$ + ", I will give you a reward.\"";
+ menu
+ "I have what you want.", L_Trade,
+ "Take all you need.", L_All,
+ "Ok, I'll get to work.", L_Next,
+ "Nah, I'm not going to help you.", L_Next;
+
+L_Next:
+ @dq_return = 1;
+ goto L_Exit;
+
+L_Trade:
+ if (countitem(@dq_name$) < @dq_count)
+ goto L_Not_Enough;
+ delitem @dq_name$, @dq_count;
+
+ Zeny = Zeny + @dq_money;
+ getexp @dq_exp, 0;
+
+ DailyQuestPoints = DailyQuestPoints - @dq_cost;
+
+ if (@dq_handle_return)
+ goto L_Exit_Good;
+
+ mes "\"Thank you!\"";
+ callsub S_SayPhrase;
+ mes "";
+ mes "[" + @dq_money + " money]";
+ mes "[" + @dq_exp + " experience points]";
+ goto L_Exit_Good;
+
+L_All:
+ if (countitem(@dq_name$) < @dq_count)
+ goto L_Not_Enough;
+
+ @item_multiple = (countitem(@dq_name$) / @dq_count);
+ @dp_multiple = (DailyQuestPoints / @dq_cost);
+
+ if (@dp_multiple > @item_multiple)
+ @multipler = @item_multiple;
+ if (@item_multiple >= @dp_multiple)
+ @multipler = @dp_multiple;
+
+ DailyQuestPoints = DailyQuestPoints - (@dq_cost * @multipler);
+
+ delitem @dq_name$, (@dq_count * @multipler);
+
+ Zeny = Zeny + (@dq_money * @multipler);
+ getexp (@dq_exp * @multipler), 0;
+
+ if (@dq_handle_return)
+ goto L_Exit_Good;
+
+ mes "\"Thank you!\"";
+ callsub S_SayPhrase;
+ mes "";
+ mes "[" + (@dq_money * @multipler) + " money]";
+ mes "[" + (@dq_exp * @multipler) + " experience points]";
+ goto L_Exit_Good;
+
+L_Exit_Good:
+ @dq_return = 4;
+ goto L_Exit;
+
+L_Not_Enough:
+ if (!@dq_handle_return)
+ mes "\"I said " + @dq_count + " " + @dq_friendly_name$ + "; you should learn to count.\"";
+ @dq_return = 3;
+ goto L_Exit;
+
+L_Low_Level:
+ if (!@dq_handle_return)
+ mes "\"Hey, you should go kill some things to get stronger first.\"";
+ @dq_return = 0;
+ goto L_Exit;
+
+L_Not_Enough_Points:
+ mes "\"You look exhausted, maybe you should rest a bit.\"";
+ @dq_return = 2;
+ goto L_Exit;
+
+L_Exit:
+ set @dq_handle_return, 0; // Incase they forget
+ return;
+
+S_SayPhrase:
+ if (@dq_handle_return)
+ goto L_Return;
+ if (DailyQuestPoints < @dq_cost)
+ goto L_Exhausted;
+ if (DailyQuestPoints > BaseLevel)
+ goto L_Over;
+ if (DailyQuestPoints > (BaseLevel*9)/10)
+ goto L_P90;
+ if (DailyQuestPoints > (BaseLevel*7)/10)
+ goto L_P70;
+ if (DailyQuestPoints > (BaseLevel*5)/10)
+ goto L_P50;
+ goto L_Low;
+
+L_Over:
+ mes "\"Woah, you're bursting with power.\"";
+ return;
+L_P90:
+ mes "\"You're in a very good shape.\"";
+ return;
+L_P70:
+ mes "\"You don't seem very exhausted by my tasks.\"";
+ return;
+L_P50:
+ mes "\"Aren't you getting weary yet?\"";
+ return;
+L_Low:
+ mes "\"You look a little tired.\"";
+ return;
+L_Exhausted:
+ mes "\"You look exhausted, maybe you should rest a bit.\"";
+ return;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/default_npc_checks.txt b/npc/functions/default_npc_checks.txt
new file mode 100644
index 00000000..ee12bc49
--- /dev/null
+++ b/npc/functions/default_npc_checks.txt
@@ -0,0 +1,137 @@
+
+function script PCtoNPCRange {
+ @npc_check = 0;
+ if(!@npc_distance) set @npc_distance, 4; // <== default distance
+ if(@npc_distance < 0) set @npc_distance, ATTACKRANGE;
+ .@x=getvariableofnpc(.x, strnpcinfo(0));
+ .@y=getvariableofnpc(.y, strnpcinfo(0));
+ if (isin(getmap(), .@x, .@y, @npc_distance))
+ goto L_Return;
+ @npc_check = 1;
+ if(@distance_handler) goto L_Return;
+ @dnpc_name$ = strnpcinfo(1);
+ if(@dnpc_name$ != "") goto L_Named;
+ message strcharinfo(0), "Server : ##BYou need to move closer to interact with this npc.";
+ goto L_Return;
+
+L_Named:
+ npctalk3 l(b("Please move closer."));
+ goto L_Return;
+
+L_Return:
+ @dnpc_name$ = "";
+ @distance_handler = 0;
+ @npc_distance = 0;
+ return;
+}
+
+function script CheckInventory {
+ @del_loop = 0;
+ @get_loop = 0;
+ @delitem_loop = 0;
+ @getitem_loop = 0;
+ @check_fail = 0;
+ @msg_loop = 0;
+
+ if (getarraysize(@delitem_ids))
+ goto L_DelItemsLoop;
+ goto L_CheckGet;
+
+L_DelItemsLoop:
+ if(countitem(@delitem_ids[@delitem_loop]) >= @delitem_counts[@delitem_loop])
+ goto L_DelLoopAgain;
+ goto L_ReturnMissing;
+
+L_DelLoopAgain:
+ @delitem_loop = (@delitem_loop + 1);
+ if(@delitem_loop == getarraysize(@delitem_ids))
+ goto L_CheckGet;
+ goto L_DelItemsLoop;
+
+L_CheckGet:
+ if (getarraysize(@getitem_ids))
+ goto L_CheckWeight;
+ goto L_DelCheck;
+
+L_CheckWeight:
+ getinventorylist;
+ if (100 < (@inventorylist_count + getarraysize(@getitem_ids)))
+ goto L_ReturnSpace;
+ goto L_GetItemsLoop;
+
+L_GetItemsLoop:
+ if (checkweight(@getitem_ids[@getitem_loop], @getitem_counts[@getitem_loop]))
+ goto L_GetLoopAgain;
+ goto L_ReturnWeight;
+
+L_GetLoopAgain:
+ @getitem_loop = (@getitem_loop + 1);
+ if(@getitem_loop == getarraysize(@getitem_ids))
+ goto L_DelCheck;
+ goto L_GetItemsLoop;
+
+L_DelCheck:
+ if (getarraysize(@delitem_ids))
+ goto L_DelLoop;
+ goto L_CheckGet2;
+
+L_DelLoop:
+ delitem @delitem_ids[@del_loop], @delitem_counts[@del_loop];
+ goto L_DelAgain;
+
+L_DelAgain:
+ @del_loop = (@del_loop + 1);
+ if(@del_loop == getarraysize(@delitem_ids))
+ goto L_GetLoop;
+ goto L_DelLoop;
+
+L_CheckGet2:
+ if (getarraysize(@getitem_ids))
+ goto L_GetLoop;
+ goto L_Return;
+
+L_GetLoop:
+ misceffect FX_GETITEM, strcharinfo(0);
+ getitem @getitem_ids[@get_loop], @getitem_counts[@get_loop];
+ goto L_GetAgain;
+
+L_GetAgain:
+ @get_loop = (@get_loop + 1);
+ if(@get_loop == getarraysize(@getitem_ids))
+ goto L_Return;
+ goto L_GetLoop;
+
+L_ReturnMissing:
+ @check_fail = 1;
+ mesq l("You are missing required items.");
+ goto L_MissingMsg;
+
+L_MissingMsg:
+ mes "[@@"+@delitem_ids[@msg_loop]+"|"+getitemlink(@delitem_ids[@msg_loop])+"@@] "+countitem(getitemlink(@delitem_ids[@msg_loop]))+"/"+@delitem_counts[@msg_loop];
+ goto L_NextMsgCheck;
+
+L_NextMsgCheck:
+ @msg_loop = (@msg_loop + 1);
+ if(@msg_loop == getarraysize(@delitem_ids))
+ goto L_Return;
+ goto L_MissingMsg;
+
+L_ReturnWeight:
+ mesq l("You need to be carrying less weight.");
+ next;
+ @check_fail = 1;
+ goto L_Return;
+
+L_ReturnSpace:
+ mesq l("You need more room in your inventory.");
+ next;
+ @check_fail = 1;
+ goto L_Return;
+
+L_Return:
+ cleararray @delitem_ids, "", getarraysize(@delitem_ids);
+ cleararray @delitem_counts, "", getarraysize(@delitem_counts);
+ cleararray @getitem_ids, "", getarraysize(@getitem_ids);
+ cleararray @getitem_counts, "", getarraysize(@getitem_counts);
+ return;
+}
diff --git a/npc/functions/dynamic_menu.txt b/npc/functions/dynamic_menu.txt
new file mode 100644
index 00000000..29902bac
--- /dev/null
+++ b/npc/functions/dynamic_menu.txt
@@ -0,0 +1,289 @@
+
+function script DynamicItemMenu {
+set @items_nr, getarraysize(@items);
+if(@items_nr != getarraysize(@item_names$)) goto L_ArrayLengthMismatch;
+if(@default_choice$ == "") set @default_choice$, "Never mind.";
+goto L_pick_one_of_many_items;
+
+L_pick_one_of_many_items:
+ @c = 0;
+ @i = 0;
+
+ setarray @choice_n$, "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "";
+ goto L_pick_choice_loop;
+
+L_pick_choice_loop:
+ if (@i >= @items_nr)
+ goto L_choice_init_done;
+ @current = @items[@i];
+ @current_name$ = @item_names$[@i];
+ @current_index = @i;
+ @i = @i + 1;
+
+ if (countitem(@current) == 0)
+ goto L_pick_choice_loop;
+ @choice_v[@c] = @current;
+ @choice_n$[@c] = @current_name$;
+ @choice_i[@c] = @current_index;
+ @c = @c + 1;
+ goto L_pick_choice_loop;
+
+L_choice_init_done:
+ @choice_v[@c] = 0;
+ @choice_n$[@c] = @default_choice$;
+ @c = @c + 1;
+
+ if (@c < 10)
+ menu
+ @choice_n$[0], L_MenuItems,
+ @choice_n$[1], L_MenuItems,
+ @choice_n$[2], L_MenuItems,
+ @choice_n$[3], L_MenuItems,
+ @choice_n$[4], L_MenuItems,
+ @choice_n$[5], L_MenuItems,
+ @choice_n$[6], L_MenuItems,
+ @choice_n$[7], L_MenuItems,
+ @choice_n$[8], L_MenuItems,
+ @choice_n$[9], L_MenuItems;
+ goto L_MenuItems;
+
+L_MenuItems:
+ if (@c < 10)
+ goto L_choice_join;
+
+ if (@c < 20)
+ menu
+ @choice_n$[0], L_MenuItems1,
+ @choice_n$[1], L_MenuItems1,
+ @choice_n$[2], L_MenuItems1,
+ @choice_n$[3], L_MenuItems1,
+ @choice_n$[4], L_MenuItems1,
+ @choice_n$[5], L_MenuItems1,
+ @choice_n$[6], L_MenuItems1,
+ @choice_n$[7], L_MenuItems1,
+ @choice_n$[8], L_MenuItems1,
+ @choice_n$[9], L_MenuItems1,
+ @choice_n$[10], L_MenuItems1,
+ @choice_n$[11], L_MenuItems1,
+ @choice_n$[12], L_MenuItems1,
+ @choice_n$[13], L_MenuItems1,
+ @choice_n$[14], L_MenuItems1,
+ @choice_n$[15], L_MenuItems1,
+ @choice_n$[16], L_MenuItems1,
+ @choice_n$[17], L_MenuItems1,
+ @choice_n$[18], L_MenuItems1,
+ @choice_n$[19], L_MenuItems1;
+ goto L_MenuItems1;
+
+L_MenuItems1:
+ if (@c < 20)
+ goto L_choice_join;
+
+ menu
+ @choice_n$[0], L_choice_join,
+ @choice_n$[1], L_choice_join,
+ @choice_n$[2], L_choice_join,
+ @choice_n$[3], L_choice_join,
+ @choice_n$[4], L_choice_join,
+ @choice_n$[5], L_choice_join,
+ @choice_n$[6], L_choice_join,
+ @choice_n$[7], L_choice_join,
+ @choice_n$[8], L_choice_join,
+ @choice_n$[9], L_choice_join,
+ @choice_n$[10], L_choice_join,
+ @choice_n$[11], L_choice_join,
+ @choice_n$[12], L_choice_join,
+ @choice_n$[13], L_choice_join,
+ @choice_n$[14], L_choice_join,
+ @choice_n$[15], L_choice_join,
+ @choice_n$[16], L_choice_join,
+ @choice_n$[17], L_choice_join,
+ @choice_n$[18], L_choice_join,
+ @choice_n$[19], L_choice_join,
+ @choice_n$[20], L_choice_join,
+ @choice_n$[21], L_choice_join,
+ @choice_n$[22], L_choice_join,
+ @choice_n$[23], L_choice_join,
+ @choice_n$[24], L_choice_join,
+ @choice_n$[25], L_choice_join,
+ @choice_n$[26], L_choice_join,
+ @choice_n$[27], L_choice_join,
+ @choice_n$[28], L_choice_join,
+ @choice_n$[29], L_choice_join,
+ @choice_n$[30], L_choice_join,
+ @choice_n$[31], L_choice_join;
+
+L_choice_join:
+ @menu = @menu - 1;
+ @item = @choice_v[@menu];
+ @index = @choice_i[@menu];
+ if (@menu >= @c)
+ @item = 0;
+ goto L_Clean;
+
+L_Clean:
+ @menu = 0;
+ @items_nr = 0;
+ @c = 0;
+ @i = 0;
+ @current = 0;
+ @current_name$ = "";
+ cleararray @choice_v, 0, getarraysize(@choice_v);
+ cleararray @choice_n$, "", getarraysize(@choice_n$);
+ cleararray @choice_i, 0, getarraysize(@choice_i);
+ return;
+
+L_ArrayLengthMismatch:
+ debugmes "@items and @item_names$ array length mismatch";
+ mapexit;
+}
+
+
+
+
+
+function script DynamicItemMenu$ {
+set @items_nr, getarraysize(@items$);
+if(@items_nr != getarraysize(@item_names$)) goto L_ArrayLengthMismatch;
+if(@default_choice$ == "") set @default_choice$, "Never mind.";
+goto L_pick_one_of_many_items;
+
+L_pick_one_of_many_items:
+ @c = 0;
+ @i = 0;
+
+ setarray @choice_n$, "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "";
+ goto L_pick_choice_loop;
+
+L_pick_choice_loop:
+ if (@i >= @items_nr)
+ goto L_choice_init_done;
+ @current$ = @items$[@i];
+ @current_name$ = @item_names$[@i];
+ @current_index = @i;
+ @i = @i + 1;
+
+ if (countitem(@current$) == 0)
+ goto L_pick_choice_loop;
+ @choice_v$[@c] = @current$;
+ @choice_n$[@c] = @current_name$;
+ @choice_i[@c] = @current_index;
+ @c = @c + 1;
+ goto L_pick_choice_loop;
+
+L_choice_init_done:
+ @choice_v$[@c] = "";
+ @choice_n$[@c] = @default_choice$;
+ @c = @c + 1;
+
+ if (@c < 10)
+ menu
+ @choice_n$[0], L_MenuItems,
+ @choice_n$[1], L_MenuItems,
+ @choice_n$[2], L_MenuItems,
+ @choice_n$[3], L_MenuItems,
+ @choice_n$[4], L_MenuItems,
+ @choice_n$[5], L_MenuItems,
+ @choice_n$[6], L_MenuItems,
+ @choice_n$[7], L_MenuItems,
+ @choice_n$[8], L_MenuItems,
+ @choice_n$[9], L_MenuItems;
+ goto L_MenuItems;
+
+L_MenuItems:
+ if (@c < 10)
+ goto L_choice_join;
+
+ if (@c < 20)
+ menu
+ @choice_n$[0], L_MenuItems1,
+ @choice_n$[1], L_MenuItems1,
+ @choice_n$[2], L_MenuItems1,
+ @choice_n$[3], L_MenuItems1,
+ @choice_n$[4], L_MenuItems1,
+ @choice_n$[5], L_MenuItems1,
+ @choice_n$[6], L_MenuItems1,
+ @choice_n$[7], L_MenuItems1,
+ @choice_n$[8], L_MenuItems1,
+ @choice_n$[9], L_MenuItems1,
+ @choice_n$[10], L_MenuItems1,
+ @choice_n$[11], L_MenuItems1,
+ @choice_n$[12], L_MenuItems1,
+ @choice_n$[13], L_MenuItems1,
+ @choice_n$[14], L_MenuItems1,
+ @choice_n$[15], L_MenuItems1,
+ @choice_n$[16], L_MenuItems1,
+ @choice_n$[17], L_MenuItems1,
+ @choice_n$[18], L_MenuItems1,
+ @choice_n$[19], L_MenuItems1;
+ goto L_MenuItems1;
+
+L_MenuItems1:
+ if (@c < 20)
+ goto L_choice_join;
+
+ menu
+ @choice_n$[0], L_choice_join,
+ @choice_n$[1], L_choice_join,
+ @choice_n$[2], L_choice_join,
+ @choice_n$[3], L_choice_join,
+ @choice_n$[4], L_choice_join,
+ @choice_n$[5], L_choice_join,
+ @choice_n$[6], L_choice_join,
+ @choice_n$[7], L_choice_join,
+ @choice_n$[8], L_choice_join,
+ @choice_n$[9], L_choice_join,
+ @choice_n$[10], L_choice_join,
+ @choice_n$[11], L_choice_join,
+ @choice_n$[12], L_choice_join,
+ @choice_n$[13], L_choice_join,
+ @choice_n$[14], L_choice_join,
+ @choice_n$[15], L_choice_join,
+ @choice_n$[16], L_choice_join,
+ @choice_n$[17], L_choice_join,
+ @choice_n$[18], L_choice_join,
+ @choice_n$[19], L_choice_join,
+ @choice_n$[20], L_choice_join,
+ @choice_n$[21], L_choice_join,
+ @choice_n$[22], L_choice_join,
+ @choice_n$[23], L_choice_join,
+ @choice_n$[24], L_choice_join,
+ @choice_n$[25], L_choice_join,
+ @choice_n$[26], L_choice_join,
+ @choice_n$[27], L_choice_join,
+ @choice_n$[28], L_choice_join,
+ @choice_n$[29], L_choice_join,
+ @choice_n$[30], L_choice_join,
+ @choice_n$[31], L_choice_join;
+
+L_choice_join:
+ @menu = @menu - 1;
+ @item$ = @choice_v$[@menu];
+ @index = @choice_i[@menu];
+ if (@menu >= @c)
+ @item$ = "";
+ goto L_Clean;
+
+L_Clean:
+ @menu = 0;
+ @items_nr = 0;
+ @c = 0;
+ @current_index = 0;
+ @i = 0;
+ @current$ = 0;
+ @current_name$ = "";
+ cleararray @choice_v$, "", getarraysize(@choice_v$);
+ cleararray @choice_n$, "", getarraysize(@choice_n$);
+ cleararray @choice_i, 0, getarraysize(@choice_i);
+ return;
+
+L_ArrayLengthMismatch:
+ debugmes "@items$ and @item_names$ array length mismatch";
+ mapexit;
+}
diff --git a/npc/functions/evil_obelisk.txt b/npc/functions/evil_obelisk.txt
new file mode 100644
index 00000000..b85884c7
--- /dev/null
+++ b/npc/functions/evil_obelisk.txt
@@ -0,0 +1,96 @@
+function script EvilObelisk {
+ mes "[Evil Obelisk]";
+ mes "(A mystical aura surrounds this stone. It seems to crave money.)";
+ next;
+
+ @Cost_jacko = 6500;
+ @Cost_gy1 = 4000;
+ @Cost_gy2 = 3000;
+ @Cost_skull = 2800;
+ @Cost_snake = 2500;
+
+ menu
+ "Don't pay it anything.", L_close,
+ "Pay it " + @Cost_jacko + " gold.", L_JACKO,
+ "Pay it " + @Cost_gy1 + " gold.", L_GRAVEYARD1,
+ "Pay it " + @Cost_gy2 + " gold.", L_GRAVEYARD2,
+ "Pay it " + @Cost_skull + " gold.", L_SKULL,
+ "Pay it " + @Cost_snake + " gold.", L_SNAKE;
+
+L_JACKO:
+ if (Zeny < @Cost_jacko)
+ goto L_NotEnough;
+ Zeny = Zeny - @Cost_jacko;
+ @mob_id = 1022;
+ @mob_count = rand(1,2);
+ goto L_Summon;
+
+L_GRAVEYARD1:
+ if (Zeny < @Cost_gy1)
+ goto L_NotEnough;
+ @temp = rand(2);
+ if(@temp == 0)
+ set @mob_id, 1036; // Zombie
+ if(@temp == 1)
+ set @mob_id, 1045; // Fallen
+ @mob_count = rand(1,2);
+ Zeny = Zeny - @Cost_gy1;
+ goto L_Summon;
+
+L_GRAVEYARD2:
+ if (Zeny < @Cost_gy2)
+ goto L_NotEnough;
+ @temp = rand(2);
+ if(@temp == 0)
+ set @mob_id, 1044; // Lady Skelly
+ if(@temp == 1)
+ set @mob_id, 1043; // Normal Skelly
+ @mob_count = rand(1,2);
+ Zeny = Zeny - @Cost_gy2;
+ goto L_Summon;
+
+L_SKULL:
+ if (Zeny < @Cost_skull)
+ goto L_NotEnough;
+ @temp = rand(2);
+ if(@temp == 0)
+ set @mob_id, 1024; // Poison
+ if(@temp == 1)
+ set @mob_id, 1023; // Fire
+ @mob_count = rand(1,4);
+ Zeny = Zeny - @Cost_skull;
+ goto L_Summon;
+
+L_SNAKE:
+ if (Zeny < @Cost_snake)
+ goto L_NotEnough;
+ @temp = rand(4);
+ if(@temp == 0)
+ set @mob_id, 1034; // Grass
+ if(@temp == 1)
+ set @mob_id, 1026; // Mnt.
+ if(@temp == 2)
+ set @mob_id, 1010; // Normal
+ if(@temp == 3)
+ set @mob_id, 1021; // Cave
+ @mob_count = rand(1,4);
+ Zeny = Zeny - @Cost_snake;
+ goto L_Summon;
+
+L_Summon:
+ areamonster @map$, @x0, @y0, @x1, @y1, "Evil", @mob_id, @mob_count;
+ goto L_close;
+
+L_NotEnough:
+ mes "You don't have that much money";
+ goto L_close;
+
+L_close:
+ @Cost_jacko = 0;
+ @Cost_gy1 = 0;
+ @Cost_gy2 = 0;
+ @Cost_skull = 0;
+ @Cost_snake = 0;
+ return;
+
+}
diff --git a/npc/functions/ferry.txt b/npc/functions/ferry.txt
new file mode 100644
index 00000000..7feef2c5
--- /dev/null
+++ b/npc/functions/ferry.txt
@@ -0,0 +1,135 @@
+
+017-9,27,28,0 script #FerryConfig NPC32767,{
+ end;
+
+OnInit:
+ disablenpc "Hurnscald South Koga";
+ disablenpc "Candor Koga";
+ disablenpc "Nivalis Koga";
+ disablenpc "Hurnscald North Koga";
+ disablenpc "Tulimshar Koga";
+ $@MainCurrentDock = 0;
+ $@CandorCurrentDock = 0;
+ $@DockTickCount = 0;
+ $@DockLeaveCount = 1;
+ setarray $@MainDocks$, "Hurnscald North", "Nivalis", "Tulimshar";
+ setarray $@CandorDocks$, "Candor", "Hurnscald South";
+ donpcevent "#"+$@MainDocks$[$@MainCurrentDock]+"Dock::OnCommandArrive";
+ donpcevent "#"+$@CandorDocks$[$@CandorCurrentDock]+"Dock::OnCommandArrive";
+ .warp_delay = 430;
+ if (debug >= 2) end;
+ initnpctimer;
+ goto L_k1city2;
+
+L_NextDock:
+ $@MainLastDock = $@MainCurrentDock;
+ $@CandorLastDock = $@CandorCurrentDock;
+ $@MainCurrentDock = $@MainCurrentDock + 1;
+ $@CandorCurrentDock = $@CandorCurrentDock + 1;
+ if($@MainCurrentDock == getarraysize($@MainDocks$))
+ $@MainCurrentDock = 0;
+ if($@CandorCurrentDock == getarraysize($@CandorDocks$))
+ $@CandorCurrentDock = 0;
+ $@DockTickCount = 0;
+ donpcevent "#"+$@MainDocks$[$@MainLastDock]+"Dock::OnCommandWarp";
+ donpcevent "#"+$@MainDocks$[$@MainCurrentDock]+"Dock::OnCommandArrive";
+ donpcevent "#"+$@CandorDocks$[$@CandorLastDock]+"Dock::OnCommandWarp";
+ donpcevent "#"+$@CandorDocks$[$@CandorCurrentDock]+"Dock::OnCommandArrive";
+
+ donpcevent "#k1sound::OnCommandDing";
+ donpcevent "#k2sound::OnCommandDing";
+
+ initnpctimer;
+
+ if($@MainCurrentDock == 2) goto L_k1city1;
+ if($@MainCurrentDock == 0) goto L_k1city2;
+ if($@MainCurrentDock == 1) goto L_k1city3;
+ end;
+
+OnTimer5000:
+ if ($@DockTickCount > $@DockLeaveCount)
+ goto L_NextDock;
+ $@DockTickCount = $@DockTickCount + 1;
+ initnpctimer;
+ end;
+
+
+L_k1city1:
+ enablenpc "#k1city1e"; disablenpc "#k1city2e"; disablenpc "#k1city3e";
+ disablenpc "#k1city1d"; enablenpc "#k1city2d"; enablenpc "#k1city3d";
+ if($@CandorCurrentDock == 1) goto L_k2city1;
+ if($@CandorCurrentDock == 0) goto L_k2city2;
+end;
+
+L_k1city2:
+ disablenpc "#k1city1e"; enablenpc "#k1city2e"; disablenpc "#k1city3e";
+ enablenpc "#k1city1d"; disablenpc "#k1city2d"; enablenpc "#k1city3d";
+ if($@CandorCurrentDock == 1) goto L_k2city1;
+ if($@CandorCurrentDock == 0) goto L_k2city2;
+end;
+
+L_k1city3:
+ disablenpc "#k1city1e"; disablenpc "#k1city2e"; enablenpc "#k1city3e";
+ enablenpc "#k1city1d"; enablenpc "#k1city2d"; disablenpc "#k1city3d";
+ if($@CandorCurrentDock == 1) goto L_k2city1;
+ if($@CandorCurrentDock == 0) goto L_k2city2;
+end;
+
+L_k2city1:
+ enablenpc "#k2city1e"; disablenpc "#k2city2e";
+ disablenpc "#k2city1d"; enablenpc "#k2city2d";
+end;
+
+L_k2city2:
+ disablenpc "#k2city1e"; enablenpc "#k2city2e";
+ enablenpc "#k2city1d"; disablenpc "#k2city2d";
+end;
+}
+
+function script FerryHelp {
+ mes "\"You wait on the dock for the ship to come in. You'll be given a chance to board the boat when it comes into port.\"";
+ mes "\"It lingers in port to allow you some time to board in case you are running behind.\"";
+ mes "\"Once on the ship, it will sail to different ports and annouce where it is docking.\"";
+ mes "\"There are 2 Ferrys, both ferrys are free to ride.\"";
+ mes "\"The Main ferry travels from Argeas, Kaizei and Tonori. It docks in the major ports Hurnscald North, Nivalis and the Tulimshar Dock.\"";
+ mes "\"The Candor Ferry only travels betwen Candor and the Hurnscald South Dock.\"";
+ mes "\"Refreshments and supplies are offered aboard both ships during the voyage.\"";
+ mes "\"We also have some slot machines in case you get bored.\"";
+ return;
+}
+
+function script BoardFerry {
+ if ($@MainCurrentDock == 0 && getmapname() == "008-1") goto L_Board;
+ else if ($@MainCurrentDock == 1 && getmapname() == "031-1") goto L_Board;
+ else if ($@MainCurrentDock == 2 && getmapname() == "001-1") goto L_Board;
+
+ @NextDock = $@MainCurrentDock + 1;
+ if(@NextDock == getarraysize($@MainDocks$)) set @NextDock, 0;
+ message strcharinfo(0),
+ "Ferry : ##3The ferry is currently at ##B"+$@MainDocks$[$@MainCurrentDock]+"##b. "
+ +"It will be arriving at ##B"+$@MainDocks$[@NextDock]+"##b next.";
+ return;
+
+L_Board:
+ warp "035-2",32,29;
+ return;
+}
+
+function script BoardCandorFerry {
+ if ($@CandorCurrentDock == 0 && getmapname() == "029-1") goto L_Board;
+ else if ($@CandorCurrentDock == 1 && getmapname() == "008-1") goto L_Board;
+
+ @NextDock = $@CandorCurrentDock + 1;
+ if(@NextDock == getarraysize($@CandorDocks$)) set @NextDock, 0;
+ message strcharinfo(0),
+ "Ferry : ##3The ferry is currently at ##B"+$@CandorDocks$[$@CandorCurrentDock]+"##b. "
+ +"It will be arriving at ##B"+$@CandorDocks$[@NextDock]+"##b next.";
+ return;
+
+L_Board:
+ warp "036-2",32,29;
+ goto L_Return;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/filters.txt b/npc/functions/filters.txt
new file mode 100644
index 00000000..91e536dc
--- /dev/null
+++ b/npc/functions/filters.txt
@@ -0,0 +1,132 @@
+// TMW2 scripts.
+// Authors:
+// Jesusalva
+// Description:
+// Several filters
+
+// filter_always( id )
+function script filter_always {
+ return true;
+}
+
+// filter_onlyme( id )
+function script filter_onlyme {
+ return (getarg(0) == getcharid(3));
+}
+
+// filter_notme( id )
+function script filter_notme {
+ return (getarg(0) != getcharid(3));
+}
+
+// filter_sameguild( id )
+function script filter_sameguild {
+ if (getarg(0) == getcharid(3))
+ return true;
+ if (getcharid(2) < 1)
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameguildnotyou( id )
+function script filter_sameguildnotyou {
+ if (getcharid(2) < 1)
+ return false;
+ if (getarg(0) == getcharid(3))
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameparty( id )
+function script filter_sameparty {
+ if (getarg(0) == getcharid(3))
+ return true;
+ if (getcharid(1) < 1 && getarg(0) != getcharid(3))
+ return false;
+ return (strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+}
+
+// filter_sameguildorparty( id )
+function script filter_sameguildorparty {
+ if (getarg(0) == getcharid(3))
+ return true;
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_sameguildorpartynotyou( id )
+function script filter_sameguildorpartynotyou {
+ if (getarg(0) == getcharid(3))
+ return false;
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_hostile( id )
+function script filter_hostile {
+ //.@type=getunitdata(getarg(0), UDT_TYPE);
+ .@type=getunittype(getarg(0));
+ .@chkid=getarg(0);
+
+ // Players outside PVP
+ if (.@type == UNITTYPE_PC) {
+ getmapxy(.@m$, .@x, .@y, .@type, .@chkid);
+ if (!ispvpmap(.@m$))
+ return false;
+ // FIXME: We already have !(filter_sameguildorparty())
+ // We might be over-processing this
+ // Honor party flag
+ if (!getmapflag(.@mapa$, mf_pvp_noparty) &&
+ getcharid(1) == getcharid(1, strcharinfo(0, "", .@chkid)))
+ return false;
+ // Honor guild flag
+ if (!getmapflag(.@mapa$, mf_pvp_noguild) &&
+ getcharid(2) == getcharid(2, strcharinfo(0, "", .@chkid)))
+ return false;
+ }
+
+ // Monsters
+ if (.@type == UNITTYPE_MOB)
+ return true;
+
+ // NPCs
+ if (.@type == UNITTYPE_NPC)
+ return false;
+
+ // Homunculus
+ if (.@type == UNITTYPE_HOM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Pets
+ if (.@type == UNITTYPE_PET)
+ .@chkid=getunitdata(getarg(0), UDT_MASTERAID);
+
+ // Mercenaries
+ if (.@type == UNITTYPE_MER)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Elementals
+ if (.@type == UNITTYPE_ELEM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ //debugmes "filter_hostile: Filtering %d (original %d) (BL %d)", .@chkid, getarg(0), .@type;
+ // Players (and slaves)
+ return !(filter_sameguildorparty(.@chkid));
+}
+
+// filter_friendly( id )
+function script filter_friendly {
+ return !(filter_hostile(getarg(0)));
+}
+
+// filter_notboss( id )
+function script filter_notboss {
+ return (!(getunitdata(getarg(0), UDT_MODE) & MD_BOSS));
+}
+
diff --git a/npc/functions/game_rules.txt b/npc/functions/game_rules.txt
new file mode 100644
index 00000000..9b416a83
--- /dev/null
+++ b/npc/functions/game_rules.txt
@@ -0,0 +1,22 @@
+
+function script GameRules {
+ mes l("##BPlease click submit.");
+ clear;
+ title l("Game Rules");
+ mes l("Players breaking the following rules may be banned for any length of time (even permanently) or have their characters reset at a GM's discretion:");
+ mes l("1) Do not abuse other players. Insults, swearing, and the like are not to be directed towards a particular person or group.");
+ mes l("2) No bots – including ##Bany##b AFK activity or automated actions of any sort.");
+ mes l("3) No spamming or flooding (including messages, whispers, and trade requests).");
+ mes l("4) No begging.");
+ mes l("5) Speak ##Bonly##b English in the public chat.");
+ mes l("6) Treat others how you would like to be treated.");
+ mes l("AFK botting will be determined by talking to players who are moving and/or attacking.");
+ mes l("Automated following will be determined by observation.");
+
+ if (TUT_var == 0)
+ TUT_var = gettimetick(2);
+ goto L_Return;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/ghost.txt b/npc/functions/ghost.txt
new file mode 100644
index 00000000..1325525b
--- /dev/null
+++ b/npc/functions/ghost.txt
@@ -0,0 +1,36 @@
+function script SpawnGhost {
+ if ($GHOSTS_DISABLED)
+ goto L_Return;
+
+ if (BaseLevel >= 40 &&
+ (getmapname() == "026-1" ||
+ getmapname() == "027-1" ||
+ getmapname() == "027-2" ||
+ getmapname() == "027-3" ||
+ getmapname() == "027-4" ||
+ getmapname() == "027-5"))
+ goto L_Spawn;
+ return;
+
+L_Spawn:
+ .@x = POS_X;
+ .@y = POS_Y;
+
+ if (!(iscollision(getmapname(), .@x, .@y - 1))) set .@y, .@y - 1; // up
+ else if (!(iscollision(getmapname(), .@x, .@y + 1))) set .@y, .@y + 1; // down
+ else if (!(iscollision(getmapname(), .@x - 1, .@y))) set .@x, .@x - 1; // left
+ else if (!(iscollision(getmapname(), .@x + 1, .@y))) set .@x, .@x + 1; // right
+ // else on the player
+
+ // FIXME (Is this a valid event label??)
+ if (@GHOST_MAP$ != "")
+ killmonster @GHOST_MAP$, "~GH~" + getcharid(0);
+
+ @GHOST_MAP$ = getmapname();
+ specialeffect(39, SELF, getcharid(3));
+ monster getmapname(), .@x, .@y, strcharinfo(0), 1136, 1, "~GH~" + getcharid(0);
+ return;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/global_event_handler.txt b/npc/functions/global_event_handler.txt
new file mode 100644
index 00000000..69972292
--- /dev/null
+++ b/npc/functions/global_event_handler.txt
@@ -0,0 +1,49 @@
+
+- script #GlobalHandler NPC32767,{
+ end;
+
+OnPCLoginEvent:
+ @login_event = 1;
+ adddefaultskills();
+ //callfunc "fixHeadStyles"; // convert headstyles
+ ClearVariables(); // removes / converts old variables
+ DisplayMOTD(); // send the motd to the client, if enabled
+ TMWBirthday();
+ // add more here
+ vaultOnLogin();
+ @login_event = 2;
+ end;
+
+OnPCLogoutEvent:
+ vaultOnLogout();
+ end;
+
+OnPCKillEvent:
+ elanore_decrease_exp(); // decrease heal exp for doing bad things
+ end;
+
+OnNPCKillEvent:
+OnMobKillEvent:
+ MobPoints();
+ end;
+
+OnPCDieEvent:
+ @necromancer = 0;
+ callfunc "SpawnGhost";
+ set @killerrid, 0; // reset killer rid
+ end;
+
+// Cleanup: Retain chat logs for 24~48 hours
+// Cleanup: Retain item logs for 2 months
+OnClock0500:
+ query_sql("DELETE FROM `picklog` WHERE `time` < '"+sqldate(0, -2)+"'");
+ query_sql("DELETE FROM `zenylog` WHERE `time` < '"+sqldate(0, -2)+"'");
+OnClock1700:
+ if (gettime(GETTIME_DAYOFMONTH) > 1)
+ query_sql("DELETE FROM `chatlog` WHERE `time` < '"+sqldate(-1)+"'");
+ end;
+
+OnInit:
+ MOTD(); // set the MOTD array
+ end;
+}
diff --git a/npc/functions/gm_island.txt b/npc/functions/gm_island.txt
new file mode 100644
index 00000000..82fc0210
--- /dev/null
+++ b/npc/functions/gm_island.txt
@@ -0,0 +1,71 @@
+- script #GmConfig NPC32767,{
+ end;
+
+OnInit:
+ disablenpc "Gm Event#1";
+ disablenpc "Gm Event#2";
+ disablenpc "Gm Event#3";
+ end;
+}
+function script GmWarp {
+ warp "028-1", 110, 30;
+ return;
+}
+function script GmDebug {
+ mes "[GM Debug]";
+ mes "What do you want to do?";
+ menu
+ "Open Event Portals.", L_GmStart,
+ "Close Event Portals.", L_GmStop,
+ "Disguise.", L_Disguise;
+
+L_GmStart:
+ enablenpc "Gm Event#1";
+ enablenpc "Gm Event#2";
+ enablenpc "Gm Event#3";
+ return;
+
+L_GmStop:
+ disablenpc "Gm Event#1";
+ disablenpc "Gm Event#2";
+ disablenpc "Gm Event#3";
+ return;
+
+L_Disguise:
+ npcaction 9;
+ mes "[GM Debug - Disguise]";
+ mes "The Disguise function allows GMs to disguise themselves as mobs or npcs.";
+ mes "---";
+ mes "Mob IDs: [@@https://www.themanaworld.org/index.php/Monster_Reference|Monster Reference@@]";
+ mes "You can either logout or use id ##B0##b to reset.";
+ mes "---";
+ mes "Please input the mob ID:";
+ input @disguise_id;
+ if (@disguise_id < 1000 || @disguise_id > 32767)
+ goto L_ResetClass;
+ mes "Your disguise has been changed.";
+ if (Class <= 5)
+ @old_class = Class;
+ Class = @disguise_id;
+ mes "";
+ mes "For technical reasons, you are not able to see your own disguise, but other players will see it.";
+ return;
+
+L_ResetClass:
+ mes "Your appearance has been reset.";
+ Class = if_then_else(@old_class, @old_class, 1);
+ callfunc "fixHeadStyles";
+ return;
+}
+001-1,49,68,0 script Gm Event#1 NPC368,0,0,{
+ callfunc "GmWarp";
+ end;
+}
+009-1,46,30,0 script Gm Event#2 NPC368,0,0,{
+ callfunc "GmWarp";
+ end;
+}
+020-1,67,89,0 script Gm Event#3 NPC368,0,0,{
+ callfunc "GmWarp";
+ end;
+}
diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt
new file mode 100644
index 00000000..b5214618
--- /dev/null
+++ b/npc/functions/goodbye.txt
@@ -0,0 +1,152 @@
+// Evol functions.
+// Authors:
+// gumi
+// Reid
+// Description:
+// script terminator functions
+
+
+
+// goodbye_msg
+// Tell a random goodbye sentence.
+// Variables:
+// .@rand = Random number between the number of "goodbye" choice.
+
+function script goodbye_msg {
+ setarray .byemsg$[0],
+ l("See you!"),
+ l("See you later!"),
+ l("See you soon!"),
+ l("Bye!"),
+ l("Farewell."),
+ l("Bye then!"),
+ l("Goodbye."),
+ l("Bye for now."),
+ l("Talk to you soon!"),
+ l("Talk to you later!"),
+ l("Have a good day!"),
+ l("Cheers!"),
+ l("Take care!");
+
+ return any_of(.byemsg$);
+}
+
+
+
+// cwarp
+// Closes the dialog, then warps the player.
+// You almost always want to use this instead of `warp`.
+// usage:
+// cwarp;
+// cwarp x, y;
+// cwarp map, x, y;
+
+function script cwarp {
+ .@map$ = getarg(0, "");
+ .@x = getarg(1, 0);
+ .@y = getarg(2, 0);
+
+ if (getargcount() > 0 && getargcount() < 3)
+ {
+ .@npc$ = strnpcinfo(0);
+ .@map$ = getvariableofnpc(.map$, .@npc$);
+ .@x = getarg(0);
+ .@y = getarg(1);
+ }
+
+ getmapxy .@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC; // get char location
+
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (getargcount() < 1)
+ {
+ warp .@pc_map$, .@pc_x, .@pc_y; // no arguments, just refresh
+ close;
+ }
+
+ if (.@map$ == .@pc_map$)
+ {
+ if (.@pc_x == .@x && .@pc_y == .@y)
+ {
+ close; // same location, don't move
+ }
+
+ else
+ {
+ slide .@x, .@y; // same map, slide instead of full warp
+ close;
+ }
+ }
+
+ warp .@map$, .@x, .@y; // different map, warp to given location
+ close;
+}
+
+
+
+// cshop
+// closes the dialog, then opens a shop
+// if no npc is given, calls "#<npc> $"
+
+function script cshop {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ shop getarg(0, "#" + strnpcinfo(0) + " $");
+ //close; => the shop buildin already sends close, and is a terminator itself
+}
+
+
+
+// cstorage
+// closes the dialog, then opens storage
+
+function script cstorage {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ openstorage;
+ close;
+}
+
+
+
+// bye
+// closes the dialog without waiting for the player to press close
+// can also display an emote
+
+function script bye {
+ .@emote = getarg(0, -1);
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ close;
+}
+
+
+
+// goodbye
+// same as bye, but also displays a canned message
+// can also display an emote
+
+function script goodbye {
+ npctalkonce(goodbye_msg());
+ bye getarg(0, -1);
+}
+
+
+
+// goodbye2
+// Waits for the player to press close, displays a canned message,
+// ends execution.
+// Can also display an emote
+
+function script goodbye2 {
+ .@emote = getarg(0, -1);
+
+ close2;
+ npctalkonce(goodbye_msg());
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ end;
+}
diff --git a/npc/functions/headstyles.txt b/npc/functions/headstyles.txt
new file mode 100644
index 00000000..c56eb900
--- /dev/null
+++ b/npc/functions/headstyles.txt
@@ -0,0 +1,46 @@
+
+function script fixHeadStyles {
+ callfunc "getHeadStyles";
+ if (!HELLOWORLD)
+ goto L_RandomHair;
+ set @style, getlook(LOOK_HAIR); // FIXME: this needs to be a param in the future
+ set @color, getlook(LOOK_HAIR_COLOR); // FIXME: this needs to be a param in the future
+ debugmes "Login OK";
+ return;
+ if (@color >= 123 && @color <= HC_WHITE) // convert shock white
+ @color = (HC_WHITE - Class) + 1;
+ if (@color < 105 && ((@color - (15 * (Class - 1))) < 0 || @color > ((15 * (Class - 1)) + (getarraysize(@HairColors$) - 1))))
+ set @color, 15 * (Class - 1); // it is possible to style color 0 but not style 0 since style is treated as an item
+ setlook LOOK_HAIR_COLOR, @color; // FIXME: this needs to be a param in the future
+ return;
+
+L_RandomHair:
+ HELLOWORLD=true;
+ //resetstatus; // <= important! gives 48 stat points
+ debugmes "Set random look....";
+ setlook LOOK_HAIR, rand(1, getarraysize(@HairStyles$));
+ setlook LOOK_HAIR_COLOR, rand(15);
+ return;
+}
+
+// Hairstyle config
+// set array of style and colors
+- script hairstyle_config NPC_HIDDEN,{
+ end;
+
+OnInit:
+ setarray $@hairstyle$[0], "(none)", "Bald", "Flat Ponytail", "Bowl Cut", // 3
+ "Combed back", "Emo", "Mohawk", "Pompadour", "Center parting", // 8
+ "Long and Slick", "Short and Curly", "Pigtails", "Long and Curly", // 12
+ "Parted", "Perky Ponytail", "Wave", "Mane", "Bun", // 17
+ "Shoulder Length and Flick", "Fizzy", "Long and Clopped", "Bunches", // 21
+ "Long Ponytail", "Indefinitely long";
+
+ setarray $@haircolor$[0], "Light Brown", "Green", "Red",
+ "Purple", "Gray", "Yellow", "Blue",
+ "Light Red", "Light Blue", "Dark Purple", "Black",
+ "Pink", "Brown", "Dark";
+
+ setarray $@REFEXP[0], 400, 900, 2250, 6500, 15000;
+ end;
+}
diff --git a/npc/functions/inc_sc_bonus.txt b/npc/functions/inc_sc_bonus.txt
new file mode 100644
index 00000000..7bd24331
--- /dev/null
+++ b/npc/functions/inc_sc_bonus.txt
@@ -0,0 +1,105 @@
+// TMW-2 Script.
+// Author:
+// Jesusalva
+// Description:
+// Applies effects for INC_* (STR doesn't exist)
+// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE SC_FURY
+// Doesn't works: SC_STRUP
+// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE
+/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken)
+// PS. SC_FURY causes crit rate to increase
+//
+// Variables:
+// .@delay Second of buffing
+// .@type SC_*
+// .@min Min amount of type
+// .@max Max amount of type (optional)
+
+// SC_Bonus(delay, SC, min{, max})
+function script SC_Bonus {
+ .@delay=getarg(0);
+ .@type=getarg(1);
+ .@min=getarg(2);
+ .@max=getarg(3, .@min);
+ if (.@delay <= 0)
+ return false;
+
+ // Get the bonus value
+ if (.@min != .@max)
+ .@bonus=rand2(.@min, .@max);
+ else
+ .@bonus=.@min;
+
+ // Remaining time and effect conversion
+ .@v=getstatus(.@type, 1);
+ .@t=getstatus(.@type, 5);
+
+ // Convert remaining time to seconds, rounded down
+ if (.@t > 1000)
+ .@t=.@t/1000;
+ else
+ .@t=0;
+
+ // If there was effect previously, get ponderate average
+ if (.@v > 0)
+ .@v=ponderate_avg(.@bonus, .@delay, .@v, .@t);
+ else
+ .@v=.@bonus;
+
+ // Update time value to ms and to stack
+ .@t+=.@delay;
+ .@t*=1000;
+
+ // Debug print if needed
+ if (debug || $@GM_OVERRIDE)
+ debugmes "Effect %d (+%d percent) for %d ms", .@type, .@bonus, .@t;
+
+ // Restart the bonus
+ sc_end .@type;
+ sc_start .@type,.@t,.@v;
+ return true;
+}
+
+// SC_Bonus2(delay, SC1, val1, val2)
+function script SC_Bonus2 {
+ .@delay=getarg(0);
+ .@type=getarg(1);
+ .@val1=getarg(2);
+ .@val2=getarg(3);
+ if (.@delay <= 0)
+ return false;
+
+ // Remaining time and effect conversion
+ .@v1=getstatus(.@type, 1);
+ .@v2=getstatus(.@type, 2);
+ .@ts=getstatus(.@type, 5);
+
+ // Convert remaining time to seconds, rounded down
+ .@ts=.@ts/1000;
+
+ // If there was effect previously, get ponderate average (val1)
+ if (.@v1 > 0)
+ .@v1=ponderate_avg(.@val1, .@delay, .@v1, .@ts);
+ else
+ .@v1=.@val1;
+
+ // If there was effect previously, get ponderate average (val2)
+ if (.@v2 > 0)
+ .@v2=ponderate_avg(.@val2, .@delay, .@v2, .@ts);
+ else
+ .@v2=.@val2;
+
+ // Update time value to ms and to stack
+ .@t+=.@delay;
+ .@t*=1000;
+
+ // Debug print if needed
+ if (debug || $@GM_OVERRIDE)
+ debugmes "Effect %d (%d/%d bonus) for %d ms", .@type, .@v1, .@v2, .@ts;
+
+ // Restart the bonus
+ sc_end .@type;
+ sc_start2 .@type, .@ts, .@v1, .@v2;
+ return true;
+}
+
diff --git a/npc/functions/inn.txt b/npc/functions/inn.txt
new file mode 100644
index 00000000..50f1afd6
--- /dev/null
+++ b/npc/functions/inn.txt
@@ -0,0 +1,34 @@
+
+function script Inn {
+ if(@npcname$ == "") set @npcname$, strnpcinfo(1);
+ mes "[" + @npcname$ + "]";
+ mes "\"Would you like to rest? It's only " + @Cost + " gp.\"";
+ next;
+ menu
+ "Yes", L_Next,
+ "No", L_close;
+
+L_Next:
+ if (Zeny < @Cost)
+ goto L_NoMoney;
+ Zeny = Zeny - @Cost;
+ heal 10000, 10000;
+
+ mes "[" + @npcname$ + "]";
+ mes "\"Sleep well!\"";
+ next;
+ goto L_close;
+
+L_close:
+ mes "[" + @npcname$ + "]";
+ mes "\"See you.\"";
+ @npcname$ = "";
+ close2;
+ return;
+
+L_NoMoney:
+ mes "[" + @npcname$ + "]";
+ mes "\"You don't have enough money to stay here.\"";
+ next;
+ goto L_close;
+}
diff --git a/npc/functions/input.txt b/npc/functions/input.txt
new file mode 100644
index 00000000..0a510b74
--- /dev/null
+++ b/npc/functions/input.txt
@@ -0,0 +1,110 @@
+// Evol functions.
+// Author:
+// 4144
+// Jesusalva
+// Description:
+// Input utility functions
+// Variables:
+// none
+
+function script menuint {
+ deletearray .@vals;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ .@vals[.@cnt] = -1;
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return -1;
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
+function script menustr {
+ deletearray .@vals$;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals$[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return "";
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals$))
+ return "";
+
+ @menuret$ = .@vals$[@menu];
+ return @menuret$;
+}
+
+// menuint2(<array>)
+function script menuint2 {
+ .@menustr$="";
+
+ if (!(getdatatype(getarg(0)) & DATATYPE_VAR))
+ Exception("Inadequate argument type - Must be var", RB_DEFAULT|RB_ISFATAL);
+
+ copyarray(.@ar$, getarg(0), getarraysize(getarg(0)));
+
+ if (getarraysize(.@ar$) % 2 != 0)
+ Exception("Invalid array size: "+getarraysize(.@ar$), RB_DEFAULT|RB_ISFATAL);
+
+ freeloop(true);
+ for (.@f=0; .@f < getarraysize(.@ar$); .@f++) {
+ // String vs Int
+ if (.@f % 2 == 0) {
+ .@menustr$+=.@ar$[.@f]+":";
+ } else {
+ array_push(.@vals, atoi(.@ar$[.@f]));
+ }
+ }
+ freeloop(false);
+
+ // Do the request
+ // We have: .@vals and .@menustr$
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ //debugmes "Option %d", @menu;
+ //debugmes "Array size %d", getarraysize(.@vals);
+
+ if (@menu == 255)
+ return -1;
+
+ @menu-=1;
+ if (@menu < 0 || @menu > getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt
new file mode 100644
index 00000000..76cdad21
--- /dev/null
+++ b/npc/functions/inventoryplace.txt
@@ -0,0 +1,36 @@
+// Evol functions.
+// Authors:
+// Qwerty Dragon
+// Reid
+// Description:
+// Check if the player have enough place on his inventory to accept new items with arguments:
+// getarg(even numbers) item ID,
+// getarg(odd numbers) number of items,
+
+function script inventoryplace {
+
+ .@argc = getargcount();
+
+ if (.@argc % 2 != 0)
+ {
+ Exception("inventoryplace: Wrong argument count.", RB_SPEECH|RB_ISFATAL|RB_PLEASEREPORT|RB_DEBUGMES);
+ }
+
+ for (.@i = .@j = 0; .@i < .@argc; .@i += 2)
+ {
+ setarray .@item[.@j], getarg(.@i);
+ setarray .@amount[.@j], getarg(.@i + 1);
+ ++.@j;
+ }
+
+ if (!checkweight2(.@item, .@amount))
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("It looks like you can't carry anything else for now."),
+ l("You should come back when you have some free space.");
+
+ close;
+ }
+
+ return true;
+}
diff --git a/npc/functions/location.txt b/npc/functions/location.txt
new file mode 100644
index 00000000..e40148c4
--- /dev/null
+++ b/npc/functions/location.txt
@@ -0,0 +1,128 @@
+// TMW2 Script
+// Author: Jesusalva
+// Location Config
+
+- script loc_config 32767,{
+ end;
+
+OnInit:
+ // TP_FORT TP_BOSSR
+ setarray $@LOCMASTER_TP, TP_CANDOR,TP_TULIM,TP_HURNS,TP_NIVAL;
+ setarray $@LOCMASTER_LOC$, "Candor", "Tulim", "Hurns", "Nival";
+ setarray $@LOCMASTER_MAP$, "029-1", "001-1", "009-1", "020-1";
+ setarray $@LOCMASTER_X, 46, 51, 52, 75;
+ setarray $@LOCMASTER_Y, 97, 78, 41, 85;
+
+ //debugmes "Locmaster: Index 0: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[0], $@LOCMASTER_MAP$[0], $@LOCMASTER_X[0], $@LOCMASTER_Y[0];
+ //debugmes "Locmaster: Index 2: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[2], $@LOCMASTER_MAP$[2], $@LOCMASTER_X[2], $@LOCMASTER_Y[2];
+ //debugmes "Locmaster: Index 5: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[5], $@LOCMASTER_MAP$[5], $@LOCMASTER_X[5], $@LOCMASTER_Y[5];
+ end;
+}
+
+// Resaves your respawn point
+function script ResaveRespawn {
+ .@i=array_find($@LOCMASTER_LOC$, LOCATION$);
+ savepoint $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i];
+ return;
+}
+
+// Warps you to last visited town
+function script ReturnTown {
+ .@i=array_find($@LOCMASTER_LOC$, LOCATION$);
+ warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i];
+ return;
+}
+
+// Convert map name to location id
+// LocToMap( LocName )
+function script LocToMap {
+ // Fill variable
+ .@v$=getarg(0);
+
+ // Error code
+ if (playerattached())
+ .@err=RB_DEFAULT;
+ else
+ .@err=RB_DEBUGMES;
+
+ // Validade variable, see npc/config/location.txt first
+ .@lx=array_find($@LOCMASTER_LOC$, .@v$);
+ if (.@lx < 0)
+ return Exception("Invalid location passed to LocToMap: "+.@v$, .@err);
+
+ return $@LOCMASTER_MAP$[.@lx];
+}
+
+// Convert map name to location id
+// MapToLoc( MapName )
+function script MapToLoc {
+ // Fill variable
+ .@v$=getarg(0);
+
+ // Error code
+ if (playerattached())
+ .@err=RB_DEFAULT;
+ else
+ .@err=RB_DEBUGMES;
+
+ // Validade variable, see npc/config/location.txt first
+ .@lx=array_find($@LOCMASTER_MAP$, .@v$);
+ if (.@lx < 0)
+ return Exception("Invalid map passed to MapToLoc: "+.@v$, .@err);
+
+ return $@LOCMASTER_LOC$[.@lx];
+}
+
+// Gets the location code for TP code
+function script TPToLoc {
+ .@i=array_find($@LOCMASTER_TP, getarg(0));
+ return $@LOCMASTER_MAP$[.@i];
+ return;
+}
+
+// Convert LOC (uppercase) to a TP variable
+// POL_LocToTP( {TOWNCODE} )
+function script POL_LocToTP {
+ .@tw$=strtoupper(getarg(0, LOCATION$));
+
+ // TODO: Change this to use the arrays instead
+ if (.@tw$ == "TULIM")
+ return TP_TULIM;
+
+ if (.@tw$ == "HURNS")
+ return TP_HURNS;
+
+ if (.@tw$ == "NIVAL")
+ return TP_NIVAL;
+
+ if (.@tw$ == "CANDOR")
+ return TP_CANDOR;
+
+ return Exception("Invalid town requested / POL_LocToTP", RB_DEFAULT|RB_SPEECH, -1);
+}
+
+// Upon entering a town
+// EnterTown( LocName )
+function script EnterTown {
+ // Fill variable
+ .@v$=getarg(0);
+
+ // Validade variable, see npc/config/location.txt first
+ if (array_find($@LOCMASTER_LOC$, .@v$) < 0)
+ return Exception("Invalid location passed to EnterTown: "+.@v$);
+
+ LOCATION$=.@v$;
+ return;
+}
+
+// Warps home and updates LOCATION$
+function script teleporthome {
+ warp "Save", 0, 0;
+ .@i=array_find($@LOCMASTER_MAP$, getmap());
+ if (.@i >= 0)
+ EnterTown($@LOCMASTER_LOC$[.@i]);
+ else
+ debugmes("[ERROR] Invalid Town Map for Time Flask: %s", getmap());
+ return;
+}
+
diff --git a/npc/functions/lockpicking.txt b/npc/functions/lockpicking.txt
new file mode 100644
index 00000000..da2da5dd
--- /dev/null
+++ b/npc/functions/lockpicking.txt
@@ -0,0 +1,87 @@
+
+function script LockPicking {
+ @lock_picking_success = 0;
+ setarray @pins, rand(1,3), rand(1,3), rand(1,3);
+ goto L_StartLockPicking;
+
+L_NeedLockPickSet:
+ menu
+ "Unfortunately, I don't have these tools... Let's look around.", L_Return;
+
+L_StartLockPicking:
+ mes "You inspect the lock and notice it isn't sophisticated.";
+ mes "With regular stuff, you should be able to lock pick it.";
+ next;
+ if (countitem ("LockPicks") < 1)
+ goto L_NeedLockPickSet;
+ menu
+ "I can try with these lock picks I just found.", L_Next,
+ "Maybe later.", L_Return;
+
+L_Next:
+ delitem "LockPicks", 1;
+ mes "You insert the hook pick inside the lock, and, without applying any tension, you discover there are only 3 pins to set.";
+ next;
+ mes "You will need to set the 3 pins to align them and turn the lock's cylinder.";
+ mes "A wrong move will make you start over. Remember how you had set the pins!";
+ next;
+ @pin = 0;
+ goto L_HandlePin;
+
+L_NextPinOrEnd:
+ if (@pin >= 2)
+ goto L_OpenDoor;
+ @pin = @pin + 1;
+ mes "Click! This pin is set!";
+ next;
+ goto L_HandlePin;
+
+L_HandlePin:
+ if (@pin == 0)
+ mes "What to do with the first pin?";
+ if (@pin == 1)
+ mes "What to do with the second pin?";
+ if (@pin == 2)
+ mes "What to do with the last pin?";
+ menu
+ "Apply a soft pressure.", L_PinSoft,
+ "Apply a normal pressure.", L_PinNormal,
+ "Apply a strong pressure.", L_PinHard,
+ "Give up. I'm in a rush!", L_GiveUp;
+
+L_GiveUp:
+ @lock_picking_success = 0;
+ @pin = 0;
+ cleararray @pins, 0, 3;
+ @pin_pressure = 0;
+ return;
+
+L_PinSoft:
+ @pin_pressure = 1;
+ goto L_TestPin;
+
+L_PinNormal:
+ @pin_pressure = 2;
+ goto L_TestPin;
+
+L_PinHard:
+ @pin_pressure = 3;
+ goto L_TestPin;
+
+L_TestPin:
+ if (@pin_pressure == @pins[@pin])
+ goto L_NextPinOrEnd;
+ mes "Nope, that did not work. And the pins are unset now...";
+ next;
+ @pin = 0;
+ goto L_HandlePin;
+
+L_Return:
+ return;
+
+L_OpenDoor:
+ @lock_picking_success = 1;
+ mes "The two sets of pins separate. You can now turn the cylinder to open the door!";
+ return;
+
+}
diff --git a/npc/functions/magic.txt b/npc/functions/magic.txt
new file mode 100644
index 00000000..e0962a15
--- /dev/null
+++ b/npc/functions/magic.txt
@@ -0,0 +1,159 @@
+
+
+function script MagicGainBasic {
+ MAGIC_FLAGS = MAGIC_FLAGS | MFLAG_DRANK_POTION;
+ return;
+}
+
+function script MagicTalkOptionsSetup {
+ @QQ_ELANORE = 1;
+ @QQ_MANASEED = 2;
+ @QQ_MANAPOTION = 4;
+ @QQ_WYARA = 8;
+ @QQ_SAGATHA = 16;
+ @QQ_AULDSBEL = 32;
+ @QQ_IMP = 64;
+ @QQ_OLDWIZ = 128;
+ @QQ_ASTRALSOUL = 256;
+ return;
+}
+
+
+function script MagicTalkMenu {
+ setarray @choice$, "", "", "", "", "", "", "", "", "", "";
+ @choices_nr = 0;
+ setarray @choice_idx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
+
+ if (@ignore & @QQ_ELANORE)
+ goto L_Q_post_elanore;
+ @choice$[@choices_nr] = "...Elanore the Healer?";
+ @choice_idx[@choices_nr] = @QQ_ELANORE;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_elanore;
+
+L_Q_post_elanore:
+ if (@ignore == @QQ_MANASEED)
+ goto L_Q_post_manaseed;
+ if (!(MAGIC_FLAGS & (MFLAG_KNOWS_MANASEED | MFLAG_MANASEED_RUMOUR)))
+ goto L_Q_post_manaseed;
+ @choice$[@choices_nr] = "...the Mana Seed?";
+ @choice_idx[@choices_nr] = @QQ_MANASEED;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_manaseed;
+
+L_Q_post_manaseed:
+ if (@ignore & @QQ_MANAPOTION)
+ goto L_Q_post_manapotion;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_MANAPOTION))
+ goto L_Q_post_manapotion;
+ @choice$[@choices_nr] = "...Mana Potions?";
+ @choice_idx[@choices_nr] = @QQ_MANAPOTION;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_manapotion;
+
+L_Q_post_manapotion:
+ if (@ignore & @QQ_WYARA)
+ goto L_Q_post_wyara;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_WYARA))
+ goto L_Q_post_wyara;
+ @choice$[@choices_nr] = "...Wyara the Witch?";
+ @choice_idx[@choices_nr] = @QQ_WYARA;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_wyara;
+
+L_Q_post_wyara:
+ if (@ignore & @QQ_SAGATHA)
+ goto L_Q_post_sagatha;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_SAGATHA))
+ goto L_Q_post_sagatha;
+ @choice$[@choices_nr] = "...Sagatha the Witch?";
+ @choice_idx[@choices_nr] = @QQ_SAGATHA;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_sagatha;
+
+L_Q_post_sagatha:
+ if (@ignore & @QQ_AULDSBEL)
+ goto L_Q_post_auldsbel;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_AULDSBEL))
+ goto L_Q_post_auldsbel;
+ @choice$[@choices_nr] = "...Auldsbel the Wizard?";
+ @choice_idx[@choices_nr] = @QQ_AULDSBEL;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_auldsbel;
+
+L_Q_post_auldsbel:
+ if (@ignore & @QQ_OLDWIZ)
+ goto L_Q_post_oldwiz;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_OLD_WIZARD))
+ goto L_Q_post_oldwiz;
+ @choice$[@choices_nr] = "...the Old Wizard?";
+ @choice_idx[@choices_nr] = @QQ_OLDWIZ;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_oldwiz;
+
+L_Q_post_oldwiz:
+ if (@ignore & @QQ_IMP)
+ goto L_Q_post_imp;
+ if (!(MAGIC_FLAGS & MFLAG_KNOWS_IMP))
+ goto L_Q_post_imp;
+ @choice$[@choices_nr] = "...the Earth Spirit in the desert well?";
+ @choice_idx[@choices_nr] = @QQ_IMP;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_imp;
+
+L_Q_post_imp:
+ if (@ignore & @QQ_ASTRALSOUL)
+ goto L_Q_post_astralsoul;
+ if (!(getskilllv(SKILL_MAGIC)))
+ goto L_Q_post_astralsoul;
+ if (!(getskilllv(SKILL_POOL)))
+ goto L_Q_post_astralsoul;
+ @choice$[@choices_nr] = "...ways to improve my magic?";
+ @choice_idx[@choices_nr] = @QQ_ASTRALSOUL;
+ @choices_nr = @choices_nr + 1;
+ goto L_Q_post_astralsoul;
+
+L_Q_post_astralsoul:
+ @choice$[@choices_nr] = "...never mind.";
+ @choice_idx[@choices_nr] = 0;
+ @choices_nr = @choices_nr + 1;
+
+ menu
+ @choice$[0], L_MenuItems,
+ @choice$[1], L_MenuItems,
+ @choice$[2], L_MenuItems,
+ @choice$[3], L_MenuItems,
+ @choice$[4], L_MenuItems,
+ @choice$[5], L_MenuItems,
+ @choice$[6], L_MenuItems,
+ @choice$[7], L_MenuItems,
+ @choice$[8], L_MenuItems,
+ @choice$[9], L_MenuItems;
+
+L_MenuItems:
+ @menu = @menu - 1;
+
+ if (@menu >= @choices_nr)
+ @menu = 0;
+
+ @c = @choice_idx[@menu];
+ return;
+}
+
+
+function script SkillUp {
+ if (getskilllv(@SUP_id) >= @SUP_lvl)
+ goto L_Shortcut;
+
+ misceffect sfx_skillup, strcharinfo(0);
+ updateskill @SUP_id, @SUP_lvl;
+ getexp @SUP_xp, 0;
+ if (@SUP_xp)
+ mes "[" + @SUP_xp + " experience points]";
+ mes "[Level " + @SUP_lvl + " in " + @SUP_name$ + "]";
+ return;
+
+L_Shortcut:
+ mes "[You already have level " + getskilllv(@SUP_id) + " in " + @SUP_name$ + "]";
+ return;
+}
diff --git a/npc/functions/main.txt b/npc/functions/main.txt
new file mode 100644
index 00000000..d072ff60
--- /dev/null
+++ b/npc/functions/main.txt
@@ -0,0 +1,750 @@
+// TMW2 Script
+// Evol functions.
+// Authors:
+// 4144
+// Travolta
+// gumi
+// Jesusalva
+// Description:
+// Built-in essential functions.
+
+function script menuimage {
+ return getarg(0) + "|" + getarg(1);
+}
+
+function script dnext {
+ if (@dnext >= GSET_LONGMENU_DENSITY) {
+ @dnext=0;
+ next;
+ } else {
+ @dnext+=1;
+ }
+ return;
+}
+
+function script menuaction {
+ return "[" + getarg(0) + "]";
+}
+
+function script setq1 {
+ // Quest, val1 , val2 , val3 , time
+ setq getarg(0), getarg(1), getq2(getarg(0)), getq3(getarg(0)), getqtime(getarg(0));
+ return;
+}
+
+function script setq2 {
+ // Quest, val1 , val2 , val3 , time
+ setq getarg(0), getq(getarg(0)), getarg(1), getq3(getarg(0)), getqtime(getarg(0));
+ return;
+}
+
+function script setq3 {
+ // Quest, val1 , val2 , val3 , time
+ setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getarg(1), getqtime(getarg(0));
+ return;
+}
+
+function script setqtime {
+ // Quest, val1 , val2 , val3 , time
+ setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getq3(getarg(0)), getarg(1);
+ return;
+}
+
+function script mesn {
+ if (getargcount() > 0) {
+ .@s$ = "[" + getarg(0) + "]";
+ } else {
+ .@s$ = "[" + strnpcinfo(1) + "]";
+ }
+ mes .@s$;
+ return;
+}
+
+function script mesq {
+ mes "\"" + getarg(0)+ "\"";
+ return;
+}
+
+function script g {
+ return Sex == 0 ? getarg(0) : getarg(1);
+}
+
+function script b {
+ return "##B" + getarg(0) + "##b";
+}
+
+function script col {
+ .@color = getarg(1,9);
+ if (.@color < 0) .@color = 0;
+ if (.@color > 9) .@color = 9;
+ return "##" + .@color + getarg(0) + "##0";
+}
+
+function script adddefaultskills {
+ if (getskilllv(NV_BASIC) < 7) {
+ skill NV_BASIC, 7, 0;
+ }
+ if (getskilllv(TMW2_FAKESKILL) < 1) {
+ skill TMW2_FAKESKILL, 1, 0;
+ }
+ return;
+}
+
+function script addremovemapmask {
+ setmapmask getarg(0), (getmapmask(getarg(0)) | (getarg(1) + getarg(2))) ^ getarg(2);
+ return;
+}
+
+function script mesc {
+ mes col(getarg(0),getarg(1,9));
+ return;
+}
+
+function script get_race {
+ .@g=getarg(0, Class);
+ return l($@allraces$[.@g]);
+}
+
+// tutmes (message, {header=Tutorial, headerfirst=True})
+function script tutmes {
+ .@header$=getarg(1, l("TUTORIAL"));
+ .@showheader=getarg(2, true);
+ .@tcol=9; // Tutorial color code
+
+ if (TUTORIAL) {
+ dnext;
+ if (.@showheader) {
+ mesf(".:: %s ::.", .@header$);
+ mesc getarg(0), .@tcol;
+ } else {
+ mesc .@header$+": "+getarg(0), .@tcol;
+ }
+ }
+ return;
+}
+
+// Function to show narrator text. Accepts string args.
+// If first arg is a number N, then it represents bit flags.
+// Bit flags :
+// 0x1 -- blank line at beginning
+// 0x2 -- blank line at the end
+// 0x4 -- use last "next;"
+// 0x8 -- don't use first "mesn;"
+function script narrator {
+ .@start = 0;
+ .@argc = getargcount();
+ .@flags = 0;
+
+ if (.@argc > 1 && !isstr(getarg(0)))
+ {
+ .@start = 1;
+ .@flags = getarg(0);
+ }
+
+ if (.@flags & 0x1)
+ mes "";
+
+ if (!(.@flags & 0x8))
+ mesn l("Narrator");
+
+ for (.@i = .@start; .@i < .@argc; .@i++)
+ {
+ mes col(getarg(.@i), 9);
+ if (.@i < .@argc - 1)
+ next;
+ }
+
+ if (.@flags & 0x4)
+ next;
+ else if (.@flags & 0x2)
+ mes "";
+
+ return;
+}
+
+// Function to show NPC speech. Accepts string args.
+// If first arg is a number N, then it represents bit flags.
+// Bit flags :
+// 0x1 -- blank line at beginning
+// 0x2 -- blank line at the end
+// 0x4 -- use last "next;"
+// 0x8 -- don't use first "mesn;"
+function script speech {
+ .@start = 0;
+ .@argc = getargcount();
+ .@flags = 0;
+
+ if (.@argc > 1 && !isstr(getarg(0)))
+ {
+ .@start = 1;
+ .@flags = getarg(0);
+ }
+
+ if (.@flags & 0x1)
+ mes "";
+
+ if (!(.@flags & 0x8))
+ mesn;
+
+ for (.@i = .@start; .@i < .@argc; .@i++)
+ {
+ mesq getarg(.@i);
+
+ if (.@i < .@argc - 1)
+ next;
+ }
+
+ if (.@flags & 0x4)
+ next;
+ else if (.@flags & 0x2)
+ mes "";
+
+ return;
+}
+
+// Show debug message if .debug variable of NPC is set to 1
+function script npcdebug {
+ if (getvariableofnpc(.debug, strnpcinfo(3)))
+ debugmes strnpcinfo(3) + ": " + getarg(0);
+ return;
+}
+
+function script askyesno {
+ return select(menuaction(l("Yes")),
+ menuaction(l("No")));
+}
+
+// Argument:
+// 0 Quest variable
+// 1 Current value
+// 2 Next value
+function script compareandsetq {
+ if (getq(getarg(0)) == getarg(1))
+ {
+ setq getarg(0), getarg(2);
+ return true;
+ }
+ return false;
+}
+
+// Use a delay to prevent spams from NPC that display text without the
+// use of (a) close/next function(s).
+// Argument:
+// 0 Text to display
+// 1 Lock delay (default = 1)
+// 2 Message function: (default = 0)
+// 0 = npctalk3
+// 1 = npctalk
+// 2 = message
+// TODO: Use temp player var, because NPC var affect other players
+function script npctalkonce {
+ // lock mechanism
+ switch (getarg(2, 0))
+ {
+ case 1:
+ if (gettimetick(2) <= getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE)))
+ return false;
+ set(getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE)), gettimetick(2) + getarg(1, 1));
+ break;
+ default:
+ if (gettimetick(2) <= @NPC_TALK_LOCK[getnpcid()])
+ return false;
+ @NPC_TALK_LOCK[getnpcid()] = gettimetick(2) + getarg(1, 1);
+ }
+
+ // talk mechanism
+ switch (getarg(2, 0))
+ {
+ case 0: npctalk3(getarg(0)); break;
+ case 1: npctalk(getarg(0)); break;
+ case 2: message(strcharinfo(0), getarg(0));
+ }
+
+ return true;
+}
+
+// Randomizer functions
+/////////////////////////////////////////////
+
+// pseudo-fix randomness
+// rand2( min, max )
+function script rand2 {
+ if (getargcount() == 2) {
+ .@min=getarg(0)*100;
+ .@max=getarg(1)*100+99;
+ } else {
+ .@min=0;
+ .@max=getarg(0)*100-1;
+ }
+ return rand(.@min, .@max)/100;
+}
+
+// returns one argument randomly
+// any( <arg>{, ...<arg>} )
+function script any {
+ return getarg(rand2(getargcount()));
+}
+
+// returns any member of the array
+// any_of( <array> )
+function script any_of {
+ return getelementofarray(getarg(0), getarrayindex(getarg(0)) + rand2(getarraysize(getarg(0)) - getarrayindex(getarg(0))));
+}
+
+function script die {
+ if ($HARDCORE) {
+ @grace=true;
+ percentheal -100, -100;
+ //setparam(Hp, 1);
+ //warp "000-1", 22, 22;
+ //end; // MUST be end; to mimic official behavior
+ } else {
+ percentheal -100, -100;
+ }
+ return;
+}
+
+// Returns if a map is on PVP Mode or Not
+// ispvpmap( {mapid} )
+function script ispvpmap {
+ .@mapa$=getarg(0, getmapname());
+ return (getmapflag(.@mapa$, mf_pvp) || getmapflag(.@mapa$, mf_pvp_noparty) || getmapflag(.@mapa$, mf_pvpnoguild));
+}
+
+// TMW2 Custom Functions
+/////////////////////////////////////////////
+
+// Function meant to be used by Main Storyline Quest
+// msObjective ( condition , message )
+function script msObjective {
+ if (getarg(0))
+ mesc getarg(1), 2;
+ else
+ mesc getarg(1), 9;
+ return;
+}
+
+function script getmap {
+ if (getmapxy(.@mapName$, .@xpos, .@ypos, getarg(0,0)) != 0)
+ return false;
+ // TODO: Maybe use getmapname() instead of getmapxy?
+ return .@mapName$;
+}
+
+// isin( map, x1, y1, {[x2, y2][radius]} )
+function script isin {
+ if (getmapxy(.@mapName$, .@xpos, .@ypos, 0) != 0)
+ return false;
+ if (.@mapName$ != getarg(0))
+ return false;
+
+ if (getarg(4,-1) < 0) {
+ // Radius Based
+ if (.@xpos >= getarg(1)-getarg(3) && .@xpos <= getarg(1)+getarg(3) && .@ypos >= getarg(2)-getarg(3) && .@ypos <= getarg(2)+getarg(3))
+ return true;
+ } else {
+ // Coordinate based
+ if (.@xpos >= getarg(1) && .@xpos <= getarg(3) && .@ypos >= getarg(2) && .@ypos <= getarg(4))
+ return true;
+ }
+ return false;
+}
+
+// Clear output of getinventorylist()
+// delinventorylist()
+function script delinventorylist {
+ deletearray @inventorylist_id;
+ deletearray @inventorylist_amount;
+ deletearray @inventorylist_equip;
+ deletearray @inventorylist_refine;
+ deletearray @inventorylist_identify;
+ deletearray @inventorylist_attribute;
+ deletearray @inventorylist_card1;
+ deletearray @inventorylist_card2;
+ deletearray @inventorylist_card3;
+ deletearray @inventorylist_card4;
+ deletearray @inventorylist_expire;
+ deletearray @inventorylist_bound;
+ @inventorylist_count=0;
+ return;
+}
+
+// Get some acc id, even if offline
+// ( Name )
+function script gf_accid {
+ .@nb = query_sql("SELECT `account_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value);
+ return .@value[0];
+}
+
+// Get some char id, even if offline
+// ( Name )
+function script gf_charnameid {
+ .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value);
+ return .@value[0];
+}
+
+// Get some char name from char ID, even if offline
+// ( Name )
+function script gf_charname {
+ .@nb = query_sql("SELECT `name` FROM `char` WHERE `char_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$);
+ return .@value$[0];
+}
+
+// Get some char ID from account ID, even if offline
+// ( Name )
+function script gf_charid {
+ .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `account_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$);
+ return .@value$[0];
+}
+
+// Request pincode and validate it. Use any non-4-digits code to cancel. Failure will dc you.
+// Returns 1 if pin check is OK.
+function script validatepin {
+ if (#FIRST_TIME < 2) {
+ mesc l("ERROR: You must set a PinCode to make use of this function."), 1;
+ return 0;
+ }
+ mesc l("Please insert your pincode."), 1;
+ mesc l("WARNING: If you insert wrong pincode, you'll be disconnected.");
+ mesc l("Use @@ to cancel.", "##B-1##b");
+ mes "";
+ input .@pin$;
+ if (getstrlen(.@pin$) != 4)
+ return 0;
+ query_sql("SELECT userid FROM `login` WHERE account_id="+escape_sql(getcharid(3))+" AND pincode='"+escape_sql(.@pin$)+"' LIMIT 2", .@value$);
+ if (getarraysize(.@value$) != 1) {
+ atcommand "@kick "+strcharinfo(0);
+ return 0;
+ }
+ // Enforce some cooldown to prevent an eventual exploit/abuse
+ sleep2(rand2(150, 400));
+ mesc l("Thanks, @@. We just wanted to be sure it was you.", .@value$[0]);
+ mes "";
+ return true;
+}
+
+// Something went wrong and must be reported (named after raise Exception in python)
+// Exception( BugID, {Flags{, Return Code}} )
+function script Exception {
+ // Fill variable
+ .@msg$=getarg(0);
+ .@gf=getarg(1,RB_DEFAULT);
+
+ if (.@gf & RB_DISPBOTTOM)
+ dispbottom("ERROR: "+.@msg$);
+
+ if (.@gf & RB_DEBUGMES)
+ debugmes("[Warning] "+.@msg$);
+
+ if (.@gf & RB_SPEECH)
+ mesc("ERROR, REPORT ME! "+.@msg$, 1);
+
+ if (.@gf & RB_IRCBROADCAST)
+ channelmes("#world", "Error in script: "+.@msg$);
+
+ if (.@gf & RB_GLOBALANNOUNCE)
+ announce("Error in script: "+.@msg$, bc_all);
+
+ if (.@gf & RB_PLEASEREPORT) {
+ if (.@gf & RB_DISPBOTTOM)
+ dispbottom("Please take a screenshot and report this bug, explaining how it happened.");
+
+ if (.@gf & RB_SPEECH)
+ mesc("Please take a screenshot and report this bug, explaining how it happened."), 1;
+ }
+
+ if (.@gf & RB_ISFATAL) {
+ if (.@gf & RB_DISPBOTTOM)
+ dispbottom("This error is fatal, we stop execution.");
+
+ if (.@gf & RB_DEBUGMES)
+ debugmes("[Error] The error is fatal.");
+
+ if (.@gf & RB_SPEECH) {
+ mesc l("This error is fatal, we stop execution."), 1;
+ close;
+ }
+ end;
+ }
+
+ return getarg(2, 0);
+}
+
+// mescordialog(text, color, {dialog=1})
+function script mescordialog {
+ if (getarg(2, true))
+ mesc getarg(0), getarg(1);
+ else
+ dispbottom col(getarg(0), getarg(1));
+ return;
+}
+
+// Delayed healing. Takes 3~5 seconds. Variates with Vit up to +100%.
+// The vit can have an additional 20% bonus as well.
+function script itheal {
+ .@bas=getarg(0);
+ .@vit=readbattleparam(getcharid(3), UDT_VIT);
+ .@vit=cap_value(.@vit-1, 0, 100);
+ if (getargcount() > 2)
+ .@tim=getarg(3);
+ else
+ .@tim=rand2(3,5);
+ .@min=.@bas*(100+.@vit)/100;
+ .@max=.@bas*(100+.@vit*120/100)/100;
+ // Now divide the HP values by the time
+ .@min=max(1, .@min/.@tim);
+ .@max=max(1, .@max/.@tim);
+ callfunc("SC_Bonus", .@tim, SC_S_LIFEPOTION, .@min, .@max);
+ if (getarg(1,0) > 0)
+ heal 0, getarg(1, 0);
+ return;
+}
+
+// sqldate({day variation, month variation})
+function script sqldate {
+ .@d=gettime(GETTIME_DAYOFMONTH)+getarg(0, 0);
+ .@m=gettime(GETTIME_MONTH)+getarg(1, 0);
+ .@y=gettime(GETTIME_YEAR);
+ // Overflow prevention
+ if (.@d <= 0) {
+ .@d=1;
+ }
+ while (.@m > 12) {
+ .@y+=1;
+ .@m-=12;
+ }
+ while (.@m < 1) {
+ .@y-=1;
+ .@m+=12;
+ }
+ .@strdate$=sprintf("%04d-%02d-%02d %02d:%02d:%02d", .@y, .@m, .@d, gettime(GETTIME_HOUR), gettime(GETTIME_MINUTE), gettime(GETTIME_SECOND));
+ return .@strdate$;
+}
+
+// Makes a monster aggro
+// set_aggro( monster{, mode=MD_AGGRESSIVE} )
+function script set_aggro {
+ .@m=getarg(0);
+ .@x=getarg(1, MD_AGGRESSIVE);
+ .@op=getunitdata(.@m, UDT_MODE);
+ .@op=.@op|.@x;
+ setunitdata(.@m, UDT_MODE, .@op);
+ return;
+}
+
+// Special function which makes a date as a number
+// numdate( - )
+function script numdate {
+ .@strdate$=sprintf("%04d%02d%02d", gettime(GETTIME_YEAR), gettime(GETTIME_MONTH), gettime(GETTIME_DAYOFMONTH));
+ // Debug payload
+ if ($@OVERRIDE_NUMDATE)
+ return $@OVERRIDE_NUMDATE;
+ return atoi(.@strdate$);
+}
+
+// json_encode( {varname, varvalue}, {varname 2, varvalue 2}... )
+// returns string
+function script json_encode {
+ if (getargcount() < 2 || getargcount() % 2 != 0)
+ return Exception("json_encode arguments must be paired");
+
+ .@json$="{";
+ .@tab=true;
+
+ // For arguments
+ for (.@i=0;.@i < getargcount(); .@i++) {
+ // Close previous item
+ if (.@tab)
+ .@tab=false;
+ else
+ .@json$+=",";
+
+ // Input variable name
+ .@json$+="\""+getarg(.@i)+"\": ";
+
+ // Input variable value
+ if (isstr(getarg(.@i+1)))
+ .@json$+="\""+getarg(.@i+1)+"\"";
+ else
+ .@json$+=getarg(.@i+1);
+
+ // Advance
+ .@i++;
+ }
+
+ // Close the JSON
+ .@json$+="}";
+ return .@json$;
+}
+
+
+// api_send( code, data )
+// sends to API
+function script api_send {
+ .@cde=getarg(0);
+ .@fm$=escape_sql(getarg(1));
+ query_sql("INSERT INTO `api_export` (`type`, `data`) VALUES ('"+.@cde+"', \""+.@fm$+"\")");
+ return;
+}
+
+// Linking functions
+/////////////////////////////////////////////
+function script getquestlink {
+ return "[@@q" + getarg(0) + "|@@]";
+}
+
+function script getmonsterlink {
+ return "[@@m" + getarg(0) + "|@@]";
+}
+
+function script getpetlink {
+ return "[@@p" + getarg(0) + "|@@]";
+}
+
+function script getmercenarylink {
+ return "[@@M" + getarg(0) + "|@@]";
+}
+
+function script gethomunculuslink {
+ return "[@@h" + getarg(0) + "|@@]";
+}
+
+// Legacy functions
+/////////////////////////////////////////////
+function script mapexit {
+ debugmes "TRYING TO MAPEXIT IS DEPRECATED";
+ return;
+}
+
+function script destroy {
+ disablenpc strnpcinfo(0);
+ return;
+}
+
+function script npcaction {
+ debugmes "Deprecated unitaction (did you mean npcsit; or whatever?)";
+ .@a=getarg(0, 0);
+ if (.@a == 9)
+ clear;
+ return;
+}
+
+function script gmlog {
+ logmes(getarg(0), LOGMES_ATCOMMAND);
+ return;
+}
+
+function script getx {
+ getmapxy(.@m$, .@x, .@y, 0);
+ return .@x;
+}
+
+function script gety {
+ getmapxy(.@m$, .@x, .@y, 0);
+ return .@y;
+}
+
+function script getnpcx {
+ return .x;
+}
+
+function script getnpcy {
+ return .y;
+}
+
+function script title {
+ setnpcdialogtitle getarg(0);
+ return;
+}
+
+function script camera {
+ if (getarg(0, "") != "")
+ setcamnpc getarg(0);
+ else
+ restorecam;
+ return;
+}
+
+function script mapmask {
+ sendmapmask getarg(0);
+ return;
+}
+
+function script getmask {
+ return 1; //getmapmask(getmapname()); // TODO: Return original map masks
+}
+
+// isat( map, x, y )
+function script isat {
+ return isin(getarg(0), getarg(1), getarg(2), 0);
+}
+
+function script if_then_else {
+ return (getarg(0) ? getarg(1) : getarg(2));
+}
+
+function script misceffect {
+ // or SELF + something
+ return specialeffect(getarg(0), AREA, getarg(1, strnpcinfo(0)));
+}
+
+function script fakenpcname {
+ if (getargcount() > 2)
+ setnpcdisplay(getarg(0), getarg(1), getarg(2));
+ else
+ setnpcdisplay(getarg(0), getarg(1));
+ return;
+}
+
+function script npcwarp {
+ if (getargcount() > 2)
+ .@id=getnpcid(getarg(3));
+ else
+ .@id=getnpcid();
+
+ getmapxy(.@m$, .@x, .@y, UNITTYPE_NPC, strnpcinfo(0, "", .@id));
+ unitwarp(.@id, .@m$, getarg(0), getarg(1));
+ return;
+}
+
+function script get {
+ return getvariableofnpc(getarg(0), getarg(1));
+}
+
+function script sc_check {
+ return getstatus(getarg(0), getarg(1, 0));
+}
+
+function script wgm {
+ charcommand("@request "+getarg(0));
+ return;
+}
+
+function script registercmd {
+ // Remove "@" from command start
+ .@cmd$=getarg(1);
+ if (charat(.@cmd$, 0) == "@")
+ delchar(.@cmd$, 0);
+ bindatcmd getarg(0), .@cmd$, getarg(2, 0);
+ return;
+}
+
+function script iscollision {
+ return checknpccell(getarg(0), getarg(1), getarg(2), cell_chkpass);
+}
+
+function script readparam2 {
+ return readbattleparam(getcharid(3), getarg(0));
+}
+
+function script updateskill {
+ skill getarg(0), getarg(1), 0;
+ return;
+}
+
+function script learnskill {
+ if (getskilllv(getarg(0)) < getarg(1, 1))
+ skill getarg(0), getarg(1, 1), 0;
+ return;
+}
+
diff --git a/npc/functions/math.txt b/npc/functions/math.txt
new file mode 100644
index 00000000..941dfa90
--- /dev/null
+++ b/npc/functions/math.txt
@@ -0,0 +1,105 @@
+// Evol functions.
+// Authors:
+// 4144
+// Reid
+// Description:
+// Math functions
+
+
+// abs(<int>)
+// returns the absolute value of the passed integer
+
+function script abs {
+ .@n = getarg(0);
+ return .@n >= 0 ? .@n : -.@n;
+}
+
+
+
+// lognbaselvl({<multiplicator>{, <min value>}})
+// returns BaseLevel * logn (BaseLevel * alpha).
+
+function script lognbaselvl {
+ .@alpha = getarg(0, 1);
+ .@min = getarg(1, 1);
+ .@ret = 0;
+ .@pc_level = BaseLevel * .@alpha;
+
+ while (.@pc_level >>= 1)
+ {
+ ++.@ret;
+ }
+ .@ret *= BaseLevel;
+
+ if (.@ret <= .@min)
+ {
+ .@ret = .@min;
+ }
+
+ return .@ret;
+}
+
+// log2(<int>)
+// returns the log base 2 of the passed integer, up to 20 (2**20=1.048.576) (round down always)
+
+function script log2 {
+ .@v=abs(getarg(0));
+ .@ok=0;
+ .@i=0;
+ if (.@v < 1)
+ return -1;
+
+ freeloop(true);
+ while (!.@ok) {
+ // exact match
+ if (2**.@i == .@v) {
+ .@ok=1;
+ // inexact match, or limit exceeded
+ } else if (2**.@i >= .@v || .@i > 20) {
+ .@ok=1;
+ .@i-=1; // round down
+ // not yet
+ } else {
+ .@i+=1;
+ }
+ }
+ freeloop(false);
+
+ return .@i;
+}
+
+
+// result is: lower < target <= higher
+// is_between ( lower, higher, target)
+function script is_between {
+ .@val=getarg(2);
+ .@min=getarg(0);
+ .@max=getarg(1);
+ return (.@min < .@val && .@val <= .@max);
+}
+
+
+// forces the equation: lower <= target <= higher.
+// Note it still works if higher and target values are swapped.
+// limit ( lower, target, higher)
+function script limit {
+ return max(getarg(0), min(getarg(1), getarg(2)));
+}
+
+
+// result is the ponderate average.
+// ponderate_avg ( arg1, sub1, arg2, sub2)
+function script ponderate_avg {
+ .@a1=getarg(0);
+ .@s1=getarg(1);
+ .@a2=getarg(2);
+ .@s2=getarg(3);
+
+ .@h1=.@a1*.@s1;
+ .@h2=.@a2*.@s2;
+ .@dd=.@s1+.@s2;
+
+ return (.@h1+.@h2)/.@dd;
+}
+
+
diff --git a/npc/functions/miriam.txt b/npc/functions/miriam.txt
new file mode 100644
index 00000000..e59b736b
--- /dev/null
+++ b/npc/functions/miriam.txt
@@ -0,0 +1,17 @@
+function script MiriamGoal {
+ if (QUEST_MIRIAM_run > 0)
+ QUEST_MIRIAM_run = 0 - (gettimetick(2) - QUEST_MIRIAM_run);
+ return;
+}
+
+function script MiriamExpire {
+ if (QUEST_MIRIAM_run > 0)
+ set QUEST_MIRIAM_run, 0 - 2; // force failute (expired)
+ return;
+}
+
+function script MiriamCheat {
+ if (QUEST_MIRIAM_run > 0)
+ set QUEST_MIRIAM_run, 0 - 1; // force failure (cheating)
+ return;
+}
diff --git a/npc/functions/mob_points.txt b/npc/functions/mob_points.txt
new file mode 100644
index 00000000..f4cbedf0
--- /dev/null
+++ b/npc/functions/mob_points.txt
@@ -0,0 +1,83 @@
+// TMW2 Scripts
+// Author: Crazyfefe
+// Jesusalva
+// Desc: Mob Points for Aidan & Ishi. You will gain MONSTER-LEVEL mob points.
+
+// fix_mobkill(mobID) → Manual fix for scripted mobs
+function script fix_mobkill {
+ killedrid=getarg(0);
+ doevent "#GlobalHandler::OnNPCKillEvent";
+ return;
+}
+
+function script MobPoints {
+ $MONSTERS_KILLED+=1;
+ MONSTERS_KILLED+=1;
+ @mobId=killedrid;
+
+ if (MPQUEST) {
+ .@moblv=strmobinfo(3,killedrid);
+
+ // You get MobLv + 20% as MobPoints.
+ // So a level 100 monster gives you 120 MobPt. Slimes gives no bonus.
+ if (compare("slime", strtolower(strmobinfo(1, killedrid))))
+ .@addval=.@moblv;
+ else
+ .@addval=.@moblv*12/10;
+
+ // Penalty/Bonus
+ .@base=(.@moblv-BaseLevel);
+
+ if (BaseLevel < .@moblv) {
+ // Target is stronger, +3% per monster level, capped at +75%
+ .@addval = .@addval * limit(100, 100+(.@base*3), 175) / 100;
+ } else if (BaseLevel > .@moblv) {
+ // Target is weaker, -1% per monster level, capped at -50%
+ .@addval = .@addval * limit(50, 101+.@base, 100) / 100;
+ }
+
+ // Sanitization
+ .@addval=max(0, .@addval);
+
+ // Grant you the Monster Points
+ Mobpt+=.@addval;
+ }
+
+ ValonCount();
+ if (((getq(CandorQuest_Valon) >= 2) &&
+ (getq(CandorQuest_Valon) < 6)) &&
+ (@mobId == $@ValonMob[@valon_mob]))
+ AddValonCntMask();
+
+
+ // Attitude adjustment for Sagatha
+ switch (@mobId) {
+ case 1018:
+ case 1020:
+ case 1027:
+ case 1094:
+ case 1112:
+ case 1113:
+ QuestSagathaAnnoy(3); break;
+ case 1028:
+ QuestSagathaAnnoy(4); break;
+ case 1038:
+ QuestSagathaAnnoy(2); break;
+ case 1003:
+ case 1004:
+ case 1009:
+ case 1057:
+ case 1104:
+ case 1105:
+ case 1106:
+ case 1107:
+ QuestSagathaHappy(1); break;
+ }
+
+ if (QL_CELESTIA >= 5 && QL_CELESTIA < 205 && @mobId == Yeti) {
+ QL_CELESTIA = QL_CELESTIA + 1;
+ if (QL_CELESTIA == 205)
+ message strcharinfo(0), "Yeti : ##3This should be enough yetis killed to please Celestia.";
+ }
+ return;
+}
diff --git a/npc/functions/motd.txt b/npc/functions/motd.txt
new file mode 100644
index 00000000..5ff58480
--- /dev/null
+++ b/npc/functions/motd.txt
@@ -0,0 +1,10 @@
+function script MOTD {
+ setarray $@MOTD$,
+
+ "Welcome to The Mana World! (running on tmwAthena)",
+ "[@@http://ow.ly/MCesp|Website & Wiki@@] [@@http://ow.ly/MCeBR|Vote For GMs@@] [@@http://ow.ly/MCehc|Bug Reports@@] [@@http://ow.ly/MCe5W|Live Support@@]",
+ "Like us on [@@http://ow.ly/MCdZW|Facebook@@] [@@http://ow.ly/MCdTt|G+@@] [@@http://ow.ly/MCdJR|Youtube@@] [@@http://ow.ly/MCePp|Twitter@@]",
+ "You can report abuse by typing in chat: @wgm Player XYZ is abusing me";
+
+ return;
+}
diff --git a/npc/functions/motdconfig.txt b/npc/functions/motdconfig.txt
new file mode 100644
index 00000000..dcb54dcc
--- /dev/null
+++ b/npc/functions/motdconfig.txt
@@ -0,0 +1,38 @@
+function script MOTDConfig {
+ mes "[MOTD]";
+ mes "lines:";
+ mes "---";
+ @line = 0;
+ callsub S_Lines;
+ mes "---";
+ mes "Enabled: " + !($@MOTD_Disabled);
+ next;
+ menu
+ "toggle|Toggle MOTD", L_Toggle;
+
+L_Toggle:
+ gmlog strcharinfo(0) + " enabled or disabled the MOTD.";
+ $@MOTD_Disabled = !$@MOTD_Disabled;
+ close2;
+ return;
+
+S_Lines:
+ mes @line + ": "+ $@MOTD$[@line];
+ @line = @line + 1;
+ if(@line != getarraysize($@MOTD$)) goto S_Lines;
+ return;
+}
+
+function script DisplayMOTD {
+ if($@MOTD_Disabled || $@MOTD$[0] == "") goto L_Return;
+ goto L_MOTD;
+
+L_MOTD:
+ message strcharinfo(0), "Server : " + $@MOTD$[@motd_index];
+ @motd_index = @motd_index + 1;
+ if($@MOTD$[@motd_index] == "") goto L_Return;
+ goto L_MOTD;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt
new file mode 100644
index 00000000..612ab036
--- /dev/null
+++ b/npc/functions/npcmove.txt
@@ -0,0 +1,142 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Moving npc utility functions
+// Variables:
+// none
+
+function script initpath {
+ deletearray getvariableofnpc(.movepathcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathy, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathx, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 3)
+ {
+ set getvariableofnpc(.movepathcmd$[.@cnt], strnpcinfo(3)), getarg(.@f);
+ set getvariableofnpc(.movepathx[.@cnt], strnpcinfo(3)), getarg(.@f + 1);
+ set getvariableofnpc(.movepathy[.@cnt], strnpcinfo(3)), getarg(.@f + 2);
+ .@cnt ++;
+ }
+ //debugmes "array size: " + str(getarraysize(getvariableofnpc(.movepath, strnpcinfo(3))));
+ return;
+}
+
+function script domoveaction {
+ //debugmes "domoveaction: " + str(getvariableofnpc(.movepos, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return;
+ //debugmes "walking";
+ .@cmd$ = getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+ //debugmes "cmd: " + .@cmd$;
+
+ if (.@cmd$ == "move")
+ {
+ npcwalkto getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "dir")
+ {
+ setnpcdir getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "emote")
+ {
+ unitemote getnpcid(), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "class")
+ {
+ .class = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "warp")
+ {
+ movenpc strnpcinfo(3), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "goto")
+ {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 0;
+ }
+ else if (.@cmd$ == "rmove")
+ {
+ getmapxy(.@mapName$, .@x, .@y, 1);
+ npcwalkto .@x + getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), .@y + getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "speed")
+ {
+ .speed = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$ == "stand")
+ {
+ npcstand;
+ }
+ return 1;
+}
+
+function script movetonextpos {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait --;
+ //debugmes "wait";
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+ .@true = 1;
+ while (.@true)
+ {
+ .@true = 0;
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ //debugmes "movetonextpos: " + str(.@pos);
+ .@res = domoveaction(.@pos);
+ if (.@res == 1 || .@res == 2)
+ {
+ .@pos++;
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))))
+ .@pos = 0;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ if (.@res == 0 || .@res == 2)
+ {
+ .@true = 1;
+ }
+ }
+ return;
+}
+
+function script initialmove {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), 0;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ movetonextpos;
+ return;
+}
+
+function script getmovecmd {
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return "";
+ return getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+}
+
+function script domovestep {
+ if (isunitwalking())
+ {
+ initnpctimer;
+ end;
+ }
+ movetonextpos;
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt
new file mode 100644
index 00000000..0877b748
--- /dev/null
+++ b/npc/functions/npcmovegraph.txt
@@ -0,0 +1,489 @@
+// Evol functions.
+// Author:
+// Travolta
+// Description:
+// Moving npc utility functions (graph-based)
+// Variables:
+// none
+
+function script initmovegraph {
+ deletearray getvariableofnpc(.movegraphcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphlabels$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphweight, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphflags, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x2, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y2, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ set getvariableofnpc(.movegraphlabels$[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_x1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ if (!isstr(getarg(.@f, "label")))
+ {
+ set getvariableofnpc(.movepos_x2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ }
+ .@cnt ++;
+ }
+ return;
+}
+
+function script findmovegraphlabel {
+ if (!getargcount())
+ {
+ debugmes "findmovegraphlabel: no argument";
+ return -1;
+ }
+ if (!isstr(getarg(0)))
+ {
+ debugmes "findmovegraphlabel: need string argument";
+ return -1;
+ }
+
+ .@arg$ = getarg(0);
+ for (.@i = 0; .@i < getarraysize(getvariableofnpc(.movegraphlabels$, strnpcinfo(3))); .@i++)
+ {
+ if (getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) == .@arg$)
+ return .@i;
+ }
+
+ npcdebug "findmovegraphlabel: label not found: " + getarg(0);
+ return -1;
+}
+
+/* setmovegraphcmd(fromPositionLabel,toPositionLabel[,moveChanceWeight[,moveFlags]],postCommand, ...);
+ * This function manipulates NPC moving graph. Before calling it, make sure
+ * `initmovegraph' was called. The function accepts 3-5 parameters (many times):
+ * fromPositionLabel, toPositionLabel -- starting and ending position of NPC move
+ * moveChanceWeight -- positive integer, represents the chance of moving in given direction. (optional)
+ * moveFlags -- if .mg_flags & moveFlags != 0, move is possible. (optional)
+ * postCommand -- either "moveon" (start moving to next location straight after arriving from
+ * fromPositionLabel to toPositionLabel) or a semicolon-separated set of commands
+ * ("wait 3", "emote 5" etc, see `execmovecmd') that will be executed after arrival.
+ * The commands don't have to end with ";moveon", it's executed in the end by default.
+ */
+function script setmovegraphcmd {
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ .@from = findmovegraphlabel(getarg(.@f++));
+ .@to = findmovegraphlabel(getarg(.@f++));
+ .@weight = 1;
+ if (!isstr(getarg(.@f)))
+ .@weight = getarg(.@f++);
+ .@flags = 0xffff;
+ if (!isstr(getarg(.@f)))
+ .@flags = getarg(.@f++);
+ .@cmd$ = getarg(.@f++);
+ .@index = .@from * .@size + .@to; // emulation of 2d array
+ set getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)), .@cmd$;
+ set getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)), .@weight;
+ set getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)), .@flags;
+ }
+ return;
+}
+
+function script execmovecmd {
+
+ explode(.@cmd$, getarg(0), " ");
+
+ if (.@cmd$[0] == "moveon")
+ {
+ return 0;
+ }
+ else if (.@cmd$[0] == "dir")
+ {
+ .dir = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$[0] == "stand")
+ {
+ npcstand;
+ }
+ else if (.@cmd$[0] == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), atoi(.@cmd$[1]);
+ return 1;
+ }
+ else if (.@cmd$[0] == "emote")
+ {
+ unitemote getnpcid(), atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "class")
+ {
+ .class = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "warp")
+ {
+ .@pos = -1;
+ .@map$ = "";
+ .@pos_idx = 1;
+ if (getarraysize(.@cmd$) == 3)
+ {
+ .@map$ = .@cmd$[1];
+ .@pos_idx = 2;
+ }
+ .@pos = findmovegraphlabel(.@cmd$[.@pos_idx]);
+ if (.@pos != -1)
+ {
+ .@x = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ if (getstrlen(.@map$) > 0)
+ unitwarp getnpcid(), .@map$, .@x, .@y;
+ else
+ movenpc strnpcinfo(3), .@x, .@y;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ else
+ {
+ debugmes "execmovecmd: unknown WARP destination label: " + .@cmd$[1];
+ }
+ }
+ else if (.@cmd$[0] == "call")
+ {
+ switch (getarraysize(.@cmd$))
+ {
+ case 1:
+ debugmes "execmovecmd: CALL command needs some parameters";
+ return 0;
+ case 2:
+ return callfunc(.@cmd$[1]);
+ break;
+ case 3:
+ return callfunc(.@cmd$[1], .@cmd$[2]);
+ case 4:
+ default:
+ return callfunc(.@cmd$[1], .@cmd$[2], .@cmd$[3]);
+ }
+ }
+ else if (.@cmd$[0] == "speed")
+ {
+ .speed = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "say")
+ {
+ deletearray .@cmd$[0], 1;
+ .@msg$=implode(.@cmd$, " ");
+ if (.@msg$ != "" && .@msg$ != " ")
+ npctalk .@msg$;
+ else
+ debugmes "Invalid message passed to execmovecmd/npctalk";
+ }
+ else if (.@cmd$[0] == "debugmes")
+ {
+ deletearray .@cmd$[0], 1;
+ debugmes implode(.@cmd$, " ");
+ }
+ else if (.@cmd$[0] == "flags")
+ {
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), axtoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "flags_0")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags &= ~axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else if (.@cmd$[0] == "flags_1")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags |= axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else
+ {
+ debugmes "Unknown move graph cmd: " + .@cmd$[0];
+ }
+ return 0;
+}
+
+function script getnextmovecmd {
+ .@cmds$ = getvariableofnpc(.nextcmd$, strnpcinfo(3));
+ .@firstCmd$ = .@cmds$;
+ .@restCmd$ = "moveon";
+ .@index = strpos(.@cmds$, ";");
+ if (.@index >= 0)
+ {
+ .@firstCmd$ = substr(.@cmds$, 0, .@index - 1);
+ .@restCmd$ = substr(.@cmds$, .@index + 1, getstrlen(.@cmds$) - 1);
+ }
+ // npcdebug "firstCmd = " + .@firstCmd$ + " restCmd = " + .@restCmd$;
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@restCmd$;
+ return strip(.@firstCmd$);
+}
+
+// getrandompoint(x1,y1,x2,y2)
+// -- Get a random walkable point within a map rectangle
+// x1, y1 -- top-left corner of rectangle
+// x2, y2 -- bottom-right corner of rectangle
+// Returns 0 on success and -1 on error;
+// Since we cannot return multiple values, the random
+// coordinates are stored in NPC variables .move__rand_x, .move__rand_y
+function script getrandompoint {
+ if (getargcount() < 4)
+ {
+ debugmes "error: getrandompoint(x1, y1, x2, y2) takes 4 arguments";
+ return -1;
+ }
+
+ .@max_pokes = 10;
+ .@x1 = getarg(0);
+ .@y1 = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+ .@rx = -1; .@ry = -1;
+
+ getmapxy(.@map$, .@cx, .@cy, 1); // npc location
+
+ // let's try max_pokes random cells
+ for (.@poke = 0; .@poke < .@max_pokes; .@poke++)
+ {
+ .@rx = rand(.@x1, .@x2);
+ .@ry = rand(.@y1, .@y2);
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ }
+
+ // we check each cell from random middle point to the end
+ for (;.@rx <= .@x2; .@rx++)
+ {
+ for (;.@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ .@ry = .@y1;
+ }
+
+ // we check the rectangle from beginning to end
+ for (.@rx = .@x1; .@rx <= .@x2; .@rx++)
+ for (.@ry = .@y1; .@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+
+ // finally, if we don't find anything
+ debugmes "error: getrandompoint: cannot find walkable cell in rectangle [(" + .@x1 + "," + .@y1 + ") , (" + .@x2 + "," + .@y2 + ")]";
+ return -1;
+
+L_Found:
+ set getvariableofnpc(.move__rand_x, strnpcinfo(3)), .@rx;
+ set getvariableofnpc(.move__rand_y, strnpcinfo(3)), .@ry;
+ return 0;
+}
+
+// wrapper function for npcwalkto. It can accept 4 parameters.
+// If #3 and #4 params are set, the walkto location is chosen
+// from rectangle (x1,y1,x2,y2).
+// It sets the npc variables .move_target_x, .move_target_y
+// that are used to resume NPC walking
+// Returns 1 if walking is possible, 0 otherwise;
+function script mg_npcwalkto {
+ if (getargcount() < 2)
+ {
+ debugmes "usage: mg_npcwalkto(x1,y1[,x2,y2])";
+ return -1;
+ }
+
+ .@x = getarg(0);
+ .@y = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+
+ if (getargcount() >= 4 && .@x2 > 0 && .@y2 > 0)
+ if (!getrandompoint(.@x, .@y, .@x2, .@y2))
+ {
+ .@x = getvariableofnpc(.move__rand_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move__rand_y, strnpcinfo(3));
+ }
+ else
+ return 0;
+
+ if (npcwalkto(.@x, .@y))
+ {
+ set getvariableofnpc(.move_target_x, strnpcinfo(3)), .@x;
+ set getvariableofnpc(.move_target_y, strnpcinfo(3)), .@y;
+ return 1;
+ }
+ return 0;
+}
+
+function script movetonextpoint {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait--;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+
+ .@nextcmd$ = "";
+ while (.@nextcmd$ != "moveon")
+ {
+ .@nextcmd$ = getnextmovecmd();
+ npcdebug " " + .@nextcmd$;
+ if (execmovecmd(.@nextcmd$))
+ return;
+ }
+
+ // choose a random path from all possible paths
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ .@curr_flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@cur = 0;
+ .@weight_sum = 0;
+ // .@dbg$ = getvariableofnpc(.movegraphlabels$[.@pos], strnpcinfo(3)) + ": ";
+
+ for (.@i = 0; .@i < .@size; .@i++)
+ {
+ .@index = .@pos * .@size + .@i;
+ .@cmd$ = getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3));
+ .@flags = getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3));
+ if (.@cmd$ != "" &&
+ .@curr_flags & .@flags)
+ {
+ .@nextpos[.@cur] = .@i;
+ .@weights[.@cur] = getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3));
+ // .@dbg$ += getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) + "=" + .@weights[.@cur] + " ";
+ .@weight_sum += .@weights[.@cur];
+ .@cur++;
+ }
+ }
+ // npcdebug .@dbg$;
+
+ if (!.@weight_sum)
+ {
+ npcdebug("error: cannot pick next walk point. flags=" +
+ getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ return;
+ }
+
+ .@pick_tries = 0;
+L_TryPick:
+ // pick a random number based on weight_sum
+ .@rnd = rand(.@weight_sum);
+ .@k = -1; .@weight_sum = 0;
+ while (.@rnd >= .@weight_sum)
+ {
+ .@k++;
+ .@weight_sum += .@weights[.@k];
+ }
+
+ .@next_idx = .@nextpos[.@k];
+ .@next_x1 = getvariableofnpc(.movepos_x1[.@next_idx], strnpcinfo(3));
+ .@next_y1 = getvariableofnpc(.movepos_y1[.@next_idx], strnpcinfo(3));
+ .@next_x2 = getvariableofnpc(.movepos_x2[.@next_idx], strnpcinfo(3));
+ .@next_y2 = getvariableofnpc(.movepos_y2[.@next_idx], strnpcinfo(3));
+
+ if (!mg_npcwalkto(.@next_x1, .@next_y1, .@next_x2, .@next_y2))
+ {
+ if (.@pick_tries < 10)
+ {
+ .@pick_tries++;
+ goto L_TryPick;
+ }
+
+ // move to a nearby position
+ .@x1 = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y1 = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ .@x2 = getvariableofnpc(.movepos_x2[.@pos], strnpcinfo(3));
+ .@y2 = getvariableofnpc(.movepos_y2[.@pos], strnpcinfo(3));
+ mg_npcwalkto(.@x1, .@y1, .@x2, .@y2);
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), "wait 1";
+
+ return;
+ }
+
+ if (getvariableofnpc(.debug, strnpcinfo(3)))
+ {
+ getmapxy(.@map$, .@cx, .@cy, 1);
+ .@dist = distance(.@cx, .@cy, .@next_x1, .@next_y1);
+ npcdebug("moving to " + getvariableofnpc(.movegraphlabels$[.@next_idx], strnpcinfo(3)) +
+ " ("+ getvariableofnpc(.move_target_x, strnpcinfo(3)) +
+ "," + getvariableofnpc(.move_target_y, strnpcinfo(3)) +
+ ") [distance=" + .@dist +
+ "] flags=" + getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ }
+
+ .@nextcmd$ = getvariableofnpc(.movegraphcmd$[.@pos * .@size + .@next_idx], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@next_idx;
+ return;
+}
+
+// initial actions for npc when using move graphs.
+// function can accept 2 arguments:
+// 1: action sequence, for example "speed 200; dir 4". Default is "moveon"
+// 2: start point label. Default is #0 from move graph labels
+function script firstmove {
+ .@nextcmd$ = getarg(0, "moveon");
+ .@initpos = findmovegraphlabel(getarg(1, ""));
+ if (.@initpos < 0) .@initpos = 0;
+
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@initpos;
+ movenpc strnpcinfo(3), getvariableofnpc(.movepos_x1[.@initpos], strnpcinfo(3)),
+ getvariableofnpc(.movepos_y1[.@initpos], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), 0xffff;
+ movetonextpoint;
+ return;
+}
+
+function script npc_pausemove {
+ stopnpctimer;
+ .@move_after = 0;
+
+ if (isunitwalking())
+ {
+ .@move_after = 1;
+ npcwalkto .x, .y;
+ npcstop;
+ }
+ set getvariableofnpc(.move_after_pause, strnpcinfo(3)), .@move_after;
+
+ return 0;
+}
+
+function script npc_resumemove {
+ startnpctimer;
+
+ if (getvariableofnpc(.move_after_pause, strnpcinfo(3)))
+ {
+ .@x = getvariableofnpc(.move_target_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move_target_y, strnpcinfo(3));
+ npcwalkto .@x, .@y;
+ }
+
+ return 0;
+}
+
+// npc_turntoxy(x,y)
+// turn npc toward an object at position (x,y)
+function script npc_turntoxy {
+ .@target_x = getarg(0);
+ .@target_y = getarg(1);
+ .@dx = abs(.@target_x - .x);
+ .@dy = abs(.@target_y - .y);
+
+ if (.@dx > .@dy)
+ .dir = .@target_x >= .x ? 6 : 2;
+ else
+ .dir = .@target_y >= .y ? 0 : 4;
+
+ return 0;
+}
+
+function script dographmovestep {
+ if (!isunitwalking())
+ {
+ movetonextpoint;
+ }
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt
new file mode 100644
index 00000000..8af2cc29
--- /dev/null
+++ b/npc/functions/permissions.txt
@@ -0,0 +1,36 @@
+// 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 has_permission("show_client_version",
+ getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// developer
+function script is_dev {
+ return has_permission(PERM_RECEIVE_REQUESTS,
+ getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// event coordinator
+function script is_evtc {
+ return can_use_command("@monster",
+ getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// game master
+function script is_gm {
+ return can_use_command("@jail",
+ getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
diff --git a/npc/functions/process_equip.txt b/npc/functions/process_equip.txt
new file mode 100644
index 00000000..e18045ff
--- /dev/null
+++ b/npc/functions/process_equip.txt
@@ -0,0 +1,26 @@
+
+function script ProcessEquip {
+ @head = getequipid(equip_head);
+ @torso = getequipid(equip_torso);
+ @legs = getequipid(equip_legs);
+
+ @torsoB = @torso;
+ if (@torsoB >= 2050 && @torsoB <= 2059) set @torsoB, 1202; // Cotton shirt
+ if (@torsoB >= 2060 && @torsoB <= 2069) set @torsoB, 624; // V Neck
+ if (@torsoB >= 2070 && @torsoB <= 2079) set @torsoB, 564; // T Neck
+ if (@torsoB >= 2080 && @torsoB <= 2089) set @torsoB, 720; // Silk Robe
+ if (@torsoB >= 2090 && @torsoB <= 2099) set @torsoB, 688; // Tanktop
+ if (@torsoB >= 2120 && @torsoB <= 2129) set @torsoB, 689; // Short tanktop
+ @torsoC = cNone;
+ if (@torso == 1202 || @torso == 624 || @torso == 564 || @torso == 688 || @torso == 689 || @torso == 720) set @torsoC, cWhite;
+ if (@torso >= 2050) set @torsoC, @torso % 10;
+
+ @legsB = @legs;
+ if (@legsB >= 2100 && @legsB <= 2109) set @legsB, 632; // Cotton skirt
+ if (@legsB >= 2110 && @legsB <= 2119) set @legsB, 586; // Cotton shorts
+ @legsC = cNone;
+ if (@legs == 632 || @legs == 586) set @legsC, cWhite;
+ if (@legs >= 2050) set @legsC, @legs % 10;
+
+ return;
+}
diff --git a/npc/functions/quests.txt b/npc/functions/quests.txt
new file mode 100644
index 00000000..4706a517
--- /dev/null
+++ b/npc/functions/quests.txt
@@ -0,0 +1,67 @@
+// The Mana World quest functions script
+
+function script QuestSagathaHappy {
+ .@value = getarg(0, @value);
+ .@unhappiness = (QUEST_MAGIC & NIBBLE_3_MASK) >> NIBBLE_3_SHIFT;
+ if (.@unhappiness < .@value)
+ .@unhappiness = 0;
+
+ .@unhappiness = .@unhappiness - .@value;
+
+ QUEST_MAGIC = (QUEST_MAGIC & ~NIBBLE_3_MASK) | (.@unhappiness << NIBBLE_3_SHIFT);
+ return;
+}
+
+function script QuestSagathaAnnoy {
+ .@value = getarg(0, @value);
+ .@unhappiness = (QUEST_MAGIC & NIBBLE_3_MASK) >> NIBBLE_3_SHIFT;
+ if ((.@unhappiness + .@value) > 15)
+ .@unhappiness = 15;
+
+ .@unhappiness = .@unhappiness + .@value;
+ QUEST_MAGIC = (QUEST_MAGIC & ~NIBBLE_3_MASK) | (.@unhappiness << NIBBLE_3_SHIFT);
+ return;
+}
+
+function script elanore_decrease_exp {
+ .@heal_exp = getq2(MagicQuest_Healing);
+
+ if (.@heal_exp < 8)
+ .@heal_exp = 0;
+ else
+ .@heal_exp -= 8;
+
+ setq2(MagicQuest_Healing, .@heal_exp);
+ return;
+}
+
+function script ValonCount {
+ @valon_mob = 0;
+ if (getq(CandorQuest_Valon) >= 2)
+ @valon_mob = (getq(CandorQuest_Valon) - 2);
+ @valon_count = ((STARTAREA & NIBBLE_2_MASK) >> NIBBLE_2_SHIFT);
+ return;
+}
+
+function script ResetValonCntMask {
+ STARTAREA = (STARTAREA & ~(NIBBLE_2_MASK) | (0 << NIBBLE_2_SHIFT));
+ return;
+}
+
+function script ValonProgress {
+ ValonCount();
+ if ((getq(CandorQuest_Valon) > 1) && (getq(CandorQuest_Valon) < 6))
+ message strcharinfo(0), getmonsterinfo($@ValonMob[@valon_mob], MOB_NAME) + ": " + @valon_count + "/" + $@ValonMobCnt[@valon_mob];
+ return;
+}
+
+function script AddValonCntMask {
+ @valon_tmp = (@valon_count + 1);
+ if (@valon_tmp > $@ValonMobCnt[@valon_mob])
+ @valon_tmp = $@ValonMobCnt[@valon_mob];
+ STARTAREA = (STARTAREA & ~(NIBBLE_2_MASK) | (@valon_tmp << NIBBLE_2_SHIFT));
+ @valon_tmp = 0;
+ ValonProgress();
+ return;
+}
+
diff --git a/npc/functions/quiz.txt b/npc/functions/quiz.txt
new file mode 100644
index 00000000..44227c4f
--- /dev/null
+++ b/npc/functions/quiz.txt
@@ -0,0 +1,92 @@
+
+function script MultiQuiz {
+ if((getarraysize(@quiz_answers$) != getarraysize(@quiz_questions$)) ||
+ (@quiz_answers$[0] == "") || (@quiz_questions$[0] == "")) goto L_ArrayError;
+ @setindex = 1;
+ @index = rand(0,(getarraysize(@quiz_answers$) - 1));
+ @question$ = @quiz_questions$[@index];
+ mes "\""+ @question$ + "\"";
+ next;
+ mes "Pick the correct answer.";
+ callfunc "Quiz";
+ return;
+
+L_ArrayError:
+ if(@quiz_answers$[0] == "") debugmes "@quiz_answers$ is empty";
+ if(@quiz_questions$[0] == "") debugmes "@quiz_questions$ is empty";
+ if(getarraysize(@quiz_answers$) != getarraysize(@quiz_questions$)) debugmes "Size of @quiz_answers$ is not equal to size of @quiz_questions$";
+ mapexit;
+}
+
+function script Quiz {
+ if((@choices_nr < 1) || (@choices_nr > 8)) set @choices_nr, 3;
+ if(@choices_nr > getarraysize(@quiz_answers$)) set @choices_nr, getarraysize(@quiz_answers$);
+ if(@quiz_answers$[0] == "") goto L_ArrayError;
+ @success = 0;
+ if(@setindex < 1) set @index, @answer;
+ @good = rand(0,(@choices_nr - 1));
+ setarray @choices$, "";
+ cleararray @choices$, "", getarraysize(@choices$);
+ @loop = 0;
+ goto L_Shuffle;
+
+L_Shuffle:
+ @nindex = rand(0,(getarraysize(@quiz_answers$) - 1));
+ if(@nindex == @index) goto L_Shuffle; // do not get the good definition
+ @loop2 = 0;
+ goto L_Search;
+
+L_Search:
+ if(@choices$[@loop2] == @quiz_answers$[@nindex]) goto L_Shuffle; // array is already populated with this choice
+ if(@loop2 >= (@choices_nr - 1)) goto L_Shuffle2;
+ @loop2 = @loop2 + 1;
+ goto L_Search;
+
+L_Shuffle2:
+ @choices$[@loop] = @quiz_answers$[@nindex];
+ if(@loop >= (@choices_nr - 1)) goto L_Answer;
+ @loop = @loop + 1;
+ goto L_Shuffle;
+
+L_Answer:
+ set @choices$[@good], @quiz_answers$[@index]; // set the good definition
+ menu
+ @choices$[0], L_Enter,
+ @choices$[1], L_Enter,
+ @choices$[2], L_Enter,
+ @choices$[3], L_Enter,
+ @choices$[4], L_Enter,
+ @choices$[5], L_Enter,
+ @choices$[6], L_Enter,
+ @choices$[7], L_Enter;
+
+L_Enter:
+ if(@menu != (@good + 1)) goto L_Shift;
+ @success = 1;
+ goto L_Shift;
+
+L_Shift:
+ if(@shift < @index) set @shift, @index;
+
+ if(@shift == @index) set @quiz_answers$[@index], ""; // do not allow twice the same question
+
+ if((@quiz_questions$[0] != "") && (@shift == @index)) set @quiz_questions$[@index], "";
+ if((@quiz_questions$[0] != "") && (@quiz_questions$[(@shift + 1)] != "")) set @quiz_questions$[@shift], @quiz_questions$[(@shift + 1)];
+ if((@quiz_questions$[0] != "") && (@quiz_questions$[(@shift + 1)] != "")) set @quiz_questions$[(@shift + 1)], "";
+
+ if(@quiz_answers$[(@shift + 1)] != "") set @quiz_answers$[@shift], @quiz_answers$[(@shift + 1)];
+ if(@quiz_answers$[(@shift + 1)] != "") set @quiz_answers$[(@shift + 1)], "";
+
+ @shift = @shift + 1;
+ if(@quiz_answers$[(@shift + 1)] != "") goto L_Shift;
+ @shift = 0;
+ goto L_close;
+
+L_close:
+ @answer = 0;
+ return;
+
+L_ArrayError:
+ if(@quiz_answers$[0] == "") debugmes "@quiz_answers$ is empty";
+ mapexit;
+}
diff --git a/npc/functions/random-talk.txt b/npc/functions/random-talk.txt
new file mode 100644
index 00000000..e6b6bee8
--- /dev/null
+++ b/npc/functions/random-talk.txt
@@ -0,0 +1,207 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Random dialog for various random NPCs.
+
+// Functions:
+// hello
+// moubootalk
+// villagertalk
+// sailortalk
+// legiontalk
+// asleep
+
+// Evol authors (some strings and code):
+// Reid
+// Akko Teru
+// Qwerty Dragon
+
+function script hello {
+
+ switch (rand2(3)) {
+ case 0:
+ npctalkonce(l("Heya!"));
+ break;
+ case 1:
+ npctalkonce(l("Hi."));
+ break;
+ case 2:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas!"));
+ else
+ npctalkonce(l("Nice day to you."));
+ break;
+ }
+
+ return;
+}
+
+function script moubootalk {
+ switch (rand2(4)) {
+ case 0:
+ npctalkonce(l("Moooooo!"));
+ break;
+ case 1:
+ npctalkonce(l("Moo!"));
+ break;
+ case 2:
+ npctalkonce(l("Moooooooooooo!"));
+ break;
+ case 3:
+ npctalkonce(l("Moooo!"));
+ break;
+ }
+ return;
+}
+
+function script sailortalk {
+
+ .@rand = rand2(8);
+ if (.@rand == 0) goodbye;
+ if (.@rand == 1) npctalkonce(l("Arr, I'm bored!"));
+ if (.@rand == 2) npctalkonce(l("Hey! Good to hear from you!"));
+ if (.@rand == 3) npctalkonce(l("Yarr arr!"));
+ if (.@rand == 4) {
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, arr yarr!!"));
+ else {
+ speech(
+ l("A sunny and hot day,"),
+ l("a quiet place,"),
+ l("a ground!"),
+ l("What else do you need?"));
+ }
+ close;
+ }
+ if (.@rand == 5) npctalkonce(l("A-hoy matey!"));
+ if (.@rand == 6) npctalkonce(l("Arr!"));
+ if (.@rand == 7) npctalkonce(l("Howdy?"));
+
+ // just to be sure
+ closedialog;
+ close;
+ end;
+}
+
+function script villagertalk {
+
+ function darn_or_smile {
+ .@darn = rand(42);
+
+ if (.@darn < 26) {
+ emotion E_JOY;
+ hello;
+ } else if (.@darn > 26) {
+ emotion E_LOOKAWAY;
+ goodbye;
+ } else {
+ npctalkonce(l("Stop it!"));
+ }
+ return;
+ }
+
+ switch (rand2(4)) {
+ case 0:
+ darn_or_smile();
+ break;
+ case 1:
+ npctalkonce(l("It is a sunny day, don't you think?"));
+ break;
+ case 2:
+ npctalkonce(l("Go fly a kite."));
+ break;
+ case 3:
+ npctalkonce(l("I just want to live my life in peace."));
+ break;
+ default:
+ emotion E_HAPPY;
+ break;
+ }
+
+ return;
+}
+
+function script legiontalk {
+ switch (rand2(15)) {
+ case 0:
+ npctalkonce(l("Do I look like a tree? I feel like one."));
+ //speech(
+ // l("Do you feel too weak even to do damage to this areas wishy-washy wildlife?"),
+ // l("Then concentrate your anger upon the trees hereabouts, you will gain experience whilst leveling your sword skill on them."),
+ // l("Oh, and a fruit may even fall for you if you are lucky! But stay alert to pick up your drops."));
+ //close;
+ break;
+ case 1:
+ npctalkonce(l("I'm a little busy right now."));
+ break;
+ case 2:
+ npctalkonce(l("Not in the mood to chat."));
+ break;
+ case 3:
+ npctalkonce(l("My breath smells bad."));
+ break;
+ case 4:
+ npctalkonce(l("Don't distract me, I have to stay alert."));
+ break;
+ case 5:
+ npctalkonce(l("Give me some space."));
+ break;
+ case 6:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, adventurer."));
+ else
+ npctalkonce(l("Can you please go away?"));
+ break;
+ case 7:
+ npctalkonce(l("Can't talk right now, I'm on patrol duty."));
+ break;
+ case 8:
+ npctalkonce(l("What're you looking at?!"));
+ break;
+ case 9:
+ npctalkonce(l("I can't stay here and talk all day. I have a job to do."));
+ break;
+ case 10:
+ npctalkonce(l("Keep moving pal."));
+ break;
+ case 11:
+ npctalkonce(l("So you think you're tough? A warrior must also be loyal and patient."));
+ break;
+ case 12:
+ emotion E_LOOKAWAY;
+ break;
+ case 13:
+ npctalkonce(l("Practice! There are no secrets to becoming a warrior."));
+ break;
+ case 14:
+ npctalkonce(l("There is no honor in fighting a weak opponent."));
+ break;
+ }
+
+ return;
+}
+
+function script asleep {
+ switch(rand2(5)) {
+ case 0: npctalkonce(l("Zzzzzzzzz...")); break;
+ case 1: npctalkonce(l("Rrrr... Pchhhh...")); break;
+ case 2: npctalkonce(l("Ggrmm... Grmmmm...")); break;
+ case 3: npctalkonce(l("Hm... Shhhh...")); break;
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
+
+function script studenttalk {
+ switch(rand2(6)) {
+ case 0: npctalkonce(l("I want to sleep...")); break;
+ case 1: npctalkonce(l("I have homework to do...")); break;
+ case 2: npctalkonce(l("I need to finish studying for my test...")); break;
+ case 3: npctalkonce(l("Ah, the Professors will get mad at me again...")); break;
+ case 4: npctalkonce(l("I'm a little busy right now.")); break;
+
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
diff --git a/npc/functions/scoreboards.txt b/npc/functions/scoreboards.txt
new file mode 100644
index 00000000..50a5823d
--- /dev/null
+++ b/npc/functions/scoreboards.txt
@@ -0,0 +1,273 @@
+// Moubootaur Legends Script
+// Author:
+// Jesusalva
+// Description:
+// Leaderboards
+
+// Scoreboard functions
+function script HallOfGuild {
+ mes "";
+ mes l("##BHall Of Guild Level: TOP5##b");
+ mesf("1. %s (%d)", $@hoguild_name$[0], $@hoguild_value[0]);
+ mesf("2. %s (%d)", $@hoguild_name$[1], $@hoguild_value[1]);
+ mesf("3. %s (%d)", $@hoguild_name$[2], $@hoguild_value[2]);
+ mesf("4. %s (%d)", $@hoguild_name$[3], $@hoguild_value[3]);
+ mesf("5. %s (%d)", $@hoguild_name$[4], $@hoguild_value[4]);
+ return;
+}
+
+function script HallOfFortune {
+ mes "";
+ mes l("##BHall Of Fortune: TOP15##b");
+ mesf("1. %s (%s GP)", $@hofortune_name$[0], fnum($@hofortune_value[0]));
+ mesf("2. %s (%s GP)", $@hofortune_name$[1], fnum($@hofortune_value[1]));
+ mesf("3. %s (%s GP)", $@hofortune_name$[2], fnum($@hofortune_value[2]));
+ mesf("4. %s (%s GP)", $@hofortune_name$[3], fnum($@hofortune_value[3]));
+ mesf("5. %s (%s GP)", $@hofortune_name$[4], fnum($@hofortune_value[4]));
+ mesf("6. %s (%s GP)", $@hofortune_name$[5], fnum($@hofortune_value[5]));
+ mesf("7. %s (%s GP)", $@hofortune_name$[6], fnum($@hofortune_value[6]));
+ mesf("8. %s (%s GP)", $@hofortune_name$[7], fnum($@hofortune_value[7]));
+ mesf("9. %s (%s GP)", $@hofortune_name$[8], fnum($@hofortune_value[8]));
+ mesf("10. %s (%s GP)", $@hofortune_name$[9], fnum($@hofortune_value[9]));
+ mesf("11. %s (%s GP)", $@hofortune_name$[10], fnum($@hofortune_value[10]));
+ mesf("12. %s (%s GP)", $@hofortune_name$[11], fnum($@hofortune_value[11]));
+ mesf("13. %s (%s GP)", $@hofortune_name$[12], fnum($@hofortune_value[12]));
+ mesf("14. %s (%s GP)", $@hofortune_name$[13], fnum($@hofortune_value[13]));
+ mesf("15. %s (%s GP)", $@hofortune_name$[14], fnum($@hofortune_value[14]));
+ return;
+}
+
+function script HallOfLevel {
+ mes "";
+ mes l("##BHall Of Level: TOP15##b");
+ mesf("1. %s (%d)", $@hoblvl_name$[0], $@hoblvl_value[0]);
+ mesf("2. %s (%d)", $@hoblvl_name$[1], $@hoblvl_value[1]);
+ mesf("3. %s (%d)", $@hoblvl_name$[2], $@hoblvl_value[2]);
+ mesf("4. %s (%d)", $@hoblvl_name$[3], $@hoblvl_value[3]);
+ mesf("5. %s (%d)", $@hoblvl_name$[4], $@hoblvl_value[4]);
+ mesf("6. %s (%d)", $@hoblvl_name$[5], $@hoblvl_value[5]);
+ mesf("7. %s (%d)", $@hoblvl_name$[6], $@hoblvl_value[6]);
+ mesf("8. %s (%d)", $@hoblvl_name$[7], $@hoblvl_value[7]);
+ mesf("9. %s (%d)", $@hoblvl_name$[8], $@hoblvl_value[8]);
+ mesf("10. %s (%d)", $@hoblvl_name$[9], $@hoblvl_value[9]);
+ mesf("11. %s (%d)", $@hoblvl_name$[10], $@hoblvl_value[10]);
+ mesf("12. %s (%d)", $@hoblvl_name$[11], $@hoblvl_value[11]);
+ mesf("13. %s (%d)", $@hoblvl_name$[12], $@hoblvl_value[12]);
+ mesf("14. %s (%d)", $@hoblvl_name$[13], $@hoblvl_value[13]);
+ mesf("15. %s (%d)", $@hoblvl_name$[14], $@hoblvl_value[14]);
+ return;
+}
+
+function script HallOfJob {
+ mes "";
+ mes l("##BHall Of Job Level: TOP15##b");
+ mesf("1. %s (%d)", $@hojlvl_name$[0], $@hojlvl_value[0]);
+ mesf("2. %s (%d)", $@hojlvl_name$[1], $@hojlvl_value[1]);
+ mesf("3. %s (%d)", $@hojlvl_name$[2], $@hojlvl_value[2]);
+ mesf("4. %s (%d)", $@hojlvl_name$[3], $@hojlvl_value[3]);
+ mesf("5. %s (%d)", $@hojlvl_name$[4], $@hojlvl_value[4]);
+ mesf("6. %s (%d)", $@hojlvl_name$[5], $@hojlvl_value[5]);
+ mesf("7. %s (%d)", $@hojlvl_name$[6], $@hojlvl_value[6]);
+ mesf("8. %s (%d)", $@hojlvl_name$[7], $@hojlvl_value[7]);
+ mesf("9. %s (%d)", $@hojlvl_name$[8], $@hojlvl_value[8]);
+ mesf("10. %s (%d)", $@hojlvl_name$[9], $@hojlvl_value[9]);
+ mesf("11. %s (%d)", $@hojlvl_name$[10], $@hojlvl_value[10]);
+ mesf("12. %s (%d)", $@hojlvl_name$[11], $@hojlvl_value[11]);
+ mesf("13. %s (%d)", $@hojlvl_name$[12], $@hojlvl_value[12]);
+ mesf("14. %s (%d)", $@hojlvl_name$[13], $@hojlvl_value[13]);
+ mesf("15. %s (%d)", $@hojlvl_name$[14], $@hojlvl_value[14]);
+ return;
+}
+
+function script HallOfAcorns {
+ mes "";
+ mes l("##BHall Of Acorns: TOP15##b");
+ mesc l("Only %s in storage will be counted.", getitemlink(Acorn));
+ mesf("1. %s (%d)", $@hoa_name$[0], $@hoa_value[0]);
+ mesf("2. %s (%d)", $@hoa_name$[1], $@hoa_value[1]);
+ mesf("3. %s (%d)", $@hoa_name$[2], $@hoa_value[2]);
+ mesf("4. %s (%d)", $@hoa_name$[3], $@hoa_value[3]);
+ mesf("5. %s (%d)", $@hoa_name$[4], $@hoa_value[4]);
+ mesf("6. %s (%d)", $@hoa_name$[5], $@hoa_value[5]);
+ mesf("7. %s (%d)", $@hoa_name$[6], $@hoa_value[6]);
+ mesf("8. %s (%d)", $@hoa_name$[7], $@hoa_value[7]);
+ mesf("9. %s (%d)", $@hoa_name$[8], $@hoa_value[8]);
+ mesf("10. %s (%d)", $@hoa_name$[9], $@hoa_value[9]);
+ mesf("11. %s (%d)", $@hoa_name$[10], $@hoa_value[10]);
+ mesf("12. %s (%d)", $@hoa_name$[11], $@hoa_value[11]);
+ mesf("13. %s (%d)", $@hoa_name$[12], $@hoa_value[12]);
+ mesf("14. %s (%d)", $@hoa_name$[13], $@hoa_value[13]);
+ mesf("15. %s (%d)", $@hoa_name$[14], $@hoa_value[14]);
+ return;
+}
+
+function script HallOfLethality {
+ mes "";
+ mes l("##BHall Of Lethality: TOP15##b");
+ mesc l("Special monsters are not counted.");
+ mesf("1. %s (%d)", $@hol_name$[0], $@hol_value[0]);
+ mesf("2. %s (%d)", $@hol_name$[1], $@hol_value[1]);
+ mesf("3. %s (%d)", $@hol_name$[2], $@hol_value[2]);
+ mesf("4. %s (%d)", $@hol_name$[3], $@hol_value[3]);
+ mesf("5. %s (%d)", $@hol_name$[4], $@hol_value[4]);
+ mesf("6. %s (%d)", $@hol_name$[5], $@hol_value[5]);
+ mesf("7. %s (%d)", $@hol_name$[6], $@hol_value[6]);
+ mesf("8. %s (%d)", $@hol_name$[7], $@hol_value[7]);
+ mesf("9. %s (%d)", $@hol_name$[8], $@hol_value[8]);
+ mesf("10. %s (%d)", $@hol_name$[9], $@hol_value[9]);
+ mesf("11. %s (%d)", $@hol_name$[10], $@hol_value[10]);
+ mesf("12. %s (%d)", $@hol_name$[11], $@hol_value[11]);
+ mesf("13. %s (%d)", $@hol_name$[12], $@hol_value[12]);
+ mesf("14. %s (%d)", $@hol_name$[13], $@hol_value[13]);
+ mesf("15. %s (%d)", $@hol_name$[14], $@hol_value[14]);
+ return;
+}
+
+function script HallOfAlmanach {
+ mes "";
+ mes l("##BHall Of Almanach: TOP15##b");
+ mesc l("The greatest heroes of all time");
+ mesf("1. %s (%s)", $@hob_name$[0], fnum($@hob_value[0]));
+ mesf("2. %s (%s)", $@hob_name$[1], fnum($@hob_value[1]));
+ mesf("3. %s (%s)", $@hob_name$[2], fnum($@hob_value[2]));
+ mesf("4. %s (%s)", $@hob_name$[3], fnum($@hob_value[3]));
+ mesf("5. %s (%s)", $@hob_name$[4], fnum($@hob_value[4]));
+ mesf("6. %s (%s)", $@hob_name$[5], fnum($@hob_value[5]));
+ mesf("7. %s (%s)", $@hob_name$[6], fnum($@hob_value[6]));
+ mesf("8. %s (%s)", $@hob_name$[7], fnum($@hob_value[7]));
+ mesf("9. %s (%s)", $@hob_name$[8], fnum($@hob_value[8]));
+ mesf("10. %s (%s)", $@hob_name$[9], fnum($@hob_value[9]));
+ mesf("11. %s (%s)", $@hob_name$[10], fnum($@hob_value[10]));
+ mesf("12. %s (%s)", $@hob_name$[11], fnum($@hob_value[11]));
+ mesf("13. %s (%s)", $@hob_name$[12], fnum($@hob_value[12]));
+ mesf("14. %s (%s)", $@hob_name$[13], fnum($@hob_value[13]));
+ mesf("15. %s (%s)", $@hob_name$[14], fnum($@hob_value[14]));
+ return;
+}
+
+
+// HallOfGame()
+function script HallOfGame {
+ mes l("Players Killed in PvP: %s", format_number($PLAYERS_KILLED));
+ mes l("Monsters Killed in PvE: %s", format_number($MONSTERS_KILLED));
+ mes "";
+ // season ; weather ; game time ; world story ; etc.
+ mes "";
+ mes l("Notable mentions and thanks for our [@@https://www.patreon.com/themanaworld|sponsors@@] for their continued support.");
+ mes "";
+ return;
+}
+
+
+// Main script handler for scoreboards
+- script @scoreboard NPC_HIDDEN,{
+ end;
+OnHour00:
+OnHour01:
+OnHour02:
+OnHour03:
+OnHour04:
+OnHour05:
+OnHour06:
+OnHour07:
+OnHour08:
+OnHour09:
+OnHour10:
+OnHour11:
+OnHour12:
+OnHour13:
+OnHour14:
+OnHour15:
+OnHour16:
+OnHour17:
+OnHour18:
+OnHour19:
+OnHour20:
+OnHour21:
+OnHour22:
+OnHour23:
+OnInit:
+ consolemes(CONSOLEMES_DEBUG, "Reloading scoreboards...");
+ .@nb = query_sql("select name, zeny from `char` ORDER BY zeny DESC LIMIT 15", $@hofortune_name$, $@hofortune_value);
+ .@nb = query_sql("select name, base_level from `char` ORDER BY base_level DESC LIMIT 15", $@hoblvl_name$, $@hoblvl_value);
+ .@nb = query_sql("select name, job_level from `char` ORDER BY job_level DESC LIMIT 15", $@hojlvl_name$, $@hojlvl_value);
+ .@nb = query_sql("select name, guild_lv from `guild` ORDER BY guild_lv DESC LIMIT 5", $@hoguild_name$, $@hoguild_value);
+ .@nb = query_sql("SELECT c.name, i.amount FROM `storage` AS i, `char` AS c WHERE i.nameid="+Acorn+" AND i.account_id=c.account_id AND c.char_num = 0 ORDER BY i.amount DESC LIMIT 15", $@hoa_name$, $@hoa_value);
+ .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='MONSTERS_KILLED' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 15", $@hol_name$, $@hol_value);
+ .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='BOSS_POINTS' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 15", $@hob_name$, $@hob_value);
+ consolemes(CONSOLEMES_DEBUG, "Scoreboards reloaded");
+ if (!$@SCOREBOARD_BIND) {
+ bindatcmd "scoreboard", "@scoreboard::OnCall", 0, 100, 0;
+ bindatcmd "scoreboards", "@scoreboard::OnCall", 0, 100, 0;
+ $@SCOREBOARD_BIND=true;
+ }
+ end;
+
+OnCall:
+ do {
+ clear;
+ mes l("The Mana World - Legacy");
+ mesc l("All scoreboards are updated hourly."), 1;
+ mes "";
+ select
+ l("Hall Of Fortune"),
+ l("Hall Of Base Level"),
+ l("Hall Of Job Level"),
+ l("Hall Of Guilds"),
+ l("Hall Of Lethality"),
+ l("Hall Of Almanach"),
+ l("Hall Of Acorns"),
+ l("Game Statistics"),
+ l("Personal Information"),
+ l("Quit");
+ mes "";
+ switch (@menu) {
+ case 1:
+ HallOfFortune();
+ next;
+ break;
+ case 2:
+ HallOfLevel();
+ next;
+ break;
+ case 3:
+ HallOfJob();
+ next;
+ break;
+ case 4:
+ HallOfGuild();
+ next;
+ break;
+ case 5:
+ HallOfLethality();
+ next;
+ break;
+ case 6:
+ HallOfAlmanach();
+ next;
+ break;
+ case 7:
+ HallOfAcorns();
+ next;
+ break;
+ case 8:
+ HallOfGame();
+ next;
+ break;
+ case 9:
+ ShowAbizit(true);
+ mes "";
+ mesc l("Total deaths: %s", fnum(PC_DIE_COUNTER));
+ mesc l("Boss points: %s", fnum(BOSS_POINTS));
+ mesc l("Mob points: %s", fnum(Mobpt));
+ mesc l("Total Gold: %s", fnum((Zeny+BankVault)));
+ next;
+ break;
+ default:
+ close;
+ }
+ } while (true);
+ end;
+}
+
+
diff --git a/npc/functions/slot_machine.txt b/npc/functions/slot_machine.txt
new file mode 100644
index 00000000..0d7e4d39
--- /dev/null
+++ b/npc/functions/slot_machine.txt
@@ -0,0 +1,92 @@
+// Slot Machine
+
+function script SlotMachineSymbol {
+ switch (getarg(0)) {
+ case 0:
+ mesn "%%A";
+ break;
+ case 1:
+ mesn "%%B";
+ break;
+ case 2:
+ mesn "%%C";
+ break;
+ case 3:
+ mesn "%%D";
+ break;
+ case 4:
+ mesn "%%E";
+ break;
+ case 5:
+ mesn "%%F";
+ break;
+ case 6:
+ mesn "7";
+ break;
+ default:
+ mesn "%%@";
+ break;
+ }
+ }
+
+function script SlotMachine {
+ mes "Pull the lever...";
+ next;
+ menu
+ "Pull", L_Play,
+ "Maybe later", L_close;
+
+L_Play:
+ if(countitem("CasinoCoins") < 1)
+ goto L_NoCoin;
+ delitem "CasinoCoins", 1;
+ .@Temp1 = rand(7);
+ .@Temp2 = rand(7);
+ .@Temp3 = rand(7);
+ //mes "Numbers: " + .@Temp1 + "/" + .@Temp2 + "/" + .@Temp3 + ".";
+ SlotMachineSymbol(.@Temp1);
+ SlotMachineSymbol(.@Temp2);
+ SlotMachineSymbol(.@Temp3);
+ next;
+
+ if (.@Temp1 != .@Temp2)
+ goto L_Lost;
+ if (.@Temp2 != .@Temp3)
+ goto L_Lost;
+ if (.@Temp1 != .@Temp3)
+ goto L_Lost;
+ if (CSN < 9 && rand(6) < CSN)
+ goto L_Jackpot;
+ mes "Congratulations! You won!";
+ mes "You get 10 casino coins";
+ getitem CasinoCoins, 10;
+ if (CSN < 9)
+ CSN+=1;
+ goto L_close;
+
+L_Jackpot:
+ mes "Congratulations! You won!";
+ mes "However, the slot machine";
+ mes "do not give you the coins!";
+ next;
+ mes "[Staff]";
+ mes "\"I apologize for this problem.";
+ mes "I see you are a huge client of";
+ mes "ours, so I'll give you a Monocle";
+ mes "as a token of apology.\"";
+ getitem Monocle, 1;
+ CSN = 9;
+ goto L_close;
+
+L_Lost:
+ mes "You lost!";
+ goto L_close;
+
+L_NoCoin:
+ mes "Insert coin";
+ goto L_close;
+
+L_close:
+ closeclientdialog;
+ return;
+}
diff --git a/npc/functions/soul_menhir.txt b/npc/functions/soul_menhir.txt
new file mode 100644
index 00000000..056058fa
--- /dev/null
+++ b/npc/functions/soul_menhir.txt
@@ -0,0 +1,60 @@
+function script SoulMenhir {
+ mesn l("Soul Menhir");
+ mes l("(A mystical aura surrounds this stone. You feel mysteriously attracted to it. Something tells you to touch it. What do you do?)");
+
+ select
+ l("Touch it."),
+ l("Leave it alone."),
+ rif(!countitem(HitchhikersTowel), l("I lost my towel..."));
+ mes "";
+ if (@menu == 1) goto L_Bind;
+ if (@menu == 3) goto L_Towel;
+ return;
+
+L_Towel:
+ if (TowelLastUsed > (gettimetick(2) - 1800))
+ goto L_DontPanic;
+ TowelLastUsed = gettimetick(2);
+ mesn l("Soul Menhir");
+ mes "(You touch the mysterious stone. Somehow it feels hard and soft at the same time.)";
+ getitembound HitchhikersTowel, 1, 4;
+ goto L_Return;
+
+L_Bind:
+ if (Menhir_Activated == 1)
+ goto L_Shortversion;
+
+ mesn l("Soul Menhir");
+ mes "(You touch the mysterious stone. Somehow it feels warm and cold at the same time.)";
+ mes "(Suddenly a strange sensation flows through you. It feels like your soul leaves your body and becomes one with the stone.)";
+ mes "(As suddenly as the feeling started it stops. The strange attraction is away from one moment to the next and the menhir feels like just an ordinary stone.)";
+ Menhir_Activated = 1;
+ goto L_Save;
+
+L_Shortversion:
+ mesn l("Soul Menhir");
+ mes "(A strange sensation flows through you. It feels like your soul leaves your body and becomes one with the stone. As suddenly as the feeling started it stops.)";
+ goto L_Save;
+
+L_Save:
+ if (@x == 0 && @y == 0)
+ goto L_FindPoint;
+ goto L_Do_Save;
+
+L_DontPanic:
+ message strcharinfo(0), "(A strange barrier keeps you from touching the stone at this time.)";
+ goto L_Return;
+
+L_Do_Save:
+ savepoint @map$, @x, @y;
+ goto L_Return;
+
+L_FindPoint:
+ @n = rand(getarraysize(@Xs));
+ @x = @Xs[@n];
+ @y = @Ys[@n];
+ goto L_Do_Save;
+
+L_Return:
+ return;
+}
diff --git a/npc/functions/stat_reset.txt b/npc/functions/stat_reset.txt
new file mode 100644
index 00000000..4201865a
--- /dev/null
+++ b/npc/functions/stat_reset.txt
@@ -0,0 +1,49 @@
+
+function script StatReset {
+
+
+ @Cost = BaseLevel * 100;
+
+ mes "[" + @npcname$ + "]";
+ mes "\"I have come across a spell that will";
+ mes "reset your status points.";
+ mes "Normally this spell is expensive, but";
+ mes "due to an unusual constellation of the";
+ mes "stars I can cast it very cheaply!";
+ mes "For you it will cost only "+@Cost+" gp.\"";
+ next;
+ menu
+ "Reset my stats",L_Next,
+ "Forget about it",L_Pass;
+
+L_Next:
+ if (Zeny<@Cost) goto L_NoMoney;
+ goto L_Reset;
+
+
+L_Reset:
+ Zeny = Zeny-@Cost;
+ resetstatus;
+
+ mes "[" + @npcname$ + "]";
+ mes "\"There you are.";
+ mes "";
+ mes "Good as new!\"";
+ goto L_Return;
+
+L_Pass:
+ mes "[" + @npcname$ + "]";
+ mes "\"Very well then, see you.\"";
+ goto L_Return;
+
+L_NoMoney:
+ mes "[" + @npcname$ + "]";
+ mes "\"Oh dear, the price cannot be bargained.";
+ mes "";
+ mes "Perhaps you can borrow from a friend?\"";
+ goto L_Return;
+
+L_Return:
+ @Cost = 0;
+ return;
+}
diff --git a/npc/functions/string.txt b/npc/functions/string.txt
new file mode 100644
index 00000000..2a38d90d
--- /dev/null
+++ b/npc/functions/string.txt
@@ -0,0 +1,211 @@
+// Evol Script
+// Author: Gumi
+
+// safe string manipulation functions
+// ** does not require PCRE
+
+
+// str(<int>)
+// returns whatever is passed, converted to string
+
+function script str {
+ return "" + getarg(0);
+}
+
+
+
+// startswith("<string>", "<search>")
+// returns true if <string> begins with <search>
+
+function script startswith {
+ return substr(getarg(0), 0, getstrlen(getarg(1)) - 1) == getarg(1);
+}
+
+
+
+// endswith("<string>", "<search>")
+// returns true if <string> ends with <search>
+
+function script endswith {
+ .@t = getstrlen(getarg(0)); // total length
+ .@n = getstrlen(getarg(1)); // substring length
+ return substr(getarg(0), .@t - .@n, .@t - 1) == getarg(1);
+}
+
+
+
+// capitalize("<string>")
+// returns <string> with its first letter capitalized
+
+function script capitalize {
+ return setchar(getarg(0), strtoupper(charat(getarg(0), 0)), 0);
+}
+
+
+
+// titlecase("<string>" {, "<delimiter>" {, <camel>}})
+// returns <string> with the first letter of each word capitalized
+// if <camel> is true, the string is joined in a camelCase fashion
+
+function script titlecase {
+ .@delimiter$ = getarg(1, " ");
+ .@c = getarg(2, 0);
+ explode(.@words$, getarg(0), .@delimiter$);
+
+ for (.@i = (.@c ? 1 : 0); .@i < 255; ++.@i)
+ {
+ if (.@words$[.@i] == "")
+ {
+ break;
+ }
+
+ .@words$[.@i] = setchar(.@words$[.@i], strtoupper(charat(.@words$[.@i], 0)), 0);
+ }
+
+ return implode(.@words$, (.@c ? "" : .@delimiter$));
+}
+
+
+
+// camelcase("<string" {, "<delimiter>"})
+
+function script camelcase {
+ return titlecase(getarg(0), getarg(1, " "), true);
+}
+
+
+
+// zfill("<string>" {, <width> {, "<padding>"}})
+// returns <string> padded to the left with <padding> up to width
+
+function script zfill {
+ .@str$ = getarg(0);
+ .@width = getarg(1, 8);
+ .@padding$ = getarg(2, "0");
+
+ for (.@s = getstrlen(.@str$); .@s < .@width; ++.@s)
+ {
+ .@str$ = .@padding$ + .@str$;
+ }
+
+ return .@str$;
+}
+
+
+
+// format_number(<integer> {, "<separator>"})
+// formats a number properly
+
+function script format_number {
+ .@number$ = str(getarg(0));
+ .@len = getstrlen(.@number$);
+ .@separator$ = getarg(1, ",");
+
+ if (getargcount() < 2 && playerattached()) {
+ // get from user language
+ switch (Lang) {
+ case LANG_FR: .@separator$ = " "; break; // French
+ case LANG_DE: .@separator$ = "."; break; // Germanic
+ case LANG_PTBR: .@separator$ = "."; break; // Brazilian
+ default: .@separator$ = ","; // English (default)
+ }
+ }
+
+ for (.@i = .@len - 3; .@i > 0; .@i -= 3) {
+ .@number$ = insertchar(.@number$, .@separator$, .@i);
+ }
+
+ return .@number$;
+}
+
+
+
+// fnum(<integer>)
+// alias for format_number
+
+function script fnum {
+ return format_number(getarg(0));
+}
+
+
+
+// strip("<string>")
+// removes spaces at the start and end
+
+function script strip {
+ .@s$ = getarg(0);
+ if (.@s$ == "") {
+ return "";
+ }
+ .@start = 0;
+ .@end = getstrlen(.@s$) - 1;
+ for (.@i = .@start; .@i < .@end; .@i++)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@start++;
+ }
+ }
+ for (.@i = .@end; .@i >= .@start; .@i--)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@end--;
+ }
+ }
+ //debugmes "STRIP.DEBUG MODE ENABLED BY JESUSALVA. PASSING SUBSTRING PARAMS";
+ //debugmes "String \""+.@s$+"\" from "+str(.@start)+" to "+str(.@end);
+ return substr(.@s$, .@start, .@end);
+}
+
+
+
+// reverse("<string>")
+// returns <string> reversed
+
+function script reverse {
+ .@str$ = getarg(0);
+ .@len = getstrlen(.@str$);
+
+ for (.@i = 0; .@i < (.@len / 2); ++.@i) {
+ .@tmp$ = charat(.@str$, .@i);
+ .@str$ = setchar(.@str$, charat(.@str$, (.@len - 1 - .@i)), .@i); // a <= b
+ .@str$ = setchar(.@str$, .@tmp$, (.@len - 1 - .@i)); // b <= a
+ }
+
+ return .@str$;
+}
+
+
+
+// repeat("<string>", <multiplier>)
+// repeats <string> many times and returns it
+
+function script repeat {
+ .@mul = getarg(1);
+
+ for (.@i = 0; .@i < .@mul; ++.@i) {
+ .@str$ += getarg(0);
+ }
+
+ return .@str$;
+}
+
+
+
+// shuffle("<string>")
+// returns <string> shuffled
+
+function script shuffle {
+ .@str$ = getarg(0);
+
+ for (.@len = getstrlen(.@str$); .@len > 0; --.@len) {
+ .@rnd = rand(.@len);
+ .@out$ += charat(.@str$, .@rnd);
+ .@str$ = delchar(.@str$, .@rnd);
+ }
+
+ return .@out$;
+}
diff --git a/npc/functions/time.txt b/npc/functions/time.txt
new file mode 100644
index 00000000..e6e4c70a
--- /dev/null
+++ b/npc/functions/time.txt
@@ -0,0 +1,117 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+
+function script now {
+ return gettimetick(2);
+}
+
+// Returns current time. A SQL update superseeded this.
+// santime( )
+function script santime {
+ return gettimetick(2);
+}
+
+function script time_from_ms {
+ return now() + (getarg(0) / 1000);
+}
+
+function script time_from_seconds {
+ return now() + getarg(0);
+}
+
+function script time_from_minutes {
+ return now() + (getarg(0) * 60);
+}
+
+function script time_from_hours {
+ return now() + (getarg(0) * 3600);
+}
+
+function script time_from_days {
+ return now() + (getarg(0) * 86400);
+}
+
+
+// FuzzyTime(<unix timestamp>{, <options>{, <precision>}})
+// gives time in a human-readable format
+//
+// <options> is bitmasked:
+// 1 do not show "ago" when in past
+// 2 do not show "in" when in the future
+// 4 show "from now" instead of "in" when in the future
+//
+// <precision> is the number of units to show,
+// by default uses two (eg. 2m30s or 1h20m).
+// Use '99' for max precision
+
+function script FuzzyTime {
+ .@future = getarg(0, now());
+ .@options = getarg(1, 3);
+ .@precision = getarg(2, 2);
+ .@diff = (.@future - now());
+
+ // check if in the past, or in the future
+ if (.@diff < 0) {
+ .@diff *= -1;
+ .@past = true;
+ }
+
+ .@diff = max(1, .@diff);
+
+ if (.@diff >= 31536000) {
+ .@years = (.@diff / 31536000);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 31536000));
+ .@ret$ += sprintf("%d %s", .@years, (.@years > 1 ? "years" : "year"));
+ }
+
+ if (.@diff >= 86400) {
+ .@days = (.@diff / 86400);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 86400));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : " and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@days, (.@days > 1 ? "days" : "day"));
+ }
+
+ if (.@diff >= 3600) {
+ .@hours = (.@diff / 3600);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 3600));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@hours, (.@hours > 1 ? "hours" : "hour"));
+ }
+
+ if (.@diff >= 60) {
+ .@minutes = (.@diff / 60);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 60));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@minutes, (.@minutes > 1 ? "minutes" : "minute"));
+ }
+
+ if (.@diff >= 1) {
+ if (++.@s > 1) {
+ .@ret$ += (.@s >= 3 ? ", " : " ") + "and ";
+ }
+
+ .@ret$ += sprintf("%d %s", .@diff, (.@diff > 1 ? "seconds" : "second"));
+ }
+
+ if (.@past && !(.@options & 1)) {
+ .@ret$ += " ago";
+ }
+
+ if (!(.@past) && !(.@options & 2)) {
+ .@ret$ = ((.@options & 4) ? sprintf("%s from now", .@ret$) : sprintf("in %s", .@ret$));
+ }
+
+ return .@ret$;
+}
diff --git a/npc/functions/timer.txt b/npc/functions/timer.txt
new file mode 100644
index 00000000..27e09f13
--- /dev/null
+++ b/npc/functions/timer.txt
@@ -0,0 +1,89 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+
+// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer {
+ // Legacy
+ if (getargcount() > 7)
+ .@ox=1;
+ // Variables
+ .@m$=getarg(.@ox); .@ox+=1;
+ .@x1=getarg(.@ox); .@ox+=1;
+ .@y1=getarg(.@ox); .@ox+=1;
+ .@x2=getarg(.@ox); .@ox+=1;
+ .@y2=getarg(.@ox); .@ox+=1;
+ .@tk=getarg(.@ox); .@ox+=1;
+ .@e$=getarg(.@ox); .@ox+=1;
+ .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(.@tk, .@e$, .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areadeltimer("<map>", <x1>, <y1>, <x2>, <y2>, "<npc>::<event>")
+function script areadeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(5), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areatimer2("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(6), .@players[.@i]);
+ addtimer(getarg(5), getarg(6), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// addtimer2(<tick>, "<npc>::<event>")
+function script addtimer2 {
+ deltimer(getarg(1));
+ addtimer(getarg(0), getarg(1));
+ return;
+}
+
+
+// maptimer("<map>", <tick>, "<npc>::<event>")
+function script maptimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// Same as maptimer() but deletes any previously running timer
+// maptimer2("<map>", <tick>, "<npc>::<event>")
+function script maptimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(2), .@players[.@i]);
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// mapdeltimer("<map>", "<npc>::<event>")
+function script mapdeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(1), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// partytimer("<map>", <tick>, "<npc>::<event>", partyid)
+function script partytimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ if (getcharid(2, strcharinfo(0,"",.@players[.@i]) ) == getarg(3))
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
diff --git a/npc/functions/travelers.txt b/npc/functions/travelers.txt
new file mode 100644
index 00000000..e76d2130
--- /dev/null
+++ b/npc/functions/travelers.txt
@@ -0,0 +1,235 @@
+
+
+017-9,27,28,0 script #TravelConfig NPC32767,{
+ end;
+
+OnInit:
+ // TravelFound
+ $@tut_bit = (1 << 2);
+ //set $@druid_tree_bit, (1 << 3);
+ $@graveyard_bit = (1 << 4);
+ $@magic_house_bit = (1 << 5);
+ $@terranite_cave_bit = (1 << 6);
+ $@tulimshar_bit = (1 << 7);
+ $@blue_sage_bit = (1 << 8);
+ $@hurnscald_bit = (1 << 9);
+ $@nivalis_bit = (1 << 10);
+ //set $@tul_mine_bit, (1 << 11);
+ $@pachua_bit = (1 << 12);
+ $@barbarians_bit = (1 << 14);
+ $@hurns_farms_bit = (1 << 15);
+ $@candor_bit = (1 << 16);
+ // Travel Base Cost
+ $@tulimshar_cost = 100;
+ $@hurnscald_cost = 100;
+ $@nivalis_cost = 100;
+ //set $@druid_tree_cost, 150;
+ //set $@tul_mine_cost, 150;
+ $@pachua_cost = 200;
+ $@graveyard_cost = 200;
+ $@magic_house_cost = 150;
+ $@terranite_cave_cost = 200;
+ $@blue_sage_cost = 200;
+ $@barbarians_cost = 150;
+ $@hurns_farms_cost = 150;
+ $@candor_cost = 200;
+ end;
+}
+
+function script Traveler {
+ if(@npcname$ == "") set @npcname$, strnpcinfo(1);
+ mes "["+@npcname$+"]";
+ mes "\"Greetings. I am "+@npcname$+" the Traveler.\"";
+ next;
+
+ if (TravelFound & $@tut_bit)
+ goto L_Main;
+ goto L_TravelTut;
+
+L_Main:
+ if (TravelFound & @NpcTravelBit)
+ goto L_BitTravelSet;
+ goto L_SetTravelBit;
+
+L_BitTravelSet:
+ @Cost = 10;
+ if (BaseLevel < 45)
+ @Cost = 5;
+ goto L_Start;
+
+L_TravelTut:
+ mes "["+@npcname$+"]";
+ mes "\"We travelers are found all over the world. Once you have found a traveler at a certain location, you can be sent back there instantly by another traveler.\"";
+ next;
+ if (TravelFound & $@tut_bit)
+ goto L_Main;
+ goto L_SetBit;
+
+L_SetBit:
+ TravelFound = TravelFound | $@tut_bit;
+ goto L_Main;
+
+L_SetTravelBit:
+ mes "["+@npcname$+"]";
+ mes "\"Uplink set. You can now return to this spot for a fee.\"";
+ next;
+ TravelFound = TravelFound | @NpcTravelBit;
+ goto L_BitTravelSet;
+
+L_Start:
+ mes "\"Where would you like to go?\"";
+ menu
+ "Tonori - Tulimshar (" + (@Cost * $@tulimshar_cost) + " GP)", L_TravelTulimshar,
+ "Argeas - Hurnscald (" + (@Cost * $@hurnscald_cost) + " GP)", L_TravelHurnscald,
+ "Kaizei - Nivalis (" + (@Cost * $@nivalis_cost) + " GP)", L_TravelNivalis,
+ "Tonori - Pachua's Village (" + (@Cost * $@pachua_cost) + " GP)", L_TravelPachua,
+ "Argeas - Candor (" + (@Cost * $@candor_cost) + " GP)", L_TravelCandor,
+ "Argeas - Magic House (" + (@Cost * $@magic_house_cost) + " GP)", L_TravelMagicHouse,
+ "Argeas - Farmsteads (" + (@Cost * $@hurns_farms_cost) + " GP)", L_TravelHurnsFarms,
+ "Argeas - Graveyard (" + (@Cost * $@graveyard_cost) + " GP)", L_TravelGraveyard,
+ "Argeas - Terranite Cave (" + (@Cost * $@terranite_cave_cost) + " GP)", L_TravelTerranite,
+ "Kaizei - Barbarian Village (" + (@Cost * $@barbarians_cost) + " GP)", L_TravelBarbarians,
+ "Kaizei - Sage Nikolai's Mansion (" + (@Cost * $@blue_sage_cost) + " GP)", L_TravelBlueSage,
+ "Who are the Travelers?", L_TravelTut,
+ "I'm not interested.", L_TravelNo;
+
+L_TravelChecks:
+ if (@NpcTravelBit == @NextLocationBit)
+ goto L_AlreadyThere;
+ if (!(TravelFound & @NextLocationBit))
+ goto L_NoFound;
+ if (Zeny < @NextLocationCost)
+ goto L_NoMoney;
+ goto L_TravelPlayer;
+
+L_TravelPlayer:
+ mes "["+@npcname$+"]";
+ mes "\"Be fearless!\"";
+ close2;
+ Zeny = Zeny - @NextLocationCost;
+ warp @NextLocationMap$,@NextLocationX,@NextLocationY;
+ goto L_Clearvars;
+
+L_TravelGraveyard:
+ @NextLocationBit = $@graveyard_bit;
+ @NextLocationCost = (@Cost * $@graveyard_cost);
+ @NextLocationMap$ = "026-1";
+ @NextLocationX = 49;
+ @NextLocationY = 45;
+ goto L_TravelChecks;
+
+L_TravelMagicHouse:
+ @NextLocationBit = $@magic_house_bit;
+ @NextLocationCost = (@Cost * $@magic_house_cost);
+ @NextLocationMap$ = "013-1";
+ @NextLocationX = 120;
+ @NextLocationY = 93;
+ goto L_TravelChecks;
+
+L_TravelTerranite:
+ @NextLocationBit = $@terranite_cave_bit;
+ @NextLocationCost = (@Cost * $@terranite_cave_cost);
+ @NextLocationMap$ = "012-3";
+ @NextLocationX = 445;
+ @NextLocationY = 65;
+ goto L_TravelChecks;
+
+L_TravelTulimshar:
+ @NextLocationBit = $@tulimshar_bit;
+ @NextLocationCost = (@Cost * $@tulimshar_cost);
+ @NextLocationMap$ = "002-1";
+ @NextLocationX = 60;
+ @NextLocationY = 42;
+ goto L_TravelChecks;
+
+L_TravelBlueSage:
+ @NextLocationBit = $@blue_sage_bit;
+ @NextLocationCost = (@Cost * $@blue_sage_cost);
+ @NextLocationMap$ = "048-2";
+ @NextLocationX = 26;
+ @NextLocationY = 47;
+ goto L_TravelChecks;
+
+L_TravelHurnscald:
+ @NextLocationBit = $@hurnscald_bit;
+ @NextLocationCost = (@Cost * $@hurnscald_cost);
+ @NextLocationMap$ = "008-1";
+ @NextLocationX = 79;
+ @NextLocationY = 84;
+ goto L_TravelChecks;
+
+L_TravelNivalis:
+ @NextLocationBit = $@nivalis_bit;
+ @NextLocationCost = (@Cost * $@nivalis_cost);
+ @NextLocationMap$ = "020-1";
+ @NextLocationX = 53;
+ @NextLocationY = 122;
+ goto L_TravelChecks;
+
+L_TravelPachua:
+ @NextLocationBit = $@pachua_bit;
+ @NextLocationCost = (@Cost * $@pachua_cost);
+ @NextLocationMap$ = "006-1";
+ @NextLocationX = 28;
+ @NextLocationY = 97;
+ callfunc "MiriamCheat";
+ goto L_TravelChecks;
+
+L_TravelBarbarians:
+ @NextLocationBit = $@barbarians_bit;
+ @NextLocationCost = (@Cost * $@barbarians_cost);
+ @NextLocationMap$ = "033-1";
+ @NextLocationX = 66;
+ @NextLocationY = 33;
+ goto L_TravelChecks;
+
+L_TravelHurnsFarms:
+ @NextLocationBit = $@hurns_farms_bit;
+ @NextLocationCost = (@Cost * $@hurns_farms_cost);
+ @NextLocationMap$ = "055-1";
+ @NextLocationX = 135;
+ @NextLocationY = 60;
+ goto L_TravelChecks;
+
+L_TravelCandor:
+ @NextLocationBit = $@candor_bit;
+ @NextLocationCost = (@Cost * $@candor_cost);
+ @NextLocationMap$ = "029-1";
+ @NextLocationX = 69;
+ @NextLocationY = 69;
+ goto L_TravelChecks;
+
+L_TravelNo:
+ mes "["+@npcname$+"]";
+ mes "\"Perhaps you will have the courage to help us some day.\"";
+ close2;
+ goto L_Clearvars;
+
+L_NoMoney:
+ mes "["+@npcname$+"]";
+ mes "\"I'm sorry, but you don't have enough money. Maybe next time.\"";
+ close2;
+ goto L_Clearvars;
+
+L_NoFound:
+ mes "["+@npcname$+"]";
+ mes "\"Sorry, but you haven't visited a traveler yet at that location. You should find and talk to a traveler there so you can quickly return to that location in the future.\"";
+ close2;
+ goto L_Clearvars;
+
+L_AlreadyThere:
+ mes "["+@npcname$+"]";
+ mes "\"Uh... You're already here. Are you sure you know where you are going?\"";
+ close2;
+ goto L_Clearvars;
+
+L_Clearvars:
+ @npcname$ = "";
+ @Cost = 0;
+ @NextLocationBit = 0;
+ @NextLocationCost = 0;
+ @NextLocationMap$ = "";
+ @NextLocationX = 0;
+ @NextLocationY = 0;
+ return;
+}
diff --git a/npc/functions/undead_debug.txt b/npc/functions/undead_debug.txt
new file mode 100644
index 00000000..693edc13
--- /dev/null
+++ b/npc/functions/undead_debug.txt
@@ -0,0 +1,111 @@
+
+function script UndeadDebug {
+ goto L_Main;
+
+L_Main:
+ if (@undeaddebug == 3)
+ goto L_UndeadDebugThree;
+ if (@undeaddebug == 4)
+ goto L_UndeadDebugFour;
+ if (@undeaddebug == 5)
+ goto L_UndeadDebugFive;
+ goto L_close;
+
+L_UndeadDebugThree:
+ mes "Reset your self to the various states.";
+ mes "Options Limited to Time and Place.";
+ menu
+ "Get Ritual Items.", L_KrukanItems,
+ "Nevermind.", L_close;
+
+L_UndeadDebugFour:
+ mes "Reset your self to the various states.";
+ mes "Options Limited to Time and Place.";
+ menu
+ "Get Ritual Items.", L_RazhaItems,
+ "Nevermind.", L_close;
+
+L_UndeadDebugFive:
+ mes "Reset your self to the various states.";
+ mes "Options Limited to Time and Place.";
+ menu
+ "Get Ritual Items.", L_TeroganItems,
+ "Nevermind.", L_close;
+
+L_InventoryNoSpace:
+ mes "\"Drop some weight then come back.\"";
+ goto L_close;
+
+L_KrukanItems:
+ if ((checkweight("Soul", 1) == 0)
+ || (checkweight("Skull", 5) == 0)
+ || (checkweight("DarkCrystal", 5) == 0)
+ || (checkweight("Bone", 5) == 0)
+ || (@inventorylist_count == 100))
+ goto L_InventoryNoSpace;
+ getitem "Soul", 1;
+ getitem "Skull", 5;
+ getitem "DarkCrystal", 5;
+ getitem "Bone", 5;
+ goto L_Main;
+
+L_RazhaItems:
+ if ((checkweight("Soul", 3) == 0)
+ || (checkweight("DiseasedHeart", 5) == 0)
+ || (checkweight("UndeadEye", 5) == 0)
+ || (checkweight("UndeadEar", 5) == 0)
+ || (@inventorylist_count == 100))
+ goto L_InventoryNoSpace;
+ getitem "Soul", 3;
+ getitem "DiseasedHeart", 5;
+ getitem "UndeadEye", 5;
+ getitem "UndeadEar", 5;
+ goto L_Main;
+
+L_TeroganItems:
+ if ((checkweight("Soul", 5) == 0)
+ || (checkweight("RottenRags", 5) == 0)
+ || (checkweight("UndeadEye", 5) == 0)
+ || (checkweight("UndeadEar", 5) == 0)
+ || (@inventorylist_count == 100))
+ goto L_InventoryNoSpace;
+ getitem "Soul", 5;
+ getitem "RottenRags", 5;
+ getitem "UndeadEye", 5;
+ getitem "UndeadEar", 5;
+ goto L_Main;
+
+L_close:
+ close2;
+ return;
+}
+
+027-3,84,89,0 script UndeadDebug3 NPC155,{
+ @undeaddebug = 3;
+ callfunc "UndeadDebug";
+ end;
+OnInit:
+ if (!debug)
+ disablenpc "UndeadDebug3";
+ end;
+}
+
+027-4,76,79,0 script UndeadDebug4 NPC155,{
+ @undeaddebug = 4;
+ callfunc "UndeadDebug";
+ end;
+OnInit:
+ if (!debug)
+ disablenpc "UndeadDebug4";
+ end;
+}
+
+027-5,72,26,0 script UndeadDebug5 NPC155,{
+ @undeaddebug = 5;
+ callfunc "UndeadDebug";
+ end;
+OnInit:
+ if (!debug)
+ disablenpc "UndeadDebug5";
+ end;
+}
diff --git a/npc/functions/vault.txt b/npc/functions/vault.txt
new file mode 100644
index 00000000..b02d113b
--- /dev/null
+++ b/npc/functions/vault.txt
@@ -0,0 +1,114 @@
+// TMW-2 Script
+// Author:
+// Jesusalva
+// Description:
+// Vault Utilities
+
+function script getvaultid {
+ // FIXME: Make this False
+ if (debug || !debug)
+ return ##VAULT;
+ else
+ return 0;
+}
+
+function script getvaultexp {
+ .@exp=getarg(0);
+ if (.@exp > 100)
+ Exception("ILLEGAL VAULT EXPERIENCE, FIXME URGENTLY. STOPPING SCRIPT BY FORCE WHILE DOING NOTHING.",
+ RB_DEBUGMES | RB_IRCBROADCAST | RB_GLOBALANNOUNCE | RB_ISFATAL);
+ if (getvaultid()) {
+ ##VAULT_EXP+=.@exp;
+ debugmes("Granting %d Soul Exp to %d under Jande's authority.",
+ .@exp, ##VAULT);
+ }
+ return;
+}
+
+function script vaultOnLogin {
+ // Mirror Lake functionality
+ if (getvaultid() && !getstatus(SC_JAILED)) {
+ .@gto=get_byte(##00_INFO, 3);
+ .@mlp=get_nibble(##00_INFO, 5);
+ // Work only on new chars, or chars which cleared Tulimshar.
+ if (.@gto == WORLD_ID) {
+ // Warp to the proper Mirror Lake
+ switch (.@mlp) {
+ //case 1: somewhere
+ default: warp "013-2", 37, 23; LOCATION$ = "Hurns"; break;
+ }
+
+ // Send debug information
+ debugmes("Vault User %d moved to lake %d.", getvaultid(), .@mlp);
+
+ // Handle new user (non-native) accounts - automatic tutorial skip
+ if (QL_BEGIN < 8) {
+ if (!TUT_var) {
+ callfunc "GameRules";
+ next;
+ closeclientdialog;
+ adddefaultskills();
+ getitem Knife, 1;
+ getitem SlingShot, 1;
+ getitem SlingBullet, 500;
+ getitem HitchhikersTowel, 1;
+ getitem CottonShirt, 1;
+ getitem RaggedShorts, 1;
+ set Zeny, Zeny + 35; // tanisha gives 5 Zeny
+ equip(CottonShirt);
+ equip(RaggedShorts);
+ equip(Knife);
+ }
+ QL_BEGIN = 8;
+ dispbottom l("Mirror Lake : Obtain help with Sorfina in Candor.");
+ }
+
+ // Unset the target lake/world
+ set_byte(##00_INFO, 3, 0);
+ set_nibble(##00_INFO, 5, 0);
+ } else if (.@gto) {
+ // Heading somewhere which is not here!
+ mesc l("WARNING: If you use any Mirror Lake feature on this world, the current Mirror Lake Quest will be marked as \"Failed\"."), 1;
+ mesc l("If this is undesired, select the correct world, and if needed create a new char on it."), 1;
+ ##VAULT_GOTO=.@gto;
+ ##VAULT_MLTO=.@mlp;
+ next;
+ closeclientdialog;
+ }
+ }
+ return;
+}
+
+function script vaultOnLogout {
+ // Send updates to Vault API
+ if (getvaultid()) {
+ .@api$=json_encode("UID", ##VAULT,
+ "GID", getcharid(3),
+ "VAR1N", "TMWQUEST",
+ "VAR1V", ##03_TMWQUEST,
+ "VAR2N", "TMWGLOBAL",
+ "VAR2V", ##03_TMWGLOBAL,
+ "VEXP", ##VAULT_EXP,
+ "GOTO", ##VAULT_GOTO,
+ "MLTO", ##VAULT_MLTO);
+ ##VAULT_EXP=0;
+ ##VAULT_GOTO=0;
+ ##VAULT_MLTO=0;
+ api_send(API_FLUSHVAULT, .@api$);
+ }
+ return;
+}
+
+// MirrorLakeSendTo(World, Lake)
+function script MirrorLakeSendTo {
+ .@w=getarg(0);
+ .@t=getarg(1);
+ ##VAULT_GOTO=.@w;
+ ##VAULT_MLTO=.@t;
+ closeclientdialog;
+ dispbottom l("Darkness fills your vision...");
+ sleep2(1000);
+ kick(getcharid(3), 7); // 7 is not a valid kick reason
+ end;
+}
+
diff --git a/npc/functions/water_bottle.txt b/npc/functions/water_bottle.txt
new file mode 100644
index 00000000..bde12a0f
--- /dev/null
+++ b/npc/functions/water_bottle.txt
@@ -0,0 +1,43 @@
+
+function script WaterBottle {
+ @COST_PER_BOTTLE = 150;
+
+ mes "How many empty bottles do you want to fill with water? It costs " + @COST_PER_BOTTLE + "gp per bottle.";
+ input @count;
+
+ if (@count == 0)
+ goto L_close;
+ @Cost = @count * @COST_PER_BOTTLE;
+ @empty = countitem("EmptyBottle");
+
+ if (@empty < @count)
+ goto L_NotEnoughBottles;
+ if (Zeny < @Cost)
+ goto L_NotEnoughMoney;
+ getinventorylist;
+ if (@inventorylist_count == 100
+ && countitem("BottleOfWater") == 0
+ && @empty > @count)
+ goto L_NotEnoughSlots;
+
+ Zeny = Zeny - @Cost;
+ delitem "EmptyBottle", @count;
+ getitem "BottleOfWater", @count;
+ goto L_close;
+
+L_NotEnoughBottles:
+ mes "You don't have that many empty bottles!";
+ goto L_close;
+
+L_NotEnoughMoney:
+ mes "You don't have enough gp! You need " + @Cost + "gp.";
+ goto L_close;
+
+L_NotEnoughSlots:
+ mes "You don't have room for these bottles!";
+ goto L_close;
+
+L_close:
+ close2;
+ return;
+}
diff --git a/npc/functions/weather.txt b/npc/functions/weather.txt
new file mode 100644
index 00000000..75605749
--- /dev/null
+++ b/npc/functions/weather.txt
@@ -0,0 +1,244 @@
+// TMW2 scripts.
+// Authors:
+// Jesusalva
+// Description:
+// Controls world seasons. RESPECT MASK_* VARS ON CONSTANTS DB
+
+- script #WeatherCore NPC_HIDDEN,{
+ end;
+
+OnInit:
+ // This is weather startup
+ .@init=true;
+ // Bind commands
+ bindatcmd "wsnow", "#WeatherCore::OnSnow", 80, 80, 1;
+ bindatcmd "wrain", "#WeatherCore::OnRain", 80, 80, 1;
+ bindatcmd "wsand", "#WeatherCore::OnSand", 80, 80, 1;
+ bindatcmd "wevil", "#WeatherCore::OnEvil", 80, 80, 1;
+ bindatcmd "wnight", "#WeatherCore::OnNight", 80, 80, 1;
+ bindatcmd "wclear", "#WeatherCore::OnClear", 80, 80, 1;
+ bindatcmd "wreset", "#WeatherCore::OnReset", 99, 99, 1;
+ bindatcmd "wset", "#WeatherCore::OnManual", 99, 99, 1;
+
+
+ // Determine which maps are subject to weather, and how weather works:
+ // eg. it should never snow on a desert, or happen a sandstorm on icelands.
+ .wcore = htnew;
+ .wtime = htnew;
+
+ // Deserts
+ htput(.wcore, "001-1", CLIMATE_DESERT);
+ htput(.wcore, "002-1", CLIMATE_DESERT);
+ htput(.wcore, "003-2", CLIMATE_DESERT);
+ htput(.wcore, "004-1", CLIMATE_DESERT);
+ htput(.wcore, "006-1", CLIMATE_DESERT);
+ htput(.wcore, "023-1", CLIMATE_DESERT);
+ htput(.wcore, "041-1", CLIMATE_DESERT);
+ htput(.wcore, "042-1", CLIMATE_DESERT);
+ htput(.wcore, "043-1", CLIMATE_DESERT);
+
+ // Woodlands
+ htput(.wcore, "007-1", CLIMATE_MODERATE);
+ htput(.wcore, "008-1", CLIMATE_MODERATE);
+ htput(.wcore, "009-1", CLIMATE_MODERATE);
+ htput(.wcore, "010-1", CLIMATE_MODERATE);
+ htput(.wcore, "011-1", CLIMATE_MODERATE);
+ htput(.wcore, "012-1", CLIMATE_MODERATE);
+ htput(.wcore, "013-1", CLIMATE_MODERATE);
+ htput(.wcore, "014-1", CLIMATE_MODERATE);
+ htput(.wcore, "015-1", CLIMATE_MODERATE);
+ htput(.wcore, "016-1", CLIMATE_MODERATE);
+ htput(.wcore, "017-1", CLIMATE_MODERATE);
+ htput(.wcore, "018-1", CLIMATE_MODERATE);
+ htput(.wcore, "025-1", CLIMATE_MODERATE);
+ htput(.wcore, "026-1", CLIMATE_MODERATE);
+ htput(.wcore, "027-1", CLIMATE_MODERATE);
+ htput(.wcore, "028-1", CLIMATE_MODERATE);
+ htput(.wcore, "029-1", CLIMATE_MODERATE);
+ htput(.wcore, "051-1", CLIMATE_MODERATE); // ?
+ htput(.wcore, "052-1", CLIMATE_MODERATE);
+ htput(.wcore, "055-1", CLIMATE_MODERATE);
+ htput(.wcore, "057-1", CLIMATE_MODERATE);
+
+ // Icelands
+ htput(.wcore, "019-1", CLIMATE_ICELAND);
+ htput(.wcore, "020-1", CLIMATE_ICELAND);
+ htput(.wcore, "030-1", CLIMATE_ICELAND);
+ htput(.wcore, "031-1", CLIMATE_ICELAND);
+ htput(.wcore, "033-1", CLIMATE_ICELAND);
+ htput(.wcore, "034-1", CLIMATE_ICELAND);
+ htput(.wcore, "045-1", CLIMATE_ICELAND);
+ htput(.wcore, "046-1", CLIMATE_ICELAND);
+ htput(.wcore, "047-1", CLIMATE_ICELAND);
+
+ // Special
+ htput(.wcore, "099-1", CLIMATE_NONE);
+
+
+ debugmes("[Weather.sys] Total Maps = " + htsize(.wcore));
+ initnpctimer;
+ end;
+
+ ///////////////////////////////////////////////////////////////////
+ // Internal functions to check or change stuff
+ // WeatherSwitch ( MASK )
+ function WeatherSwitch {
+ // Get map
+ .@key$=getmap();
+
+ // Sanitize
+ .@m = htget(.wcore, .@key$, CLIMATE_NONE);
+
+ // Change Weather or abort
+ if (.@m == CLIMATE_NONE)
+ dispbottom l("Command not permitted on this map! Check npc/functions/weather.conf");
+ else
+ addmapmask(.@key$, getarg(0));
+ return;
+ }
+
+ // WeatherClear ( MAP )
+ function WeatherClear {
+ .@key$ = getarg(0);
+ .@mk=getmapmask(.@key$);
+ if (.@mk & MASK_NIGHT)
+ .@mk=.@mk^MASK_NIGHT;
+ if (.@mk & MASK_RAIN)
+ .@mk=.@mk^MASK_RAIN;
+ if (.@mk & MASK_SANDSTORM)
+ .@mk=.@mk^MASK_SANDSTORM;
+ if (.@mk & MASK_SNOW)
+ .@mk=.@mk^MASK_SNOW;
+ setmapmask(.@key$, .@mk);
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // "#WeatherCore"::climate(mapid)
+ public function climate {
+ .@v = htget(.wcore, getarg(0), CLIMATE_NONE);
+ return .@v;
+ }
+
+ // "#WeatherCore"::weather(weather{, mapid})
+ public function weather {
+ .@mk=getarg(0);
+ .@m$=getarg(1, getmapname());
+ return getmapmask(.@m$) & .@mk;
+ }
+
+ // "#WeatherCore"::weather_override(weather, duration{, mapid, override=false})
+ public function weather_override {
+ .@mk=getarg(0);
+ .@tm=getarg(1);
+ .@m$=getarg(2, getmapname());
+ .@force=getarg(3, false);
+
+ if (!.@force) {
+ // Obtain map climate & validate
+ .@cl = htget(.wcore, .@m$, CLIMATE_NONE);
+ if (.@cl == CLIMATE_NONE)
+ return false;
+
+ // Forbidden changes (Disallowed by climate)
+ if (.@cl == CLIMATE_DESERT && (.@mk == MASK_SNOW))
+ return false;
+ if (.@cl == CLIMATE_ICELAND && (.@mk == MASK_SANDSTORM))
+ return false;
+ }
+
+ // Rain is not granted in desert or iceland
+ // But "#WeatherCore"::climate() will have this handled
+ // So we assume it is OK here and apply the changes
+
+ // Maybe there's already a timer running?
+ .@t = htget(.wtime, .@m$, 0);
+ if (.@t <= 0) {
+ addmapmask(.@m$, .@mk);
+ htput(.wtime, .@m$, gettimetick(2)+.@tm);
+ } else {
+ htput(.wtime, .@m$, .@t+.@tm);
+ }
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Every 2.5 seconds, we clean up any ongoing weather effects
+OnTimer2500:
+ // No weather effect
+ if (!htsize(.wtime)) {
+ initnpctimer;
+ end;
+ }
+ // We got a job to do
+ .@hti = htiterator(.wtime);
+ .@cur = gettimetick(2);
+ for (.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti))
+ {
+ .@target=htget(.wtime, .@key$, 0);
+ debugmes "Map %s (Time %d/%d)", .@key$, .@target, .@cur;
+ // Not yet expired
+ if (.@target > .@cur)
+ continue;
+ // Remove the effect
+ htput(.wtime, .@key$, 0);
+ WeatherClear(.@key$);
+ }
+ htidelete(.@hti);
+ initnpctimer;
+ end;
+
+///////////////////////////////////////////////////////////////////
+// Some commands, for GMs manually override weather
+OnRain:
+ WeatherSwitch(MASK_RAIN);
+ end;
+
+OnSand:
+ WeatherSwitch(MASK_SANDSTORM);
+ end;
+
+OnSnow:
+ WeatherSwitch(MASK_SNOW);
+ end;
+
+OnNight:
+ WeatherSwitch(MASK_NIGHT);
+ end;
+
+OnEvil:
+ WeatherSwitch(MASK_EVILSANCTUM);
+ end;
+
+OnManual:
+ if (!.@atcmd_numparameters) {
+ dispbottom l("Syntax: @wset <map_mask>");
+ end;
+ }
+
+ // Never allow negative numbers, or to disable map mask 1 (never, EVER, do such insane thing)
+ .@rq = atoi(.@atcmd_parameters$[0]);
+ if (.@rq <= 1 || .@rq % 2 == 1) {
+ dispbottom l("Invalid map mask");
+ end;
+ }
+
+ // <Insert a helpful comment here>
+ getmapxy(.@key$,.@a,.@b,0);
+ .@mk=getmapmask(.@key$);
+ .@mk=.@mk^.@rq;
+ setmapmask(.@key$, .@mk);
+ end;
+
+// Clear works on any map
+OnClear:
+ WeatherClear(getmap());
+ end;
+
+// Reset the whole map, including season, event and weather masks
+OnReset:
+ getmapxy(.@key$,.@a,.@b,0);
+ setmapmask(.@key$, MASK_NONE);
+ end;
+
+}