380 lines
13 KiB
Text
380 lines
13 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;
|
|
int strackers_cnt;
|
|
bool mnotify;
|
|
bool allkills, allitems, allsecrets;
|
|
bool mapclear;
|
|
int mapclearagain, restartmus, startmus;
|
|
double musvol;
|
|
String lastmus;
|
|
int lastorder;
|
|
bool lastloop;
|
|
transient ThinkerIterator cti;
|
|
Actor tauntedboss;
|
|
|
|
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 ( !deathmatch && !(gameinfo.gametype&GAME_Hexen) && (level.total_items == level.found_items) && !allitems )
|
|
{
|
|
allitems = true;
|
|
Console.Printf(StringTable.Localize("$SWWM_LASTITEM"),players[i].GetUserName(),500);
|
|
score += 490;
|
|
SWWMUtility.AchievementProgressInc("allitems",1,players[i]);
|
|
}
|
|
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()
|
|
{
|
|
// 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 friends
|
|
if ( a.IsFriend(players[consoleplayer].mo) ) 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 ( 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+120))) || (!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 ( swwm_silencemap )
|
|
{
|
|
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) )
|
|
{
|
|
if ( swwm_silencemap )
|
|
{
|
|
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;
|
|
if ( swwm_silencemap )
|
|
{
|
|
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.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++;
|
|
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]);
|
|
}
|
|
|
|
// "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;
|
|
}
|
|
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 is 'GhostTarget' )
|
|
continue;
|
|
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
|
|
continue;
|
|
if ( !thesight && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !(a.IsFriend(players[consoleplayer].mo) && !(a.player && (a.player.mo != a))) && !players[consoleplayer].Camera.CheckSight(a,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(a);
|
|
}
|
|
// we need to refer to the suckables array to find missiles
|
|
for ( int i=0; i<suckableactors.Size(); i++ )
|
|
{
|
|
let a = suckableactors[i];
|
|
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 ( !thesight && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(a);
|
|
}
|
|
for ( int i=0; i<beams.Size(); i++ )
|
|
{
|
|
let a = beams[i];
|
|
if ( !a ) continue;
|
|
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
|
|
double rad = a.speed*cos(a.pitch);
|
|
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
|
|
continue;
|
|
if ( !thesight && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(a);
|
|
}
|
|
bt.Destroy();
|
|
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]]);
|
|
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 ( !thesight && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !a.IsFriend(players[consoleplayer].mo) && !players[consoleplayer].Camera.CheckSight(a,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(a);
|
|
}
|
|
// we need to refer to the suckables array to find missiles
|
|
for ( int i=0; i<suckableactors.Size(); i++ )
|
|
{
|
|
let a = suckableactors[i];
|
|
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 ( !thesight && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
continue;
|
|
SWWMSimpleTracker.Track(a);
|
|
}
|
|
for ( int i=0; i<beams.Size(); i++ )
|
|
{
|
|
let a = beams[i];
|
|
if ( !a ) continue;
|
|
Vector2 rv = a.pos.xy-relpos;
|
|
double rad = a.speed*cos(a.pitch);
|
|
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
|
|
continue;
|
|
if ( !thesight && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !players[consoleplayer].Camera.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
|
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.target.default.bMISSILE && !trk.target.bMISSILE)
|
|
|| trk.target.bUnmorphed )
|
|
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;
|
|
}
|
|
}
|
|
}
|