diff options
author | Jesusaves <cpntb1@ymail.com> | 2025-03-29 18:29:43 -0300 |
---|---|---|
committer | Jesusaves <cpntb1@ymail.com> | 2025-03-29 18:29:43 -0300 |
commit | fbd3a755f7fd28aed02a82c39b96525e58a84dce (patch) | |
tree | 9ec1d35a504065c5965f123a8e4608798512262b | |
parent | 3b88f44a9e53b4ef6707c197a4b83789eaa84556 (diff) | |
download | serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.gz serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.bz2 serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.tar.xz serverdata-fbd3a755f7fd28aed02a82c39b96525e58a84dce.zip |
This is the massive commit with an unfinished update
-rw-r--r-- | conf/map/battle/limits.conf | 1 | ||||
-rw-r--r-- | conf/map/storage.conf | 5 | ||||
-rw-r--r-- | npc/000-1/exit.txt | 6 | ||||
-rw-r--r-- | npc/001-1/bgmaster.txt | 31 | ||||
-rw-r--r-- | npc/001-15/_import.txt | 1 | ||||
-rw-r--r-- | npc/001-15/_mobs.txt | 3 | ||||
-rw-r--r-- | npc/001-15/ctrl.c | 960 | ||||
-rw-r--r-- | npc/001-16/_import.txt | 1 | ||||
-rw-r--r-- | npc/001-16/_mobs.txt | 9 | ||||
-rw-r--r-- | npc/001-16/obelisk.c | 92 | ||||
-rw-r--r-- | npc/003-6/tamiloc.txt | 6 | ||||
-rw-r--r-- | npc/functions/hub.txt | 51 | ||||
-rw-r--r-- | npc/functions/mobpoint.txt | 1 | ||||
-rw-r--r-- | npc/functions/util.txt | 12 | ||||
-rw-r--r-- | npc/items/inc_sc_bonus.txt | 20 | ||||
-rw-r--r-- | npc/items/legacy_heal.txt | 6 | ||||
-rw-r--r-- | npc/items/rand_mp_heal.txt | 6 | ||||
-rw-r--r-- | npc/items/rand_sc_heal.txt | 6 |
18 files changed, 1204 insertions, 13 deletions
diff --git a/conf/map/battle/limits.conf b/conf/map/battle/limits.conf index d61e4ea2c..bce04a451 100644 --- a/conf/map/battle/limits.conf +++ b/conf/map/battle/limits.conf @@ -25,6 +25,7 @@ //= along with this program. If not, see <http://www.gnu.org/licenses/>. //========================================================================= // Battle (Limits) Configuration File +// Most of them are redefined in plugin/status.c so edit both! //========================================================================= // basic attack limits diff --git a/conf/map/storage.conf b/conf/map/storage.conf index 82d3144bc..ae36ebde8 100644 --- a/conf/map/storage.conf +++ b/conf/map/storage.conf @@ -69,4 +69,9 @@ storage_conf: ( Name: "Deluxe Storage" Capacity: 500 }, +{ + Id: 6 + Name: "Temporary Storage" + Capacity: 180 +}, ) diff --git a/npc/000-1/exit.txt b/npc/000-1/exit.txt index aed9a8c92..84d2ff323 100644 --- a/npc/000-1/exit.txt +++ b/npc/000-1/exit.txt @@ -14,6 +14,12 @@ OnTalkNearby: end; } + // If your soul is lost, Soul Menhir logic is invalid + if (Class == Skelli) { + warp "001-16", 90, 45; + end; + } + // Switch LOCATION$ and warp to nearest town's Soul Menhir .@lx=array_find($@LOCMASTER_LOC$, LOCATION$); if (.@lx >= 0) { diff --git a/npc/001-1/bgmaster.txt b/npc/001-1/bgmaster.txt index d2b63f31e..b9c8d63c3 100644 --- a/npc/001-1/bgmaster.txt +++ b/npc/001-1/bgmaster.txt @@ -7,6 +7,7 @@ 001-1,96,37,0 script Cassia NPC_FEMALE,{ mesn; + if (Class == Skelli) goto L_Busy; if ($GAME_STORYLINE >= 5 && getq(General_Narrator) >= 23) goto L_Moubootaur; if (is_gm()) goto L_Control; if ($@BGMaster1) goto L_Busy; @@ -14,7 +15,7 @@ // 001-15 Battlegrounds Functions L_Moubootaur: - mesq l("Hello! I am Cassia and you are qualified to enter in the cave behind me. Do you want to?"); + mesq l("Hello! I am Cassia and you are qualified to enter in the cave behind me. Are you interested?"); if (askyesnosafe() == ASK_NO) { closeclientdialog; end; } setpcblock(PCBLOCK_HARD, true); mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1; @@ -27,13 +28,36 @@ L_Moubootaur: next; mesn; mesq l("And maybe not even death can stop you! You're entering directly in the Mana Plane section of the World's Heart - the Soul Menhir will %s work inside.", b(l("NOT"))); + next; + mesn; + mesq l("I have absolutely no idea on what to expect inside, so you should come prepared! Make sure you have a party with you, because you might be stuck there %s if you fail.", b(l("FOREVER"))); // You _can_ leave, but it's somewhat difficult + next; + + // Superior Safeguard + if (!is_staff()) goto L_TODO; + + // Ask if the player is sure they want to go to the showdown + mesc l("With all that said, are you sure you want to enter?!"), 1; + if (askyesnosafe() == ASK_NO) goto L_Close; + + warp "001-15", any(52,53), 58; + // Start main timer + if (!$@ML_SHOWDOWN) + donpcevent "#Moubootaur::OnIntroCutscene"; + // TODO: bg_join? + // TODO: "#Moubootaur"::PermanentDebuffs() + goto L_Close; +L_TODO: // Lore goes here... // Endtrail mes ""; mesc l("@@ You need to wait further releases to continue this quest!", b(l("WARNING:"))), 1; - next; + goto L_Close; + +// This label closes the dialogue after unblocking the player +L_Close: setpcblock(PCBLOCK_HARD, false); closeclientdialog; close; @@ -55,9 +79,6 @@ L_Intro: } close; -L_Close: - close; - L_Busy: if ($@BGMaster1 == 2) { mesq l("The soldiers are resting at the moment."); diff --git a/npc/001-15/_import.txt b/npc/001-15/_import.txt index 1fb979870..8e0f742e7 100644 --- a/npc/001-15/_import.txt +++ b/npc/001-15/_import.txt @@ -1,3 +1,4 @@ // Map 001-15: Moubootaur Showdown // This file is generated automatically. All manually added changes will be removed when running the Converter. "npc/001-15/_mobs.txt", +"npc/001-15/ctrl.c", diff --git a/npc/001-15/_mobs.txt b/npc/001-15/_mobs.txt index 6339d55fa..e369cf266 100644 --- a/npc/001-15/_mobs.txt +++ b/npc/001-15/_mobs.txt @@ -1,3 +1,4 @@ // This file is generated automatically. All manually added changes will be removed when running the Converter. // Map 001-15: Moubootaur Showdown mobs -001-15,54,39,34,19 monster Vampire Bat 1063,12,300000,30000 +001-15,50,39,30,19 monster Vampire Bat 1063,12,300000,30000 +001-15,85,39,3,19 monster House Maggot 1084,6,30000,30000 diff --git a/npc/001-15/ctrl.c b/npc/001-15/ctrl.c new file mode 100644 index 000000000..928a53bdc --- /dev/null +++ b/npc/001-15/ctrl.c @@ -0,0 +1,960 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Moubootaur Legends :: Final Showdown +// +001-15 mapflag zone FinalMMO +// Do we need a battlegrounds MF? + +001-15,0,0,0 script #Moubootaur NPC_HIDDEN,{ + end; + +public function DeathHandler; +public function ReviveHandler; + +function _boostMe { + .@mg = getarg(0); + .@bat=getunitdata(.@mg, UDT_ATKMAX); + .@bai=getunitdata(.@mg, UDT_ATKMIN); + .@bdf=getunitdata(.@mg, UDT_DEF); + .@bcr=getunitdata(.@mg, UDT_CRIT); + .@bag=getunitdata(.@mg, UDT_AGI); + .@bf = getarg(1, $@ML_SHOWDOWN) + 3; + .@s=.@bf+rand2(5); + .@bat = .@bat * (.@bf / 2); + .@bcr = .@bcr * (.@bf / 2); + setunitdata(.@mg, UDT_ATKMAX, .@bat+(.@s*5)); + setunitdata(.@mg, UDT_DEF, .@bdf+(.@s*4)); + setunitdata(.@mg, UDT_CRIT, .@bcr+(.@s*3)); + setunitdata(.@mg, UDT_ATKMIN, .@bai+(.@s*2)); + setunitdata(.@mg, UDT_AGI, .@bag+(.@s*1)); + return; +} + +// Maybe this should be a function +public function MapMode { + // Define the planned new map mode + .@new_mode = rand2(10); + + // Revert the old map mode + switch (.mapMode) { + case ML_MAPMODE_PVP: + pvpoff("001-15"); break; + case ML_MAPMODE_NOCHAT: + removemapflag("001-15",mf_nocommand); + removemapflag("001-15",mf_nochat); + break; + case ML_MAPMODE_NOPETS: + removemapflag("001-15",mf_nopet); + break; + default: + break; + } + + // Set the new map mode + switch (.@new_mode) { + case ML_MAPMODE_PVP: + pvpon("001-15"); break; + case ML_MAPMODE_NOCHAT: + setmapflag("001-15",mf_nocommand,true); + setmapflag("001-15",mf_nochat,true); + break; + case ML_MAPMODE_NOPETS: + setmapflag("001-15",mf_nopet,true); + break; + default: + break; + } + + // Save the new map mode + .mapMode = .@new_mode; + + // TODO: update/set map mask (visual information) + return; +} + +OnDoT: + if (ispcdead()) DeathHandler(true); + percentheal -1, 0; + end; + +// The functions which the Generals call when they die +OnBlue: + if (!playerattached()) { + .@x=72; .@y=41; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyWater, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnBrown: + if (!playerattached()) { + .@x=25; .@y=55; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyEarth, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnRed: + if (!playerattached()) { + .@x=74; .@y=55; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyFire, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnGreen: + if (!playerattached()) { + .@x=42; .@y=36; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyWind, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnWhite: + if (!playerattached()) { + .@x=25; .@y=25; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeySacred, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnGolden: + if (!playerattached()) { + .@x=40; .@y=50; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyMana, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnPurple: + if (!playerattached()) { + .@x=70; .@y=30; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyDeath, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + +OnBlack: + if (!playerattached()) { + .@x=25; .@y=40; + } else { + getmapxy(.@m$, .@x, .@y, 0); + } + makeitem(MKeyEvil, 1, "001-15", .@x+any(-1,0,1), .@y+any(-1,0,1)); + .@m=monster("001-15", .@x-3, .@y-3, .@x+3, .@y+3, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m, 3); + end; + + + + + +// Setup first stage +OnFirstStage: + // Eliminate all existing keys + DelItemFromEveryPlayer(MKeyWater); + DelItemFromEveryPlayer(MKeyEarth); + DelItemFromEveryPlayer(MKeyFire); + DelItemFromEveryPlayer(MKeyWind); + DelItemFromEveryPlayer(MKeySacred); + DelItemFromEveryPlayer(MKeyMana); + DelItemFromEveryPlayer(MKeyDeath); + DelItemFromEveryPlayer(MKeyEvil); + + // Spawn the map-wide reinforcements, 20 units for 8 generals + areamonster("001-15", 20, 20, 80, 59, strmobinfo(1, FlyingUnderling), FlyingUnderling, 20); + + // Spawn all Generals and their personal guard + areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 62, 37, 76, 45, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 72, 41, "Guardian of Water", TopUnderling, 1, "#Moubootaur::OnBlue"); + _boostMe(.@gen, 6); + + areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 20, 50, 32, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnEarth"); + _boostMe(.@gen, 6); + + areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 68, 51, 80, 59, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 74, 55, "Guardian of Fire", TopUnderling, 1, "#Moubootaur::OnRed"); + _boostMe(.@gen, 6); + + areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 35, 23, 47, 40, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 42, 36, "Guardian of Wind", TopUnderling, 1, "#Moubootaur::OnGreen"); + _boostMe(.@gen, 6); + + areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 20, 20, 32, 30, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 25, 25, "Guardian of Sacred", TopUnderling, 1, "#Moubootaur::OnWhite"); + _boostMe(.@gen, 6); + + areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 33, 43, 46, 56, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 40, 50, "Guardian of Mana", TopUnderling, 1, "#Moubootaur::OnGolden"); + _boostMe(.@gen, 6); + + areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 64, 24, 77, 33, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 70, 30, "Guardian of Death", TopUnderling, 1, "#Moubootaur::OnPurple"); + _boostMe(.@gen, 6); + + areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, GunnerUnderling), GunnerUnderling, 2); + areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, HalberdUnderling), HalberdUnderling, 2); + areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, WizardUnderling), WizardUnderling, 2); + areamonster("001-15", 21, 33, 30, 47, strmobinfo(1, BansheeUnderling), BansheeUnderling, 2); + .@gen = monster("001-15", 25, 40, "Guardian of Evil", TopUnderling, 1, "#Moubootaur::OnBlack"); + _boostMe(.@gen, 6); + + end; + + + + + + + + + +// **MOUBOOTAUR HEARTBEAT** +OnTimer25000: +OnTimer20000: +OnTimer15000: + consolewarn("Warning, fail-safe mechanism triggered to Moubootaur (Awakened)."); +OnTimer10000: + .mana += any(1,2,3,5); // Recover mana + + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_DOT) { + maptimer2("001-15", 10, "#Moubootaur::OnDoT"); + } + + // TODO: Defeat Conditions + // TODO: Work in Progress + + + // Maybe we need to advance the turn + .tbet += 1; + if (!(.tbet % 9)) { + // Reset turn cycle (90s) and advance turn counter + .tbet=0; + .turn+=1; + + // Change map mode + if ($@ML_SHOWDOWN >= 3) + MapMode(); + + // Spawn the air corps + if ($@ML_SHOWDOWN >= 2) { + for (.@i=0; .@i<5; .@i++) { + .@m=areamonster("001-15", 49, 30, 60, 42, strmobinfo(1, FlyingUnderling), FlyingUnderling, 1); + _boostMe(.@m); // Will boost according to showdown state + } + } + // TODO: Tally Battlegrounds results + } + + initnpctimer; + end; + + + + + + + + + + + +// Scope: MLDIE_ +// DeathHandler( end = false , dead = true ) +public function DeathHandler { + // First death, handle it! + debugmes "Change Class from %d to %d", Class, Skelli; + if (Class != Skelli) { + MLDIE_Class = Class; + jobchange Skelli; + } + // Save your equipment selection for a quick equip() after unstoring + // (Handled by ReviveHandler) + for ( .@i=EQI_HEAD_TOP ; .@i<=EQI_SHADOW_ACC_L ; .@i++ ) { + array_push( MLDIE_Eqp, getequipid(.@i) ); + } + + // All your items go away to Special Storage + charcommand("@storeall 6"); + // All your money goes away to Special Storage + MLDIE_Zeny += Zeny; + Zeny = 0; + + // Permanent Debuff is handled by hub functions + + // TODO: Death NPC which recovers all your items + // (Do we have bindings for unstoring? We might need a new C-level function) + // + // (Also, we need to check if Death NPC already exists) + // (And this check must be independent on your Class) + // (DeathNPC should call ReviveHandler if Class is Skelli or ...?) + // (Also, we're failing to revive you here D:) + + // TODO: Check if DeathNpc already exists + // TODO: If not and getmap is 001-15, create it + // TODO: Else, create it close to a random player in map + // TODO: If that fails, create in entrance + // TODO: We need a way to distinguish undead players from normal players? + // (Will we use Benjamin sprite? What npc_duplicate gives us to track?) + // NPC_GUARD_DEAD, NPC_INJURIED_GIRL, or can we make it dynamic? + + // Eliminate any key you may have and revive the trupe + // No state check is necessary; the keys should not be duplicated + if (countitem(MKeyWater)) { + delitem MKeyWater, 1; + .@gen = monster("001-15", 72, 41, "Guardian of Water", TopUnderling, 1, "#Moubootaur::OnBlue"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyEarth)) { + delitem MKeyEarth, 1; + .@gen = monster("001-15", 25, 55, "Guardian of Earth", TopUnderling, 1, "#Moubootaur::OnEarth"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyFire)) { + delitem MKeyFire, 1; + .@gen = monster("001-15", 74, 55, "Guardian of Fire", TopUnderling, 1, "#Moubootaur::OnRed"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyWind)) { + delitem MKeyWind, 1; + .@gen = monster("001-15", 42, 36, "Guardian of Wind", TopUnderling, 1, "#Moubootaur::OnGreen"); + _boostMe(.@gen, 12); + } + if (countitem(MKeySacred)) { + delitem MKeySacred, 1; + .@gen = monster("001-15", 25, 25, "Guardian of Sacred", TopUnderling, 1, "#Moubootaur::OnWhite"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyMana)) { + delitem MKeyMana, 1; + .@gen = monster("001-15", 40, 50, "Guardian of Mana", TopUnderling, 1, "#Moubootaur::OnGolden"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyDeath)) { + delitem MKeyDeath, 1; + .@gen = monster("001-15", 70, 30, "Guardian of Death", TopUnderling, 1, "#Moubootaur::OnPurple"); + _boostMe(.@gen, 12); + } + if (countitem(MKeyEvil)) { + delitem MKeyEvil, 1; + .@gen = monster("001-15", 25, 40, "Guardian of Evil", TopUnderling, 1, "#Moubootaur::OnBlack"); + _boostMe(.@gen, 12); + } + + // Warp you to the respawn area if set (and revives you) + if (getarg(1, true)) { + debugmes "Now reviving ML victim..."; + atcommand("@alive"); + warp "001-15", rand2(84, 87), rand2(21, 26); + } + + // Prevent you from being slaughtered right after + // By granting you **4** seconds of invincibility! + // TODO: Check if this works correctly + sc_start SC_INVINCIBLE, rand(4000,4900), 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_NOICON; + + debugmes "We have now completed the Death Handler"; + + // We were instructed to terminate the interaction + if (getarg(0, false)) + end; + // Otherwise, return the control to caller + return; +} + + + + + + + + +// Undo everything DeathHandler did, and takes same arguments +public function ReviveHandler { + // TODO: "#Moubootaur"::PermanentDebuffs() if getmap() == 001-15 + // Restore your Class + jobchange MLDIE_Class; + Zeny += MLDIE_Zeny; + // Reset these variables + MLDIE_Class = 0; + MLDIE_Zeny = 0; + + // TODO: !! Unstash all your items !! + unstoreall(6); + /* This code does not work + sc_start SC_INVINCIBLE, 99999, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_NOICON; + openstorage(6, STORAGE_ACCESS_GET); + sc_end SC_INVINCIBLE; + sc_start SC_INVINCIBLEOFF, 1, 1; + */ + + // Re-equip your items + // Do not call equip() if < 1, it can be negative after all + for (.@i=0 ; .@i < getarraysize(MLDIE_Eqp) ; .@i++) { + if (MLDIE_Eqp[.@i] > 1) + equip(MLDIE_Eqp[.@i]); + } + deletearray MLDIE_Eqp; + + // TODO: Eliminate DeathNPC if it exists + + // We were instructed to terminate the interaction + if (getarg(0, false)) + end; + // Otherwise, return the control to caller + return; +} + +// Begin Introduction Cutscene (Event Start!!) +OnIntroCutscene: + // Reset Showdown Status (Clear Previous Attempts) + stopnpctimer; + $@ML_SHOWDOWN=1; + .turn = 0; + .tbet = 0; + .ML = 0; + disablenpc "#ML_EastWarp"; + disablenpc "#MLWA+"; + + // Create Andrei Sakar + .HERO=monster("001-15", 55, 47, "Andrei Sakar", ManaGhost, 1); + immortal(.HERO); + sc_start SC_STUN, 99990, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .HERO; + + // This is the prologue cutscene. It's kinda long so everyone has time to arrive. + sleep(1000); + unittalk(.HERO, "Oooohhhh. Someone actually came!"); + sleep(5000); + unittalk(.HERO, "Maybe I should wait a while to see if more people show up!"); + sleep(5000); + unittalk(.HERO, "Sorry, I haven't existed properly for a while."); + sleep(5000); + unittalk(.HERO, "But this is the Mana Plane section of the World Heart, so it matters not."); + sleep(10000); + unittalk(.HERO, "Anyway, my name is Andrei, and I used to be a legendary hero of this world."); + sleep(8000); + unittalk(.HERO, "Then, during a campaign to the Fortress, I noticed the lack of aid from Artis region."); + sleep(8000); + unittalk(.HERO, "While investigating Artis, Isbamuth minions killed me as a sacrifice to revive the Moubootaur."); + sleep(9000); + unittalk(.HERO, "I'm not entirely sure why. Mr. Saves should have prevented Talpans from allying with the Moubootaur and monsters from allying with Elli, but I assume exceptions were made. Likely a bug!"); + sleep(14000); + unittalk(.HERO, "Anyway, this means I'm unable to use the Soul Menhir and became a lost soul. Same will happen to all of you, shall you die here!"); + sleep(8000); + unittalk(.HERO, "But if you find your corpse back, or one of the Obelisks of Power, you might be able to return."); + sleep(7000); + // Official justification in case your body (Death NPC) is not available. + unittalk(.HERO, "Even if your body is consumed like mine was, the Obelisk of Power can still bring you back. However..."); + sleep(7500); + unittalk(.HERO, "Elli still has jurisdiction over Talpans. She probably vetoed my return, because I couldn't revive! Hahahah."); + sleep(7500); + /* TODO: Apply Elli's curse to all players */ + unittalk(.HERO, "Anyway! The Moubootaur is in the area ahead plotting something, but you'll need to find a way to enable the warp I'm standing on first."); + sleep(10000); + unittalk(.HERO, "I'll enable the warp to the west so you can explore! Good luck, adventurers!"); + sleep(2000); + enablenpc "#MLWA+"; + + // And now that everything is ready and done, start the timer and set turn 1 + .turn = 1; + initnpctimer; + sc_end SC_STUN, .HERO; + goto OnFirstStage; + +OnInit: + .warpsOnline = true; + .mapMode = 0; + + .ML=0; + .HERO=0; + .maxhp = 1000000; // 1,000,000 HP (used by scripts) + .mana = 0; // More mana = more likely to cast skills + + .memohp = 999; // Memorand HP, controls spawns + .start_time = gettimetick(2); // Controls Death Touch + .tbet = 0; // Internal counter for turn system + .turn = 0; // Actual turn counter + + .dmhp1 = 750000; // The "default" max HP + .dmhp2 = 250000; // The "extra" max HP + + // Platforms Cardinal Sequence is + // Central, Clock, Wind, Fire, Plague, Cold, Blood, Disease, Curse + // XXX: Moubootaur goes from [1] to [8] + setarray $@MLFX, 52, 44, 43, 78, 64, 31, 22, 27, 74, 44, 43, 78, 64, 31, 22, 27, 74; + setarray $@MLFY, 49, 55, 24, 56, 44, 20, 51, 34, 31, 55, 24, 56, 44, 20, 51, 34, 31; + end; +} + + + +001-15,55,47,0 script #ML_NorthWarp NPC_ML_CIRCLE,0,0,{ + end; +OnTouch: + if ($@ML_SHOWDOWN < 2) goto L_InitCheck; + slide 54, 29; + + // Kill any stray timer too in the Central Platform + // It's overkill and not really necessary, BUT better safe than sorry + deltimer("#DungeonCore::OnClocked"); + deltimer("#DungeonCore::OnWindy"); + deltimer("#DungeonCore::OnHeat"); + deltimer("#DungeonCore::OnLeech"); + deltimer("#DungeonCore::OnFrost"); + deltimer("#DungeonCore::OnBleed"); + deltimer("#DungeonCore::OnSick"); + deltimer("#DungeonCore::OnCurse"); + end; + +L_InitCheck: + // First way to trigger: All keys in a single player inventory + if (countitem(MKeyWater) == 1 && + countitem(MKeyEarth) == 1 && + countitem(MKeyDeath) == 1 && + countitem(MKeyFire) == 1 && + countitem(MKeyWind) == 1 && + countitem(MKeyMana) == 1 && + countitem(MKeyEvil) == 1 && + countitem(MKeySacred) == 1) goto L_InitDel; + // Second (prefered) way to trigger: All keys in the magic circle + .@i=0; + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWater, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEarth, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyDeath, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyFire, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWind, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyMana, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEvil, false)); + .@i+=min(1,getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeySacred,false)); + if (.@i == 8) goto L_InitOk; + dispbottom l("This circle doesn't work right now. Maybe there's a clue on what is needed?"); + end; + +L_InitDel: + delitem MKeySacred, 1; + delitem MKeyWater, 1; + delitem MKeyEarth, 1; + delitem MKeyDeath, 1; + delitem MKeyFire, 1; + delitem MKeyWind, 1; + delitem MKeyEvil, 1; + delitem MKeyMana, 1; + // FALLTHROUGH +L_InitOk: + $@ML_SHOWDOWN=2; + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWater, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEarth, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyDeath, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyFire, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyWind, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyMana, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeyEvil, true); + getareadropitem("001-15", .x-3, .y-3, .x+3, .y+3, MKeySacred,true); + // Send the hero to the warp circle + .@hero = getvariableofnpc(.HERO, "#Moubootaur"); + unitwalk(.@hero, 55, 47); + // Mark the activation + sleep2(50); + specialeffect(312, AREA, .name$); + // Enable the warp and NPC dialogue + sleep2(300); + enablenpc "#ML_EastWarp"; + unittalk(getvariableofnpc(.HERO, "#Moubootaur"), "Good job! Chop chop!"); + // Warp NPC and activator to the platform + sleep2(250); + unitwarp(.@hero, "001-15", 55, 29); + sc_start SC_STUN, 86400000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@hero; + // TODO: Begin 2nd Stage Cutscene + goto OnTouch; + end; +OnInit: + end; +} + + + +001-15,54,28,0 script #ML_NorthExit NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($@ML_SHOWDOWN < 2) end; + slide 55, 48; + end; +L_InitCheck: + end; +OnInit: + end; +} + + + +001-15,60,53,0 script #ML_EastWarp NPC_SUMMONING_CIRC,0,0,{ + end; +OnTouch: + // Verify if this portal is active (it should, however, be disabled!) + if (!getvariableofnpc(.turn, "#Moubootaur")) + end; + if ($@ML_SHOWDOWN < 2) goto L_Inactive; + + // Assuming nothing is wrong, then do the warp + slide 85, 57; + end; + +// Report the error to the user & console +L_Inactive: + dispbottom l("There's nothing of interest in this direction... for now."); + consolewarn("ML Showdown: Eastern Portal was visible while disabled."); + end; +OnInit: + end; +} + + +// You can always leave the battleground area, there's no restriction whatsoever +001-15,85,58,0 script #ML_EastExit NPC_SUMMONING_CIRC,0,0,{ + end; +OnTouch: + slide 60, 54; + end; +OnInit: + end; +} + + + +/* Miller system (otherwise, you can't reach the switches & platforms) */ +001-15,51,49,0 script #MLWA+ NPC_FANCY_CIRCLE,0,0,{ + end; +OnTouch: + // TODO: Verify if portalling is active in the Showdown + // Probably via a public function in #Moubootaur + if (false) { + dispbottom l("Oh no! The Moubootaur has disabled this portal!"); + end; + } + if (!$@ML_SHOWDOWN) + end; + if (!getvariableofnpc(.turn, "#Moubootaur")) + end; + + // Dungeon Climate System (stop effect, start next) + // TODO: Disable previous Climate System timer, before @state is changed + switch (miller_rand(@state, getcharid(0), 9)) { + case 1: + deltimer("#DungeonCore::OnClocked"); break; + case 2: + deltimer("#DungeonCore::OnWindy"); break; + case 3: + deltimer("#DungeonCore::OnHeat"); break; + case 4: + deltimer("#DungeonCore::OnLeech"); break; + case 5: + deltimer("#DungeonCore::OnFrost"); break; + case 6: + deltimer("#DungeonCore::OnBleed"); break; + case 7: + deltimer("#DungeonCore::OnSick"); break; + case 8: + deltimer("#DungeonCore::OnCurse"); break; + default: + // Case 0 has no special effects whatsoever + break; + } + + // All portals are the same, anyway + if (compare(strnpcinfo(2), "+")) + @state += 3; + else + @state -= 2; + .@index = miller_rand(@state, getcharid(0), 9); + slide $@MLFX[.@index], $@MLFY[.@index]; + + // Dungeon Climate System (start effect, stop previous) + switch (.@index) { + case 1: + addtimer(10, "#DungeonCore::OnClocked"); break; + case 2: + addtimer(10, "#DungeonCore::OnWindy"); break; + case 3: + addtimer(10, "#DungeonCore::OnHeat"); break; + case 4: + addtimer(10, "#DungeonCore::OnLeech"); break; + case 5: + addtimer(10, "#DungeonCore::OnFrost"); break; + case 6: + addtimer(10, "#DungeonCore::OnBleed"); break; + case 7: + addtimer(10, "#DungeonCore::OnSick"); break; + case 8: + addtimer(10, "#DungeonCore::OnCurse"); break; + default: + // Case 0 has no special effects whatsoever + break; + } + end; +} +//001-15,28,136,0 duplicate(#MLWA+) #MLWA- NPC_FANCY_CIRCLE,0,0 +// Clock, Wind, Fire, Plague, Cold, Blood, Disease, Curse +// B, C, D, E, F, G, H, I +001-15,34,44,0 duplicate(#MLWA+) #MLWB+ NPC_NO_SPRITE,0,0 +001-15,45,55,0 duplicate(#MLWA+) #MLWB- NPC_NO_SPRITE,0,0 +001-15,36,38,0 duplicate(#MLWA+) #MLWC+ NPC_FANCY_CIRCLE,0,0 +001-15,44,24,0 duplicate(#MLWA+) #MLWC- NPC_FANCY_CIRCLE,0,0 +001-15,69,53,0 duplicate(#MLWA+) #MLWD+ NPC_FANCY_CIRCLE,0,0 +001-15,78,55,0 duplicate(#MLWA+) #MLWD- NPC_FANCY_CIRCLE,0,0 +001-15,75,38,0 duplicate(#MLWA+) #MLWE+ NPC_FANCY_CIRCLE,0,0 +001-15,63,44,0 duplicate(#MLWA+) #MLWE- NPC_FANCY_CIRCLE,0,0 +001-15,21,24,0 duplicate(#MLWA+) #MLWF+ NPC_FANCY_CIRCLE,0,0 +001-15,31,22,0 duplicate(#MLWA+) #MLWF- NPC_FANCY_CIRCLE,0,0 +001-15,31,59,0 duplicate(#MLWA+) #MLWG+ NPC_FANCY_CIRCLE,0,0 +001-15,21,51,0 duplicate(#MLWA+) #MLWG- NPC_FANCY_CIRCLE,0,0 +001-15,22,43,0 duplicate(#MLWA+) #MLWH+ NPC_FANCY_CIRCLE,0,0 +001-15,28,34,0 duplicate(#MLWA+) #MLWH- NPC_FANCY_CIRCLE,0,0 +001-15,65,26,0 duplicate(#MLWA+) #MLWI+ NPC_FANCY_CIRCLE,0,0 +001-15,75,31,0 duplicate(#MLWA+) #MLWI- NPC_FANCY_CIRCLE,0,0 + + + + + + + + + + + + + + + + + + +// Main Room Traps, only against players +001-15,0,0,0 script #ML_Trap01 NPC_TRAP_ONLINE,0,0,{ + end; + +OnTouch: +OnTouchNPC: + WorldHeartTrap(); + sleep(500); // Wait 500ms for animation + setnpcdisplay .name$, NPC_TRAP_ONLINE; + // Move the trap away after it disarms (up to 30 attempts) + +OnInit: + .@e=0; + do { + if (.@e >= 30) + break; + .@x = rand2(48, 60); + .@y = rand2(30, 44); + .@e+=1; + } while (!checknpccell("001-15", .@x, .@y, cell_chkpass)); + movenpc .name$, .@x, .@y; + end; +} + +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap02 NPC_TRAP_ONLINE,0,0 +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap03 NPC_TRAP_ONLINE,0,0 +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap04 NPC_TRAP_ONLINE,0,0 +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap05 NPC_TRAP_ONLINE,0,0 +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap06 NPC_TRAP_ONLINE,0,0 +001-15,0,0,0 duplicate(#ML_Trap01) #ML_Trap07 NPC_TRAP_ONLINE,0,0 + + + + + +// Convenience Shop NPC +// TODO: enablenpc; and disablenpc; depending on state? Move to (58,50)? +001-15,50,47,0 script Tiki NPC_TIKI,{ + mesn; + mes l("Hey hey, my name is Micksha, and I'm Arthur's grandfather!"); + mes l("I retired to my own laboratory and sell strange drinks on the side!"); + mes l("The Mirror Lake here is twisty, so I can do things to you... for a price!"); + next; + // Pricing + .@price = 15; + if (!MOUBOOTAUR_WINNER) .@price += 150; + if (!EPISODE_WINNER) .@price += 150; + if (islegendary()) .@price += 65; + mesc l("Special Price for You! %s GP!", fnum(.@price)), 1; + // Friendly Advise, "free" of charge + if (any(true, false)) + mesc l("* \"Make sure to be well stocked and stashed for the fights! Preparation is the utmost for victory!\" - The Micksha"); + else + mesc l("* \"Prepare, go and destroy! Don't tell your parents I've said that!\" - The Micksha"); + + // Require the money NOW + if (Zeny >= .@price) + Zeny -= .@price; + else if (BankVault >= .@price) + BankVault -= .@price; + else + close; + + // Main Menu + select + l("I don't need anything!"), + l("Withdraw Money"), + rif($@ML_SHOWDOWN < 2, l("Open Storage")), + l("Nursery services"), + l("Acquire goods"); + switch (@menu) { + case 2: + Banking(); + break; + case 3: + Banker("Micksha", "Impregnable Fortress", 999999); + break; + case 4: + Nurse("Micksha", 10, 6); + break; + case 5: + closeclientdialog; + openshop(.name$); + break; + default: + Zeny += .@price; + break; + } + close; + +// x2( Item , Multiplier=2x ) +function x2 { + return getiteminfo(getarg(0), ITEMINFO_BUYPRICE) * getarg(1, 2); +} + +OnInit: + .sex = G_MALE; + .distance = 3; + sellitem InsuranceContract, x2(InsuranceContract, 1); + sellitem ChamomileTea, x2(ChamomileTea, 4); // Mana F + sellitem YerbaMate, x2(YerbaMate, 1); // Mana S + sellitem LemonCake, x2(LemonCake, 3); // Homun F + sellitem WhiteCake, x2(WhiteCake, 5); // Homun A + sellitem PiberriesInfusion, x2(PiberriesInfusion, 3); + sellitem FatesPotion, x2(FatesPotion, 3); + sellitem ClothoLiquor, x2(ClothoLiquor, 3); + sellitem LachesisBrew, x2(LachesisBrew, 3); + sellitem AtroposMixture, x2(AtroposMixture, 3); + sellitem TrainingAmmoBox, x2(TrainingAmmoBox); // Arrow E + sellitem IronAmmoBox, x2(IronAmmoBox); // Arrow C + sellitem BigBulletSack, x2(BigBulletSack, 1); // Bullets MAX + sellitem StatusResetPotion, 4999; + sellitem MercBoxA, 9999; + sellitem BottleOfSewerWater, 9999; + sellitem BlueberryCake, 9999; + sellitem SmokeGrenade, 9999; + sellitem ScentGrenade, 9999; + sellitem HerbalTea, 9999; + sellitem EmptyBox, 9999; + sellitem Coffee, 9999; + sellitem IcedBottle, 9999; + sellitem PurificationPotion, 9999; + sellitem ScrollSCave, 9999; + sellitem CommonCarp, 9999; + sellitem FishingRod, 9999; + sellitem TreasureKey, 9999; + sellitem SaxsoKey, 9999; + sellitem Flour, 9999; + sellitem StrangeCoin, 9999; + sellitem GuildCoin, 9999; + sellitem HeroCoin, 9999; + sellitem Lifestone, 9999; + sellitem Quill, 9999; + sellitem EverburnPowder, 9999; + sellitem EarthPowder, 9999; + sellitem WoodenLog, 9999; + sellitem MinerKnife, 9999; + sellitem ShortBow, 9999; + sellitem Lockpicks, 9999; + sellitem IronShovel, 9999; + sellitem EmptyBottle, 12999; + sellitem ArcmageBoxset, 14999; + sellitem LeatherQuiver, 19999; + sellitem HomunResetPotion, 19999; + sellitem DeathPotion, 19999; + sellitem NymphPoison, 19999; + sellitem BrokenWarpCrystal, 19999; + sellitem SacredImmortalityPotion, 29999; + sellitem GoldenApple, 49999; + sellitem ElixirOfLife, 99999; + sellitem MagicApple, 199999; + sellitem LegendaryTortuga, 1499999; + sellitem PiouEgg, 3499999; + sellitem Skypiercer, 9999999; + sellitem BlackyCatFix, INT_MAX/2; + end; +} + +function script ML_MobKill { + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_PAY2KILL) { + // Not a target of the map-wide effect + if (getmap() != "001-15") + return; + // Price is 10 GP per monster level (~3000 gp for Tortuga) + .@price = strmobinfo(3,killedrid) * 10; + .@price += rand2(JobLevel); + .@debit = 0; + // Pay the money or set .@debit + if (Zeny >= .@price) + Zeny -= .@price; + else + .@debit = .@price - Zeny; + // For each unpaid GP, you lose 1% HP + if (.@debit) { + Zeny = 0; + percentheal -(.@debit), -(.@debit); + } + } + return; +} + + diff --git a/npc/001-16/_import.txt b/npc/001-16/_import.txt index f3db9f4ec..6edab71ba 100644 --- a/npc/001-16/_import.txt +++ b/npc/001-16/_import.txt @@ -1,3 +1,4 @@ // Map 001-16: Existential Limbo // This file is generated automatically. All manually added changes will be removed when running the Converter. "npc/001-16/_mobs.txt", +"npc/001-16/obelisk.c", diff --git a/npc/001-16/_mobs.txt b/npc/001-16/_mobs.txt index a55fb6ddc..2cebe5e0e 100644 --- a/npc/001-16/_mobs.txt +++ b/npc/001-16/_mobs.txt @@ -1,4 +1,9 @@ // This file is generated automatically. All manually added changes will be removed when running the Converter. // Map 001-16: Existential Limbo mobs -001-16,169,154,120,135 monster Cave Maggot 1027,44,35000,300000 -001-16,136,148,115,129 monster Black Scorpion 1074,23,35000,300000 +001-16,169,154,120,135 monster Cave Maggot 1027,77,35000,300000 +001-16,136,148,115,129 monster Black Scorpion 1074,36,35000,300000 +001-16,145,164,125,129 monster Magic Goblin 1052,23,35000,300000 +001-16,169,147,125,129 monster Black Cat 1192,23,35000,300000 +001-16,165,229,141,80 monster Maverick 1452,12,35000,300000 +001-16,202,69,94,58 monster Maverick 1452,12,35000,300000 +001-16,158,172,141,120 monster Shadow Tortuga 1429,4,35000,300000 diff --git a/npc/001-16/obelisk.c b/npc/001-16/obelisk.c new file mode 100644 index 000000000..afcdef451 --- /dev/null +++ b/npc/001-16/obelisk.c @@ -0,0 +1,92 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Moubootaur Legends :: Final Showdown - Post Mortem Realm + +001-16 mapflag zone FinalMMO + +// This Obelisk will warp you back to the Showdown. Are you sure? 89 48 +001-16,89,48,0 script Obelisk#MLDL1 NPC_FINAL_POINT,{ + mesn; + mesc l("Lost soul who wanders the mazes of life and death, the path lies herein ahead, but only those of stout heart may return."); + mesc l("This is the challenge which Mr. Saves has determined upon all those whom die outside the Mana Source's Jurisdiction."); + .@ans = ASK_NO; + if ($@ML_SHOWDOWN) { + next; + mesc l("Do you wish to return to the Moubootaur Showdown as an undead?"), 1; + next; + .@ans = askyesno(); + } + closeclientdialog; + if (.@ans == ASK_YES) + "#Moubootaur"::DeathHandler(true); + close; + +OnInit: + .distance=2; + end; +} + +// This Obelisk will warp you to a random town. Are you sure? 215 162 +001-16,215,162,0 script Obelisk#MLDL2 NPC_FINAL_POINT,{ + mesc l("This Obelisk will warp you to a random town. Are you sure?"), 1; + .@ans = askyesno(); + closeclientdialog; + if (.@ans == ASK_YES) { + .@dest = any(TP_FROST, TP_HALIN, TP_LOF, TP_FORT, TP_LILIT, TP_CANDOR, TP_ARTIS); // TP_BOSSR? TP_NIVAL? + .@i = array_find($@LOCMASTER_TP, .@dest); + warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i]; + EnterTown($@LOCMASTER_LOC$[.@i], true); + "#Moubootaur"::ReviveHandler(true); + } + close; + +OnInit: + .distance=2; + end; +} + +// This Obelist will warp you to the Optional Dungeon. Are you sure? 286 287 +// if you can't, then the Obelisk does not react +001-16,286,287,0 script Obelisk#MLDL3 NPC_FINAL_POINT,{ + if (!MK_WINNER) { + npctalk3 l("The Obelisk doesn't react. You likely haven't fulfilled the requisites to use it."); + end; + } + mesc l("This Obelisk will warp you to the Optional Dungeon. Are you sure?"), 1; + .@ans = askyesno(); + closeclientdialog; + if (.@ans == ASK_YES) { + .@dest = TP_HEART; + .@i = array_find($@LOCMASTER_TP, .@dest); + warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i]; + EnterTown($@LOCMASTER_LOC$[.@i], true); + "#Moubootaur"::ReviveHandler(true); + } + close; + +OnInit: + .distance=2; + end; +} + +// This Obelisk will return you to your savepoint and heal you fully. Are you sure? +// 108 216 +001-16,108,216,0 script Obelisk#MLDL4 NPC_FINAL_POINT,{ + mesc l("This Obelisk will return you to your savepoint and heal you fully. Are you sure?"), 1; + .@ans = askyesno(); + closeclientdialog; + if (.@ans == ASK_YES) { + teleporthome(); + "#Moubootaur"::ReviveHandler(false); + percentheal 100, 100; + SC_Bonus(180, SC_KAIZEL, 1); // Revives with 1% HP if you die + } + end; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/003-6/tamiloc.txt b/npc/003-6/tamiloc.txt index cb2e11f1c..6c08fa2df 100644 --- a/npc/003-6/tamiloc.txt +++ b/npc/003-6/tamiloc.txt @@ -85,7 +85,8 @@ OnSaviorCall: select l("Nooo! I want my MP back!"), rif(is_gm() || REBIRTH >= 5, l("I want to change my Race!")), - l("I want to change my hair color."); + l("I want to change my hair color."), + rif(FINAL_WINNER && REBIRTH >= 5, l("Please kill me and make me a lame undead!")); mes ""; switch (@menu) { case 2: @@ -94,6 +95,9 @@ OnSaviorCall: case 3: Sp -= 200; BarberChangeColor(); break; + case 4: + Sp -= 200; jobchange Skelli; + break; } close; diff --git a/npc/functions/hub.txt b/npc/functions/hub.txt index 5db919aa1..ba0c0b5c5 100644 --- a/npc/functions/hub.txt +++ b/npc/functions/hub.txt @@ -119,7 +119,7 @@ function script HUB_Logout { if (@grace) { // Grace is upon you (ie. script death) @grace=false; - } else if ((.@zone$ == "MMO" || .@zone$ == "SuperMMO") && ABSOLVE_CNT <= 3) { + } else if ((.@zone$ == "MMO" || .@zone$ == "SuperMMO" || .@zone$ == "FinalMMO") && ABSOLVE_CNT <= 3) { // Absolve (limited attempts) ABSOLVE_CNT+=1; dispbottom l("This is a special map so your death is not counted."); @@ -288,6 +288,10 @@ function script HUB_Logout { if (compare(.@mapa$, "fyrb")) { doevent "sBossRaid::OnDie"; } + // Died or logged out at Final Showdown event + if (.@mapa$ == "001-15") { + "#Moubootaur"::DeathHandler(false, .@dead); + } // Died or logged out during Yeti King Fight if (getq(HurnscaldQuest_Celestia) > 1) setq HurnscaldQuest_Celestia, 1; @@ -561,6 +565,12 @@ function script HUB_SkillInvoke { if (checkidle() > 180) return; + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOMAGIC) { + if (getmap() == "001-15") + return; + } + // Record to database skillInvoke[@skillId] = skillInvoke[@skillId] + 1; callfunc "FYE_Olympics_SK"; @@ -1418,6 +1428,7 @@ function script HUB_PCBonus { if (PCBONUS & PCB_LEGENDARY) { bonus bDefRatioAtkRace, RC_Legendary; } + /* Passive Skills */ bonus2 bSubRace, RC_Legendary, getskilllv(AL_DP)-10; if (getq(LoFQuest_Barbara) >= 4 || getq(General_Narrator) >= 19) @@ -1430,6 +1441,44 @@ function script HUB_PCBonus { /* Stat reassignment */ bonus bMaxHP, (readparam2(bVit)*2)-(readparam2(bAgi)*2); + /* Nullify all assigned stats, and general debuffs */ + if (Class == Skelli) { + bonus bStr, 1-readparam(bStr); + bonus bAgi, 1-readparam(bAgi); + bonus bVit, 1-readparam(bVit); + bonus bDex, 1-readparam(bDex); + bonus bInt, 1-readparam(bInt); + bonus bLuk, 1-readparam(bLuk); + bonus bAllStats, -REBIRTH; + // Now comes SEVERAL debuffs for this specific class + bonus bMaxHPrate, -25; + bonus bMaxSPrate, -25; + bonus bAtkRate, -25; + bonus bDefRate, -25; + bonus bDef2Rate, -25; + bonus bMatkRate, 10; // MATK actually gets a buff for... reasons? + bonus bMdefRate, -25; + bonus bMdef2Rate, -25; + bonus bHitRate, -25; + bonus bCriticalRate, -25; + bonus bFleeRate, -25; + bonus bFlee2Rate, 10; // Perfect Evasion actually gets a buff for... dunno + bonus bSpeedAddRate, 1; // So... Moving speed bonus/debuff? + bonus bAspdRate, -25; + bonus bHPrecovRate, -25; + bonus bSPrecovRate, -25; + bonus bAddItemHealRate, -25; + // Shuffle your defense to work better against ranged attacks + // But make you weaker to traps, fixed-damage and magic + bonus bMagicAtkDef, -15; + bonus bMiscAtkDef, -15; + bonus bLongAtkDef, 20; + // Also, cannot be knocked back for... reasons + bonus bNoKnockback, 1; + bonus bIntravision, 1; + bonus bRestartFullRecover, 75; // Is 75 ignored, or revive with 75% HP + } + // Double hack fix if (isequipped(ExplosiveArrow)) { if (getiteminfo(getequipid(EQI_HAND_R), ITEMINFO_SUBTYPE) != W_BOW) diff --git a/npc/functions/mobpoint.txt b/npc/functions/mobpoint.txt index 243cfed1d..2456f7110 100644 --- a/npc/functions/mobpoint.txt +++ b/npc/functions/mobpoint.txt @@ -124,6 +124,7 @@ OnNPCKillEvent: callfunc "GeminiKill"; callfunc "SK_drops"; callfunc "BonusEXP"; + callfunc "ML_MobKill"; // Other updates $@MK_TRIGGERED=true; diff --git a/npc/functions/util.txt b/npc/functions/util.txt index 8f0405faa..345628ade 100644 --- a/npc/functions/util.txt +++ b/npc/functions/util.txt @@ -1160,11 +1160,19 @@ function script POL_LocToTP { // Upon entering a town -// EnterTown( LocName ) +// EnterTown( LocName{, force=false} ) function script EnterTown { // Fill variable .@v$=getarg(0); + // If you're dead and did not want to, forceful correction + // (MLDIE_Class however CAN be zero, check isn't perfect) + if (Class == Skellie && MLDIE_Class && !getarg(1, false)) { + Exception(sprintf("EnterTown: Player %d tried to enter %s but had no soul!", getcharid(0), .@v$), RB_DEBUGMES|RB_IRCBROADCAST); + warp "001-16", 90, 45; + return; + } + // Validade variable, see npc/config/location.txt first if (array_find($@LOCMASTER_LOC$, .@v$) < 0) return Exception("Invalid location passed to EnterTown: "+.@v$); @@ -1239,7 +1247,7 @@ function script teleporthome { } .@i=array_find($@LOCMASTER_MAP$, getmap()); if (.@i >= 0) - EnterTown($@LOCMASTER_LOC$[.@i]); + EnterTown($@LOCMASTER_LOC$[.@i], true); else consolewarn("[ERROR] Invalid Town Map for Time Flask: %s", getmap()); return; diff --git a/npc/items/inc_sc_bonus.txt b/npc/items/inc_sc_bonus.txt index b0680b680..a07a084c6 100644 --- a/npc/items/inc_sc_bonus.txt +++ b/npc/items/inc_sc_bonus.txt @@ -4,7 +4,7 @@ // Description: // Applies effects for INC_* (STR doesn't exist) // Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE -// Doesn't works: SC_STRUP +// Doesn't works: SC_STRUP -> SC_STR_SCROLL instead // Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE /// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken) // PS. SC_CRITICALPERCENT (Crit) SC_FOOD_CRITICALSUCCESSVALUE (Crit) SC_STRIKING (Crit , ATK) @@ -24,6 +24,13 @@ function script SC_Bonus { if (.@delay <= 0) return false; + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOBOOST) { + // Semi Hardcoded-way to find buffs + if (getmap() == "001-15" && .@min > 0 && array_find($@sc_buffs, .@type)) + return false; + } + // Get the bonus value if (.@min != .@max) .@bonus=rand2(.@min, .@max); @@ -62,11 +69,22 @@ function script SC_Bonus { - script inc_sc_bonus -1,{ OnUse: + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOBOOST) { + if (getmap() == "001-15") + end; + } + SC_Bonus(@delay, @type, @min, @max); @delay=0; @type=0; @min=0; @max=0; end; + +// Document all buffs +OnInit: + setarray $@sc_buffs, SC_ATTHASTE_POTION1, SC_INCHIT, SC_KAIZEL, SC_INCATKRATE, SC_ATTHASTE_POTION1, SC_PLUSATTACKPOWER, SC_WALKSPEED, SC_INCLUK, SC_INCDEX, SC_INCAGI, SC_INCVIT, SC_INCINT, SC_STR_SCROLL, SC_CASH_DEATHPENALTY, SC_INCFLEE, SC_FOOD_CRITICALSUCCESSVALUE, SC_CRITICALPERCENT, SC_INCMSPRATE, SC_INCMHPRATE, SC_INCMSP, SC_INCMHP, SC_STRUP, SC_STRIKING, SC_INCHITRATE, SC_INCATKRATE, SC_INCFLEERATE, SC_INCDEFRATE, SC_ATTHASTE_POTION2, SC_OVERLAPEXPUP; + end; } diff --git a/npc/items/legacy_heal.txt b/npc/items/legacy_heal.txt index 2beb820d3..d3d3b4f8a 100644 --- a/npc/items/legacy_heal.txt +++ b/npc/items/legacy_heal.txt @@ -20,6 +20,12 @@ function script ItHeal { .@min=getarg(1, @min); .@max=getarg(2, (@max ? @max : .@min)); + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOHEAL) { + if (getmap() == "001-15") + return; + } + if (.@delay <= 0) { Exception("Invalid legacy healing item, deleting without healing effect."); end; diff --git a/npc/items/rand_mp_heal.txt b/npc/items/rand_mp_heal.txt index 4b6a5ca11..5b9365d82 100644 --- a/npc/items/rand_mp_heal.txt +++ b/npc/items/rand_mp_heal.txt @@ -18,6 +18,12 @@ function script MPHeal { end; } + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOMANA) { + if (getmap() == "001-15") + return; + } + // +1 max MP per 3 Int, +1 min MP per 5 int. // Original max MP will be respected @max = min(@max*2, @min+(readparam2(bInt)/5)); diff --git a/npc/items/rand_sc_heal.txt b/npc/items/rand_sc_heal.txt index 41ea3b3a7..454c20887 100644 --- a/npc/items/rand_sc_heal.txt +++ b/npc/items/rand_sc_heal.txt @@ -37,6 +37,12 @@ function script ItHeal2 { .@rarity=getarg(1); .@delay=getarg(2, 0); + // Moubootaur Showdown Effects + if (getvariableofnpc(.mapMode, "#Moubootaur") == ML_MAPMODE_NOHEAL) { + if (getmap() == "001-15") + return 0; + } + // Calculate healing value in % .@min=.@rarity * ((.@type*1) + 1); .@max=.@rarity * ((.@type*2) + 1); |