More SWWMHandler code splitting.

Revert fallback sprite stuff. It's glitchy.
This commit is contained in:
Marisa the Magician 2021-03-07 20:50:10 +01:00
parent ab0270071a
commit a74a9ae795
9 changed files with 2181 additions and 5904 deletions

View File

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r330 \cu(Sun 7 Mar 20:16:13 CET 2021)\c-";
SWWM_SHORTVER="\cw0.9.11b-pre r330 \cu(2021-03-07 20:16:13)\c-";
SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r331 \cu(Sun 7 Mar 20:50:10 CET 2021)\c-";
SWWM_SHORTVER="\cw0.9.11b-pre r331 \cu(2021-03-07 20:50:10)\c-";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@ version "4.5"
#include "zscript/swwm_onfire.zsc"
// handler code
#include "zscript/handler/swwm_handler_cheats.zsc"
#include "zscript/handler/swwm_handler_damage.zsc"
#include "zscript/handler/swwm_handler_flash.zsc"
#include "zscript/handler/swwm_handler_iwantdie.zsc"
#include "zscript/handler/swwm_handler_oneliners.zsc"
@ -41,6 +42,9 @@ version "4.5"
#include "zscript/handler/swwm_handler_replacements.zsc"
#include "zscript/handler/swwm_handler_shaders.zsc"
#include "zscript/handler/swwm_handler_vanillaboss.zsc"
#include "zscript/handler/swwm_handler_worldload.zsc"
#include "zscript/handler/swwm_handler_worldthings.zsc"
#include "zscript/handler/swwm_handler_worldtick.zsc"
// menu code
#include "zscript/menu/swwm_menus.zsc"
#include "zscript/menu/swwm_title.zsc"

View File

