// TMW2 scripts. // Author: // Jesusalva // Description: // The Impregnable Fortress Control Files - Boss Chamber - Final Showdown 026-7,39,34,0 script Impregnable#B7F NPC_HIDDEN,{ function _moveNpc; end; OnInit: .MK=0; .maxhp = 1000000; // 1,000,000 HP (used by scripts) .mana = 0; // More mana = more likely to cast skills .immortal = true; // Set to FALSE when all four guardians are defeated .memohp = 999; // Memorand HP, controls spawns .mou = 0; // Temporary for Cutscene .start_time = gettimetick(2); // Controls Death Touch .dmhp1 = 750000; // The "default" max HP .dmhp2 = 250000; // The "extra" max HP .reward = 0; // Reward modifier ($@MK_CHALLENGE) end; // Maybe not OnTouch, but OnSit? OnTouch: if (strcharinfo(2) == "Monster King") end; slide 39, 35; percentheal -15,0; dispbottom l("The throne is cursed, only the Monster King may sit on it."); end; function _moveNpc { // Try to warp randomly, up to 30 attempts .@e=0; .@mx=getmapinfo(MAPINFO_SIZE_X, getarg(0))-20; .@my=getmapinfo(MAPINFO_SIZE_Y, getarg(0))-20; do { if (.@e >= 30) break; .npc_x = rand2(20, .@mx); .npc_y = rand2(20, .@my); .@e+=1; } while (!checknpccell(getarg(0), .npc_x, .npc_y, cell_chkpass)); return; } // _moveNpc 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 = $@MK_CHALLENGE + 2; // The booster from selected difficulty .@s=.@bf+limit(0, (TOP3AVERAGELVL()-100 / 5), 20); // Over-100 scaling .@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; } // Controls the Event // TODO: Intro Cutscene OnBegin: // Monster King is somewhere else, so nothing happens if ($@MK_SCENE != MK_NONE) end; // It is a false positive? Something gone wrong? if ($@MK_CHALLENGE < 1) end; // Lock the MK in the showdown $@MK_SCENE=MK_SHOWDOWN; .start_time = gettimetick(2); // Controls Death Touch // Recalculate Max HP .maxhp = .dmhp1 + (.dmhp2 * $@MK_CHALLENGE); // Initial assortment of monsters //siege_cast("026-7", .name$, 15, TP_TULIM|TP_HURNS|TP_NIVAL); .@am = ($@MK_CHALLENGE + 1) / 2; areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", EvilWisp, .@am); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", EpiphanyWisp, .@am); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", SacredWisp, .@am); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", PanthomWisp, .@am); // Spawn the boss himself .MK=monster("026-7", 39, 34, "The Monster King", MonsterKing, 1); immortal(.MK); // Immortal until conditions are met .immortal = true; // Mana is mostly carried over between attempts, but no negatives .mana = max(.mana, 0); .mana += $@MK_CHALLENGE; // Difficulty mode starting MP booster .memohp = 999; // Reset Memorand HP // Give MK some extra defense setunitdata(.MK, UDT_DEF, getunitdata(.MK, UDT_DEF) * 4 / 3); // +30% setunitdata(.MK, UDT_MDEF, getunitdata(.MK, UDT_DEF) * 5 / 4); // +25% setunitdata(.MK, UDT_LUK, getunitdata(.MK, UDT_LUK) * 2); // +100% // Reconfigure the AI .@opt=getunitdata(.MK, UDT_MODE); // Add knockback immunity .@opt=.@opt|MD_NOKNOCKBACK; // Mark as boss .@opt=.@opt|MD_BOSS; // Mark as aggressive .@opt=.@opt|MD_AGGRESSIVE; .@opt=.@opt|MD_ANGRY; // Make it more op .@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(.MK, UDT_MODE, .@opt); setunitdata(.MK, UDT_RACE, RC_Legendary); // If the number of players is below the expected, BUFF the damage // +20% max damage every missing player if (getmapusers("026-7") < 5) { .@bat=getunitdata(.MK, UDT_ATKMAX); .@bat += .@bat / 5 * (5-getmapusers("026-7")); setunitdata(.MK, UDT_ATKMAX, .@bat); .@bat=getunitdata(.MK, UDT_ATKMIN); .@bat += .@bat / 5 * (5-getmapusers("026-7")); setunitdata(.MK, UDT_ATKMIN, .@bat); } // Some restrictions are based on the players .@crc=5; .@leg=5; .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "026-7"); for (.@i = 0; .@i < .@c; .@i++) { if (readparam(BaseLevel, .@pcs[.@i]) >= 109) .@crc -= 1; if (islegendary(strcharinfo(0, "", .@pcs[.@i]))) .@leg -= 1; // For every player, increase MK's starting mana .mana += 1; } // If the level of the players is below the expected, BUFF the criticals if (.@crc > 0) { .@bcr=getunitdata(.MK, UDT_CRIT); .@bcr += .@bcr / 5 * .@crc; setunitdata(.MK, UDT_CRIT, .@bcr); } // If the legendary players is below the expected, BUFF the speed and range if (.@leg > 0) { .@bs1=getunitdata(.MK, UDT_SPEED); .@bs1 -= .@leg * 10; .@bs2=getunitdata(.MK, UDT_ATKRANGE); .@bs1 += .@leg; setunitdata(.MK, UDT_SPEED, .@bs1); setunitdata(.MK, UDT_ATKRANGE, .@bs2); .mana += .@leg; } // The Four Generals which command his immortality .GUARD1=monster("026-7", 35, 30, "Air General", MonsterGeneral, 1); .GUARD4=monster("026-7", 44, 45, "Fire General", MonsterGeneral, 1); .GUARD3=monster("026-7", 35, 45, "Earth General", MonsterGeneral, 1); .GUARD2=monster("026-7", 44, 30, "Darkness General", MonsterGeneral, 1); // Boost these four generals or they'll become cheese too fast // Also, fix their elementals, but they have no elemental skills to speak of. setunitdata(.GUARD1, UDT_HP, 90000); setunitdata(.GUARD1, UDT_MAXHP, 90000); setunitdata(.GUARD2, UDT_HP, 90000); setunitdata(.GUARD2, UDT_MAXHP, 90000); setunitdata(.GUARD3, UDT_HP, 90000); setunitdata(.GUARD3, UDT_MAXHP, 90000); setunitdata(.GUARD4, UDT_HP, 90000); setunitdata(.GUARD4, UDT_MAXHP, 90000); setunitdata(.GUARD1, UDT_ELETYPE, Ele_Wind); setunitdata(.GUARD2, UDT_ELETYPE, Ele_Dark); setunitdata(.GUARD4, UDT_ELETYPE, Ele_Fire); setunitdata(.GUARD3, UDT_ELETYPE, Ele_Earth); setunitdata(.GUARD1, UDT_RACE, RC_Legendary); setunitdata(.GUARD2, UDT_RACE, RC_Legendary); setunitdata(.GUARD3, UDT_RACE, RC_Legendary); setunitdata(.GUARD4, UDT_RACE, RC_Legendary); // Use the wrapper to make all the guards minimally decent // (or at very least, as strong as during town sieges) _boostMe(.GUARD1); _boostMe(.GUARD2); _boostMe(.GUARD3); _boostMe(.GUARD4); // The remainder of MK army (Centered at the generals / 20 total) .@x = 35; .@y = 30; areamonster("026-7", .@x-3, .@y-3, .@x+3, .@y+3, "Air Officer", Reaper, 5); // Air Troops .@x = 44; .@y = 45; areamonster("026-7", .@x-3, .@y-3, .@x+3, .@y+3, "Fire Officer", LavaSkullSlime, 6); // Fire Troops .@x = 35; .@y = 45; areamonster("026-7", .@x-3, .@y-3, .@x+3, .@y+3, "Earth Officer", Snail, 5); // Earth Troops .@x = 44; .@y = 30; areamonster("026-7", .@x-3, .@y-3, .@x+3, .@y+3, "Darkness Officer", NightmareDragon, 5); // Darkness Troops // Monster King Personal Bodyguard is special (4 total) for (.@i = 0; .@i < 4; ++.@i) { areamonster("026-7", 37, 32, 41, 36, "Imperial Officer", any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie, BlackSkullSlime, PinkieSuseran), 1); } // General ranged troops / Artillery (20 total) areamonster("026-7", 30, 30, 45, 45, "Ranged Support", RobinBandit, 12); areamonster("026-7", 30, 30, 45, 45, "Ranged Support", DustRevolver, 5); areamonster("026-7", 30, 30, 45, 45, "Ranged Support", DustRifle, 3); // Some "cutscene" most likely no one will see, taking half second sleep(150); unittalk(.MK, "You dare to challenge me?!"); sleep(350); unittalk(.MK, "You noobs, you all deserve to die!"); initnpctimer; end; OnMFDispose: if (ispcdead() || getq(General_Fortress) < 6) warp("025-2", 100, 27); // Summons, for a whole minute, an allied solider (0.1% per siege won) if (rand2(1000) < $MK_TEMPVAR) summon("Allied Guard", any(FallenGuard1, FallenGuard2, FallenGuard3)); end; // Fail-safe Mechanism (will never happen) OnTimer60000: consolebug("Warning! final fail-safe mechanism triggered to Monster King."); initnpctimer; end; // Run every 10 seconds OnTimer25000: OnTimer15000: consolewarn("Warning, fail-safe mechanism triggered to Monster King."); OnTimer10000: maptimer2("026-7", 10, "Impregnable#B7F::OnMFDispose"); .mana += (.immortal ? 1 : 2); // Recover mana // Bonus MP regeneration from difficulty mode selection if ($@MK_CHALLENGE > 1) .mana += $@MK_CHALLENGE-1; /* Regeneration & Defeat Loop */ .@end = false; if (.immortal) setunitdata(.MK, UDT_HP, INT_MAX); if (!getmapusers("026-7")) { if (.immortal || getunittype(.MK) < 0) { .@end = true; } else { .@hp = getunitdata(.MK, UDT_HP); .@mh = .maxhp; .@hp = max(.@mh, .@hp + (.@mh / 500)); // Regenerates 0.2% HP // Regeneration setunitdata(.MK, UDT_HP, .@hp); // Fully healed, players lost if (.@hp >= .@mh) .@end = true; } } /* Maybe the fight is over */ if (!mobcount("026-7", "all") || getunittype(.MK) < 0 || .@end) { stopnpctimer; .reward = max(1, $@MK_CHALLENGE); $@MK_SCENE = MK_NONE; $@MK_CHALLENGE=false; killmonsterall("026-7"); enablenpc .name$; if (!.@end) { // This is not due to full health! We actually won! kamibroadcast("The MONSTER KING has been DEFEATED!", b("WORLD HEART")); $@MK_COOLDOWN = gettimetick(2) + 7200; if ($GAME_STORYLINE != 5) goto L_NextAct; // Will not be cast if L_NextAct is summoned maptimer2("026-7", 10, "Impregnable#B7F::OnVictory"); goto OnPrepBlight; } else { // We actually lost?! kamibroadcast("The MONSTER KING has WON the showdown!", b("WORLD HEART")); $@MK_COOLDOWN = gettimetick(2) + 1800; // Apply some kind of penalty, like lowering EXP rate in 5% for a hour // (NLib was 50% for 24 hours...) if ($GAME_STORYLINE >= 5) { $@EXP_EVENT=-5; $@EXP_EVENT_TIME=1; donpcevent "@exprate::OnPlayerCall"; } } end; } /* Prepare some combat data */ getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .MK); .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, .@m$); .@mvp=0;.@rnd=0;.@def=-1; for (.@i = 0; .@i < .@c; .@i++) { if (!.@rnd || !rand2(.@c)) .@rnd=.@pcs[.@i]; if (readbattleparam(.@pcs[.@i], UDT_DEF) > .@def) { if (readparam(Hp, .@pcs[.@i]) < 1) continue; .@mvp=.@pcs[.@i]; .@def=readbattleparam(.@pcs[.@i], UDT_DEF); } } /* Everyone is dead, get rid of their corpses and loop over */ if (!.@mvp || !.@rnd) { mapwarp("026-7", "025-2", 100, 27); initnpctimer; end; } /* Maybe he lost his immortality */ if (.immortal) { if (getunittype(.GUARD1) < 0 && getunittype(.GUARD2) < 0 && getunittype(.GUARD3) < 0 && getunittype(.GUARD4) < 0) { // Secondary throttle (1m), see code copy-pasta below if (.start_time < gettimetick(2) + 60) { unittalk(.MK, "Death touch!"); maptimer2("026-7", 10, "Impregnable#B7F::OnDeathTouch"); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", EvilWisp, 1); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", EpiphanyWisp, 1); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", SacredWisp, 1); areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", PanthomWisp, 1); .@t=areamonster("026-7", 30, 27, 50, 50, "Heart's Curse", Tortuga, 1); set_aggro(.@t); } // Remove the immortality and reset Death Touch timer .immortal = false; kamibroadcast("The Monster King has lost his immortality.", "Monster King"); setunitdata(.MK, UDT_HP, .maxhp); setunitdata(.MK, UDT_MAXHP, .maxhp); .mana += 25; .start_time = gettimetick(2); } } /* Spawn every 20% HP lost, using siege logic 'cause lazy */ if (!.immortal) { .@hp = getunitdata(.MK, UDT_HP) * 5 / .maxhp; if (.@hp < .memohp) { .memohp = .@hp; .mana += 25; siege_cast("026-7", .name$, 35, TP_TULIM|TP_HURNS|TP_NIVAL); if (any(true,false,false)) unitwarp(.MK, "026-7", rand2(38, 40), rand2(35, 37)); else unitwarp(.MK, "026-7", rand2(35, 44), rand2(40, 45)); } // If Monster King is taking too much damage and too fast, throttle players // Aegis Shield, however, provides immunity to this if (.@hp <= 2 && .start_time < gettimetick(2) + 180) { unittalk(.MK, "Death touch!"); maptimer2("026-7", 10, "Impregnable#B7F::OnDeathTouch"); .mana += 5; // Replenish the skill mana cost // The next touch is in a minute, but in a Judgement Day, every 10s .start_time = gettimetick(2) + 180 + 64 - ($@MK_CHALLENGE * 4); monster("026-7", 39, 34, "Summoned Reaper", Reaper, 1); } } /* Decide whenever the Monster King will use a skill. */ // Each Mana point is worth 1% chance. He gains 6 points per minute. (12 later) // (While immortal, each mana point is worth 0.5% chance) if (rand2(.immortal ? 200 : 100) <= .mana) { .mana -= 5; // Mana cost for skill use. It can go negative // Give players 500ms to prepare specialeffect(FX_SPECIAL, AREA, .MK); sleep(500); // Initialization .@skill = rand2(25); .@mob = .MK; .@msg$ = ""; .@lv = 120; .@t = rand2(15); // Skills taken from Blanc, Terogan and Moubootaur (Sealed) 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=(rand2(.@lv) > 50 ? DeathCat : GreenSkullSlime); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 2: .@msg$ = sprintf("Chaos shall be my founding stone! Falling star!"); attachrid(.@rnd); percentheal -5, -10; detachrid(); .@mobid=(rand2(.@lv) > 50 ? BlackSkullSlime : RedSkullSlime); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 3: .@msg$ = sprintf("I demand this world! Tyranny!"); attachrid(.@rnd); percentheal -1, -1; SC_Bonus(.@t, any(SC_BLIND, SC_POISON), 1); detachrid(); .@mobid=(rand2(.@lv) > 50 ? RedFollowerF : HoodedNinja); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 4: .@msg$ = sprintf("Stop on your tracks, unfair being! Freeze!"); attachrid(.@rnd); SC_Bonus((.@t / 2), any(SC_FREEZE, SC_SLEEP, SC_SLEEP, SC_SLEEP), 1); detachrid(); .@mobid=(rand2(.@lv) > 50 ? LavaSkullSlime : AzulSkullSlime); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 5: .@msg$ = sprintf("There is no free speech. Censorship!"); attachrid(.@rnd); SC_Bonus(.@t, SC_SILENCE, 1); detachrid(); .@mobid=(rand2(.@lv) > 50 ? RedFollower : RedSlimeMother); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 6: .@msg$ = sprintf("And then... There was a quake. And all life died. Bleed!"); attachrid(.@rnd); SC_Bonus(.@t, SC_BLOODING, 1); detachrid(); .@mobid=(rand2(.@lv) > 50 ? BlackSkullSlime : BlackMamba); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 7: .@msg$ = sprintf("Puny mortal, do your best to entertain me! Curse!"); attachrid(.@rnd); SC_Bonus(.@t, SC_CURSE, 1); detachrid(); .@mobid=(rand2(.@lv) > 50 ? Reaper : ArmoredSkeleton); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 8: .@msg$ = sprintf("The problem with typos is - unpredictable side effects."); attachrid(.@rnd); 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=(rand2(.@lv) > 50 ? ShadowTortuga : SuperiorShroom); monster(.@m$, .@x, .@y, strmobinfo(1, .@mobid), .@mobid, 1); break; case 9: mapannounce("026-7", "Monster King : ##BAncient Magic: Lag", 0); areasc2("026-7", 39, 38, 20, 15000, SC_CONFUSION, BL_PC, 1); break; case 10: mapannounce("026-7", "Monster King : ##BAncient Magic: Sleep", 0); areasc2("026-7", 39, 38, 20, 15000, SC_SLEEP, BL_PC, 1); break; case 11: mapannounce("026-7", "Monster King : ##BAncient Magic: Burning", 0); areasc2("026-7", 39, 38, 20, 15000, SC_BURNING, BL_PC, 1); break; case 12: mapannounce("026-7", "Monster King : ##BAncient Magic: Fear", 0); areasc2("026-7", 39, 38, 20, 15000, SC_FEAR, BL_PC, 1); break; case 13: .@msg$ = sprintf("##BAncient Magic: Obliterate"); rectharm(.MK, 7, 7, rand2(700, 900), HARM_MISC, Ele_Dark, "filter_always", BL_PC | BL_MER | BL_HOM); break; case 14: .@msg$ = sprintf("##BAncient Magic: Terminate Homunculi"); rectharm(.MK, 14, 14, rand2(1500, 2500), HARM_MISC, Ele_Dark, "filter_always", BL_MER | BL_HOM); break; case 15: mapannounce("026-7", "Monster King : ##BAncient Magic: Wizcat", 0); for (.@i=0;.@i <= 2+rand2(7);.@i++) { .@m=monster("026-7", rand2(30,50), rand2(30,45), "Reinforcement", BlackCat, 1); setunitdata(.@m, UDT_RACE, RC_Legendary); } break; case 16: mapannounce("026-7", "Monster King : ##BAncient Magic: Silencing Nuke", 0); rectharm(.MK, 14, 14, rand2(450, 750), HARM_MISC, Ele_Dark, "filter_always", BL_PC | BL_MER | BL_HOM); areasc2("026-7", 40, 35, 20, 15000, SC_SILENCE, BL_PC, 1); break; case 17: // Second Attack Pattern: Holy Light (vs Tank) .@msg$ = sprintf("%s, I'll show you no mercy! ##BThunder Bolt##b!", strcharinfo(0, "cursed player", .@mvp)); .@PW=125; .@SPW=25; .@RG=1; .@mtk = calcdmg(.@mob, .@mvp, HARM_MAGI); .@dmg = .@mtk * .@PW / 100; .@dsb = .@mtk * .@SPW / 100; sleep(1000); specialeffect(FX_LIGHTNING, AREA, .@mvp); areaharm(.@mvp, .@RG, .@dsb, HARM_MAGI, Ele_Wind, "filter_always", BL_PC|BL_MER|BL_HOM); harm(.@mvp, .@dmg, HARM_MAGI, Ele_Holy); break; case 18: .@msg$ = sprintf("Come forth, my minions! Wreak chaos and havoc!"); specialeffect(FX_MGWARP, AREA, .MK); // Maybe 65 would also work sleep(1000); // Default amount: Half players + 3 .@max = (getmapusers("026-7")/2) + 3; // Keep boundaries: Never less than 1, never more than 10 .@max = limit(1, .@max, 10); for (.@i=0; .@i < .@max; .@i++) { .@mid = any(GoboBear, Golem, BloodyMouboo, GreenSkullSlime, BlackMamba, JackO, Brainic, TerraniteProtector, Mandragora, Yeti, Reaper); .@x = monster(.@m$, .@x, .@y, strmobinfo(1, .@mid), .@mid, 1); set_aggro(.@x); } break; case 19: .@msg$ = sprintf("I shall ##Bdisable##b you, %s!", strcharinfo(0, "cursed player", .@mvp)); specialeffect(FX_SMOKE, AREA, .MK); attachrid(.@mvp); SC_Bonus(.@t, SC_SILENCE, 1); SC_Bonus(.@t, SC_BLIND, 1); SC_Bonus(.@t/3, SC_CURSE, 1); detachrid(); break; case 20: // First Attack Pattern: Napalm Beat (vs MVP) .@msg$ = sprintf("This battle is over, %s! ##BThunder Neddle##b!", strcharinfo(0, "cursed player", .@mvp)); .@PW=35; .@SPW=5; .@RG=2; .@mtk = calcdmg(.@mob, .@mvp, HARM_MAGI); .@dmg = .@mtk * .@PW / 100; .@dsb = .@mtk * .@SPW / 100; sleep(1000); specialeffect(FX_LIGHTNING, AREA, .@mvp); areaharm(.@mvp, .@RG, .@dsb, HARM_MAGI, Ele_Wind, "filter_always", BL_PC|BL_MER|BL_HOM); harm(.@mvp, .@dmg, HARM_MAGI, Ele_Holy); break; case 21: .@msg$ = sprintf("##BMagic: Armageddon"); rectharm(.MK, 6, 6, rand2(300, 500), HARM_MAGI, Ele_Fire, "filter_always", BL_PC | BL_MER | BL_HOM); break; case 22: .mana += 1; .@msg$ = sprintf("##BMagic: Tempest"); rectharm(.MK, 3, 3, rand2(200, 300), HARM_MAGI, Ele_Wind, "filter_always", BL_PC | BL_MER | BL_HOM); break; case 23: .@msg$ = sprintf("##BMagic: Gaia Break"); sc_start SC_INCDEFRATE, 15000, 10, .MK; rectharm(.MK, 2, 5, rand2(250, 350), HARM_MAGI, Ele_Earth, "filter_always", BL_PC | BL_MER | BL_HOM); break; case 24: .@msg$ = sprintf("##BMagic: Ground Strike"); .@EF=any(SC_STUN, SC_BLIND, SC_BLOODING, SC_BLIND, SC_BLOODING); areasc2("026-7", .@x, .@y, 3, 12000, .@EF, BL_PC, 1); rectharm(.MK, 3, 3, rand2(250, 350), HARM_PHYS, Ele_Neutral, "filter_always", BL_PC | BL_MER | BL_HOM); break; // TODO: Skills which manipulate the map or weather default: .@mx=55; .@my=55; .@x=22; .@y=22; .@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); .@y4=rand2(.@my, .@y); .@y5=rand2(.@my, .@y); // TODO: Maybe replace Dummy/EnergyBall with an invisible monster/FX? .@t1=monster("026-7", .@x1, .@y1, "", Dummy, 1); .@t2=monster("026-7", .@x2, .@y2, "", Dummy, 1); .@t3=monster("026-7", .@x3, .@y3, "", Dummy, 1); .@t4=monster("026-7", .@x4, .@y4, "", Dummy, 1); .@t5=monster("026-7", .@x5, .@y5, "", Dummy, 1); specialeffect(67, AREA, .@t1); specialeffect(67, AREA, .@t2); specialeffect(67, AREA, .@t3); specialeffect(67, AREA, .@t4); specialeffect(67, AREA, .@t5); immortal(.@t1); immortal(.@t2); immortal(.@t3); immortal(.@t4); immortal(.@t5); 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); 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); areaharm(.@t1, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t2, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t3, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t4, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t5, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); sleep(1000); // FIXME: M+ fails to remove them, need @refresh (maybe @refreshall?) // NOTE: The effect NÂș 67 (halo circle) is also not cleaned by M+ unitkill(.@t1); unitkill(.@t2); unitkill(.@t4); unitkill(.@t5); break; } if (.@msg$ != "") unittalk(.@mob, .@msg$); } // Restart the endless loop initnpctimer; end; OnDeathTouch: if (strcharinfo(0) == $AEGIS_HOLDER$) end; SC_Bonus(30, SC_BLIND, 1); SC_Bonus(25, SC_SILENCE, 1); SC_Bonus(15, SC_CURSE, 1); // Consume some mana if (islegendary()) percentheal -2, -15; else percentheal -2, -25; // If you ran out of MP, consume 1/3 HP (Death Touch) if (Sp < 10) { dispbottom l("Death grazes your soul (-%s HP)", fnum(Hp*3/10)); percentheal -30, 0; } // Buff the Monster King slightly (approx. +3.5% damage per player) .@bat=getunitdata(.MK, UDT_ATKMAX); .@bat += .@bat / 28; setunitdata(.MK, UDT_ATKMAX, .@bat); .@bat=getunitdata(.MK, UDT_ATKMIN); .@bat += .@bat / 28; setunitdata(.MK, UDT_ATKMIN, .@bat); end; // Monster King was defeated - game won L_NextAct: $@MK_SCENE=MK_CUTSCENE; $GAME_STORYLINE=5; $MANA_BLVL-=10; // Set base level to 5~15 $MANA_BLVL=max(0, $MANA_BLVL); // Thanksgiving event no longer happens after Monster King dies if ($EVENT$ == "Thanksgiving") { kamibroadcast("Thanksgiving ended!"); sClear(); $EVENT$=""; } donpcevent "sThankAeros::OnTimer90000"; // XXX Cutscene sleep(3000); .mou = monster("026-7", 39, 37, "Moubootaur", MobMoubootaur, 1); immortal(.mou); sc_start(SC_STUN, 90000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .mou); maptimer2("026-7", 30, "Book#FoS::OnMFShake"); sleep(3000); unittalk(.mou, "##1I'm free... At least!"); kamibroadcast("##1I'm free... At least!", "Moubootaur"); sleep(6500); unittalk(.mou, "##1The blood in the Heart, as the prophecy foretold!"); kamibroadcast("##1The blood in the Heart, as the prophecy foretold!", "Moubootaur"); sleep(4500); maptimer2("026-7", 30, "Book#FoS::OnMFShake"); sleep(30); unittalk(.mou, "##1All monsters, heed my command! ##BHostile Takeover!##b"); kamibroadcast("##1All monsters, heed my command!", "Moubootaur"); sleep(1500); freeloop(true); for (.@i=0; .@i < 25; .@i++) { .@m = areamonster("026-7", 20, 20, 55, 55, "Subjugated Monster", any(PinkieEmperor, PanthomLord, TerraniteKing, YetiKing, PsiConscience, DemureSecondForm, Luvia, Birb, MonsterGeneral, MonsterColonel, LavaSlimeMother, BlackSlimeMother, WanderingShadow, BanditLord, HoodedAssassin, SpiderQueen, NightScorpion, SaxsoGhost, GiantMutatedBat), 1); immortal(.@m); // Broken? sc_start(SC_STUN, 90000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@m); sleep(200); // 5 s } freeloop(false); sleep(3000); unittalk(.mou, "##1My children, ##BArise!##b"); sleep(1500); freeloop(true); for (.@i=0; .@i < 25; .@i++) { .@m = areamonster("026-7", 20, 20, 55, 55, "Subjugated Monster", any(Moubi, Moubi, Moubi, MoubooSlime, GreatMoubooSlime, Mouboo, Mouboo, EasterMouboo, AlphaMouboo, Shrewboo, BloodyMouboo), 1); immortal(.@m); sc_start(SC_STUN, 90000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@m); sleep(160); // 4 s } freeloop(false); sleep(3000); unittalk(.mou, "##1I have taken over the World Heart... Your chances are forfeit."); kamibroadcast("##1I have taken over the World Heart... Your chances are forfeit.", "Moubootaur"); sleep(6000); unittalk(.mou, "##1But first, I'll make a Monster King puppet."); kamibroadcast("##1But first, I'll make a Monster King puppet.", "Moubootaur"); sleep(2000); .@m = monster("026-7", 39, 34, "Monster King (puppet)", MonsterKing, 1); immortal(.@m); sc_start(SC_STUN, 90000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@m); sleep(3000); unittalk(.mou, "##1Now, it would be inconvenient if you were to disturb me while I work..."); sleep(6000); unittalk(.mou, "##1It'll take several days or even months to corrupt the Heart. Can't be bothered."); sleep(8000); unittalk(.mou, "##1Enjoy your final days while you can. The day on which everything will be a Mouboo is upon us!"); kamibroadcast("##1Enjoy your final days while you can. The day on which everything will be a Mouboo is upon us!", "Moubootaur"); sleep(5000); maptimer2("026-7", 10, "Impregnable#B7F::OnVictory"); // Cleanup sleep(10000); $@MK_SCENE=MK_NONE; killmonsterall("026-7"); .mou = 0; // This disables all mosnters :< //setbattleflag("monster_ai", 0x209); //setbattleflag("monster_active_enable", false); //setbattleflag("mob_count_rate", 25); //charcommand("@reloadbattleconf"); // Careful! //donpcevent("@exprate::OnReload"); //donpcevent("@droprate::OnReload"); //end; goto OnPrepBlight; OnVictory: // Not killed by a player? It doesn't counts, then if (!playerattached()) end; // Prizes Mobpt += 150 + (.reward * 15); getitem StrangeCoin, 45 + min(55, .reward * 5); // 50 ~ 100 coins reward // Give some additional EXP for higher difficulty settings if (.reward > 1) getexp (.reward/2) * getmonsterinfo(MonsterKing, MOB_BASEEXP), (.reward/2) * getmonsterinfo(MonsterKing, MOB_JOBEXP); if (!MK_WINNER) { MK_WINNER=gettimetick(2); // TODO getitem StrangeCoin, 1450; // Total: 1,500 Strange Coins on first win getitembound MysteriousFruit, 3, 4; // You get 3 char bound fruits getexp 0, 250000; // 250k job exp on first victory } // Effects specialeffect(FX_FANFARE, SELF, getcharid(3)); // Bailout sleep2(5000); warp "025-1", 99, 22; close; OnPrepBlight: if ($SHADY_HOLDER$ != "") end; .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "026-7"); .@mvp=0;.@def=-1; for (.@i = 0; .@i < .@c; .@i++) { .@atk = readbattleparam(.@pcs[.@i], UDT_ATKMAX); if (.@atk > .@def) { // Lets check if they qualify attachrid(.@pcs[.@i]); if (islegendary()) .@atk=false; // Already legendary, so nope if (ispcdead()) .@atk=false; // Dead, so nope if (!checkweight(Blightbringer, 1)) .@atk=false; // Overweight, so nope detachrid(); // They qualify: Remember this, and look for someone worthier if (.@atk) { .@mvp=.@pcs[.@i]; .@def=.@atk; } } // if qualified } // for Loop if (.@mvp) { attachrid(.@mvp); addtimer(10, "Impregnable#B7F::OnBlightbringer"); } stopnpctimer; end; OnBlightbringer: // Repeat all checks if ($SHADY_HOLDER$ != "") end; if (islegendary()) end; if (ispcdead()) end; inventoryplace Blightbringer, 1; $SHADY_HOLDER$ = strcharinfo(0); getitembound Blightbringer, 1, 1; // Account bound or char bound? (1 or 4) dispbottom l("For defeating the Monster King, you've got the Legendary @@.", getitemlink(Blightbringer)); dispbottom l("This item cannot be traded normally and is a Legendary Item."); dispbottom l("You can transfer it with \"@grantpower\" command. Please contact a GM for more info."); dispbottom l("Protip: If you plan in selling it, it's adviseable to ask for GM mediation."); end; } // Room Traps, only against players 026-7,0,0,0 script #MKBossTrap01 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(21, 60); .@y = rand2(21, 60); .@e+=1; } while (!checknpccell("026-7", .@x, .@y, cell_chkpass)); movenpc .name$, .@x, .@y; end; } // Create more traps 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap02 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap03 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap04 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap05 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap06 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap07 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap08 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap09 NPC_TRAP_ONLINE,0,0 026-7,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap10 NPC_TRAP_ONLINE,0,0