// TMW-2 Script
// Author:
// Jesusalva
// Description:
// GM Bot for the Monster King.
// VARIABLES
// $GAME_STORYLINE - Current Storyline status
// $@MK - Monster King Game ID
// $@MK_SCENE - Current event being handled by the Monster King
// $MK_TEMPVAR - Temporary Variable
// $@MK_THROTTLE - Event Throttler
//
// Storyline statuses:
// 0 - The Monster King is inactive (leading sieges to Hurnscald and Nivalis)
// 1 - The Monster King is known by players and is giving them a month break
// 2 - The Monster King is currently sieging towns at random
// 3 - The Monster King is preparing to perfom the Rite and Lightbringer seeks
// a wielder
// 4 - The Rite is CONCLUDED. Players must walk to MK evil lair and fight.
// 5 - The Monster King is dead. Or something. Depends on players.
//
// $MK_TEMPVAR meaning depends on GAME STORYLINE
// GS 0
// Ignored
// GS 1
// Tracks the day since 1970 when the town was cleared. A month break.
// GS 2
// Player score (1pt per Lieutenant, 10pts per Colonel)
// Affects the end of Game Story 2 and begin of Game Story 3
// Because we must wait players...
// GS 3
// Number of successful Fortress Town breaches
// GS 4
// Inheirs stage 3
000-0,0,0,0 script Monster King NPC_HIDDEN,{
OnSlaveDie:
end;
OnBourneAgain:
// Reset aggro
$@MK_AGGRO=0;
if (playerattached()) {
if (!FLAGS & FLAG_MKFIRSTBLOOD) {
channelmes("#world", strcharinfo(0)+" did an act worth of notice.");
FLAGS = FLAGS | FLAG_MKFIRSTBLOOD;
dispbottom l("Oh well, this sucks, but that was only an illusion.");
dispbottom l("The real Monster King is probably on his fortress. It'll take more than that to take him down.");
} else {
dispbottom l("While it is utterly pointless, you once again vanish the Monster King's illusion.");
}
if ($REBIRTH_WINNER$ == "" && TOP3AVERAGELVL() < 100)
getexp min(641500, BaseLevel**3), 0;
else if (!$FIRESOFSTEAM)
Mobpt+=max(13500, rand2(10000, BaseLevel*90));
else
Mobpt+=(max(13500, rand2(10000, BaseLevel*90)) * 2 / 3);
Mobpt = Mobpt + 165;
//$MOST_HEROIC$=strcharinfo(0);
specialeffect(FX_FANFARE, AREA, getcharid(3));
}
// We need to start over
.bar=true;
OnInit:
$@MK=monster("boss", 45, 45, "The Monster King", MonsterKing, 1, "Monster King::OnBourneAgain");
if (!.bar) {
// Variables which other NPCs must take in account
$@MK_AGGRO=0;
$@MK_SCENE=0;
} else {
.bar=false;
}
// Variables only for this NPC
.users=getusers(1);
.nearby=getusers(8);
.mp$="boss";
.aid=2000000;
.cid=150002;
// Constants
// This script is superseded, so no initialization is required.
// (Sieges and Personal Sieges no longer happen after Monster King dies)
if ($GAME_STORYLINE >= 5)
end;
// We should jump straight to loop (it runs every 90 seconds)
OnTimer90000:
// Regenerate some data, and kill spurious mobs
.users=getusers(1);
if (mobcount(.mp$, "Monster King::OnSlaveDie")) {
announce ("Monster King: Noobs, you are all a bunch of noobs!"), bc_map|bc_npc;
killmonster(.mp$, "Monster King::OnSlaveDie");
}
// We are on an event, so skip this loop
if ($@MK_SCENE || $@GM_EVENT) {
initnpctimer;
end;
}
// Check if MK is still alive
if (getunittype($@MK) < 0) {
consolebug("Monster King GID %d is not a valid unit type!", $@MK);
sleep(300); // Wait 5 minutes for the respawn
consolewarn("Forcefully respawning Monster King now (GID was invalid)");
.bar=true;
goto OnInit;
}
// The Monster King is online. This loop is not needed
if (isloggedin(.aid, .cid)) {
if (!$@MK_SCENE)
unitwarp($@MK, "boss", 45, 45);
else
rodex_sendmail(.cid, "MKBot", "Running Event", "An event is currently running by the MK Bot. Please logout and suppress it.");
initnpctimer;
}
// Raise aggro (1 pt per 2 users)
$@MK_AGGRO+=(.users/2);
// Mana Stone
if (.mp$ == "011-1")
enablenpc "Mana Stone";
// The Monster King will not move anymore because story
if ($GAME_STORYLINE == 0 ||
$GAME_STORYLINE >= 5) {
if (.mp$ != "boss")
unitwarp($@MK, "boss", 45, 45);
// Do not leave orphan timers
if ($GAME_STORYLINE >= 5)
stopnpctimer;
else
initnpctimer;
end;
}
// Check if MK is hurt, and if so, respawn it in boss map (summon cleanup)
if (getunitdata($@MK, UDT_HP) < getunitdata($@MK, UDT_MAXHP)) {
unitwarp($@MK, "boss", 45, 45);
sleep(50); // No exp for hitters
unitkill($@MK);
sleep(50); // No exp for hitters
$@MK=monster("boss", 45, 45, "The Monster King", MonsterKing, 1, "Monster King::OnBourneAgain");
// Monster King shall keep tending to his wounds in the keep for a cycle
initnpctimer;
end;
}
// Select a random map. Never shows up at Candor and cities, nor indoors. Not all maps either.
setarray .@m$, "boss", "boss", "001-1", "001-3", "001-4", "001-5", "001-6", "001-7", "001-10",
"003-1", "003-1-3", "004-1", "004-2", "007-1", "009-1", "010-1", "010-1-1", "010-2", "011-1",
"012-1", "014-1", "014-2", "014-3", "014-4", "014-5", "015-1", "015-2", "015-3", "015-5",
"018-1-1", "018-2", "018-3", "018-4", "018-4-1",
"019-1", "019-2", "019-4", "020-1", "021-1", "022-1", "023-1";
.mp$=any_of(.@m$);
// Try to warp randomly, up to 30 attempts
.@e=0; .@x=0; .@y=0;
.@mx=getmapinfo(MAPINFO_SIZE_X, .mp$)-20;
.@my=getmapinfo(MAPINFO_SIZE_Y, .mp$)-20;
do {
if (.@e >= 30) {
.mp$="boss";
.@x=45;
.@y=45;
break;
}
.@x = rand2(20, .@mx);
.@y = rand2(20, .@my);
.@e+=1;
} while (!checknpccell(.mp$, .@x, .@y, cell_chkpass));
if (!checknpccell(.mp$, .@x, .@y, cell_chkpass)) {
Exception("mk.bot runtime error: GM_ERR_128 highlight @Jesusalva", RB_DEBUGMES|RB_IRCBROADCAST); .mp$="boss"; .@x=45; .@y=45;
}
// Monster King will not warp around for sightseeing if he is threatened
if ($GAME_STORYLINE > 2 && $@MK_AGGRO < 150) {
.mp$="boss";.@x=45;.@y=45;
}
unitwarp($@MK, .mp$, .@x, .@y);
sleep(50); // For some reason or other, adding sleep(norid) and sleep2(rid).
.nearby=getusers(8);
// Handle Mana Stone
if (.mp$ == "011-1")
disablenpc "Mana Stone";
// Debug markers
if ($@GM_OVERRIDE)
debugmes "Monster King (bot): "+.mp$+" ("+.@x+", "+.@y+")";
// If too few players are online, we don't need an event AT ALL!
if (.users < rand2(2,4)) {
initnpctimer;
end;
}
// Siege events (req. 300 aggro, 3 users, and 70% chances to begin)
if ($@MK_AGGRO >= ($GAME_STORYLINE == 2 ? 300 : 900) && .users >= 3 && rand2(0,100) < 70 &&
is_between(1, 4, $GAME_STORYLINE) && $@MK_THROTTLE < gettimetick(2) &&
$@MK_TRIGGERED){
// Delta handles the compulsory wait time between waves.
// 7 hours normally, 24 hours if the army is in disarray.
.@delta=7;
$@SIEGE_ABORTED = false;
if ($GAME_STORYLINE >= 3)
.@delta=24;
// Tulimshar
if (compare(.mp$, "003-")) {
announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc;
$@MK_THROTTLE=gettimetick(2)+.@delta*60*60;
$@MK_SCENE=MK_SIEGE_TULIM;
donpcevent("Lieutenant Dausen::OnMKSiege");
}
// Halinarzo
else if (compare(.mp$, "009-")) {
announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc;
$@MK_THROTTLE=gettimetick(2)+.@delta*60*60;
$@MK_SCENE=MK_SIEGE_HALIN;
donpcevent("Lieutenant Jacob::OnMKSiege");
}
// Hurnscald
else if (compare(.mp$, "012-")) {
announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc;
$@MK_THROTTLE=gettimetick(2)+.@delta*60*60;
$@MK_SCENE=MK_SIEGE_HURNS;
donpcevent("#HurnscaldSiege::OnMKSiege");
}
// Nivalis
else if (compare(.mp$, "020-")) {
announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc;
$@MK_THROTTLE=gettimetick(2)+.@delta*60*60;
$@MK_SCENE=MK_SIEGE_NIVAL;
donpcevent("Lieutenant Joshua::OnMKSiege");
}
}
// If a player is nearby while the Monster King prepares, event may happen
// Minimum 80 Aggro
if (.nearby > 1 && $@MK_AGGRO >= 80 &&
($GAME_STORYLINE == 1 || ($GAME_STORYLINE >= 3 && $@MK_THROTTLE >= gettimetick(2)) )){
// We should decide event kind, but that's NYI
announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc;
getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@MK);
// Spawn stuff
areamonster(.@m$, .@x-20, .@y-20, .@x+20, .@y+20, "Monster", ManaGhost, min(60, $@MK_AGGRO/10)+.nearby, "Monster King::OnSlaveDie");
// Remove some aggro. TODO: Do not remove so much aggro if it was high
.@diff=($@MK_AGGRO*$GAME_STORYLINE)/5;
$@MK_AGGRO=max(.@diff, 0);
}
// Maybe, just maybe, game storyline must be updated here
if ($GAME_STORYLINE == 1 && $MK_TEMPVAR &&
$MK_TEMPVAR <= gettimeparam(GETTIME_DAYOFMONTH)) {
// Game Story Change: Idle MK -> Active MK
kamibroadcast("I can't handle it anymore! NO MORE!", "Monster King");
sleep(2500);
kamibroadcast("Come, my minions! Lay siege to towns! LEAVE NO OPPOSITION TO ME!", "Monster King");
sleep(2500);
kamibroadcast("Burn, destroy, do whatever you need, until your last breath, my lieutenants and colonels!", "Monster King");
sleep(2500);
kamibroadcast("##4 .:: Game Story Instructions on #world ::.");
channelmes("#world", "##1 **GAME STORY CHANGE** - The Monster King minions will now attack cities and lay waste to them.");
channelmes("#world", "##1 Players must defeat the lieutenants and colonels in order to prevent sieges from continuing.");
// Apply the changes
$GAME_STORYLINE=2;
$MK_TEMPVAR=0;
}
if ($GAME_STORYLINE == 2 &&
$MK_TEMPVAR >= MK_SIEGE_TOTALPOWER) {
kamibroadcast("##1##BThe Monster King army is in total disarray and disorder!", "Saulc");
sleep(2500);
kamibroadcast("##1##BIt's our chance to strike back!", "Saulc");
sleep(2500);
// Apply the changes
$GAME_STORYLINE=3;
$MK_TEMPVAR=0; // In past, we forced 1 month wait. Now we have KW mechs...
$MANA_BLVL-=10; // Set level to 20~30
$MANA_JLVL-=5; // Set job level to 15
}
// We're done, restart loop timer
$@MK_TRIGGERED=false;
initnpctimer;
end;
}