@ -0,0 +1,302 @@
// WorldThingDamaged and friends
extend Class SWWMHandler
{
bool tookdamage[MAXPLAYERS];
int spreecount[MAXPLAYERS];
int lastkill[MAXPLAYERS];
int multilevel[MAXPLAYERS];
// gibbing
private void DoGibThing( WorldEvent e )
{
// no gib if it was erased or used the kill monsters cheat
if ( (e.DamageType == 'Ynykron') || (e.DamageType == 'Massacre') ) return;
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
bool forcegibbed = false;
// force gib availability for some vanilla Doom monsters
if ( gotgibbed && ((e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "Cacodemon") || (e.Thing.GetClass() == "Revenant") || (e.Thing.GetClass() == "Archvile")) )
forcegibbed = true;
if ( !e.Thing.FindState("XDeath",true) && !e.Thing.FindState("Death.Extreme",true) && !forcegibbed )
gotgibbed = false;
// only do special handling if they use our blood
if ( (e.Thing.GetBloodType(0) != "mkBlood") || e.Thing.bNOBLOOD )
return;
CorpseFallTracker.TrackBody(e.Thing);
bool b;
Actor a;
// special handling of some monsters
if ( e.Thing.GetClass() == "Cyberdemon" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 40;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
return;
}
else if ( e.Thing.GetClass() == "SpiderMastermind" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 60;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
}
else if ( gotgibbed )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
}
}
// damage numbers, combat tracking, etc.
private void DoDamageHandling( WorldEvent e )
{
bool spawnme = true;
if ( swwm_accdamage )
{
// find existing damage number
for ( SWWMScoreObj d=damnums; d; d=d.next )
{
if ( (d.starttic < level.maptime) || (d.acc != e.Thing) ) continue;
if ( d.score-e.Damage > d.score ) d.score = int.min;
else d.score -= e.Damage;
spawnme = false;
break;
}
}
if ( spawnme ) SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),ST_Damage,e.Thing);
// update combat tracker for it
if ( !(e.Thing is 'BossBrain') )
{
for ( SWWMCombatTracker t=trackers; t; t=t.next )
{
if ( t.mytarget != e.Thing ) continue;
t.updated = level.maptime+35;
break;
}
}
// fall dmg
SWWMWhoPushedMe.SetInstigator(e.Thing,e.DamageSource);
// stats
if ( e.Thing.player )
{
tookdamage[e.Thing.PlayerNumber()] = true;
let s = SWWMStats.Find(e.Thing.player);
if ( s ) // deathmatch telefrag-on-spawn may cause this to be null
{
s.AddDamageTaken(e.Damage);
if ( e.Damage > s.toptaken ) s.toptaken = e.Damage;
}
}
if ( e.DamageSource && e.DamageSource.player )
{
let s = SWWMStats.Find(e.DamageSource.player);
if ( s ) // deathmatch telefrag-on-spawn may cause this to be null
{
s.AddDamageDealt(e.Damage);
if ( e.Damage > s.topdealt ) s.topdealt = e.Damage;
}
}
}
// combat hit chatter
private void DoCombatHit( WorldEvent e )
{
if ( (e.DamageSource.bISMONSTER || e.DamageSource.player || (e.DamageSource is 'ScriptedMarine')) && (e.Thing == players[consoleplayer].mo) && (e.Thing.Health > 0) )
{
if ( !lastcombat || (gametic > lastcombat+40) )
{
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.DamageSource)) )
lastcombat = AddOneliner("friendhit",1,10);
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.DamageSource.bBOSS?2:4) && !SWWMHDoomHandler.IsCuteGirl(e.DamageSource) ) // [HDoom] don't shout at the girls
lastcombat = AddOneliner("gethit",1,15);
}
highesttic = gametic;
}
// friendly fire lines only fire up if we didn't kill them right away (because then the teamkill line should take priority)
if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player || (e.Thing is 'ScriptedMarine')) && (e.Thing.Health > 0) )
{
// make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot)
if ( (e.Thing.IsFriend(e.DamageSource) || SWWMUtility.IsCivilian(e.Thing)) && !(e.Thing is 'LampMoth') )
{
if ( !lastcombat || (gametic > lastcombat+40) )
lastcombat = AddOneliner("hitfriend",1,10);
highesttic = gametic;
}
}
}
// kill scoring
private void DoKillScoring( WorldEvent e )
{
// fall damage tracking hack
let src = e.DamageSource;
if ( (e.DamageType == 'Falling') && !e.DamageSource )
src = SWWMWhoPushedMe.RecallInstigator(e.Thing);
if ( (!src || !src.player || (src == e.Thing)) ) return;
let s = SWWMStats.Find(src.player);
if ( s )
{
s.kills++;
s.AddWeaponKill(e.Inflictor,e.Thing,e.DamageType);
}
if ( src == players[consoleplayer].mo )
{
highesttic = gametic;
if ( !lastcombat || (gametic > lastcombat+40) )
{
if ( e.Thing.IsFriend(src) || SWWMUtility.IsCivilian(e.Thing) )
lastcombat = AddOneliner("friendkill",1,5);
else if ( (!lastcombat || (gametic > lastcombat+100)) && !Random[DemoLines](0,e.Thing.bBOSS?2:5) && !SWWMHDoomHandler.IsCuteGirl(e.Thing) ) // [HDoom] don't shout at the girls
lastcombat = AddOneliner("scorekill",1,15);
}
}
// no credits unless it's a counted kill or marine (that isn't friendly)
if ( e.Thing.IsFriend(src) || (!e.Thing.default.bCountKill && !(e.Thing is 'ScriptedMarine')) )
return;
int pnum = src.PlayerNumber();
if ( level.maptime < (lastkill[pnum]+5*GameTicRate) )
multilevel[pnum]++;
else multilevel[pnum] = 0;
if ( s && (multilevel[pnum]+1 > s.mkill) )
s.mkill = multilevel[pnum]+1;
lastkill[pnum] = level.maptime;
// scoring
int score = min(1000,int(ceil(e.Thing.GetSpawnHealth()*.05)*10));
SWWMScoreObj scr = null;
if ( src.player == players[consoleplayer] )
scr = SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
int ofs = 0;
if ( e.DamageType == 'Push' )
{
score += 500;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xstr[ofs] = StringTable.Localize("$SWWM_SHAMEFUL");
scr.xcnt = ++ofs;
}
}
else if ( e.DamageType == 'Buttslam' )
{
score += 300;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xstr[ofs] = StringTable.Localize("$SWWM_BUTTSLAM");
scr.xcnt = ++ofs;
}
}
else if ( e.DamageType == 'Love' )
{
score += 600;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.FindFontColor('BlushPink');
scr.xstr[ofs] = StringTable.Localize((e.Thing is 'WolfensteinSS')?"$SWWM_LOVED_ALT":"$SWWM_LOVED");
scr.xcnt = ++ofs;
}
}
else if ( e.Inflictor is 'FroggyChair' )
{
score += 1440;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_GREEN;
scr.xstr[ofs] = StringTable.Localize("$SWWM_FROGGED");
scr.xcnt = ++ofs;
}
}
Inventory pb;
if ( e.Inflictor && (pb = e.Inflictor.FindInventory('ParriedBuff')) )
{
score += 200;
if ( pb.special1&1 ) score += 200;
if ( scr )
{
scr.xscore[ofs] = 0;
if ( pb.special1&1 ) scr.xstr[ofs] = StringTable.Localize("$SWWM_PPARRY");
else scr.xstr[ofs] = StringTable.Localize("$SWWM_PARRY");
scr.xcnt = ++ofs;
}
}
if ( (e.Damage >= e.Thing.GetSpawnHealth()*2) || (((e.Thing.Health <= e.Thing.GetGibHealth()) || (src.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageType == 'Extreme')) && !src.bNOEXTREMEDEATH && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH)) )
{
score *= 2;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xstr[ofs] = StringTable.Localize("$SWWM_OVERKILL");
scr.xcnt = ++ofs;
}
}
score = int(score*(1.+.5*min(multilevel[pnum],16)));
if ( (multilevel[pnum] > 0) && scr )
{
if ( scr )
{
scr.xscore[ofs] = (multilevel[pnum]>=16)?int.max:(multilevel[pnum]+1);
scr.xstr[ofs] = StringTable.Localize("$SWWM_MULTIKILL");
scr.xcnt = ++ofs;
}
}
spreecount[pnum]++;
if ( s && (spreecount[pnum] > s.skill) && !tookdamage[pnum] )
s.skill = spreecount[pnum];
if ( !tookdamage[pnum] )
{
int spreebonus = 10*(spreecount[pnum]);
// taper off after 10x (some people go really far with these, holy fuck)
if ( spreecount[pnum] > 10 ) spreebonus = int(spreebonus*((spreecount[pnum]/10.)**.25));
score += 100+spreebonus;
if ( (spreecount[pnum] > 0) && scr )
{
scr.xscore[ofs] = spreecount[pnum];
scr.xstr[ofs] = StringTable.Localize("$SWWM_SPREEKILL");
scr.xcnt = ++ofs;
}
}
if ( e.Thing.bBOSS )
{
score += 2000;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xstr[ofs] = StringTable.Localize("$SWWM_BOSSKILL");
scr.xcnt = ++ofs;
}
}
SWWMCredits.Give(src.player,score);
if ( scr ) scr.score = score; // update final score
if ( (level.killed_monsters+1 == level.total_monsters) && !allkills )
{
allkills = true;
SWWMCredits.Give(src.player,1000);
Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),src.player.GetUserName(),1000);
SWWMScoreObj.Spawn(1000,src.Vec3Offset(0,0,src.Height/2));
}
}
override void WorldThingDamaged( WorldEvent e )
{
if ( e.Damage > 0 ) DoDamageHandling(e);
if ( e.DamageSource && (e.DamageSource != e.Thing) ) DoCombatHit(e);
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
DoGibThing(e);
// romero hax
if ( (e.Thing is 'BossBrain') && (e.DamageType == 'Telefrag') )
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
// voodoo doll telefragging barrel hax (eviternity death exits)
if ( (e.Thing is 'ExplosiveBarrel') && (e.DamageType == 'Telefrag') && e.DamageSource.player && (e.DamageSource.player.mo != e.DamageSource) )
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill && !(e.Thing is 'ScriptedMarine') ) return;
DoKillScoring(e);
}
}

