summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2025-05-16 20:25:18 -0300
committerJesusaves <cpntb1@ymail.com>2025-05-16 20:25:18 -0300
commitab3ba889e818fad7721e1f967f613dcf0cefe9cf (patch)
treee296ed26a7d7caf8678a0fa6250bd745b9807f65
parent7409ca4b0bb6faf85cbe0f21bf02095975e78da9 (diff)
downloadserverdata-ab3ba889e818fad7721e1f967f613dcf0cefe9cf.tar.gz
serverdata-ab3ba889e818fad7721e1f967f613dcf0cefe9cf.tar.bz2
serverdata-ab3ba889e818fad7721e1f967f613dcf0cefe9cf.tar.xz
serverdata-ab3ba889e818fad7721e1f967f613dcf0cefe9cf.zip
Update all files, but remove comments or wips or todos and keep it broken
-rw-r--r--npc/001-15/ctrl.c1363
-rw-r--r--npc/029-9/boss.txt2
-rw-r--r--npc/functions/weather.txt8
-rw-r--r--npc/items/books.txt8
4 files changed, 1186 insertions, 195 deletions
diff --git a/npc/001-15/ctrl.c b/npc/001-15/ctrl.c
index 928a53bdc..8e4da57b5 100644
--- a/npc/001-15/ctrl.c
+++ b/npc/001-15/ctrl.c
@@ -1,11 +1,7 @@
// 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;
@@ -13,6 +9,242 @@
public function DeathHandler;
public function ReviveHandler;
+function _calcDebuff {
+ .@beat=getarg(0, 11);
+ .@all_down=0;
+ .@atk_down=0;
+ .@vulnerab=0;
+ .@imprecis=0;
+ .@dumbhead=0;
+ .@slowpoke=0;
+ .@inspired=0;
+
+ if (!getq(General_Milly)) {
+ .@dumbhead+=5;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of understanding on the world penalizes you."), 1);
+ }
+ if (!YETIKING_WINNER) {
+ .@vulnerab+=5;
+ if (!@ml_penwarn) dispbottom col(l("Not toughned by the Yeti King, you suffer some vulnerability."), 1);
+ }
+ if (!HEROESHOLD_WINNER) {
+ .@imprecis+=5;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of exploration in dungeons causes you difficulty to follow up enemies."), 1);
+ }
+ if (REBIRTH < 5) {
+ .@slowpoke+=5;
+ if (!@ml_penwarn) dispbottom col(l("Your mortal coil is inadequate, causing you slowdowns."), 1);
+ }
+ if (!QUIRINO_WINNER) {
+ .@vulnerab+=5;
+ if (!@ml_penwarn) dispbottom col(l("Not tampered by PvP, you feel your body weaker in this realm."), 1);
+ }
+ if (!MOUBOOTAUR_WINNER) {
+ .@dumbhead+=10;
+ .@atk_down+=10;
+ if (!@ml_penwarn) dispbottom col(l("You don't know enough about Andrei Sakar, giving you a considerable penalty."), 1);
+ }
+ if (!GHQ_WINNER) {
+ .@imprecis+=5;
+ if (!@ml_penwarn) dispbottom col(l("You didn't kill enough monsters to know how to fight effectively."), 1);
+ }
+ if (!EPISODE_WINNER) {
+ .@atk_down+=30;
+ .@all_down+=25;
+ .@vulnerab+=20;
+ if (!@ml_penwarn) dispbottom col(l("You never received Elli's blessing. The sheer aura of this place scares you."), 1);
+ }
+ if (!MK_WINNER) {
+ .@atk_down+=15;
+ if (!@ml_penwarn) dispbottom col(l("Never having faced such disadvantageous odds before, you fail to bring out your full potential."), 1);
+ }
+ if (#BETA_REVIVE) {
+ .@imprecis+=1;
+ }
+ if (!countitem(JesusalvaGrimorium)) {
+ .@dumbhead+=10;
+ if (!@ml_penwarn) dispbottom col(l("You feel your knowledge increasingly amiss, if only you had brought a Grimorium with you."), 1);
+ }
+ if (PETER_REPEAT < 50 &&
+ getq2(HalinarzoQuest_LifeDelight) < 51 &&
+ getq2(FrostiaQuest_JhonH) < 500 &&
+ MERCENARY_DAILYQUEST < 500) {
+ .@imprecis+=5;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of patience makes you feel too impatient to fight adequately."), 1);
+ }
+ if (bitmask_count(getq2(General_EasterEggs)) < 14) {
+ .@all_down += 14 - bitmask_count(getq2(General_EasterEggs));
+ if (!@ml_penwarn) dispbottom col(l("You feel like you didn't saw enough Easter Eggs in this world yet."), 1);
+ }
+ if (getq3(TulimsharQuest_Swezanne) < 20) {
+ .@slowpoke+=1;
+ if (!@ml_penwarn) dispbottom col(l("Without Swezanne's support, you feel slightly heavy."), 1);
+ }
+ if (getq(TulimsharQuest_DarkInvocator) < 7) {
+ .@dumbhead+=2;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of understanding of dark arts gives you a penalty."), 1);
+ }
+ if (getq(HalinarzoQuest_SickWife) < 5) {
+ .@all_down+=1;
+ if (!@ml_penwarn) dispbottom col(l("Never seeing how Elixir of Life is made, you feel weakened."), 1);
+ }
+ // Not solving Injuried Mouboo (either way) will cause atk_down
+ if (getq(HurnscaldQuest_InjuriedMouboo) <= 2) {
+ .@atk_down+=25;
+ if (!@ml_penwarn) dispbottom col(l("Not knowing anything about the Moubootaur's curse, your attack is severely reduced."), 1);
+ }
+ if (!getq(LilitQuest_Access)) {
+ .@slowpoke+=3;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of experience swimming causes you to be unable to cope with heavy air in this place."), 1);
+ }
+ if (getq(LoFQuest_Inspector) < 9) {
+ .@dumbhead+=5;
+ .@imprecis+=1;
+ if (!@ml_penwarn) dispbottom col(l("Your lack of experience in investigations penalizes you."), 1);
+ }
+ if (BARBARA_STATE != 3) {
+ .@imprecis+=1;
+ }
+ if (getq(FortressQuest_SlimeHunter) < 2 &&
+ getq(FortressQuest_RangedHunt) < 2 &&
+ getq(FortressQuest_Over100) < 2) {
+ .@imprecis+=1;
+ .@all_down+=1;
+ if (!@ml_penwarn) dispbottom col(l("You feel that if you had done Commander Cadis' absurd grind, you would be able to fight better."), 1);
+ }
+ if (getequipid(EQI_SHADOW_ACC_L) < 1) {
+ .@vulnerab+=1;
+ .@imprecis+=1;
+ .@slowpoke+=1;
+ if (!@ml_penwarn) dispbottom col(l("You are starving. Maybe you should have cooked something beforehand."), 1);
+ }
+ if (getq(NivalisQuest_Henry) < 2) {
+ .@vulnerab+=3;
+ if (.@atk_down)
+ .@atk_down+=5;
+ if (!@ml_penwarn) dispbottom col(l("You never really dealt with illegal drugs and strong poison, which affects you negatively."), 1);
+ }
+ if (!gethominfo(0)) {
+ .@atk_down+=1;
+ if (!@ml_penwarn) dispbottom col(l("Without a homunculus active, you feel weaker."), 1);
+ } else if (homstatus()) {
+ .@atk_down+=1;
+ if (!@ml_penwarn) dispbottom col(l("Without a homunculus active, you feel weaker."), 1);
+ }
+
+ if (!.wh) {
+ .@all_down+=1;
+ .@slowpoke+=5;
+ if (!@ml_penwarn) dispbottom col(l("You feel demotivated without the World Hero around!"), 1);
+ }
+ if (!.lb) {
+ .@all_down+=10;
+ if (!@ml_penwarn) dispbottom col(l("Your party is not protected by the Mana Source, greatly penalizing the team!"), 1);
+ }
+ if (!.da) {
+ .@atk_down+=20;
+ if (!@ml_penwarn) dispbottom col(l("Without Demure's great war cry, you feel unable to cause as much damage as you normally do!"), 1);
+ }
+ if (!.ty) {
+ .@imprecis+=15;
+ if (!@ml_penwarn) dispbottom col(l("Without Tyranny's guidance, you feel like you can't concentrate well!"), 1);
+ }
+ if (!.rs) {
+ .@dumbhead+=10;
+ if (!@ml_penwarn) dispbottom col(l("Without the Runestaff great feats, you feel your intelligence leaving you!"), 1);
+ }
+ if (!.as) {
+ .@vulnerab+=15;
+ .@slowpoke+=5;
+ if (!@ml_penwarn) dispbottom col(l("Without a solid tanker, your legs start shaking!"), 1);
+ }
+ if (!.bb) {
+ .@slowpoke+=1;
+ if (!@ml_penwarn) dispbottom col(l("Your party wasn't quick enough to grab the weapon the Monster King left behind, and it shows!"), 1);
+ }
+
+ /* These are penalties associated with MAP */
+ .@factor = log2(.bgBonus) * ((1 + $@ML_SHOWDOWN) / 2);
+ if (.bgBonus > 0) {
+ .@inspired += limit(1, .@factor, 50);
+ if (!@ml_penwarn) dispbottom col(l("Allied forces are winning in Eastern Front, inspiring you."), 3);
+ } else if (.bgBonus < 0) {
+ .@vulnerab += limit(1, .@factor, 50);
+ if (!@ml_penwarn) dispbottom col(l("Allied forces are losing in Eastern Front, debuffing you."), 1);
+ }
+
+ @ml_penwarn=true;
+
+ if (!.@beat)
+ return;
+
+ if (.@all_down)
+ SC_Bonus(.@beat, SC_ALL_DOWN, .@all_down);
+ if (.@atk_down)
+ SC_Bonus(.@beat, SC_ATK_DOWN, .@atk_down);
+ if (.@vulnerab)
+ SC_Bonus(.@beat, SC_VULNERABLE, .@vulnerab);
+ if (.@imprecis)
+ SC_Bonus(.@beat, SC_IMPRECISE, .@imprecis);
+ if (.@dumbhead)
+ SC_Bonus(.@beat, SC_DUMBHEAD, .@dumbhead);
+ if (.@slowpoke)
+ SC_Bonus(.@beat, SC_SLOWPOKE, .@slowpoke);
+ if (.@inspired)
+ SC_Bonus(.@beat, SC_INSPIRED, .@inspired);
+ return;
+}
+
+function checkPenalties {
+ .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "001-15");
+ .wh=false; .lb=false; .da=false; .ty=false; .rs=false; .as=false; .bb=false;
+ for (.@i = 0; .@i < .@c; .@i++) {
+ attachrid(.@pcs[.@i]);
+ .@wpn = getequipid(EQI_HAND_R);
+ .@all = readparam2(bStr) + readparam2(bAgi) + readparam2(bVit) + readparam2(bInt) + readparam2(bLuk) + readparam2(bDex);
+ if (.@all > 9000) {
+ warp "001-16", 90, 45;
+ nude();
+ }
+ if (Class == Skelli && .@wpn > 1) {
+ percentheal 0, any(-1, -2, -3, -1);
+ unequip(EQI_HAND_R);
+ dispbottom l("The %s is too hot, or perhaps you are too cold, to use it adequately.", getitemname(.@wpn));
+ nude(); // TODO Check if this works
+ }
+
+ if (ispcdead() || Class == Skelli) {
+ detachrid();
+ continue;
+ }
+ if (strcharinfo(0) == $MOST_HEROIC$)
+ .wh = true;
+ switch (.@wpn) {
+ case Lightbringer: .lb=true; break;
+ case DemureAxe: .da=true; break;
+ case Tyranny: .ty=true; break;
+ case Runestaff: .rs=true; break;
+ case AegisShield: .as=true; break;
+ case Blightbringer: .bb=true; break;
+ }
+ detachrid();
+ }
+ for (.@i = 0; .@i < .@c; .@i++) {
+ attachrid(.@pcs[.@i]);
+ if (ispcdead() || Class == Skelli) {
+ SC_Bonus(60, SC_ELLIBAN, 1);
+ detachrid();
+ continue;
+ }
+ if (getarg(0, -1) >= 0)
+ _calcDebuff( getarg(0, 0) );
+ else
+ _calcDebuff( );
+ detachrid();
+ }
+ return;
+}
+
function _boostMe {
.@mg = getarg(0);
.@bat=getunitdata(.@mg, UDT_ATKMAX);
@@ -32,12 +264,25 @@ function _boostMe {
return;
}
-// Maybe this should be a function
+function _spawnMob {
+ .@mob=areamonster("001-15", getarg(1), getarg(2), getarg(3), getarg(4),
+ getarg(5, strmobinfo(1, getarg(0))), getarg(0), 1);
+
+ .@opt=getunitdata(.@mob, UDT_MODE);
+ .@opt=.@opt|MD_AGGRESSIVE;
+ setunitdata(.@mob, UDT_MODE, .@opt);
+
+ if (getarg(6, false))
+ setunitdata(.@mob, UDT_AI, 1); // 1 = AI_ATTACK = attack/friendly
+
+ return .@mob;
+}
+
+
public function MapMode {
- // Define the planned new map mode
.@new_mode = rand2(10);
- // Revert the old map mode
+ removemapmask "001-15", .mapMode;
switch (.mapMode) {
case ML_MAPMODE_PVP:
pvpoff("001-15"); break;
@@ -45,6 +290,9 @@ public function MapMode {
removemapflag("001-15",mf_nocommand);
removemapflag("001-15",mf_nochat);
break;
+ case ML_MAPMODE_NOMAGIC:
+ setmapflag(.@m$,mf_zone,"FinalMMO");
+ break;
case ML_MAPMODE_NOPETS:
removemapflag("001-15",mf_nopet);
break;
@@ -52,7 +300,9 @@ public function MapMode {
break;
}
- // Set the new map mode
+ if (!getarg(0, true))
+ return 0;
+
switch (.@new_mode) {
case ML_MAPMODE_PVP:
pvpon("001-15"); break;
@@ -60,6 +310,9 @@ public function MapMode {
setmapflag("001-15",mf_nocommand,true);
setmapflag("001-15",mf_nochat,true);
break;
+ case ML_MAPMODE_NOMAGIC:
+ setmapflag(.@m$,mf_zone,"FinalMMO No Tricks");
+ break;
case ML_MAPMODE_NOPETS:
setmapflag("001-15",mf_nopet,true);
break;
@@ -67,19 +320,55 @@ public function MapMode {
break;
}
- // Save the new map mode
.mapMode = .@new_mode;
+ addmapmask "001-15", .@new_mode;
- // TODO: update/set map mask (visual information)
- return;
+ return .mapMode;
}
+
+
+
+
OnDoT:
if (ispcdead()) DeathHandler(true);
percentheal -1, 0;
end;
-// The functions which the Generals call when they die
+OnRev:
+ atcommand("@alive");
+ atcommand("@refresh");
+
+ if (!@ml_skelli) {
+ @ml_skelli=true;
+ sleep2(50);
+ setpcblock(PCBLOCK_HARD, true);
+ mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1;
+ next;
+
+ mesn l("Mr. Saves");
+ mesq l("You have died outside of the world, and as such, the Soul Menhir has no effect. This is a permanent death to you.");
+ next;
+ mesn l("Mr. Saves");
+ mesq l("But I'll make an exception for you: If you go back to where you died, you'll revive and can pick where you left off.");
+ next;
+ mesn l("Mr. Saves");
+ mesq l("If you logout, then you'll need to overcome my maze and touch an obelisk to return to the world.");
+ next;
+ mesn l("Mr. Saves");
+ mesq l("You'll be a weak skeleton on the meanwhile, so be careful. The trial to assess if you're a being worth existing begin now.");
+ mesc l("And now shush, go and try to find your body!");
+
+ next;
+ setpcblock(PCBLOCK_HARD, false);
+ closeclientdialog;
+ }
+ end;
+
+
+
+
+
OnBlue:
if (!playerattached()) {
.@x=72; .@y=41;
@@ -87,7 +376,7 @@ OnBlue:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -98,7 +387,7 @@ OnBrown:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -109,7 +398,7 @@ OnRed:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -120,7 +409,7 @@ OnGreen:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -131,7 +420,7 @@ OnWhite:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -142,7 +431,7 @@ OnGolden:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -153,7 +442,7 @@ OnPurple:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -164,7 +453,7 @@ OnBlack:
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);
+ .@m=areamonster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
_boostMe(.@m, 3);
end;
@@ -172,9 +461,7 @@ OnBlack:
-// Setup first stage
OnFirstStage:
- // Eliminate all existing keys
DelItemFromEveryPlayer(MKeyWater);
DelItemFromEveryPlayer(MKeyEarth);
DelItemFromEveryPlayer(MKeyFire);
@@ -184,66 +471,71 @@ OnFirstStage:
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);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
+ areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
.@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, GunnerUnderling), GunnerUnderling, 1);
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");
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
+ areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
+ .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnBrown");
_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);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
+ areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
.@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, GunnerUnderling), GunnerUnderling, 1);
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);
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
+ areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
.@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, GunnerUnderling), GunnerUnderling, 1);
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
- areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
.@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, GunnerUnderling), GunnerUnderling, 1);
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
+ areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
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, GunnerUnderling), GunnerUnderling, 1);
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
+ areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, WizardUnderling), WizardUnderling, 1);
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, GunnerUnderling), GunnerUnderling, 1);
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, HalberdUnderling), HalberdUnderling, 1);
areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, WizardUnderling), WizardUnderling, 2);
- areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2);
+ areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, BansheeUnderling), BansheeUnderling, 1);
.@gen = monster("001-15", 25, 40, "Guardian of Evil", TopUnderling, 1, "#Moubootaur::OnBlack");
_boostMe(.@gen, 6);
+ sleep(100);
+ for (.@i = 0; .@i < 8; .@i++) {
+ areamonster("001-15", 20, 20, 80, 59, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ sleep(500);
+ }
+
+ unitwalk(.HERO, 50, 53);
+ unittalk(.HERO, b(col("Watch out!", 1)));
+ initnpctimer;
end;
@@ -254,7 +546,6 @@ OnFirstStage:
-// **MOUBOOTAUR HEARTBEAT**
OnTimer25000:
OnTimer20000:
OnTimer15000:
@@ -262,34 +553,435 @@ OnTimer15000:
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
-
+ checkPenalties();
- // 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
+ .@amount = ($@ML_SHOWDOWN >= 2 ? 5 : 2);
+ for (.@i=0; .@i < .@amount; .@i++) {
+ .@m=areamonster("001-15", 49, 30, 60, 42, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1);
+ _boostMe(.@m); // Will boost according to showdown state
+ sleep(rand2(100)); // Give a small interval to avoid instakills
+ }
+
+ if (.battleGrd) {
+ freeloop(true);
+ .bgBonus = 0;
+ .@c=getunits(BL_MOB, .@pcs, false, "001-15", 82, 20, 90, 60);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ .@id = .@pcs[.@i];
+ if (getunitdata(.@id, UDT_AI) == 1)
+ .bgBonus += getunitdata(.@id, UDT_HP);
+ else
+ .bgBonus -= getunitdata(.@id, UDT_HP);
+ }
+
+ .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "001-15");
+ for (.@i = 0; .@i < .@c; .@i++) {
+ attachrid(.@pcs[.@i]);
+ .@ref = _spawnMob(any(FallenGuard1, FallenGuard2, FallenGuard3), 82, 50, 90, 60, "Allied Guard", true);
+ if (strcharinfo(0) == $MOST_HEROIC$)
+ _spawnMob(DustGatling, 82, 50, 90, 60, "Allied Guard", true);
+ if (getcharid(3) == .lb)
+ _spawnMob(DustRifle, 82, 50, 90, 60, "Allied Guard", true);
+ if (getcharid(3) == .as)
+ _spawnMob(DustRevolver, 82, 50, 90, 60, "Allied Guard", true);
+ if (islegendary())
+ _boostMe(.@ref, 0);
+ if ($ML_PREPARADNESS) {
+ $ML_PREPARADNESS -= 1;
+ .@ref = _spawnMob(any(FallenGuard1, FallenGuard2), 82, 50, 90, 60, "Allied Guard", true);
+ _boostMe(.@ref, limit(0, log2($ML_PREPARADNESS), 22));
}
+ detachrid();
+
+ for (.@i=0; .@i <= rand2(3); .@i++) {
+ .@mob = _spawnMob(any(Moubi, BloodyMouboo, Shrewboo, AlphaMouboo, Mouboo, Shrewboo, Mouboo, BloodyMouboo), 82, 20, 90, 26);
+ _boostMe(.@mob);
+ unitwalk(.@mob, 85, 52);
+ }
+ }
+ if (.bgBonus > 0)
+ .@msg$ = ", the battle is in ##BTalpans##b favor!";
+ else if (.bgBonus < 0)
+ .@msg$ = ", the battle is in ##BMonsters##b favor!";
+ mapannounce("001-15", sprintf("Turn %d%s", .turn, .@msg$), 0);
+ freeloop(false);
+ } // .battleGrd = if Eastern BattleGround is active
+ }
+
+ if (!.canAttack) {
+ initnpctimer; end;
+ }
+
+ if (rand2(90) > .mana) {
+ initnpctimer; end;
+ }
+
+ .mana -= 5;
+
+ specialeffect(FX_SPECIAL, AREA, .ML);
+ sleep(500);
+
+ .@bonusAttacks = 1;
+
+ getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .ML);
+
+ .@mvp = 0; // An array of (char, score) for relative_arr_rnd
+ .@rnd = 0; // Random selection
+ .@tkn = 0; .@def = 1; // The Tanker (most DEF)
+ .@dps = 0; .@atk = 1; // The DPS (most ATK)
+ .@mag = 0; .@mtk = 1; // The Mage (most MATK)
+
+ .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, .@m$);
+ for (.@i = 0; .@i < .@c; .@i++) {
+ .@id = .@pcs[.@i];
+
+ if (readparam(Class, .@id) == Skelli || readparam(Hp, .@id) < 1)
+ continue;
+
+ if (!.@rnd || !rand2(.@c))
+ .@rnd=.@id;
+
+ .@tmp = readbattleparam(.@id, UDT_DEF) / 3; // 3 def = 1 point
+ .@tmp += readbattleparam(.@id, UDT_HP) / 1000; // 1kHP = 1 point
+ .@tmp += readbattleparam(.@id, UDT_MDEF) / 10; // 10 mdef = 1 point
+ .@tmp += readbattleparam(.@id, UDT_FLEE) / 10; // 10 evade = 1 point
+ if (.@tmp > .@def) {
+ .@tkn=.@id; .@def=.@tmp;
+ }
+
+ .@tmp = (readbattleparam(.@id, UDT_ATKMIN) + readbattleparam(.@id, UDT_ATKMAX)) / 2;
+ .@tiles = 10 + (readbattleparam(.@id, UDT_ATKRANGE) / 2);
+ .@tmp = .@tmp * .@tiles / 10;
+ .@tmp = (.@tmp/2) * 1000 / readbattleparam(.@id, UDT_ADELAY);
+ .@tmp += readbattleparam(.@id, UDT_CRIT) * 5;
+ .@tmp += readbattleparam(.@id, UDT_HIT) * 4;
+ if (.@tmp > .@atk) {
+ .@dps=.@id; .@atk=.@tmp;
+ }
+
+ .@tmp = (readbattleparam(.@id, UDT_MATKMIN) + readbattleparam(.@id, UDT_MATKMAX)) / 2;
+ .@tmp += readbattleparam(.@id, UDT_CRIT) * 2; // 4 crit = 1 point
+ .@tmp += readbattleparam(.@id, UDT_HIT) * 2; // 4 acc. = 1 point
+ if (.@tmp > .@mtk) {
+ .@mag=.@id; .@mtk=.@tmp;
+ }
+
+ .@tmp = 0;
+ .@tmp = readbattleparam(.@id, UDT_DEF) / 3; // 3 def = 1 point
+ .@tmp += readbattleparam(.@id, UDT_HP) / 1000; // 1kHP = 1 point
+ .@tmp += readbattleparam(.@id, UDT_MDEF) / 10; // 10 mdef = 1 point
+ .@tmp += readbattleparam(.@id, UDT_FLEE) / 10; // 10 evade = 1 point
+ .@tmp += readbattleparam(.@id, UDT_ATKRANGE) * 3; // 1 tile = 3 points
+ .@tmp += (readbattleparam(.@id, UDT_ATKMIN) + readbattleparam(.@id, UDT_ATKMAX)) / 20; // 10 atk = 1 point
+ .@tmp += max(2000-readbattleparam(.@id, UDT_ADELAY), 0) / 50; // 1 point for every 50ms not added to attack speed
+ .@tmp += readbattleparam(.@id, UDT_PDODGE); // 1 block = 1 point
+ .@tmp += readbattleparam(.@id, UDT_CRIT) / 4; // 4 crit = 1 point
+ .@tmp += readbattleparam(.@id, UDT_HIT) / 4; // 4 acc. = 1 point
+ array_push(.@mvp, .@id);
+ array_push(.@mvp, .@tmp);
+ }
+ .@mvp = relative_array_random(.@mvp);
+
+ .@hpc = getunitdata(.ML, UDT_HP) * 100 / getunitdata(.ML, UDT_MAXHP);
+
+ .@lost = .lastHpc - .@hpc;
+
+ // "If the Moubootaur is failing too fast, intervene in his favor" -- Mr. Saves
+ if (.@lost > 3) {
+ specialeffect(FX_HEALINGPART, AREA, .ML);
+ .@hp=getunitdata(.ML, UDT_HP);
+ .@mp=getunitdata(.ML, UDT_MAXHP);
+ .@hp = limit(.@hp, .@hp + (.@mp / 100), .@mp); // Regenerates 1% HP
+ setunitdata(.ML, UDT_HP, min(.@hp, .@mp));
+ sleep(250);
+ }
+ if (.@lost > 2)
+ .@bonusAttacks += .@lost - 2;
+ if (.@lost > 1)
+ .mana += .@lost;
+
+ .lastHpc = .@hpc;
+
+
+ .@presumed_index = abs(12 - (.@hpc / 12));
+ if (.MLindex < .@presumed_index && $@ML_SHOWDOWN < 6) {
+ .MLindex += 1;
+ .@chunks = 100 - (.MLindex * 12);
+ .@presumed_health = abs(getunitdata(.ML, UDT_MAXHP) * .@chunks / 100);
+ setunitdata(.ML, UDT_HP, .@presumed_health);
+ unitwarp(.ML, "001-15", $@MLFX[.MLindex], $@MLFY[.MLindex]);
+ .warpsOnline = false;
+ if (.MLindex == 1) goto OnThirdStage;
+ }
+
+ .@m=monster(.@m$, .@x, .@y, "Mouboo", Moubi, 1);
+ setunitdata(.@m, UDT_RACE, RC_Legendary);
+ set_aggro(.@m);
+ _boostMe(.@m);
+
+ if (!.@mvp || !.@rnd || !.@tkn || !.@dps || !.@mag) {
+ unittalk(.ML, "Sublime Regeration!"); // TODO: Change this to latin
+ .mana += rand2(6); // Moubootaur may recover some mana!
+ .@hp=getunitdata(.ML, UDT_HP);
+ .@mp=getunitdata(.ML, UDT_MAXHP);
+ .@hp = limit(.@hp, .@hp + (.@mp / 20), .@mp); // Regenerates 5% HP
+ setunitdata(.ML, UDT_HP, min(.@hp, .@mp));
+ initnpctimer;
+ end;
+ }
+
+ .@skill = (rand2(25 * .@hpc / 100) + rand2(25)) / 2;
+ .@mob = .ML; .@msg$ = ""; .@lv = 120; .@t = 2+rand2(8)+rand2(15);
+ .@target = any(.@mvp, .@dps, .@tkn); .@sub = any(.@tkn, .@mag, .@rnd);
+ switch (.@skill) {
+ case 1:
+ .@msg$ = sprintf("Witness my sublime rain of death. Regeneration!");
+ .@hp=getunitdata(.@mob, UDT_HP);
+ .@mp=getunitdata(.@mob, UDT_MAXHP);
+ .@hp = limit(.@hp, .@hp + (.@mp / 25), .@mp); // Regenerates 4% HP
+ setunitdata(.@mob, UDT_HP, min(.@hp, .@mp));
+ .@mobid=any(GunnerUnderling, WizardUnderling, BansheeUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ monster(.@m$, .@x, .@y, strmobinfo(1, DeathCat), DeathCat, 1);
+ break;
+ case 2:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Silencing Nuke", 0);
+ squareharm(.@mob, 14, rand2(450, 750), HARM_MISC, Ele_Dark);
+ areasc2("001-15", 40, 35, 20, 15000, SC_SILENCE, BL_PC, 1);
+ break;
+ case 3:
+ .@msg$ = sprintf("%s, I'll show you no mercy! ##BThunder Bolt##b!", strcharinfo(0, "cursed player", .@target));
+ .@PW=125; .@SPW=25; .@RG=1;
+ .@mtk = calcdmg(.@mob, .@target, HARM_MAGI);
+ .@dmg = .@mtk * .@PW / 100;
+ .@dsb = .@mtk * .@SPW / 100;
+ sleep(1000);
+ specialeffect(FX_LIGHTNING, AREA, .@target);
+ squareharm(.@target, .@RG, .@dsb, HARM_MAGI, Ele_Wind, .@mob);
+ harm(.@target, .@dmg, HARM_MAGI, Ele_Holy, .@mob);
+ break;
+ case 4:
+ .@msg$ = sprintf("##BAncient Magic: Obliterate");
+ squareharm(.@mob, 7, rand(12000, 18000), HARM_MAGI, Ele_Dark);
+ harm(.@target, rand(1000, 4000), HARM_MAGI, Ele_Holy, .@mob);
+ break;
+ case 5:
+ .@msg$ = sprintf("I shall ##Bdisable##b you, %s!", strcharinfo(0, "cursed player", .@target));
+ specialeffect(FX_SMOKE, AREA, .ML);
+ attachrid(.@target);
+ SC_Bonus(.@t, SC_SILENCE, 1);
+ SC_Bonus(.@t, SC_BLIND, 1);
+ SC_Bonus(.@t/3, SC_CURSE, 1);
+ detachrid();
+ break;
+ case 6:
+ .@msg$ = sprintf("This battle is over, %s! ##BThunder Neddle##b!", strcharinfo(0, "cursed player", .@target));
+ .@PW=35; .@SPW=5; .@RG=2;
+ .@mtk = calcdmg(.@mob, .@target, HARM_MAGI);
+ .@dmg = .@mtk * .@PW / 100;
+ .@dsb = .@mtk * .@SPW / 100;
+ sleep(1000);
+ specialeffect(FX_LIGHTNING, AREA, .@target);
+ squareharm(.@target, .@RG, .@dsb, HARM_MAGI, Ele_Wind, .@mob);
+ harm(.@target, .@dmg, HARM_MAGI, Ele_Holy, .@mob);
+ break;
+ case 7:
+ .@msg$ = sprintf("##BMagic: Armageddon");
+ squareharm(.@mob, 6, rand2(8300, 9500), HARM_MAGI, Ele_Fire);
+ break;
+ case 8:
+ .@msg$ = sprintf("##BMagic: Gaia Break");
+ sc_start SC_INCDEFRATE, 15000, 15, .@mob;
+ rectharm(.@mob, 2, 5, rand2(8250, 9350), HARM_MAGI, Ele_Earth);
+ break;
+ case 9:
+ .@msg$ = sprintf("##BMagic: Ground Strike");
+ .@EF=any(SC_STUN, SC_BLIND, SC_BLOODING, SC_BLIND, SC_BLOODING);
+ areasc2("001-15", .@x, .@y, 3, 12000, .@EF, BL_PC, 1);
+ squareharm(.@mob, 3, rand2(9250, 10350), HARM_PHYS, Ele_Neutral);
+ break;
+ case 10:
+ .mana += 1;
+ .@msg$ = sprintf("##BMagic: Tempest");
+ squareharm(.@mob, 3, rand2(8200, 9300), HARM_MAGI, Ele_Wind);
+ break;
+ case 11:
+ .@msg$ = sprintf("Chaos shall be my founding stone! Falling star!");
+ attachrid(.@rnd);
+ percentheal -15, -25;
+ detachrid();
+ .@mobid=any(HalberdUnderling, FlyingUnderling, TopUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 12:
+ .@msg$ = sprintf("I demand this world! Tyranny!");
+ attachrid(.@rnd);
+ percentheal -5, -5;
+ SC_Bonus(.@t, any(SC_BLIND, SC_POISON), 1);
+ detachrid();
+ .@mobid=any(HalberdUnderling, FlyingUnderling, TopUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 13:
+ .@msg$ = sprintf("Stop on your tracks, unfair being! Freeze!");
+ attachrid(.@rnd);
+ SC_Bonus((.@t * 2 / 3), any(SC_FREEZE, SC_SLEEP, SC_SLEEP, SC_SLEEP), 1);
+ detachrid();
+ .@mobid=any(HalberdUnderling, WizardUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 14:
+ .@mx=any(80, 80, 90); .@my=60; .@x=20; .@y=20;
+ .@x1=rand2(.@mx, .@x); .@x2=rand2(.@mx, .@x); .@x3=rand2(.@mx, .@x);
+ .@y1=rand2(.@my, .@y); .@y2=rand2(.@my, .@y); .@y3=rand2(.@my, .@y);
+ .@x4=rand2(.@mx, .@x); .@x5=rand2(.@mx, .@x); .@x6=rand2(.@mx, .@x);
+ .@y4=rand2(.@my, .@y); .@y5=rand2(.@my, .@y); .@y6=rand2(.@my, .@y);
+ .@mobId = any(EntAbomination, Dummy, Pumpkandy);
+ .@t1=monster("001-15", .@x1, .@y1, "", .@mobId, 1);
+ .@t2=monster("001-15", .@x2, .@y2, "", .@mobId, 1);
+ .@t3=monster("001-15", .@x3, .@y3, "", .@mobId, 1);
+ .@t4=monster("001-15", .@x4, .@y4, "", .@mobId, 1);
+ .@t5=monster("001-15", .@x5, .@y5, "", .@mobId, 1);
+ .@t6=monster("001-15", .@x6, .@y6, "", .@mobId, 1);
+ specialeffect(67, AREA, .@t1);
+ specialeffect(67, AREA, .@t2);
+ specialeffect(67, AREA, .@t3);
+ specialeffect(67, AREA, .@t4);
+ specialeffect(67, AREA, .@t5);
+ specialeffect(67, AREA, .@t6);
+ immortal(.@t1); immortal(.@t2); immortal(.@t3);
+ immortal(.@t4); immortal(.@t5); immortal(.@t6);
+ sleep(1500); // Saw a pillar? RUN!
+ specialeffect(FX_LIGHTNING, AREA, .@t1);
+ specialeffect(FX_LIGHTNING, AREA, .@t2);
+ specialeffect(FX_LIGHTNING, AREA, .@t3);
+ specialeffect(FX_LIGHTNING, AREA, .@t4);
+ specialeffect(FX_LIGHTNING, AREA, .@t5);
+ specialeffect(FX_LIGHTNING, AREA, .@t6);
+ sleep(500);
+ specialeffect(FX_CRITICAL, AREA, .@t1);
+ specialeffect(FX_CRITICAL, AREA, .@t2);
+ specialeffect(FX_CRITICAL, AREA, .@t3);
+ specialeffect(FX_CRITICAL, AREA, .@t4);
+ specialeffect(FX_CRITICAL, AREA, .@t5);
+ specialeffect(FX_CRITICAL, AREA, .@t6);
+ squareharm(.@t1, 3, 14500, HARM_MISC, Ele_Neutral);
+ squareharm(.@t2, 3, 14500, HARM_MISC, Ele_Neutral);
+ squareharm(.@t3, 3, 14500, HARM_MISC, Ele_Neutral);
+ squareharm(.@t4, 3, 14500, HARM_MISC, Ele_Neutral);
+ squareharm(.@t5, 3, 14500, HARM_MISC, Ele_Neutral);
+ sleep(1000);
+ unitkill(.@t1);
+ unitkill(.@t2);
+ unitkill(.@t3);
+ unitkill(.@t4);
+ unitkill(.@t5);
+ unitkill(.@t6);
+ break;
+ case 15:
+ .@msg$ = sprintf("And then... There was a quake. And all life died. Bleed!");
+ attachrid(.@target);
+ SC_Bonus(.@t, SC_BLOODING, 1);
+ detachrid();
+ .@mobid=any(HalberdUnderling, BansheeUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 16:
+ .@msg$ = sprintf("Puny mortal, do your best to entertain me! Curse!");
+ attachrid(.@target);
+ SC_Bonus(.@t, SC_CURSE, 1);
+ detachrid();
+ .@mobid=any(WizardUnderling, BansheeUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 17:
+ .@msg$ = sprintf("The problem with typos is - unpredictable side effects.");
+ attachrid(.@target);
+ SC_Bonus(.@t, any(SC_SILENCE, SC_CURSE, SC_FREEZE, SC_BLOODING, SC_BLIND, SC_POISON, SC_DPOISON, SC_POISON, SC_BURNING, SC_SLEEP), 1);
+ detachrid();
+ .@mobid=any(WizardUnderling, BansheeUnderling, TopUnderling, GunnerUnderling, FlyingUnderling, HalberdUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 18:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Disarm Homun", 0);
+ areasc2("001-15", .@x, .@y, 20, 30000, SC_STUN, BL_HOM, 1);
+ break;
+ case 19:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Sleep", 0);
+ areasc2("001-15", .@x, .@y, 20, 15000, SC_SLEEP, BL_PC, 1);
+ break;
+ case 20:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Burning", 0);
+ areasc2("001-15", .@x, .@y, 20, 15000, SC_BURNING, BL_PC, 1);
+ break;
+ case 21:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Fear", 0);
+ areasc2("001-15", .@x, .@y, 20, 15000, SC_FEAR, BL_PC, 1);
+ break;
+ case 22:
+ .@msg$ = sprintf("##BAncient Magic: Terminate Homunculi");
+ squareharm(.@mob, 14, rand2(3000, 5000), HARM_MISC, Ele_Dark, .@mob, BL_MER | BL_HOM);
+ specialeffect(FX_LIGHTNING, AREA, .@sub);
+ harm(.@sub, rand2(1000, 2500), HARM_MAGI, Ele_Holy, .@mob);
+ break;
+ case 23:
+ .@msg$ = sprintf("There is no free speech. Censorship!");
+ attachrid(.@target);
+ SC_Bonus(.@t, SC_SILENCE, 1);
+ detachrid();
+ .@mobid=any(BansheeUnderling, GunnerUnderling);
+ monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1);
+ break;
+ case 24:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Lag", 0);
+ areasc2("001-15", .@x, .@y, 20, 15000, SC_CONFUSION, BL_PC, 1);
+ break;
+ case 25:
+ mapannounce("001-15", "Moubootaur : ##BAncient Magic: Wizcat", 0);
+ for (.@i=0;.@i <= 8+rand2(7);.@i++) {
+ .@m=monster("001-15", rand2(30,50), rand2(30,45),
+ "Reinforcement", BlackCat, 1);
+ setunitdata(.@m, UDT_RACE, RC_Legendary);
}
- // TODO: Tally Battlegrounds results
+ break;
+ default:
+ .@msg$ = sprintf("Come forth, my minions! Wreak chaos and havoc!");
+ specialeffect(FX_MGWARP, AREA, .ML); // Maybe 65 would also work
+ sleep(1000);
+ .@max = (getmapusers("001-15")/2) + rand2(5);
+ .@max = limit(2, .@max, 12);
+ for (.@i=0; .@i < .@max; .@i++) {
+ .@mid = any(WizardUnderling, BansheeUnderling, TopUnderling, GunnerUnderling, FlyingUnderling, HalberdUnderling, Tortuga, SacredWisp, EvilWisp, PanthomWisp, EpiphanyWisp, WindElement, EarthElement, WaterElement, FireElement);
+ .@x = monster(.@m$, .@x, .@y, strmobinfo(1, .@mid), .@mid, 1);
+ set_aggro(.@x);
+ }
+ break;
+ }
+ if (.@msg$ != "")
+ unittalk(.@mob, .@msg$);
+
+ for (.@i=0; .@i < .@bonusAttacks; .@i++) {
+ .@target1 = any(.@mvp, .@tkn, .@mvp, .@mvp);
+ .@target2 = any(.@dps, .@mag, .@tkn, .@dps, .@tkn, .@tkn);
+ specialeffect(FX_LIGHTNING, AREA, .@target1);
+ specialeffect(FX_CRITICAL, AREA, .@target1);
+ specialeffect(FX_LIGHTNING, AREA, .@target2);
+ harm(.@target1, rand2(2250, 3500), HARM_MAGI, Ele_Dark, .ML);
+ harm(.@target2, rand2(1250, 2400), HARM_MAGI, Ele_Dark, .ML);
+ if ((.@i + 1) < .@bonusAttacks)
+ sleep(250);
}
initnpctimer;
@@ -305,47 +997,15 @@ OnTimer10000:
-// 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");
@@ -353,7 +1013,7 @@ public function DeathHandler {
}
if (countitem(MKeyEarth)) {
delitem MKeyEarth, 1;
- .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnEarth");
+ .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnBrown");
_boostMe(.@gen, 12);
}
if (countitem(MKeyFire)) {
@@ -387,24 +1047,35 @@ public function DeathHandler {
_boostMe(.@gen, 12);
}
- // Warp you to the respawn area if set (and revives you)
+ charcommand("@storeall 6");
+ MLDIE_Zeny += Zeny;
+ Zeny = 0;
+
+ getmapxy(.@m$, .@x, .@y, 0);
+ if (.@m$ == "001-15") {
+ if (getstrlen(strcharinfo(0)) > 12)
+ .@n$=substr(strcharinfo(0), 0, 12);
+ else
+ .@n$=strcharinfo(0);
+ .@n$=sprintf("%s#ML!%06d", .@n$, getcharid(0));
+ .@bodyId = getnpcid(.@n$);
+ debugmes "%s is ID %d [DeathHandler]", .@n$, .@bodyId;
+ if (.@bodyId < 1) {
+ if (array_find($@ML_DUPES, getcharid(0)) < 0) {
+ npc_duplicate("Dead_Body#ML!000000", .@n$, .@m$, .@x, .@y, (Sex ? NPC_GUARD_DEAD : NPC_INJURIED_GIRL), 0, 0, 0);
+ htput($@_DUPES, .@n$, gettimetick(2) + 900);
+ array_push($@ML_DUPES, getcharid(0));
+ }
+ }
+ }
+
if (getarg(1, true)) {
- debugmes "Now reviving ML victim...";
- atcommand("@alive");
+ addtimer(20, "#Moubootaur::OnRev");
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;
}
@@ -415,45 +1086,46 @@ public function DeathHandler {
-// Undo everything DeathHandler did, and takes same arguments
public function ReviveHandler {
- // TODO: "#Moubootaur"::PermanentDebuffs() if getmap() == 001-15
- // Restore your Class
+ sc_end SC_ELLIBAN;
+ if (Class != Skelli) {
+ Exception(sprintf("Cannot revive player \"%s\" who is not dead!", strcharinfo(0)), RB_PLEASEREPORT|RB_DEBUGMES|RB_SPEECH|RB_IRCBROADCAST);
+ getmapxy(.@m$, .@x, .@y, 0);
+ consolebug("%s (%d, %d)", .@m$, .@x, .@y);
+ return;
+ }
+
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
+ array_remove($@ML_DUPES, getcharid(0));
+ if (getstrlen(strcharinfo(0)) > 12)
+ .@n$=substr(strcharinfo(0), 0, 12);
+ else
+ .@n$=strcharinfo(0);
+ .@n$=sprintf("%s#ML!%06d", .@n$, getcharid(0));
+ .@t = htget($@_DUPES, .name$, 0);
+ if (.@t) {
+ .@t = min(gettimetick(2)+2, .@t);
+ htput($@_DUPES, .name$, .@t);
+ }
- // 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;
@@ -461,18 +1133,26 @@ OnIntroCutscene:
.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.
+ .warpsOnline = true;
+ .canAttack = false;
+ .battleGrd = false;
+ .mapMode = MapMode(false);
+ .lastHpc = 100;
+ .bgBonus = 0;
+ .MLindex = 0;
+ deletearray $@ML_DUPES;
+
+ .HERO=getnpcid("Andrei");
+ unitwarp(.HERO, "001-15", 55, 47);
sleep(1000);
+ unittalk(getnpcid("Tiki"), "Hey, talk to me if you need to acquire anything!");
+ sleep(5000);
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(getnpcid("Tiki"), "Hey, don't I count?! I can sell information and more!");
+ 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.");
@@ -487,46 +1167,217 @@ OnIntroCutscene:
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.");
+ unittalk(.HERO, b("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 */
+ sleep(6000);
+ checkPenalties(22);
+ sleep(3500);
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);
+ sleep(1500);
enablenpc "#MLWA+";
+ sleep(250);
+ unittalk(.HERO, "Also, be careful! The warps work differently for everyone, and they can even point to themselves!");
+ unitwalk(.HERO, 55, 53);
+ sleep(250);
- // And now that everything is ready and done, start the timer and set turn 1
+ .tbet = 0;
.turn = 1;
- initnpctimer;
- sc_end SC_STUN, .HERO;
+ sleep(1500);
goto OnFirstStage;
+OnSecondStage:
+ sleep(1500);
+ unittalk(.HERO, "Wait. Someone is here.");
+ maptimer("001-15", 2000, "shake::OnGM", true);
+ sleep(2500);
+ mapannounce("001-15", "Moubootaur : ##BROAARRRRRRR!", 0);
+ sleep(1500);
+ unittalk(.HERO, "For the record, I cannot intervene in this fight due to my current state!");
+ maptimer("001-15", 5000, "shake::OnGM", true);
+ sleep(5000);
+ mapannounce("001-15", "Moubootaur : ##BTALPANS!!", 0);
+ .ML=monster("001-15", 54, 37, "The Moubootaur", MobMoubootaur, 1);
+ immortal(.ML);
+ sc_start SC_STUN, 99990, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .ML;
+ sleep(3500);
+ unittalk(.ML, "I see... You've all come here... TO DIE!");
+ sleep(5000);
+ if (.wh)
+ unittalk(.HERO, sprintf("%s, say something! You are the world's hero, not me!", $WORLD_HERO$));
+ else
+ unittalk(.HERO, sprintf("Damn, where is %s to make the heroic speech! It's the Moubootaur you're about to face!", $WORLD_HERO$));
+ sleep(8000);
+ unittalk(.ML, "It matters not... The Mana Source holds no special powers in this place...");
+ sleep(5000);
+ unittalk(.ML, "Come and face me, those whom survive may even get the honor of becoming Mouboos after I'm done with you! HAHAHAH!");
+ sleep(6000);
+ enablenpc "#ML_EastWarp";
+ unittalk(.HERO, "Watch out, this is not the only active battle front! The Alliance is now clashing with the Moubootaur's followers to the east!");
+ sleep(6000);
+ unittalk(.ML, "I hope everyone is ready... Because the battle...");
+ sleep(3000);
+ unittalk(.ML, "Begins ##BNOW!##b");
+
+ .canAttack = true;
+ .battleGrd = true;
+ .turn = 0;
+ .tbet = 8;
+ .bgBonus = 0;
+ initnpctimer;
+ setunitdata(.ML, UDT_LEVEL, 300);
+ setunitdata(.ML, UDT_STR, 255);
+ setunitdata(.ML, UDT_AGI, 255);
+ setunitdata(.ML, UDT_VIT, 255);
+ setunitdata(.ML, UDT_INT, 255);
+ setunitdata(.ML, UDT_DEX, 255);
+ setunitdata(.ML, UDT_LUK, 255);
+ setunitdata(.ML, UDT_ADELAY, 1600);
+ setunitdata(.ML, UDT_ATKRANGE, 4);
+ setunitdata(.ML, UDT_MAXHP, 9999999);
+ setunitdata(.ML, UDT_HP, 9999999);
+ setunitdata(.ML, UDT_ATKMIN, 4500);
+ setunitdata(.ML, UDT_ATKMAX, 6500);
+ setunitdata(.ML, UDT_DEF, 750);
+ setunitdata(.ML, UDT_MDEF, 400);
+ setunitdata(.ML, UDT_HIT, 9999);
+ setunitdata(.ML, UDT_FLEE, 600);
+ setunitdata(.ML, UDT_CRIT, 150);
+ .@opt=getunitdata(.ML, UDT_MODE);
+ if (.@opt & MD_LOOTER)
+ .@opt=.@opt^MD_LOOTER;
+ .@opt=.@opt|MD_NOKNOCKBACK;
+ .@opt=.@opt|MD_BOSS;
+ .@opt=.@opt|MD_AGGRESSIVE;
+ .@opt=.@opt|MD_ANGRY;
+ .@opt=.@opt|MD_DETECTOR;
+ .@opt=.@opt|MD_CASTSENSOR_CHASE;
+ .@opt=.@opt|MD_CASTSENSOR_IDLE;
+ .@opt=.@opt|MD_CHANGECHASE;
+ .@opt=.@opt|MD_CHANGETARGET_MELEE;
+ .@opt=.@opt|MD_CHANGETARGET_CHASE;
+ setunitdata(.ML, UDT_MODE, .@opt);
+ end;
+
+OnThirdStage:
+ .warpsOnline = false;
+ .canAttack = false;
+ mapannounce( "001-15", "Moubootaur : Hah, I was sure you would all die in one hit, but it seems you will be able to at least entertain me!", 0);
+ sleep(6500);
+ mapannounce( "001-15", "Moubootaur : But this is strange... You shouldn't have been able to harm me, yet it seems I bleed?", 0);
+ sleep(6500);
+ mapannounce( "001-15", "Moubootaur : Unfortunately for you, I hold total control over Aeros Island... including the warp you used to reach me.", 0);
+ sleep(6500);
+ .canAttack = true;
+ mapannounce( "001-15", "Moubootaur : Yes, that's right! I'll slowly destroy you with long range spells while you're stuck! HAHAHAHAH!", 0);
+ sleep(6500);
+ unittalk(.HERO, "Don't worry, I've been here for months, I know a trick to make the warps work again.");
+ sleep(6500);
+ unittalk(.HERO, "But each platform has its own gimmick, so everytime he changes platform, you'll need to do something different to make the warps work again.");
+ sleep(6500);
+ unittalk(.HERO, "Now, see that statue which just showed up out of thin air? Yes, not from thin air, I did that.");
+ sleep(6500);
+ unittalk(.HERO, "Now, each statue has a challenge, it may be killing something, lockpicking, or even fishing.");
+ sleep(6500);
+ unittalk(.HERO, "Once anyone completes it, the portals get enabled for everyone.");
+ sleep(6500);
+ unittalk(.HERO, b("What are you waiting for? Go talk to the Statue and hunt the Moubootaur down!"));
+ $@ML_SHOWDOWN = 3;
+ initnpctimer;
+ end;
+
+OnFourthStage:
+ .canAttack = false;
+ immortal(.ML);
+ sc_start SC_STUN, 86400000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .ML;
+
+ unittalk(.ML, "Hmm. You talpans are fighting more seriously than I anticipated.");
+ sleep(6500);
+ unittalk(.ML, "Seems like I'll not be allowed to do whatever I want without you being given a chance to resist.");
+ sleep(6500);
+ unittalk(.ML, "Unworthy creatures given the chance to be a challenge to me... 'Tis is truly a joke.");
+ sleep(6500);
+ unittalk(.ML, "At this rate I won't complete the grand spell. But I can do something else.");
+ sleep(6500);
+ unittalk(.ML, "I'll need just a few more minutes to finish it. So I cannot keep you lousy kids entertained any longer.");
+ sleep(6500);
+ unittalk(.ML, "So on the meanwhile, why don't you...");
+ sleep(6500);
+ unittalk(.ML, "...try to stop Aeros from crashing in Tulimshar while I prepare? MUAHAHAH!");
+ sleep(3500);
+ unitwarp(.ML, "001-15", 54, 26);
+ unittalk(.ML, "*poofs*");
+ sleep(3500);
+ unittalk(.HERO, "Well. That's not good. Aeros is massive, this is going to be worse than the Great Fire ever was.");
+ sleep(5500);
+ unittalk(.HERO, "If Aeros falls over Tulimshar... I don't think Talpans can survive.");
+ sleep(4500);
+ unittalk(.HERO, "Well, what are you waiting for? I'm already dead AND retired!");
+ sleep(4500);
+ unittalk(.HERO, "There are two pumps in the plataforms - have someone go there and check them. I don't know. This is WIP. Go to every platform and chant FLOAT FLOAT or whatever. WIP.");
+ sleep(6500);
+ sc_end SC_STUN, .ML;
+ end;
+
+OnFifthStage:
+ unittalk(.ML, "You know, I trusted you.");
+ sleep(4500);
+ unittalk(.ML, "I knew you wouldn't have let Aeros fall. You wouldn't kill everyone just to get rid of me.");
+ sleep(6500);
+ unittalk(.ML, "And this is why I've won. Come, lets have a final match. Don't hold back. Show me your determination!");
+ sleep(6500);
+ .canAttack = true;
+ unitwarp(.ML, "001-15", 54, 29);
+ setunitdata(.ML, UDT_MAXHP, 2500000);
+ setunitdata(.ML, UDT_HP, 2500000);
+ setunitdata(.ML, UDT_ADELAY, 1500);
+ setunitdata(.ML, UDT_MDEF, 450);
+ setunitdata(.ML, UDT_CRIT, 200);
+ setunitdata(.ML, UDT_ATKRANGE, 5);
+ unittalk(.ML, "Don't run! Come and face me if you're serious about stopping me!");
+ $@ML_SHOWDOWN = 6;
+ changemusic "001-15", "Slaying_Shovel.ogg"; // FIXME reset on player move
+ sleep(500);
+ end;
+
+OnEpilogue:
+ stopnpctimer;
+ $@ML_SHOWDOWN = 7;
+ changemusic "001-15", "epilogue.ogg";
+ mapannounce( "001-15", "Moubootaur : Not bad. And thus, the curtains begin to fall to a close, the epilogue of the Talpan race, their last act of defiance against my person.", 0);
+ sleep(8000);
+ mapannounce( "001-15", "Moubootaur : Everything is now in motion. Cherish the few moments you have left, and rejoice knowing you only delayed the inevitable.", 0);
+ sleep(8000);
+ mapannounce( "001-15", "Moubootaur : Because I have finally sealed Elli. While I was sealed, Talpans prospered. Now the tables have turned.", 0);
+ sleep(8000);
+ mapannounce( "001-15", "Moubootaur : Mouboos will rise to their rightful place, and Talpans will only watch their slow decline.", 0);
+ sleep(8000);
+ //show centered text "== Moubootaur Legends =="
+ //show centered text "=== Normal Ending: The Sealed Shrine ==="
+ end;
+
OnInit:
.warpsOnline = true;
+ .canAttack = false;
+ .battleGrd = false;
+ .lastHpc = 100;
.mapMode = 0;
+ .bgBonus = 0;
+ .MLindex = 0;
+
+ .wh=false; .lb=false; .da=false; .ty=false; .rs=false; .as=false; .bb=false;
.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;
@@ -539,9 +1390,11 @@ OnInit:
OnTouch:
if ($@ML_SHOWDOWN < 2) goto L_InitCheck;
slide 54, 29;
+ if ($@ML_SHOWDOWN == 6)
+ changeplayermusic "Slaying_Shovel.ogg";
+ else
+ changeplayermusic "savior_of_humanity.ogg";
- // 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");
@@ -553,7 +1406,6 @@ OnTouch:
end;
L_InitCheck:
- // First way to trigger: All keys in a single player inventory
if (countitem(MKeyWater) == 1 &&
countitem(MKeyEarth) == 1 &&
countitem(MKeyDeath) == 1 &&
@@ -596,21 +1448,15 @@ L_InitOk:
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
+ unittalk(.@hero, "Good job! Chop chop!");
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
+ donpcevent "#Moubootaur::OnSecondStage";
goto OnTouch;
end;
OnInit:
@@ -624,6 +1470,7 @@ OnInit:
OnTouch:
if ($@ML_SHOWDOWN < 2) end;
slide 55, 48;
+ changeplayermusic "around_the_world.ogg";
end;
L_InitCheck:
end;
@@ -637,12 +1484,13 @@ OnInit:
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;
+ if (!getvariableofnpc(.battleGrd, "#Moubootaur"))
+ end;
// Assuming nothing is wrong, then do the warp
slide 85, 57;
+ changeplayermusic "the_fire_dragon.ogg";
end;
// Report the error to the user & console
@@ -660,6 +1508,7 @@ OnInit:
end;
OnTouch:
slide 60, 54;
+ changeplayermusic "around_the_world.ogg";
end;
OnInit:
end;
@@ -668,12 +1517,14 @@ OnInit:
/* Miller system (otherwise, you can't reach the switches & platforms) */
-001-15,51,49,0 script #MLWA+ NPC_FANCY_CIRCLE,0,0,{
+001-15,51,49,0 script #MLWA+ NPC_SUMMONING_CIRC,0,0,{
end;
OnTouch:
- // TODO: Verify if portalling is active in the Showdown
- // Probably via a public function in #Moubootaur
- if (false) {
+ // Rate Limiting
+ if (@nowarp >= gettimetick(2))
+ end;
+
+ if (!getvariableofnpc(.warpsOnline, "#Moubootaur")) {
dispbottom l("Oh no! The Moubootaur has disabled this portal!");
end;
}
@@ -682,24 +1533,30 @@ OnTouch:
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)) {
+ switch (miller_rand(@state, getcharid(0), 17)) {
case 1:
+ case 9:
deltimer("#DungeonCore::OnClocked"); break;
case 2:
+ case 10:
deltimer("#DungeonCore::OnWindy"); break;
case 3:
+ case 11:
deltimer("#DungeonCore::OnHeat"); break;
case 4:
+ case 12:
deltimer("#DungeonCore::OnLeech"); break;
case 5:
+ case 13:
deltimer("#DungeonCore::OnFrost"); break;
case 6:
+ case 14:
deltimer("#DungeonCore::OnBleed"); break;
case 7:
+ case 15:
deltimer("#DungeonCore::OnSick"); break;
case 8:
+ case 16:
deltimer("#DungeonCore::OnCurse"); break;
default:
// Case 0 has no special effects whatsoever
@@ -711,26 +1568,64 @@ OnTouch:
@state += 3;
else
@state -= 2;
- .@index = miller_rand(@state, getcharid(0), 9);
- slide $@MLFX[.@index], $@MLFY[.@index];
+ .@index = miller_rand(@state, getcharid(0), 17);
+ sleep2(15);
+ warp "001-15", $@MLFX[.@index], $@MLFY[.@index];
+ switch (.@index) {
+ case 1:
+ case 9:
+ changeplayermusic "last_minute.ogg"; break;
+ case 2:
+ case 10:
+ changeplayermusic "adonthell.ogg"; break;
+ case 3:
+ case 11:
+ changeplayermusic "Arabesque.ogg"; break;
+ case 4:
+ case 12:
+ changeplayermusic "valkyries.ogg"; break;
+ case 5:
+ case 13:
+ changeplayermusic "misuse.ogg"; break;
+ case 6:
+ case 14:
+ changeplayermusic "forest-sprint.ogg"; break;
+ case 7:
+ case 15:
+ changeplayermusic "broken_kingdom.ogg"; break;
+ case 8: // Curse
+ case 16:
+ changeplayermusic "let_the_battles_begin.ogg"; break;
+ default:
+ changeplayermusic "around_the_world.ogg"; break;
+ }
+ @nowarp=gettimetick(2)+1;
// Dungeon Climate System (start effect, stop previous)
switch (.@index) {
case 1:
+ case 9:
addtimer(10, "#DungeonCore::OnClocked"); break;
case 2:
+ case 10:
addtimer(10, "#DungeonCore::OnWindy"); break;
case 3:
+ case 11:
addtimer(10, "#DungeonCore::OnHeat"); break;
case 4:
+ case 12:
addtimer(10, "#DungeonCore::OnLeech"); break;
case 5:
+ case 13:
addtimer(10, "#DungeonCore::OnFrost"); break;
case 6:
+ case 14:
addtimer(10, "#DungeonCore::OnBleed"); break;
case 7:
+ case 15:
addtimer(10, "#DungeonCore::OnSick"); break;
case 8:
+ case 16:
addtimer(10, "#DungeonCore::OnCurse"); break;
default:
// Case 0 has no special effects whatsoever
@@ -738,25 +1633,22 @@ OnTouch:
}
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
+001-15,36,38,0 duplicate(#MLWA+) #MLWC+ NPC_SUMMONING_CIRC,0,0
+001-15,44,24,0 duplicate(#MLWA+) #MLWC- NPC_SUMMONING_CIRC,0,0
+001-15,69,53,0 duplicate(#MLWA+) #MLWD+ NPC_SUMMONING_CIRC,0,0
+001-15,78,55,0 duplicate(#MLWA+) #MLWD- NPC_SUMMONING_CIRC,0,0
+001-15,75,38,0 duplicate(#MLWA+) #MLWE+ NPC_SUMMONING_CIRC,0,0
+001-15,63,44,0 duplicate(#MLWA+) #MLWE- NPC_SUMMONING_CIRC,0,0
+001-15,21,24,0 duplicate(#MLWA+) #MLWF+ NPC_SUMMONING_CIRC,0,0
+001-15,31,22,0 duplicate(#MLWA+) #MLWF- NPC_SUMMONING_CIRC,0,0
+001-15,31,59,0 duplicate(#MLWA+) #MLWG+ NPC_SUMMONING_CIRC,0,0
+001-15,21,51,0 duplicate(#MLWA+) #MLWG- NPC_SUMMONING_CIRC,0,0
+001-15,22,43,0 duplicate(#MLWA+) #MLWH+ NPC_SUMMONING_CIRC,0,0
+001-15,28,34,0 duplicate(#MLWA+) #MLWH- NPC_SUMMONING_CIRC,0,0
+001-15,65,26,0 duplicate(#MLWA+) #MLWI+ NPC_SUMMONING_CIRC,0,0
+001-15,75,31,0 duplicate(#MLWA+) #MLWI- NPC_SUMMONING_CIRC,0,0
@@ -813,6 +1705,8 @@ OnInit:
// Convenience Shop NPC
// TODO: enablenpc; and disablenpc; depending on state? Move to (58,50)?
001-15,50,47,0 script Tiki NPC_TIKI,{
+ public function tikiInfo;
+
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!");
@@ -843,8 +1737,9 @@ OnInit:
l("I don't need anything!"),
l("Withdraw Money"),
rif($@ML_SHOWDOWN < 2, l("Open Storage")),
- l("Nursery services"),
- l("Acquire goods");
+ rif($@ML_SHOWDOWN < 6, l("Nursery services")),
+ rif($@ML_SHOWDOWN < 4, l("Acquire goods")),
+ l("Help, I'm lost and I want to pay for information!");
switch (@menu) {
case 2:
Banking();
@@ -859,12 +1754,60 @@ OnInit:
closeclientdialog;
openshop(.name$);
break;
+ case 6:
+ tikiInfo();
+ break;
default:
Zeny += .@price;
break;
}
close;
+// This is also used by the Grimorium (and for free, even)
+// Theoretically, "default" can be triggered by Grimorium (and it IS a bug)
+public function tikiInfo {
+ mesc l("If you die in this map, you'll become a Skeleton unable to fight back unless you find your corpse.");
+ dnext;
+ mesc l(" In rare cases though, you might end up in a maze. The Obelisks there will be able to revive you, there's no escapatory otherwise.");
+ dnext;
+ switch ($@ML_SHOWDOWN) {
+ case 1:
+ mesc l("You should find a way to activate the warp portal to the north.");
+ mesc l("Most likely, finding key items in the platforms and dropping them in the portal all at once will work?");
+ break;
+ case 2:
+ mesc l("Try using the warp portal to the north. But be prepared for a fight.");
+ mesc l("Alternatively, head east to aid the Alliance and those whom die in this map.");
+ mesc l("From this point onwards, I no longer offer storage services."), 1;
+ break;
+ case 3:
+ mesc l("Keep pursuing the Moubootaur through the platforms.");
+ mesc l("Alternatively, head east to aid the Alliance and those whom die in this map.");
+ break;
+ case 4:
+ mesc l("The Moubootaur used a distraction. He must be plotting something now that he saw he can lose.");
+ mesc l("Solve the distraction.");
+ mesc l("Alternatively, head east to aid the Alliance and those whom die in this map.");
+ mesc l("From this point onwards, I no longer offer shop services."), 1;
+ break;
+ case 5: // FIXME
+ mesc l("I don't know. It might be a bug.");
+ mesc l("Alternatively, head east to aid the Alliance and those whom die in this map.");
+ break;
+ case 6: // WIP
+ mesc l("All bets are off! Do your best to defeat the Moubootaur!");
+ mesc l("From this point onwards, I no longer offer nurse services."), 1;
+ break;
+ case 7: // WIP
+ mesc l("Enjoy the Epilogue."), 1;
+ break;
+ default:
+ mesc l("I don't know. It might be a bug.");
+ break;
+ }
+ return;
+}
+
// x2( Item , Multiplier=2x )
function x2 {
return getiteminfo(getarg(0), ITEMINFO_BUYPRICE) * getarg(1, 2);
@@ -958,3 +1901,35 @@ function script ML_MobKill {
}
+
+001-15,0,0,0 script Dead_Body#ML!000000 NPC_HIDDEN,0,0,{
+ end;
+OnTouch:
+ if (!.dead) end;
+ explode(.@parts$, strnpcinfo(0), "!");
+ .@gid=atoi(.@parts$[1]);
+ if (getcharid(0) != .@gid) end;
+ "#Moubootaur"::ReviveHandler(false);
+ .dead = false;
+ unitwarp(getnpcid(), "001-15", 1, 1);
+ array_remove($@ML_DUPES, getcharid(0));
+ .@t = htget($@_DUPES, .name$, 0);
+ if (.@t) {
+ .@t = min(gettimetick(2)+2, .@t);
+ htput($@_DUPES, .name$, .@t);
+ }
+ end;
+
+OnInit:
+ .dead = true;
+ end;
+}
+
+001-15,1,1,0 script Andrei NPC_ML_ANDREI,{
+ npctalk3 col(l("Talk to Tiki if you need help or information."), 9);
+ end;
+OnInit:
+ npcsit;
+ end;
+}
+
diff --git a/npc/029-9/boss.txt b/npc/029-9/boss.txt
index b53ee6a64..f7ed54908 100644
--- a/npc/029-9/boss.txt
+++ b/npc/029-9/boss.txt
@@ -223,7 +223,7 @@ OnProlEnd:
sleep(3200); // Dramatic Silence
mapannounce("029-9", "##1##BGRRRRRRRRR!!!!", 0);
sleep(800);
- mapannounce("029-9", "##1##BHUMANS!!!!", 0);
+ mapannounce("029-9", "##1##BTALPANS!!!!", 0);
sleep(2000);
unittalk(.Support2, "Sister, we should go.");
sleep(3000);
diff --git a/npc/functions/weather.txt b/npc/functions/weather.txt
index 8c2683c0d..e6de82ddf 100644
--- a/npc/functions/weather.txt
+++ b/npc/functions/weather.txt
@@ -122,6 +122,14 @@ OnMinute45:
// Update the online average activity
.@idx = (gettime(GETTIME_HOUR) * 4) + (gettime(GETTIME_MINUTE) / 15);
$@PLAYER_ACTIVITY[.@idx] = getusers(1);
+ if (!$BEAT10 && GETUSERSAVG() >= 10) {
+ $BEAT10 = gettimetick(2);
+ announce("##2Frostia Townhall is now open!", bc_all);
+ $@EXP_EVENT=10;
+ $@EXP_EVENT_TIME=10;
+ donpcevent "@exprate::OnPlayerCall";
+
+ }
// There is no weather in test servers
if (debug && !$@GM_OVERRIDE)
end;
diff --git a/npc/items/books.txt b/npc/items/books.txt
index 3100c3e52..e8afd4beb 100644
--- a/npc/items/books.txt
+++ b/npc/items/books.txt
@@ -514,6 +514,7 @@ function myself {
.@fort = FORT_1ST_VISIT;
.@seal = MOUBOOTAUR_WINNER;
.@king = MK_WINNER;
+ .@game = FINAL_WINNER;
detachrid();
attachrid(.@why);
mes ".:: " + l("Personal Feats") + " ::.";
@@ -537,6 +538,8 @@ function myself {
mesc l("Defeated the Moubootaur (Sealed) %s ago", FuzzyTime(.@seal));
if (.@king)
mesc l("Defeated the Monster King %s ago", FuzzyTime(.@king));
+ if (.@game)
+ mesc l("Defeated the Moubootaur %s ago", FuzzyTime(.@game));
/* Heroic Data */
detachrid();
@@ -644,6 +647,7 @@ function read_book {
rif2(15, getq(LoFQuest_Barbara) >= 5, l("Ch 9 — The World's Heart")),
rif2(16, $GAME_STORYLINE >= 5 && getq(General_Narrator) >= 23, l("Ch 10 — The Moubootaur vs Mana Source")),
rif2(17, getq(General_Narrator) >= 23, l("Ch 11 — The Bloody War")),
+ rif2(18, getmap() == "001-15", l("Ch 12 — Moubootaur's Showdown")),
l("Close");
mes "";
@@ -858,6 +862,10 @@ function read_book {
mesc l("WARNING, Remember Me! Cassia was dispatched to guard the location where the showdown will happen, but player is never led to her!"), 1;
next;
break;
+ case 18:
+ "Tiki"::tikiInfo();
+ next;
+ break;
default:
close;
}