summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2021-04-10 03:00:20 -0300
committerJesusaves <cpntb1@ymail.com>2021-04-10 03:00:20 -0300
commitba1e827b6b4c17c35a163e6b55be8c122de632b8 (patch)
tree819f93d0ffee3697e336471710afb9681f0b8d86
parent6e7f3113c0faad9edd4367d100ba9dd77e8d3130 (diff)
downloadserverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.gz
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.bz2
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.tar.xz
serverdata-ba1e827b6b4c17c35a163e6b55be8c122de632b8.zip
Add several convenience functions. Fix some bugs regarding misuse of readparam()
-rw-r--r--db/constants.conf9
-rw-r--r--db/pre-re/item_db.conf16
-rwxr-xr-xnpc/006-1/miriam.txt6
-rwxr-xr-xnpc/009-2/kfahr.txt4
-rwxr-xr-xnpc/016-1/gwendolyn.txt2
-rwxr-xr-xnpc/018-2/caul.txt2
-rwxr-xr-xnpc/commands/changesex.txt31
-rwxr-xr-xnpc/commands/class.txt30
-rw-r--r--npc/commands/debug-quest.txt93
-rwxr-xr-xnpc/commands/debug.txt312
-rwxr-xr-xnpc/commands/destroynpc.txt34
-rwxr-xr-xnpc/commands/hug.txt24
-rw-r--r--npc/commands/ipcheck.txt71
-rw-r--r--npc/commands/kami.txt86
-rw-r--r--npc/commands/language.txt60
-rwxr-xr-xnpc/commands/marry.txt68
-rwxr-xr-xnpc/commands/music.txt33
-rwxr-xr-xnpc/commands/mute.txt92
-rwxr-xr-xnpc/commands/npctalk.txt22
-rwxr-xr-xnpc/commands/numa.txt43
-rwxr-xr-xnpc/commands/pullrabbit.txt25
-rw-r--r--npc/commands/python.txt27
-rwxr-xr-xnpc/commands/remotecmd.txt61
-rw-r--r--npc/commands/resync.txt45
-rwxr-xr-xnpc/commands/warp.txt56
-rwxr-xr-xnpc/commands/zeny.txt78
-rw-r--r--npc/functions/array.txt464
-rw-r--r--npc/functions/asklanguage.txt72
-rw-r--r--npc/functions/filters.txt126
-rw-r--r--npc/functions/goodbye.txt152
-rw-r--r--npc/functions/inc_sc_bonus.txt72
-rw-r--r--npc/functions/input.txt110
-rw-r--r--npc/functions/inventoryplace.txt36
-rw-r--r--npc/functions/main.txt37
-rw-r--r--npc/functions/math.txt123
-rw-r--r--npc/functions/npcmove.txt142
-rw-r--r--npc/functions/npcmovegraph.txt489
-rw-r--r--npc/functions/permissions.txt35
-rw-r--r--npc/functions/random-talk.txt207
-rw-r--r--npc/functions/string.txt211
-rwxr-xr-xnpc/functions/time.txt256
-rw-r--r--npc/functions/timer.txt89
-rwxr-xr-xnpc/scripts.conf33
43 files changed, 2893 insertions, 1091 deletions
diff --git a/db/constants.conf b/db/constants.conf
index f73ee858..a339559a 100644
--- a/db/constants.conf
+++ b/db/constants.conf
@@ -5216,10 +5216,11 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
comment__: "Language constants"
LANG_EN: 0
- LANG_FR: 1
- LANG_ES: 2
+ LANG_PTBR: 1
+ LANG_FR: 2
LANG_DE: 3
- MAX_LANG: 3
+ LANG_ES: 4
+ MAX_LANG: 4
comment__: "Daily Constants"
DAILY_LOWLEVEL: 0
@@ -5634,6 +5635,7 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
AMMO_BOW:1
AMMO_SLING:2
AMMO_WAND:3
+
// tmw npcs
NPC45:45
NPC100:100
@@ -5814,6 +5816,7 @@ more than one separator can be used in a row (so 12_3___456 is illegal).
NPC384:384
NPC385:385
NPC386:386
+ NPC393:393
NPC394:394
NPC395:395
NPC400:400
diff --git a/db/pre-re/item_db.conf b/db/pre-re/item_db.conf
index 9c31d7a7..d4b46dd2 100644
--- a/db/pre-re/item_db.conf
+++ b/db/pre-re/item_db.conf
@@ -874,7 +874,7 @@ item_db: (
BindOnEquip: false
Subtype: "W_2HSTAFF"
OnEquipScript: <"
- set @bStat, Int;
+ set @bStat, readparam(bInt);
set @minbStatVal, 60;
callfunc "RequireStat";
">
@@ -900,7 +900,7 @@ item_db: (
BindOnEquip: false
Subtype: "W_STAFF"
OnEquipScript: <"
- set @bStat, Int;
+ set @bStat, readparam(bInt);
set @minbStatVal, 5;
callfunc "RequireStat";
">
@@ -973,7 +973,7 @@ item_db: (
BindOnEquip: false
Subtype: "W_BOW"
OnEquipScript: <"
- set @bStat, Dex;
+ set @bStat, readparam(bDex);
set @minbStatVal, 80;
callfunc "RequireStat";
bonus2 bHPDrainRate, 100, -2;
@@ -6251,10 +6251,10 @@ item_db: (
BindOnEquip: false
Delay: 0
OnEquipScript: <"
- set @bStat, Int;
+ set @bStat, readparam(bInt);
set @minbStatVal, 70;
callfunc "RequireStat";
- set @bStat, Vit;
+ set @bStat, readparam(bVit);
set @minbStatVal, 40;
callfunc "RequireStat";
bonus bSpeedAddRate, -15;
@@ -10923,13 +10923,13 @@ item_db: (
BindOnEquip: false
Delay: 0
OnEquipScript: <"
- set @bStat, Str;
+ set @bStat, readparam(bStr);
set @minbStatVal, 70;
callfunc "RequireStat";
- set @bStat, Vit;
+ set @bStat, readparam(bVit);
set @minbStatVal, 50;
callfunc "RequireStat";
- set @bStat, Luk;
+ set @bStat, readparam(bLuk);
set @minbStatVal, 50;
callfunc "RequireStat";
bonus bAtkRange, -10;
diff --git a/npc/006-1/miriam.txt b/npc/006-1/miriam.txt
index a42253e6..05e59d5e 100755
--- a/npc/006-1/miriam.txt
+++ b/npc/006-1/miriam.txt
@@ -144,7 +144,7 @@ L_Prepared:
@weight = MaxWeight/Weight;
if (@weight < 3)
goto L_heavyweight;
- if (Agi < 60)
+ if (readparam(bAgi) < 60)
goto L_slow;
if (BaseLevel < 60)
goto L_LowLevel;
@@ -175,7 +175,7 @@ L_heavymetal:
L_slow:
mes "[Miriam]";
- mes "Oh, wait, wait... " +Agi+ " Agility? Are you serious? What do you do with all your Character points? Put them all to 'Strength'? Really... I don't even want to know... Come back here when you have a decent character points distribution!\"";
+ mes "Oh, wait, wait... " +readparam(bAgi)+ " Agility? Are you serious? What do you do with all your Character points? Put them all to 'Strength'? Really... I don't even want to know... Come back here when you have a decent character points distribution!\"";
close;
L_LowLevel:
@@ -194,7 +194,7 @@ L_Offer:
L_TorsoNext:
mes "[Miriam]";
- mes "\"I see. I like the way you distributed your Character Points too... " +Agi+ " points to Agility! You really know how to prepare yourself for a good fight!\"";
+ mes "\"I see. I like the way you distributed your Character Points too... " +readparam(bAgi)+ " points to Agility! You really know how to prepare yourself for a good fight!\"";
next;
mes "\"Well... Even if you have a potential to be a good fighter, moving the way you do will not help. You need to be fast to avoid the monsters. \"";
next;
diff --git a/npc/009-2/kfahr.txt b/npc/009-2/kfahr.txt
index 8962e0a7..2d1b47cf 100755
--- a/npc/009-2/kfahr.txt
+++ b/npc/009-2/kfahr.txt
@@ -905,8 +905,8 @@ L_gs_wrestle:
@KFAHR_AGI = 60 - @beer_count * 5;
@kfahr_stamina = 120;
- @PC_STR = Str;
- @PC_AGI = Agi;
+ @PC_STR = readparam(bStr);
+ @PC_AGI = readparam(bAgi);
@PC_MAX_STAMINA = Vit + 20;
@pc_stamina = @PC_MAX_STAMINA;
diff --git a/npc/016-1/gwendolyn.txt b/npc/016-1/gwendolyn.txt
index b01ab292..87ad8031 100755
--- a/npc/016-1/gwendolyn.txt
+++ b/npc/016-1/gwendolyn.txt
@@ -414,7 +414,7 @@ L_target_hit:
next;
mes "\"Let me inspect it...\" She walks to the target.";
next;
- if( rand(500) > (2*Dex + Agi))
+ if( rand(500) > (2*readparam(bDex) + readparam(bAgi)))
goto L_target_nohit;
.@Q_hawkseye = 6;
callsub S_Update_Var;
diff --git a/npc/018-2/caul.txt b/npc/018-2/caul.txt
index e738dbde..e5c3626f 100755
--- a/npc/018-2/caul.txt
+++ b/npc/018-2/caul.txt
@@ -450,7 +450,7 @@ L_mo_auto_bad:
L_mo_explode:
mes "[Exploding monster oil]";
mes "Your brew explodes!";
- if (rand(Agi) > 40)
+ if (rand(readparam(bAgi)) > 40)
goto L_mo_explode_dodge;
mes "The burst of boiling monster brew hits you.";
next;
diff --git a/npc/commands/changesex.txt b/npc/commands/changesex.txt
deleted file mode 100755
index de20d91b..00000000
--- a/npc/commands/changesex.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-- script @changesex NPC32767,{
- callfunc "argv_splitter";
- .@n$ = if_then_else(@argv$[1] != "", "char", "") + "changecharsex()";
- if (GM < CMD_CHANGESex && GM < G_SYSOP) goto L_GM; // check if you can use it on self
- .@target_id = BL_ID;
- if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]);
- if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self
- if (@argv$[1] != "" && GM < CMD_CHARCHANGESex && GM < G_SYSOP) goto L_GM; // when target is not self, use charchangecharsex() permission
-
- set .@s, 3; // default to non-binary
- if (@argv$[0] == "M" || @argv$[0] == "m") set .@s, 1;
- if (@argv$[0] == "F" || @argv$[0] == "f") set .@s, 0;
- Sex = .@s, .@target_id;
- gmlog "@"+.@n$+" " + @args$;
- message strcharinfo(0), .@n$+" : The operation succeeded.";
- end;
-
-L_Failed:
- // XXX: should we allow GMs to change Sex of users that are not logged in?
- message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARCHANGESex, CMD_CHANGESex) +", but you are level " + GM;
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "changecharsex()", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "charchangecharsex()", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/class.txt b/npc/commands/class.txt
deleted file mode 100755
index 81ac0c7c..00000000
--- a/npc/commands/class.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-- script @class NPC32767,{
- callfunc "argv_splitter";
- .@n$ = if_then_else(@argv$[1] != "", "char", "") + "class";
- if (GM < CMD_CLASS && GM < G_SYSOP) goto L_GM; // check if you can use it on self
- .@target_id = BL_ID;
- if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]);
- if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self
- if (@argv$[1] != "" && GM < CMD_CHARCLASS && GM < G_SYSOP) goto L_GM; // when target is not self, use charclass permission
-
- set .@c, 1; // default to human
- if (@argv[0] >= 1 || @argv[0] <= 32767) set .@c, @argv[0];
- Class = .@c, .@target_id;
- gmlog "@"+.@n$+" " + @args$;
- message strcharinfo(0), .@n$+" : The operation succeeded.";
- end;
-
-L_Failed:
- // XXX: should we allow GMs to change class of users that are not logged in?
- message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARCLASS, CMD_CLASS) +", but you are level " + GM;
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "class", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "charclass", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/debug-quest.txt b/npc/commands/debug-quest.txt
new file mode 100644
index 00000000..a4c6572f
--- /dev/null
+++ b/npc/commands/debug-quest.txt
@@ -0,0 +1,93 @@
+// Evol Script
+// Author: Gumi, Jesusalva
+
+// TODO: This script must be auto-generated from db/quests.conf to be of any use
+function script GlobalQuestDebug {
+ 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,
+ l("Custom"), -136;
+
+ switch (@menuret)
+ {
+ case -1: return;
+ case -136:
+ mes "Determine the quest number, as stated in db/quests.conf";
+ input .@quest;
+ if (!.@quest) return;
+ mes "";
+ mes l("DEBUG: Changing @@, Values: (@@, @@, @@).", getquestlink(.@quest), getq(.@quest), getq2(.@quest), getq3(.@quest));
+ select
+ "set 1",
+ "set 2",
+ "set 3",
+ "reset";
+ mes l("DEBUG: Changing @@ field @@ to something else.", getquestlink(.@quest), @menu);
+ mes "";
+ mes "Determine the new value (numeric only)";
+ input .@value;
+ if (.@value < 0) return;
+ if (@menu == 1)
+ setq1 .@quest, .@value;
+ if (@menu == 2)
+ setq2 .@quest, .@value;
+ if (@menu == 3)
+ setq3 .@quest, .@value;
+ if (@menu == 4)
+ setq .@quest, .@value, 0, 0;
+
+ return;
+ default: return;
+ }
+ } while (1);
+ return;
+}
+
+
+
+- script @qdebug 32767,{
+ end;
+
+OnCall:
+ if (!is_gm()) {
+ end;
+ }
+ GlobalQuestDebug;
+ closedialog;
+ 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 @@ modified by GM", getquestlink(.@q));
+ specialeffect 50, 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", 99, 99, 1;
+ bindatcmd "setq", "@qdebug::OnSetq", 99, 99, 1;
+}
diff --git a/npc/commands/debug.txt b/npc/commands/debug.txt
index c1ebf81d..f14d1363 100755
--- a/npc/commands/debug.txt
+++ b/npc/commands/debug.txt
@@ -1,11 +1,6 @@
function script Debug {
- if(!@debug_npc) goto L_Begin;
- mes "The debug NPCs have been deprecated. Please use this command instead:";
- mes "";
- mes "%%E ##a@debug##0";
- @debug_npc = 0;
- goto L_close;
+ goto L_Begin;
L_Begin:
@debug_mask = 65535;
@@ -13,8 +8,8 @@ L_Begin:
@mexp = ((MAGIC_EXPERIENCE & @debug_mask) >> @debug_shift);
mes "What do you want to do?";
menu
+ "Reset stat points.", L_Level,
"Change my level.", L_Level,
- "Change my stats.", L_Status,
"Change my basic skills.", L_BasicSkills,
"Change my focus skills.", L_FocusSkills,
"Change my magic skills.", L_MagicSkills,
@@ -51,188 +46,6 @@ L_SameLevel:
next;
goto L_Begin;
-L_Status:
- mes "What do you want to do?";
- menu
- "Set all of my stats myself.", L_ChangeStrength,
- "Set one of my stats myself.", L_ChangeSingleStat,
- "Get maximum points in all stats.", L_ChangeAllStats,
- "Reset my status points.", L_ResetStatusPoints,
- "Back to the main menu.", L_Begin,
- "Close.", L_close;
-
-L_ChangeAllStats:
- Str = 99;
- Agi = 99;
- Vit = 99;
- Int = 99;
- Dex = 99;
- Luk = 99;
- mes "You now have 99 in all stats.";
- next;
- goto L_Begin;
-
-L_ChangeStrength:
- mes "How much strength do you want to have (min: 1 - max: 99)?";
- input @str;
- if (@str < 1)
- goto L_StatTooLow;
- if (@str > 99)
- goto L_StatTooHigh;
- Str = @str;
- goto L_ChangeAgility;
-
-L_ChangeAgility:
- mes "How much agility do you want to have (min: 1 - max: 99)?";
- input @agi;
- if (@agi < 1)
- goto L_StatTooLow;
- if (@agi > 99)
- goto L_StatTooHigh;
- Agi = @agi;
- goto L_ChangeVitality;
-
-L_ChangeVitality:
- mes "How much vitality do you want to have (min: 1 - max: 99)?";
- input @vit;
- if (@vit < 1)
- goto L_StatTooLow;
- if (@vit > 99)
- goto L_StatTooHigh;
- Vit = @vit;
- goto L_ChangeIntelligence;
-
-L_ChangeIntelligence:
- mes "How much intelligence do you want to have (min: 1 - max: 99)?";
- input @int;
- if (@int < 1)
- goto L_StatTooLow;
- if (@int > 99)
- goto L_StatTooHigh;
- Int = @int;
- goto L_ChangeDexterity;
-
-L_ChangeDexterity:
- mes "How much dexterity do you want to have (min: 1 - max: 99)?";
- input @dex;
- if (@dex < 1)
- goto L_StatTooLow;
- if (@dex > 99)
- goto L_StatTooHigh;
- Dex = @dex;
- goto L_ChangeLuck;
-
-L_ChangeLuck:
- mes "How much luck do you want to have (min: 1 - max: 99)?";
- input @luk;
- if (@luk < 1)
- goto L_StatTooLow;
- if (@luk > 99)
- goto L_StatTooHigh;
- Luk = @luk;
- mes "You now have " + Str + " in strength.";
- mes "You now have " + Agi + " in agility.";
- mes "You now have " + Vit + " in vitality.";
- mes "You now have " + Int + " in intelligence.";
- mes "You now have " + Dex + " in dexterity.";
- mes "You now have " + Luk + " in luck.";
- next;
- goto L_Begin;
-
-L_StatTooLow:
- mes "Bad choice. Minimum stat value is 1. Aborting.";
- next;
- goto L_Status;
-
-L_StatTooHigh:
- mes "Bad choice. Maximum stat value is 99. Aborting.";
- next;
- goto L_Status;
-
-L_ChangeSingleStat:
- mes "Which stat do you want to change?";
- menu
- "Strength.", L_ChangeStrengthSingle,
- "Agility.", L_ChangeAgilitySingle,
- "Vitality.", L_ChangeVitalitySingle,
- "Intelligence.", L_ChangeIntelligenceSingle,
- "Dexterity.", L_ChangeDexteritySingle,
- "Luck.", L_ChangeLuckSingle,
- "Back to the main menu.", L_Begin,
- "Close.", L_close;
-
-L_ChangeStrengthSingle:
- mes "How much strength do you want to have (min: 1 - max: 99)?";
- input @str;
- if (@str < 1)
- goto L_StatTooLow;
- if (@str > 99)
- goto L_StatTooHigh;
- Str = @str;
- mes "You now have " + Str + " in strength.";
- next;
- goto L_Begin;
-
-L_ChangeAgilitySingle:
- mes "How much agility do you want to have (min: 1 - max: 99)?";
- input @agi;
- if (@agi < 1)
- goto L_StatTooLow;
- if (@agi > 99)
- goto L_StatTooHigh;
- Agi = @agi;
- mes "You now have " + Agi + " in agility.";
- next;
- goto L_Begin;
-
-L_ChangeVitalitySingle:
- mes "How much vitality do you want to have (min: 1 - max: 99)?";
- input @vit;
- if (@vit < 1)
- goto L_StatTooLow;
- if (@vit > 99)
- goto L_StatTooHigh;
- Vit = @vit;
- mes "You now have " + Vit + " in vitality.";
- next;
- goto L_Begin;
-
-L_ChangeIntelligenceSingle:
- mes "How much intelligence do you want to have (min: 1 - max: 99)?";
- input @int;
- if (@int < 1)
- goto L_StatTooLow;
- if (@int > 99)
- goto L_StatTooHigh;
- Int = @int;
- mes "You now have " + Int + " in intelligence.";
- next;
- goto L_Begin;
-
-L_ChangeDexteritySingle:
- mes "How much dexterity do you want to have (min: 1 - max: 99)?";
- input @dex;
- if (@dex < 1)
- goto L_StatTooLow;
- if (@dex > 99)
- goto L_StatTooHigh;
- Dex = @dex;
- mes "You now have " + Dex + " in dexterity.";
- next;
- goto L_Begin;
-
-L_ChangeLuckSingle:
- mes "How much luck do you want to have (min: 1 - max: 99)?";
- input @luk;
- if (@luk < 1)
- goto L_StatTooLow;
- if (@luk > 99)
- goto L_StatTooHigh;
- Luk = @luk;
- mes "You now have " + Luk + " in luck.";
- next;
- goto L_Begin;
-
L_ResetStatusPoints:
resetstatus;
mes "Stats successfully resetted.";
@@ -240,113 +53,13 @@ L_ResetStatusPoints:
goto L_Begin;
L_BasicSkills:
- @emote = getskilllv(SKILL_EMOTE);
- @trade = getskilllv(SKILL_TRADE);
- @party = getskilllv(SKILL_PARTY);
- menu
- "Overview of my basic skills.", L_BasicSkillsOverview,
- "Add basic skills.", L_AddBasicSkills,
- "Reset basic skills.", L_ResetBasicSkills,
- "Back to main menu.", L_Begin,
- "Close.", L_close;
-
-L_BasicSkillsOverview:
- mes "Your level in the emote skill is " + @emote + ".";
- mes "Your level in the trade skill is " + @trade + ".";
- mes "Your level in the party skill is " + @party + ".";
- next;
- goto L_BasicSkills;
-
-L_AddBasicSkills:
- menu
- "Emote", L_ChangeEmoteSkill,
- "Trade.", L_ChangeTradeSkill,
- "Party.", L_ChangePartySkill,
- "All basic skills to their maximum level.", L_AllBasicSkills,
- "Back to the basic skills menu.", L_BasicSkills,
- "Close.", L_close;
-
-L_ChangeEmoteSkill:
- mes "Your level in the emote skill is " + @emote + ". What do you want to do?";
- menu
- "Get level 0.", L_Next,
- "Get level 1.", L_ChangeEmoteSkill1;
-
-L_Next:
- if (@menu == 1)
- addtoskill SKILL_EMOTE, 0;
- mes "Emote skill changed to level 0.";
- next;
- goto L_BasicSkills;
-
-L_ChangeEmoteSkill1:
- addtoskill SKILL_EMOTE, 1;
- mes "Emote skill changed to level 1.";
- next;
- goto L_BasicSkills;
-
-L_ChangeTradeSkill:
- mes "Your level in the trade skill is " + @trade + ". What do you want to do?";
- menu
- "Get level 0.", L_Next1,
- "Get level 1.", L_ChangeTradeSkill1;
-
-L_Next1:
- if (@menu == 1)
- addtoskill SKILL_TRADE, 0;
- mes "Trade skill changed to level 0.";
- next;
- goto L_BasicSkills;
-
-L_ChangeTradeSkill1:
- addtoskill SKILL_TRADE, 1;
- mes "Trade skill changed to level 1.";
- next;
- goto L_BasicSkills;
-
-L_ChangePartySkill:
- mes "Your level in the party skill is " + @trade + ". What do you want to do?";
- menu
- "Get level 0.", L_Next2,
- "Get level 1.", L_ChangePartySkill1,
- "Get level 2.", L_ChangePartySkill2;
-
-L_Next2:
- if (@menu == 1)
- addtoskill SKILL_PARTY, 0;
- mes "Party skill changed to level 0.";
- next;
- goto L_BasicSkills;
-
-L_ChangePartySkill1:
- addtoskill SKILL_PARTY, 1;
- mes "Party skill changed to level 1.";
- next;
- goto L_BasicSkills;
-
-L_ChangePartySkill2:
- addtoskill SKILL_PARTY, 2;
- mes "Party skill changed to level 2.";
- next;
- goto L_BasicSkills;
-
-L_AllBasicSkills:
- addtoskill SKILL_EMOTE, 1;
- addtoskill SKILL_TRADE, 1;
- addtoskill SKILL_PARTY, 2;
- mes "Basic skills added.";
- next;
- goto L_BasicSkills;
-
-L_ResetBasicSkills:
- addtoskill SKILL_EMOTE, 0;
- addtoskill SKILL_TRADE, 0;
- addtoskill SKILL_PARTY, 0;
- mes "Basic skills removed.";
- next;
- goto L_BasicSkills;
+ skill SKILL_EMOTE, 6, 0;
+ //addtoskill SKILL_TRADE, 0;
+ //addtoskill SKILL_PARTY, 0;
+ goto L_Begin;
L_FocusSkills:
+ /*
@pool = getskilllv(SKILL_POOL);
@mallard = getskilllv(SKILL_MALLARDS_EYE);
@brawling = getskilllv(SKILL_BRAWLING);
@@ -546,6 +259,8 @@ L_ResetFocusSkills:
mes "Focus skills removed.";
next;
goto L_FocusSkills;
+ */
+ goto L_Begin;
L_MagicSkills:
@general = getskilllv(SKILL_MAGIC);
@@ -973,12 +688,3 @@ OnInit:
end;
}
-027-2,125,103,0 script Debug#5 NPC154,{
- @debug_npc = 1;
- callfunc "Debug";
- end;
-OnInit:
- if (!debug)
- disablenpc "Debug#5";
- end;
-}
diff --git a/npc/commands/destroynpc.txt b/npc/commands/destroynpc.txt
deleted file mode 100755
index 156c8200..00000000
--- a/npc/commands/destroynpc.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-- script @destroynpc NPC32767,{
- callfunc "argv_splitter";
- if (GM < CMD_DESTROYNPC && GM < G_SYSOP)
- goto L_GM;
-
- .@id = getnpcid(@argv$[0]);
- if (@argv$[0] == "" || .@id < 1)
- goto L_Failed;
- if (gettimetick(2) - @destroynpc[0] > 300 || @destroynpc[1] != .@id)
- goto L_Confirm;
-
- gmlog "@destroynpc " + @args$;
- message strcharinfo(0), "destroynpc : The operation succeeded.";
- debugmes "!!! => npc destroyed: `"+@argv$[0]+"` ("+.@id+")";
- if (1==1) destroy .@id; // FIXME: allow destroy to work as a non-terminator when arg0 is given (TMWA)
- end;
-
-L_Confirm:
- message strcharinfo(0), "destroynpc : ##BDANGER ZONE!##b This command permanently destroys a npc and its puppets (if any). Use the command again to confirm.";
- setarray @destroynpc[0], gettimetick(2), .@id;
- end;
-
-L_Failed:
- message strcharinfo(0), "destroynpc : Impossible to find the target npc. Did you try putting the name in \"quotation marks\"? Some npcs also have an invisible postfix in their name, ie `#_M`.";
- end;
-
-L_GM:
- message strcharinfo(0), "destroynpc : GM command is level "+ CMD_DESTROYNPC +", but you are level " + GM;
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "destroynpc", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/hug.txt b/npc/commands/hug.txt
deleted file mode 100755
index 4f7c086d..00000000
--- a/npc/commands/hug.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-- script nonmagic-hug NPC32767,{
- explode .@name$[0], @args$, "*"; // strip the trailing *
- @target_id = if_then_else(.@name$[0] != "", getcharid(3, .@name$[0]), BL_ID);
- if (@target_id < 1 || !(isloggedin(@target_id))) set @target_id, BL_ID; // fallback to self
- if (.@name$[0] == "Tree" || .@name$[0] == "tree") set @target_id, .tree_id;
- .@range = if_then_else(@target_id == .tree_id, 3, 6);
- if (distance(BL_ID, @target_id) >= .@range) end;
- if (gettimetick(2) - @hugspell_time < 3) end;
-
- @hugspell_time = gettimetick(2);
- misceffect FX_HUG, strcharinfo(0);
- if (@target_id != BL_ID) misceffect FX_HUG, @target_id;
-
- if (@target_id != .tree_id) end;
- @flag = 2;
- callfunc "QuestTreeTrigger";
- close;
-
-OnInit:
- .tree_id = getnpcid("#DruidTree0#_M");
- registercmd "*hugs", strnpcinfo(0); // eq: /me hugs (target)
- registercmd "*hugs*", strnpcinfo(0); // eq: /me hugs
- end;
-}
diff --git a/npc/commands/ipcheck.txt b/npc/commands/ipcheck.txt
new file mode 100644
index 00000000..a241d3c0
--- /dev/null
+++ b/npc/commands/ipcheck.txt
@@ -0,0 +1,71 @@
+// TMW2 script
+// Author: Jesusalva <admin@tmw2.org>
+//
+// @ipcheck <player_name>
+// #ipcheck <player_name>
+//
+// Returns user IP
+
+
+- script @ipcheck 32767,{
+ end;
+
+OnCall:
+ if (.@atcmd_numparameters == 0)
+ .@request$ = strcharinfo(0);
+ else
+ .@request$ = implode(.@atcmd_parameters$, " ");
+ dispbottom strip(.@request$)+": IP "+getcharip(.@request$);
+ //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$);
+ end;
+
+OnBan:
+ if (.@atcmd_numparameters == 0) {
+ dispbottom col("Syntax: #ipban <reason>", 1);
+ }
+ // Do not allow banning high-level staff
+ if (is_admin() || is_gm())
+ end;
+ .@target$=strcharinfo(0);
+ .@reason$ = implode(.@atcmd_parameters$, " ");
+ dispbottom col(l("You were permanently banned by the GM Team."), 1);
+ sleep2(200);
+ query_sql "INSERT INTO ipbanlist (list,btime,rtime,reason) VALUES ('"+getcharip(.@target$)+"','"+gettime(7)+"-"+gettime(6)+"-"+gettime(5)+" "+gettime(3)+":"+gettime(2)+":"+gettime(1)+"','2037-01-01 00:00:00','"+.@reason$+"')";
+ logmes("was IP-Blocked, and will not connect again."), LOGMES_ATCOMMAND;
+ sleep2(2000);
+ charcommand("@kick "+.@target$);
+ end;
+
+OnInit:
+ bindatcmd "ipcheck", "@ipcheck::OnCall", 60, 100, 0;
+ bindatcmd "ipban", "@ipcheck::OnBan", 99, 100, 1;
+ end;
+}
+
+// TMW2 script
+// Author: Jesusalva <admin@tmw2.org>
+//
+// @checkidle <player_name>
+// #checkidle <player_name>
+//
+// Returns user idle time in seconds.
+// Useful when the game prohibits warping to player.
+
+
+- script @checkidle 32767,{
+ end;
+
+OnCall:
+ if (.@atcmd_numparameters == 0)
+ .@request$ = strcharinfo(0);
+ else
+ .@request$ = implode(.@atcmd_parameters$, " ");
+ dispbottom strip(.@request$)+" idle time: "+checkidle(.@request$);
+ //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$);
+ end;
+
+OnInit:
+ bindatcmd "checkidle", "@checkidle::OnCall", 60, 80, 0;
+ end;
+}
+
diff --git a/npc/commands/kami.txt b/npc/commands/kami.txt
new file mode 100644
index 00000000..1211fae0
--- /dev/null
+++ b/npc/commands/kami.txt
@@ -0,0 +1,86 @@
+// TMW2 Script
+//
+// @k <message>
+// Broadcast, and broadcast to #world too
+//
+// @servmsg <message>
+// Experimental, uses servicemessage() - requires up to date server
+
+- script @k 32767,{
+ end;
+
+OnCall:
+ .@request$ = strcharinfo(0)+": ";
+ .@request$ += implode(.@atcmd_parameters$, " ");
+ channelmes("#world", .@request$);
+ announce l(.@request$), bc_all|bc_npc;
+ end;
+
+OnServMsg:
+ .@request$ = strcharinfo(0)+": ";
+ .@request$ += implode(.@atcmd_parameters$, " ");
+ // This can be slow, beware
+ .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ message(.@players[.@i], .@request$);
+ }
+ end;
+
+OnBuff:
+ // Disabled command, used for debug purposes
+ .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ attachrid(.@players[.@i]);
+ sc_start SC_INCMHPRATE, 300000, 100;
+ sc_start SC_INCMSPRATE, 300000, 100;
+ sc_start SC_INCFLEERATE, 300000, 100;
+ sc_start SC_INCHITRATE, 300000, 100;
+ sc_start SC_WALKSPEED, 300000, 150;
+ sc_start SC_ATTHASTE_POTION3, 300000, 50;
+ percentheal 100, 100;
+ dispbottom l("YOU WERE BLESSED BY JESUSALVA");
+ dispbottom l("YOU CAN FEEL THE POWER FLOWING TROUGH YOU.");
+ detachrid();
+ }
+ end;
+
+OnInstDestroy:
+ .@request = implode(.@atcmd_parameters$, " ");
+ if (.@request != 0)
+ instance_destroy(.@request);
+ end;
+
+OnInstCheck:
+ .@request$ = implode(.@atcmd_parameters$, " ");
+ dispbottom has_instance2(.@request$);
+ end;
+
+OnInit:
+ bindatcmd "k", "@k::OnCall", 60, 80, 1;
+ bindatcmd "servmsg", "@k::OnServMsg", 80, 99, 1;
+
+ bindatcmd "blessing", "@k::OnBuff", 99, 100, 1;
+ bindatcmd "instcheck", "@k::OnInstCheck", 99, 100, 1;
+ bindatcmd "instdestr", "@k::OnInstDestroy", 99, 100, 1;
+ end;
+}
+
+// kamibroadcast( message{, sender} )
+function script kamibroadcast {
+ .@msg$=getarg(0);
+ .@snd$=getarg(1, "");
+
+ // Send to #world
+ if (.@snd$ == "")
+ channelmes("#world", " "+.@msg$);
+ else
+ channelmes("#world", "[ "+.@snd$+" ] : "+.@msg$);
+
+ // Make an announce
+ if (.@snd$ == "")
+ announce .@msg$, bc_all|bc_npc;
+ else
+ announce .@snd$+" : "+.@msg$, bc_all|bc_npc;
+
+ return;
+}
diff --git a/npc/commands/language.txt b/npc/commands/language.txt
new file mode 100644
index 00000000..ce7122f5
--- /dev/null
+++ b/npc/commands/language.txt
@@ -0,0 +1,60 @@
+// TMW2 Script
+// Author: Jesusalva
+// With code parts from Julia (Evol)
+
+// @lang atcommand
+// Changes Language
+//
+// group lv: 0
+// group char lv: 0
+// log: False
+//
+// usage:
+// @lang
+//
+
+function script CMD_lang {
+ callfunc "checkclientversion";
+ mesq l("Which language do you speak?");
+ next;
+ asklanguage(LANG_IN_SHIP);
+ mes "";
+ mesn;
+ mesq l("Ok, done.");
+ return;
+}
+
+- script @lang 32767,{
+ end;
+
+OnCall:
+ CMD_lang();
+ close;
+
+OnTranslate:
+ // Implode, using a slash at whitespaces
+ .@request$ = implode(.@atcmd_parameters$, "%2F");
+ // No NPC provided?
+ if (.@request$ == "") {
+ dispbottom l("Usage: @translate <npc file>");
+ dispbottom l("Example: @translate Nard");
+ dispbottom l("Example: @translate Elmo");
+ dispbottom l("Example: @translate npc/002-1/arpan");
+ dispbottom l("PS. Doesn't always work. You need an account at %s and to be at ManaPlus Team.", "@@https://www.transifex.com/arctic-games|Transifex@@");
+ end;
+ }
+ // Add .txt extension of needed
+ if (!compare(.@request$, ".txt"))
+ .@request$ += ".txt";
+ // Fix stuff for URL format
+ .@request$ = replacestr(.@request$, "/", "%2F");
+ .@request$ = strtolower(.@request$);
+ // Give your translation link
+ dispbottom "@@https://www.transifex.com/arctic-games/moubootaur-legends/translate/#"+languagecode()+"/serverdata?q=occurrence%3A"+.@request$+"|Translate with Transifex@@";
+ close;
+
+OnInit:
+ bindatcmd "lang", "@lang::OnCall", 0, 60, 0;
+ bindatcmd "translate", "@lang::OnTranslate", 0, 60, 0;
+ end;
+}
diff --git a/npc/commands/marry.txt b/npc/commands/marry.txt
deleted file mode 100755
index ad2ed5fc..00000000
--- a/npc/commands/marry.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-- script special-marry NPC32767,{
- .@target_id = getcharid(3, @args$);
- if (.@target_id < 1 || !(isloggedin(.@target_id)) || .@target_id == BL_ID) goto L_NotFound;
- if (PARTNER || get(PARTNER, .@target_id)) goto L_AlreadyMarried;
- if (isin("014-1",29,36,34,39) == 0 && isin("001-1",20,27,22,27) == 0) goto L_NotInArea;
- if (distance(BL_ID, .@target_id) != 1) goto L_AwayFromPartner;
- if (BaseLevel < WEDDING_MIN_LEVEL || get(BaseLevel, .@target_id) < WEDDING_MIN_LEVEL) goto L_TooYoung;
- if (getequipid(equip_shield) != 702 || getequipid(equip_shield, @args$) != 702) goto L_NoRing;
-
- if (get(@marriage[0], .@target_id) == BL_ID) goto L_Proceed;
-
- setarray @marriage[0], .@target_id, gettimetick(2);
- addtimer (.timeout * 1000), strnpcinfo(0) + "::OnTimeout";
- announce strcharinfo(0) + " is asking " + strcharinfo(0, .@target_id) + " for marriage.", 2;
- message strcharinfo(0, .@target_id), "Marriage : ##3##B" + strcharinfo(0) + " wishes to marry you. To accept, write `##1#marry "+strcharinfo(0)+"##3` within the next "+.timeout+" seconds.";
- end;
-
-L_NotFound:
- message strcharinfo(0), "Marriage : ##3##BThe target player is either not found or yourself.";
- end;
-
-L_TooYoung:
- message strcharinfo(0), "Marriage : ##3##BYou and your partner need to be at least level "+ WEDDING_MIN_LEVEL +".";
- end;
-
-L_NoRing:
- message strcharinfo(0), "Marriage : ##3##BYou and your partner need to have ["+ getitemlink("WeddingRing") +"] equipped.";
- end;
-
-L_AwayFromPartner:
- message strcharinfo(0), "Marriage : ##3##BYou and your partner need to be standing next to each other.";
- end;
-
-L_NotInArea:
- message strcharinfo(0), "Marriage : ##3##BYou are not standing in a designated marriage area.";
- end;
-
-L_Proceed:
- if ((gettimetick(2) - .timeout) > get(@marriage[1], .@target_id)) goto L_TooLate;
- PARTNER = CHAR_ID, .@target_id;
- if (PARTNER == get(CHAR_ID, .@target_id)) goto L_Success;
- PARTNER = 0, .@target_id;
- PARTNER = 0;
- end;
-
-L_Success:
- announce strcharinfo(0) + " and " + strcharinfo(0, .@target_id) + " are now married.", 2;
- end;
-
-OnTimeout:
- goto L_TooLate;
-
-L_TooLate:
- message strcharinfo(0), "Marriage : ##3##BThe proposal expired. Please try again.";
- message strcharinfo(0, @marriage[0]), "Marriage : ##3##BThe proposal expired. Please try again.";
- @marriage[0] = 0, @marriage[0];
- @marriage[0] = 0;
- end;
-
-L_AlreadyMarried:
- message strcharinfo(0), "Marriage : ##3##BYou"+ if_then_else(PARTNER, " are", "r partner is") +" already married.";
- end;
-
-OnInit:
- set .timeout, 30; // timeout for proposal
- registercmd "#marry", strnpcinfo(0); // we NEED to use a # before `marry` because otherwise manaplus does not strip colors
- end;
-}
diff --git a/npc/commands/music.txt b/npc/commands/music.txt
deleted file mode 100755
index 5c62f008..00000000
--- a/npc/commands/music.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-- script @music NPC32767,{
- callfunc "argv_splitter";
- if (GM < CMD_MUSIC && GM < G_SYSOP) goto L_GM;
- if (@argv$[0] == "") goto L_Failed;
-
- gmlog "@music " + @args$;
- .@find = array_search(@argv$[0], .find$);
- if (.@find >= 0)
- @argv$[0] = .replace$[max(.@find, 0)];
- .file$ = @argv$[0];
- areatimer 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), 0, strnpcinfo(0)+"::OnPC";
- message strcharinfo(0), "music : The music has ben temporarily changed.";
- end;
-
-OnPC:
- music .file$ + ".ogg";
- end;
-
-L_Failed:
- message strcharinfo(0), "music : You must specify a music file.";
- end;
-
-L_GM:
- message strcharinfo(0), "music : GM command is level "+ CMD_MUSIC +", but you are level " + GM;
- end;
-
-OnInit:
- setarray .find$, "this", "cave", "chilling", "clouds", "dimond", "explorers", "gy", "reid", "magick", "mystique", "night", "ride", "sail", "snow", "forest", "xmas";
- setarray .replace$, "this", "cavesong", "chilling-environment", "clouds-calling", "dimonds-cove", "explorers-melody", "graveyard", "inquisitive-inn", "magick-real", "mystique-forest", "night-is-calling", "ride-of-the-valkyries", "sail-away", "snow-village", "the-forest", "white-christmas";
- set .range, 14; // FIXME: make this a const
- registercmd chr(ATCMD_SYMBOL) + "music", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/mute.txt b/npc/commands/mute.txt
deleted file mode 100755
index 555fa724..00000000
--- a/npc/commands/mute.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-- script @mute NPC32767,{
- callfunc "argv_splitter";
- if (GM < CMD_MUTE && GM < G_SYSOP) goto L_GM;
- if (@argv$[1] == "") goto L_Failed;
-
- .@target_id = getcharid(3, @argv$[1]);
- if (.@target_id < 1 || !(isloggedin(.@target_id))) goto L_Failed;
-
- if (@argv[0] > 120)
- set @argv[0], 120; // max 2 hours
-
- gmlog "@mute " + @args$;
- if (@argv[0] == 0) goto L_UnMute;
-
- message strcharinfo(0, .@target_id), "Server : ##BYou have been muted by a GM for "+@argv[0]+" minutes.";
- MUTE_GLOBAL = 1, .@target_id;
- #MUTE_UNTIL = (gettimetick(2) + (@argv[0] * 60)), .@target_id;
- addtimer (@argv[0] * 60000) + 100, strnpcinfo(0) + "::OnCheckMute", .@target_id;
- message strcharinfo(0), "mute : Player `"+strcharinfo(0, .@target_id)+"` has been muted for "+@argv[0]+" minutes.";
- end;
-
-OnSTFU:
- if (GM < CMD_MUTE && GM < G_SYSOP) goto L_GM;
- callfunc "argv_splitter";
- if (@argv[0] < 1) set @argv[0], 1;
- if (@argv[0] > 10) set @argv[0], 10;
- gmlog "@stfu " + @argv[0];
- @stfu_nr = 0;
- foreach 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), strnpcinfo(0)+"::OnSTFUPC";
- message strcharinfo(0), "mute : Every player within "+.range+" tiles have been muted for "+@argv[0]+" minutes. ["+@stfu_nr+"]";
- @stfu_nr = 0;
- end;
-
-OnSTFUPC:
- if (@target_id == BL_ID) end;
- .@future = (gettimetick(2) + (@argv[0] * 60));
- if (get(#MUTE_UNTIL, @target_id) > .@future) end; // if player already has a mute, don't reduce it
- MUTE_GLOBAL = 1, @target_id;
- #MUTE_UNTIL = .@future, @target_id;
- addtimer (@argv[0] * 60000) + 100, strnpcinfo(0) + "::OnCheckMute", @target_id;
- @stfu_nr = @stfu_nr + 1;
- end;
-
-OnPCLoginEvent:
- if (#MUTE_UNTIL < 1) end;
- .@s = (#MUTE_UNTIL - gettimetick(2));
- if (.@s < 5) goto L_ClearMute;
- .@m = .@s / 60;
- message strcharinfo(0), "Server : ##BYou have been muted for "+ max(1, .@m) +" minutes.";
- MUTE_GLOBAL = 1;
- addtimer (.@s * 1000) + 100, strnpcinfo(0) + "::OnCheckMute";
- end;
-
-L_ClearMute:
- message strcharinfo(0), "Server : ##BYour mute has expired while you were away. You have been automatically unmuted.";
- if ((#MUTE_UNTIL - gettimetick(2)) >= (0 - 900))
- wgm "=> Player `"+ strcharinfo(0) +"` has been automatically unmuted."; // only send if unmuted 15 minutes ago or less
- #MUTE_UNTIL = 0;
- end;
-
-OnCheckMute:
- if (#MUTE_UNTIL < 1) end;
- if (gettimetick(2) - #MUTE_UNTIL < 0) end;
- message strcharinfo(0), "Server : ##BYou have been automatically unmuted.";
- wgm "=> Player `"+ strcharinfo(0) +"` has been automatically unmuted.";
- MUTE_GLOBAL = 0;
- #MUTE_UNTIL = 0;
- end;
-
-L_UnMute:
- if (get(MUTE_GLOBAL, .@target_id))
- message strcharinfo(0, .@target_id), "Server : ##BYou have been unmuted by a GM.";
- MUTE_GLOBAL = 0, .@target_id;
- #MUTE_UNTIL = 0, .@target_id;
- message strcharinfo(0), "mute : Player `"+strcharinfo(0, .@target_id)+"` has been unmuted.";
- end;
-
-L_Failed:
- message strcharinfo(0), "mute : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), "mute : GM command is level "+ CMD_MUTE +", but you are level " + GM;
- end;
-
-OnInit:
- set .range, 14; // FIXME: this should be a const
- registercmd chr(ATCMD_SYMBOL) + "mute", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "stfu", strnpcinfo(0) + "::OnSTFU";
- registercmd chr(ATCMD_SYMBOL) + "areamute", strnpcinfo(0) + "::OnSTFU"; // alias of STFU
- end;
-}
diff --git a/npc/commands/npctalk.txt b/npc/commands/npctalk.txt
deleted file mode 100755
index b1179dc6..00000000
--- a/npc/commands/npctalk.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-- script @npctalk NPC32767,{
- callfunc "argv_splitter";
- if (GM < CMD_NPCTALK && GM < G_SYSOP) goto L_GM;
- if (@argv$[0] == "" || @argv$[1] == "") goto L_Failed;
- if (getnpcid(@argv$[0]) < 1) goto L_Failed;
-
- gmlog "@npctalk " + @args$;
- npctalk @argv$[0], @argv$[1];
- end;
-
-L_Failed:
- message strcharinfo(0), "npctalk : Impossible to attach to the target npc. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), "npctalk : GM command is level "+ CMD_NPCTALK +", but you are level " + GM;
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "npctalk", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/numa.txt b/npc/commands/numa.txt
index 92cd5c9d..b8b0a924 100755
--- a/npc/commands/numa.txt
+++ b/npc/commands/numa.txt
@@ -1,8 +1,8 @@
- script SuperDebug NPC32767,{
if (GM < MAP_LOUNGE && GM < G_SYSOP && !debug) goto L_GM2; // make sure you can enter the gm lounge
- if (target(BL_ID,getnpcid("Numa"),1)) goto L_Main;
- npcaction 6, 12;
+ //if (target(BL_ID,getnpcid("Numa"),1)) goto L_Main;
+ //npcaction 6, 12;
title "Numa";
goto L_Main;
@@ -78,51 +78,18 @@ L_GM:
goto L_close;
L_Quest:
- callfunc "QuestDebug";
+ callfunc "GlobalQuestDebug";
goto L_close;
L_close:
close;
OnInit:
- registercmd chr(ATCMD_SYMBOL) + "numa", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "superdebug", strnpcinfo(0);
+ registercmd "numa", strnpcinfo(0);
+ registercmd "superdebug", strnpcinfo(0);
end;
}
017-9,30,28,0 duplicate(SuperDebug) Numa NPC393
-function script QuestDebug {
- goto L_ChooseContinent;
-L_ChooseContinent:
- mes "Choose a continent.";
- next;
- menu
- "Argeas", L_Argeas,
- "Close", L_Return;
-
-L_Argeas:
- mes "Choose an area.";
- next;
- menu
- "Woodland", L_Woodland,
- "Choose a continent", L_ChooseContinent,
- "Close", L_Return;
-
-L_Woodland:
- mes "Choose a quest.";
- next;
- menu
- "Illia Sisters", L_Valia,
- "Choose an area", L_Argeas,
- "Close", L_Return;
-
-L_Return:
- return;
-
-
-L_Valia:
- callfunc "IlliaDebug";
- goto L_Return;
-}
diff --git a/npc/commands/pullrabbit.txt b/npc/commands/pullrabbit.txt
deleted file mode 100755
index 8ca0a69a..00000000
--- a/npc/commands/pullrabbit.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-- script @pullrabbit NPC32767,{
- if (GM < EVT_KILLTHEGM && GM < G_SYSOP) end;
- if (getequipid(equip_head) != 888) end;
- getinventorylist;
- if ((checkweight("MurdererCrown", 1) == 0) || (@inventorylist_count == 100))
- goto L_Inventory;
- // Get the current reward of the event. This may be changed later
- getitem "MurdererCrown", 1;
- // Set HP and SP to max
- heal MaxHp, MaxSp;
- // Display an effect
- misceffect FX_CHANNELLING_CAST, strcharinfo(0);
- // Log the usage of this spell
- gmlog strcharinfo(0)+" used the Magic GM Top Hat.";
- end;
-
-L_Inventory:
- message strcharinfo(0), "You cannot create this item. You're too heavy or you don't have a free slot.";
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "pullrabbit", strnpcinfo(0);
- registercmd chr(MAGIC_SYMBOL) + "pullrabbit", strnpcinfo(0); // former pullrabbit invocation
- end;
-}
diff --git a/npc/commands/python.txt b/npc/commands/python.txt
new file mode 100644
index 00000000..5273731c
--- /dev/null
+++ b/npc/commands/python.txt
@@ -0,0 +1,27 @@
+// The Mana World script
+// Author: Gumi <gumi@themanaworld.org>
+// Author: Jesusalva <jesusalva@themanaworld.org>
+//
+// Stomp stomp stomp (use with caution)
+
+- script @python 32767,{
+ end;
+
+OnCall:
+ specialeffect(34, AREA, playerattached());
+ .@zone$=getmapinfo(MAPINFO_ZONE, .@mapa$);
+ if (.@zone$ == "MMO")
+ end;
+ sc_start SC_CASH_DEATHPENALTY, 1000, 1;
+ addtimer 380, .name$+"::OnKill";
+ end;
+
+OnKill:
+ percentheal -100, -100;
+ //dispbottom l("Oh look, it is Cupid!");
+ end;
+
+OnInit:
+ bindatcmd "python", "@python::OnCall", 60, 60, 1;
+ end;
+}
diff --git a/npc/commands/remotecmd.txt b/npc/commands/remotecmd.txt
deleted file mode 100755
index 20f33f8c..00000000
--- a/npc/commands/remotecmd.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-- script @remotecmd NPC32767,{
- callfunc "argv_splitter";
- if (GM < CMD_REMOTECMD && GM < G_SYSOP) goto L_GM; // check if you can use it on self
- .@target_id = BL_ID;
- .@t$ = @argv$[1];
- setarray @remotecmd[0], 0, 1, 1; // nr, to_self, multi_target
- if (.@t$ == "map") goto L_Map;
- if (.@t$ == "map!") goto L_AllMap;
- if (.@t$ == "area") goto L_Area;
- if (.@t$ == "area!") goto L_AllArea;
- if (.@t$ != "") set .@target_id, getcharid(3, @argv$[1]);
- if (.@t$ != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self
-
- @target_id = .@target_id;
- set @remotecmd[2], 0; // only one target
- addtimer 0, strnpcinfo(0) + "::OnPC";
- end;
-
-L_Map:
- set @remotecmd[1], 0; // do not include self
- goto L_AllMap;
-
-L_AllMap:
- foreach 0, getmapname(), 0, 0, 32767, 32767, strnpcinfo(0)+"::OnPC";
- goto L_Success;
-
-L_Area:
- set @remotecmd[1], 0; // do not include self
- goto L_AllMap;
-
-L_AllArea:
- foreach 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), strnpcinfo(0)+"::OnPC";
- goto L_Success;
-
-OnPC:
- if (@target_id == BL_ID && @remotecmd[1] < 1)
- end;
- remotecmd @argv$[0], strcharinfo(0, @target_id);
- @remotecmd[0] = @remotecmd[0] + 1;
- if (@remotecmd[2] < 1)
- goto L_Success;
- end;
-
-L_Success:
- gmlog "@remotecmd " + @args$;
- message strcharinfo(0), "remotecmd : The operation succeeded. ["+ @remotecmd[0] + "]";
- end;
-
-L_Failed:
- message strcharinfo(0), "remotecmd : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), "remotecmd : GM command is level "+ CMD_REMOTECMD +", but you are level " + GM;
- end;
-
-OnInit:
- set .range, 14; // visible range
- registercmd chr(ATCMD_SYMBOL) + "remotecmd", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/resync.txt b/npc/commands/resync.txt
new file mode 100644
index 00000000..246bfa88
--- /dev/null
+++ b/npc/commands/resync.txt
@@ -0,0 +1,45 @@
+// TMW2 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
+//
+// This also introduces @resyncall
+// Which is an alias for @refresh and causes client to reload the whole map,
+// Including yourself and monsters.
+
+- script @resync 32767,{
+ end;
+
+// Soft Resync
+OnCall:
+ if (ispcdead()) {
+ dispbottom l("Impossible to resync: You are dead.");
+ end;
+ }
+ if (@rsync_delay > gettimetick(2)) {
+ dispbottom l("Not resync'ing to prevent flood.");
+ end;
+ }
+ getmapxy(.@m$, .@x, .@y, 0);
+ slide .@x, .@y;
+ @rsync_delay=gettimetick(2)+rand2(3,5);
+ end;
+
+// Hard Resync
+OnCallRefresh:
+ if (@rsync_delay > gettimetick(2)) {
+ dispbottom l("Not resync'ing to prevent flood.");
+ end;
+ }
+ @rsync_delay=gettimetick(2)+rand2(3,5);
+ atcommand("@refresh");
+ end;
+
+OnInit:
+ bindatcmd "resync", "@resync::OnCall", 0, 60, 0;
+ bindatcmd "resyncall", "@resync::OnCallRefresh", 0, 60, 0;
+ end;
+}
diff --git a/npc/commands/warp.txt b/npc/commands/warp.txt
deleted file mode 100755
index d2eee4e2..00000000
--- a/npc/commands/warp.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-- script @warp NPC32767,{
- callfunc "argv_splitter";
- if (@argv$[3] == "" && @argv$[2] == "" && @argv$[1] != "" && @argv[1] < 2)
- @argv$[3] = @argv$[1];
-
- .@n$ = if_then_else(@argv$[3] != "", "char", "") + "warp";
- if (GM < CMD_WARP && GM < G_SYSOP) goto L_GM; // check if you can use it on self
- .@target_id = BL_ID;
- if (@argv$[3] != "") set .@target_id, getcharid(3, @argv$[3]);
- if (@argv$[3] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self
- if (@argv$[3] != "" && GM < CMD_CHARWARP && GM < G_SYSOP) goto L_GM; // when target is not self, use charwarp permission
-
- if (@argv$[0] == "")
- @argv$[0] = getmapname();
-
- setarray @map_anchor$[0], "", "", "", "";
- callfunc "map2anchor";
-
- gmlog "@"+.@n$+" " + @args$;
- if (.@target_id != BL_ID)
- message strcharinfo(0), .@n$+" : The operation succeeded.";
-
- @GMWARP_map$ = @map_anchor$[0];
- @GMWARP_x = if_then_else(@argv[1] > 1, @argv[1], @map_anchor$[1]);
- @GMWARP_y = if_then_else(@argv[2] > 1, @argv[2], @map_anchor$[2]);
- addtimer 0, strnpcinfo(0) + "::OnWarp", .@target_id;
-
- if (@map_anchor$[3] == "no" && @knows_anchors < 1)
- goto L_SuggestAnchors;
- end;
-
-L_SuggestAnchors:
- message strcharinfo(0), .@n$+" : The warp command has been improved. You might want to consider using [@@https://www.themanaworld.org/index.php/Dev:GM_Commands/anchors|map anchors@@].";
- @knows_anchors = 1;
- end;
-
-OnWarp:
- warp @GMWARP_map$, @GMWARP_x, @GMWARP_y;
- @GMWARP_map$ = "";
- @GMWARP_x = 0;
- @GMWARP_y = 0;
- end;
-
-L_Failed:
- message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_GM:
- message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARWARP, CMD_WARP) +", but you are level " + GM;
- end;
-
-OnInit:
- registercmd chr(ATCMD_SYMBOL) + "warp", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "charwarp", strnpcinfo(0);
- end;
-}
diff --git a/npc/commands/zeny.txt b/npc/commands/zeny.txt
deleted file mode 100755
index de1013eb..00000000
--- a/npc/commands/zeny.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-- script @zeny NPC32767,{
- callfunc "argv_splitter";
- .@n$ = if_then_else(@argv$[1] != "", "char", "") + "Zeny";
- if (GM < CMD_ZENY && GM < G_SYSOP) goto L_GM; // check if you can use it on self
- .@target_id = BL_ID;
- if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]);
- if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self
- if (@argv$[1] != "" && GM < CMD_CHARZENY && GM < G_SYSOP) goto L_GM; // when target is not self, use charZeny permission
- if (@argv$[0] == "--") goto L_Remove;
- if (@argv$[0] == "---") goto L_RemoveAll;
- if (@argv$[0] == "++") goto L_Max;
- if (@argv$[0] == "+++") goto L_MaxAll;
- set .@delta, @argv[0]; // ± Zeny
- set .@Zeny, get(Zeny, .@target_id); // get the number of Zeny in char
- set .@bank, get(#BankAccount, .@target_id); // get number of Zeny in (world) account
- set .@new_Zeny, .@Zeny + .@delta; // new balance in char
- if (.@new_Zeny < 0) goto L_MaybeRemoveBank; // Zeny would be below 0 so check if we can take from bank
- if (.@new_Zeny > .max_Zeny) goto L_MaybeAddBank; // Zeny would be over the limit so check if we can store in bank
- Zeny = (.@Zeny + .@delta), .@target_id;
- goto L_Success;
-
-L_Remove:
- Zeny = 0, .@target_id;
- goto L_Success;
-
-L_RemoveAll:
- Zeny = 0, .@target_id;
- #BankAccount = 0, .@target_id;
- goto L_Success;
-
-L_Max:
- Zeny = .max_Zeny, .@target_id;
- goto L_Success;
-
-L_MaxAll:
- Zeny = .max_Zeny, .@target_id;
- #BankAccount = .max_int, .@target_id;
- goto L_Success;
-
-L_MaybeAddBank:
- .@new_bank = (.@bank + (.@new_Zeny - .max_Zeny));
- if (.@new_bank > .max_int || .@new_bank < 0) goto L_OutOfBounds;
- Zeny = .max_Zeny, .@target_id;
- #BankAccount = .@new_bank, .@target_id;
- goto L_Success;
-
-L_MaybeRemoveBank:
- if ((.@bank + .@new_Zeny) < 0) goto L_OutOfBounds;
- Zeny = 0, .@target_id;
- #BankAccount = (.@bank + .@new_Zeny), .@target_id;
- goto L_Success;
-
-L_OutOfBounds:
- // XXX: maybe we could also take from other chars from the same accout?
- message strcharinfo(0), .@n$+" : Impossible to proceed! This would cause the player to have less than 0 Zeny or more than " + .max_int + ".";
- end;
-
-L_Failed:
- // XXX: should we allow GMs to change Zeny of users that are not logged in?
- message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?";
- end;
-
-L_Success:
- gmlog "@Zeny " + @args$;
- message strcharinfo(0), .@n$+" : The operation succeeded.";
- end;
-
-L_GM:
- message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARZENY, CMD_ZENY) +", but you are level " + GM;
- end;
-
-OnInit:
- set .max_Zeny, 1000000000; // hardcoded in tmwa
- set .max_int, 2147483647; // max int32 value
- registercmd chr(ATCMD_SYMBOL) + "Zeny", strnpcinfo(0);
- registercmd chr(ATCMD_SYMBOL) + "charZeny", strnpcinfo(0);
- end;
-}
diff --git a/npc/functions/array.txt b/npc/functions/array.txt
new file mode 100644
index 00000000..d433abd7
--- /dev/null
+++ b/npc/functions/array.txt
@@ -0,0 +1,464 @@
+// Evol Script
+// Author: Gumi
+
+// array_pad(<array>, <size>, <value>)
+// prepend or append <value> until the array is of <size> size
+// returns the amount added on success, or false (0) if nothing changed
+
+function script array_pad {
+ .@index = getarrayindex(getarg(0)); // passed index
+ .@count = getarraysize(getarg(0)) - .@index; // actual size
+ .@size = getarg(1); // desired size
+ .@absolute = (.@size >= 0 ? .@size : -(.@size)); // |size|
+ .@delta = .@absolute - .@count; // amount to fill
+
+ if (.@absolute <= .@count) {
+ return false; // nothing to do
+ }
+
+ if (.@size < 0) {
+ copyarray(getelementofarray(getarg(0), .@index + .@delta), getarg(0), .@count); // shift to the right
+ cleararray(getarg(0), getarg(2), .@delta); // prepend
+ } else {
+ cleararray(getelementofarray(getarg(0), .@index + .@count), getarg(2), .@delta); // append
+ }
+
+ return .@delta;
+}
+
+
+
+// array_replace(<array>, <needle>, <replace>{, <neq>})
+// replace every occurence of <needle> with <replace>
+// returns the number of replaced elements
+
+function script array_replace {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(3, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ set(getelementofarray(getarg(0), .@i), getarg(2));
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_find(<array>, <needle>{, <neq>})
+// return the index of the first occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_find {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_rfind(<array>, <needle>{, <neq>})
+// return the index of the last occurence of <needle> in <array>
+// if not found it returns -1
+
+function script array_rfind {
+ .@min = getarrayindex(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = (getarraysize(getarg(0)) - 1); .@i >= .@min; --.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ freeloop(false);
+ return .@i;
+ }
+ }
+
+ freeloop(false);
+ return -1;
+}
+
+
+
+// array_exists(<array>, <needle>{, <neq>})
+// return true or false accordingly if <needle> is found in <array>
+
+function script array_exists {
+ return array_find(getarg(0), getarg(1), getarg(2, false)) > -1;
+}
+
+
+
+// array_count(<array>, <needle>{, <neq>})
+// counts the number of occurrence of <needle> in the <array>
+
+function script array_count {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ ++.@count;
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_entries(<array>)
+// returns the number of non-empty entries
+
+function script array_entries {
+ if (isstr(getarg(0)) == 1) {
+ return array_count(getarg(0), "", true);
+ }
+ return array_count(getarg(0), 0, true);
+}
+
+
+
+// array_remove(<array>, <needle>{, <neq>})
+// removes every occurrence of <needle> in the <array> while shifting left
+
+function script array_remove {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) ||
+ (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_reverse(<array>)
+// reverses the array
+
+function script array_reverse {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = .@index; .@i < ((.@size + .@index) / 2); ++.@i) {
+ swap(getelementofarray(getarg(0), .@i), getelementofarray(getarg(0), .@size + .@index - 1 - .@i)); // a <> b
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_sum(<array>)
+// return the sum of every element of the array
+
+function script array_sum {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@sum += getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@sum;
+}
+
+
+
+// array_difference(<array>)
+// return the difference of every element of the array
+
+function script array_difference {
+ .@size = getarraysize(getarg(0));
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@diff -= getelementofarray(getarg(0), .@i);
+ }
+
+ freeloop(false);
+ return .@diff;
+}
+
+
+
+// array_shift(<array>)
+// returns the first element of the array and removes it, while shifting left
+
+function script array_shift {
+ if (isstr(getarg(0)) == 1) {
+ .@val$ = getarg(0);
+ } else {
+ .@int = true;
+ .@val = getarg(0);
+ }
+
+ deletearray(getarg(0), 1); // shift left
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// array_unshift(<array>, <value>)
+// adds <value> to the start of the array, while shifting right
+// returns the new size
+
+function script array_unshift {
+ .@size = getarraysize(getarg(0)) + 1;
+ array_pad(getarg(0), -(.@size - getarrayindex(getarg(0))), getarg(1));
+ return .@size;
+}
+
+
+
+// array_pop(<array>)
+// returns the last element of the array and removes it
+
+function script array_pop {
+ .@last = getarraysize(getarg(0)) - 1;
+
+ if (isstr(getelementofarray(getarg(0), .@last)) == 1) {
+ .@val$ = getelementofarray(getarg(0), .@last);
+ } else {
+ .@int = true;
+ .@val = getelementofarray(getarg(0), .@last);
+ }
+
+ deletearray(getelementofarray(getarg(0), .@last), 1);
+
+ return .@int ? .@val : .@val$;
+}
+
+
+
+// TODO: Rename to array_append >.<
+// array_push(<array>, <value>)
+// adds <value> to the end of the array
+// returns the new size
+
+function script array_push {
+ .@size = getarraysize(getarg(0));
+ set(getelementofarray(getarg(0), .@size), getarg(1));
+ return .@size + 1;
+}
+
+
+
+// array_shuffle(<array>)
+// shuffles the array
+
+function script array_shuffle {
+ .@index = getarrayindex(getarg(0));
+ .@size = getarraysize(getarg(0)) - .@index;
+ freeloop(true);
+
+ if (isstr(getarg(0)) == 1) {
+ copyarray(.@tmp$[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp$[rand(.@size)]));
+ }
+ } else {
+ copyarray(.@tmp[0], getarg(0), .@size);
+ for (; .@size >= 1; --.@size) {
+ set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp[rand(.@size)]));
+ }
+ }
+
+ freeloop(false);
+ return true;
+}
+
+
+
+// array_unique(<array>{, <threshold>})
+// allows entries to appear up to <threshold> in the array
+
+function script array_unique {
+ .@size = getarraysize(getarg(0));
+ .@max = getarg(1, 1);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@count = 1;
+ for (.@e = .@i + 1; .@e < .@size; ++.@e) {
+ if (getelementofarray(getarg(0), .@i) == getelementofarray(getarg(0), .@e)) {
+ if (++.@count >= .@max) {
+ deletearray(getelementofarray(getarg(0), .@e), 1);
+ ++.@removed; // increase counter
+ --.@size; // reduce size
+ --.@e; // step back
+ }
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@removed;
+}
+
+
+
+// array_diff(<array1>, <array2>{, <array>...}, <array>)
+// compares array1 against one or more other arrays and fills the last array
+// with the values in array1 that are not present in any of the other arrays
+// returns the number of entries not matching
+
+function script array_diff {
+ .@size = getarraysize(getarg(0));
+ .@index = getarrayindex(getarg(0));
+ freeloop(true);
+
+ for (.@a = 1; .@a < (getargcount() - 1); ++.@a) {
+ for (.@i = .@index; .@i < .@size; ++.@i) {
+ if (!array_exists(getarg(.@a), getelementofarray(getarg(0), .@i))) {
+ array_push(getarg(getargcount() - 1), getelementofarray(getarg(0), .@i));
+ ++.@count;
+ }
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+
+
+// array_filter(<array>, "<function>")
+// filters the array using a callback function
+
+function script array_filter {
+ .@size = getarraysize(getarg(0));
+ .@neq = getarg(2, false);
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ .@eq = callfunc(getarg(1), getelementofarray(getarg(0), .@i)) != false;
+ if ((.@neq && .@eq) || (!(.@neq) && !(.@eq))) {
+ deletearray(getelementofarray(getarg(0), .@i), 1); // shift left
+ ++.@count; // increase the counter
+ --.@size; // reduce the size
+ --.@i; // step back
+ }
+ }
+
+ freeloop(false);
+ return .@count;
+}
+
+// array_highest(<array>)
+// Returns the index of the highest value in <array>
+// NOTE: Array must be an INT array!
+
+function script array_highest {
+ .@size = getarraysize(getarg(0));
+ .@win=0;
+ .@idx=0;
+ .@dw=false;
+ freeloop(true);
+
+ for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) {
+ if (getelementofarray(getarg(0), .@i) > .@win) {
+ .@win=getelementofarray(getarg(0), .@i);
+ .@idx=.@i;
+ if (.@dw) {
+ deletearray .@draw;
+ .@dw=false;
+ }
+ } else if (getelementofarray(getarg(0), .@i) == .@win) {
+ if (!.@dw)
+ array_push(.@draw, .@idx);
+ array_push(.@draw, .@i);
+ .@dw=true;
+ }
+ }
+
+ // Will we return .@idx or do we need to draw a loot?
+ freeloop(false);
+ if (.@dw)
+ return any_of(.@draw);
+ else
+ return .@idx;
+}
+
+// relative_array_random(<array: 0, {[value, probability]..}>)
+// returns a random entry from the array, by relative probability
+// the first key of the array should be 0 and every entries are a tuple
+// of [value, probability]
+
+function script relative_array_random {
+ .@is_str = getdatatype(getarg(0)) & DATATYPE_STR;
+ .@total_prob = getelementofarray(getarg(0), 0);
+ .@initial_index = getarrayindex(getarg(0));
+ .@initial_index = .@initial_index ? .@initial_index : 1;
+ freeloop(true);
+
+ if (.@total_prob < 1 || getarg(1, false))
+ {
+ // first calculation, or forced re-calculation
+ .@total_prob = 0;
+ .@size = getarraysize(getarg(0));
+
+ for (.@i = .@initial_index + 1; .@i < .@size; .@i += 2) {
+ if (.@is_str) {
+ .@total_prob += max(1, atoi(getelementofarray(getarg(0), .@i)));
+ } else {
+ .@total_prob += max(1, getelementofarray(getarg(0), .@i));
+ }
+ }
+
+ // we cache on the first key
+ set(getelementofarray(getarg(0), 0), .@total_prob);
+ }
+
+ .@target_sum = rand(0, .@total_prob);
+
+ for (.@i = .@initial_index; .@sum < .@target_sum; .@i += 2) {
+ if (.@is_str) {
+ .@sum += atoi(getelementofarray(getarg(0), .@i + 1));
+ } else {
+ .@sum += getelementofarray(getarg(0), .@i + 1);
+ }
+
+ if (.@sum >= .@target_sum) {
+ break;
+ }
+ }
+
+ freeloop(false);
+ return getelementofarray(getarg(0), .@i);
+}
+
diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt
new file mode 100644
index 00000000..32e0f7bc
--- /dev/null
+++ b/npc/functions/asklanguage.txt
@@ -0,0 +1,72 @@
+// TMW2 script
+// Evol functions.
+// Author:
+// Reid, Jesusalva
+// Description:
+// Function setting the player language
+
+function script languagecode {
+ switch (Lang) {
+ case LANG_PTBR:
+ return "pt_BR";
+ case LANG_FR:
+ return "fr";
+ case LANG_DE:
+ return "de";
+ case LANG_ES:
+ return "es";
+ default:
+ return "en";
+ }
+}
+
+function script asklanguage {
+
+ dispbottom col("We need help with translations. [@@help://translate|Learn more@@]", 1);
+ switch (getarg(0, LANG_IN_SHIP))
+ {
+ case LANG_ON_SEA:
+ setarray .@messages$[0], "I hear you... (English)", // English
+ "Eu te ouço... (Português)", // Portuguese
+ "Je vous entends... (Français)", // French
+ "Ich höre euch... (Deutsch)", // German
+ "Te oigo... (Español)"; // Spanish
+ break;
+ case LANG_IN_SHIP:
+ setarray .@messages$[0], "I speak English.", // English
+ "Eu falo Português.", // Portuguese
+ "Je parle français.", // French
+ "Ich spreche Deutsch.", // German
+ "Hablo Español."; // Spanish
+ break;
+ default:
+ return;
+ }
+
+ setarray .@flags$[0], "flags/en",
+ "flags/pt_BR",
+ "flags/fr",
+ "flags/de",
+ "flags/es";
+
+ .@menustr$ = "";
+ .@separator$ = ":";
+
+ for (.@i = 0; .@i <= MAX_LANG; .@i++)
+ {
+ if (.@i == MAX_LANG) {
+ .@separator$ = "";
+ }
+ .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$;
+ }
+
+ select(.@menustr$);
+
+ .@lang = @menu - 1;
+
+ if (.@lang >= 0 || .@lang <= MAX_LANG) {
+ Lang = .@lang;
+ }
+
+ return;
+}
diff --git a/npc/functions/filters.txt b/npc/functions/filters.txt
new file mode 100644
index 00000000..01e3a856
--- /dev/null
+++ b/npc/functions/filters.txt
@@ -0,0 +1,126 @@
+// TMW2 scripts.
+// Authors:
+// Jesusalva
+// Description:
+// Several filters
+
+// filter_always( id )
+function script filter_always {
+ return true;
+}
+
+// filter_onlyme( id )
+function script filter_onlyme {
+ return (getarg(0) == getcharid(3));
+}
+
+// filter_notme( id )
+function script filter_notme {
+ return (getarg(0) != getcharid(3));
+}
+
+// filter_sameguild( id )
+function script filter_sameguild {
+ if (getcharid(2) < 1)
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameguildnotyou( id )
+function script filter_sameguildnotyou {
+ if (getcharid(2) < 1)
+ return false;
+ if (getarg(0) == getcharid(3))
+ return false;
+ return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+}
+
+// filter_sameparty( id )
+function script filter_sameparty {
+ if (getcharid(1) < 1 && getarg(0) != getcharid(3))
+ return false;
+ return (strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+}
+
+// filter_sameguildorparty( id )
+function script filter_sameguildorparty {
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_sameguildorpartynotyou( id )
+function script filter_sameguildorpartynotyou {
+ if (getarg(0) == getcharid(3))
+ return false;
+ if (getcharid(2) < 1 && getcharid(1) < 1)
+ return false;
+ .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1));
+ .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2));
+ return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild));
+}
+
+// filter_hostile( id )
+function script filter_hostile {
+ //.@type=getunitdata(getarg(0), UDT_TYPE);
+ .@type=getunittype(getarg(0));
+ .@chkid=getarg(0);
+
+ // Players outside PVP
+ if (.@type == UNITTYPE_PC) {
+ getmapxy(.@m$, .@x, .@y, .@type, .@chkid);
+ if (!ispvpmap(.@m$))
+ return false;
+ // FIXME: We already have !(filter_sameguildorparty())
+ // We might be over-processing this
+ // Honor party flag
+ if (!getmapflag(.@mapa$, mf_pvp_noparty) &&
+ getcharid(1) == getcharid(1, strcharinfo(0, "", .@chkid)))
+ return false;
+ // Honor guild flag
+ if (!getmapflag(.@mapa$, mf_pvp_noguild) &&
+ getcharid(2) == getcharid(2, strcharinfo(0, "", .@chkid)))
+ return false;
+ }
+
+ // Monsters
+ if (.@type == UNITTYPE_MOB)
+ return true;
+
+ // NPCs
+ if (.@type == UNITTYPE_NPC)
+ return false;
+
+ // Homunculus
+ if (.@type == UNITTYPE_HOM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Pets
+ if (.@type == UNITTYPE_PET)
+ .@chkid=getunitdata(getarg(0), UDT_MASTERAID);
+
+ // Mercenaries
+ if (.@type == UNITTYPE_MER)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ // Elementals
+ if (.@type == UNITTYPE_ELEM)
+ .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID));
+
+ //debugmes "filter_hostile: Filtering %d (original %d) (BL %d)", .@chkid, getarg(0), .@type;
+ // Players (and slaves)
+ return !(filter_sameguildorparty(.@chkid));
+}
+
+// filter_friendly( id )
+function script filter_friendly {
+ return !(filter_hostile(getarg(0)));
+}
+
+// filter_notboss( id )
+function script filter_notboss {
+ return (!(getunitdata(getarg(0), UDT_MODE) & MD_BOSS));
+}
+
diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt
new file mode 100644
index 00000000..b5214618
--- /dev/null
+++ b/npc/functions/goodbye.txt
@@ -0,0 +1,152 @@
+// Evol functions.
+// Authors:
+// gumi
+// Reid
+// Description:
+// script terminator functions
+
+
+
+// goodbye_msg
+// Tell a random goodbye sentence.
+// Variables:
+// .@rand = Random number between the number of "goodbye" choice.
+
+function script goodbye_msg {
+ setarray .byemsg$[0],
+ l("See you!"),
+ l("See you later!"),
+ l("See you soon!"),
+ l("Bye!"),
+ l("Farewell."),
+ l("Bye then!"),
+ l("Goodbye."),
+ l("Bye for now."),
+ l("Talk to you soon!"),
+ l("Talk to you later!"),
+ l("Have a good day!"),
+ l("Cheers!"),
+ l("Take care!");
+
+ return any_of(.byemsg$);
+}
+
+
+
+// cwarp
+// Closes the dialog, then warps the player.
+// You almost always want to use this instead of `warp`.
+// usage:
+// cwarp;
+// cwarp x, y;
+// cwarp map, x, y;
+
+function script cwarp {
+ .@map$ = getarg(0, "");
+ .@x = getarg(1, 0);
+ .@y = getarg(2, 0);
+
+ if (getargcount() > 0 && getargcount() < 3)
+ {
+ .@npc$ = strnpcinfo(0);
+ .@map$ = getvariableofnpc(.map$, .@npc$);
+ .@x = getarg(0);
+ .@y = getarg(1);
+ }
+
+ getmapxy .@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC; // get char location
+
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (getargcount() < 1)
+ {
+ warp .@pc_map$, .@pc_x, .@pc_y; // no arguments, just refresh
+ close;
+ }
+
+ if (.@map$ == .@pc_map$)
+ {
+ if (.@pc_x == .@x && .@pc_y == .@y)
+ {
+ close; // same location, don't move
+ }
+
+ else
+ {
+ slide .@x, .@y; // same map, slide instead of full warp
+ close;
+ }
+ }
+
+ warp .@map$, .@x, .@y; // different map, warp to given location
+ close;
+}
+
+
+
+// cshop
+// closes the dialog, then opens a shop
+// if no npc is given, calls "#<npc> $"
+
+function script cshop {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ shop getarg(0, "#" + strnpcinfo(0) + " $");
+ //close; => the shop buildin already sends close, and is a terminator itself
+}
+
+
+
+// cstorage
+// closes the dialog, then opens storage
+
+function script cstorage {
+ closedialog; // XXX: maybe send closeclientdialog in the future
+ openstorage;
+ close;
+}
+
+
+
+// bye
+// closes the dialog without waiting for the player to press close
+// can also display an emote
+
+function script bye {
+ .@emote = getarg(0, -1);
+ closedialog; // XXX: maybe send closeclientdialog in the future
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ close;
+}
+
+
+
+// goodbye
+// same as bye, but also displays a canned message
+// can also display an emote
+
+function script goodbye {
+ npctalkonce(goodbye_msg());
+ bye getarg(0, -1);
+}
+
+
+
+// goodbye2
+// Waits for the player to press close, displays a canned message,
+// ends execution.
+// Can also display an emote
+
+function script goodbye2 {
+ .@emote = getarg(0, -1);
+
+ close2;
+ npctalkonce(goodbye_msg());
+
+ if (.@emote >= 0)
+ emotion .@emote;
+
+ end;
+}
diff --git a/npc/functions/inc_sc_bonus.txt b/npc/functions/inc_sc_bonus.txt
new file mode 100644
index 00000000..8e6b6e5b
--- /dev/null
+++ b/npc/functions/inc_sc_bonus.txt
@@ -0,0 +1,72 @@
+// TMW-2 Script.
+// Author:
+// Jesusalva
+// Description:
+// Applies effects for INC_* (STR doesn't exist)
+// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE SC_FURY
+// Doesn't works: SC_STRUP
+// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE
+/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken)
+// PS. SC_FURY causes crit rate to increase
+//
+// Variables:
+// .@delay Second of buffing
+// .@type SC_*
+// .@min Min amount of type
+// .@max Max amount of type (optional)
+
+// SC_Bonus(delay, SC, min{, max})
+function script SC_Bonus {
+ .@delay=getarg(0);
+ .@type=getarg(1);
+ .@min=getarg(2);
+ .@max=getarg(3, .@min);
+ if (.@delay <= 0)
+ return false;
+
+ // Get the bonus value
+ if (.@min != .@max)
+ .@bonus=rand2(.@min, .@max);
+ else
+ .@bonus=.@min;
+
+ // Remaining time and effect conversion
+ .@v=getstatus(.@type, 1);
+ .@t=getstatus(.@type, 5);
+
+ // Convert remaining time to seconds, rounded down
+ if (.@t > 1000)
+ .@t=.@t/1000;
+ else
+ .@t=0;
+
+ // If there was effect previously, get ponderate average
+ if (.@v > 0)
+ .@v=ponderate_avg(.@bonus, .@delay, .@v, .@t);
+ else
+ .@v=.@bonus;
+
+ // Update time value to ms and to stack
+ .@t+=.@delay;
+ .@t*=1000;
+
+ // Debug print if needed
+ if (debug || $@GM_OVERRIDE)
+ debugmes "Effect %d (+%d percent) for %d ms", .@type, .@bonus, .@t;
+
+ // Restart the bonus
+ sc_end .@type;
+ sc_start .@type,.@t,.@v;
+ return true;
+}
+
+- script inc_sc_bonus -1,{
+OnUse:
+ SC_Bonus(@delay, @type, @min, @max);
+ @delay=0;
+ @type=0;
+ @min=0;
+ @max=0;
+ end;
+}
+
diff --git a/npc/functions/input.txt b/npc/functions/input.txt
new file mode 100644
index 00000000..0a510b74
--- /dev/null
+++ b/npc/functions/input.txt
@@ -0,0 +1,110 @@
+// Evol functions.
+// Author:
+// 4144
+// Jesusalva
+// Description:
+// Input utility functions
+// Variables:
+// none
+
+function script menuint {
+ deletearray .@vals;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ .@vals[.@cnt] = -1;
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return -1;
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
+function script menustr {
+ deletearray .@vals$;
+ .@menustr$ = "";
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 2)
+ {
+ if (getarg(.@f) != "")
+ {
+ .@menustr$ = .@menustr$ + getarg(.@f) + ":";
+ .@vals$[.@cnt] = getarg(.@f + 1);
+ .@cnt ++;
+ }
+ }
+
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ if (@menu == 255)
+ return "";
+
+ @menu --;
+ if (@menu < 0 || @menu >= getarraysize(.@vals$))
+ return "";
+
+ @menuret$ = .@vals$[@menu];
+ return @menuret$;
+}
+
+// menuint2(<array>)
+function script menuint2 {
+ .@menustr$="";
+
+ if (!(getdatatype(getarg(0)) & DATATYPE_VAR))
+ Exception("Inadequate argument type - Must be var", RB_DEFAULT|RB_ISFATAL);
+
+ copyarray(.@ar$, getarg(0), getarraysize(getarg(0)));
+
+ if (getarraysize(.@ar$) % 2 != 0)
+ Exception("Invalid array size: "+getarraysize(.@ar$), RB_DEFAULT|RB_ISFATAL);
+
+ freeloop(true);
+ for (.@f=0; .@f < getarraysize(.@ar$); .@f++) {
+ // String vs Int
+ if (.@f % 2 == 0) {
+ .@menustr$+=.@ar$[.@f]+":";
+ } else {
+ array_push(.@vals, atoi(.@ar$[.@f]));
+ }
+ }
+ freeloop(false);
+
+ // Do the request
+ // We have: .@vals and .@menustr$
+ @menu = 255;
+ @menuret = -1;
+ select(.@menustr$);
+ //debugmes "Option %d", @menu;
+ //debugmes "Array size %d", getarraysize(.@vals);
+
+ if (@menu == 255)
+ return -1;
+
+ @menu-=1;
+ if (@menu < 0 || @menu > getarraysize(.@vals) - 1)
+ return -1;
+
+ @menuret = .@vals[@menu];
+ return @menuret;
+}
+
diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt
new file mode 100644
index 00000000..76cdad21
--- /dev/null
+++ b/npc/functions/inventoryplace.txt
@@ -0,0 +1,36 @@
+// Evol functions.
+// Authors:
+// Qwerty Dragon
+// Reid
+// Description:
+// Check if the player have enough place on his inventory to accept new items with arguments:
+// getarg(even numbers) item ID,
+// getarg(odd numbers) number of items,
+
+function script inventoryplace {
+
+ .@argc = getargcount();
+
+ if (.@argc % 2 != 0)
+ {
+ Exception("inventoryplace: Wrong argument count.", RB_SPEECH|RB_ISFATAL|RB_PLEASEREPORT|RB_DEBUGMES);
+ }
+
+ for (.@i = .@j = 0; .@i < .@argc; .@i += 2)
+ {
+ setarray .@item[.@j], getarg(.@i);
+ setarray .@amount[.@j], getarg(.@i + 1);
+ ++.@j;
+ }
+
+ if (!checkweight2(.@item, .@amount))
+ {
+ narrator S_FIRST_BLANK_LINE,
+ l("It looks like you can't carry anything else for now."),
+ l("You should come back when you have some free space.");
+
+ close;
+ }
+
+ return true;
+}
diff --git a/npc/functions/main.txt b/npc/functions/main.txt
index a3e222e8..eac02526 100644
--- a/npc/functions/main.txt
+++ b/npc/functions/main.txt
@@ -307,6 +307,13 @@ function script die {
return;
}
+// Returns if a map is on PVP Mode or Not
+// ispvpmap( {mapid} )
+function script ispvpmap {
+ .@mapa$=getarg(0, getmapname());
+ return (getmapflag(.@mapa$, mf_pvp) || getmapflag(.@mapa$, mf_pvp_noparty) || getmapflag(.@mapa$, mf_pvpnoguild));
+}
+
// TMW2 Custom Functions
/////////////////////////////////////////////
@@ -602,33 +609,7 @@ function script registercmd {
return;
}
-//////////////////////////////////////////////////////////////////////
-// maptimer("<map>", <tick>, "<npc>::<event>")
-function script maptimer {
- .@c = getunits(BL_PC, .@players, false, getarg(0));
- for (.@i = 0; .@i < .@c; .@i++) {
- addtimer(getarg(1), getarg(2), .@players[.@i]);
- }
- return .@i;
-}
-
-// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
-function script areatimer {
- // Legacy
- if (getargcount() > 7)
- .@ox=1;
- // Variables
- .@m$=getarg(.@ox); .@ox+=1;
- .@x1=getarg(.@ox); .@ox+=1;
- .@y1=getarg(.@ox); .@ox+=1;
- .@x2=getarg(.@ox); .@ox+=1;
- .@y2=getarg(.@ox); .@ox+=1;
- .@tk=getarg(.@ox); .@ox+=1;
- .@e$=getarg(.@ox); .@ox+=1;
- .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2);
- for (.@i = 0; .@i < .@c; .@i++) {
- addtimer(.@tk, .@e$, .@players[.@i]);
- }
- return .@i;
+function script iscollision {
+ return checknpccell(getarg(0), getarg(1), getarg(2), cell_chkpass);
}
diff --git a/npc/functions/math.txt b/npc/functions/math.txt
new file mode 100644
index 00000000..9c93fbb7
--- /dev/null
+++ b/npc/functions/math.txt
@@ -0,0 +1,123 @@
+// Evol functions.
+// Authors:
+// 4144
+// Reid
+// Description:
+// Math functions
+
+
+// abs(<int>)
+// returns the absolute value of the passed integer
+
+function script abs {
+ .@n = getarg(0);
+ return .@n >= 0 ? .@n : -.@n;
+}
+
+
+
+// lognbaselvl({<multiplicator>{, <min value>}})
+// returns BaseLevel * logn (BaseLevel * alpha).
+
+function script lognbaselvl {
+ .@alpha = getarg(0, 1);
+ .@min = getarg(1, 1);
+ .@ret = 0;
+ .@pc_level = BaseLevel * .@alpha;
+
+ while (.@pc_level >>= 1)
+ {
+ ++.@ret;
+ }
+ .@ret *= BaseLevel;
+
+ if (.@ret <= .@min)
+ {
+ .@ret = .@min;
+ }
+
+ return .@ret;
+}
+
+// log2(<int>)
+// returns the log base 2 of the passed integer, up to 20 (2**20=1.048.576) (round down always)
+
+function script log2 {
+ .@v=abs(getarg(0));
+ .@ok=0;
+ .@i=0;
+ if (.@v < 1)
+ return -1;
+
+ freeloop(true);
+ while (!.@ok) {
+ // exact match
+ if (2**.@i == .@v) {
+ .@ok=1;
+ // inexact match, or limit exceeded
+ } else if (2**.@i >= .@v || .@i > 20) {
+ .@ok=1;
+ .@i-=1; // round down
+ // not yet
+ } else {
+ .@i+=1;
+ }
+ }
+ freeloop(false);
+
+ return .@i;
+}
+
+
+// result is: lower < target <= higher
+// is_between ( lower, higher, target)
+function script is_between {
+ .@val=getarg(2);
+ .@min=getarg(0);
+ .@max=getarg(1);
+ return (.@min < .@val && .@val <= .@max);
+}
+
+
+// forces the equation: lower <= target <= higher.
+// Note it still works if higher and target values are swapped.
+// limit ( lower, target, higher)
+function script limit {
+ return max(getarg(0), min(getarg(1), getarg(2)));
+}
+
+
+// result is the ponderate average.
+// ponderate_avg ( arg1, sub1, arg2, sub2)
+function script ponderate_avg {
+ .@a1=getarg(0);
+ .@s1=getarg(1);
+ .@a2=getarg(2);
+ .@s2=getarg(3);
+
+ .@h1=.@a1*.@s1;
+ .@h2=.@a2*.@s2;
+ .@dd=.@s1+.@s2;
+
+ return (.@h1+.@h2)/.@dd;
+}
+
+// bitmask_count(<int>)
+// returns the number of bits set in <int> (max. 4096)
+
+function script bitmask_count {
+ .@n = getarg(0); // Number evaluated
+ .@p=0; // Bits set/unset
+ .@s=0; // Stack and Check
+ .@m=0; // Memory
+
+ // Loop only as needed
+ while (.@s < .@n) {
+ .@s=2**.@m;
+ if (.@n & .@s)
+ .@p++;
+ .@m++;
+ }
+ return .@p;
+}
+
diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt
new file mode 100644
index 00000000..612ab036
--- /dev/null
+++ b/npc/functions/npcmove.txt
@@ -0,0 +1,142 @@
+// Evol functions.
+// Author:
+// 4144
+// Description:
+// Moving npc utility functions
+// Variables:
+// none
+
+function script initpath {
+ deletearray getvariableofnpc(.movepathcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathy, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepathx, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount(); .@f = .@f + 3)
+ {
+ set getvariableofnpc(.movepathcmd$[.@cnt], strnpcinfo(3)), getarg(.@f);
+ set getvariableofnpc(.movepathx[.@cnt], strnpcinfo(3)), getarg(.@f + 1);
+ set getvariableofnpc(.movepathy[.@cnt], strnpcinfo(3)), getarg(.@f + 2);
+ .@cnt ++;
+ }
+ //debugmes "array size: " + str(getarraysize(getvariableofnpc(.movepath, strnpcinfo(3))));
+ return;
+}
+
+function script domoveaction {
+ //debugmes "domoveaction: " + str(getvariableofnpc(.movepos, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return;
+ //debugmes "walking";
+ .@cmd$ = getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+ //debugmes "cmd: " + .@cmd$;
+
+ if (.@cmd$ == "move")
+ {
+ npcwalkto getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "dir")
+ {
+ setnpcdir getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "emote")
+ {
+ unitemote getnpcid(), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "class")
+ {
+ .class = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "warp")
+ {
+ movenpc strnpcinfo(3), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "goto")
+ {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 0;
+ }
+ else if (.@cmd$ == "rmove")
+ {
+ getmapxy(.@mapName$, .@x, .@y, 1);
+ npcwalkto .@x + getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), .@y + getvariableofnpc(.movepathy[.@pos], strnpcinfo(3));
+ }
+ else if (.@cmd$ == "speed")
+ {
+ .speed = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3));
+ return 2;
+ }
+ else if (.@cmd$ == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$ == "stand")
+ {
+ npcstand;
+ }
+ return 1;
+}
+
+function script movetonextpos {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait --;
+ //debugmes "wait";
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+ .@true = 1;
+ while (.@true)
+ {
+ .@true = 0;
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ //debugmes "movetonextpos: " + str(.@pos);
+ .@res = domoveaction(.@pos);
+ if (.@res == 1 || .@res == 2)
+ {
+ .@pos++;
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))))
+ .@pos = 0;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ if (.@res == 0 || .@res == 2)
+ {
+ .@true = 1;
+ }
+ }
+ return;
+}
+
+function script initialmove {
+ set getvariableofnpc(.movepos, strnpcinfo(3)), 0;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ movetonextpos;
+ return;
+}
+
+function script getmovecmd {
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0)
+ return "";
+ return getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3));
+}
+
+function script domovestep {
+ if (isunitwalking())
+ {
+ initnpctimer;
+ end;
+ }
+ movetonextpos;
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt
new file mode 100644
index 00000000..0877b748
--- /dev/null
+++ b/npc/functions/npcmovegraph.txt
@@ -0,0 +1,489 @@
+// Evol functions.
+// Author:
+// Travolta
+// Description:
+// Moving npc utility functions (graph-based)
+// Variables:
+// none
+
+function script initmovegraph {
+ deletearray getvariableofnpc(.movegraphcmd$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphlabels$, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphweight, strnpcinfo(3));
+ deletearray getvariableofnpc(.movegraphflags, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x1, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_x2, strnpcinfo(3));
+ deletearray getvariableofnpc(.movepos_y2, strnpcinfo(3));
+ .@cnt = 0;
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ set getvariableofnpc(.movegraphlabels$[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_x1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y1[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ if (!isstr(getarg(.@f, "label")))
+ {
+ set getvariableofnpc(.movepos_x2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ set getvariableofnpc(.movepos_y2[.@cnt], strnpcinfo(3)), getarg(.@f++);
+ }
+ .@cnt ++;
+ }
+ return;
+}
+
+function script findmovegraphlabel {
+ if (!getargcount())
+ {
+ debugmes "findmovegraphlabel: no argument";
+ return -1;
+ }
+ if (!isstr(getarg(0)))
+ {
+ debugmes "findmovegraphlabel: need string argument";
+ return -1;
+ }
+
+ .@arg$ = getarg(0);
+ for (.@i = 0; .@i < getarraysize(getvariableofnpc(.movegraphlabels$, strnpcinfo(3))); .@i++)
+ {
+ if (getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) == .@arg$)
+ return .@i;
+ }
+
+ npcdebug "findmovegraphlabel: label not found: " + getarg(0);
+ return -1;
+}
+
+/* setmovegraphcmd(fromPositionLabel,toPositionLabel[,moveChanceWeight[,moveFlags]],postCommand, ...);
+ * This function manipulates NPC moving graph. Before calling it, make sure
+ * `initmovegraph' was called. The function accepts 3-5 parameters (many times):
+ * fromPositionLabel, toPositionLabel -- starting and ending position of NPC move
+ * moveChanceWeight -- positive integer, represents the chance of moving in given direction. (optional)
+ * moveFlags -- if .mg_flags & moveFlags != 0, move is possible. (optional)
+ * postCommand -- either "moveon" (start moving to next location straight after arriving from
+ * fromPositionLabel to toPositionLabel) or a semicolon-separated set of commands
+ * ("wait 3", "emote 5" etc, see `execmovecmd') that will be executed after arrival.
+ * The commands don't have to end with ";moveon", it's executed in the end by default.
+ */
+function script setmovegraphcmd {
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+
+ for (.@f = 0; .@f < getargcount();)
+ {
+ .@from = findmovegraphlabel(getarg(.@f++));
+ .@to = findmovegraphlabel(getarg(.@f++));
+ .@weight = 1;
+ if (!isstr(getarg(.@f)))
+ .@weight = getarg(.@f++);
+ .@flags = 0xffff;
+ if (!isstr(getarg(.@f)))
+ .@flags = getarg(.@f++);
+ .@cmd$ = getarg(.@f++);
+ .@index = .@from * .@size + .@to; // emulation of 2d array
+ set getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)), .@cmd$;
+ set getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)), .@weight;
+ set getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)), .@flags;
+ }
+ return;
+}
+
+function script execmovecmd {
+
+ explode(.@cmd$, getarg(0), " ");
+
+ if (.@cmd$[0] == "moveon")
+ {
+ return 0;
+ }
+ else if (.@cmd$[0] == "dir")
+ {
+ .dir = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "sit")
+ {
+ npcsit;
+ }
+ else if (.@cmd$[0] == "stand")
+ {
+ npcstand;
+ }
+ else if (.@cmd$[0] == "wait")
+ {
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), atoi(.@cmd$[1]);
+ return 1;
+ }
+ else if (.@cmd$[0] == "emote")
+ {
+ unitemote getnpcid(), atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "class")
+ {
+ .class = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "warp")
+ {
+ .@pos = -1;
+ .@map$ = "";
+ .@pos_idx = 1;
+ if (getarraysize(.@cmd$) == 3)
+ {
+ .@map$ = .@cmd$[1];
+ .@pos_idx = 2;
+ }
+ .@pos = findmovegraphlabel(.@cmd$[.@pos_idx]);
+ if (.@pos != -1)
+ {
+ .@x = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ if (getstrlen(.@map$) > 0)
+ unitwarp getnpcid(), .@map$, .@x, .@y;
+ else
+ movenpc strnpcinfo(3), .@x, .@y;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos;
+ }
+ else
+ {
+ debugmes "execmovecmd: unknown WARP destination label: " + .@cmd$[1];
+ }
+ }
+ else if (.@cmd$[0] == "call")
+ {
+ switch (getarraysize(.@cmd$))
+ {
+ case 1:
+ debugmes "execmovecmd: CALL command needs some parameters";
+ return 0;
+ case 2:
+ return callfunc(.@cmd$[1]);
+ break;
+ case 3:
+ return callfunc(.@cmd$[1], .@cmd$[2]);
+ case 4:
+ default:
+ return callfunc(.@cmd$[1], .@cmd$[2], .@cmd$[3]);
+ }
+ }
+ else if (.@cmd$[0] == "speed")
+ {
+ .speed = atoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "say")
+ {
+ deletearray .@cmd$[0], 1;
+ .@msg$=implode(.@cmd$, " ");
+ if (.@msg$ != "" && .@msg$ != " ")
+ npctalk .@msg$;
+ else
+ debugmes "Invalid message passed to execmovecmd/npctalk";
+ }
+ else if (.@cmd$[0] == "debugmes")
+ {
+ deletearray .@cmd$[0], 1;
+ debugmes implode(.@cmd$, " ");
+ }
+ else if (.@cmd$[0] == "flags")
+ {
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), axtoi(.@cmd$[1]);
+ }
+ else if (.@cmd$[0] == "flags_0")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags &= ~axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else if (.@cmd$[0] == "flags_1")
+ {
+ .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@flags |= axtoi(.@cmd$[1]);
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags;
+ }
+ else
+ {
+ debugmes "Unknown move graph cmd: " + .@cmd$[0];
+ }
+ return 0;
+}
+
+function script getnextmovecmd {
+ .@cmds$ = getvariableofnpc(.nextcmd$, strnpcinfo(3));
+ .@firstCmd$ = .@cmds$;
+ .@restCmd$ = "moveon";
+ .@index = strpos(.@cmds$, ";");
+ if (.@index >= 0)
+ {
+ .@firstCmd$ = substr(.@cmds$, 0, .@index - 1);
+ .@restCmd$ = substr(.@cmds$, .@index + 1, getstrlen(.@cmds$) - 1);
+ }
+ // npcdebug "firstCmd = " + .@firstCmd$ + " restCmd = " + .@restCmd$;
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@restCmd$;
+ return strip(.@firstCmd$);
+}
+
+// getrandompoint(x1,y1,x2,y2)
+// -- Get a random walkable point within a map rectangle
+// x1, y1 -- top-left corner of rectangle
+// x2, y2 -- bottom-right corner of rectangle
+// Returns 0 on success and -1 on error;
+// Since we cannot return multiple values, the random
+// coordinates are stored in NPC variables .move__rand_x, .move__rand_y
+function script getrandompoint {
+ if (getargcount() < 4)
+ {
+ debugmes "error: getrandompoint(x1, y1, x2, y2) takes 4 arguments";
+ return -1;
+ }
+
+ .@max_pokes = 10;
+ .@x1 = getarg(0);
+ .@y1 = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+ .@rx = -1; .@ry = -1;
+
+ getmapxy(.@map$, .@cx, .@cy, 1); // npc location
+
+ // let's try max_pokes random cells
+ for (.@poke = 0; .@poke < .@max_pokes; .@poke++)
+ {
+ .@rx = rand(.@x1, .@x2);
+ .@ry = rand(.@y1, .@y2);
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ }
+
+ // we check each cell from random middle point to the end
+ for (;.@rx <= .@x2; .@rx++)
+ {
+ for (;.@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+ .@ry = .@y1;
+ }
+
+ // we check the rectangle from beginning to end
+ for (.@rx = .@x1; .@rx <= .@x2; .@rx++)
+ for (.@ry = .@y1; .@ry <= .@y2; .@ry++)
+ if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass))
+ goto L_Found;
+
+ // finally, if we don't find anything
+ debugmes "error: getrandompoint: cannot find walkable cell in rectangle [(" + .@x1 + "," + .@y1 + ") , (" + .@x2 + "," + .@y2 + ")]";
+ return -1;
+
+L_Found:
+ set getvariableofnpc(.move__rand_x, strnpcinfo(3)), .@rx;
+ set getvariableofnpc(.move__rand_y, strnpcinfo(3)), .@ry;
+ return 0;
+}
+
+// wrapper function for npcwalkto. It can accept 4 parameters.
+// If #3 and #4 params are set, the walkto location is chosen
+// from rectangle (x1,y1,x2,y2).
+// It sets the npc variables .move_target_x, .move_target_y
+// that are used to resume NPC walking
+// Returns 1 if walking is possible, 0 otherwise;
+function script mg_npcwalkto {
+ if (getargcount() < 2)
+ {
+ debugmes "usage: mg_npcwalkto(x1,y1[,x2,y2])";
+ return -1;
+ }
+
+ .@x = getarg(0);
+ .@y = getarg(1);
+ .@x2 = getarg(2);
+ .@y2 = getarg(3);
+
+ if (getargcount() >= 4 && .@x2 > 0 && .@y2 > 0)
+ if (!getrandompoint(.@x, .@y, .@x2, .@y2))
+ {
+ .@x = getvariableofnpc(.move__rand_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move__rand_y, strnpcinfo(3));
+ }
+ else
+ return 0;
+
+ if (npcwalkto(.@x, .@y))
+ {
+ set getvariableofnpc(.move_target_x, strnpcinfo(3)), .@x;
+ set getvariableofnpc(.move_target_y, strnpcinfo(3)), .@y;
+ return 1;
+ }
+ return 0;
+}
+
+function script movetonextpoint {
+ .@wait = getvariableofnpc(.waitticks, strnpcinfo(3));
+ if (.@wait > 0)
+ {
+ .@wait--;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait;
+ return;
+ }
+
+ .@nextcmd$ = "";
+ while (.@nextcmd$ != "moveon")
+ {
+ .@nextcmd$ = getnextmovecmd();
+ npcdebug " " + .@nextcmd$;
+ if (execmovecmd(.@nextcmd$))
+ return;
+ }
+
+ // choose a random path from all possible paths
+ .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3)));
+ .@pos = getvariableofnpc(.movepos, strnpcinfo(3));
+ .@curr_flags = getvariableofnpc(.mg_flags, strnpcinfo(3));
+ .@cur = 0;
+ .@weight_sum = 0;
+ // .@dbg$ = getvariableofnpc(.movegraphlabels$[.@pos], strnpcinfo(3)) + ": ";
+
+ for (.@i = 0; .@i < .@size; .@i++)
+ {
+ .@index = .@pos * .@size + .@i;
+ .@cmd$ = getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3));
+ .@flags = getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3));
+ if (.@cmd$ != "" &&
+ .@curr_flags & .@flags)
+ {
+ .@nextpos[.@cur] = .@i;
+ .@weights[.@cur] = getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3));
+ // .@dbg$ += getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) + "=" + .@weights[.@cur] + " ";
+ .@weight_sum += .@weights[.@cur];
+ .@cur++;
+ }
+ }
+ // npcdebug .@dbg$;
+
+ if (!.@weight_sum)
+ {
+ npcdebug("error: cannot pick next walk point. flags=" +
+ getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ return;
+ }
+
+ .@pick_tries = 0;
+L_TryPick:
+ // pick a random number based on weight_sum
+ .@rnd = rand(.@weight_sum);
+ .@k = -1; .@weight_sum = 0;
+ while (.@rnd >= .@weight_sum)
+ {
+ .@k++;
+ .@weight_sum += .@weights[.@k];
+ }
+
+ .@next_idx = .@nextpos[.@k];
+ .@next_x1 = getvariableofnpc(.movepos_x1[.@next_idx], strnpcinfo(3));
+ .@next_y1 = getvariableofnpc(.movepos_y1[.@next_idx], strnpcinfo(3));
+ .@next_x2 = getvariableofnpc(.movepos_x2[.@next_idx], strnpcinfo(3));
+ .@next_y2 = getvariableofnpc(.movepos_y2[.@next_idx], strnpcinfo(3));
+
+ if (!mg_npcwalkto(.@next_x1, .@next_y1, .@next_x2, .@next_y2))
+ {
+ if (.@pick_tries < 10)
+ {
+ .@pick_tries++;
+ goto L_TryPick;
+ }
+
+ // move to a nearby position
+ .@x1 = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3));
+ .@y1 = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3));
+ .@x2 = getvariableofnpc(.movepos_x2[.@pos], strnpcinfo(3));
+ .@y2 = getvariableofnpc(.movepos_y2[.@pos], strnpcinfo(3));
+ mg_npcwalkto(.@x1, .@y1, .@x2, .@y2);
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), "wait 1";
+
+ return;
+ }
+
+ if (getvariableofnpc(.debug, strnpcinfo(3)))
+ {
+ getmapxy(.@map$, .@cx, .@cy, 1);
+ .@dist = distance(.@cx, .@cy, .@next_x1, .@next_y1);
+ npcdebug("moving to " + getvariableofnpc(.movegraphlabels$[.@next_idx], strnpcinfo(3)) +
+ " ("+ getvariableofnpc(.move_target_x, strnpcinfo(3)) +
+ "," + getvariableofnpc(.move_target_y, strnpcinfo(3)) +
+ ") [distance=" + .@dist +
+ "] flags=" + getvariableofnpc(.mg_flags, strnpcinfo(3)));
+ }
+
+ .@nextcmd$ = getvariableofnpc(.movegraphcmd$[.@pos * .@size + .@next_idx], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@next_idx;
+ return;
+}
+
+// initial actions for npc when using move graphs.
+// function can accept 2 arguments:
+// 1: action sequence, for example "speed 200; dir 4". Default is "moveon"
+// 2: start point label. Default is #0 from move graph labels
+function script firstmove {
+ .@nextcmd$ = getarg(0, "moveon");
+ .@initpos = findmovegraphlabel(getarg(1, ""));
+ if (.@initpos < 0) .@initpos = 0;
+
+ set getvariableofnpc(.movepos, strnpcinfo(3)), .@initpos;
+ movenpc strnpcinfo(3), getvariableofnpc(.movepos_x1[.@initpos], strnpcinfo(3)),
+ getvariableofnpc(.movepos_y1[.@initpos], strnpcinfo(3));
+ set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$;
+ set getvariableofnpc(.waitticks, strnpcinfo(3)), -1;
+ set getvariableofnpc(.mg_flags, strnpcinfo(3)), 0xffff;
+ movetonextpoint;
+ return;
+}
+
+function script npc_pausemove {
+ stopnpctimer;
+ .@move_after = 0;
+
+ if (isunitwalking())
+ {
+ .@move_after = 1;
+ npcwalkto .x, .y;
+ npcstop;
+ }
+ set getvariableofnpc(.move_after_pause, strnpcinfo(3)), .@move_after;
+
+ return 0;
+}
+
+function script npc_resumemove {
+ startnpctimer;
+
+ if (getvariableofnpc(.move_after_pause, strnpcinfo(3)))
+ {
+ .@x = getvariableofnpc(.move_target_x, strnpcinfo(3));
+ .@y = getvariableofnpc(.move_target_y, strnpcinfo(3));
+ npcwalkto .@x, .@y;
+ }
+
+ return 0;
+}
+
+// npc_turntoxy(x,y)
+// turn npc toward an object at position (x,y)
+function script npc_turntoxy {
+ .@target_x = getarg(0);
+ .@target_y = getarg(1);
+ .@dx = abs(.@target_x - .x);
+ .@dy = abs(.@target_y - .y);
+
+ if (.@dx > .@dy)
+ .dir = .@target_x >= .x ? 6 : 2;
+ else
+ .dir = .@target_y >= .y ? 0 : 4;
+
+ return 0;
+}
+
+function script dographmovestep {
+ if (!isunitwalking())
+ {
+ movetonextpoint;
+ }
+ initnpctimer;
+ end;
+}
diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt
new file mode 100644
index 00000000..96e69fde
--- /dev/null
+++ b/npc/functions/permissions.txt
@@ -0,0 +1,35 @@
+// Evol scripts.
+// Author:
+// gumi
+// Description:
+// checks player permissions
+// ** admins are implicitly everything
+
+// administrator
+function script is_admin {
+ return has_permission(PERM_USE_ALL_COMMANDS, getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// any staff member
+function script is_trusted {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ has_permission("show_client_version", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// developer
+function script is_dev {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ has_permission(PERM_RECEIVE_REQUESTS, getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// event coordinator
+function script is_evtc {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ can_use_command("@monster", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
+
+// game master
+function script is_gm {
+ return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) ||
+ can_use_command("@jail", getarg(0, getcharid(CHAR_ID_ACCOUNT)));
+}
diff --git a/npc/functions/random-talk.txt b/npc/functions/random-talk.txt
new file mode 100644
index 00000000..e6b6bee8
--- /dev/null
+++ b/npc/functions/random-talk.txt
@@ -0,0 +1,207 @@
+// TMW2 Script
+// Author:
+// Jesusalva
+// Description:
+// Random dialog for various random NPCs.
+
+// Functions:
+// hello
+// moubootalk
+// villagertalk
+// sailortalk
+// legiontalk
+// asleep
+
+// Evol authors (some strings and code):
+// Reid
+// Akko Teru
+// Qwerty Dragon
+
+function script hello {
+
+ switch (rand2(3)) {
+ case 0:
+ npctalkonce(l("Heya!"));
+ break;
+ case 1:
+ npctalkonce(l("Hi."));
+ break;
+ case 2:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas!"));
+ else
+ npctalkonce(l("Nice day to you."));
+ break;
+ }
+
+ return;
+}
+
+function script moubootalk {
+ switch (rand2(4)) {
+ case 0:
+ npctalkonce(l("Moooooo!"));
+ break;
+ case 1:
+ npctalkonce(l("Moo!"));
+ break;
+ case 2:
+ npctalkonce(l("Moooooooooooo!"));
+ break;
+ case 3:
+ npctalkonce(l("Moooo!"));
+ break;
+ }
+ return;
+}
+
+function script sailortalk {
+
+ .@rand = rand2(8);
+ if (.@rand == 0) goodbye;
+ if (.@rand == 1) npctalkonce(l("Arr, I'm bored!"));
+ if (.@rand == 2) npctalkonce(l("Hey! Good to hear from you!"));
+ if (.@rand == 3) npctalkonce(l("Yarr arr!"));
+ if (.@rand == 4) {
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, arr yarr!!"));
+ else {
+ speech(
+ l("A sunny and hot day,"),
+ l("a quiet place,"),
+ l("a ground!"),
+ l("What else do you need?"));
+ }
+ close;
+ }
+ if (.@rand == 5) npctalkonce(l("A-hoy matey!"));
+ if (.@rand == 6) npctalkonce(l("Arr!"));
+ if (.@rand == 7) npctalkonce(l("Howdy?"));
+
+ // just to be sure
+ closedialog;
+ close;
+ end;
+}
+
+function script villagertalk {
+
+ function darn_or_smile {
+ .@darn = rand(42);
+
+ if (.@darn < 26) {
+ emotion E_JOY;
+ hello;
+ } else if (.@darn > 26) {
+ emotion E_LOOKAWAY;
+ goodbye;
+ } else {
+ npctalkonce(l("Stop it!"));
+ }
+ return;
+ }
+
+ switch (rand2(4)) {
+ case 0:
+ darn_or_smile();
+ break;
+ case 1:
+ npctalkonce(l("It is a sunny day, don't you think?"));
+ break;
+ case 2:
+ npctalkonce(l("Go fly a kite."));
+ break;
+ case 3:
+ npctalkonce(l("I just want to live my life in peace."));
+ break;
+ default:
+ emotion E_HAPPY;
+ break;
+ }
+
+ return;
+}
+
+function script legiontalk {
+ switch (rand2(15)) {
+ case 0:
+ npctalkonce(l("Do I look like a tree? I feel like one."));
+ //speech(
+ // l("Do you feel too weak even to do damage to this areas wishy-washy wildlife?"),
+ // l("Then concentrate your anger upon the trees hereabouts, you will gain experience whilst leveling your sword skill on them."),
+ // l("Oh, and a fruit may even fall for you if you are lucky! But stay alert to pick up your drops."));
+ //close;
+ break;
+ case 1:
+ npctalkonce(l("I'm a little busy right now."));
+ break;
+ case 2:
+ npctalkonce(l("Not in the mood to chat."));
+ break;
+ case 3:
+ npctalkonce(l("My breath smells bad."));
+ break;
+ case 4:
+ npctalkonce(l("Don't distract me, I have to stay alert."));
+ break;
+ case 5:
+ npctalkonce(l("Give me some space."));
+ break;
+ case 6:
+ if ($EVENT$ == "Christmas")
+ npctalkonce(l("Merry Christmas, adventurer."));
+ else
+ npctalkonce(l("Can you please go away?"));
+ break;
+ case 7:
+ npctalkonce(l("Can't talk right now, I'm on patrol duty."));
+ break;
+ case 8:
+ npctalkonce(l("What're you looking at?!"));
+ break;
+ case 9:
+ npctalkonce(l("I can't stay here and talk all day. I have a job to do."));
+ break;
+ case 10:
+ npctalkonce(l("Keep moving pal."));
+ break;
+ case 11:
+ npctalkonce(l("So you think you're tough? A warrior must also be loyal and patient."));
+ break;
+ case 12:
+ emotion E_LOOKAWAY;
+ break;
+ case 13:
+ npctalkonce(l("Practice! There are no secrets to becoming a warrior."));
+ break;
+ case 14:
+ npctalkonce(l("There is no honor in fighting a weak opponent."));
+ break;
+ }
+
+ return;
+}
+
+function script asleep {
+ switch(rand2(5)) {
+ case 0: npctalkonce(l("Zzzzzzzzz...")); break;
+ case 1: npctalkonce(l("Rrrr... Pchhhh...")); break;
+ case 2: npctalkonce(l("Ggrmm... Grmmmm...")); break;
+ case 3: npctalkonce(l("Hm... Shhhh...")); break;
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
+
+function script studenttalk {
+ switch(rand2(6)) {
+ case 0: npctalkonce(l("I want to sleep...")); break;
+ case 1: npctalkonce(l("I have homework to do...")); break;
+ case 2: npctalkonce(l("I need to finish studying for my test...")); break;
+ case 3: npctalkonce(l("Ah, the Professors will get mad at me again...")); break;
+ case 4: npctalkonce(l("I'm a little busy right now.")); break;
+
+ default: emotion(E_SLEEPY);
+ }
+ end;
+}
diff --git a/npc/functions/string.txt b/npc/functions/string.txt
new file mode 100644
index 00000000..2a38d90d
--- /dev/null
+++ b/npc/functions/string.txt
@@ -0,0 +1,211 @@
+// Evol Script
+// Author: Gumi
+
+// safe string manipulation functions
+// ** does not require PCRE
+
+
+// str(<int>)
+// returns whatever is passed, converted to string
+
+function script str {
+ return "" + getarg(0);
+}
+
+
+
+// startswith("<string>", "<search>")
+// returns true if <string> begins with <search>
+
+function script startswith {
+ return substr(getarg(0), 0, getstrlen(getarg(1)) - 1) == getarg(1);
+}
+
+
+
+// endswith("<string>", "<search>")
+// returns true if <string> ends with <search>
+
+function script endswith {
+ .@t = getstrlen(getarg(0)); // total length
+ .@n = getstrlen(getarg(1)); // substring length
+ return substr(getarg(0), .@t - .@n, .@t - 1) == getarg(1);
+}
+
+
+
+// capitalize("<string>")
+// returns <string> with its first letter capitalized
+
+function script capitalize {
+ return setchar(getarg(0), strtoupper(charat(getarg(0), 0)), 0);
+}
+
+
+
+// titlecase("<string>" {, "<delimiter>" {, <camel>}})
+// returns <string> with the first letter of each word capitalized
+// if <camel> is true, the string is joined in a camelCase fashion
+
+function script titlecase {
+ .@delimiter$ = getarg(1, " ");
+ .@c = getarg(2, 0);
+ explode(.@words$, getarg(0), .@delimiter$);
+
+ for (.@i = (.@c ? 1 : 0); .@i < 255; ++.@i)
+ {
+ if (.@words$[.@i] == "")
+ {
+ break;
+ }
+
+ .@words$[.@i] = setchar(.@words$[.@i], strtoupper(charat(.@words$[.@i], 0)), 0);
+ }
+
+ return implode(.@words$, (.@c ? "" : .@delimiter$));
+}
+
+
+
+// camelcase("<string" {, "<delimiter>"})
+
+function script camelcase {
+ return titlecase(getarg(0), getarg(1, " "), true);
+}
+
+
+
+// zfill("<string>" {, <width> {, "<padding>"}})
+// returns <string> padded to the left with <padding> up to width
+
+function script zfill {
+ .@str$ = getarg(0);
+ .@width = getarg(1, 8);
+ .@padding$ = getarg(2, "0");
+
+ for (.@s = getstrlen(.@str$); .@s < .@width; ++.@s)
+ {
+ .@str$ = .@padding$ + .@str$;
+ }
+
+ return .@str$;
+}
+
+
+
+// format_number(<integer> {, "<separator>"})
+// formats a number properly
+
+function script format_number {
+ .@number$ = str(getarg(0));
+ .@len = getstrlen(.@number$);
+ .@separator$ = getarg(1, ",");
+
+ if (getargcount() < 2 && playerattached()) {
+ // get from user language
+ switch (Lang) {
+ case LANG_FR: .@separator$ = " "; break; // French
+ case LANG_DE: .@separator$ = "."; break; // Germanic
+ case LANG_PTBR: .@separator$ = "."; break; // Brazilian
+ default: .@separator$ = ","; // English (default)
+ }
+ }
+
+ for (.@i = .@len - 3; .@i > 0; .@i -= 3) {
+ .@number$ = insertchar(.@number$, .@separator$, .@i);
+ }
+
+ return .@number$;
+}
+
+
+
+// fnum(<integer>)
+// alias for format_number
+
+function script fnum {
+ return format_number(getarg(0));
+}
+
+
+
+// strip("<string>")
+// removes spaces at the start and end
+
+function script strip {
+ .@s$ = getarg(0);
+ if (.@s$ == "") {
+ return "";
+ }
+ .@start = 0;
+ .@end = getstrlen(.@s$) - 1;
+ for (.@i = .@start; .@i < .@end; .@i++)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@start++;
+ }
+ }
+ for (.@i = .@end; .@i >= .@start; .@i--)
+ {
+ if (charat(.@s$, .@i) != " ") {
+ break;
+ } else {
+ .@end--;
+ }
+ }
+ //debugmes "STRIP.DEBUG MODE ENABLED BY JESUSALVA. PASSING SUBSTRING PARAMS";
+ //debugmes "String \""+.@s$+"\" from "+str(.@start)+" to "+str(.@end);
+ return substr(.@s$, .@start, .@end);
+}
+
+
+
+// reverse("<string>")
+// returns <string> reversed
+
+function script reverse {
+ .@str$ = getarg(0);
+ .@len = getstrlen(.@str$);
+
+ for (.@i = 0; .@i < (.@len / 2); ++.@i) {
+ .@tmp$ = charat(.@str$, .@i);
+ .@str$ = setchar(.@str$, charat(.@str$, (.@len - 1 - .@i)), .@i); // a <= b
+ .@str$ = setchar(.@str$, .@tmp$, (.@len - 1 - .@i)); // b <= a
+ }
+
+ return .@str$;
+}
+
+
+
+// repeat("<string>", <multiplier>)
+// repeats <string> many times and returns it
+
+function script repeat {
+ .@mul = getarg(1);
+
+ for (.@i = 0; .@i < .@mul; ++.@i) {
+ .@str$ += getarg(0);
+ }
+
+ return .@str$;
+}
+
+
+
+// shuffle("<string>")
+// returns <string> shuffled
+
+function script shuffle {
+ .@str$ = getarg(0);
+
+ for (.@len = getstrlen(.@str$); .@len > 0; --.@len) {
+ .@rnd = rand(.@len);
+ .@out$ += charat(.@str$, .@rnd);
+ .@str$ = delchar(.@str$, .@rnd);
+ }
+
+ return .@out$;
+}
diff --git a/npc/functions/time.txt b/npc/functions/time.txt
index 21b94ac5..e6e4c70a 100755
--- a/npc/functions/time.txt
+++ b/npc/functions/time.txt
@@ -1,159 +1,117 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+function script now {
+ return gettimetick(2);
+}
-function script time_stamp {
- // local variables
- // if there is reasonable demand, these might be exported
- // (that is what the builtin is likely to do)
- @ts_year = gettime(7);
- @ts_month = gettime(6);
- @ts_mday = gettime(5);
- //set @ts_wday, gettime(4);
- @ts_hour = gettime(3);
- @ts_minute = gettime(2);
- @ts_second = gettime(1);
-
- // locals used to generate leading zeroes
- @ts_month_pad$ = "";
- @ts_mday_pad$ = "";
- @ts_hour_pad$ = "";
- @ts_minute_pad$ = "";
- @ts_second_pad$ = "";
-
- if (@ts_month < 10)
- @ts_month_pad$ = "0";
- if (@ts_mday < 10)
- @ts_mday_pad$ = "0";
- if (@ts_hour < 10)
- @ts_hour_pad$ = "0";
- if (@ts_minute < 10)
- @ts_minute_pad$ = "0";
- if (@ts_second < 10)
- @ts_second_pad$ = "0";
-
- @ts_date$ = @ts_year + "-" + @ts_month_pad$ + @ts_month + "-" + @ts_mday_pad$ + @ts_mday;
- @ts_time$ = @ts_hour_pad$ + @ts_hour + ":" + @ts_minute_pad$ + @ts_minute + ":" +@ts_second_pad$ + @ts_second;
-
- // cleanup
- @ts_year = 0;
- @ts_month = 0;
- @ts_mday = 0;
- @ts_hour = 0;
- @ts_minute = 0;
- @ts_second = 0;
- @ts_month_pad$ = "";
- @ts_mday_pad$ = "";
- @ts_hour_pad$ = "";
- @ts_minute_pad$ = "";
- @ts_second_pad$ = "";
-
- return;
+// Returns current time. A SQL update superseeded this.
+// santime( )
+function script santime {
+ return gettimetick(2);
}
+function script time_from_ms {
+ return now() + (getarg(0) / 1000);
+}
+function script time_from_seconds {
+ return now() + getarg(0);
+}
+function script time_from_minutes {
+ return now() + (getarg(0) * 60);
+}
+function script time_from_hours {
+ return now() + (getarg(0) * 3600);
+}
+
+function script time_from_days {
+ return now() + (getarg(0) * 86400);
+}
-function script HumanTime {
- @time$ = "now";
- if(@seconds) set @ms, @ms + (@seconds * 1000);
- if(@minutes) set @ms, @ms + (@minutes * 60000);
- if(@days) set @ms, @ms + (@days * 1440000);
- if(@ms < 1000) goto L_Millis; // under 1 second we have nothing to count
- @seconds = @ms / 1000;
- @ms = @ms % 1000;
- if(@seconds < 60) goto L_Seconds;
- @minutes = @seconds / 60;
- @seconds = @seconds % 60;
- if(@minutes < 60) goto L_Minutes;
- @hours = @minutes / 60;
- @minutes = @minutes % 60;
- if(@hours < 24) goto L_Hours;
- @days = @hours / 24;
- @hours = @hours % 24;
- if(@days) goto L_Days;
- goto L_Clean;
-
-L_Millis:
- @time$ = @ms + "ms";
- return;
-
-L_Seconds:
- @unit$ = "second";
- if(@seconds > 1) set @unit$, "seconds";
- @unit2$ = "millisecond";
- if(@ms > 1) set @unit2$, "milliseconds";
- @time$ = @seconds + " " + @unit$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit2$;
- goto L_Clean;
-
-L_Minutes:
- @unit$ = "minute";
- if(@minutes > 1) set @unit$, "minutes";
- @unit2$ = "second";
- if(@seconds > 1) set @unit2$, "seconds";
- @unit3$ = "millisecond";
- if(@ms > 1) set @unit3$, "milliseconds";
- @time$ = @minutes + " " + @unit$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit2$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit3$;
- goto L_Clean;
-
-L_Hours:
- @unit$ = "hour";
- if(@hours > 1) set @unit$, "hours";
- @unit2$ = "minute";
- if(@minutes > 1) set @unit2$, "minutes";
- @unit3$ = "second";
- if(@seconds > 1) set @unit3$, "seconds";
- @unit4$ = "millisecond";
- if(@ms > 1) set @unit4$, "milliseconds";
- @time$ = @hours + " " + @unit$;
- @separator$ = " and ";
- if(@seconds || @ms) set @separator$, ", ";
- if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit2$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$;
- goto L_Clean;
-
-L_Days:
- @unit$ = "day";
- if(@hours > 1) set @unit$, "days";
- @unit2$ = "hour";
- if(@hours > 1) set @unit2$, "hours";
- @unit3$ = "minute";
- if(@minutes > 1) set @unit3$, "minutes";
- @unit4$ = "second";
- if(@seconds > 1) set @unit4$, "seconds";
- @unit5$ = "millisecond";
- if(@ms > 1) set @unit5$, "milliseconds";
- @time$ = @days + " " + @unit$;
- @separator$ = " and ";
- if(@minutes || @seconds || @ms) set @separator$, ", ";
- if(@hours) set @time$, @time$ + @separator$ + @hours + " " + @unit2$;
- @separator$ = " and ";
- if(@seconds || @ms) set @separator$, ", ";
- if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit3$;
- @separator$ = " and ";
- if(@ms) set @separator$, ", ";
- if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$;
- if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$;
- goto L_Clean;
-
-L_Clean:
- @unit$ = "";
- @unit2$ = "";
- @unit3$ = "";
- @unit4$ = "";
- @unit5$ = "";
- @seconds = 0;
- @minutes = 0;
- @hours = 0;
- @days = 0;
- @separator$ = "";
- return;
+// FuzzyTime(<unix timestamp>{, <options>{, <precision>}})
+// gives time in a human-readable format
+//
+// <options> is bitmasked:
+// 1 do not show "ago" when in past
+// 2 do not show "in" when in the future
+// 4 show "from now" instead of "in" when in the future
+//
+// <precision> is the number of units to show,
+// by default uses two (eg. 2m30s or 1h20m).
+// Use '99' for max precision
+
+function script FuzzyTime {
+ .@future = getarg(0, now());
+ .@options = getarg(1, 3);
+ .@precision = getarg(2, 2);
+ .@diff = (.@future - now());
+
+ // check if in the past, or in the future
+ if (.@diff < 0) {
+ .@diff *= -1;
+ .@past = true;
+ }
+
+ .@diff = max(1, .@diff);
+
+ if (.@diff >= 31536000) {
+ .@years = (.@diff / 31536000);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 31536000));
+ .@ret$ += sprintf("%d %s", .@years, (.@years > 1 ? "years" : "year"));
+ }
+
+ if (.@diff >= 86400) {
+ .@days = (.@diff / 86400);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 86400));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : " and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@days, (.@days > 1 ? "days" : "day"));
+ }
+
+ if (.@diff >= 3600) {
+ .@hours = (.@diff / 3600);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 3600));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@hours, (.@hours > 1 ? "hours" : "hour"));
+ }
+
+ if (.@diff >= 60) {
+ .@minutes = (.@diff / 60);
+ .@diff = (++.@s == .@precision ? 0 : (.@diff % 60));
+
+ if (.@s > 1) {
+ .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and ");
+ }
+
+ .@ret$ += sprintf("%d %s", .@minutes, (.@minutes > 1 ? "minutes" : "minute"));
+ }
+
+ if (.@diff >= 1) {
+ if (++.@s > 1) {
+ .@ret$ += (.@s >= 3 ? ", " : " ") + "and ";
+ }
+
+ .@ret$ += sprintf("%d %s", .@diff, (.@diff > 1 ? "seconds" : "second"));
+ }
+
+ if (.@past && !(.@options & 1)) {
+ .@ret$ += " ago";
+ }
+
+ if (!(.@past) && !(.@options & 2)) {
+ .@ret$ = ((.@options & 4) ? sprintf("%s from now", .@ret$) : sprintf("in %s", .@ret$));
+ }
+
+ return .@ret$;
}
diff --git a/npc/functions/timer.txt b/npc/functions/timer.txt
new file mode 100644
index 00000000..27e09f13
--- /dev/null
+++ b/npc/functions/timer.txt
@@ -0,0 +1,89 @@
+// Evol Script
+// Authors: Gumi, Jesusalva
+
+// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer {
+ // Legacy
+ if (getargcount() > 7)
+ .@ox=1;
+ // Variables
+ .@m$=getarg(.@ox); .@ox+=1;
+ .@x1=getarg(.@ox); .@ox+=1;
+ .@y1=getarg(.@ox); .@ox+=1;
+ .@x2=getarg(.@ox); .@ox+=1;
+ .@y2=getarg(.@ox); .@ox+=1;
+ .@tk=getarg(.@ox); .@ox+=1;
+ .@e$=getarg(.@ox); .@ox+=1;
+ .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(.@tk, .@e$, .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areadeltimer("<map>", <x1>, <y1>, <x2>, <y2>, "<npc>::<event>")
+function script areadeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(5), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// areatimer2("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>")
+function script areatimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(6), .@players[.@i]);
+ addtimer(getarg(5), getarg(6), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// addtimer2(<tick>, "<npc>::<event>")
+function script addtimer2 {
+ deltimer(getarg(1));
+ addtimer(getarg(0), getarg(1));
+ return;
+}
+
+
+// maptimer("<map>", <tick>, "<npc>::<event>")
+function script maptimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// Same as maptimer() but deletes any previously running timer
+// maptimer2("<map>", <tick>, "<npc>::<event>")
+function script maptimer2 {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(2), .@players[.@i]);
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// mapdeltimer("<map>", "<npc>::<event>")
+function script mapdeltimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ deltimer(getarg(1), .@players[.@i]);
+ }
+ return .@i;
+}
+
+// partytimer("<map>", <tick>, "<npc>::<event>", partyid)
+function script partytimer {
+ .@c = getunits(BL_PC, .@players, false, getarg(0));
+ for (.@i = 0; .@i < .@c; .@i++) {
+ if (getcharid(2, strcharinfo(0,"",.@players[.@i]) ) == getarg(3))
+ addtimer(getarg(1), getarg(2), .@players[.@i]);
+ }
+ return .@i;
+}
+
diff --git a/npc/scripts.conf b/npc/scripts.conf
index 568123ff..5f1d6335 100755
--- a/npc/scripts.conf
+++ b/npc/scripts.conf
@@ -1,6 +1,25 @@
+// Critical functions
"npc/functions/main.txt",
+"npc/functions/string.txt",
+"npc/functions/array.txt",
+"npc/functions/math.txt",
+"npc/functions/permissions.txt",
+
+// General-Purpose Framework Functions
+"npc/functions/input.txt",
+"npc/functions/time.txt",
+"npc/functions/timer.txt",
+"npc/functions/goodbye.txt",
+
+// Pre-Loading Functions
"npc/functions/clear_vars.txt",
+"npc/functions/asklanguage.txt",
+"npc/functions/inventoryplace.txt",
+"npc/functions/random-talk.txt",
+"npc/functions/inc_sc_bonus.txt",
+"npc/commands/kami.txt",
+// Main Functions
"npc/functions/banker.txt",
"npc/functions/barber.txt",
"npc/functions/dailyquest.txt",
@@ -29,6 +48,7 @@
"npc/functions/motd.txt",
"npc/functions/motdconfig.txt",
+// Items
"npc/items/purification_potion.txt",
"npc/items/scissors.txt",
"npc/items/pickled_beets.txt",
@@ -45,8 +65,16 @@
//@include "npc/magic/_import.txt",
-//@include "npc/commands/_import.txt",
+// Commands
+"npc/commands/debug-quest.txt",
+"npc/commands/debug.txt",
+"npc/commands/ipcheck.txt",
+"npc/commands/language.txt",
+"npc/commands/numa.txt",
+"npc/commands/python.txt",
+"npc/commands/resync.txt",
+// Events
"npc/functions/gm_island.txt",
"npc/annuals/fathertime.txt",
"npc/annuals/check_time.txt",
@@ -65,5 +93,8 @@
"npc/annuals/halloween/munro.txt",
"npc/annuals/halloween/trick_or_treat.txt",
+// Post Loading Functions
+"npc/functions/filters.txt",
+
@include "npc/_import.txt"