View File

@ -0,0 +1,277 @@
// WorldLoaded/WorldUnloaded events
extend Class SWWMHandler
{
// list contains a sector that belongs to each portal group
// used to ease some portal-aware functions
Array<int> psectors;
// for minimap
Array<int> ffsectors;
// level end stats
override void WorldUnloaded( WorldEvent e )
{
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
SWWMStats s;
while ( s = SWWMStats(ti.Next()) )
{
if ( !(gameinfo.gametype&GAME_STRIFE) )
{
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;
}
ClearAllShaders(players[consoleplayer]);
// reset score on dead players (death exit™)
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (players[i].Health > 0) ) continue;
let c = SWWMCredits.Find(players[i]);
if ( c ) c.credits = c.hcredits = 0;
}
// end of episode resets
if ( level.nextsecretmap.Left(6) == "enDSeQ" )
{
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
players[i].mo.GiveInventory("InventoryWipeToken",1);
}
}
}
private void SetupLockdefsCache( SWWMCachedLockInfo cli )
{
for ( int i=0; i<Wads.GetNumLumps(); i++ )
{
String lname = Wads.GetLumpName(i);
if ( !(lname ~== "LOCKDEFS") ) continue;
String data = Wads.ReadLump(i);
Array<String> lines;
lines.Clear();
data.Split(lines,"\n");
bool valid = false;
for ( int j=0; j<lines.Size(); j++ )
{
// strip leading whitespace
while ( (lines[j].Left(1) == " ") || (lines[j].Left(1) == "\t") )
lines[j] = lines[j].Mid(1);
if ( lines[j].Left(10) ~== "CLEARLOCKS" )
{
for ( int k=0; k<cli.ent.Size(); k++ )
cli.ent[k].Destroy();
cli.ent.Clear();
}
else if ( Lines[j].Left(5) ~== "LOCK " )
{
Array<String> spl;
spl.Clear();
lines[j].Split(spl," ",TOK_SKIPEMPTY);
// check game string (if any)
if ( spl.Size() > 2 )
{
if ( (spl[2] ~== "DOOM") && !(gameinfo.gametype&GAME_Doom) ) continue;
else if ( (spl[2] ~== "HERETIC") && !(gameinfo.gametype&GAME_Heretic) ) continue;
else if ( (spl[2] ~== "HEXEN") && !(gameinfo.gametype&GAME_Hexen) ) continue;
else if ( (spl[2] ~== "STRIFE") && !(gameinfo.gametype&GAME_Strife) ) continue;
else if ( (spl[2] ~== "CHEX") && !(gameinfo.gametype&GAME_Chex) ) continue;
}
// valid lock, prepare it
let li = new("LIEntry");
li.locknumber = spl[1].ToInt();
li.hascolor = false;
// see if there's a Mapcolor defined
int k = j+1;
for ( int k=j+2; k<lines.Size(); k++ )
{
// strip leading whitespace
while ( (lines[k].Left(1) == " ") || (lines[k].Left(1) == "\t") )
lines[k] = lines[k].Mid(1);
if ( lines[k].Left(5) ~== "LOCK " )
break; // we reached the next lock
if ( !(lines[k].Left(9) ~== "MAPCOLOR ") )
continue;
// here it is
spl.Clear();
lines[k].Split(spl," ",TOK_SKIPEMPTY);
if ( spl.Size() < 4 ) break;
li.hascolor = true;
li.mapcolor = Color(spl[1].ToInt(),spl[2].ToInt(),spl[3].ToInt());
}
cli.ent.Push(li);
}
}
}
}
override void WorldLoaded( WorldEvent e )
{
if ( level.levelname ~== "Modder Test Map" )
{
level.ReplaceTextures("-noflat-","kinstile",0);
S_ChangeMusic("music/CARDISH1.XM");
}
if ( !e.IsSaveGame && !e.IsReopen && (gamestate != GS_TITLELEVEL) )
AddOneliner("mapstart",3);
if ( !e.IsSaveGame && !e.IsReopen )
{
// doom vacation map01 hackaround for OPEN script not letting us
// change certain line specials in levelpostprocessor because
// HOLY FUCK IS EVERYTHING SHIT SOMETIMES
if ( (level.GetChecksum() ~== "F286BABF0D152259CD6B996E8920CA70")
|| (level.GetChecksum() ~== "A52BD2038CF814101AAB7D9C78F9ACE2") )
level.ExecuteSpecial(ACS_Execute,null,null,false,-Int('DVACATION_UNFUCK'));
// setup cached lockdefs data
let cli = SWWMCachedLockInfo.GetInstance();
if ( cli.ent.Size() == 0 ) SetupLockdefsCache(cli);
// keep a list of sectors containing 3D floors, for use by the minimap
// also does the same for the portal group list
ffsectors.Clear();
psectors.Clear();
for ( int i=0; i<level.sectors.Size(); i++ )
{
Sector s = level.sectors[i];
if ( psectors.Size() <= s.portalgroup )
psectors.Resize(s.portalgroup+1);
psectors[s.portalgroup] = s.Index();
if ( !s.Get3DFloorCount() ) continue;
int realcount = 0;
for ( int j=0; j<s.Get3DFloorCount(); j++ )
{
F3DFloor rover = s.Get3DFloor(j);
if ( rover.flags&F3DFloor.FF_THISINSIDE ) continue;
if ( !(rover.flags&F3DFloor.FF_EXISTS) ) continue;
if ( rover.alpha == 0 ) continue;
realcount++;
}
if ( !realcount ) continue;
ffsectors.Push(s.Index());
}
// for skipping over merged exit lines (sharing vertices)
Array<Line> skipme;
skipme.Clear();
// find exit lines, and use lines that aren't exits
for ( int i=0; i<level.lines.Size(); i++ )
{
Line l = level.lines[i];
// while we're at it, add teleporter sparks
if ( SWWMUtility.IsTeleportLine(l) )
{
let a = SWWMTeleportLine(Actor.Spawn("SWWMTeleportLine"));
a.tline = l;
}
if ( !SWWMUtility.IsExitLine(l) )
continue;
if ( skipme.Find(l) < skipme.Size() ) continue;
Vector3 lpos = SWWMUtility.UseLinePos(l);
// look for connected lines
int xcnt = 1;
if ( l.frontsector )
{
for ( int j=0; j<l.frontsector.Lines.Size(); j++ )
{
let l2 = l.frontsector.Lines[j];
if ( (l2 == l) || (l2.special != l.special) ) continue;
// needs to have a point in common with this one or any of the added lines
if ( (l2.v1 != l.v1) && (l2.v2 != l.v2) && (l2.v1 != l.v2) && (l2.v2 != l.v1) )
{
bool nomatches = true;
for ( int k=0; k<skipme.Size(); k++ )
{
if ( (l2.v1 != skipme[k].v1) && (l2.v2 != skipme[k].v2) && (l2.v1 != skipme[k].v2) && (l2.v2 != skipme[k].v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
}
skipme.Push(l2);
xcnt++;
lpos += SWWMUtility.UseLinePos(l2);
}
}
if ( l.backsector )
{
for ( int j=0; j<l.backsector.Lines.Size(); j++ )
{
let l2 = l.backsector.Lines[j];
if ( (l2 == l) || (l2.special != l.special) ) continue;
// needs to have a point in common with this one or any of the added lines
if ( (l2.v1 != l.v1) && (l2.v2 != l.v2) && (l2.v1 != l.v2) && (l2.v2 != l.v1) )
{
bool nomatches = true;
for ( int k=0; k<skipme.Size(); k++ )
{
if ( (l2.v1 != skipme[k].v1) && (l2.v2 != skipme[k].v2) && (l2.v1 != skipme[k].v2) && (l2.v2 != skipme[k].v1) )
continue;
nomatches = false;
break;
}
if ( nomatches ) continue;
}
skipme.Push(l2);
xcnt++;
lpos += SWWMUtility.UseLinePos(l2);
}
}
lpos /= xcnt;
SWWMInterest.Spawn(lpos,theline:l);
}
// spawn loot
Chancebox.SpawnChanceboxes();
}
else if ( e.IsSaveGame || e.IsReopen )
{
// clear all floating numbers
for ( SWWMScoreObj sc=scorenums; sc; sc=sc.Next )
sc.lifespan = 0;
for ( SWWMScoreObj sc=damnums; sc; sc=sc.Next )
sc.lifespan = 0;
// restore underwater sounds for players
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !(players[i].mo is 'Demolitionist') ) continue;
Demolitionist(players[i].mo).CheckUnderwaterAmb(true);
}
}
ClearAllShaders(players[consoleplayer]);
// recheck queues in case limits changed
RecheckQueues();
}
}

