summaryrefslogtreecommitdiff
path: root/npc/commands
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2021-04-09 11:00:49 -0300
committerJesusaves <cpntb1@ymail.com>2021-04-09 11:00:49 -0300
commit8a4bf716002a017de77fe7df301ef8e4aaf00a2e (patch)
tree4947d0b015baad639fa07133369e9a09c8a468bc /npc/commands
downloadserverdata-8a4bf716002a017de77fe7df301ef8e4aaf00a2e.tar.gz
serverdata-8a4bf716002a017de77fe7df301ef8e4aaf00a2e.tar.bz2
serverdata-8a4bf716002a017de77fe7df301ef8e4aaf00a2e.tar.xz
serverdata-8a4bf716002a017de77fe7df301ef8e4aaf00a2e.zip
Initial commit
Diffstat (limited to 'npc/commands')
-rw-r--r--npc/commands/bodytype.txt53
-rw-r--r--npc/commands/debug-look.txt83
-rw-r--r--npc/commands/debug-preset.txt253
-rw-r--r--npc/commands/debug-quest.txt217
-rw-r--r--npc/commands/debug-skill.txt94
-rw-r--r--npc/commands/debug.txt145
-rw-r--r--npc/commands/event.txt57
-rw-r--r--npc/commands/gm.txt41
-rw-r--r--npc/commands/mobinfo.txt29
-rw-r--r--npc/commands/motd.txt194
-rw-r--r--npc/commands/music.txt75
-rw-r--r--npc/commands/python.txt24
-rw-r--r--npc/commands/rate-management.txt107
-rw-r--r--npc/commands/resync.txt40
-rw-r--r--npc/commands/scheduled-broadcasts.txt227
-rw-r--r--npc/commands/super-menu.txt68
-rw-r--r--npc/commands/warp.txt91
-rw-r--r--npc/commands/zeny.txt98
18 files changed, 1896 insertions, 0 deletions
diff --git a/npc/commands/bodytype.txt b/npc/commands/bodytype.txt
new file mode 100644
index 00000000..290c6fd6
--- /dev/null
+++ b/npc/commands/bodytype.txt
@@ -0,0 +1,53 @@
+// @bodytype atcommand
+// changes or returns the body type
+
+- script @bodytype 32767,{
+ end;
+
+OnCall:
+ if (.@atcmd_parameters$[0] == "") {
+ dispbottom("Your current body type is " + bodytypeToString());
+ end;
+ }
+
+ .@desired = stringToBodytype(.@atcmd_parameters$[0]);
+
+ if (.@desired == BodyType) {
+ dispbottom("Your body type is already " + bodytypeToString());
+ } else {
+ BodyType = .@desired;
+ dispbottom("Body type changed to " + bodytypeToString());
+ }
+ end;
+
+OnInit:
+ bindatcmd("gender", "@bodytype::OnCall", 99, 99, false);
+ bindatcmd("bodytype", "@bodytype::OnCall", 99, 99, false);
+ bindatcmd("body", "@bodytype::OnCall", 99, 99, false);
+ bindatcmd("type", "@bodytype::OnCall", 99, 99, false);
+ bindatcmd("changesex", "@bodytype::OnCall", 99, 99, false);
+
+ add_group_command("gender", 40, true, false);
+ add_group_command("bodytype", 40, true, false);
+ add_group_command("body", 40, true, false);
+ add_group_command("type", 40, true, false);
+ add_group_command("changesex", 40, true, false);
+
+ add_group_command("gender", 50, true, false);
+ add_group_command("bodytype", 50, true, false);
+ add_group_command("body", 50, true, false);
+ add_group_command("type", 50, true, false);
+ add_group_command("changesex", 50, true, false);
+
+ add_group_command("gender", 60, true, false);
+ add_group_command("bodytype", 60, true, false);
+ add_group_command("body", 60, true, false);
+ add_group_command("type", 60, true, false);
+ add_group_command("changesex", 60, true, false);
+
+ add_group_command("gender", 80, true, false);
+ add_group_command("bodytype", 80, true, false);
+ add_group_command("body", 80, true, false);
+ add_group_command("type", 80, true, false);
+ add_group_command("changesex", 80, true, false);
+}
diff --git a/npc/commands/debug-look.txt b/npc/commands/debug-look.txt
new file mode 100644
index 00000000..0a4a953b
--- /dev/null
+++ b/npc/commands/debug-look.txt
@@ -0,0 +1,83 @@
+function script BarberDebug {
+ function setStyle {
+ clear;
+ setnpcdialogtitle l("Appearance Debug - Barber");
+ mes l("Hair style") + ": " + getlook(LOOK_HAIR);
+ next;
+ mes l("Please enter the desired style") + " (1-255)";
+ input .@h, 1, 0xFF;
+ setlook LOOK_HAIR, max(1, min(0xFF, .@h));
+ return;
+ }
+ function setColor {
+ clear;
+ setnpcdialogtitle l("Appearance Debug - Barber");
+ mes l("Hair color") + ": " + getlook(LOOK_HAIR_COLOR);
+ next;
+ mes l("Please enter the desired color") + " (0-255)";
+ input .@h, 0, 0xFF;
+ setlook LOOK_HAIR_COLOR, max(0, min(0xFF, .@h));
+ return;
+ }
+ function setRace {
+ clear;
+ setnpcdialogtitle l("Appearance Debug - Race");
+ mes l("Race") + ": " + Class + " (" + get_race(GETRACE_FULL) + ")";
+ next;
+ mes l("Please enter the desired race") + " (0-32767)";
+ input .@r, 0, 0x7FFF;
+ jobchange max(0, min(0x7FFF, .@r));
+ return;
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Appearance Debug");
+ mes l("This menu allows you to customize your appearance.");
+ mes "";
+
+ mes "---";
+ mes l("Body type") + ": " + bodytypeToString(BodyType);
+ mes l("Hair style") + ": " + getlook(LOOK_HAIR);
+ mes l("Hair color") + ": " + getlook(LOOK_HAIR_COLOR);
+ mes l("Race") + ": " + Class + " (" + get_race() + ")";;
+ mes "---";
+
+ next;
+ mes l("What do you want to change?");
+ select
+ menuimage("actions/edit", l("Body type") + " [" + l("Requires logout") + "]"),
+ menuimage("actions/edit", l("Hair style")),
+ menuimage("actions/edit", l("Hair color")),
+ menuimage("actions/edit", l("Race")),
+ rif(getarg(0,0), menuimage("actions/back", l("Return to Debug menu")));
+
+ switch (@menu)
+ {
+ case 1: BarberChangeBodyType(); break;
+ case 2: setStyle; break;
+ case 3: setColor; break;
+ case 4: setRace; break;
+ case 5: return;
+ }
+ } while (1);
+}
+
+
+
+- script @look 32767,{
+ end;
+
+OnCall:
+ if (!debug && !is_dev())
+ {
+ end;
+ }
+ BarberDebug;
+ closeclientdialog;
+ end;
+
+OnInit:
+ bindatcmd "look", "@look::OnCall", 0, 99, 0;
+}
diff --git a/npc/commands/debug-preset.txt b/npc/commands/debug-preset.txt
new file mode 100644
index 00000000..1fbad520
--- /dev/null
+++ b/npc/commands/debug-preset.txt
@@ -0,0 +1,253 @@
+// Preset / routine system
+// Author:
+// gumi
+// Description:
+// allows to execute multiple commands in a single step
+// to ease testing and debugging
+//
+// usage: @pre [options] <instruction>[, <instruction>...]
+// usage: DoRoutine "[options] <instruction>[, <instruction>...]";
+//
+// example: @pre -s a22 v14
+// resets all stats, gives 22 agi, gives 14 vit
+//
+// ^ actual documentation may come one day, when I feel like it
+// *hides*
+
+function script DoRoutine {
+
+ function parsev {
+ // parsev(base, patterns{, min{, max}}) => value
+ .@value = getarg(0, 0);
+ .@raw$ = getarg(1, "");
+ .@patterns = explode(.@patterns$, .@raw$, "|");
+
+ for (.@pattern = 0; .@pattern < .@patterns; ++.@pattern)
+ {
+ .@pattern$ = .@patterns$[.@pattern];
+ .@len = getstrlen(.@pattern$);
+
+ if (charat(.@pattern$, 0) == "=" && .@len >= 2)
+ {
+ .@value = atoi(delchar(.@pattern$, 0));
+ break;
+ }
+
+ else if (charat(.@pattern$, 0) == "+" && .@len >= 2)
+ {
+ if (charat(.@pattern$, 1) == "+" && getargcount() >= 4)
+ .@value = getarg(3);
+ else
+ .@value += atoi(delchar(.@pattern$, 0));
+ }
+
+ else if (charat(.@pattern$, 0) == "-" && .@len >= 2)
+ {
+ if (charat(.@pattern$, 1) == "-" && getargcount() >= 3)
+ .@value = getarg(2);
+ else
+ .@value -= atoi(delchar(.@pattern$, 0));
+ }
+
+ else if (charat(.@pattern$, 0) == ">" && .@len >= 2)
+ {
+ if (charat(.@pattern$, 1) == "=" && .@len >= 3)
+ .@value = max(atoi(substr(.@pattern$, 2, .@len - 1)), .@value);
+ else
+ .@value = max(atoi(delchar(.@pattern$, 0)) + 1, .@value);
+ }
+
+ else if (charat(.@pattern$, 0) == "<" && .@len >= 2)
+ {
+ if (charat(.@pattern$, 1) == "=" && .@len >= 3)
+ .@value = min(atoi(substr(.@pattern$, 2, .@len - 1)), .@value);
+ else
+ .@value = min(atoi(delchar(.@pattern$, 0)) - 1, .@value);
+ }
+
+ else if (.@len >= 1)
+ {
+ .@value = atoi(.@pattern$);
+ break;
+ }
+ }
+
+ if (getargcount() >= 3)
+ .@value = max(getarg(2), .@value);
+
+ if (getargcount() >= 4)
+ .@value = min(getarg(3), .@value);
+
+ return .@value;
+ }
+
+ .@routine$ = strip(getarg(0,""));
+ .@m = explode(.@routine$[0], .@routine$, " "); // prep the base array
+
+ if (charat(.@routine$[0], 0) == "-")
+ {
+ if (compare(.@routine$[0], "t"))
+ {
+ clearitem;
+ }
+
+ if (compare(.@routine$[0], "e"))
+ {
+ nude;
+ }
+
+ if (compare(.@routine$[0], "k"))
+ {
+ resetskill;
+ }
+
+ if (compare(.@routine$[0], "s"))
+ {
+ resetstatus;
+ }
+
+ if (compare(.@routine$[0], "x"))
+ {
+ resetlvl 2;
+ }
+
+ if (compare(.@routine$[0], "q"))
+ {
+ //doevent "::OnGlobalQuestReset"; // executes in all quest npcs // FIXME: maybe have a `resetquest` buildin?
+ // FIXME: ^ need a buildin that can run *right now* instead of on script end
+ }
+
+ if (compare(.@routine$[0], "n"))
+ {
+ closeclientdialog;
+ }
+
+ .@i = 1;
+ }
+
+ for (; .@i < .@m; ++.@i)
+ {
+ .@type = ord(charat(strip(.@routine$[.@i]), 0));
+
+ if (.@type > 0)
+ {
+ .@raw$ = delchar(.@routine$[.@i], 0);
+ .@args = explode(.@args$, .@raw$, ",");
+ .@a = atoi(.@args$[0]);
+ .@b = atoi(.@args$[1]);
+ .@c = atoi(.@args$[2]);
+ .@d = atoi(.@args$[3]);
+ .@e = atoi(.@args$[4]);
+
+ switch (.@type)
+ {
+ case 97: /* a => Agi */
+ .@base = readparam(bAgi);
+ statusup2 bAgi, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ case 98: /* b => body type */
+ .@desired_bt = max(1, min(3, .@a));
+ break;
+ case 99: /* c => job */
+ jobchange max(0, min(6, .@a));
+ break;
+ case 100: /* d => Dex */
+ .@base = readparam(bDex);
+ statusup2 bDex, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ case 101: /* e => equip */
+ equip max(1, min(32767, .@a));
+ break;
+ /* (f) */
+ /* (g) */
+ /* (h) */
+ case 105: /* i => Int */
+ .@base = readparam(bInt);
+ statusup2 bInt, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ /* (j) */
+ case 107: /* k => skill */
+ .@k = max(1, min(32767, .@a));
+ skill .@k, parsev(getskilllv(.@k), .@args$[1], 0, 10), 0;
+ break;
+ case 108: /* l => luk */
+ .@base = readparam(bLuk);
+ statusup2 bLuk, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ /* (m) => mercenary (reserved) */
+ /* (n) => talk to npc (reserved) */
+ /* (o) */
+ case 112: /* p => pet */
+ makepet max(1002, min(32767, .@a));
+ break;
+ case 113: /* q => quest */
+ .@q = max(0, min(32767, .@a));
+ setq .@q, parsev(getq(.@q), .@args$[1]),
+ parsev(getq2(.@q), .@args$[2]),
+ parsev(getq3(.@q), .@args$[3]),
+ parsev(getqtime(.@q), .@args$[4]);
+ break;
+ /* (r) */
+ case 115: /* s => Str */
+ .@base = readparam(bStr);
+ statusup2 bStr, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ case 116: /* t => item */
+ .@t = max(1, min(32767, .@a));
+ .@c = countitem(.@t);
+ .@v = parsev(.@c, .@args$[1], 0, 32767);
+ if (.@c > .@v)
+ delitem .@t, .@c - .@v;
+ else if (.@c < .@v)
+ getitem .@t, .@v - .@c;
+ break;
+ /* (u) */
+ case 118: /* v => vit */
+ .@base = readparam(bVit);
+ statusup2 bVit, parsev(.@base, .@args$[0], 1, 99) - .@base;
+ break;
+ case 119: /* w => warp */
+ warp .@args$[0], .@b, .@c;
+ break;
+ case 120: /* x => base level */
+ BaseLevel = parsev(BaseLevel, .@args$[0], 1, 99);
+ BaseExp = parsev(BaseExp, .@args$[1], 0);
+ break;
+ case 121: /* y => job level */
+ JobLevel = parsev(JobLevel, .@args$[0], 1, 255);
+ JobExp = parsev(JobExp, .@args$[1], 0);
+ break;
+ case 122: /* z => zeny */
+ Zeny = parsev(Zeny, .@args$[0], 0, 0x7FFFFFFE);
+ BankVault = parsev(BankVault, .@args$[1], 0, MAX_BANK_ZENY);
+ break;
+ }
+ }
+ }
+
+ if (.@desired_bt && BodyType != .@desired_bt) {
+ sleep2(500);
+ BodyType = .@desired_bt; // this forces a logout so we must be it last
+ }
+
+ return;
+}
+
+- script @pre 32767,{
+ end;
+
+OnCall:
+ if (!debug && !is_admin())
+ {
+ end;
+ }
+ if (.@atcmd_parameters$[0] != "")
+ {
+ .@atcmd_parameters$[0] = implode(.@atcmd_parameters$[0], " ");
+ }
+ DoRoutine strip(.@atcmd_parameters$[0]);
+ end;
+
+OnInit:
+ bindatcmd "pre", "@pre::OnCall", 0, 99, 0;
+}
diff --git a/npc/commands/debug-quest.txt b/npc/commands/debug-quest.txt
new file mode 100644
index 00000000..580173d0
--- /dev/null
+++ b/npc/commands/debug-quest.txt
@@ -0,0 +1,217 @@
+function script GlobalQuestDebug {
+
+ function qDebugShip {
+ function qDebugGugli {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug") + " - " + l("Prologue") + " - Gugli";
+ mes l("This menu gives access to quest debug menus for @@ quest subquests.", "Gugli");
+ next;
+ mes l("Please select a quest:");
+
+ menuint
+ menuimage("actions/back", l("Go back")), -1,
+ l("Gugli (main quest)"), ShipQuests_Gugli,
+ "Ale", ShipQuests_Ale,
+ "Astapolos", ShipQuests_Astapolos,
+ "Gulukan", ShipQuests_Gulukan,
+ "Jalad", ShipQuests_Jalad,
+ "Q'Muller", ShipQuests_QMuller,
+ "Tibbo", ShipQuests_Tibbo;
+
+ switch (@menuret)
+ {
+ case -1: return;
+ default: callfunc "QuestDebug" + @menuret;
+ }
+ } while (1);
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug") + " - " + l("Prologue");
+ mes l("This menu gives access to quest debug menus for @@ quests.", l("Prologue"));
+ next;
+ mes l("Please select a quest:");
+
+ menuint
+ menuimage("actions/back", l("Go back")), -1,
+ "Julia", ShipQuests_Julia,
+ "Arpan", ShipQuests_Arpan,
+ "Alige", ShipQuests_Alige,
+ "Peter", ShipQuests_Peter,
+ "Nard", ShipQuests_Nard,
+ l("Knife"), ShipQuests_Knife,
+ l("Money"), ShipQuests_ArpanMoney,
+ l("Door"), ShipQuests_Door,
+ "Couwan", ShipQuests_Couwan,
+ l("Treasure Chest"), ShipQuests_TreasureChest,
+ "Gugli", ShipQuests_Gugli,
+ "Gado", ShipQuests_ChefGado;
+
+ switch (@menuret)
+ {
+ case 16: qDebugGugli; break;
+ case -1: return;
+ default: callfunc "QuestDebug" + @menuret;
+ }
+ } while (1);
+ }
+
+ function qDebugArtis {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug") + " - " + l("Artis");
+ mes l("This menu gives access to quest debug menus for @@ quests.", "Artis");
+ next;
+ mes l("Please select a quest:");
+
+ menuint
+ menuimage("actions/back", l("Go back")), -1,
+ l("Lazy Brother"), ArtisQuests_LazyBrother,
+ l("Urchin"), ArtisQuests_Urchin,
+ l("Catching a piou"), ArtisQuests_CatchPiou,
+ "Eugene (" + l("fisherman") + ")", ArtisQuests_Fishman,
+ "Q'Onan", ArtisQuests_QOnan,
+ "Enora", ArtisQuests_Enora,
+ "Fexil", ArtisQuests_Fexil,
+ "Lloyd", ArtisQuests_Lloyd,
+ l("Mona's dad"), ArtisQuests_MonaDad,
+ l("Artis legion progress"), Artis_Legion_Progress,
+ l("Legion training"), ArtisQuests_TrainingLegion,
+ "Henry", ThiefQuests_Artis;
+
+ switch (@menuret)
+ {
+ case -1: return;
+ default: callfunc "QuestDebug" + @menuret;
+ }
+ } while (1);
+ }
+
+ function qDebugHurnscald {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug") + " - Hurnscald";
+ mes l("This menu gives access to quest debug menus for @@ quests.", "Hurnscald");
+ next;
+ mes l("Please select a quest:");
+
+ menuint
+ menuimage("actions/back", l("Go back")), -1,
+ "Hinnak", HurnscaldQuests_Hinnak,
+ l("Maggot soup"), HurnscaldQuests_Soup,
+ l("Inspector"), HurnscaldQuests_Inspector,
+ l("Forest bow"), HurnscaldQuests_ForestBow,
+ l("Wooden shield"), HurnscaldQuests_WoodenShield,
+ "Kfahr", HurnscaldQuests_Kfahr,
+ "Galimatia", ArgaesQuest_Galimatia,
+ "Rossy", HurnscaldQuests_Rossy;
+
+ switch (@menuret)
+ {
+ case -1: return;
+ default: callfunc "QuestDebug" + @menuret;
+ }
+ } while (1);
+ }
+
+ function qDebugGeneral {
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug") + " - " + l("Other");
+ mes l("This menu gives access to quest debug menus for @@ quests.", strtolower(l("Other")));
+ next;
+ mes l("Please select a quest:");
+
+ menuint
+ menuimage("actions/back", l("Go back")), -1,
+ "Hal", General_CptHal,
+ l("Cookies"), General_Cookies,
+ "Rumly", General_Rumly,
+ l("Narrator"), General_Narrator,
+ "Janus", General_Janus,
+ l("Cooking"), General_Cooking,
+ l("Brotherhood"), General_Brotherhood;
+
+ switch (@menuret)
+ {
+ case -1: return;
+ default: callfunc "QuestDebug" + @menuret;
+ }
+ } while (1);
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Quest debug");
+ mes l("This menu gives access to all quest debug menus.");
+ next;
+ mes l("Please select a category:");
+
+ select
+ l("Prologue"),
+ "Artis",
+ "Hurnscald",
+ l("Other"),
+ rif(getarg(0,0), menuimage("actions/back", l("Return to Debug menu")));
+
+ switch (@menu)
+ {
+ case 1: qDebugShip; break;
+ case 2: qDebugArtis; break;
+ case 3: qDebugHurnscald; break;
+ case 4: qDebugGeneral; break;
+ case 5: return;
+ }
+ } while (1);
+}
+
+
+
+- script @qdebug 32767,{
+ end;
+
+OnCall:
+ if (!debug && !is_trusted())
+ {
+ end;
+ }
+ GlobalQuestDebug;
+ closeclientdialog;
+ end;
+
+OnSetq:
+ if (.@atcmd_numparameters < 2) {
+ dispbottom "setq called with invalid arguments (min. 2)";
+ dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>";
+ end;
+ }
+ .@q=atoi(.@atcmd_parameters$[0]);
+ switch (.@atcmd_numparameters) {
+ case 4:
+ setq3 .@q, atoi(.@atcmd_parameters$[3]);
+ case 3:
+ setq2 .@q, atoi(.@atcmd_parameters$[2]);
+ case 2:
+ setq1 .@q, atoi(.@atcmd_parameters$[1]);
+ dispbottom l("Quest %s modified by GM", getquestlink(.@q));
+ specialeffect 54, SELF, playerattached();
+ break;
+ default:
+ dispbottom "setq called with invalid arguments (max. 4)";
+ dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>";
+ break;
+ }
+ end;
+
+OnInit:
+ bindatcmd "qdebug", "@qdebug::OnCall", 0, 99, 0;
+ bindatcmd "setq", "@qdebug::OnSetq", 99, 99, 1;
+}
diff --git a/npc/commands/debug-skill.txt b/npc/commands/debug-skill.txt
new file mode 100644
index 00000000..2a3ef551
--- /dev/null
+++ b/npc/commands/debug-skill.txt
@@ -0,0 +1,94 @@
+function script GlobalSkillDebug {
+
+ function modifySkill {
+
+ function setSkill {
+ clear;
+ mes l("Enter desired skill level:");
+ input .@y;
+ skill getarg(0,1), max(0, min(10, .@y)), 0;
+ return;
+ }
+
+ setnpcdialogtitle l("Skill Debug - Modify Skill");
+ .@k = getarg(0,1);
+ .@v = getskilllv(.@k);
+ select
+ menuimage("actions/abort", l("Abort")),
+ rif(.@v > 0, menuimage("actions/remove", l("Remove this skill"))),
+ rif(.@v < 10, menuimage("actions/raise", l("Raise this skill"))),
+ rif(.@v > 0, menuimage("actions/lower", l("Lower this skill"))),
+ menuimage("actions/edit", l("Set the level manually")),
+ menuimage("actions/back", l("Return to skill debug menu"));
+
+ switch (@menu)
+ {
+ case 2: skill .@k, 0, 0; break;
+ case 3: skill .@k, min(10, .@v + 1), 0; break;
+ case 4: skill .@k, max(0, .@v - 1), 0; break;
+ case 5: setSkill .@k; break;
+ }
+
+ return;
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Skill Debug");
+ mes l("This menu allows you to change your skills.");
+ mes "";
+
+ mes "---";
+
+ .@size = getarraysize(getvariableofnpc(.debug_skills$, "@sdebug"));
+
+ for (.@s = 0; .@s < .@size; ++.@s) {
+ .@skill$ = getvariableofnpc(.debug_skills$[.@s], "@sdebug");
+ mesf("%s: %d", .@skill$, getskilllv(string_to_data(.@skill$)));
+ }
+
+ mes "---";
+
+ mes "";
+ mes l("Which skill do you wish to change?");
+ next;
+
+ .@select$ = implode(getvariableofnpc(.debug_skills$, "@sdebug"), ":");
+
+ if (getarg(0, 0)) {
+ .@select$ += ":" + menuimage("actions/back", l("Return to Debug menu"));
+ }
+
+ select(.@select$);
+
+ if (--@menu == .@size) {
+ return;
+ } else {
+ .@skill$ = getvariableofnpc(.debug_skills$[@menu], "@sdebug");
+ modifySkill(string_to_data(.@skill$));
+ }
+ } while (1);
+}
+
+
+
+- script @sdebug 32767,{
+ end;
+
+OnCall:
+ if (!debug && !is_admin())
+ {
+ end;
+ }
+ GlobalSkillDebug;
+ closeclientdialog;
+ end;
+
+OnInit:
+ setarray(.debug_skills$,
+ "NV_BASIC",
+ "EVOL_CRAFTING");
+
+ bindatcmd "sdebug", "@sdebug::OnCall", 0, 99, 0;
+}
diff --git a/npc/commands/debug.txt b/npc/commands/debug.txt
new file mode 100644
index 00000000..8cc1f1cd
--- /dev/null
+++ b/npc/commands/debug.txt
@@ -0,0 +1,145 @@
+function script GlobalDebugMenu {
+ function resetAll {
+ function doReset {
+ resetstatus;
+ resetskill;
+ resetlvl 1;
+ dispbottom l("Reset done!");
+ if (getarg(0,0) == 3)
+ {
+ closeclientdialog;
+ doevent "::OnGlobalQuestReset"; // executes in all quest npcs // FIXME: maybe have a `resetquest` buildin?
+ Zeny = 0;
+ BankVault = 0;
+ clearitem;
+ warp "000-0", 0, 0; // starting point
+ end; // script must end for doevent to execute
+ }
+ return;
+ }
+ clear;
+ setnpcdialogtitle l("Debug - Reset");
+ mes l("What do you want to reset?");
+ select
+ menuimage("actions/abort", l("Abort")),
+ menuimage("actions/reset", l("Reset stats, skills, level")),
+ menuimage("actions/nuke", l("Reset EVERYTHING")),
+ menuimage("actions/back", l("Return to Debug menu"));
+
+ switch (@menu)
+ {
+ case 2:
+ case 3: doReset @menu;
+ }
+
+ return;
+ }
+
+ function changeLevel {
+ clear;
+ setnpcdialogtitle l("Debug - Change level");
+ mes l("To change your base level, use this command:");
+ mes "";
+ mes " " + col("@blvl <" + l("delta") + ">", 3);
+ next;
+ mes l("Example:");
+ mes "@blvl 50";
+ mes " " + l("Raises your base level by 50");
+ mes "@blvl -50";
+ mes " " + l("Reduces your base level by 50");
+ next;
+ mes l("To change your job level, use this command:");
+ mes "";
+ mes " " + col("@jlvl <" + l("delta") + ">", 3);
+ next;
+ mes l("Example:");
+ mes "@jlvl 50";
+ mes " " + l("Raises your job level by 50");
+ mes "@jlvl -50";
+ mes " " + l("Reduces your job level by 50");
+ next;
+ return;
+ }
+ function changeStats {
+ clear;
+ setnpcdialogtitle l("Debug - Change stats");
+ mes l("To change your stats, use these commands:");
+ mes "";
+ mes " " + col("@str <" + l("delta") + ">", 3);
+ mes " " + col("@agi <" + l("delta") + ">", 3);
+ mes " " + col("@vit <" + l("delta") + ">", 3);
+ mes " " + col("@int <" + l("delta") + ">", 3);
+ mes " " + col("@dex <" + l("delta") + ">", 3);
+ mes " " + col("@luk <" + l("delta") + ">", 3);
+ next;
+ mes l("Example:");
+ mes "@int 50";
+ mes " " + l("Raises your Int by 50");
+ mes "@int -50";
+ mes " " + l("Reduces your Int by 50");
+ next;
+ mes l("If you simply wish to get 99 in all stats:");
+ mes "";
+ mes " " + col("@allstats", 3);
+ next;
+ mes l("If you wish to reset your stats:");
+ mes "";
+ mes " " + col("@streset", 3);
+ next;
+ return;
+ }
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Debug");
+ mes l("This menu allows you to modify your account data.");
+ mes "";
+ mes l("What do you want to do?");
+ select
+ menuimage("actions/manage", l("Change my level")),
+ menuimage("actions/manage", l("Change my stats")),
+ menuimage("actions/manage", l("Change my skills")),
+ menuimage("actions/manage", l("Change my appearance")),
+ menuimage("actions/add", l("Create items")),
+ menuimage("actions/add", l("Get money")),
+ menuimage("actions/edit", l("Change my quests")),
+ rif(debug, menuimage("actions/reset", l("Set Legacy Account"))),
+ menuimage("actions/reset", l("Reset")),
+ rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu")));
+
+ .@c = getarg(0,0) ? 2 : 1;
+
+ switch (@menu)
+ {
+ case 1: changeLevel; break;
+ case 2: changeStats; break;
+ case 3: GlobalSkillDebug .@c; break;
+ case 4: BarberDebug .@c; break;
+ case 5: closeclientdialog; clientcommand "createitems"; end;
+ case 6: Zeny = 0x7FFFFFFE; break;
+ case 7: GlobalQuestDebug .@c; break;
+ case 8: setfakelegacyaccount(); break;
+ case 9: resetAll; break;
+ case 10: return;
+ }
+ } while(1);
+}
+
+
+
+- script @debug 32767,{
+ end;
+
+OnCall:
+ if (!debug && !is_admin())
+ {
+ end;
+ }
+ GlobalDebugMenu;
+ closeclientdialog;
+ end;
+
+OnInit:
+ bindatcmd "debug", "@debug::OnCall", 0, 99, 0;
+ // TODO / FIXME: add a @test command that opens the help window for test-server
+}
diff --git a/npc/commands/event.txt b/npc/commands/event.txt
new file mode 100644
index 00000000..3f0d61de
--- /dev/null
+++ b/npc/commands/event.txt
@@ -0,0 +1,57 @@
+function script GlobalEventMenu {
+
+ function rateManagement {
+ clear;
+ mes l("To get the current rate:");
+ mes col(" @exprate", 7);
+ next;
+ mes l("To set the exp rate:");
+ mes col(" @exprate ##Brate##b hours", 7);
+ next;
+ mes l("To reset back to normal:");
+ mes col(" @exprate default", 7); // note to translators: any non-numerical value will reset so "default" is safe to translate
+ next;
+ return;
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Event Management");
+ mes l("This menu allows you to manage events and gives access to event-related tools.");
+ mes "";
+ mes l("What do you want to access?");
+
+ select
+ l("Rate management"),
+ rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu")));
+
+ //.@c = getarg(0,0) ? 2 : 1; // 1 = back to event menu, 2 = back to super menu
+
+ switch (@menu)
+ {
+ case 1: rateManagement; break;
+ default: return;
+ }
+
+ } while (true);
+}
+
+
+
+- script @event 32767,{
+ end;
+
+OnCall:
+ if (!is_evtc())
+ {
+ end;
+ }
+
+ GlobalEventMenu;
+ closeclientdialog;
+ end;
+
+OnInit:
+ bindatcmd "event", "@event::OnCall", 0, 99, 0;
+}
diff --git a/npc/commands/gm.txt b/npc/commands/gm.txt
new file mode 100644
index 00000000..b402cda8
--- /dev/null
+++ b/npc/commands/gm.txt
@@ -0,0 +1,41 @@
+// @showgm/@hidegm atcommand
+// TEMPORALY hides GM level (or revert it)
+//
+// group lv: 20+
+// group char lv: 99
+// log: False
+//
+// usage:
+// @showgm
+// #showgm "char" <delta>
+//
+
+- script @group 32767,{
+ end;
+
+OnHide:
+ .@gm=getgroupid();
+ if (.@gm < 20) end;
+ if (.@gm % 10 != 0) end;
+ setgroupid(.@gm+1);
+ dispbottom "hidelevel : "+l("Your GM level is now hidden.");
+ end;
+
+OnShow:
+ .@gm=getgroupid();
+ if (.@gm < 20) end;
+ if (.@gm % 10 != 1) end;
+ setgroupid(.@gm-1);
+ dispbottom "showlevel : "+l("Your GM level is now visible.");
+ end;
+
+OnInit:
+ bindatcmd "showgroup", "@group::OnShow", 20, 99, 0;
+ bindatcmd "showgm", "@group::OnShow", 20, 99, 0;
+ bindatcmd "showlevel", "@group::OnShow", 20, 99, 0;
+
+ bindatcmd "hidegroup", "@group::OnHide", 20, 99, 0;
+ bindatcmd "hidegm", "@group::OnHide", 20, 99, 0;
+ bindatcmd "hidelevel", "@group::OnHide", 20, 99, 0;
+ end;
+}
diff --git a/npc/commands/mobinfo.txt b/npc/commands/mobinfo.txt
new file mode 100644
index 00000000..691bfb68
--- /dev/null
+++ b/npc/commands/mobinfo.txt
@@ -0,0 +1,29 @@
+// The Mana World Script
+//
+// @monsterinfo <monsterAegis>
+// Sends @mobinfo with a delay (moved from atcommand.conf)
+// Requires EVOL_MONSTER_IDENTIFY
+//
+- script @monsterinfo 32767,{
+ end;
+
+OnCall:
+ // Check for skill level
+ if (!getskilllv(EVOL_MONSTER_IDENTIFY))
+ end;
+
+ // ...
+ if (@rsync_delay > gettimetick(2)) {
+ dispbottom l("Not doing that to prevent flood.");
+ end;
+ }
+
+ // Send @mobinfo and set a cooldown of 3 seconds.
+ atcommand("@mobinfo " + implode(.@atcmd_parameters$, " "));
+ @rsync_delay=gettimetick(2)+3;
+ end;
+
+OnInit:
+ bindatcmd "monsterinfo", "@monsterinfo::OnCall", 0, 60, 0;
+ end;
+}
diff --git a/npc/commands/motd.txt b/npc/commands/motd.txt
new file mode 100644
index 00000000..2f6a5e2a
--- /dev/null
+++ b/npc/commands/motd.txt
@@ -0,0 +1,194 @@
+function script displayMOTD {
+ .@size = getvariableofnpc(.size, "@motd");
+ .@dsize = getvariableofnpc(.dsize, "@motd");
+
+ // git stuff and such
+ if (debug)
+ {
+ for (.@i = 0; .@i < .@dsize; ++.@i)
+ {
+ dispbottom $@Debug_Messages$[.@i]; // FIXME: send this to the Debug tab instead
+ }
+
+ if (.@dsize > 0)
+ dispbottom "---";
+ }
+
+ // generic MOTD
+ for (.@i = 0; .@i < .@size; ++.@i)
+ {
+ dispbottom $MOTD_Messages$[.@i];
+ }
+
+ return;
+}
+
+function script MOTDConfig {
+
+ function toggleMOTD {
+ $MOTD_Disabled = !($MOTD_Disabled);
+ // FIXME: log to GM log
+ }
+
+ function addNewLine {
+ clear;
+ mes l("Please enter the new line.");
+ input .@s$;
+ .@s$ = strip(.@s$);
+ if (.@s$ != "")
+ {
+ .@size = getvariableofnpc(.size, "@motd");
+ $MOTD_Messages$[.@size] = .@s$;
+ set getvariableofnpc(.size, "@motd"), getarraysize($MOTD_Messages$);
+ // FIXME: log to GM log
+ }
+ }
+
+ function modifyLine {
+
+ function removeLine {
+ .@l = getarg(0);
+ deletearray $MOTD_Messages$[.@l], 1; // remove and shift
+ mes l("Line @@ has been removed.", .@l);
+ set getvariableofnpc(.size, "@motd"), getarraysize($MOTD_Messages$);
+ // FIXME: log to GM log
+ }
+
+ function moveUp {
+ .@l = getarg(0);
+ .@top$ = $MOTD_Messages$[.@l - 1];
+ $MOTD_Messages$[.@l - 1] = $MOTD_Messages$[.@l];
+ $MOTD_Messages$[.@l] = .@top$;
+ }
+
+ function moveDown {
+ .@l = getarg(0);
+ .@bottom$ = $MOTD_Messages$[.@l + 1];
+ $MOTD_Messages$[.@l + 1] = $MOTD_Messages$[.@l];
+ $MOTD_Messages$[.@l] = .@bottom$;
+ }
+
+ function editLine {
+ .@l = getarg(0);
+ clear;
+ mes l("Old line:");
+ mes "---";
+ mes $MOTD_Messages$[.@l];
+ mes "---";
+ mes "";
+ mes l("Enter new line:");
+ next;
+ input .@s$;
+ .@s$ = strip(.@s$);
+ if (.@s$ != "")
+ {
+ $MOTD_Messages$[.@l] = .@s$;
+ // FIXME: log to GM log
+ }
+ }
+
+ .@max = (getarg(0) - 1);
+
+ do
+ {
+ mes l("Enter line number:");
+ next;
+ input .@n;
+ if ($MOTD_Messages$[.@n] != "")
+ {
+ clear;
+ mes l("line @@: @@", .@n, $MOTD_Messages$[.@n]);
+ next;
+ select
+ menuimage("actions/back", l("Modify another line")),
+ menuimage("actions/remove", l("Remove this line")),
+ menuimage("actions/edit", l("Modify this line")),
+ rif(.@n > 0, menuimage("actions/raise", l("Move this line up"))),
+ rif(.@n < .@max, menuimage("actions/lower", l("Move this line down"))),
+ menuimage("actions/home", l("Return to main menu"));
+
+ switch (@menu)
+ {
+ case 2: removeLine .@n; return;
+ case 3: editLine .@n; return;
+ case 4: moveUp .@n; return;
+ case 5: moveDown .@n; return;
+ case 6: return;
+ }
+ }
+ } while (1);
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("MOTD Config");
+ mes l("This menu allows you to modify the generic message that is sent to players when they log in.");
+ mes "";
+
+ mes "---";
+ .@size = getvariableofnpc(.size, "@motd");
+ for (.@i = 0; .@i < .@size; ++.@i)
+ {
+ mes l("line @@: @@", .@i, $MOTD_Messages$[.@i]);
+ }
+ if (.@size == 0)
+ {
+ mes "(" + l("no active MOTD") + ")";
+ }
+ mes "---";
+ .@d = $MOTD_Disabled;
+ mes l("Enabled: @@", (.@d ? l("no") : l("yes")));
+ next;
+
+ select
+ menuimage("actions/toggle", (.@d ? l("Enable") : l("Disable"))),
+ menuimage("actions/add", l("Add a new line")),
+ rif(.@size, menuimage("actions/manage", l("Modify, move, or remove a line"))),
+ rif(.@size, menuimage("actions/test", l("Test MOTD"))),
+ rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu")));
+
+ switch (@menu)
+ {
+ case 1: toggleMOTD; break;
+ case 2: addNewLine; break;
+ case 3: modifyLine .@size; break;
+ case 4: displayMOTD; break;
+ default: return;
+ }
+ } while (1);
+}
+
+
+
+- script @motd 32767,{
+ end;
+
+OnCall:
+ if (!is_dev())
+ {
+ end;
+ }
+
+ MOTDConfig;
+ closeclientdialog;
+ end;
+
+OnInit:
+ MOTD_debug_text;
+ .size = getarraysize($MOTD_Messages$);
+ .dsize = getarraysize($@Debug_Messages$);
+ bindatcmd "motd", "@motd::OnCall", 0, 99, 0;
+}
+
+function script ReceiveMOTD {
+ if ($MOTD_Disabled < 1)
+ {
+ displayMOTD;
+ }
+ if (debug)
+ {
+ dispbottom "##7<<##B @@help://test-server|" + col(l("Click here for instructions on how to use the test server."),6) + "@@ ##7>>";
+ }
+ return;
+}
diff --git a/npc/commands/music.txt b/npc/commands/music.txt
new file mode 100644
index 00000000..b817b949
--- /dev/null
+++ b/npc/commands/music.txt
@@ -0,0 +1,75 @@
+// @music atcommand
+// changes the music for all players on the map
+//
+// group lv: 2
+// group char lv: 99
+// log: True
+//
+// usage:
+// @music <short name>
+//
+// example:
+// @music fun
+
+- script @music 32767,{
+ end;
+
+OnCall:
+ if (!is_evtc())
+ {
+ end;
+ }
+
+ // TODO: tmw-like argv splitter
+ .@map$ = getmap();
+
+ .@m$ = strtolower(.@atcmd_parameters$[0]);
+
+ // GMs might not know and want a list of musics
+ if (.@m$ == "" || .@m$ == "list") {
+ .@r$="list";
+
+ freeloop(true);
+ .@size=getarraysize($MUSIC_ARRAY$);
+ for (.@i = 0; .@i < .@size; ++.@i) {
+ .@r$+=", "+$MUSIC_ARRAY$[.@i];
+ }
+ freeloop(false);
+
+ dispbottom("Music list: "+.@r$);
+ end;
+ }
+
+ .@key = array_exists($MUSIC_ARRAY$, .@m$);
+
+ if (!.@key)
+ {
+ //.@m$ = implode(.@atcmd_parameters$[0], " ");
+ message(getcharid(CHAR_ID_ACCOUNT), sprintf("Can't broadcast: %s.ogg", .@m$));
+ } else {
+ message(getcharid(CHAR_ID_ACCOUNT), sprintf("Now broadcasting: %s", .@m$));
+ changemusic .@map$, .@m$ + ".ogg";
+ }
+ end;
+
+OnMyself:
+ .@m$ = strtolower(.@atcmd_parameters$[0]);
+ .@key = array_exists($MUSIC_ARRAY$, .@m$);
+
+ // TODO: Check if you have the music unlocked? Bitmask? Array?
+
+ if (!.@key)
+ {
+ //.@m$ = implode(.@atcmd_parameters$[0], " ");
+ message(getcharid(CHAR_ID_ACCOUNT), sprintf("Can't play: %s.ogg", .@m$));
+ } else {
+ message(getcharid(CHAR_ID_ACCOUNT), sprintf("Now playing: %s", .@m$));
+ changeplayermusic .@m$ + ".ogg";
+ }
+ end;
+
+OnInit:
+ bindatcmd "music", "@music::OnCall", 0, 99, 1;
+ bindatcmd "jukebox", "@music::OnMyself", 0, 50, 0;
+ end;
+}
diff --git a/npc/commands/python.txt b/npc/commands/python.txt
new file mode 100644
index 00000000..e2fdc5bf
--- /dev/null
+++ b/npc/commands/python.txt
@@ -0,0 +1,24 @@
+// The Mana World script
+// Author: Gumi <gumi@themanaworld.org>
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Stomp stomp stomp (use with caution)
+
+- script @python 32767,{
+ end;
+
+// Soft Resync
+OnCall:
+ specialeffect(69, AREA, playerattached());
+ addtimer 380, .name$+"::OnKill";
+ end;
+
+OnKill:
+ percentheal -100, -100;
+ //dispbottom l("Oh look, it is Cupid!");
+ end;
+
+OnInit:
+ bindatcmd "python", "@python::OnCall", 20, 20, 1;
+ end;
+}
diff --git a/npc/commands/rate-management.txt b/npc/commands/rate-management.txt
new file mode 100644
index 00000000..995ef940
--- /dev/null
+++ b/npc/commands/rate-management.txt
@@ -0,0 +1,107 @@
+- script @exprate 32767,{
+ end;
+
+ function rateCleanUp {
+ stopnpctimer;
+ .hours = 0;
+ .max_hours = 0;
+ .current_rate = .original_exp_rate;
+ setbattleflag("base_exp_rate", .original_exp_rate);
+ setbattleflag("quest_exp_rate", .original_quest_rate);
+ charcommand("@reloadmobdb"); // this is on purpose (callable without RID)
+ charcommand("@reloadquestdb");
+ }
+
+ function remainingTime {
+ .@total_seconds = (3600 * .max_hours);
+ .@seconds_elapsed = (3600 * .hours) + (getnpctimer(0) / 1000);
+ .@seconds_remaining = max(1, .@total_seconds - .@seconds_elapsed);
+ return FuzzyTime(time_from_seconds(.@seconds_remaining), 2, 2);
+ }
+
+OnCall:
+ if (!is_evtc())
+ {
+ end;
+ }
+
+ .@special$ = strip(.@atcmd_parameters$[0]); // special value
+ .@new_rate = min(atoi(.@special$), 1000); // or just a regular integer
+ .@hours = min(0x7FFFFFFE, max(1, atoi(strip(.@atcmd_parameters$[1])))); // number of hours
+
+ if (.@new_rate > 0)
+ {
+ // set new exp rate
+ .hours = 0;
+ .max_hours = .@hours;
+ .current_rate = .@new_rate;
+ setbattleflag("base_exp_rate", .@new_rate);
+ setbattleflag("quest_exp_rate", .@new_rate);
+ charcommand("@reloadmobdb");
+ charcommand("@reloadquestdb");
+ initnpctimer; // start counting
+
+ dispbottom l("You successfully set the exp rate to @@%. It will reset to @@% (default value) in @@.",
+ .@new_rate, .original_exp_rate, FuzzyTime(time_from_hours(.max_hours), 2, 2));
+ dispbottom l("You can also manually stop it at any time with: @exprate default");
+ }
+
+ else if (.@new_rate == 0 && .@special$ == "")
+ {
+ // get current exp rate
+ if (.current_rate == .original_exp_rate)
+ {
+ dispbottom l("Current exp rate is set to @@% (default value).", .current_rate);
+ }
+
+ else
+ {
+ dispbottom l("Current exp rate is set to @@%, and will reset to @@% (default value) in @@.",
+ .current_rate, .original_exp_rate, remainingTime());
+
+ dispbottom l("If you meant to reset the exp rate to its default value: @exprate default");
+ }
+ }
+
+ else
+ {
+ // reset
+ rateCleanUp;
+ dispbottom l("Exp rate has been reset to @@% (default value).",
+ .original_exp_rate);
+ }
+
+ end;
+
+OnTimer3600000:
+ // runs every hour
+ if (++.hours == .max_hours)
+ {
+ rateCleanUp;
+ end;
+ }
+ initnpctimer;
+ end;
+
+OnPCLoginEvent:
+ if (.max_hours > 0)
+ {
+ dispbottom col(l("Exp rate is set to @@% for the next @@.",
+ .current_rate, remainingTime()), 6);
+ }
+ end;
+
+OnInit:
+ bindatcmd "exprate", "@exprate::OnCall", 0, 99, 1; // change exp rate
+
+ // WARNING: using @reloadscript will change the "original" value
+ .original_exp_rate = getbattleflag("base_exp_rate");
+ .original_quest_rate = getbattleflag("quest_exp_rate");
+ .current_rate = .original_exp_rate;
+
+ // XXX: maybe in the future:
+ //.original_item_rate = getbattleflag("item_rate_common");
+ //.original_job_rate = getbattleflag("base_job_rate");
+ //.original_pk_mode = getbattleflag("pk_mode");
+ //.original_death_penalty = getbattleflag("death_penalty_type");
+}
diff --git a/npc/commands/resync.txt b/npc/commands/resync.txt
new file mode 100644
index 00000000..a535a343
--- /dev/null
+++ b/npc/commands/resync.txt
@@ -0,0 +1,40 @@
+// Evol script
+// Author: Jesusalva <admin@tmw2.org>
+//
+// Introduces @resync
+//
+// It'll cast slide to your own position
+// Hopefully making client update your real position without causing server warning
+
+- script @resync 32767,{
+ end;
+
+// Soft Resync
+OnCall:
+ // Sliding a dead player have undesired side effects
+ if (ispcdead()) {
+ dispbottom l("Impossible to resync: You are dead.");
+ end;
+ }
+ // I add a small delay in case slide() trigger some invincibility timer or something
+ if (@rsync_delay > gettimetick(2)) {
+ dispbottom l("Not resync'ing to prevent flood.");
+ end;
+ }
+
+ // This is a hack
+ getmapxy(.@m$, .@x, .@y, 0);
+ slide .@x, .@y;
+ @rsync_delay=gettimetick(2)+rand(4,6);
+
+ // Uncomment this line to cause server to resend every packet to ManaPlus
+ // and get rid of latency effect (like dead mobs still being shown)
+ //atcommand("@refresh");
+ end;
+
+// Anyone can call @resync, but only support and upwards for other players.
+// ie. GMs can try to fix lag for other people.
+OnInit:
+ bindatcmd "resync", "@resync::OnCall", 0, 20, 0;
+ end;
+}
diff --git a/npc/commands/scheduled-broadcasts.txt b/npc/commands/scheduled-broadcasts.txt
new file mode 100644
index 00000000..8699c581
--- /dev/null
+++ b/npc/commands/scheduled-broadcasts.txt
@@ -0,0 +1,227 @@
+function script StoneBoard {
+
+ function setMessage {
+ do
+ {
+ clear;
+ mes l("Please enter the message:");
+ next;
+ input .@msg$;
+ .@msg$ = strip(.@msg$);
+ if (.@msg$ != "")
+ {
+ return .@msg$;
+ }
+ mes l("The message cannot be empty");
+ next;
+ } while (1);
+ }
+
+ function setInterval {
+ clear;
+ mes l("Please select the interval:");
+ next;
+ menuint
+ l("Every 1 hour"), 1,
+ l("Every 3 hours"), 3,
+ l("Every 5 hours"), 5,
+ l("Every 6 hours"), 6,
+ l("Every 12 hours"), 12,
+ l("Every 24 hours"), 24,
+ l("Never (only on login)"), 0;
+
+ return @menuret;
+ }
+
+ function setMaxRep {
+ if (getarg(0,0) == 0)
+ {
+ return 0;
+ }
+ clear;
+ mes l("Repeat how many times?");
+ next;
+ menuint
+ l("Send only once"), 1,
+ l("Send 2 times"), 2,
+ l("Send 3 times"), 3,
+ l("Send 5 times"), 5,
+ l("Send 10 times"), 10,
+ l("Send 20 times"), 20,
+ l("Send indefinitely"), 0;
+
+ return @menuret;
+ }
+
+ function setOnLogin {
+ if (getarg(0,0) == 0)
+ {
+ return 1;
+ }
+ clear;
+ mes l("Send this message also on login?");
+ next;
+ select
+ l("No"),
+ l("Yes");
+
+ return (@menu - 1);
+ }
+
+ function newBroadcast {
+ do
+ {
+ setnpcdialogtitle l("Scheduled broadcasts - Create new");
+
+ // go through all steps
+ .@msg$ = setMessage();
+ .@int = setInterval();
+ .@max = setMaxRep(.@int);
+ .@login = setOnLogin(.@int);
+
+ // recap
+ clear;
+ mes l("Message:");
+ mes "---";
+ mes .@msg$;
+ mes "---";
+ if (.@int)
+ {
+ mes l("Interval: every @@ hour(s)", .@int);
+ mes l("Repeat: @@ times", .@max ? .@max : "∞");
+ mes l("Sent on login: @@", .@login ? l("yes") : l("no"));
+ }
+ else
+ {
+ mes l("Interval: (none, only sent on login)");
+ mes l("Sent on login: yes");
+ }
+
+ next;
+ select
+ menuimage("actions/cancel", l("Discard")),
+ menuimage("actions/edit", l("Start over")),
+ menuimage("actions/test", l("Start broadcasting")),
+ menuimage("actions/test", l("Start broadcasting, and make an extra broadcast right now"));
+
+ switch (@menu)
+ {
+ case 3:
+ case 4:
+ stopnpctimer "@sched";
+ $@SCHED_Opt[0] = .@login;
+ $@SCHED_Opt[1] = .@int;
+ $@SCHED_Opt[2] = 0;
+ $@SCHED_Opt[3] = .@max;
+ $@SCHED_Opt[4] = 0;
+ $@SCHED_Msg$ = .@msg$;
+ if (.@int)
+ {
+ initnpctimer "@sched";
+ }
+ if (@menu == 4)
+ {
+ announce $@SCHED_Msg$, bc_all;
+ }
+ case 1: return;
+ }
+
+ } while(1);
+ }
+
+ do
+ {
+ clear;
+ setnpcdialogtitle l("Scheduled broadcasts");
+ mes l("This menu allows you to set the scheduled broadcast that is sent to all players at a specific interval.");
+ mes "";
+
+ .@a = $@SCHED_Msg$ != ""; // any active broadcast?
+ mes "---";
+ mes .@a ? $@SCHED_Msg$ : "(" + l("no active broadcast") +")";
+ mes "---";
+ if (.@a)
+ {
+ mes l("Sent on login: @@", ($@SCHED_Opt[0] ? l("yes") : l("no")));
+ if ($@SCHED_Opt[1])
+ {
+ .@next = max(1, ((3600000 * ($@SCHED_Opt[1] - $@SCHED_Opt[4])) - getnpctimer(0, "@sched")));
+ mes l("Interval: every @@ hour(s)", $@SCHED_Opt[1]);
+ mes l("Next broadcast: @@", FuzzyTime(time_from_ms(.@next), 0, 99));
+ }
+ else
+ {
+ mes l("Interval: (none, only sent on login)");
+ mes l("Next broadcast: (never)");
+ }
+ mes l("Sent: @@ times out of @@", $@SCHED_Opt[2], ($@SCHED_Opt[3] ? $@SCHED_Opt[3] : "∞"));
+ }
+ next;
+
+ select
+ menuimage("actions/abort", l("Abort")),
+ rif(.@a, menuimage("actions/test", l("Manually trigger the current broadcast"))),
+ rif(.@a, menuimage("actions/remove", l("Stop broadcasting"))),
+ rif(!(.@a), menuimage("actions/add", l("Set a new broadcast"))),
+ rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu")));
+
+ switch (@menu)
+ {
+ case 2: announce $@SCHED_Msg$, bc_all; break;
+ case 3: $@SCHED_Msg$ = ""; break;
+ case 4: newBroadcast; break;
+ default: return;
+ }
+ } while (1);
+}
+
+
+
+- script @sched 32767,{
+ end;
+
+OnTimer3600000:
+ if ($@SCHED_Msg$ == "")
+ {
+ stopnpctimer;
+ end;
+ }
+
+ ++$@SCHED_Opt[4]; // increase hours counter
+ if ($@SCHED_Opt[4] == $@SCHED_Opt[1])
+ {
+ stopnpctimer;
+ ++$@SCHED_Opt[2]; // increase total counter
+ announce $@SCHED_Msg$, bc_all;
+ $@SCHED_Opt[4] = 0; // reset hours counter
+ if ($@SCHED_Opt[2] >= $@SCHED_Opt[3] && $@SCHED_Opt[3] > 0)
+ {
+ $@SCHED_Msg$ = ""; // reset message
+ end;
+ }
+ }
+ initnpctimer;
+ end;
+
+OnCall:
+ if (!is_evtc() && !is_dev())
+ {
+ end;
+ }
+
+ StoneBoard;
+ closeclientdialog;
+ end;
+
+OnInit:
+ bindatcmd "sched", "@sched::OnCall", 0, 99, 0;
+}
+
+function script ReceiveScheduledBroadcast {
+ if ($@SCHED_Opt[0] && $@SCHED_Msg$ != "")
+ {
+ announce $@SCHED_Msg$, bc_self;
+ }
+ return;
+}
+
diff --git a/npc/commands/super-menu.txt b/npc/commands/super-menu.txt
new file mode 100644
index 00000000..5ed7ced3
--- /dev/null
+++ b/npc/commands/super-menu.txt
@@ -0,0 +1,68 @@
+function script SuperMenu {
+ do
+ {
+ if (debug && !is_dev())
+ {
+ GlobalDebugMenu;
+ return;
+ }
+
+ clear;
+ setnpcdialogtitle l("Super Menu");
+ mes l("This menu contains all options available to you, based on your access privileges.");
+ mes "";
+ mes l("What do you want to access?");
+ next;
+ select
+ l("Scheduled broadcasts"),
+ l("MOTD"),
+ rif(is_evtc(), l("Event management")),
+ l("Debug");
+
+ switch (@menu)
+ {
+ case 1: StoneBoard 1; break;
+ case 2: MOTDConfig 1; break;
+ case 3: GlobalEventMenu 1; break;
+ case 4: GlobalDebugMenu 1; break;
+ }
+ } while (1);
+}
+
+
+
+- script @super 32767,{
+ end;
+
+OnCall:
+
+ if (!debug && !is_dev())
+ {
+ dispbottom l("You do not have the required access privileges to use the Super Menu.");
+ end;
+ }
+
+ SuperMenu;
+ closeclientdialog;
+ end;
+
+OnInit:
+ bindatcmd "super", "@super::OnCall", 0, 99, 0;
+ bindatcmd "numa", "@super::OnCall", 0, 99, 0; // alias for those used to TMW's @numa
+}
+
+function script GrantSuperSkill {
+ .@debug_skill = getskilllv(EVOL_SUPER_MENU);
+
+ if (.@debug_skill > 0 && !debug)
+ {
+ skill EVOL_SUPER_MENU, 0, 0; // remove debug skill. Not needed (skill tree)
+ }
+
+ else if (.@debug_skill < 1 && debug)
+ {
+ skill EVOL_SUPER_MENU, 1, 0; // give debug skill
+ }
+ return;
+}
+
diff --git a/npc/commands/warp.txt b/npc/commands/warp.txt
new file mode 100644
index 00000000..22eeda39
--- /dev/null
+++ b/npc/commands/warp.txt
@@ -0,0 +1,91 @@
+// @w atcommand
+// warps using anchors or map name
+//
+// group lv: 1
+// group char lv: 2
+// log: True
+//
+// usage:
+// @w <map or anchor> [, x [, y]]
+// #w "char" <map or anchor> [, x [, y]]
+//
+// example:
+// @w artis
+// #w "char" artis
+
+- script @w 32767,{
+ end;
+
+OnCall:
+ .@params$ = strtoupper(strip(implode(.@atcmd_parameters$[0], " ")));
+ .@request$ = replacestr(.@params$, " ", "");
+
+ cleararray($@regexmatch$[1], "", 3);
+ if (.@params$ ~= "^(.+) ([0-9]+) ([0-9]+)$")
+ {
+ .@request$ = replacestr(strip($@regexmatch$[1]), " ", "");
+ .@req_x = atoi(strip($@regexmatch$[2]));
+ .@req_y = atoi(strip($@regexmatch$[3]));
+ }
+
+ .@ht = getvariableofnpc(.ht, "__anchors__");
+ .@it = htiterator(.@ht);
+ for (.@key$ = htifirstkey(.@it); hticheck(.@it); .@key$ = htinextkey(.@it))
+ {
+ if (.@request$ ~= .@key$)
+ {
+ sscanf(htget(.@ht, .@key$, ""), "%s %d %d", .@map$, .@x, .@y);
+ break;
+ }
+ }
+ htidelete(.@it);
+
+ .@map$ = .@map$ ? .@map$ : .@request$;
+ .@x = .@req_y ? .@req_x : .@x;
+ .@y = .@req_y ? .@req_y : .@y;
+
+ if (!map_exists(.@map$))
+ {
+ if (map_exists(.@atcmd_parameters$[0]))
+ {
+ .@map$ = .@atcmd_parameters$[0];
+ }
+ else
+ {
+ dispbottom(l("Map or anchor not found: %s", .@atcmd_parameters$[0]));
+ end;
+ }
+ }
+
+ while (!checkcell(.@map$, .@x, .@y, cell_chkpass))
+ {
+ // FIXME: this whole cell finding loop is DIRTY!
+ // we should have a command to get a random free coordinate
+ // or we should make buildin_warp silently ignore 0,0
+
+ if (.@e == 50) break; // FIXME: triggers a console warning
+ .@x = rand(20, 20 + (.@e * 5));
+ .@y = rand(20, 20 + (.@e * 5));
+ ++.@e;
+ }
+
+ slide_or_warp(.@map$, .@x, .@y);
+ updateSpotlight();
+ end;
+
+OnInit:
+ if (debug > 0)
+ {
+ bindatcmd("w", "@w::OnCall", 0, 20, 0);
+ bindatcmd("go", "@w::OnCall", 0, 20, 0);
+ bindatcmd("to", "@w::OnCall", 0, 20, 0);
+ bindatcmd("warp", "@w::OnCall", 0, 20, 0);
+ }
+ else
+ {
+ bindatcmd("w", "@w::OnCall", 20, 60, 1);
+ bindatcmd("go", "@w::OnCall", 20, 60, 1);
+ bindatcmd("to", "@w::OnCall", 20, 60, 1);
+ bindatcmd("warp", "@w::OnCall", 20, 60, 1);
+ }
+}
diff --git a/npc/commands/zeny.txt b/npc/commands/zeny.txt
new file mode 100644
index 00000000..944c1bb9
--- /dev/null
+++ b/npc/commands/zeny.txt
@@ -0,0 +1,98 @@
+// @esp atcommand
+// changes the number of Esperin
+//
+// group lv: 3
+// group char lv: 99
+// log: True
+//
+// usage:
+// @esp <delta>
+// #esp "char" <delta>
+//
+// example:
+// @esp +5
+// @esp -5
+// @esp +++
+
+- script @esp 32767,{
+ end;
+
+OnCall:
+ .@delta$ = .@atcmd_parameters$[0];
+
+ if (debug && startswith(.@delta$, "--"))
+ {
+ Zeny = 0;
+ if (.@delta$ == "---")
+ {
+ BankVault = 0;
+ }
+ }
+ else if (debug && (startswith(.@delta$, "++") || .@delta$ == ""))
+ {
+ Zeny = MAX_ZENY;
+ if (.@delta$ == "+++")
+ {
+ BankVault = MAX_BANK_ZENY;
+ }
+ }
+ else
+ {
+ .@d = atoi(.@delta$);
+ if (.@d < 0)
+ {
+ .@a = Zeny + .@d; // The amount of zeny remaining after
+ if (.@a < 0) // If we can't remove that much zeny, try removing from bank too
+ {
+ Zeny = 0;
+ .@b = BankVault + .@a; // amount remaining in bank after
+ if (.@b < 0)
+ {
+ BankVault = 0;
+ }
+ else
+ {
+ BankVault += .@a;
+ }
+ }
+ else // We can remove that much zeny
+ {
+ Zeny += .@d;
+ }
+ }
+ else
+ {
+ .@a = Zeny + .@d; // The amount of zeny after
+ if (.@a < 0 || .@a >= MAX_ZENY) // If we can't add that much zeny, try adding to bank
+ {
+ .@c = .@d - (.@a - Zeny); // the amount to put in bank
+ Zeny = MAX_ZENY;
+ .@b = BankVault + .@c; // amout in bank after
+ if (.@b < 0 || .@b == MAX_BANK_ZENY)
+ {
+ BankVault = MAX_BANK_ZENY;
+ }
+ else
+ {
+ BankVault += .@c;
+ }
+ }
+ else // We can add that much zeny
+ {
+ Zeny += .@d;
+ }
+ }
+ }
+ end;
+
+OnInit:
+ if (debug > 0)
+ {
+ bindatcmd "e", "@esp::OnCall", 0, 99, 0;
+ bindatcmd "esp", "@esp::OnCall", 0, 99, 0;
+ bindatcmd "money", "@esp::OnCall", 0, 99, 0;
+ end;
+ }
+
+ bindatcmd "esp", "@esp::OnCall", 99, 99, 1;
+}