diff options
author | Jesusaves <cpntb1@ymail.com> | 2023-10-13 20:47:33 -0300 |
---|---|---|
committer | Jesusaves <cpntb1@ymail.com> | 2023-10-13 20:47:33 -0300 |
commit | 7852faa31dcd71b170962122ff6cd3bb3197e946 (patch) | |
tree | 64dce5260bef5e71d0516892a81578150fb51b74 | |
parent | c360713b7adbbcb68c513a0fdbfe87e0c2bbc4df (diff) | |
download | serverdata-7852faa31dcd71b170962122ff6cd3bb3197e946.tar.gz serverdata-7852faa31dcd71b170962122ff6cd3bb3197e946.tar.bz2 serverdata-7852faa31dcd71b170962122ff6cd3bb3197e946.tar.xz serverdata-7852faa31dcd71b170962122ff6cd3bb3197e946.zip |
Monster King Core Logic. He still cannot be challenged. Not tested.
-rw-r--r-- | npc/026-7/boss.txt | 401 | ||||
-rw-r--r-- | npc/042-11/boss.txt | 1 |
2 files changed, 387 insertions, 15 deletions
diff --git a/npc/026-7/boss.txt b/npc/026-7/boss.txt index 0f8b97119..6eae36cf3 100644 --- a/npc/026-7/boss.txt +++ b/npc/026-7/boss.txt @@ -9,6 +9,11 @@ end; OnInit: + .MK=0; + .maxhp = 1000000; // 1,000,000 HP + .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 end; // Maybe not OnTouch, but OnSit? @@ -41,11 +46,53 @@ OnBegin: // Initial assortment of monsters siege_cast("026-7", .name$, 15, TP_TULIM|TP_HURNS|TP_NIVAL); // Spawn the boss himself - .MK=monster("026-7", 39, 34, "The Monster King", MonsterKing, 1, .name$+"::OnVictory"); + .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); + .memohp = 999; // Reset Memorand HP + // 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); + // 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); + // The remainder of MK army (Centered at the generals) + .@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 + for (.@i = 0; .@i < 4; ++.@i) { + areamonster("026-7", 37, 32, 41, 36, "Imperial Officer", any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie, BlackSkullSlime, PinkieSuseran), 1); + } 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."); @@ -57,24 +104,329 @@ OnTimer25000: OnTimer15000: consolewarn("Warning, fail-safe mechanism triggered to Monster King."); OnTimer10000: - // TODO: Heal fully if in immortal mode, fix HP when immortality is lost - // TODO: (Also heal if no players are around) - initnpctimer: + maptimer2("026-7", 10, "Impregnable#B7F::OnMFDispose"); + .mana += 1; // Recover mana + + /* Regeneration & Defeat Loop */ + .@end = false; + if (.immortal) + setunitdata(.MK, UDT_HP, INT_MAX); + if (!getmapusers("026-7")) { + if (.immortal) { + .@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) { + $@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!", "WORLD HEART"); + if ($GAME_STORYLINE != 5) + goto L_NextAct; + maptimer2("026-7", 10, "Impregnable#B7F::OnVictory"); + } else { + // We actually lost?! + kamibroadcast("The MONSTER KING has WON the showdown!", "WORLD HEART"); + } + stopnpctimer; + 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) { + .immortal = false; + kamibroadcast("The Monster King has lost his immortality.", "Monster King"); + setunitdata(.MK, UDT_HP, .maxhp); + setunitdata(.MK, UDT_MAXHP, .maxhp); + } + } + + /* Spawn every 20% HP lost, using siege logic 'cause lazy */ + if (!.immortal) { + .@hp = getunitdata(.MK, UDT_HP) * 5 / .maxhp; + if (.@hp < .memohp) { + .memohp = .@hp; + siege_cast("026-7", .name$, 15, TP_TULIM|TP_HURNS|TP_NIVAL); + } + } + + /* Decide whenever the Monster King will use a skill. */ + // Each Mana point is worth 1% chance. He gains 6 points per minute. + // (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 = max(.@mp, .@hp + (.@mp / 250)); // Regenerates 0.4% HP + setunitdata(.@mob, UDT_HP, min(.@hp, .@mp)); + .@mobid=(rand2(.@lv) > 50 ? DeathCat : GreenSlime); + 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 ? BlackScorpion : RedSlime); + 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 ? DarkLizard : Assassin); + 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 ? BlueSlime : WhiteSlime); + 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 ? Thug : RedMushroom); + 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 ? BlackSlime : OldSnake); + 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 ? FireSkull : Skeleton); + 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 ? Swashbuckler : Bluepar); + 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, Centaur, BloodyMouboo, GreenSkullSlime, BlackMamba, JackO, Brainic, TerraniteProtector, 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; // Monster King was defeated - game won -OnVictory: - // Not killed by a player? It doesn't counts, then - if (!playerattached()) - end; - $@MK_CHALLENGE=false; - kamibroadcast("has just defeated the Monster King.", strcharinfo(0)); - stopnpctimer; - mapwarp("boss", "017-1", 120, 88); +L_NextAct: $GAME_STORYLINE=5; $MANA_BLVL-=10; // Set base level to 5~15 $MANA_BLVL=max(0, $MANA_BLVL); - specialeffect(FX_FANFARE, AREA, getcharid(3)); // Without the Monster King to rule monsters... TODO Isbamuth, Moubootaur // This disables all mosnters :< setbattleflag("monster_ai", 0x209); @@ -83,8 +435,27 @@ OnVictory: //charcommand("@reloadbattleconf"); // Careful! donpcevent("@exprate::OnReload"); donpcevent("@droprate::OnReload"); - // Player Reward - /*getitembound(AegisShield, 1, 1); + end; + +OnVictory: + // Not killed by a player? It doesn't counts, then + if (!playerattached()) + end; + // Prizes + Mobpt += 165; + getitem StrangeCoin, 50; + if (!MK_WINNER) { + MK_WINNER=gettimetick(2); // TODO + getitem StrangeCoin, 2450; + getitembound MysteriousFruit, 3, 4; // You get 3 char bound fruits + } + // Effects + specialeffect(FX_FANFARE, SELF, getcharid(3)); + // Bailout + sleep2(5000); + warp "025-1", 99, 22; + // Disabled code (for translation only) + /* dispbottom l("For defeating the Monster King, you've got the Legendary @@.", getitemlink(AegisShield)); 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."); diff --git a/npc/042-11/boss.txt b/npc/042-11/boss.txt index f2f9af363..df5e02171 100644 --- a/npc/042-11/boss.txt +++ b/npc/042-11/boss.txt @@ -154,6 +154,7 @@ OnTimer5000: } } .@lv = $KAMELOT_MX[.@g]; + .@t = rand2(2 + .@lv / 10); // Execute the skill switch (.@skill) { case 1: |