View File

@ -0,0 +1,360 @@
// WorldThing* events (excluding damage)
extend Class SWWMHandler
{
// prevents revived monsters from spawning in more golden shells
Array<Actor> alreadygold;
// attempt to optimize Ynykron singularity suction
Array<Actor> suckableactors;
override void WorldThingRevived( WorldEvent e )
{
// reattach combat tracker
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
SWWMCombatTracker.Spawn(e.Thing);
// reattach headpats
if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing)
|| SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) )
{
// you can pet the dog, and you can also pet the caco (and friends)
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
}
if ( !(e.Thing is 'PlayerPawn') ) return;
// reset some vars
if ( e.Thing.playernumber() != -1 )
{
multilevel[e.Thing.playernumber()] = 0;
spreecount[e.Thing.playernumber()] = 0;
tookdamage[e.Thing.playernumber()] = false;
lastkill[e.Thing.playernumber()] = int.min;
}
// reset uptime since player had just died
SWWMStats s = SWWMStats.Find(e.Thing.player);
if ( s ) s.lastspawn = level.totaltime;
}
private bool HexenMap40()
{
if ( level.GetChecksum() ~== "2A6C4235B942467D25FD50D5B313E67A" ) return true; // 1.1
if ( level.GetChecksum() ~== "1C5DE5A921DEE405E98E7E09D9829387" ) return true; // 1.0
if ( level.GetChecksum() ~== "EFAFE59092DE5E613562ACF52B86C37F" ) return true; // beta
return false;
}
private static bool ShouldSpawnGold()
{
int totalneeded = 0;
// check "free space" in player inventories
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let cg = players[i].mo.FindInventory("GoldShell");
if ( cg ) totalneeded += cg.MaxAmount-cg.Amount;
else totalneeded = GetDefaultByType("GoldShell").MaxAmount;
}
// subtract any shells already in the world
let ti = ThinkerIterator.Create("GoldShell");
GoldShell g;
while ( g = GoldShell(ti.Next()) )
{
if ( g.Owner ) continue;
totalneeded -= g.Amount;
}
return (totalneeded > 0);
}
override void WorldThingDied( WorldEvent e )
{
if ( e.Thing.default.bISMONSTER && ((e.Thing.default.bBOSS) || (e.Thing.GetSpawnHealth() >= 1000)) && (alreadygold.Find(e.Thing) == alreadygold.Size()) )
{
// make sure we can't farm drops from revivable enemies
// (or cause some things to spam-spawn gold shells)
// (*cough* Touhou Doom *cough*)
alreadygold.Push(e.Thing);
// weight drop chance based on total count of this monster type
// guarantees that maps that get a bit slaughtery won't become easy farms for drops
int dropweight = 0;
let ti = ThinkerIterator.Create(e.Thing.GetClass());
while ( ti.Next() ) dropweight++;
int minchance = max(1,6-(e.Thing.GetSpawnHealth()/1000));
dropweight = max(minchance,dropweight/4);
// make sure the gold shell is "worth spawning", too
if ( !Random[GoldDrop](0,dropweight) && ShouldSpawnGold() )
{
let g = Actor.Spawn("GoldShell",e.Thing.Vec3Offset(0,0,e.Thing.Height/2));
double ang = FRandom[SpareShells](0,360);
g.vel.xy = (cos(ang),sin(ang))*FRandom[SpareShells](.4,.8);
g.vel.z = FRandom[SpareShells](2.,4.);
}
}
// Korax instakill
if ( (e.Thing is 'Korax') && !e.Thing.special2 && HexenMap40() )
{
e.Thing.special2 = 1;
// terminate the monster closet scripts, open all the
// doors ourselves
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220);
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,220);
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,221);
level.ExecuteSpecial(ACS_Terminate,e.Thing,null,false,255);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,11,16);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,12,16);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,13,16);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,14,16);
level.ExecuteSpecial(Door_Open,e.Thing,null,false,10,16);
// keep the portal closed, you can't leave unless you
// kill everyone else
let t = new("UglyBoyGetsFuckedUp");
t.ChangeStatNum(Thinker.STAT_USER);
}
if ( swwm_partytime )
{
let pt = Actor.Spawn("PartyTime",e.Thing.pos);
pt.target = e.Thing;
}
// force insert gib animations on some vanilla Doom monsters
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
if ( !gotgibbed ) return;
if ( (e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") )
ExtraGibDeaths.GibThis(e.Thing,"DemonXDeath");
else if ( e.Thing.GetClass() == "HellKnight" )
ExtraGibDeaths.GibThis(e.Thing,"KnightXDeath");
else if ( e.Thing.GetClass() == "BaronOfHell" )
ExtraGibDeaths.GibThis(e.Thing,"BaronXDeath");
else if ( e.Thing.GetClass() == "Cacodemon" )
ExtraGibDeaths.GibThis(e.Thing,"CacoXDeath");
else if ( e.Thing.GetClass() == "Revenant" )
ExtraGibDeaths.GibThis(e.Thing,"BonerXDeath");
else if ( e.Thing.GetClass() == "Archvile" )
ExtraGibDeaths.GibThis(e.Thing,"VileXDeath");
}
private void DoKeyTagFix( Actor a )
{
if ( a is 'SWWMKey' ) return; // mod's custom keys are fine
if ( a is 'RedCard' ) a.SetTag("$T_REDCARD");
else if ( a is 'BlueCard' ) a.SetTag("$T_BLUECARD");
else if ( a is 'YellowCard' ) a.SetTag("$T_YELLOWCARD");
else if ( a is 'RedSkull' ) a.SetTag("$T_REDSKULL");
else if ( a is 'BlueSkull' ) a.SetTag("$T_BLUESKULL");
else if ( a is 'YellowSkull' ) a.SetTag("$T_YELLOWSKULL");
else if ( a is 'KeyYellow' ) a.SetTag("$T_YELLOWKEY");
else if ( a is 'KeyGreen' ) a.SetTag("$T_GREENKEY");
else if ( a is 'KeyBlue' ) a.SetTag("$T_BLUEKEY");
else if ( a.GetClassName() == 'KeyRed' ) a.SetTag("$T_REDKEY");
else if ( a is 'KeySteel' ) a.SetTag("$T_KEYSTEEL");
else if ( a is 'KeyCave' ) a.SetTag("$T_KEYCAVE");
else if ( a is 'KeyAxe' ) a.SetTag("$T_KEYAXE");
else if ( a is 'KeyFire' ) a.SetTag("$T_KEYFIRE");
else if ( a is 'KeyEmerald' ) a.SetTag("$T_KEYEMERALD");
else if ( a is 'KeyDungeon' ) a.SetTag("$T_KEYDUNGEON");
else if ( a is 'KeySilver' ) a.SetTag("$T_KEYSILVER");
else if ( a is 'KeyRusted' ) a.SetTag("$T_KEYRUSTED");
else if ( a is 'KeyHorn' ) a.SetTag("$T_KEYHORN");
else if ( a is 'KeySwamp' ) a.SetTag("$T_KEYSWAMP");
else if ( a is 'KeyCastle' ) a.SetTag("$T_KEYCASTLE");
}
// tempfix keys have no tags
static void KeyTagFix( Actor a )
{
let hnd = SWWMHandler(Find("SWWMHandler"));
if ( hnd ) hnd.DoKeyTagFix(a);
}
// copies the floatbob of overlapping identical items, so it doesn't look weird
private void CopyFloatBob( Actor a )
{
let bt = BlockThingsIterator.Create(a,16);
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
a.floatbobphase = t.floatbobphase;
a.angle = t.angle; // also copy angle
break;
}
}
override void WorldThingDestroyed( WorldEvent e )
{
if ( !e.Thing.default.bSHOOTABLE || !e.Thing.default.bMISSILE )
return;
// remove from suckables
int pos = suckableactors.Find(e.Thing);
if ( pos >= suckableactors.Size() ) return;
suckableactors.Delete(pos);
}
override void WorldThingSpawned( WorldEvent e )
{
IWantDieSpawn(e);
if ( (e.Thing is 'TeleportDest') || (e.Thing is 'BossTarget') )
{
let d = Actor.Spawn("SWWMTeleportDest",e.Thing.pos);
d.bNOGRAVITY = e.Thing.bNOGRAVITY;
}
if ( e.Thing is 'Inventory' )
CopyFloatBob(e.Thing);
if ( swwm_doomfall && e.Thing.bISMONSTER )
e.Thing.bFALLDAMAGE = true;
if ( e.Thing is 'Key' )
{
DoKeyTagFix(e.Thing);
SWWMInterest.Spawn(thekey:Key(e.Thing));
}
if ( indoomvacation == -1 ) indoomvacation = SWWMUtility.InDoomVacation();
if ( e.Thing.GetClass() == 'Pig' )
e.Thing.SetTag("$FN_PIG"); // missing in gzdoom
// eviternity stuff
else if ( (e.Thing.GetClassName() == "Archangelus")
|| (e.Thing.GetClassName() == "ArchangelusA")
|| (e.Thing.GetClassName() == "ArchangelusB") )
e.Thing.SetTag("$FN_ANGEL");
else if ( e.Thing.GetClassName() == "AstralCaco" )
e.Thing.SetTag("$FN_ASTRAL");
else if ( e.Thing.GetClassName() == "Annihilator" )
{
e.Thing.SetTag("$FN_ANNIHIL");
// OH BOY, THESE AREN'T CHANGEABLE
//e.Thing.Obituary = "$OB_ANNIHIL";
}
else if ( e.Thing.GetClassName() == "FormerCaptain" )
{
e.Thing.SetTag("$FN_FCAPTAIN");
//e.Thing.Obituary = "$OB_FCAPTAIN";
}
else if ( e.Thing.GetClassName() == "NightmareDemon" )
{
e.Thing.SetTag("$FN_NDEMON");
//e.Thing.Obituary = "$OB_NDEMON";
}
// doom vacation stuff
else if ( indoomvacation )
{
if ( e.Thing.GetClassName() == "Babe" )
{
e.Thing.bSHOOTABLE = false; // no hurt
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .2;
}
else if ( e.Thing.GetClassName() == "CommanderKeen" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .4;
HeadpatTracker(hp).hdoomangfix = 5;
}
else if ( e.Thing.GetClassName() == "BBChair" )
{
e.Thing.bUSESPECIAL = false;
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .2;
HeadpatTracker(hp).hdoomangfix = 15;
HeadpatTracker(hp).patstate = e.Thing.MeleeState;
}
else if ( e.Thing.GetClassName() == "EvilEye" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .1;
}
else if ( e.Thing.GetClassName() == "HeadCandles" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomangfix = 20;
}
else if ( e.Thing.GetClassName() == "HeartColumn" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = -.3;
}
else if ( e.Thing.GetClassName() == "Meat2" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .6;
HeadpatTracker(hp).hdoomangfix = -15;
HeadpatTracker(hp).dvacationarghack = true;
}
else if ( e.Thing.GetClassName() == "Meat3" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .6;
HeadpatTracker(hp).hdoomangfix = 20;
HeadpatTracker(hp).dvacationarghack = true;
}
else if ( e.Thing.GetClassName() == "LegsBabe" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = -1.5;
HeadpatTracker(hp).hdoomangfix = 20;
HeadpatTracker(hp).dvacationarghack = true;
}
else if ( e.Thing.GetClassName() == "Meat4" )
{
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
HeadpatTracker(hp).hdoomheightfix = .6;
HeadpatTracker(hp).hdoomangfix = 15;
HeadpatTracker(hp).dvacationarghack = true;
}
}
if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing)
|| SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) )
{
// you can pet the dog, and you can also pet the caco (and friends)
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
}
SWWMCombatTracker trk;
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
trk = SWWMCombatTracker.Spawn(e.Thing);
if ( !(e.Thing is 'LampMoth') && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER || e.Thing.bCORPSE || (e.Thing is 'Inventory') || (e.Thing is 'CompanionLamp')) )
{
if ( (swwm_shadows == 2) || ((swwm_shadows == 1) && ((e.Thing is 'Demolitionist') || (e.Thing.SpawnState.sprite == e.Thing.GetSpriteIndex('XZW1')))) )
SWWMShadow.Track(e.Thing);
}
// Ynykron vortex optimization (faster than a thinker iterator)
if ( e.Thing.bSHOOTABLE || e.Thing.bMISSILE )
SuckableActors.Push(e.Thing);
// vanilla blood color changes
if ( (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "Bishop") || (e.Thing.GetClass() == "Korax") )
{
let gb = Actor.Spawn("GreenBloodReference");
e.Thing.CopyBloodColor(gb);
gb.Destroy();
}
else if ( e.Thing.GetClass() == "Cacodemon" )
{
let bb = Actor.Spawn("BlueBloodReference");
e.Thing.CopyBloodColor(bb);
bb.Destroy();
}
else if ( (e.Thing.GetClass() == "Wizard") || (e.Thing.GetClass() == "Heresiarch") || (e.Thing.GetClass() == "Sorcerer2") )
{
let pb = Actor.Spawn("PurpleBloodReference");
e.Thing.CopyBloodColor(pb);
pb.Destroy();
}
else if ( e.Thing.GetClass() == "LostSoul" )
e.Thing.bNOBLOOD = true;
VanillaBossSpawn(e,trk);
// inflation check
if ( trk )
{
trk.maxhealth = trk.lasthealth = e.Thing.Health;
trk.intp.Reset(trk.lasthealth);
}
}
}

