summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2025-03-29 18:29:43 -0300
committerJesusaves <cpntb1@ymail.com>2025-03-29 18:29:43 -0300
commitfbd3a755f7fd28aed02a82c39b96525e58a84dce (patch)
tree9ec1d35a504065c5965f123a8e4608798512262b
parent3b88f44a9e53b4ef6707c197a4b83789eaa84556 (diff)
downloadserverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.gz
serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.bz2
serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.xz
serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.zip
This is the massive commit with an unfinished update
-rw-r--r--conf/map/battle/limits.conf1
-rw-r--r--conf/map/storage.conf5
-rw-r--r--npc/000-1/exit.txt6
-rw-r--r--npc/001-1/bgmaster.txt31
-rw-r--r--npc/001-15/_import.txt1
-rw-r--r--npc/001-15/_mobs.txt3
-rw-r--r--npc/001-15/ctrl.c960
-rw-r--r--npc/001-16/_import.txt1
-rw-r--r--npc/001-16/_mobs.txt9
-rw-r--r--npc/001-16/obelisk.c92
-rw-r--r--npc/003-6/tamiloc.txt6
-rw-r--r--npc/functions/hub.txt51
-rw-r--r--npc/functions/mobpoint.txt1
-rw-r--r--npc/functions/util.txt12
-rw-r--r--npc/items/inc_sc_bonus.txt20
-rw-r--r--npc/items/legacy_heal.txt6
-rw-r--r--npc/items/rand_mp_heal.txt6
-rw-r--r--npc/items/rand_sc_heal.txt6
18 files changed, 1204 insertions, 13 deletions
diff --git a/conf/map/battle/limits.conf b/conf/map/battle/limits.conf
index d61e4ea2c..bce04a451 100644
--- a/conf/map/battle/limits.conf
+++ b/conf/map/battle/limits.conf
@@ -25,6 +25,7 @@
//= along with this program. If not, see <http://www.gnu.org/licenses/>.
//=========================================================================
// Battle (Limits) Configuration File
+// Most of them are redefined in plugin/status.c so edit both!
//=========================================================================
// basic attack limits
diff --git a/conf/map/storage.conf b/conf/map/storage.conf
index 82d3144bc..ae36ebde8 100644
--- a/conf/map/storage.conf
+++ b/conf/map/storage.conf
@@ -69,4 +69,9 @@ storage_conf: (
Name: "Deluxe Storage"
Capacity: 500
},
+{
+ Id: 6
+ Name: "Temporary Storage"
+ Capacity: 180
+},
)
diff --git a/npc/000-1/exit.txt b/npc/000-1/exit.txt
index aed9a8c92..84d2ff323 100644
--- a/npc/000-1/exit.txt
+++ b/npc/000-1/exit.txt
@@ -14,6 +14,12 @@ OnTalkNearby:
end;
}
+ // If your soul is lost, Soul Menhir logic is invalid
+ if (Class == Skelli) {
+ warp "001-16", 90, 45;
+ end;
+ }
+
// Switch LOCATION$ and warp to nearest town's Soul Menhir
.@lx=array_find($@LOCMASTER_LOC$, LOCATION$);
if (.@lx >= 0) {
diff --git a/npc/001-1/bgmaster.txt b/npc/001-1/bgmaster.txt
index d2b63f31e..b9c8d63c3 100644
--- a/npc/001-1/bgmaster.txt
+++ b/npc/001-1/bgmaster.txt
@@ -7,6 +7,7 @@
001-1,96,37,0 script Cassia NPC_FEMALE,{
mesn;
+ if (Class == Skelli) goto L_Busy;
if ($GAME_STORYLINE >= 5 && getq(General_Narrator) >= 23) goto L_Moubootaur;
if (is_gm()) goto L_Control;
if ($@BGMaster1) goto L_Busy;
@@ -14,7 +15,7 @@
// 001-15 Battlegrounds Functions
L_Moubootaur:
- mesq l("Hello! I am Cassia and you are qualified to enter in the cave behind me. Do you want to?");
+ mesq l("Hello! I am Cassia and you are qualified to enter in the cave behind me. Are you interested?");
if (askyesnosafe() == ASK_NO) { closeclientdialog; end; }
setpcblock(PCBLOCK_HARD, true);
mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1;
@@ -27,13 +28,36 @@ L_Moubootaur:
next;
mesn;
mesq l("And maybe not even death can stop you! You're entering directly in the Mana Plane section of the World's Heart - the Soul Menhir will %s work inside.", b(l("NOT")));
+ next;
+ mesn;
+ mesq l("I have absolutely no idea on what to expect inside, so you should come prepared! Make sure you have a party with you, because you might be stuck there %s if you fail.", b(l("FOREVER"))); // You _can_ leave, but it's somewhat difficult
+ next;
+
+ // Superior Safeguard
+ if (!is_staff()) goto L_TODO;
+
+ // Ask if the player is sure they want to go to the showdown
+ mesc l("With all that said, are you sure you want to enter?!"), 1;
+ if (askyesnosafe() == ASK_NO) goto L_Close;
+
+ warp "001-15", any(52,53), 58;
+ // Start main timer
+ if (!$@ML_SHOWDOWN)
+ donpcevent "#Moubootaur::OnIntroCutscene";
+ // TODO: bg_join?
+ // TODO: "#Moubootaur"::PermanentDebuffs()
+ goto L_Close;
+L_TODO:
// Lore goes here...
// Endtrail
mes "";
mesc l("@@ You need to wait further releases to continue this quest!", b(l("WARNING:"))), 1;
-
next;
+ goto L_Close;
+
+// This label closes the dialogue after unblocking the player
+L_Close:
setpcblock(PCBLOCK_HARD, false);
closeclientdialog;
close;
@@ -55,9 +79,6 @@ L_Intro:
}
close;
-L_Close:
- close;
-
L_Busy:
if ($@BGMaster1 == 2) {
mesq l("The soldiers are resting at the moment.");
diff --git a/npc/001-15/_import.txt b/npc/001-15/_import.txt
index 1fb979870..8e0f742e7 100644
--- a/npc/001-15/_import.txt
+++ b/npc/001-15/_import.txt
@@ -1,3 +1,4 @@
// Map 001-15: Moubootaur Showdown
// This file is generated automatically. All manually added changes will be removed when running the Converter.
"npc/001-15/_mobs.txt",
+"npc/001-15/ctrl.c",
diff --git a/npc/001-15/_mobs.txt b/npc/001-15/_mobs.txt
index 6339d55fa..e369cf266 100644
--- a/npc/001-15/_mobs.txt
+++ b/npc/001-15/_mobs.txt
@@ -1,3 +1,4 @@
// This file is generated automatically. All manually added changes will be removed when running the Converter.
// Map 001-15: Moubootaur Showdown mobs
-001-15,54,39,34,19 monster Vampire Bat 1063,12,300000,30000
+001-15,50,39,30,19 monster Vampire Bat 1063,12,300000,30000
+001-15,85,39,3,19 monster House Maggot 1084,6,30000,30000
diff --git a/npc/001-15/ctrl.c b/npc/001-15/ctrl.c
new file mode 100644
index 000000000..928a53bdc
--- /dev/null
+++ b/npc/001-15/ctrl.c
@@ -0,0 +1,960 @@
+// TMW2 scripts.
+// Author:
+// Jesusalva
+// Description:
+// Moubootaur Legends :: Final Showdown
+//
+001-15 mapflag zone FinalMMO
+// Do we need a battlegrounds MF?
+
+001-15,0,0,0 script #Moubootaur NPC_HIDDEN,{
+ end;
+
+public function DeathHandler;
+public function ReviveHandler;
+
+function _boostMe {
+ .@mg = getarg(0);
+ .@bat=getunitdata(.@mg, UDT_ATKMAX);
+ .@bai=getunitdata(.@mg, UDT_ATKMIN);
+ .@bdf=getunitdata(.@mg, UDT_DEF);
+ .@bcr=getunitdata(.@mg, UDT_CRIT);
+ .@bag=getunitdata(.@mg, UDT_AGI);
+ .@bf = getarg(1, $@ML_SHOWDOWN) + 3;
+ .@s=.@bf+rand2(5);
+ .@bat = .@bat * (.@bf / 2);
+ .@bcr = .@bcr * (.@bf / 2);
+ setunitdata(.@mg, UDT_ATKMAX, .@bat+(.@s*5));
+ setunitdata(.@mg, UDT_DEF, .@bdf+(.@s*4));
+ setunitdata(.@mg, UDT_CRIT, .@bcr+(.@s*3));
+ setunitdata(.@mg, UDT_ATKMIN, .@bai+(.@s*2));
+ setunitdata(.@mg, UDT_AGI, .@bag+(.@s*1));
+ return;
+}
+
+// Maybe this should be a function
+public function MapMode {
+ // Define the planned new map mode
+ .@new_mode = rand2(10);
+
+ // Revert the old map mode
+ switch (.mapMode) {
+ case ML_MAPMODE_PVP:
+ pvpoff("001-15"); break;
+ case ML_MAPMODE_NOCHAT:
+ removemapflag("001-15",mf_nocommand);
+ removemapflag("001-15",mf_nochat);
+ break;
+ case ML_MAPMODE_NOPETS:
+ removemapflag("001-15",mf_nopet);
+ break;
+ default:
+ break;
+ }
+
+ // Set the new map mode
+ switch (.@new_mode) {
+ case ML_MAPMODE_PVP:
+ pvpon("001-15"); break;
+ case ML_MAPMODE_NOCHAT:
+ setmapflag("001-15",mf_nocommand,true);
+ setmapflag("001-15",mf_nochat,true);
+ break;
+ case ML_MAPMODE_NOPETS:
+ setmapflag("001-15",mf_nopet,true);
+ break;
+ default:
+ break;
+ }
+
+ // Save the new map mode
+ .mapMode = .@new_mode;
+
+ // TODO: update/set map mask (visual information)
+ return;
+}
+
+OnDoT:
+ if (ispcdead()) DeathHandler(true);
+ percentheal -1, 0;
+ end;
+
+// The functions which the Generals call when they die
+OnBlue:
+ if (!playerattached()) {
+ .@x=72; .@y=41;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyWater, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnBrown:
+ if (!playerattached()) {
+ .@x=25; .@y=55;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyEarth, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnRed:
+ if (!playerattached()) {
+ .@x=74; .@y=55;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyFire, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnGreen:
+ if (!playerattached()) {
+ .@x=42; .@y=36;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyWind, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnWhite:
+ if (!playerattached()) {
+ .@x=25; .@y=25;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeySacred, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnGolden:
+ if (!playerattached()) {
+ .@x=40; .@y=50;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyMana, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnPurple:
+ if (!playerattached()) {
+ .@x=70; .@y=30;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyDeath, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+OnBlack:
+ if (!playerattached()) {
+ .@x=25; .@y=40;
+ } else {
+ getmapxy(.@m$, .@x, .@y, 0);
+ }
+ makeitem(MKeyEvil, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1));
+ .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m, 3);
+ end;
+
+
+
+
+
+// Setup first stage
+OnFirstStage:
+ // Eliminate all existing keys
+ DelItemFromEveryPlayer(MKeyWater);
+ DelItemFromEveryPlayer(MKeyEarth);
+ DelItemFromEveryPlayer(MKeyFire);
+ DelItemFromEveryPlayer(MKeyWind);
+ DelItemFromEveryPlayer(MKeySacred);
+ DelItemFromEveryPlayer(MKeyMana);
+ DelItemFromEveryPlayer(MKeyDeath);
+ DelItemFromEveryPlayer(MKeyEvil);
+
+ // Spawn the map-wide reinforcements, 20 units for 8 generals
+ areamonster("001-15", 20, 20, 80, 59, strmobinfo(1, FlyingUnderling), FlyingUnderling, 20);
+
+ // Spawn all Generals and their personal guard
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 72, 41, "Guardian of Water", TopUnderling, 1, "#Moubootaur::OnBlue");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnEarth");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 74, 55, "Guardian of Fire", TopUnderling, 1, "#Moubootaur::OnRed");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 42, 36, "Guardian of Wind", TopUnderling, 1, "#Moubootaur::OnGreen");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 25, 25, "Guardian of Sacred", TopUnderling, 1, "#Moubootaur::OnWhite");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 40, 50, "Guardian of Mana", TopUnderling, 1, "#Moubootaur::OnGolden");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 70, 30, "Guardian of Death", TopUnderling, 1, "#Moubootaur::OnPurple");
+ _boostMe(.@gen, 6);
+
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2);
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2);
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ .@gen = monster("001-15", 25, 40, "Guardian of Evil", TopUnderling, 1, "#Moubootaur::OnBlack");
+ _boostMe(.@gen, 6);
+
+ end;
+
+
+
+
+
+
+
+
+
+// **MOUBOOTAUR HEARTBEAT**
+OnTimer25000:
+OnTimer20000:
+OnTimer15000:
+ consolewarn("Warning, fail-safe mechanism triggered to Moubootaur (Awakened).");
+OnTimer10000:
+ .mana += any(1,2,3,5); // Recover mana
+
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_DOT) {
+ maptimer2("001-15", 10, "#Moubootaur::OnDoT");
+ }
+
+ // TODO: Defeat Conditions
+ // TODO: Work in Progress
+
+
+ // Maybe we need to advance the turn
+ .tbet += 1;
+ if (!(.tbet % 9)) {
+ // Reset turn cycle (90s) and advance turn counter
+ .tbet=0;
+ .turn+=1;
+
+ // Change map mode
+ if ($@ML_SHOWDOWN >= 3)
+ MapMode();
+
+ // Spawn the air corps
+ if ($@ML_SHOWDOWN >= 2) {
+ for (.@i=0; .@i<5; .@i++) {
+ .@m=areamonster("001-15", 49, 30, 60, 42, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m); // Will boost according to showdown state
+ }
+ }
+ // TODO: Tally Battlegrounds results
+ }
+
+ initnpctimer;
+ end;
+
+
+
+
+
+
+
+
+
+
+
+// Scope: MLDIE_
+// DeathHandler( end = false , dead = true )
+public function DeathHandler {
+ // First death, handle it!
+ debugmes "Change Class from %d to %d", Class, Skelli;
+ if (Class != Skelli) {
+ MLDIE_Class = Class;
+ jobchange Skelli;
+ }
+ // Save your equipment selection for a quick equip() after unstoring
+ // (Handled by ReviveHandler)
+ for ( .@i=EQI_HEAD_TOP ; .@i<=EQI_SHADOW_ACC_L ; .@i++ ) {
+ array_push( MLDIE_Eqp, getequipid(.@i) );
+ }
+
+ // All your items go away to Special Storage
+ charcommand("@storeall 6");
+ // All your money goes away to Special Storage
+ MLDIE_Zeny += Zeny;
+ Zeny = 0;
+
+ // Permanent Debuff is handled by hub functions
+
+ // TODO: Death NPC which recovers all your items
+ // (Do we have bindings for unstoring? We might need a new C-level function)
+ //
+ // (Also, we need to check if Death NPC already exists)
+ // (And this check must be independent on your Class)
+ // (DeathNPC should call ReviveHandler if Class is Skelli or ...?)
+ // (Also, we're failing to revive you here D:)
+
+ // TODO: Check if DeathNpc already exists
+ // TODO: If not and getmap is 001-15, create it
+ // TODO: Else, create it close to a random player in map
+ // TODO: If that fails, create in entrance
+ // TODO: We need a way to distinguish undead players from normal players?
+ // (Will we use Benjamin sprite? What npc_duplicate gives us to track?)
+ // NPC_GUARD_DEAD, NPC_INJURIED_GIRL, or can we make it dynamic?
+
+ // Eliminate any key you may have and revive the trupe
+ // No state check is necessary; the keys should not be duplicated
+ if (countitem(MKeyWater)) {
+ delitem MKeyWater, 1;
+ .@gen = monster("001-15", 72, 41, "Guardian of Water", TopUnderling, 1, "#Moubootaur::OnBlue");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyEarth)) {
+ delitem MKeyEarth, 1;
+ .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnEarth");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyFire)) {
+ delitem MKeyFire, 1;
+ .@gen = monster("001-15", 74, 55, "Guardian of Fire", TopUnderling, 1, "#Moubootaur::OnRed");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyWind)) {
+ delitem MKeyWind, 1;
+ .@gen = monster("001-15", 42, 36, "Guardian of Wind", TopUnderling, 1, "#Moubootaur::OnGreen");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeySacred)) {
+ delitem MKeySacred, 1;
+ .@gen = monster("001-15", 25, 25, "Guardian of Sacred", TopUnderling, 1, "#Moubootaur::OnWhite");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyMana)) {
+ delitem MKeyMana, 1;
+ .@gen = monster("001-15", 40, 50, "Guardian of Mana", TopUnderling, 1, "#Moubootaur::OnGolden");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyDeath)) {
+ delitem MKeyDeath, 1;
+ .@gen = monster("001-15", 70, 30, "Guardian of Death", TopUnderling, 1, "#Moubootaur::OnPurple");
+ _boostMe(.@gen, 12);
+ }
+ if (countitem(MKeyEvil)) {
+ delitem MKeyEvil, 1;
+ .@gen = monster("001-15", 25, 40, "Guardian of Evil", TopUnderling, 1, "#Moubootaur::OnBlack");
+ _boostMe(.@gen, 12);
+ }
+
+ // Warp you to the respawn area if set (and revives you)
+ if (getarg(1, true)) {
+ debugmes "Now reviving ML victim...";
+ atcommand("@alive");
+ warp "001-15", rand2(84, 87), rand2(21, 26);
+ }
+
+ // Prevent you from being slaughtered right after
+ // By granting you **4** seconds of invincibility!
+ // TODO: Check if this works correctly
+ sc_start SC_INVINCIBLE, rand(4000,4900), 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_NOICON;
+
+ debugmes "We have now completed the Death Handler";
+
+ // We were instructed to terminate the interaction
+ if (getarg(0, false))
+ end;
+ // Otherwise, return the control to caller
+ return;
+}
+
+
+
+
+
+
+
+
+// Undo everything DeathHandler did, and takes same arguments
+public function ReviveHandler {
+ // TODO: "#Moubootaur"::PermanentDebuffs() if getmap() == 001-15
+ // Restore your Class
+ jobchange MLDIE_Class;
+ Zeny += MLDIE_Zeny;
+ // Reset these variables
+ MLDIE_Class = 0;
+ MLDIE_Zeny = 0;
+
+ // TODO: !! Unstash all your items !!
+ unstoreall(6);
+ /* This code does not work
+ sc_start SC_INVINCIBLE, 99999, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_NOICON;
+ openstorage(6, STORAGE_ACCESS_GET);
+ sc_end SC_INVINCIBLE;
+ sc_start SC_INVINCIBLEOFF, 1, 1;
+ */
+
+ // Re-equip your items
+ // Do not call equip() if < 1, it can be negative after all
+ for (.@i=0 ; .@i < getarraysize(MLDIE_Eqp) ; .@i++) {
+ if (MLDIE_Eqp[.@i] > 1)
+ equip(MLDIE_Eqp[.@i]);
+ }
+ deletearray MLDIE_Eqp;
+
+ // TODO: Eliminate DeathNPC if it exists
+
+ // We were instructed to terminate the interaction
+ if (getarg(0, false))
+ end;
+ // Otherwise, return the control to caller
+ return;
+}
+
+// Begin Introduction Cutscene (Event Start!!)
+OnIntroCutscene:
+ // Reset Showdown Status (Clear Previous Attempts)
+ stopnpctimer;
+ $@ML_SHOWDOWN=1;
+ .turn = 0;
+ .tbet = 0;
+ .ML = 0;
+ disablenpc "#ML_EastWarp";
+ disablenpc "#MLWA+";
+
+ // Create Andrei Sakar
+ .HERO=monster("001-15", 55, 47, "Andrei Sakar", ManaGhost, 1);
+ immortal(.HERO);
+ sc_start SC_STUN, 99990, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .HERO;
+
+ // This is the prologue cutscene. It's kinda long so everyone has time to arrive.
+ sleep(1000);
+ unittalk(.HERO, "Oooohhhh. Someone actually came!");
+ sleep(5000);
+ unittalk(.HERO, "Maybe I should wait a while to see if more people show up!");
+ sleep(5000);
+ unittalk(.HERO, "Sorry, I haven't existed properly for a while.");
+ sleep(5000);
+ unittalk(.HERO, "But this is the Mana Plane section of the World Heart, so it matters not.");
+ sleep(10000);
+ unittalk(.HERO, "Anyway, my name is Andrei, and I used to be a legendary hero of this world.");
+ sleep(8000);
+ unittalk(.HERO, "Then, during a campaign to the Fortress, I noticed the lack of aid from Artis region.");
+ sleep(8000);
+ unittalk(.HERO, "While investigating Artis, Isbamuth minions killed me as a sacrifice to revive the Moubootaur.");
+ sleep(9000);
+ unittalk(.HERO, "I'm not entirely sure why. Mr. Saves should have prevented Talpans from allying with the Moubootaur and monsters from allying with Elli, but I assume exceptions were made. Likely a bug!");
+ sleep(14000);
+ unittalk(.HERO, "Anyway, this means I'm unable to use the Soul Menhir and became a lost soul. Same will happen to all of you, shall you die here!");
+ sleep(8000);
+ unittalk(.HERO, "But if you find your corpse back, or one of the Obelisks of Power, you might be able to return.");
+ sleep(7000);
+ // Official justification in case your body (Death NPC) is not available.
+ unittalk(.HERO, "Even if your body is consumed like mine was, the Obelisk of Power can still bring you back. However...");
+ sleep(7500);
+ unittalk(.HERO, "Elli still has jurisdiction over Talpans. She probably vetoed my return, because I couldn't revive! Hahahah.");
+ sleep(7500);
+ /* TODO: Apply Elli's curse to all players */
+ unittalk(.HERO, "Anyway! The Moubootaur is in the area ahead plotting something, but you'll need to find a way to enable the warp I'm standing on first.");
+ sleep(10000);
+ unittalk(.HERO, "I'll enable the warp to the west so you can explore! Good luck, adventurers!");
+ sleep(2000);
+ enablenpc "#MLWA+";
+
+ // And now that everything is ready and done, start the timer and set turn 1
+ .turn = 1;
+ initnpctimer;
+ sc_end SC_STUN, .HERO;
+ goto OnFirstStage;
+
+OnInit:
+ .warpsOnline = true;
+ .mapMode = 0;
+
+ .ML=0;
+ .HERO=0;
+ .maxhp = 1000000; // 1,000,000 HP (used by scripts)
+ .mana = 0; // More mana = more likely to cast skills
+
+ .memohp = 999; // Memorand HP, controls spawns
+ .start_time = gettimetick(2); // Controls Death Touch
+ .tbet = 0; // Internal counter for turn system
+ .turn = 0; // Actual turn counter
+
+ .dmhp1 = 750000; // The "default" max HP
+ .dmhp2 = 250000; // The "extra" max HP
+
+ // Platforms Cardinal Sequence is
+ // Central, Clock, Wind, Fire, Plague, Cold, Blood, Disease, Curse
+ // XXX: Moubootaur goes from [1] to [8]
+ setarray $@MLFX, 52, 44, 43, 78, 64, 31, 22, 27, 74, 44, 43, 78, 64, 31, 22, 27, 74;
+ setarray $@MLFY, 49, 55, 24, 56, 44, 20, 51, 34, 31, 55, 24, 56, 44, 20, 51, 34, 31;
+ end;
+}
+
+
+
+001-15,55,47,0 script #ML_NorthWarp NPC_ML_CIRCLE,0,0,{
+ end;
+OnTouch:
+ if ($@ML_SHOWDOWN < 2) goto L_InitCheck;
+ slide 54, 29;
+
+ // Kill any stray timer too in the Central Platform
+ // It's overkill and not really necessary, BUT better safe than sorry
+ deltimer("#DungeonCore::OnClocked");
+ deltimer("#DungeonCore::OnWindy");
+ deltimer("#DungeonCore::OnHeat");
+ deltimer("#DungeonCore::OnLeech");
+ deltimer("#DungeonCore::OnFrost");
+ deltimer("#DungeonCore::OnBleed");
+ deltimer("#DungeonCore::OnSick");
+ deltimer("#DungeonCore::OnCurse");
+ end;
+
+L_InitCheck:
+ // First way to trigger: All keys in a single player inventory
+ if (countitem(MKeyWater) == 1 &&
+ countitem(MKeyEarth) == 1 &&
+ countitem(MKeyDeath) == 1 &&
+ countitem(MKeyFire) == 1 &&
+ countitem(MKeyWind) == 1 &&
+ countitem(MKeyMana) == 1 &&
+ countitem(MKeyEvil) == 1 &&
+ countitem(MKeySacred) == 1) goto L_InitDel;
+ // Second (prefered) way to trigger: All keys in the magic circle
+ .@i=0;
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWater, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEarth, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyDeath, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyFire, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWind, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyMana, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEvil, false));
+ .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeySacred,false));
+ if (.@i == 8) goto L_InitOk;
+ dispbottom l("This circle doesn't work right now. Maybe there's a clue on what is needed?");
+ end;
+
+L_InitDel:
+ delitem MKeySacred, 1;
+ delitem MKeyWater, 1;
+ delitem MKeyEarth, 1;
+ delitem MKeyDeath, 1;
+ delitem MKeyFire, 1;
+ delitem MKeyWind, 1;
+ delitem MKeyEvil, 1;
+ delitem MKeyMana, 1;
+ // FALLTHROUGH
+L_InitOk:
+ $@ML_SHOWDOWN=2;
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWater, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEarth, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyDeath, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyFire, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWind, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyMana, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEvil, true);
+ getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeySacred,true);
+ // Send the hero to the warp circle
+ .@hero = getvariableofnpc(.HERO, "#Moubootaur");
+ unitwalk(.@hero, 55, 47);
+ // Mark the activation
+ sleep2(50);
+ specialeffect(312, AREA, .name$);
+ // Enable the warp and NPC dialogue
+ sleep2(300);
+ enablenpc "#ML_EastWarp";
+ unittalk(getvariableofnpc(.HERO, "#Moubootaur"), "Good job! Chop chop!");
+ // Warp NPC and activator to the platform
+ sleep2(250);
+ unitwarp(.@hero, "001-15", 55, 29);
+ sc_start SC_STUN, 86400000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@hero;
+ // TODO: Begin 2nd Stage Cutscene
+ goto OnTouch;
+ end;
+OnInit:
+ end;
+}
+
+
+
+001-15,54,28,0 script #ML_NorthExit NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ if ($@ML_SHOWDOWN < 2) end;
+ slide 55, 48;
+ end;
+L_InitCheck:
+ end;
+OnInit:
+ end;
+}
+
+
+
+001-15,60,53,0 script #ML_EastWarp NPC_SUMMONING_CIRC,0,0,{
+ end;
+OnTouch:
+ // Verify if this portal is active (it should, however, be disabled!)
+ if (!getvariableofnpc(.turn, "#Moubootaur"))
+ end;
+ if ($@ML_SHOWDOWN < 2) goto L_Inactive;
+
+ // Assuming nothing is wrong, then do the warp
+ slide 85, 57;
+ end;
+
+// Report the error to the user & console
+L_Inactive:
+ dispbottom l("There's nothing of interest in this direction... for now.");
+ consolewarn("ML Showdown: Eastern Portal was visible while disabled.");
+ end;
+OnInit:
+ end;
+}
+
+
+// You can always leave the battleground area, there's no restriction whatsoever
+001-15,85,58,0 script #ML_EastExit NPC_SUMMONING_CIRC,0,0,{
+ end;
+OnTouch:
+ slide 60, 54;
+ end;
+OnInit:
+ end;
+}
+
+
+
+/* Miller system (otherwise, you can't reach the switches & platforms) */
+001-15,51,49,0 script #MLWA+ NPC_FANCY_CIRCLE,0,0,{
+ end;
+OnTouch:
+ // TODO: Verify if portalling is active in the Showdown
+ // Probably via a public function in #Moubootaur
+ if (false) {
+ dispbottom l("Oh no! The Moubootaur has disabled this portal!");
+ end;
+ }
+ if (!$@ML_SHOWDOWN)
+ end;
+ if (!getvariableofnpc(.turn, "#Moubootaur"))
+ end;
+
+ // Dungeon Climate System (stop effect, start next)
+ // TODO: Disable previous Climate System timer, before @state is changed
+ switch (miller_rand(@state, getcharid(0), 9)) {
+ case 1:
+ deltimer("#DungeonCore::OnClocked"); break;
+ case 2:
+ deltimer("#DungeonCore::OnWindy"); break;
+ case 3:
+ deltimer("#DungeonCore::OnHeat"); break;
+ case 4:
+ deltimer("#DungeonCore::OnLeech"); break;
+ case 5:
+ deltimer("#DungeonCore::OnFrost"); break;
+ case 6:
+ deltimer("#DungeonCore::OnBleed"); break;
+ case 7:
+ deltimer("#DungeonCore::OnSick"); break;
+ case 8:
+ deltimer("#DungeonCore::OnCurse"); break;
+ default:
+ // Case 0 has no special effects whatsoever
+ break;
+ }
+
+ // All portals are the same, anyway
+ if (compare(strnpcinfo(2), "+"))
+ @state += 3;
+ else
+ @state -= 2;
+ .@index = miller_rand(@state, getcharid(0), 9);
+ slide $@MLFX[.@index], $@MLFY[.@index];
+
+ // Dungeon Climate System (start effect, stop previous)
+ switch (.@index) {
+ case 1:
+ addtimer(10, "#DungeonCore::OnClocked"); break;
+ case 2:
+ addtimer(10, "#DungeonCore::OnWindy"); break;
+ case 3:
+ addtimer(10, "#DungeonCore::OnHeat"); break;
+ case 4:
+ addtimer(10, "#DungeonCore::OnLeech"); break;
+ case 5:
+ addtimer(10, "#DungeonCore::OnFrost"); break;
+ case 6:
+ addtimer(10, "#DungeonCore::OnBleed"); break;
+ case 7:
+ addtimer(10, "#DungeonCore::OnSick"); break;
+ case 8:
+ addtimer(10, "#DungeonCore::OnCurse"); break;
+ default:
+ // Case 0 has no special effects whatsoever
+ break;
+ }
+ end;
+}
+//001-15,28,136,0 duplicate(#MLWA+) #MLWA- NPC_FANCY_CIRCLE,0,0
+// Clock, Wind, Fire, Plague, Cold, Blood, Disease, Curse
+// B, C, D, E, F, G, H, I
+001-15,34,44,0 duplicate(#MLWA+) #MLWB+ NPC_NO_SPRITE,0,0
+001-15,45,55,0 duplicate(#MLWA+) #MLWB- NPC_NO_SPRITE,0,0
+001-15,36,38,0 duplicate(#MLWA+) #MLWC+ NPC_FANCY_CIRCLE,0,0
+001-15,44,24,0 duplicate(#MLWA+) #MLWC- NPC_FANCY_CIRCLE,0,0
+001-15,69,53,0 duplicate(#MLWA+) #MLWD+ NPC_FANCY_CIRCLE,0,0
+001-15,78,55,0 duplicate(#MLWA+) #MLWD- NPC_FANCY_CIRCLE,0,0
+001-15,75,38,0 duplicate(#MLWA+) #MLWE+ NPC_FANCY_CIRCLE,0,0
+001-15,63,44,0 duplicate(#MLWA+) #MLWE- NPC_FANCY_CIRCLE,0,0
+001-15,21,24,0 duplicate(#MLWA+) #MLWF+ NPC_FANCY_CIRCLE,0,0
+001-15,31,22,0 duplicate(#MLWA+) #MLWF- NPC_FANCY_CIRCLE,0,0
+001-15,31,59,0 duplicate(#MLWA+) #MLWG+ NPC_FANCY_CIRCLE,0,0
+001-15,21,51,0 duplicate(#MLWA+) #MLWG- NPC_FANCY_CIRCLE,0,0
+001-15,22,43,0 duplicate(#MLWA+) #MLWH+ NPC_FANCY_CIRCLE,0,0
+001-15,28,34,0 duplicate(#MLWA+) #MLWH- NPC_FANCY_CIRCLE,0,0
+001-15,65,26,0 duplicate(#MLWA+) #MLWI+ NPC_FANCY_CIRCLE,0,0
+001-15,75,31,0 duplicate(#MLWA+) #MLWI- NPC_FANCY_CIRCLE,0,0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// Main Room Traps, only against players
+001-15,0,0,0 script #ML_Trap01 NPC_TRAP_ONLINE,0,0,{
+ end;
+
+OnTouch:
+OnTouchNPC:
+ WorldHeartTrap();
+ sleep(500); // Wait 500ms for animation
+ setnpcdisplay .name$, NPC_TRAP_ONLINE;
+ // Move the trap away after it disarms (up to 30 attempts)
+
+OnInit:
+ .@e=0;
+ do {
+ if (.@e >= 30)
+ break;
+ .@x = rand2(48, 60);
+ .@y = rand2(30, 44);
+ .@e+=1;
+ } while (!checknpccell("001-15", .@x, .@y, cell_chkpass));
+ movenpc .name$, .@x, .@y;
+ end;
+}
+
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap02 NPC_TRAP_ONLINE,0,0
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap03 NPC_TRAP_ONLINE,0,0
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap04 NPC_TRAP_ONLINE,0,0
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap05 NPC_TRAP_ONLINE,0,0
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap06 NPC_TRAP_ONLINE,0,0
+001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap07 NPC_TRAP_ONLINE,0,0
+
+
+
+
+
+// Convenience Shop NPC
+// TODO: enablenpc; and disablenpc; depending on state? Move to (58,50)?
+001-15,50,47,0 script Tiki NPC_TIKI,{
+ mesn;
+ mes l("Hey hey, my name is Micksha, and I'm Arthur's grandfather!");
+ mes l("I retired to my own laboratory and sell strange drinks on the side!");
+ mes l("The Mirror Lake here is twisty, so I can do things to you... for a price!");
+ next;
+ // Pricing
+ .@price = 15;
+ if (!MOUBOOTAUR_WINNER) .@price += 150;
+ if (!EPISODE_WINNER) .@price += 150;
+ if (islegendary()) .@price += 65;
+ mesc l("Special Price for You! %s GP!", fnum(.@price)), 1;
+ // Friendly Advise, "free" of charge
+ if (any(true, false))
+ mesc l("* \"Make sure to be well stocked and stashed for the fights! Preparation is the utmost for victory!\" - The Micksha");
+ else
+ mesc l("* \"Prepare, go and destroy! Don't tell your parents I've said that!\" - The Micksha");
+
+ // Require the money NOW
+ if (Zeny >= .@price)
+ Zeny -= .@price;
+ else if (BankVault >= .@price)
+ BankVault -= .@price;
+ else
+ close;
+
+ // Main Menu
+ select
+ l("I don't need anything!"),
+ l("Withdraw Money"),
+ rif($@ML_SHOWDOWN < 2, l("Open Storage")),
+ l("Nursery services"),
+ l("Acquire goods");
+ switch (@menu) {
+ case 2:
+ Banking();
+ break;
+ case 3:
+ Banker("Micksha", "Impregnable Fortress", 999999);
+ break;
+ case 4:
+ Nurse("Micksha", 10, 6);
+ break;
+ case 5:
+ closeclientdialog;
+ openshop(.name$);
+ break;
+ default:
+ Zeny += .@price;
+ break;
+ }
+ close;
+
+// x2( Item , Multiplier=2x )
+function x2 {
+ return getiteminfo(getarg(0), ITEMINFO_BUYPRICE) * getarg(1, 2);
+}
+
+OnInit:
+ .sex = G_MALE;
+ .distance = 3;
+ sellitem InsuranceContract, x2(InsuranceContract, 1);
+ sellitem ChamomileTea, x2(ChamomileTea, 4); // Mana F
+ sellitem YerbaMate, x2(YerbaMate, 1); // Mana S
+ sellitem LemonCake, x2(LemonCake, 3); // Homun F
+ sellitem WhiteCake, x2(WhiteCake, 5); // Homun A
+ sellitem PiberriesInfusion, x2(PiberriesInfusion, 3);
+ sellitem FatesPotion, x2(FatesPotion, 3);
+ sellitem ClothoLiquor, x2(ClothoLiquor, 3);
+ sellitem LachesisBrew, x2(LachesisBrew, 3);
+ sellitem AtroposMixture, x2(AtroposMixture, 3);
+ sellitem TrainingAmmoBox, x2(TrainingAmmoBox); // Arrow E
+ sellitem IronAmmoBox, x2(IronAmmoBox); // Arrow C
+ sellitem BigBulletSack, x2(BigBulletSack, 1); // Bullets MAX
+ sellitem StatusResetPotion, 4999;
+ sellitem MercBoxA, 9999;
+ sellitem BottleOfSewerWater, 9999;
+ sellitem BlueberryCake, 9999;
+ sellitem SmokeGrenade, 9999;
+ sellitem ScentGrenade, 9999;
+ sellitem HerbalTea, 9999;
+ sellitem EmptyBox, 9999;
+ sellitem Coffee, 9999;
+ sellitem IcedBottle, 9999;
+ sellitem PurificationPotion, 9999;
+ sellitem ScrollSCave, 9999;
+ sellitem CommonCarp, 9999;
+ sellitem FishingRod, 9999;
+ sellitem TreasureKey, 9999;
+ sellitem SaxsoKey, 9999;
+ sellitem Flour, 9999;
+ sellitem StrangeCoin, 9999;
+ sellitem GuildCoin, 9999;
+ sellitem HeroCoin, 9999;
+ sellitem Lifestone, 9999;
+ sellitem Quill, 9999;
+ sellitem EverburnPowder, 9999;
+ sellitem EarthPowder, 9999;
+ sellitem WoodenLog, 9999;
+ sellitem MinerKnife, 9999;
+ sellitem ShortBow, 9999;
+ sellitem Lockpicks, 9999;
+ sellitem IronShovel, 9999;
+ sellitem EmptyBottle, 12999;
+ sellitem ArcmageBoxset, 14999;
+ sellitem LeatherQuiver, 19999;
+ sellitem HomunResetPotion, 19999;
+ sellitem DeathPotion, 19999;
+ sellitem NymphPoison, 19999;
+ sellitem BrokenWarpCrystal, 19999;
+ sellitem SacredImmortalityPotion, 29999;
+ sellitem GoldenApple, 49999;
+ sellitem ElixirOfLife, 99999;
+ sellitem MagicApple, 199999;
+ sellitem LegendaryTortuga, 1499999;
+ sellitem PiouEgg, 3499999;
+ sellitem Skypiercer, 9999999;
+ sellitem BlackyCatFix, INT_MAX/2;
+ end;
+}
+
+function script ML_MobKill {
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_PAY2KILL) {
+ // Not a target of the map-wide effect
+ if (getmap() != "001-15")
+ return;
+ // Price is 10 GP per monster level (~3000 gp for Tortuga)
+ .@price = strmobinfo(3,killedrid) * 10;
+ .@price += rand2(JobLevel);
+ .@debit = 0;
+ // Pay the money or set .@debit
+ if (Zeny >= .@price)
+ Zeny -= .@price;
+ else
+ .@debit = .@price - Zeny;
+ // For each unpaid GP, you lose 1% HP
+ if (.@debit) {
+ Zeny = 0;
+ percentheal -(.@debit), -(.@debit);
+ }
+ }
+ return;
+}
+
+
diff --git a/npc/001-16/_import.txt b/npc/001-16/_import.txt
index f3db9f4ec..6edab71ba 100644
--- a/npc/001-16/_import.txt
+++ b/npc/001-16/_import.txt
@@ -1,3 +1,4 @@
// Map 001-16: Existential Limbo
// This file is generated automatically. All manually added changes will be removed when running the Converter.
"npc/001-16/_mobs.txt",
+"npc/001-16/obelisk.c",
diff --git a/npc/001-16/_mobs.txt b/npc/001-16/_mobs.txt
index a55fb6ddc..2cebe5e0e 100644
--- a/npc/001-16/_mobs.txt
+++ b/npc/001-16/_mobs.txt
@@ -1,4 +1,9 @@
// This file is generated automatically. All manually added changes will be removed when running the Converter.
// Map 001-16: Existential Limbo mobs
-001-16,169,154,120,135 monster Cave Maggot 1027,44,35000,300000
-001-16,136,148,115,129 monster Black Scorpion 1074,23,35000,300000
+001-16,169,154,120,135 monster Cave Maggot 1027,77,35000,300000
+001-16,136,148,115,129 monster Black Scorpion 1074,36,35000,300000
+001-16,145,164,125,129 monster Magic Goblin 1052,23,35000,300000
+001-16,169,147,125,129 monster Black Cat 1192,23,35000,300000
+001-16,165,229,141,80 monster Maverick 1452,12,35000,300000
+001-16,202,69,94,58 monster Maverick 1452,12,35000,300000
+001-16,158,172,141,120 monster Shadow Tortuga 1429,4,35000,300000
diff --git a/npc/001-16/obelisk.c b/npc/001-16/obelisk.c
new file mode 100644
index 000000000..afcdef451
--- /dev/null
+++ b/npc/001-16/obelisk.c
@@ -0,0 +1,92 @@
+// TMW2 scripts.
+// Author:
+// Jesusalva
+// Description:
+// Moubootaur Legends :: Final Showdown - Post Mortem Realm
+
+001-16 mapflag zone FinalMMO
+
+// This Obelisk will warp you back to the Showdown. Are you sure? 89 48
+001-16,89,48,0 script Obelisk#MLDL1 NPC_FINAL_POINT,{
+ mesn;
+ mesc l("Lost soul who wanders the mazes of life and death, the path lies herein ahead, but only those of stout heart may return.");
+ mesc l("This is the challenge which Mr. Saves has determined upon all those whom die outside the Mana Source's Jurisdiction.");
+ .@ans = ASK_NO;
+ if ($@ML_SHOWDOWN) {
+ next;
+ mesc l("Do you wish to return to the Moubootaur Showdown as an undead?"), 1;
+ next;
+ .@ans = askyesno();
+ }
+ closeclientdialog;
+ if (.@ans == ASK_YES)
+ "#Moubootaur"::DeathHandler(true);
+ close;
+
+OnInit:
+ .distance=2;
+ end;
+}
+
+// This Obelisk will warp you to a random town. Are you sure? 215 162
+001-16,215,162,0 script Obelisk#MLDL2 NPC_FINAL_POINT,{
+ mesc l("This Obelisk will warp you to a random town. Are you sure?"), 1;
+ .@ans = askyesno();
+ closeclientdialog;
+ if (.@ans == ASK_YES) {
+ .@dest = any(TP_FROST, TP_HALIN, TP_LOF, TP_FORT, TP_LILIT, TP_CANDOR, TP_ARTIS); // TP_BOSSR? TP_NIVAL?
+ .@i = array_find($@LOCMASTER_TP, .@dest);
+ warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i];
+ EnterTown($@LOCMASTER_LOC$[.@i], true);
+ "#Moubootaur"::ReviveHandler(true);
+ }
+ close;
+
+OnInit:
+ .distance=2;
+ end;
+}
+
+// This Obelist will warp you to the Optional Dungeon. Are you sure? 286 287
+// if you can't, then the Obelisk does not react
+001-16,286,287,0 script Obelisk#MLDL3 NPC_FINAL_POINT,{
+ if (!MK_WINNER) {
+ npctalk3 l("The Obelisk doesn't react. You likely haven't fulfilled the requisites to use it.");
+ end;
+ }
+ mesc l("This Obelisk will warp you to the Optional Dungeon. Are you sure?"), 1;
+ .@ans = askyesno();
+ closeclientdialog;
+ if (.@ans == ASK_YES) {
+ .@dest = TP_HEART;
+ .@i = array_find($@LOCMASTER_TP, .@dest);
+ warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i];
+ EnterTown($@LOCMASTER_LOC$[.@i], true);
+ "#Moubootaur"::ReviveHandler(true);
+ }
+ close;
+
+OnInit:
+ .distance=2;
+ end;
+}
+
+// This Obelisk will return you to your savepoint and heal you fully. Are you sure?
+// 108 216
+001-16,108,216,0 script Obelisk#MLDL4 NPC_FINAL_POINT,{
+ mesc l("This Obelisk will return you to your savepoint and heal you fully. Are you sure?"), 1;
+ .@ans = askyesno();
+ closeclientdialog;
+ if (.@ans == ASK_YES) {
+ teleporthome();
+ "#Moubootaur"::ReviveHandler(false);
+ percentheal 100, 100;
+ SC_Bonus(180, SC_KAIZEL, 1); // Revives with 1% HP if you die
+ }
+ end;
+
+OnInit:
+ .distance=2;
+ end;
+}
+
diff --git a/npc/003-6/tamiloc.txt b/npc/003-6/tamiloc.txt
index cb2e11f1c..6c08fa2df 100644
--- a/npc/003-6/tamiloc.txt
+++ b/npc/003-6/tamiloc.txt
@@ -85,7 +85,8 @@ OnSaviorCall:
select
l("Nooo! I want my MP back!"),
rif(is_gm() || REBIRTH >= 5, l("I want to change my Race!")),
- l("I want to change my hair color.");
+ l("I want to change my hair color."),
+ rif(FINAL_WINNER && REBIRTH >= 5, l("Please kill me and make me a lame undead!"));
mes "";
switch (@menu) {
case 2:
@@ -94,6 +95,9 @@ OnSaviorCall:
case 3:
Sp -= 200; BarberChangeColor();
break;
+ case 4:
+ Sp -= 200; jobchange Skelli;
+ break;
}
close;
diff --git a/npc/functions/hub.txt b/npc/functions/hub.txt
index 5db919aa1..ba0c0b5c5 100644
--- a/npc/functions/hub.txt
+++ b/npc/functions/hub.txt
@@ -119,7 +119,7 @@ function script HUB_Logout {
if (@grace) {
// Grace is upon you (ie. script death)
@grace=false;
- } else if ((.@zone$ == "MMO" || .@zone$ == "SuperMMO") && ABSOLVE_CNT <= 3) {
+ } else if ((.@zone$ == "MMO" || .@zone$ == "SuperMMO" || .@zone$ == "FinalMMO") && ABSOLVE_CNT <= 3) {
// Absolve (limited attempts)
ABSOLVE_CNT+=1;
dispbottom l("This is a special map so your death is not counted.");
@@ -288,6 +288,10 @@ function script HUB_Logout {
if (compare(.@mapa$, "fyrb")) {
doevent "sBossRaid::OnDie";
}
+ // Died or logged out at Final Showdown event
+ if (.@mapa$ == "001-15") {
+ "#Moubootaur"::DeathHandler(false, .@dead);
+ }
// Died or logged out during Yeti King Fight
if (getq(HurnscaldQuest_Celestia) > 1)
setq HurnscaldQuest_Celestia, 1;
@@ -561,6 +565,12 @@ function script HUB_SkillInvoke {
if (checkidle() > 180)
return;
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOMAGIC) {
+ if (getmap() == "001-15")
+ return;
+ }
+
// Record to database
skillInvoke[@skillId] = skillInvoke[@skillId] + 1;
callfunc "FYE_Olympics_SK";
@@ -1418,6 +1428,7 @@ function script HUB_PCBonus {
if (PCBONUS & PCB_LEGENDARY) {
bonus bDefRatioAtkRace, RC_Legendary;
}
+
/* Passive Skills */
bonus2 bSubRace, RC_Legendary, getskilllv(AL_DP)-10;
if (getq(LoFQuest_Barbara) >= 4 || getq(General_Narrator) >= 19)
@@ -1430,6 +1441,44 @@ function script HUB_PCBonus {
/* Stat reassignment */
bonus bMaxHP, (readparam2(bVit)*2)-(readparam2(bAgi)*2);
+ /* Nullify all assigned stats, and general debuffs */
+ if (Class == Skelli) {
+ bonus bStr, 1-readparam(bStr);
+ bonus bAgi, 1-readparam(bAgi);
+ bonus bVit, 1-readparam(bVit);
+ bonus bDex, 1-readparam(bDex);
+ bonus bInt, 1-readparam(bInt);
+ bonus bLuk, 1-readparam(bLuk);
+ bonus bAllStats, -REBIRTH;
+ // Now comes SEVERAL debuffs for this specific class
+ bonus bMaxHPrate, -25;
+ bonus bMaxSPrate, -25;
+ bonus bAtkRate, -25;
+ bonus bDefRate, -25;
+ bonus bDef2Rate, -25;
+ bonus bMatkRate, 10; // MATK actually gets a buff for... reasons?
+ bonus bMdefRate, -25;
+ bonus bMdef2Rate, -25;
+ bonus bHitRate, -25;
+ bonus bCriticalRate, -25;
+ bonus bFleeRate, -25;
+ bonus bFlee2Rate, 10; // Perfect Evasion actually gets a buff for... dunno
+ bonus bSpeedAddRate, 1; // So... Moving speed bonus/debuff?
+ bonus bAspdRate, -25;
+ bonus bHPrecovRate, -25;
+ bonus bSPrecovRate, -25;
+ bonus bAddItemHealRate, -25;
+ // Shuffle your defense to work better against ranged attacks
+ // But make you weaker to traps, fixed-damage and magic
+ bonus bMagicAtkDef, -15;
+ bonus bMiscAtkDef, -15;
+ bonus bLongAtkDef, 20;
+ // Also, cannot be knocked back for... reasons
+ bonus bNoKnockback, 1;
+ bonus bIntravision, 1;
+ bonus bRestartFullRecover, 75; // Is 75 ignored, or revive with 75% HP
+ }
+
// Double hack fix
if (isequipped(ExplosiveArrow)) {
if (getiteminfo(getequipid(EQI_HAND_R), ITEMINFO_SUBTYPE) != W_BOW)
diff --git a/npc/functions/mobpoint.txt b/npc/functions/mobpoint.txt
index 243cfed1d..2456f7110 100644
--- a/npc/functions/mobpoint.txt
+++ b/npc/functions/mobpoint.txt
@@ -124,6 +124,7 @@ OnNPCKillEvent:
callfunc "GeminiKill";
callfunc "SK_drops";
callfunc "BonusEXP";
+ callfunc "ML_MobKill";
// Other updates
$@MK_TRIGGERED=true;
diff --git a/npc/functions/util.txt b/npc/functions/util.txt
index 8f0405faa..345628ade 100644
--- a/npc/functions/util.txt
+++ b/npc/functions/util.txt
@@ -1160,11 +1160,19 @@ function script POL_LocToTP {
// Upon entering a town
-// EnterTown( LocName )
+// EnterTown( LocName{, force=false} )
function script EnterTown {
// Fill variable
.@v$=getarg(0);
+ // If you're dead and did not want to, forceful correction
+ // (MLDIE_Class however CAN be zero, check isn't perfect)
+ if (Class == Skellie && MLDIE_Class && !getarg(1, false)) {
+ Exception(sprintf("EnterTown: Player %d tried to enter %s but had no soul!", getcharid(0), .@v$), RB_DEBUGMES|RB_IRCBROADCAST);
+ warp "001-16", 90, 45;
+ return;
+ }
+
// Validade variable, see npc/config/location.txt first
if (array_find($@LOCMASTER_LOC$, .@v$) < 0)
return Exception("Invalid location passed to EnterTown: "+.@v$);
@@ -1239,7 +1247,7 @@ function script teleporthome {
}
.@i=array_find($@LOCMASTER_MAP$, getmap());
if (.@i >= 0)
- EnterTown($@LOCMASTER_LOC$[.@i]);
+ EnterTown($@LOCMASTER_LOC$[.@i], true);
else
consolewarn("[ERROR] Invalid Town Map for Time Flask: %s", getmap());
return;
diff --git a/npc/items/inc_sc_bonus.txt b/npc/items/inc_sc_bonus.txt
index b0680b680..a07a084c6 100644
--- a/npc/items/inc_sc_bonus.txt
+++ b/npc/items/inc_sc_bonus.txt
@@ -4,7 +4,7 @@
// Description:
// Applies effects for INC_* (STR doesn't exist)
// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE
-// Doesn't works: SC_STRUP
+// Doesn't works: SC_STRUP -> SC_STR_SCROLL instead
// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE
/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken)
// PS. SC_CRITICALPERCENT (Crit) SC_FOOD_CRITICALSUCCESSVALUE (Crit) SC_STRIKING (Crit , ATK)
@@ -24,6 +24,13 @@ function script SC_Bonus {
if (.@delay <= 0)
return false;
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOBOOST) {
+ // Semi Hardcoded-way to find buffs
+ if (getmap() == "001-15" && .@min > 0 && array_find($@sc_buffs, .@type))
+ return false;
+ }
+
// Get the bonus value
if (.@min != .@max)
.@bonus=rand2(.@min, .@max);
@@ -62,11 +69,22 @@ function script SC_Bonus {
- script inc_sc_bonus -1,{
OnUse:
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOBOOST) {
+ if (getmap() == "001-15")
+ end;
+ }
+
SC_Bonus(@delay, @type, @min, @max);
@delay=0;
@type=0;
@min=0;
@max=0;
end;
+
+// Document all buffs
+OnInit:
+ setarray $@sc_buffs, SC_ATTHASTE_POTION1, SC_INCHIT, SC_KAIZEL, SC_INCATKRATE, SC_ATTHASTE_POTION1, SC_PLUSATTACKPOWER, SC_WALKSPEED, SC_INCLUK, SC_INCDEX, SC_INCAGI, SC_INCVIT, SC_INCINT, SC_STR_SCROLL, SC_CASH_DEATHPENALTY, SC_INCFLEE, SC_FOOD_CRITICALSUCCESSVALUE, SC_CRITICALPERCENT, SC_INCMSPRATE, SC_INCMHPRATE, SC_INCMSP, SC_INCMHP, SC_STRUP, SC_STRIKING, SC_INCHITRATE, SC_INCATKRATE, SC_INCFLEERATE, SC_INCDEFRATE, SC_ATTHASTE_POTION2, SC_OVERLAPEXPUP;
+ end;
}
diff --git a/npc/items/legacy_heal.txt b/npc/items/legacy_heal.txt
index 2beb820d3..d3d3b4f8a 100644
--- a/npc/items/legacy_heal.txt
+++ b/npc/items/legacy_heal.txt
@@ -20,6 +20,12 @@ function script ItHeal {
.@min=getarg(1, @min);
.@max=getarg(2, (@max ? @max : .@min));
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOHEAL) {
+ if (getmap() == "001-15")
+ return;
+ }
+
if (.@delay <= 0) {
Exception("Invalid legacy healing item, deleting without healing effect.");
end;
diff --git a/npc/items/rand_mp_heal.txt b/npc/items/rand_mp_heal.txt
index 4b6a5ca11..5b9365d82 100644
--- a/npc/items/rand_mp_heal.txt
+++ b/npc/items/rand_mp_heal.txt
@@ -18,6 +18,12 @@ function script MPHeal {
end;
}
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOMANA) {
+ if (getmap() == "001-15")
+ return;
+ }
+
// +1 max MP per 3 Int, +1 min MP per 5 int.
// Original max MP will be respected
@max = min(@max*2, @min+(readparam2(bInt)/5));
diff --git a/npc/items/rand_sc_heal.txt b/npc/items/rand_sc_heal.txt
index 41ea3b3a7..454c20887 100644
--- a/npc/items/rand_sc_heal.txt
+++ b/npc/items/rand_sc_heal.txt
@@ -37,6 +37,12 @@ function script ItHeal2 {
.@rarity=getarg(1);
.@delay=getarg(2, 0);
+ // Moubootaur Showdown Effects
+ if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOHEAL) {
+ if (getmap() == "001-15")
+ return 0;
+ }
+
// Calculate healing value in %
.@min=.@rarity * ((.@type*1) + 1);
.@max=.@rarity * ((.@type*2) + 1);