As of this commit, do not consider the experience when playing that new expansion to be complete. I've only partially written some of the mission texts and rudimentarily enhanced some boss fights. Currently there is one major limitation in that the intermission texts cannot be replaced, as they're hardcoded inside the UMAPINFO. I don't know if I can work around that.
547 lines
18 KiB
Text
547 lines
18 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.IsEviternityTwo() )
|
|
{
|
|
// clusters have to be remapped here
|
|
let clus = level.cluster;
|
|
if ( clus == 5 ) clust = 1;
|
|
else if ( (clus == 6) || (clus == 13) ) clust = 2;
|
|
else if ( (clus == 7) || (clus == 14) ) clust = 3;
|
|
else if ( (clus == 8) || (clus == 15) ) clust = 4;
|
|
else if ( (clus == 9) || (clus == 16) ) clust = 5;
|
|
else if ( (clus == 10) || (clus == 17) ) clust = 6;
|
|
else if ( (clus == 11) || (clus == 12) || (clus == 18) || (clus == 19) ) clust = 7;
|
|
}
|
|
else if ( SWWMUtility.IsEviternity() )
|
|
{
|
|
// we have to do some heavy lifting here because episodes don't match clusters
|
|
if ( level.levelnum <= 0 ) {}
|
|
else 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 ( SWWMUtility.IsLegacyOfRust() )
|
|
{
|
|
// clusters must be manually assigned
|
|
if ( level.levelnum <= 0 ) {}
|
|
if ( (level.levelnum <= 7) || (level.levelnum == 15) ) clust = 28;
|
|
else if ( (level.levelnum <= 14) || (level.levelnum == 16) ) clust = 29;
|
|
if ( (level.levelnum == 15) || (level.levelnum == 16) )
|
|
secret = true;
|
|
}
|
|
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() ~== "442504BA06E5EFB6C7EBD452E159522D" )
|
|
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:
|
|
case MAP_DE1M8B:
|
|
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:
|
|
if ( FindClass('Robot_BossBrain','Actor') )
|
|
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_EVIIMAP30:
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2K");
|
|
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");
|
|
// SIGIL 2 E6M8
|
|
if ( csum ~== "5BA3D00F6B64F6268E11C6851D47ECBF" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.SIGIL2");
|
|
// 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");
|
|
// Eviternity 2
|
|
// MAP01
|
|
else if ( csum ~== "8EB38D5289C47BB68D64F2832EFA096D" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2A");
|
|
// MAP05
|
|
else if ( csum ~== "457CAF066596B6AF59F7273C8D5461B7" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2E");
|
|
// MAP10
|
|
else if ( csum ~== "066653E60ACC99D7B8EB5EBBFEF4F11A" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2F");
|
|
// MAP15
|
|
else if ( csum ~== "8FB3513B313002B1287610F545F0FDFF" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2G");
|
|
// MAP20
|
|
else if ( csum ~== "641A394145EF638B972E87C4CDFB34EF" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2H");
|
|
// MAP25
|
|
else if ( csum ~== "67A80E78AEBA38AB0A0DD0616040F4F2" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2I");
|
|
// MAP33
|
|
else if ( csum ~== "442504BA06E5EFB6C7EBD452E159522D" )
|
|
SendInterfaceEvent(consoleplayer,"swwmsetdialogue.EV2P");
|
|
// 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)
|
|
if ( FindClass('KdikdizdCompatEventHandler','EventHandler') )
|
|
{
|
|
ti = ThinkerIterator.Create('Thinker');
|
|
foreach ( t:ti )
|
|
{
|
|
if ( t.GetClassName() != 'VoodooPusher' )
|
|
continue;
|
|
t.Destroy();
|
|
}
|
|
}
|
|
// 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() ~== "442504BA06E5EFB6C7EBD452E159522D" )
|
|
{
|
|
nogroundanchor = true;
|
|
allclearsector = 18414; // only check all-clear if the player is standing in this sector
|
|
gdat.disablerevive = true; // ONE TRY
|
|
}
|
|
Array<Line> exits;
|
|
Array<int> exittypes;
|
|
exits.Clear();
|
|
exittypes.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;
|
|
exits.Push(l);
|
|
exittypes.Push(exittype);
|
|
}
|
|
// for skipping over merged exit lines (sharing vertices)
|
|
Array<Line> skipme;
|
|
skipme.Clear();
|
|
for ( int i=0; i<exits.Size(); i++ )
|
|
{
|
|
let l = exits[i];
|
|
if ( skipme.Find(l) < skipme.Size() ) continue;
|
|
skipme.Push(l);
|
|
// look for connected lines
|
|
// only stop once we cannot find more
|
|
Array<Line> con;
|
|
con.Clear();
|
|
con.Push(l);
|
|
int found;
|
|
do
|
|
{
|
|
found = 0;
|
|
for ( int j=0; j<exits.Size(); j++ )
|
|
{
|
|
let l2 = exits[j];
|
|
if ( (l2 == l) || !SWWMUtility.SameSpecial(l,l2) || (skipme.Find(l2) < skipme.Size()) || (con.Find(l2) < con.size()) ) continue;
|
|
// needs to have at least one point in common with this one or any of the added lines
|
|
bool nomatches = true;
|
|
foreach ( c:con )
|
|
{
|
|
if ( (l2.v1.p != c.v1.p) && (l2.v2.p != c.v2.p) && (l2.v1.p != c.v2.p) && (l2.v2.p != c.v1.p) )
|
|
continue;
|
|
nomatches = false;
|
|
break;
|
|
}
|
|
if ( nomatches ) continue;
|
|
found++;
|
|
skipme.Push(l2);
|
|
con.Push(l2);
|
|
}
|
|
}
|
|
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:exittypes[i]);
|
|
}
|
|
// 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;
|
|
}
|
|
}
|
|
}
|