View File

@ -0,0 +1,297 @@
// WorldTick functions
extend Class SWWMHandler
{
transient Array<Actor> combatactors;
transient Array<Int> combattics;
transient int highesttic, lastcombat;
int lastitemcount[MAXPLAYERS];
transient String curlang;
transient bool curfuntags;
SWWMSimpleTracker strackers;
int strackers_cnt;
bool mnotify;
bool allkills, allitems, allsecrets;
bool mapclear;
int mapclearagain, restartmus;
String lastmus;
int lastorder;
bool lastloop;
private void LangRefresh()
{
if ( (language != curlang) || (swwm_funtags != curfuntags) )
{
// manually refresh some tags if language has changed
for ( SWWMCombatTracker t=trackers; t; t=t.next )
t.UpdateTag();
for ( SWWMInterest p=intpoints; p; p=p.next )
{
if ( (p.type != INT_Key) || !p.trackedkey ) continue;
p.keytag = p.trackedkey.GetTag();
}
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !Demolitionist(players[i].mo) ) continue;
for ( SWWMItemSense s=Demolitionist(players[i].mo).itemsense; s; s=s.next )
s.UpdateTag();
}
}
curlang = language;
curfuntags = swwm_funtags;
}
// countable item scoring
private void ItemCountTrack()
{
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] ) continue;
if ( players[i].itemcount > lastitemcount[i] )
{
int score = 10*(players[i].itemcount-lastitemcount[i]);
if ( (level.total_items == level.found_items) && !allitems )
{
allitems = true;
Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),players[i].GetUserName(),500);
score += 490;
}
SWWMCredits.Give(players[i],score);
SWWMScoreObj.Spawn(score,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
lastitemcount[i] = players[i].itemcount;
let s = SWWMStats.Find(players[i]);
s.items++;
}
}
}
// combat tracking
private void CombatTrack()
{
// prune old entries
for ( int i=0; i<combatactors.Size(); i++ )
{
if ( combattics[i] > highesttic )
highesttic = combattics[i];
if ( combatactors[i]
&& (combatactors[i].Health > 0)
&& !combatactors[i].bKILLED
&& !combatactors[i].bCORPSE
&& (combatactors[i].target == players[consoleplayer].mo)
&& (combattics[i]+2000 > gametic) )
continue;
combatactors.Delete(i);
combattics.Delete(i);
i--;
}
bool enteredcombat = false;
// add new entries
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !a.player && !a.bISMONSTER ) continue;
// ignore the dead
if ( (a.Health <= 0) || a.bKILLED || a.bCORPSE ) continue;
// ignore friends
if ( a.IsFriend(players[consoleplayer].mo) ) continue;
// [Strife] ignore if not in combat
if ( (gameinfo.gametype&GAME_Strife) && !a.bINCOMBAT && !a.bJUSTATTACKED ) continue;
// [Strife] ignore certain classes
if ( (a is 'RatBuddy') || (a is 'Peasant') || (a is 'Beggar') ) continue;
// [Strife] ignore Oracle's spectre while it's inactive
if ( (a is 'AlienSpectre3') && a.InStateSequence(a.CurState,a.FindState("Spawn")) ) continue;
// ignore if not targetted or player can't see it
if ( (a.target != players[consoleplayer].mo)
|| !SWWMUtility.InPlayerFOV(players[consoleplayer],a) ) continue;
// [HDoom] ignore cute girls
if ( SWWMHDoomHandler.IsCuteGirl(a.target) ) continue;
// is it already in?
bool addme = true;
for ( int i=0; i<combatactors.Size(); i++ )
{
if ( combatactors[i] != a ) continue;
addme = false;
combattics[i] = gametic;
break;
}
// add it in
if ( addme )
{
combatactors.Push(a);
combattics.Push(gametic);
enteredcombat = true;
}
}
if ( enteredcombat && (!highesttic || (gametic > highesttic+700)) )
lastcombat = AddOneliner("fightstart",1,10);
}
private void OneHundredPercentCheck()
{
if ( !mapclear && (restartmus > 0) )
{
restartmus--;
if ( restartmus == 0 ) S_ChangeMusic(lastmus,lastorder,lastloop,true);
return;
}
// ignore levels that have NOTHING
if ( (level.total_secrets <= 0) && (level.total_items <= 0) && (level.total_monsters <= 0) ) return;
if ( mapclear )
{
if ( (swwm_silencemap == 1) && (musplaying.name != "") )
{
lastmus = musplaying.name;
lastorder = musplaying.baseorder;
lastloop = musplaying.loop;
S_ChangeMusic("",force:true);
}
else if ( (swwm_silencemap > 1) && (musplaying.name != "music/olg.ogg") )
{
lastmus = musplaying.name;
lastorder = musplaying.baseorder;
lastloop = musplaying.loop;
S_ChangeMusic("music/olg.ogg",force:true);
}
if ( (level.found_secrets < level.total_secrets) || (level.found_items < level.total_items) || (level.killed_monsters < level.total_monsters) )
{
if ( swwm_silencemap > 0 )
{
restartmus = 25;
S_ChangeMusic("",force:true);
}
S_StartSound("recordscratch",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
mapclear = false;
if ( mapclearagain > 1 ) Console.Printf(StringTable.Localize("$SWWM_NOTCLEARAGAIN"));
else Console.Printf(StringTable.Localize("$SWWM_NOTCLEAR"));
}
return;
}
if ( (level.found_secrets < level.total_secrets) || (level.found_items < level.total_items) || (level.killed_monsters < level.total_monsters) ) return;
restartmus = 0;
mapclear = true;
if ( mapclearagain ) Console.Printf(StringTable.Localize("$SWWM_ALLCLEARAGAIN"),500);
else Console.Printf(StringTable.Localize("$SWWM_ALLCLEAR"),5000);
S_StartSound("misc/wow",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE|CHANF_OVERLAP,1,ATTN_NONE);
lastmus = musplaying.name;
lastorder = musplaying.baseorder;
lastloop = musplaying.loop;
if ( swwm_silencemap == 1 ) S_ChangeMusic("",force:true);
else if ( swwm_silencemap > 1 ) S_ChangeMusic("music/olg.ogg",force:true);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
let f = Actor.Spawn("PartyTime",players[i].mo.pos);
f.bAMBUSH = true;
if ( mapclearagain )
{
SWWMCredits.Give(players[i],500);
SWWMScoreObj.Spawn(500,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
}
else
{
SWWMCredits.Give(players[i],5000);
SWWMScoreObj.Spawn(5000,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
}
}
mapclearagain++;
}
// "simple" tracking (used by the minimap)
private void SimpleTracking()
{
if ( !swwm_mm_enable )
{
while ( strackers )
{
SWWMSimpleTracker next = strackers.next;
strackers.Destroy();
strackers = next;
}
strackers_cnt = 0;
return;
}
// update trackers for anything around the player
bool thesight = players[consoleplayer].mo.FindInventory("Omnisight");
double viewdist = SWWMStatusBar.MAPVIEWDIST*swwm_mm_zoom;
BlockThingsIterator bt = BlockThingsIterator.Create(players[consoleplayer].Camera,viewdist);
while ( bt.Next() )
{
let a = bt.Thing;
if ( !a ) continue;
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( a == players[consoleplayer].Camera )
continue;
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
continue;
if ( !thesight && !a.IsFriend(players[consoleplayer].mo) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( a.bKILLED || (a.Health <= 0) )
continue;
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner) )
continue;
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
continue;
SWWMSimpleTracker.Track(a);
}
if ( swwm_mm_portaloverlay && (psectors.Size() > 1) )
{
// oh boy here we go
int thisgroup = players[consoleplayer].Camera.CurSector.portalgroup;
for ( int i=0; i<psectors.Size(); i++ )
{
if ( i == thisgroup ) continue;
Vector2 relpos = players[consoleplayer].Camera.pos.xy+SWWMUtility.PortalDisplacement(level.Sectors[psectors[thisgroup]],level.Sectors[psectors[i]]);
bt = BlockThingsIterator.CreateFromPos(relpos.x,relpos.y,players[consoleplayer].Camera.pos.z,players[consoleplayer].Camera.pos.z+players[consoleplayer].Camera.height,viewdist,false);
while ( bt.Next() )
{
let a = bt.Thing;
if ( !a ) continue;
Vector2 rv = a.pos.xy-relpos;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( a == players[consoleplayer].Camera )
continue;
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
continue;
if ( !thesight && !a.IsFriend(players[consoleplayer].mo) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( a.bKILLED || (a.Health <= 0) )
continue;
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner) )
continue;
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
continue;
SWWMSimpleTracker.Track(a);
}
}
}
SWWMSimpleTracker trk = strackers;
while ( trk )
{
SWWMSimpleTracker next = trk.next;
// minimize lifespan of destroyed targets
if ( !trk.target ) trk.lastupdate = min(trk.lastupdate,level.maptime);
else if ( !trk.expired )
{
// "last breath" update
if ( (trk.target.bKILLED || (trk.target.Health <= 0))
|| ((trk.target is 'Inventory') && (!trk.target.bSPECIAL || Inventory(trk.target).Owner))
|| ((trk.target is 'Chancebox') && (trk.target.CurState != trk.target.SpawnState)) )
trk.Update();
}
// prune expired trackers
if ( trk.lastupdate+140 < level.maptime )
{
if ( !trk.prev ) strackers = trk.next;
else trk.prev.next = trk.next;
if ( trk.next ) trk.next.prev = trk.prev;
trk.Destroy();
strackers_cnt--;
}
trk = next;
}
}
}

File diff suppressed because it is too large Load Diff