- Try to get rid of all implicit casts from string to name, color or class. - Use FindClass where needed. - Used a map in a case where a dictionary was unneeded. - Use new bounce flags where needed. - Replace Legacy of Rust weapons/ammo.
537 lines
18 KiB
Text
537 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 <= 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() ~== "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;
|
|
}
|
|
}
|
|
}
|