swwmgz_m/zscript/handler/swwm_handler_worldload.zsc

521 lines
16 KiB
Text

// WorldLoaded/WorldUnloaded events
Class RampancyLogonDummy : Actor
{
States
{
Spawn:
TNT1 A 7;
TNT1 AAAAAAAAAAAAAAAA 1
{
for ( int i=0; i<16; i++ )
A_Log("Remote login failed.");
}
TNT1 A 7;
TNT1 A 1 A_Log("\cgWARNING:\cj 256 failed remote login attempts have been reported in the last second.\c-");
Stop;
}
}
// this is used to speed up iteration through sector thinglists within a specific area
Class SectorBounds
{
Vector4 bounds;
int portalgroup;
clearscope bool PointInSectorBounds( Vector2 p, int pg = -1 ) const
{
if ( (pg >= 0) && (level.GetPortalGroupCount() > 0) && (pg != portalgroup) )
p += level.GetDisplacement(pg,portalgroup);
if ( p.x < bounds.x ) return false;
if ( p.y < bounds.y ) return false;
if ( p.x > bounds.z ) return false;
if ( p.y > bounds.w ) return false;
return true;
}
clearscope bool BoxInSectorBounds( Vector2 p, double r, int pg = -1 ) const
{
if ( (pg >= 0) && (level.GetPortalGroupCount() > 0) && (pg != portalgroup) )
p += level.GetDisplacement(pg,portalgroup);
if ( p.x+r < bounds.x ) return false;
if ( p.y+r < bounds.y ) return false;
if ( p.x-r > bounds.z ) return false;
if ( p.y-r > bounds.w ) return false;
return true;
}
}
extend Class SWWMHandler
{
bool maphaskeys;
bool nogroundanchor;
int allclearsector;
// weird optimization
Array<SectorBounds> sbounds;
// level end stats
override void WorldUnloaded( WorldEvent e )
{
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
SWWMStats s;
while ( s = SWWMStats(ti.Next()) )
{
int clust = 0;
bool secret = false;
if ( SWWMUtility.IsEviternity() )
{
// we have to do some heavy lifting here because episodes don't match clusters
if ( level.levelnum <= 5 ) clust = 1;
else if ( level.levelnum <= 10 ) clust = 2;
else if ( level.levelnum <= 15 ) clust = 3;
else if ( level.levelnum <= 20 ) clust = 4;
else if ( level.levelnum <= 25 ) clust = 5;
else if ( level.levelnum <= 30 ) clust = 6;
else if ( level.levelnum <= 32 )
{
secret = true;
if ( level.levelnum <= 31 ) clust = 7;
else clust = 8;
}
}
else
{
if ( (gameinfo.gametype&GAME_DOOM) && ((level.cluster == 9) || (level.cluster == 10)) )
secret = true;
clust = level.cluster;
}
int csiz = s.clustervisit.Size();
if ( csiz == 0 )
{
s.clustervisit.Push(clust);
s.secretdone.Push(secret);
}
else if ( s.clustervisit[csiz-1] != clust )
{
s.clustervisit.Push(clust);
s.secretdone.Push(secret|s.secretdone[csiz-1]);
}
s.AddLevelStats();
s.lastcluster = level.cluster;
// nazi cleanup
let ti = ThinkerIterator.Create("Actor");
Actor a;
bool hasnazis = false;
bool livenazis = false;
while ( a = Actor(ti.Next()) )
{
// yes, the dogs don't count (they're just dogs)
if ( !(a is 'SWWMGuard') && !(a is 'SWWMSS') && !(a is 'SWWMHans') )
continue;
hasnazis = true;
if ( a.Health > 0 ) livenazis = true;
}
if ( hasnazis && !livenazis )
{
if ( level.levelnum == 31 ) s.nazicleanup |= 1;
else if ( level.levelnum == 32 ) s.nazicleanup |= 2;
}
if ( s.nazicleanup == 3 )
SWWMUtility.MarkAchievement("trash",s.myplayer);
}
// re-enable retries after Eviternity 2 MAP33
if ( level.GetChecksum() ~== "043FE06534270E95882CA128AF7B0402" )
gdat.disablerevive = false;
// reset score on dead players (death exit™)
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (players[i].playerstate != PST_DEAD) ) continue;
let c = SWWMCredits.Find(players[i]);
if ( c ) c.credits = 0;
}
// end of episode resets and enforced pistol starts
LevelInfo nextlv = LevelInfo.FindLevelInfo(e.NextMap);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let demo = Demolitionist(players[i].mo);
if ( !demo ) continue;
// Death exit counter breaks Eviternity's episode transitions
// so we need this little patch-up work here
if ( (players[i].playerstate == PST_DEAD) && nextlv && (nextlv.flags2&LEVEL2_RESETINVENTORY) )
demo.invwipe |= Demolitionist.WIPE_EPISODE;
if ( level.nextsecretmap.Left(6) == "enDSeQ" ) demo.invwipe |= Demolitionist.WIPE_EPISODE;
if ( nextlv && (level.cluster!=nextlv.cluster) ) demo.invwipe |= (Demolitionist.WIPE_CLUSTER|Demolitionist.WIPE_MAP);
if ( !(level.clusterflags&LevelLocals.CLUSTER_HUB) ) demo.invwipe |= Demolitionist.WIPE_MAP;
// the playerpawn will know what to do with this in its PreTravelled()
}
// did we complete this map without collecting any of its keys? (doesn't work for hubs)
if ( maphaskeys && !(level.clusterflags&LevelLocals.CLUSTER_HUB) )
{
bool collected = false;
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
for ( Inventory inv=players[i].mo.inv; inv; inv=inv.inv )
{
if ( !(inv is 'Key') ) continue;
collected = true;
break;
}
}
if ( !collected ) SWWMUtility.MarkAchievement("cliffyb",players[consoleplayer]);
}
// these can't be done on hexen
if ( gameinfo.GameType&GAME_Hexen ) return;
// beat the par time?
if ( level.partime && (Thinker.Tics2Seconds(level.maptime) <= level.partime) )
SWWMUtility.AchievementProgressInc("par",1,players[consoleplayer]);
// blaze it?
if ( Thinker.Tics2Seconds(level.maptime) == 260 )
SWWMUtility.MarkAchievement("blaze",players[consoleplayer]);
// one standing?
if ( (level.total_monsters-level.killed_monsters) == 1 )
SWWMUtility.MarkAchievement("onestanding",players[consoleplayer]);
// nice?
if ( players[consoleplayer].Health == 69 )
SWWMUtility.MarkAchievement("nice",players[consoleplayer]);
// peaceful/untouchable?
if ( !dealtdamage[consoleplayer] )
SWWMUtility.MarkAchievement("peace",players[consoleplayer]);
if ( !reallytookdamage[consoleplayer] )
SWWMUtility.MarkAchievement("untouchable",players[consoleplayer]);
// in a hurry?
if ( ((level.total_monsters > 0) || (level.total_items > 0) || (level.total_secrets > 0))
&& (level.killed_monsters == 0) && (level.found_items == 0) && (level.found_secrets == 0) )
SWWMUtility.MarkAchievement("hurry",players[consoleplayer]);
}
clearscope bool PointInSectorBounds( Sector s, Vector2 p, int pg = -1 ) const
{
if ( sbounds.Size() <= 0 ) return false;
int i = s.Index();
return sbounds[i].PointInSectorBounds(p,pg);
}
clearscope bool BoxInSectorBounds( Sector s, Vector2 p, double r, int pg = -1 ) const
{
if ( sbounds.Size() <= 0 ) return false;
int i = s.Index();
return sbounds[i].BoxInSectorBounds(p,r,pg);
}
void PrecalculateSectorBounds()
{
sbounds.Resize(level.Sectors.Size());
foreach ( s:level.Sectors )
{
let sb = new("SectorBounds");
sb.portalgroup = s.portalgroup;
sb.bounds = ( 32767, 32767, -32768, -32768 );
foreach ( l:s.Lines )
{
if ( l.v1.p.x < sb.bounds.x )
sb.bounds.x = l.v1.p.x;
if ( l.v2.p.x < sb.bounds.x )
sb.bounds.x = l.v2.p.x;
if ( l.v1.p.y < sb.bounds.y )
sb.bounds.y = l.v1.p.y;
if ( l.v2.p.y < sb.bounds.y )
sb.bounds.y = l.v2.p.y;
if ( l.v1.p.x > sb.bounds.z )
sb.bounds.z = l.v1.p.x;
if ( l.v2.p.x > sb.bounds.z )
sb.bounds.z = l.v2.p.x;
if ( l.v1.p.y > sb.bounds.w )
sb.bounds.w = l.v1.p.y;
if ( l.v2.p.y > sb.bounds.w )
sb.bounds.w = l.v2.p.y;
}
sbounds[s.Index()] = sb;
}
}
private void MapStartDialogues()
{
int whichboss = WhichVanillaBossMap();
switch ( whichboss )
{
case MAP_DE1M8:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.PHOBOS");
break;
case MAP_DE2M8:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DEIMOS");
break;
case MAP_DE3M8:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DIS");
break;
case MAP_DE4M8:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.THY");
break;
case MAP_DMAP07:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DIMPLE");
break;
case MAP_DMAP30:
bool rampancy = false;
foreach ( cls:AllActorClasses )
{
if ( cls.GetClassName() != "Robot_BossBrain" ) continue;
rampancy = true;
break;
}
if ( rampancy ) SendInterfaceEvent(consoleplayer,"swwmsetdialogue.RAMPANCY");
else SendInterfaceEvent(consoleplayer,"swwmsetdialogue.IOS");
break;
case MAP_DLVL08:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.NERVE");
break;
case MAP_EVMAP30:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EVIA");
break;
case MAP_HE1M8_HE4M8:
if ( level.mapname ~== "E1M8" ) SendInterfaceEvent(consoleplayer,"swwmsetdialogue.MAW");
else SendInterfaceEvent(consoleplayer,"swwmsetdialogue.HEADS");
break;
case MAP_HE2M8_HE5M8:
if ( level.mapname ~== "E2M8" ) SendInterfaceEvent(consoleplayer,"swwmsetdialogue.PORTALS");
else SendInterfaceEvent(consoleplayer,"swwmsetdialogue.BULLS");
break;
case MAP_HE3M8:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DSPARIL");
break;
case MAP_HMAP38:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CLERIC");
break;
case MAP_HMAP36:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.FIGHTER");
break;
case MAP_HMAP37:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.MAGE");
break;
case MAP_HMAP12:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.HYPO");
break;
case MAP_HMAP40:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.KORAX");
break;
case MAP_HMAP23_HMAP27_HMAP48_HMAP55:
if ( level.mapname ~== "MAP48" ) SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CONSTABLE");
break;
case MAP_HMAP60:
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DEATHKINGS");
break;
case MAP_NONE:
String csum = level.GetChecksum();
// SIGIL E5M8
if ( (csum ~== "3D72FD17F36D2D43FD9A21E6E57EE357")
|| (csum ~== "09B30C9DA9D73D3D5A709502FBB947AA")
|| (csum ~== "6EAD80DA1F30B4B3546FA294EEF9F87C") )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.SIGIL");
// Doom 2 MAP11
else if ( (csum ~== "73D9E03CEE7BF1A97EFD2EAD86688EF8")
|| (csum ~== "F4F2A769609988837458772AAE99008C")
|| (csum ~== "DF6A001A6C42DB5CCA599EE5883B294A") )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CIRCLE");
// Doom 2 MAP20
else if ( (csum ~== "8898F5EC9CBDCD98019A1BC1BF892A8A")
|| (csum ~== "CC53CFFCB30E873669AA2F09DA0D3566") )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.GOTCHA");
// Eviternity
// MAP05
else if ( csum ~== "33B8501B10CE5E2555C03725F765A914" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.DMN");
// MAP10
else if ( csum ~== "9E83602D325677B8D7C3BC44BEF9B03F" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CRE");
// MAP15
else if ( csum ~== "CA40E6DDAB6B5C924CDC36B1F851421E" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CRY");
// MAP20
else if ( csum ~== "F34B3FD4D13AC763469A8E0D7379B9D0" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CON");
// MAP25
else if ( csum ~== "196BC735473C593F924A59B238574C35" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.SLA");
// Deathkings
// Blight
else if ( csum ~== "E3EFB0156A20ADF2DF00915A0EA85DF5" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.BLIGHT");
// Nave
else if ( csum ~== "E2B5D1400279335811C1C1C0B437D9C8" )
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.NAVE");
break;
}
}
override void WorldLoaded( WorldEvent e )
{
// session globals must be loaded here
// (if we do it in OnRegister the existing thinker might not have been deserialized yet)
gdat = SWWMGlobals.Get();
if ( e.IsReopen ) return;
PrecalculateSectorBounds();
MapStartDialogues();
if ( gamestate != GS_TITLELEVEL )
{
if ( (level.GetChecksum() ~== "D0E5ECD94BD38DF33F25515C00148693")
|| (level.GetChecksum() ~== "D20297AE8447232F6DBE4851E3104668")
|| (level.GetChecksum() ~== "EBBB8663AD4AB22294A8A1D211A026CD") )
{
if ( !AddOneliner("nutstart",3) )
AddOneliner("mapstart",3);
}
else AddOneliner("mapstart",3);
}
if ( level.levelname ~== "Modder Test Map" )
{
level.ReplaceTextures("-noflat-","kinstile",0);
S_ChangeMusic("music/CARDISH1.XM");
AddBoss(6666,"$BT_DOOMTEST"); // for testing boss healthbars
}
// doom vacation map01 hackaround for OPEN script not letting us
// change certain line specials in levelpostprocessor because
// ACS is just mindbogglingly weird like that, seriously
if ( (level.GetChecksum() ~== "F286BABF0D152259CD6B996E8920CA70")
|| (level.GetChecksum() ~== "A52BD2038CF814101AAB7D9C78F9ACE2") )
level.ExecuteSpecial(ACS_Execute,null,null,false,-Int('DVACATION_UNSCREW'));
// rampancy boss brain fix (repeatedly triggering "map clear")
let ti = ThinkerIterator.Create("Actor");
Actor a, brain;
bool haseye = false;
while ( a = Actor(ti.Next()) )
{
if ( a.GetClassName() == "Robot_BossEye" )
haseye = true;
if ( a.GetClassName() == "Robot_BossBrain" )
brain = a;
}
if ( haseye && brain )
{
brain.bCOUNTKILL = true;
level.total_monsters++;
// while we're at it
Actor.Spawn("RampancyLogonDummy");
}
// KDiKDiZD abort fix due to voodoo doll post-spawn replacement
// (we have our own mikoportal compatibility, anyway)
//
// note that the mods are mutually incompatible nonetheless
// since KDiKDiZD requires software rendering, while this is a
// hardware-only mod... but hey, they can sort-of-work together
// (with broken visual effects, but still... somewhat working)
foreach ( cls:AllClasses )
{
if ( cls.GetClassName() != 'KdikdizdCompatEventHandler' )
continue;
ti = ThinkerIterator.Create("Thinker");
Thinker t;
while ( t = ti.Next() )
{
if ( t.GetClassName() != 'VoodooPusher' )
continue;
t.Destroy();
}
break;
}
// Eviternity II MAP33 fix. Player movement physics need to
// have ground anchoring disabled, as it will make some
// segments impossible due to the player's feet immediately
// touching the instant-kill lava
if ( level.GetChecksum() ~== "043FE06534270E95882CA128AF7B0402" )
{
nogroundanchor = true;
allclearsector = 18414; // only check all-clear if the player is standing in this sector
gdat.disablerevive = true; // ONE TRY
}
// for skipping over merged exit lines (sharing vertices)
Array<Line> skipme;
skipme.Clear();
// find exit lines, and use lines that aren't exits
foreach ( l:level.Lines )
{
// all lines are immediately visible in DM
if ( deathmatch && !(l.flags&Line.ML_DONTDRAW) )
l.flags |= Line.ML_MAPPED;
// while we're at it, add teleporter sparks
if ( SWWMUtility.IsTeleportLine(l) )
{
let a = SWWMTeleportLine(Actor.Spawn("SWWMTeleportLine"));
a.tline = l;
}
let [isexit, exittype] = SWWMUtility.IsExitLine(l);
if ( !isexit ) continue;
if ( skipme.Find(l) < skipme.Size() ) continue;
skipme.Push(l);
// look for connected lines
Array<Line> con;
con.Clear();
con.Push(l);
int found;
if ( l.frontsector )
{
do
{
found = 0;
foreach ( l2:l.frontsector.Lines )
{
if ( (l2.special != l.special) || (con.Find(l2) < con.Size()) ) continue;
// needs to have a point in common with this one or any of the added lines
bool nomatches = true;
foreach ( c:con )
{
if ( (l2.v1 != c.v1) && (l2.v2 != c.v2) && (l2.v1 != c.v2) && (l2.v2 != c.v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
skipme.Push(l2);
con.Push(l2);
found++;
}
}
while ( found > 0 );
}
if ( l.backsector )
{
do
{
found = 0;
foreach ( l2:l.backsector.Lines )
{
if ( (l2.special != l.special) || (con.Find(l2) < con.Size()) ) continue;
// needs to have a point in common with this one or any of the added lines
bool nomatches = true;
foreach ( s:skipme )
{
if ( (l2.v1 != s.v1) && (l2.v2 != s.v2) && (l2.v1 != s.v2) && (l2.v2 != s.v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
skipme.Push(l2);
con.Push(l2);
found++;
}
}
while ( found > 0 );
}
Vector3 lpos = (0,0,0);
foreach ( c:con )
lpos += SWWMUtility.UseLinePos(c);
lpos /= con.Size();
SWWMInterest.Spawn(self,lpos,theline:l,theexit:exittype);
}
// spawn loot
if ( !deathmatch ) Chancebox.SpawnChanceboxes();
// list map keys
maphaskeys = false;
ti = ThinkerIterator.Create("Key");
Key k;
while ( k = Key(ti.Next()) )
{
if ( k.Owner ) continue;
maphaskeys = true;
break;
}
}
}