// TMW2 Script
// Notes: The Monster King will retake the town every
// OnTue0000
// (Tuesday, midnight)
// Only the world hero may begin a siege.
// Only one siege per day is allowed
// Writes to MK Temp Var. This variable will unlock the castle gates
// Then the inner gates, and finally, will be a co-requisite to the floors
// Variables:
// $FORTRESS_STATE = int
// 0 - Locked
// 1 - Unlocked
// $@FORTRESS_STATUE = bitmask
// 1,2,4,8,16 - broken statues
// 1024 - Fortress Gate
// 2048 - Siege started
// 4096 - Governor was spawned
// $@FORT_BLACKLIST = int array
// Char ID which already raided this week
// MAPFLAGS
025-1 mapflag zone SuperMMO
025-3 mapflag zone SuperMMO
026-0 mapflag zone SuperMMO
026-1 mapflag zone SuperMMO
026-2 mapflag zone SuperMMO
/////////////////////////////////////////
// FUNCTIONS
// FTCleanup(status)
function script FTCleanup {
// Kills whatever called this function if time is wrong
if ($GAME_STORYLINE < 3)
end;
// Reset variables
$FORTRESS_STATE=getarg(0);
$@FORTRESS_STATUE=0;
// Enable the Magic Statues
enablenpc "Magic Statue#1";
enablenpc "Magic Statue#2";
enablenpc "Magic Statue#4";
enablenpc "Magic Statue#8";
enablenpc "Magic Statue#16";
// Kill stray monsters (including town gate)
killmonsterall("025-1");
// Main gate
if ($FORTRESS_STATE) {
hideonnpc "Gate#F";
enablenpc "Yuko";
enablenpc "Ihclot";
enablenpc "Salohnic";
enablenpc "Xovilam";
enablenpc "Drahcir";
enablenpc "Selim";
enablenpc "Rum Barrel";
enablenpc "Commander Povo";
enablenpc "Commander Cadis";
enablenpc "Phoenix Rebirth";
enablenpc "Anin The Traveler";
enablenpc "The Impregnable Fortress";
enablenpc "#025-3_100_179";
donpcevent "#025-1_99_112::OnDisable";
deletearray $@FORT_BLACKLIST;
} else {
hideoffnpc "Gate#F";
disablenpc "Yuko";
disablenpc "Ihclot";
disablenpc "Salohnic";
disablenpc "Xovilam";
disablenpc "Drahcir";
disablenpc "Selim";
disablenpc "Rum Barrel";
disablenpc "Commander Povo";
disablenpc "Commander Cadis";
disablenpc "Phoenix Rebirth";
disablenpc "Anin The Traveler";
disablenpc "The Impregnable Fortress";
disablenpc "#025-3_100_179";
donpcevent "#025-1_99_112::OnEnable";
mapwarp("025-1", "025-2", 100, 27);
}
enablenpc "#025-1_100_123";
return;
}
// FTStatue(id)
function script FTStatue {
.@id=getarg(0);
mesn l("Magic Statue");
mes l("There is a reading: The Mana Source. The Moubootaur. The Monster King.");
mes l("The war. The blood. The inspiration. The mana. The world. The defiance.");
mes l("The guard. The heir. The originals. The races. The later. The seal.");
mes l("The fragments. The war. The Terranite. AEGIS MAGNA PROTECTIVE SCUTUM.");
next;
mesc l("It seems to be a defensive spell.");
if ($FORTRESS_STATE)
return false;
// Break the statues?
mesc l("Break the Statue?"), 1;
mesc l("* Will be stun you for 10 seconds!");
next;
if (askyesno() == ASK_YES) {
if ($@FORTRESS_STATUE & .@id)
return false;
sc_start SC_STUN, 10000, 1;
doevent("Gate#F::OnStatueBreach");
mapannounce("025-1", strcharinfo(0)+" has broken a statue!", bc_map);
$@FORTRESS_STATUE=$@FORTRESS_STATUE|.@id;
return true;
}
return false;
}
/////////////////////////////////////////
// NPC SCRIPTS
// Main gate - Also where the World Hero can begin the siege
025-1,99,112,0 script Gate#F NPC_NO_SPRITE,{
function spawnMob;
function spawnCore;
// Main Story block - WHAT
if ($GAME_STORYLINE < 3)
die();
// Still open
if ($FORTRESS_STATE)
end;
// Siege ongoing
if ($@FORTRESS_STATUE)
end;
// Only World Hero may interact
if (strcharinfo(0) != $MOST_HEROIC$) {
dispbottom l("I will not assault the Fortress Island by myself. Instead, I'll wait for %s.", $MOST_HEROIC$);
end;
}
// Hey, you can assault the town!
mesc ".:: "+l("THE FORTRESS ISLAND TOWN") + " ::.", 1;
mes l("Behind this gate, lies the Fortress Island Town.");
next;
mesc ".:: "+l("THE FORTRESS ISLAND TOWN") + " ::.", 1;
mesc l("Assault?"), 1;
mes l("* Ensure you and your team is ready and at their positions.");
next;
select
l("Not now."),
l("Information"),
l("Bring it on, we're ready!");
mes "";
if (@menu == 1) {
close;
} else if (@menu == 2) {
mesc l("* The siege resets at Tuesday 00:00");
mesc l("* Sieges increase in difficulty as they are won");
mesc l("* Unlike monster sieges, the difficulty does not fluctuates based on nº of players or their level!");
dnext;
mesc l("* The warp to this map will be suspended until the siege is finished.");
mesc l("* The town is liberated once the Monster Governor is slain.");
mesc l("* Monster Governor will only show up when the town magic shield is broken.");
mesc l("* Monster Governor gives 500,000 exp and 100,000 jexp to whoever deals most damage to it");
dnext;
mesc l("* All monsters in the town and Impregnable Fortress give +25%% EXP");
mesc l("* You cannot teleport in the town.");
mesc l("* This is a Cursed Lands map, watch out for MP during the fights as well");
mesc l("* The siege is lost if all players die.");
close;
}
// FIRE THE EVENT
hideonnpc "Gate#F";
disablenpc "#025-1_100_123";
initnpctimer;
// Reset variables
$@FORTRESS_STATUE=2048;
// Spawn the gate
.@g=monster("025-1", 99, 113, strmobinfo(1, FortressGate), FortressGate, 1, "Gate#F::OnSesame");
.@bhp=getunitdata(.@g, UDT_MAXHP);
.@bhp=.@bhp*(25+$MK_TEMPVAR)/25; // +4% per success or 8,000 HP
setunitdata(.@g, UDT_MAXHP, .@bhp);
setunitdata(.@g, UDT_HP, .@bhp);
// Initial defending waves
spawnCore(true);
spawnCore(false);
// Front Gate Guardians (Lv 70~80)
getmapxy(.@m$, .@x, .@y, 0);
.@x1=.@x-5;
.@x2=.@x+5;
.@y1=.@y-5;
.@y2=.@y+5;
for (.@i = 0; .@i < 4; ++.@i) {
spawnMob(any(AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite), .@x1, .@y1, .@x2, .@y2);
}
// Player blacklist (unable to use 025-2 warp)
maptimer2("025-1", 10, "Gate#F::OnMPBlacklist");
kamibroadcast($MOST_HEROIC$+"'s team has begun a siege on Fortress Town. Will they prevail?");
close;
/////////////////////////////////////////////////////////
OnSesame:
debugmes("[INFO] FORTRESS TOWN WAS BREACHED");
$@FORTRESS_STATUE = $@FORTRESS_STATUE|1024;
donpcevent "#025-1_99_112::OnDisable";
kamibroadcast("The Fortress Town Gate has been breached!");
close;
// Heartbeat (B1)
OnTimer15000:
if ($FORTRESS_STATE)
end;
.@breach=($@FORTRESS_STATUE & 1024);
if (mobcount("025-1", "all") < (.@breach ? 200 : 100))
spawnCore(.@breach);
end;
// Heartbeat (B2)
OnTimer30000:
if ($FORTRESS_STATE)
end;
// Initial variables
.@breach=($@FORTRESS_STATUE & 1024);
.@ppl=getmapusers("025-1");
// Fail condition
if (.@ppl <= 0) {
kamibroadcast("Players failed to conquer the Fortress Island!");
FTCleanup($FORTRESS_STATE);
end;
}
// Spawn mobs
spawnCore(.@breach);
// Summon reinforcements
maptimer2("025-1", 10, "Gate#F::OnMPReinforce");
// Restart timer
initnpctimer;
end;
/////////////////////////////////////////////////////////
OnMPBlacklist:
array_push($@FORT_BLACKLIST, getcharid(0));
goto OnMPReinforce;
OnMPReinforce:
// Dispose dead bodies
if (ispcdead() || getmap() != "025-1") {
warp "025-2", 96, 25;
end;
}
// Summon allies
// Last a whole minute
summon("Allied Guard", any(FallenGuard1, FallenGuard2, FallenGuard3));
end;
/////////////////////////////////////////////////////////
OnStatueBreach:
spawnCore(true);
getmapxy(.@m$, .@x, .@y, 0);
.@x1=.@x-5;
.@x2=.@x+5;
.@y1=.@y-5;
.@y2=.@y+5;
// Statue Guardians (Lv 70~90)
for (.@i = 0; .@i < 6; ++.@i) {
spawnMob(any(AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite, JackO, BlackMamba, Centaur, GoboBear, TerraniteProtector), .@x1, .@y1, .@x2, .@y2);
}
// Maybe spawn monster governor
if (
($@FORTRESS_STATUE & 1) &&
($@FORTRESS_STATUE & 2) &&
($@FORTRESS_STATUE & 4) &&
($@FORTRESS_STATUE & 8) &&
($@FORTRESS_STATUE & 16)) {
// The monster Governor shall now make their appearance!
spawnCore(true);
.@x1=97;
.@y1=21;
.@x2=102;
.@y2=25;
// Governor's Personal Bodyguard (Lv 90~110)
for (.@i = 0; .@i < 4; ++.@i) {
spawnMob(any(TerraniteProtector, LavaSkullSlime, VanityPixie, HolyPixie, ShadowPixie, NulityPixie, BlackSkullSlime, Reaper, NightmareDragon, WhirlyBird, PinkieSuseran), .@x1, .@y1, .@x2, .@y2);
}
.@mob=monster("025-1", any(99, 100), any(22, 23, 24), "Monster Governor", MonsterGeneral, 1, "Gate#F::OnConquest");
// Stat and Strengthen the governor
// Set governor metadata
setunitdata(.@mob, UDT_LEVEL, 100+$MK_TEMPVAR);
// Update monster modes
.@opt=getunitdata(.@mob, UDT_MODE);
.@opt=.@opt|MD_AGGRESSIVE;
.@opt=.@opt|MD_BOSS;
.@opt=.@opt|MD_NOKNOCKBACK;
setunitdata(.@mob, UDT_MODE, .@opt);
// Increase health in 2% per siege
.@bhp=75000;
.@bhp=.@bhp*(50+$MK_TEMPVAR)/50;
setunitdata(.@mob, UDT_MAXHP, .@bhp);
setunitdata(.@mob, UDT_HP, .@bhp);
// Accuracy is very very high, immune to crits, always crit
setunitdata(.@mob, UDT_HIT, 9999);
setunitdata(.@mob, UDT_LUK, 65535);
// Increase damage in 1% per siege (remember crit)
.@atk=320;
.@atk=.@atk*(100+$MK_TEMPVAR)/100;
setunitdata(.@mob, UDT_ATKMIN, .@atk);
setunitdata(.@mob, UDT_ATKMAX, .@atk);
// Ranged monster
setunitdata(.@mob, UDT_ATKRANGE, 8);
// Increase defenses in 0.5% per siege
.@def=getunitdata(.@mob, UDT_DEF);
.@def=.@def*(200+$MK_TEMPVAR)/200;
setunitdata(.@mob, UDT_DEF, .@def);
.@def=getunitdata(.@mob, UDT_MDEF);
.@def=.@def*(200+$MK_TEMPVAR)/200;
setunitdata(.@mob, UDT_MDEF, .@def);
.@def=getunitdata(.@mob, UDT_FLEE);
.@def=.@def*(200+$MK_TEMPVAR)/200;
setunitdata(.@mob, UDT_FLEE, .@def);
.@def=getunitdata(.@mob, UDT_PDODGE);
.@def=.@def*(200+$MK_TEMPVAR)/200;
setunitdata(.@mob, UDT_PDODGE, .@def);
// "Normalize" criticals
setunitdata(.@mob, UDT_CRIT, rand2(900, 2700));
// Announce that the Fortress Town can now be conquered
kamibroadcast("##1"+"Aegis Scutum has been nullified, the Monster Governor has appeared at Fortress Town!");
mapannounce "025-1", "Defeat the Monster Governor to capture the fortress town.", bc_map;
}
end;
/////////////////////////////////////////////////////////
// spawnMob(Mob, X1, Y1, X2, Y2)
function spawnMob {
//.@mob=monster("025-1", rand2(getarg(1), getarg(3)), rand2(getarg(2), getarg(4)), strmobinfo(1, getarg(0)), getarg(0), 1);
.@mob=areamonster("025-1", getarg(1), getarg(2), getarg(3), getarg(4),
strmobinfo(1, getarg(0)), getarg(0), 1);
.@opt=getunitdata(.@mob, UDT_MODE);
// Make aggressive
.@opt=.@opt|MD_AGGRESSIVE;
// All forces can suffer knockback
if (.@opt & MD_NOKNOCKBACK)
.@opt=.@opt^MD_NOKNOCKBACK;
// Save new options
setunitdata(.@mob, UDT_MODE, .@opt);
// Increase health in 1%+1% per siege
.@bhp=getunitdata(.@mob, UDT_MAXHP);
.@bhp=.@bhp*(101+$MK_TEMPVAR)/100;
setunitdata(.@mob, UDT_MAXHP, .@bhp);
setunitdata(.@mob, UDT_HP, .@bhp);
// Increase accuracy in 10%+1% per siege
.@acc=getunitdata(.@mob, UDT_HIT);
.@acc=.@acc*(110+$MK_TEMPVAR)/100;
setunitdata(.@mob, UDT_HIT, .@acc);
// TODO: adjust ViewRange
return;
}
// spawnCore(breach)
function spawnCore {
// Now, the thing is, I don't care with how powerful your invading forces are.
// I only care with how many success you have.
if (getarg(0)) {
.@x1=24;
.@y1=21;
.@x2=175;
.@y2=105;
.@am=2+$MK_TEMPVAR;
} else {
.@x1=25;
.@y1=110;
.@x2=180;
.@y2=120;
.@am=1+($MK_TEMPVAR/3);
}
freeloop(true);
// Level 40~60 Section
for (.@i = 0; .@i < .@am*3; ++.@i) {
spawnMob(any(Tipiou, Pollet, Wolvern, FireSkull, DarkLizard, BlackScorpion, EarthFairy, FireFairy, WaterFairy, WindFairy, PoisonFairy, DustGatling, DustRifle, DustRevolver, MountainSnake, HoodedNinja, ForestMushroom, GoldenScorpion, Yeti), .@x1, .@y1, .@x2, .@y2);
}
// Level 60~80 Section
for (.@i = 0; .@i < .@am*2; ++.@i) {
spawnMob(any(Yeti, WickedMushroom, Archant, Scar, Crafty, AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite), .@x1, .@y1, .@x2, .@y2);
}
// Level 80~100 section
for (.@i = 0; .@i < .@am; ++.@i) {
spawnMob(any(RedSkullSlime, Terranite, JackO, BlackMamba, GreenSkullSlime, Centaur, GoboBear, TerraniteProtector), .@x1, .@y1, .@x2, .@y2);
}
// Summoners Section
for (.@i = 0; .@i < .@am; ++.@i) {
spawnMob(any(GreenSlimeMother, BlueSlimeMother, YellowSlimeMother, RedSlimeMother, WhiteSlimeMother, AzulSlimeMother, LavaSlimeMother, BlackSlimeMother), .@x1, .@y1, .@x2, .@y2);
}
if (getarg(0)) {
// DemiBoss section (Internal only, increases every ~2 weeks)
for (.@i = 0; .@i < (.@am/2); ++.@i) {
spawnMob(any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie, Reaper, BlackSkullSlime, NightmareDragon, WhirlyBird, PinkieSuseran), .@x1, .@y1, .@x2, .@y2);
}
}
freeloop(false);
return;
}
/////////////////////////////////////////////////////////
OnInit:
sleep(200); // Ensure the NPCs will exist when this run
FTCleanup($FORTRESS_STATE);
end;
OnTue0000:
.@cl=$FORTRESS_STATE;
FTCleanup(false);
if (.@cl)
kamibroadcast("The Monster Army has retaken Fortress Town!");
end;
OnConquest:
// Failsafe
if ($GAME_STORYLINE < 3)
end;
// Advance the victory count
$MK_TEMPVAR+=1;
// Handle rewards in low priority
freeloop(true);
for (.@i=0; .@i < getarraysize($@FORT_BLACKLIST); .@i++) {
.@cid=$@FORT_BLACKLIST[.@i];
rodex_sendmail(.@cid, "Commander Povo", "Fortress Town Reward", "For your bravure in Fortress Town conquest! Cheers!", 0, StrangeCoin, 1+$MK_TEMPVAR);
}
freeloop(false);
// Clean up the remainders of the fight
FTCleanup(true);
kamibroadcast("Fortress Town has been captured by the Allied Forces!");
// Experience injection to the brave (500k EXP, 100k JEXP)
if (playerattached()) {
getexp 500000, 100000;
getitem StrangeCoin, rand2(40,50);
}
// Experience injection to the survivors
maptimer("025-1", 10, "Gate#F::OnConBonus");
stopnpctimer;
// Begin Fires of Steam if needed
if ($MK_TEMPVAR == MK_FIRESOFSTEAM_START) {
kamibroadcast("Andrei Sakar : It has been about three months since we begun battling for control overthe Fortress Town.");
sleep(5000);
kamibroadcast("Andrei Sakar : Since this begun, however, we've heard nothing of Artis Town.");
sleep(5000);
kamibroadcast("Andrei Sakar : Therefore, I invite all of you, brave adventurers, to go on an expedition with me to Artis.");
sleep(5000);
kamibroadcast("Andrei Sakar : "+b("Hopefully nothing terrible happened... Nard shall provide us transport."));
sleep(5000);
kamibroadcast("Andrei Sakar : I only want people strong enough to battle on the Fortress because the risks are high. Onwards! To victory! I'll be waiting");
$FIRESOFSTEAM = 1;
}
end;
OnConBonus:
message strcharinfo(0), l("Commander Povo : Good job %s. You did well.", lg("girl", "man")); // TRANSLATORS: Preserve whitespace.
.@it=any(ApanaCake, SacredLifePotion, SacredManaPotion, SacredImmortalityPotion, DeathPotion, PurificationPotion, ApanaCake);
if (.@it == ApanaCake || .@it == PurificationPotion)
.@qn = rand2(2, 5);
else
.@qn = 1;
getitem .@it, .@qn;
getexp 25000, 10000;
end;
}
/////////////////////////////////////////////////////////
// Statue NPCs
025-1,32,55,0 script Magic Statue#1 NPC_STATUE_WIZARD,{
.@b=FTStatue(strnpcinfo(2, "0"));
if (.@b)
disablenpc .name$;
close;
OnInit:
.distance=2;
end;
}
025-1,160,25,0 duplicate(Magic Statue#1) Magic Statue#2 NPC_STATUE_BACCHUS
025-1,156,97,0 duplicate(Magic Statue#1) Magic Statue#4 NPC_STATUE_FAFA
025-1,134,70,0 duplicate(Magic Statue#1) Magic Statue#8 NPC_STATUE_EVILMAN
025-1,80,37,0 duplicate(Magic Statue#1) Magic Statue#16 NPC_STATUE_GUARD
/////////////////////////////////////////////////////////
// The Impregnable Fortress Gate
025-1,99,20,0 script The Impregnable Fortress NPC_NO_SPRITE,3,1,{
// Operation not permitted
if (!$FORTRESS_STATE || $@FORTRESS_STATUE)
end;
// Not unlocked
if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_0F) {
mesc l("The gate is sealed shut."), 1;
mesc l("The monster army is still strong on this floor!"), 1;
mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_0F), 1;
close;
}
mesc l("Visit the Impregnable Fortress, 0F?");
mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_0F);
if (askyesno() == ASK_YES)
warp "025-3", 100, 178;
closeclientdialog;
close;
OnTouch:
// Operation not permitted
if (!$FORTRESS_STATE || $@FORTRESS_STATUE)
end;
// Not unlocked
if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_0F)
end;
warp "025-3", 100, 178;
end;
OnInit:
.distance=4;
end;
}
/////////////////////////////////////////////////////////
// Real access to 025-1 map
025-2,96,24,0 script Fortress Town Access NPC_HIDDEN,8,0,{
end;
OnTouch:
// Disabled
if ($GAME_STORYLINE < 3 || $@FORTRESS_STATUE)
end;
// Open
if ($FORTRESS_STATE)
cwarp "025-1", 99, 122;
// Blacklisted
if (array_find($@FORT_BLACKLIST, getcharid(0)) != -1)
end;
// Not blacklisted
warp "025-1", 99, 122;
end;
}