413 lines
14 KiB
Text
413 lines
14 KiB
Text
// 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;
|
|
bool mnotify;
|
|
bool allkills, allitems, allsecrets;
|
|
bool mapclear;
|
|
int mapclearagain, restartmus, startmus;
|
|
double musvol;
|
|
String lastmus;
|
|
int lastorder;
|
|
bool lastloop;
|
|
transient ThinkerIterator cti, qti;
|
|
|
|
private void LangRefresh()
|
|
{
|
|
if ( (language != curlang) || (swwm_funtags != curfuntags) )
|
|
{
|
|
// manually refresh some tags if language has changed
|
|
if ( !qti ) qti = ThinkerIterator.Create("SWWMQuickCombatTracker",Thinker.STAT_INVENTORY);
|
|
else qti.Reinit();
|
|
SWWMQuickCombatTracker qt;
|
|
while ( qt=SWWMQuickCombatTracker(qti.Next()) )
|
|
qt.UpdateTag(self);
|
|
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 ( !deathmatch && !(gameinfo.gametype&GAME_Hexen) && (level.total_items == level.found_items) && !allitems )
|
|
{
|
|
allitems = true;
|
|
if ( i == consoleplayer ) Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),500);
|
|
else Console.Printf(StringTable.Localize("$SWWM_LASTITEMREM"),players[i].GetUserName(),500);
|
|
score += 490;
|
|
SWWMUtility.AchievementProgressInc("allitems",1,players[i]);
|
|
}
|
|
SWWMCredits.Give(players[i],score);
|
|
SWWMScoreObj.SpawnFromHandler(self,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()
|
|
{
|
|
// if players are above 1HP, reset the "one hp kill" counter
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] ) continue;
|
|
if ( players[i].Health != 1 ) onehpspree[i] = 0;
|
|
}
|
|
// 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
|
|
if ( !cti ) cti = ThinkerIterator.Create("Actor");
|
|
else cti.Reinit();
|
|
Actor a, keyactor = null;
|
|
bool bossfound = false;
|
|
while ( a = Actor(cti.Next()) )
|
|
{
|
|
if ( !a.player && !a.bISMONSTER ) continue;
|
|
// ignore the dead
|
|
if ( (a.Health <= 0) || a.bKILLED || a.bCORPSE ) continue;
|
|
// ignore if not targetted
|
|
if ( a.target != players[consoleplayer].mo ) continue;
|
|
// ignore friends
|
|
if ( a.IsFriend(players[consoleplayer].mo) ) continue;
|
|
// [HDoom] ignore cute girls
|
|
if ( SWWMHDoomHandler.IsCuteGirl(a.target) ) continue;
|
|
// ignore if player can't see it
|
|
if ( !SWWMUtility.InPlayerFOV(players[consoleplayer],a) ) 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 ( a.bBOSS || a.FindInventory("BossMarker") )
|
|
bossfound = true;
|
|
}
|
|
}
|
|
// be smart, demo-chan, don't shout if you're invisible, or you'll make it worse
|
|
if ( enteredcombat && ((bossfound && (!lastcombat || (gametic > lastcombat+240))) || (!bossfound && (!highesttic || (gametic > highesttic+700)))) && !players[consoleplayer].mo.FindInventory("GhostPower") )
|
|
lastcombat = AddOneliner("fightstart",1,10);
|
|
}
|
|
|
|
private void OneHundredPercentCheck()
|
|
{
|
|
// not in DM
|
|
if ( deathmatch ) return;
|
|
// not in Hexen, due to its fully hub-based nature
|
|
if ( gameinfo.gametype&GAME_Hexen ) return;
|
|
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 ( (musplaying.name != "music/nomusic.ogg") && (musplaying.name != "music/solitary.ogg") )
|
|
{
|
|
lastmus = musplaying.name;
|
|
lastorder = musplaying.baseorder;
|
|
lastloop = musplaying.loop;
|
|
S_ChangeMusic((startmus>0)?"music/nomusic.ogg":"music/solitary.ogg",force:true);
|
|
}
|
|
if ( startmus > 0 ) startmus--;
|
|
else if ( startmus == 0 )
|
|
{
|
|
startmus = -1;
|
|
S_ChangeMusic("music/solitary.ogg",force:true);
|
|
}
|
|
if ( (level.found_secrets < level.total_secrets) || (level.found_items < level.total_items) || (level.killed_monsters < level.total_monsters) )
|
|
{
|
|
restartmus = 25;
|
|
S_ChangeMusic("music/nomusic.ogg",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;
|
|
S_ChangeMusic("music/nomusic.ogg",force:true);
|
|
startmus = 1050;
|
|
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.SpawnFromHandler(self,500,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
}
|
|
else
|
|
{
|
|
SWWMCredits.Give(players[i],5000);
|
|
SWWMScoreObj.SpawnFromHandler(self,5000,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
|
|
}
|
|
}
|
|
mapclearagain++;
|
|
if ( !iwantdie ) return;
|
|
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
|
|
SWWMStats s;
|
|
while ( s = SWWMStats(ti.Next()) )
|
|
{
|
|
if ( s.deaths > 0 )
|
|
return;
|
|
}
|
|
SWWMUtility.MarkAchievement("wantdie",players[consoleplayer]);
|
|
}
|
|
|
|
private void UpdateHUDObjects()
|
|
{
|
|
// score objects
|
|
SWWMScoreObj so = scorenums;
|
|
SWWMScoreObj soprev = null, sonext;
|
|
while ( so )
|
|
{
|
|
sonext = so.next;
|
|
if ( so.Tick() )
|
|
{
|
|
if ( soprev ) soprev.next = sonext;
|
|
else scorenums = sonext;
|
|
so.Destroy();
|
|
}
|
|
else soprev = so;
|
|
so = sonext;
|
|
}
|
|
SWWMDamNum dn = damnums;
|
|
SWWMDamNum dnprev = null, dnnext;
|
|
while ( dn )
|
|
{
|
|
dnnext = dn.next;
|
|
if ( dn.Tick() )
|
|
{
|
|
if ( dnprev ) dnprev.next = dnnext;
|
|
else damnums = dnnext;
|
|
dn.Destroy();
|
|
}
|
|
else dnprev = dn;
|
|
dn = dnnext;
|
|
}
|
|
// interest markers
|
|
SWWMInterest ip = intpoints;
|
|
SWWMInterest ipprev = null, ipnext;
|
|
while ( ip )
|
|
{
|
|
ipnext = ip.next;
|
|
if ( ip.Tick() )
|
|
{
|
|
if ( ipprev ) ipprev.next = ipnext;
|
|
else intpoints = ipnext;
|
|
ip.Destroy();
|
|
}
|
|
else ipprev = ip;
|
|
ip = ipnext;
|
|
}
|
|
}
|
|
|
|
// "simple" tracking (used by the minimap)
|
|
private void SimpleTracking()
|
|
{
|
|
if ( (gamestate != GS_LEVEL) || !swwm_mm_enable )
|
|
{
|
|
while ( strackers )
|
|
{
|
|
SWWMSimpleTracker next = strackers.next;
|
|
strackers.Destroy();
|
|
strackers = next;
|
|
}
|
|
return;
|
|
}
|
|
// update trackers for anything around the player
|
|
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 is 'GhostTarget' )
|
|
continue;
|
|
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
|
|
continue;
|
|
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !(a.IsFriend(players[consoleplayer].mo) && !(a.player && (a.player.mo != a))) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
if ( a.bKILLED || (a.Health <= 0) || a.bUnmorphed )
|
|
continue;
|
|
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner) )
|
|
continue;
|
|
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
// we need to refer to the suckables array to find missiles
|
|
foreach ( a:suckableactors )
|
|
{
|
|
if ( !a || !a.bMISSILE ) 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 ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
foreach ( a:beams )
|
|
{
|
|
if ( !a ) continue;
|
|
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
|
|
double rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
|
|
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
|
|
continue;
|
|
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
bt.Destroy();
|
|
// oh boy here we go
|
|
int thisgroup = players[consoleplayer].Camera.CurSector.portalgroup;
|
|
for ( int i=0; i<level.GetPortalGroupCount(); i++ )
|
|
{
|
|
if ( i == thisgroup ) continue;
|
|
Vector2 relpos = players[consoleplayer].Camera.pos.xy+level.GetDisplacement(thisgroup,i);
|
|
if ( bt ) bt.Destroy();
|
|
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 is 'GhostTarget' )
|
|
continue;
|
|
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
|
|
continue;
|
|
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !a.IsFriend(players[consoleplayer].mo) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
if ( a.bKILLED || (a.Health <= 0) || a.bUnmorphed )
|
|
continue;
|
|
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner) )
|
|
continue;
|
|
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
// we need to refer to the suckables array to find missiles
|
|
foreach ( a:suckableactors )
|
|
{
|
|
if ( !a || !a.bMISSILE ) continue;
|
|
Vector2 rv = a.pos.xy-relpos;
|
|
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
|
|
continue;
|
|
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
foreach ( a:beams )
|
|
{
|
|
if ( !a ) continue;
|
|
Vector2 rv = a.pos.xy-relpos;
|
|
double rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
|
|
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
|
|
continue;
|
|
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(self,a);
|
|
}
|
|
}
|
|
SWWMSimpleTracker trk = strackers;
|
|
SWWMSimpleTracker prev = null, next;
|
|
while ( trk )
|
|
{
|
|
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.target.default.bMISSILE && !trk.target.bMISSILE)
|
|
|| trk.target.bUnmorphed )
|
|
trk.Update();
|
|
}
|
|
// prune expired trackers
|
|
if ( trk.lastupdate+140 < level.maptime )
|
|
{
|
|
if ( !prev ) strackers = trk.next;
|
|
else prev.next = trk.next;
|
|
trk.Destroy();
|
|
}
|
|
else prev = trk;
|
|
trk = next;
|
|
}
|
|
}
|
|
}
|