summaryrefslogtreecommitdiff
path: root/npc/001-15/ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'npc/001-15/ctrl.c')
-rw-r--r--npc/001-15/ctrl.c960
1 files changed, 960 insertions, 0 deletions
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;
+}
+
+