// TMW2 Script // Author: // Jesusalva // Description: // Crazyfefe's Shrine (remastered) 006-10 mapflag zone MMO 026-2 mapflag zone MMO // We need to refactor this warp system, replace NPC_HIDDEN, and include the boss 006-10,54,58,0 script Blanc NPC_BLANC,{ if (getunittype(.blanc) >= 0) end; mesc l("Isn't that Blanc? Should we attempt to capture him?"); mesc l("Advised: 6+ players"), 1; mesc l("Advised: 1+ mage, 1+ tanker, 2+ healers"), 1; mesc l("No Time Limit"), 1; mesc l("Enter/Leave after start: %s", b(l("YES"))), 1; next; select l("Not yet."), l("Bring it on!"), rif(is_gm(), l("Bring me, my worst nightmare.")); mes ""; if (@menu == 1) { closeclientdialog; close; } if (@menu == 2) .hard = false; else .hard = true; // Create the boss .blanc = monster("006-10", 54, 61, "Blanc Halifax", Blanc, 1); .@mlt = (.hard ? 15 : 10); // Basic attributes .maxhp = (750000 * .@mlt / 10); // 750k ~ 1250k setunitdata(.blanc, UDT_MAXHP, .maxhp); setunitdata(.blanc, UDT_HP, .maxhp); setunitdata(.blanc, UDT_ATKRANGE, (.hard ? 7 : 6)); // Reconfigure the AI .@opt=getunitdata(.blanc, UDT_MODE); // Disable looting if (.@opt & MD_LOOTER) .@opt=.@opt^MD_LOOTER; // 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(.blanc, UDT_MODE, .@opt); // Nerf the damage, but never miss a hit setunitdata(.blanc, UDT_ATKMIN, 60 * .@mlt / 10); // 60~90 dmg setunitdata(.blanc, UDT_ATKMAX, 60 * .@mlt / 10); setunitdata(.blanc, UDT_ADELAY, 2220 / .@mlt * 10); // 2220 or 1480ms setunitdata(.blanc, UDT_HIT, 2400); // Boosting the defense is not necessary // It nerfs weapons to 40% (bows to 20%) // Then it resists 50% of Neutral element. // Note it is strong against Water (25% dmg) // And weak against Fire (snow) and Wind (100% dmg) // Otherwise, behave as Ghost element // Make Blanc damn hard to damage until his reinforcements appear sc_start SC_PRESTIGE, 5000, 120000, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE, .blanc; // TODO: Defense and attributes are wrong? disablenpc .name$; initnpctimer; closeclientdialog; close; OnRw: dispbottom l("Mission accomplished. Well played!"); // Mark as "done" on Mirror Lake Quest Tracker ##01_CRQUEST = ##01_CRQUEST | MLP_CR_DEBUT; specialeffect(FX_FANFARE, AREA, getcharid(3)); sleep2(15000); $@EVENT_08 = PORTHOS_ACTIVE; warp("033-1", 72, 191); end; // Fail-safe Mechanism (will never happen) OnTimer60000: consolebug("Warning! final fail-safe mechanism triggered to Blanc."); initnpctimer; end; OnTimer25000: OnTimer15000: consolewarn("Warning, fail-safe mechanism triggered to Blanc."); // This is the boss' core OnTimer5000: /* Regeneration & Defeat Loop */ .@end = false; // FIXME: Don't check the WHOLE map, just the current platform? if (!getmapusers("006-10")) { .@hp = getunitdata(.blanc, UDT_HP); .@mh = .maxhp; .@hp = max(.@mh, .@hp + (.@mh / 500)); // Regenerates 0.2% HP // Fully healed, players lost if (.@hp >= .@mh) .@end = true; // Regeneration setunitdata(.blanc, UDT_HP, .@hp); } /* Maybe the fight is over */ if (!mobcount("006-10", "all") || getunittype(.blanc) < 0) { $@EVENT_08 = PORTHOS_BUSY; maptimer2("006-10", 10, "Blanc::OnRw"); } if (!mobcount("006-10", "all") || getunittype(.blanc) < 0 || .@end) { killmonsterall("006-10"); enablenpc .name$; kamibroadcast("The battle is over!", "Blanc Showdown"); .beats = 0; stopnpctimer; end; } /* Move platform according to HP threshold */ .@hp = getunitdata(.blanc, UDT_HP) * 10 / .maxhp; .@state = 9 - limit(0, .@hp, 9); // The platform ID they should be on debugmes "HP %d / %d (%d%%) (S %d/%d) [%03d@%d]", getunitdata(.blanc, UDT_HP), .maxhp, .@hp, .@state, .state, .beats, .blanc; if (.state != .@state) { unittalk(.blanc, "Damn, you guys are tough! I must fall back!"); sleep(100); unitwarp(.blanc, "006-10", $@BLANC_X[.@state], $@BLANC_Y[.@state]); sleep(100); unittalk(.blanc, "Outta the way!"); .state = .@state; } /* Prepare some combat data */ // TODO: Maybe not universal? Maybe we should impose a range? getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .blanc); .@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); } } .beats += 1; /* Everyone is dead, get rid of their corpses */ if (!.@mvp || !.@rnd) { mapwarp("006-10", "033-1", 72, 191); initnpctimer; end; } //debugmes "----------- Skill Loop, beat is %d", .beats % 18; /* Decide the skill to use based on ~5s beats over 3 minutes */ switch (.beats % 18) { // (1/6) Summon Reinforcements (every 60s) case 1: case 7: case 13: unittalk(.blanc, "Come forth, ##BHalifax's Crew##b, do not let these fools arrest your captain!"); specialeffect(FX_MGWARP, AREA, .blanc); // Maybe 65 would also work sleep(1000); // Default amount: Half players + Half magnifying HP .@max = (getmapusers("006-10")/2) + (max(1, (11 - .@hp) / 10)/2); // Keep boundaries: Never less than 1, never more than 10 .@max = limit(1, .@max, 10); for (.@i=0; .@i < .@max; .@i++) { .@mob = any(Thug, Grenadier, Swashbuckler); monster(.@m$, .@x, .@y, strmobinfo(1, .@mob), .@mob, 1); } break; // (2/6) Tanker (~30s) case 0: case 4: case 6: case 10: case 12: case 16: specialeffect(FX_ATTACK, AREA, .blanc); sleep(1000); if (.@hp < 3) { // Third Attack Pattern: Judgment unittalk(.blanc, sprintf("%s cannot stop me! ##BElectric Judgment##b!", strcharinfo(0, "cursed player", .@mvp))); .@PW=240; .@SPW=60; .@RG=3; } else if (.@hp < 7) { // Second Attack Pattern: Holy Light unittalk(.blanc, sprintf("%s, I'll show you no mercy! ##BThunder Bolt##b!", strcharinfo(0, "cursed player", .@mvp))); .@PW=125; .@SPW=25; .@RG=1; } else { // First Attack Pattern: Napalm Beat unittalk(.blanc, sprintf("This battle is over, %s! ##BThunder Neddle##b!", strcharinfo(0, "cursed player", .@mvp))); .@PW=35; .@SPW=5; .@RG=2; } .@mtk = calcdmg(.blanc, .@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; // (3/6) Random Target (~60s) case 2: case 8: case 14: specialeffect(FX_SMOKE, AREA, .blanc); sleep(1000); .@time=rand2(18000, 36000) + 10000 - (.@hp * 1000); // Switch between curse and disable if (any(true,false)) { unittalk(.blanc, sprintf("I hereby ##Bcurse##b you, %s!", strcharinfo(0, "cursed player", .@rnd))); sc_start(SC_CURSE, .@time, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); } else { unittalk(.blanc, sprintf("I shall ##Bdisable##b you, %s!", strcharinfo(0, "cursed player", .@rnd))); sc_start(SC_BLIND, .@time / 2, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); sc_start(SC_SILENCE, .@time / 2, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); } // Second pattern: Bleeding ON if (.@hp < 7) { sc_start(SC_BLOODING, 10000, 1, 9000-(.@hp*1000), SCFLAG_FIXEDRATE, .@rnd); } specialeffect(FX_LIGHTNING, AREA, .@rnd); break; // (4/6) Traps (~60s) case 3: case 9: case 15: // Determine if there'll be 4 or 2 AoE explosions .@extra = (.@hp < 5 || .hard); // Coordinates are between origin and Blanc // ID 3 and ID 6 were disabled - the platforms are small .@mx=$@BLANC_X[.@state]; .@my=$@BLANC_Y[.@state]; .@x1=rand2(.@mx, .@x); .@x2=rand2(.@mx, .@x); .@y1=rand2(.@my, .@y); .@y2=rand2(.@my, .@y); // TODO: Maybe replace Dummy/EnergyBall with an invisible monster? .@t1=monster("006-10", .@x1, .@y1, "", EnergyBall, 1); .@t2=monster("006-10", .@x2, .@y2, "", EnergyBall, 1); specialeffect(67, AREA, .@t1); specialeffect(67, AREA, .@t2); immortal(.@t1); immortal(.@t2); if (.@extra) { .@x4=rand2(.@mx, .@x); .@x5=rand2(.@mx, .@x); .@y4=rand2(.@my, .@y); .@y5=rand2(.@my, .@y); .@t4=monster("006-10", .@x1, .@y1, "", EnergyBall, 1); .@t5=monster("006-10", .@x2, .@y2, "", EnergyBall, 1); specialeffect(67, AREA, .@t4); specialeffect(67, AREA, .@t5); immortal(.@t4); immortal(.@t5); } sleep(1500); // This is just a prop specialeffect(FX_LIGHTNING, AREA, .@t1); specialeffect(FX_LIGHTNING, AREA, .@t2); if (.@extra) { specialeffect(FX_LIGHTNING, AREA, .@t4); specialeffect(FX_LIGHTNING, AREA, .@t5); } sleep(500); specialeffect(FX_CRITICAL, AREA, .@t1); specialeffect(FX_CRITICAL, AREA, .@t2); if (.@extra) { specialeffect(FX_CRITICAL, AREA, .@t4); specialeffect(FX_CRITICAL, AREA, .@t5); } areaharm(.@t1, 1, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t2, 1, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); if (.@extra) { areaharm(.@t4, 1, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); areaharm(.@t5, 1, 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); if (.@extra) { unitkill(.@t4); unitkill(.@t5); } break; // (5/6) Weak AOE (~60s) case 5: case 11: case 17: specialeffect(60, AREA, .blanc); sleep(500); switch (rand2(3)) { case 1: unittalk(.blanc, "Cease! I shall ##Bpoison##b all of you!"); .@sc = (.@hp < 1 ? SC_DPOISON : SC_POISON); break; case 2: unittalk(.blanc, "Cease! I shall ##Bhurt##b all of you!"); .@sc = SC_BLOODING; break; case 3: unittalk(.blanc, "Cease! I shall ##Bsilence##b all of you!"); .@sc = SC_SILENCE; break; default: unittalk(.blanc, "Cease! I shall ##Bcripple##b all of you!"); .@sc = SC_BLIND; break; } areasc((.hard ? 7 : 5), 30000, .@sc, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .blanc, 95000); areaharm(.blanc, (.hard ? 7 : 5), 100, HARM_MAGI, Ele_Wind, "filter_always", BL_PC|BL_MER|BL_HOM); // Forces everyone to sit, for cosmetic effect areasc(18, 500, SC_BANANA_BOMB_SITDOWN_POSTDELAY, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .blanc); break; } // Strong AOE: Every 3 minutes (case = 0) // Stalls all other skills for a while if (.beats % 18 == 0) { specialeffect(66, AREA, .blanc); unittalk(.blanc, "I am Blanc, Halifax's captain!"); sleep((.hard ? 1000 : 1500)); specialeffect(700, AREA, .blanc); unittalk(.blanc, "I'll never be caught by your ilk alive!"); sleep((.hard ? 1000 : 1500)); specialeffect(700, AREA, .blanc); if (.@hp < 3) { unittalk(.blanc, "Perish! ##BRage of Hungry Thunder##b!"); .@dmg=rand2(900, 1100); areasc3((.hard ? 5 : 4), 11000, SC_STOMACHACHE, BL_PC|BL_HOM|BL_MER, (.hard ? 20 : 15), "filter_always", .blanc, 6000); areasc(6, 10000, SC_BLIND, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .blanc, 2000); } else if (.@hp < 7) { unittalk(.blanc, "Perish! ##BVoracious Thunder Storm##b!"); .@dmg=rand2(650, 900); areasc3((.hard ? 4 : 3), 11000, SC_STOMACHACHE, BL_PC|BL_HOM|BL_MER, (.hard ? 15 : 10), "filter_always", .blanc, 4500); } else { unittalk(.blanc, "Perish! ##BEletric Needles##b!"); .@dmg=rand2(400, 650); } /* Three blocks */ specialeffect(66, AREA, .blanc); areaharm(.blanc, (.hard ? 7 : 5), .@dmg, HARM_MAGI, Ele_Fire, "filter_always", BL_PC|BL_MER|BL_HOM); sleep(500); specialeffect(66, AREA, .blanc); areaharm(.blanc, (.hard ? 15 : 10), .@dmg, HARM_MAGI, Ele_Holy, "filter_always", BL_PC|BL_MER|BL_HOM); sleep(500); specialeffect(66, AREA, .blanc); specialeffect(FX_LIGHTNING, AREA, .blanc); areaharm(.blanc, (.hard ? 24 : 18), .@dmg, HARM_MAGI, Ele_Wind, "filter_always", BL_PC|BL_MER|BL_HOM); areasc((.hard ? 24 : 18), .@dmg*rand2(50, 100), SC_FROSTMISTY, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .blanc, 10001 - (.@hp * 1000)); // SC_FROSTMISTY - General crippling } initnpctimer; end; OnInit: .distance = 4; .beats = 0; // Skill cooldown timer .hard = 0; // Hard mode (for GMs) .blanc = 0; // Boss GID .state = 0; // Current platform ID .maxhp = 0; // Blanc's max HP (getunitdata(.blanc, UDT_MAXHP) is broken) // TODO: In general, we have no idea of platforms size setarray $@BLANC_X, 53, 72, 71, 72, 52, 36, 34, 35, 35, 52; setarray $@BLANC_Y, 63, 67, 53, 30, 30, 27, 48, 36, 63, 36; // .@index = miller_rand(.@state, getcharid(0), 10); end; } // Below be the warp system. Rightmost forwards, leftmost rewinds. 006-10,51,64,0 script #00610XX NPC_HIDDEN,1,0,{ end; OnTouch: // Now... WTF are we on miller rand? if (!@miller_fix) { for (.@i=0;.@i<10;.@i++) { if (miller_rand(.@i, getcharid(0), 10) == 0) { @miller_fix = .@i; break; } } } // Set "@state" properly @state = @miller_fix; end; } 006-10,57,61,0 script #00610WA+ NPC_FANCY_CIRCLE,0,0,{ end; OnTouch: // All portals are the same, anyway if (compare(strnpcinfo(2), "+")) @state = ((@state+1) % 10); // Technically, the loop is not needed else @state = ((@state-1) % 10); // Technically, this loop is not needed .@index = miller_rand(@state, getcharid(0), 10); slide $@BLANC_X[.@index], $@BLANC_Y[.@index]; end; } // NOTE: Vampire Bats are aggressive, they're tasked in disposing non-fighters 006-10,49,60,0 duplicate(#00610WA+) #00610WA- NPC_FANCY_CIRCLE,0,0 006-10,75,65,0 duplicate(#00610WA+) #00610WB+ NPC_FANCY_CIRCLE,0,0 006-10,68,64,0 duplicate(#00610WA+) #00610WB- NPC_FANCY_CIRCLE,0,0 006-10,75,50,0 duplicate(#00610WA+) #00610WC+ NPC_FANCY_CIRCLE,0,0 006-10,67,49,0 duplicate(#00610WA+) #00610WC- NPC_FANCY_CIRCLE,0,0 006-10,76,27,0 duplicate(#00610WA+) #00610WD+ NPC_FANCY_CIRCLE,0,0 006-10,68,26,0 duplicate(#00610WA+) #00610WD- NPC_FANCY_CIRCLE,0,0 006-10,56,27,0 duplicate(#00610WA+) #00610WE+ NPC_FANCY_CIRCLE,0,0 006-10,48,26,0 duplicate(#00610WA+) #00610WE- NPC_FANCY_CIRCLE,0,0 006-10,40,24,0 duplicate(#00610WA+) #00610WF+ NPC_FANCY_CIRCLE,0,0 006-10,32,24,0 duplicate(#00610WA+) #00610WF- NPC_FANCY_CIRCLE,0,0 006-10,37,51,0 duplicate(#00610WA+) #00610WG+ NPC_FANCY_CIRCLE,0,0 006-10,30,45,0 duplicate(#00610WA+) #00610WG- NPC_FANCY_CIRCLE,0,0 006-10,41,33,0 duplicate(#00610WA+) #00610WH+ NPC_FANCY_CIRCLE,0,0 006-10,30,33,0 duplicate(#00610WA+) #00610WH- NPC_FANCY_CIRCLE,0,0 006-10,44,63,0 duplicate(#00610WA+) #00610WI+ NPC_FANCY_CIRCLE,0,0 006-10,35,57,0 duplicate(#00610WA+) #00610WI- NPC_FANCY_CIRCLE,0,0 006-10,55,37,0 duplicate(#00610WA+) #00610WJ+ NPC_FANCY_CIRCLE,0,0 006-10,49,37,0 duplicate(#00610WA+) #00610WJ- NPC_FANCY_CIRCLE,0,0