Healthbar / Damnum backport from sidemods.

This commit is contained in:
Mari the Deer 2022-08-22 17:36:57 +02:00
commit b40d8d0633
1743 changed files with 683 additions and 869 deletions

View file

@ -63,7 +63,15 @@ extend Class SWWMHandler
// damage numbers, combat tracking, etc.
private void DoDamageHandling( WorldEvent e )
{
SWWMScoreObj.SpawnFromHandler(self,-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),ST_Damage);
SWWMDamNum.SpawnFromHandler(self,e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),e.DamageType);
// combat tracker updates if either an ally of the player is hit, or an ally hit this enemy
// (this automatically also does the same for the player itself, of course)
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo ) continue;
if ( players[i].mo.IsFriend(e.Thing) || (e.DamageSource && players[i].mo.IsFriend(e.DamageSource)) )
SWWMQuickCombatTracker.Update(self,players[i],e.Thing,e.Damage);
}
// stats
if ( e.Thing.player )
{

View file

@ -126,9 +126,6 @@ extend Class SWWMHandler
spreecount[e.playernumber] = 0;
tookdamage[e.playernumber] = false;
lastkill[e.playernumber] = int.min;
// reset combat tracker
if ( !swwm_notrack )
SWWMCombatTracker.Spawn(self,players[e.playernumber].mo,true);
// reset score (optional) if inventory should be cleared
if ( swwm_resetscore && level.info.flags2&LEVEL2_RESETINVENTORY && !e.IsReturn )
c.credits = 0;
@ -152,8 +149,5 @@ extend Class SWWMHandler
spreecount[e.playernumber] = 0;
tookdamage[e.playernumber] = false;
lastkill[e.playernumber] = int.min;
// reset combat tracker
if ( !swwm_notrack )
SWWMCombatTracker.Spawn(self,players[e.playernumber].mo,true);
}
}

View file

@ -257,40 +257,6 @@ extend Class SWWMHandler
Console.Printf("Extended hitboxes can't be re-enabled at runtime. Please restart the map.");
return;
}
else if ( e.Name ~== "swwmupdatetrackers" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
if ( swwm_notrack )
{
int n = trackers_cnt;
while ( trackers )
{
let next = trackers.next;
trackers.Destroy(); // wow that's simple, all in one line
trackers = next;
}
trackers_cnt = 0;
Console.Printf("%d trackers removed.",n);
return;
}
int n = trackers_cnt;
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( (!a.bSHOOTABLE && !a.bISMONSTER) || (a is 'LampMoth') || (a is 'CompanionLamp') ) continue;
let trk = SWWMCombatTracker.Spawn(self,a,true);
if ( !a.player ) trk.maxhealth = max(a.health,a.GetSpawnHealth());
}
n = (trackers_cnt-n);
Console.Printf("%d trackers added.",n);
return;
}
else if ( e.Name ~== "swwmtrimsuckables" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )

View file

@ -92,7 +92,8 @@ extend Class SWWMHandler
ui bool ui_initialized;
ui TextureID bbar_f, bbar_r, bbar_d;
ui double bossalpha;
ui DynamicValueInterpolator ihealth, ihealthr;
ui SmoothLinearValueInterpolator ihealth;
ui SmoothDynamicValueInterpolator ihealthr;
ui int thealth, hmax;
ui int oldhealth[30];
ui int cummdamage, lastcummtic; // please do not misread
@ -184,7 +185,7 @@ extend Class SWWMHandler
return MAP_EVMAP30;
return MAP_NONE;
}
private void VanillaBossSpawn( WorldEvent e, SWWMCombatTracker trk )
private void VanillaBossSpawn( WorldEvent e )
{
if ( bossmap == -1 ) bossmap = WhichVanillaBossMap();
if ( bossmap == MAP_DE1M8 )
@ -193,7 +194,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 3;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_BRUISERS";
@ -204,7 +204,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_CYBIE";
@ -215,7 +214,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 6;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
}
@ -227,7 +225,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_SPIDER2";
@ -238,7 +235,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_DIMPLE";
@ -250,7 +246,6 @@ extend Class SWWMHandler
bossbrainactor = e.Thing;
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 40; // goodbye, instakills
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
}
@ -267,7 +262,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_CYBIE2";
@ -278,7 +272,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_LICHES";
@ -289,7 +282,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 3;
if ( trk ) trk.bBOSS = true;
e.Thing.GiveInventory('BossMarker',1);
}
bosstag = "$BT_MINOTAUR";
@ -300,7 +292,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DSPARIL";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -312,7 +303,6 @@ extend Class SWWMHandler
initialized = false;
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 8;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DSPARIL2";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -325,7 +315,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_CLERIC";
e.Thing.GiveInventory('BossMarker',1);
}
@ -336,7 +325,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_FIGHTER";
e.Thing.GiveInventory('BossMarker',1);
}
@ -347,7 +335,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 2;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_MAGE";
e.Thing.GiveInventory('BossMarker',1);
}
@ -358,7 +345,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DRAGON";
e.Thing.GiveInventory('BossMarker',1);
}
@ -369,7 +355,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 8;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_HERESIARCH";
e.Thing.GiveInventory('BossMarker',1);
}
@ -380,7 +365,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 10;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_KORAX";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -392,7 +376,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 4;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_DEATHKINGS";
initialized = true; // healthbar shows from the start
e.Thing.GiveInventory('BossMarker',1);
@ -416,7 +399,6 @@ extend Class SWWMHandler
initialized = false;
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_ARCHANGELUS";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -429,7 +411,6 @@ extend Class SWWMHandler
initialized = false;
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 5;
if ( trk ) trk.bBOSS = true;
bosstag = "$BT_ARCHANGELUS";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -440,7 +421,6 @@ extend Class SWWMHandler
{
bossactors.Push(e.Thing);
e.Thing.StartHealth = e.Thing.Health *= 10;
if ( trk ) trk.bBOSS = true;
bosstag = "ROM3R-0.666";
e.Thing.GiveInventory('BossMarker',1);
e.Thing.GiveInventory('EndgameBossMarker',1);
@ -497,9 +477,9 @@ extend Class SWWMHandler
hmax = thealth;
for ( int i=0; i<30; i++ ) oldhealth[i] = thealth;
cummdamage = 0;
if ( !ihealth ) ihealth = DynamicValueInterpolator.Create(thealth,.1,1,1000);
if ( !ihealth ) ihealth = SmoothLinearValueInterpolator.Create(thealth,max(1,hmax/120));
else ihealth.Reset(thealth);
if ( !ihealthr ) ihealthr = DynamicValueInterpolator.Create(thealth,.5,1,1000);
if ( !ihealthr ) ihealthr = SmoothDynamicValueInterpolator.Create(thealth,.5,1,100);
else ihealthr.Reset(thealth);
return;
}
@ -534,7 +514,7 @@ extend Class SWWMHandler
ui void DrawBossBar( SWWMStatusBar bar )
{
if ( !ui_initialized || (bossalpha <= 0.) ) return;
if ( !mSmallFont ) mSmallFont = Font.GetFont('TewiFont');
if ( !mSmallFont ) mSmallFont = Font.GetFont('TewiFontOutline');
if ( !bbar_f ) bbar_f = TexMan.CheckForTexture("graphics/HUD/BossHealthBarBox.png",TexMan.Type_Any);
if ( !bbar_r ) bbar_r = TexMan.CheckForTexture("graphics/HUD/BossHealthBar.png",TexMan.Type_Any);
if ( !bbar_d ) bbar_d = TexMan.CheckForTexture("graphics/HUD/BossHealthBarDecay.png",TexMan.Type_Any);
@ -542,10 +522,10 @@ extend Class SWWMHandler
Screen.DrawTexture(bbar_f,false,vpos.x-2,vpos.y-2,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha);
if ( hmax )
{
int rw = int(clamp((ihealthr.GetValue()*300.)/hmax,0.,300.));
int dw = int(clamp((ihealth.GetValue()*300.)/hmax,0.,300.));
Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,dw);
Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRight,rw);
double rw = clamp((ihealthr.GetValue(bar.FracTic)*300.)/hmax,0.,300.);
double dw = clamp((ihealth.GetValue(bar.FracTic)*300.)/hmax,0.,300.);
Screen.DrawTexture(bbar_d,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRightF,dw);
Screen.DrawTexture(bbar_r,false,vpos.x,vpos.y,DTA_VirtualWidthF,bar.ss.x,DTA_VirtualHeightF,bar.ss.y,DTA_KeepRatio,true,DTA_Alpha,bossalpha,DTA_WindowRightF,rw);
}
if ( (cummdamage > 0) && (gametic < lastcummtic+150) )
{

View file

@ -11,12 +11,12 @@ extend Class SWWMHandler
// for displaying beam-type projectiles
Array<Actor> beams;
// legendary monster markers (for the "has mutated" message)
Array<Inventory> legtrack;
override void WorldThingRevived( WorldEvent e )
{
if ( profiling ) ProfileTick();
// reattach combat tracker
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
SWWMCombatTracker.Spawn(self,e.Thing,true);
// reattach headpats
if ( SWWMUtility.IdentifyingDog(e.Thing) || SWWMUtility.IdentifyingCaco(e.Thing)
|| SWWMUtility.IdentifyingDrug(e.Thing) || SWWMUtility.IdentifyingDoubleBoi(e.Thing) )
@ -275,6 +275,9 @@ extend Class SWWMHandler
p.target = e.Thing;
p.SetStateLabel("Pickup");
}
// for notification
if ( e.Thing.GetClassName() == "LDLegendaryMonsterTransformed" )
legtrack.Push(Inventory(e.Thing));
}
if ( swwm_doomfall && e.Thing.bISMONSTER && !e.Thing.bBOSS )
e.Thing.bFALLDAMAGE = true;
@ -396,9 +399,6 @@ extend Class SWWMHandler
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
}
SWWMCombatTracker trk = null;
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') )
trk = SWWMCombatTracker.Spawn(self,e.Thing);
if ( swwm_shadows && !(e.Thing is 'LampMoth') && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER || (e.Thing is 'Inventory') || (e.Thing is 'CompanionLamp')) && ((e.Thing is 'Demolitionist') || (e.Thing.SpawnState.sprite == e.Thing.GetSpriteIndex('XZW1'))) )
SWWMShadow.Track(e.Thing);
// Ynykron vortex optimization (faster than a thinker iterator)
@ -427,13 +427,7 @@ extend Class SWWMHandler
}
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);
}
VanillaBossSpawn(e);
if ( profiling ) ProfileTock(PT_WORLDTHINGSPAWNED);
}
}

View file

@ -9,7 +9,6 @@ extend Class SWWMHandler
transient String curlang;
transient bool curfuntags;
SWWMSimpleTracker strackers;
int strackers_cnt;
bool mnotify;
bool allkills, allitems, allsecrets;
bool mapclear;
@ -18,15 +17,18 @@ extend Class SWWMHandler
String lastmus;
int lastorder;
bool lastloop;
transient ThinkerIterator cti;
transient ThinkerIterator cti, qti;
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(self);
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;
@ -229,25 +231,23 @@ extend Class SWWMHandler
if ( soprev ) soprev.next = sonext;
else scorenums = sonext;
so.Destroy();
scorenums_cnt--;
}
else soprev = so;
so = sonext;
}
so = damnums;
soprev = null;
while ( so )
SWWMDamNum dn = damnums;
SWWMDamNum dnprev = null, dnnext;
while ( dn )
{
sonext = so.next;
if ( so.Tick() )
dnnext = dn.next;
if ( dn.Tick() )
{
if ( soprev ) soprev.next = sonext;
else damnums = sonext;
so.Destroy();
damnums_cnt--;
if ( dnprev ) dnprev.next = dnnext;
else damnums = dnnext;
dn.Destroy();
}
else soprev = so;
so = sonext;
else dnprev = dn;
dn = dnnext;
}
// interest markers
SWWMInterest ip = intpoints;
@ -260,29 +260,10 @@ extend Class SWWMHandler
if ( ipprev ) ipprev.next = ipnext;
else intpoints = ipnext;
ip.Destroy();
intpoints_cnt--;
}
else ipprev = ip;
ip = ipnext;
}
// combat trackers
SWWMCombatTracker trk = trackers;
SWWMCombatTracker trkprev = null, trknext;
int dbar = swwm_damagetarget;
while ( trk )
{
trknext = trk.next;
trk.dbar = dbar;
if ( trk.Tick() )
{
if ( trkprev ) trkprev.next = trknext;
else trackers = trknext;
trk.Destroy();
trackers_cnt--;
}
else trkprev = trk;
trk = trknext;
}
}
// "simple" tracking (used by the minimap)
@ -296,7 +277,6 @@ extend Class SWWMHandler
strackers.Destroy();
strackers = next;
}
strackers_cnt = 0;
return;
}
// update trackers for anything around the player
@ -432,7 +412,6 @@ extend Class SWWMHandler
if ( !prev ) strackers = trk.next;
else prev.next = trk.next;
trk.Destroy();
strackers_cnt--;
}
else prev = trk;
trk = next;

View file

@ -68,19 +68,15 @@ Enum EMiniHUDFontColor
Class SWWMStatusBar : BaseStatusBar
{
TextureID StatusTex, WeaponTex, ScoreTex, InventoryTex, ChatTex[6],
HealthTex[9], FuelTex[2], DashTex, EnemyBTex, EnemyHTex[6],
HealthTex[9], FuelTex[2], DashTex, EnemyBTex, EnemyHTex[7],
GenericAmmoTex[3], AmmoTex[3], MiniBox, bgtex, FaceTex[19];
Font mSmallFont, mBigFont, mTinyFont, MiniHUDFont, MiniHUDFontOutline;
Font mSmallFont, mSmallFontOutline, mTinyFont, mTinyFontOutline, MiniHUDFont, MiniHUDFontOutline;
int mhudfontcol[NUM_MINIHUD_COLOR];
Array<MsgLine> MainQueue, PickupQueue;
// sorted arrays of various elements
Array<SWWMInterest> intpoints;
Array<SWWMScoreObj> scoreobjs;
Array<SWWMCombatTracker> trackers;
Array<SWWMItemSense> senseitems;
transient ThinkerIterator cti;
// the event handler, holding all sorts of stuff
SWWMHandler hnd;
@ -824,150 +820,6 @@ Class SWWMStatusBar : BaseStatusBar
return true;
}
private bool CmpTarget( SWWMCombatTracker a, SWWMCombatTracker b )
{
if ( !a || !b ) return true;
return (a.myplayer && !b.myplayer);
}
private bool CmpScore( SWWMScoreObj a, SWWMScoreObj b )
{
if ( !a || !b ) return true;
int srt[4] = { Font.CR_GOLD, Font.CR_FIRE, Font.CR_GREEN, Font.CR_RED };
int s1 = 0, s2 = 0;
for ( int i=0; i<3; i++ )
{
if ( a.tcolor == srt[i] ) s1 = i;
if ( b.tcolor == srt[i] ) s2 = i;
}
return s1 < s2;
}
private bool CmpInterest( SWWMInterest a, SWWMInterest b )
{
if ( !a || !b ) return true;
return a.type < b.type;
}
private bool CmpDist( Vector3 a, Vector3 b )
{
double dista = level.Vec3Diff(viewpos,a).length();
double distb = level.Vec3Diff(viewpos,b).length();
return (dista < distb);
}
// quicksort (points of interest)
private int partition_intpoints( Array<SWWMInterest> a, int l, int h )
{
SWWMInterest pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInterest(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMInterest tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMInterest tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_intpoints( Array<SWWMInterest> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_intpoints(a,l,h);
qsort_intpoints(a,l,p-1);
qsort_intpoints(a,p+1,h);
}
// quicksort (combat trackers)
private int partition_trackers( Array<SWWMCombatTracker> a, int l, int h )
{
SWWMCombatTracker pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpTarget(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMCombatTracker tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMCombatTracker tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_trackers( Array<SWWMCombatTracker> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_trackers(a,l,h);
qsort_trackers(a,l,p-1);
qsort_trackers(a,p+1,h);
}
// quicksort (score objects)
private int partition_scoreobjs( Array<SWWMScoreObj> a, int l, int h )
{
SWWMScoreObj pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpScore(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMScoreObj tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMScoreObj tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_scoreobjs( Array<SWWMScoreObj> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_scoreobjs(a,l,h);
qsort_scoreobjs(a,l,p-1);
qsort_scoreobjs(a,p+1,h);
}
// quicksort (item sense)
private int partition_itemsense( Array<SWWMItemSense> a, int l, int h )
{
SWWMItemSense pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMItemSense tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMItemSense tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_itemsense( Array<SWWMItemSense> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_itemsense(a,l,h);
qsort_itemsense(a,l,p-1);
qsort_itemsense(a,p+1,h);
}
// quicksort (player scores)
private int partition_playerscore( Array<PlayerInfo> a, int l, int h )
{
@ -1148,129 +1000,6 @@ Class SWWMStatusBar : BaseStatusBar
minimapzoom = desiredzoom;
}
if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
let cam = players[consoleplayer].camera;
Vector3 viewvec = SWWMUtility.Vec3FromAngles(viewrot.x,viewrot.y);
int sz;
if ( level.allmap && swwm_pois )
{
// update omnisight stuff
if ( intpoints.Size() != hnd.intpoints_cnt )
intpoints.Resize(hnd.intpoints_cnt);
int i = 0;
for ( SWWMInterest poi=hnd.intpoints; poi; poi=poi.next )
{
// ignore points clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,poi.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
intpoints[i++] = poi;
}
// squeeze if some were discarded
if ( i != hnd.intpoints_cnt )
intpoints.Resize(i);
// sort by distance
qsort_intpoints(intpoints,0,intpoints.Size()-1);
}
if ( swwm_targeter )
{
// update target stuff
if ( trackers.Size() != hnd.trackers_cnt )
trackers.Resize(hnd.trackers_cnt);
int i = 0;
for ( SWWMCombatTracker trk=hnd.trackers; trk; trk=trk.next )
{
// ignore dormant/invisible targets
if ( !trk.mytarget || trk.mytarget.bDORMANT || trk.mytarget.bINVISIBLE ) continue;
// ignore local player
if ( trk.mytarget == CPlayer.mo ) continue;
if ( trk.mytarget is 'PlayerGone' ) continue; // ignore "gone" players
int mtime = GameTicRate;
if ( level.allmap && (trk.lasthealth > 0) ) mtime += GameTicRate*3;
if ( level.maptime > trk.updated+mtime ) continue;
// ignore trackers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,trk.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
trackers[i++] = trk;
}
// squeeze if some were discarded
if ( i != hnd.trackers_cnt )
trackers.Resize(i);
// sort by distance (give priority to players)
qsort_trackers(trackers,0,trackers.Size()-1);
}
else trackers.Clear();
// update floating scores, adding the scorenums first, then the damnums
int total_sz = 0;
if ( swwm_scorenums ) total_sz += hnd.scorenums_cnt;
if ( swwm_healthnums ) total_sz += hnd.damnums_cnt;
if ( scoreobjs.Size() != total_sz )
scoreobjs.Resize(total_sz);
int i = 0;
if ( swwm_scorenums )
{
for ( SWWMScoreObj scr=hnd.scorenums; scr && (i<total_sz); scr=scr.next )
{
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
scoreobjs[i++] = scr;
}
}
if ( swwm_healthnums )
{
for ( SWWMScoreObj scr=hnd.damnums; scr && (i<total_sz); scr=scr.next )
{
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
scoreobjs[i++] = scr;
}
}
// squeeze if some were discarded
if ( i != total_sz )
scoreobjs.Resize(i);
// sort by distance
qsort_scoreobjs(scoreobjs,0,scoreobjs.Size()-1);
// check if player has item sense
let demo = Demolitionist(CPlayer.mo);
if ( demo && (demo.itemsense_cnt > 0) )
{
if ( senseitems.Size() != demo.itemsense_cnt )
senseitems.Resize(demo.itemsense_cnt);
i = 0;
for ( SWWMItemSense s=demo.itemsense; s; s=s.next )
{
if ( !level.allmap && (level.maptime > s.updated+GameTicRate) ) continue;
// ignore points clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,s.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
senseitems[i++] = s;
}
// squeeze if some were discarded
if ( i != demo.itemsense_cnt )
senseitems.Resize(i);
// sort by distance
qsort_itemsense(senseitems,0,senseitems.Size()-1);
}
// part of gross hackery to override nametag display
if ( CPlayer.inventorytics > 0 )
{
@ -1383,12 +1112,13 @@ Class SWWMStatusBar : BaseStatusBar
ChatTex[5] = TexMan.CheckForTexture("graphics/HUD/ChatBoxBottom_Smol.png",TexMan.Type_Any);
InventoryTex = TexMan.CheckForTexture("graphics/HUD/InventoryBox.png",TexMan.Type_Any);
EnemyBTex = TexMan.CheckForTexture("graphics/HUD/EnemyBox.png",TexMan.Type_Any);
EnemyHTex[0] = TexMan.CheckForTexture("graphics/HUD/EnemyBar0.png",TexMan.Type_Any);
EnemyHTex[1] = TexMan.CheckForTexture("graphics/HUD/EnemyBar1.png",TexMan.Type_Any);
EnemyHTex[2] = TexMan.CheckForTexture("graphics/HUD/EnemyBar2.png",TexMan.Type_Any);
EnemyHTex[3] = TexMan.CheckForTexture("graphics/HUD/EnemyBar3.png",TexMan.Type_Any);
EnemyHTex[4] = TexMan.CheckForTexture("graphics/HUD/EnemyBarS.png",TexMan.Type_Any);
EnemyHTex[5] = TexMan.CheckForTexture("graphics/HUD/EnemyBarD.png",TexMan.Type_Any);
EnemyHTex[0] = TexMan.CheckForTexture("graphics/HUD/EnemyBar.png",TexMan.Type_Any);
EnemyHTex[1] = TexMan.CheckForTexture("graphics/HUD/EnemyBarS.png",TexMan.Type_Any);
EnemyHTex[2] = TexMan.CheckForTexture("graphics/HUD/EnemyBarL.png",TexMan.Type_Any);
EnemyHTex[3] = TexMan.CheckForTexture("graphics/HUD/EnemyBar1.png",TexMan.Type_Any);
EnemyHTex[4] = TexMan.CheckForTexture("graphics/HUD/EnemyBar2.png",TexMan.Type_Any);
EnemyHTex[5] = TexMan.CheckForTexture("graphics/HUD/EnemyBar3.png",TexMan.Type_Any);
EnemyHTex[6] = TexMan.CheckForTexture("graphics/HUD/EnemyBarD.png",TexMan.Type_Any);
GenericAmmoTex[0] = TexMan.CheckForTexture("graphics/HUD/GenericAmmoBoxL.png",TexMan.Type_Any);
GenericAmmoTex[1] = TexMan.CheckForTexture("graphics/HUD/GenericAmmoBoxM.png",TexMan.Type_Any);
GenericAmmoTex[2] = TexMan.CheckForTexture("graphics/HUD/GenericAmmoBoxR.png",TexMan.Type_Any);
@ -1418,8 +1148,9 @@ Class SWWMStatusBar : BaseStatusBar
FaceTex[18] = TexMan.CheckForTexture("graphics/HUD/DemoFace_Wink.png",TexMan.Type_Any);
// other expressions will be added when needed
mSmallFont = Font.GetFont('TewiFont');
mBigFont = Font.GetFont('TewiFontOutline');
mSmallFontOutline = Font.GetFont('TewiFontOutline');
mTinyFont = Font.GetFont('MiniwiFont');
mTinyFontOutline = Font.GetFont('MiniwiFontOutline');
MiniHudFont = Font.GetFont("MiniHUDShadow");
MiniHudFontOutline = Font.GetFont("MiniHUDOutline");
mhudfontcol[MCR_DEMOHUD] = Font.FindFontColor("MiniDemoHUD");
@ -1534,218 +1265,345 @@ Class SWWMStatusBar : BaseStatusBar
return String.Format("\cj%d\cc%s",int(meters),StringTable.Localize("$SWWM_UNIT_METER"));
}
private void DrawTarget()
private void DrawInterest( Vector3 viewvec, out bool projinit )
{
// don't draw when dead or with automap open
if ( (CPlayer.health <= 0) || automapactive ) return;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
// points of interest
String tag;
if ( level.allmap )
SWWMInterest poi = hnd.intpoints;
if ( !poi ) return;
do
{
for ( int i=0; i<intpoints.Size(); i++ )
// this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing
if ( !projinit )
{
let poi = intpoints[i];
if ( !poi ) continue;
Vector3 tdir = level.Vec3Diff(ViewPos,poi.pos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag);
else if ( poi.type == INT_Exit )
{
if ( (poi.trackedline.special == Teleport_EndGame)
|| ((poi.trackedline.special == Exit_Secret) && (level.nextsecretmap.Left(6) == "enDSeQ"))
|| ((poi.trackedline.special == Exit_Normal) && (level.nextmap.Left(6) == "enDSeQ")) )
tag = String.Format("\cg%s\c-",StringTable.Localize("$SWWM_EEXIT"));
else if ( poi.trackedline.special == Exit_Secret )
{
LevelInfo l = LevelInfo.FindLevelInfo(level.nextsecretmap);
if ( l && l.isValid() ) tag = String.Format("\cx%s:\c- %s\c-",StringTable.Localize("$SWWM_SEXIT"),l.LookupLevelName());
else tag = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_SEXIT"));
}
else if ( (poi.trackedline.special == Exit_Normal) || ((poi.trackedline.special == ACS_Execute) && (poi.trackedline.Args[0] == -Int('E1M8_KNOCKOUT'))) )
{
LevelInfo l = LevelInfo.FindLevelInfo(level.nextmap);
if ( l && l.isValid() ) tag = String.Format("\cy%s:\c- %s\c-",StringTable.Localize("$SWWM_NEXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
else if ( poi.trackedline.special == Teleport_NewMap )
{
LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[0]);
if ( l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
else if ( ((poi.trackedline.special == ACS_Execute) || (poi.trackedline.special == ACS_ExecuteAlways)) && (poi.trackedline.Args[0] == -Int('MapFadeOut')) )
{
LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[2]);
if ( (level.levelnum != 1) && l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
}
Screen.DrawText(mTinyFont,Font.CR_WHITE,(vpos.x-hs2*mTinyFont.StringWidth(tag)/2.)/hs2,(vpos.y-hs2*mTinyFont.GetHeight()/2.)/hs2,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(mTinyFont,Font.CR_WHITE,(vpos.x-hs2*mTinyFont.StringWidth(tag)/2.)/hs2,(vpos.y+hs2*mTinyFont.GetHeight()/2.)/hs2,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true);
projinit = true;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
}
}
// sensed items
let demo = Demolitionist(CPlayer.mo);
if ( demo && (demo.itemsense_cnt > 0) )
{
for ( int i=0; i<senseitems.Size(); i++ )
{
let s = senseitems[i];
if ( !s || !s.item ) continue;
Vector3 tdir = level.Vec3Diff(ViewPos,s.pos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
int mtime = level.allmap?(GameTicRate*2):GameTicRate;
double alph = clamp(((s.updated+mtime)-(level.maptime+fractic))/double(GameTicRate),0.,1.);
alph *= clamp(1.5-1.5*(tdir.length()/(level.allmap?1200.:800.)),0.,1.);
tag = s.tag;
Screen.DrawText(mTinyFont,s.vipitem?Font.CR_PURPLE:s.scoreitem?Font.CR_GOLD:Font.CR_GREEN,(vpos.x-hs1*mTinyFont.StringWidth(tag)/2.)/hs1,(vpos.y-hs1*mTinyFont.GetHeight()/2.)/hs1,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(mTinyFont,Font.CR_WHITE,(vpos.x-hs1*mTinyFont.StringWidth(tag)/2.)/hs1,(vpos.y+hs1*mTinyFont.GetHeight()/2.)/hs1,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
if ( s.item is 'SWWMRespawnTimer' )
{
tag = String.Format(StringTable.Localize("$SWWM_TRESPAWN"),s.item.special2/GameTicRate);
Screen.DrawText(mTinyFont,Font.CR_WHITE,(vpos.x-hs1*mTinyFont.StringWidth(tag)/2.)/hs1,(vpos.y+hs1*mTinyFont.GetHeight()*2)/hs1,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
}
}
// targetting array
for ( int i=0; i<trackers.Size(); i++ )
{
let targ = trackers[i];
if ( !targ ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+level.Vec3Diff(ViewPos,SWWMUtility.LerpVector3(targ.prevpos,targ.pos,fractic)));
Vector3 tdir = level.Vec3Diff(ViewPos,poi.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
tag = targ.mytag;
if ( (tag != "") && !targ.myplayer )
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs2;
if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag);
else if ( poi.type == INT_Exit )
{
if ( targ.legged && (targ.mutated || swwm_ldspoil) )
if ( (poi.trackedline.special == Teleport_EndGame)
|| ((poi.trackedline.special == Exit_Secret) && (level.nextsecretmap.Left(6) == "enDSeQ"))
|| ((poi.trackedline.special == Exit_Normal) && (level.nextmap.Left(6) == "enDSeQ")) )
tag = String.Format("\cg%s\c-",StringTable.Localize("$SWWM_EEXIT"));
else if ( poi.trackedline.special == Exit_Secret )
{
LevelInfo l = LevelInfo.FindLevelInfo(level.nextsecretmap);
if ( l && l.isValid() ) tag = String.Format("\cx%s:\c- %s\c-",StringTable.Localize("$SWWM_SEXIT"),l.LookupLevelName());
else tag = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_SEXIT"));
}
else if ( (poi.trackedline.special == Exit_Normal) || ((poi.trackedline.special == ACS_Execute) && (poi.trackedline.Args[0] == -Int('E1M8_KNOCKOUT'))) )
{
LevelInfo l = LevelInfo.FindLevelInfo(level.nextmap);
if ( l && l.isValid() ) tag = String.Format("\cy%s:\c- %s\c-",StringTable.Localize("$SWWM_NEXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
else if ( poi.trackedline.special == Teleport_NewMap )
{
LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[0]);
if ( l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
else if ( ((poi.trackedline.special == ACS_Execute) || (poi.trackedline.special == ACS_ExecuteAlways)) && (poi.trackedline.Args[0] == -Int('MapFadeOut')) )
{
LevelInfo l = LevelInfo.FindLevelByNum(poi.trackedline.Args[2]);
if ( (level.levelnum != 1) && l && l.isValid() ) tag = String.Format("\cy%s\c-%s\c-",StringTable.Localize("$SWWM_EXIT"),l.LookupLevelName());
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
}
Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss2.x,DTA_VirtualHeightF,ss2.y,DTA_KeepRatio,true);
}
while ( poi = poi.next );
}
private void DrawItemSense( Vector3 viewvec, out bool projinit )
{
let demo = Demolitionist(CPlayer.mo);
if ( !demo ) return;
SWWMItemSense s = demo.itemsense;
if ( !s ) return;
do
{
if ( !s.item ) continue;
Vector3 tdir = level.Vec3Diff(ViewPos,s.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1;
int mtime = level.allmap?(GameTicRate*2):GameTicRate;
double alph = clamp(((s.updated+mtime)-(level.maptime+fractic))/double(GameTicRate),0.,1.);
alph *= clamp(1.5-1.5*(tdir.length()/(level.allmap?1200.:800.)),0.,1.);
String tag = s.tag;
Screen.DrawText(mTinyFontOutline,s.vipitem?Font.CR_PURPLE:s.scoreitem?Font.CR_GOLD:Font.CR_GREEN,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y-mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()/2.,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
if ( s.item is 'SWWMRespawnTimer' )
{
tag = String.Format(StringTable.Localize("$SWWM_TRESPAWN"),s.item.special2/GameTicRate);
Screen.DrawText(mTinyFontOutline,Font.CR_WHITE,vpos.x-mTinyFontOutline.StringWidth(tag)/2.,vpos.y+mTinyFontOutline.GetHeight()*2,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
}
while ( s = s.next );
}
private bool IsLegendary( Actor a )
{
for ( Inventory i=a.inv; i; i=i.inv )
{
if ( (i.GetClassName() == "LDLegendaryMonsterToken") && swwm_ldspoil ) return true;
else if ( i.GetClassName() == "LDLegendaryMonsterTransformed" ) return true;
}
return false;
}
private void DrawTrackers( Vector3 viewvec, out bool projinit )
{
let cam = players[consoleplayer].camera;
if ( !cti ) cti = ThinkerIterator.Create("SWWMQuickCombatTracker",Thinker.STAT_INVENTORY);
else cti.Reinit();
SWWMQuickCombatTracker ct;
while ( ct = SWWMQuickCombatTracker(cti.Next()) )
{
// this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing
if ( !projinit )
{
projinit = true;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
}
// ignore unowned (can happen?)
if ( !ct.Owner ) continue;
// ignore player trackers
if ( ct.Owner.player ) continue;
// ignore local player or camera
if ( (ct.Owner.player == CPlayer) || (ct.Owner == cam) ) continue;
// ignore trackers not of this player
if ( ct.myplayer != CPlayer ) continue;
// ignore trackers clearly outside of player view
Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight));
Vector3 tdir = level.Vec3Diff(viewpos,smpos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1;
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
double fin = clamp(ct.fadein+fractic,0.,5.)/5.;
double fout = clamp(ct.lifespan-fractic,0.,25.)/25.;
double alph = fin*fout;
Vector2 barpos = vpos-(27,7);
if ( swwm_targettags && (ct.mytag != "") )
{
Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline;
String tag = ct.mytag;
if ( IsLegendary(ct.Owner) )
{
if ( StringTable.Localize("$SWWM_LEGPREFIX") == "R" ) tag = tag..StringTable.Localize("$SWWM_LEG");
else tag = StringTable.Localize("$SWWM_LEG")..tag;
}
if ( targ.bBOSS )
if ( ct.Owner.bBOSS || ct.Owner.FindInventory("BossMarker") )
{
if ( swwm_bigtags ) tag = "\cx★\c- "..tag.." \cx★\c-";
else tag = "\cx*\c- "..tag.." \cx*\c-"; // miniwi has no stars
}
if ( targ.bFRIENDLY ) tag = "\cg♥\c- "..tag.." \cg♥\c-";
if ( ct.Owner.IsFriend(CPlayer.mo) ) tag = "\cg♥\c- "..tag.." \cg♥\c-";
Screen.DrawText(fnt,Font.CR_WHITE,vpos.x-fnt.StringWidth(tag)/2,barpos.y-(fnt.GetHeight()+2),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
int mtime = GameTicRate;
if ( level.allmap && (targ.lasthealth > 0) ) mtime += GameTicRate*3;
double alph = clamp(((targ.updated+mtime)-(level.maptime+fractic))/double(GameTicRate),0.,1.);
Vector2 barsiz = TexMan.GetScaledSize(EnemyBTex);
barsiz.x *= hs1;
barsiz.y *= hs1;
Vector2 barpos = vpos-(barsiz/2.);
barpos.y -= 16.;
Font fnt = swwm_bigtags?mSmallFont:mTinyFont;
Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth);
double hw = (ht*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[ct.Owner.bINVULNERABLE?1:0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
double ohw = hw;
ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth);
hw = (ht*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw);
if ( ct.cummdamage <= 0 ) continue;
double calph = clamp(ct.cummspan-fractic,0.,20.)/20.;
Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph);
if ( ct.cummflash <= 0 ) continue;
double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5;
Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add);
}
// player-owned trackers are drawn last
cti.Reinit();
while ( ct = SWWMQuickCombatTracker(cti.Next()) )
{
// this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing
if ( !projinit )
{
projinit = true;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
}
// ignore unowned (can happen?)
if ( !ct.Owner ) continue;
// ignore non-player trackers
if ( !ct.Owner.player ) continue;
// ignore local player or camera
if ( (ct.Owner.player == CPlayer) || (ct.Owner == cam) ) continue;
// ignore trackers not of this player
if ( ct.myplayer != CPlayer ) continue;
// ignore trackers clearly outside of player view
Vector3 smpos = level.Vec3Offset(SWWMUtility.LerpVector3(ct.Owner.prev,ct.Owner.pos,fractic),(0,0,ct.lvheight));
Vector3 tdir = level.Vec3Diff(viewpos,smpos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1;
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
double fin = clamp(ct.fadein+fractic,0.,5.)/5.;
double fout = clamp(ct.lifespan-fractic,0.,25.)/25.;
double alph = fin*fout;
Vector2 barpos = vpos-(27,7);
Font fnt = swwm_bigtags?mSmallFontOutline:mTinyFontOutline;
int col = Font.CR_WHITE;
if ( teamplay )
{
int team = targ.myplayer.GetTeam();
int team = ct.Owner.player.GetTeam();
if ( team != -1 ) col = Font.FindFontColor(Teams[team].mName); // this works in practice because team names are the same as their text colors
if ( col == -1 ) col = Font.CR_WHITE;
}
if ( swwm_targettags || targ.myplayer && (tag != "") )
Screen.DrawText(fnt,col,(barpos.x+barsiz.x/2.-(fnt.StringWidth(tag)*hs1)/2.)/hs1,(barpos.y-fnt.GetHeight()*hs1)/hs1,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawTexture(EnemyBTex,false,barpos.x/hs1,barpos.y/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
double ht = clamp(targ.intp.GetValue(),0,targ.maxhealth*100);
double hw = (min(ht,targ.maxhealth)*50.)/targ.maxhealth;
if ( targ.mytarget && (targ.mytarget.bInvulnerable || (targ.myplayer && (targ.myplayer.cheats&(CF_GODMODE|CF_GODMODE2))) || targ.mytarget.FindInventory("InvinciballPower")) )
Screen.DrawText(fnt,col,vpos.x-fnt.StringWidth(ct.mytag)/2,barpos.y-(fnt.GetHeight()+2),ct.mytag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawTexture(EnemyBTex,false,barpos.x,barpos.y,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
double ht = clamp(ct.intp.GetValue(fractic),0,ct.maxhealth*100);
double hw = (ht*50.)/ct.maxhealth;
double ohw = hw;
if ( ct.Owner.bINVULNERABLE || (ct.Owner.player.cheats&(CF_GODMODE|CF_GODMODE2)) || ct.Owner.FindInventory("InvinciballPower") )
Screen.DrawTexture(EnemyHTex[1],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
else
{
Screen.DrawTexture(EnemyHTex[4],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[0],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
if ( ht > ct.maxhealth )
{
hw = (min(ht-ct.maxhealth,ct.maxhealth)*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[3],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
if ( ht > ct.maxhealth*2 )
{
hw = (min(ht-ct.maxhealth*2,ct.maxhealth*3)*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[4],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
if ( ht > ct.maxhealth*5 )
{
hw = (min(ht-ct.maxhealth*5,ct.maxhealth*5)*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[5],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
}
if ( ct.Owner.FindInventory("DivineSpriteEffect") )
{
double falph = clamp((ht-ct.maxhealth*10)/(ct.maxhealth*60.),0.,1.);
Screen.DrawTexture(EnemyHTex[6],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph*falph,DTA_LegacyRenderStyle,STYLE_Add);
}
else
{
Screen.DrawTexture(EnemyHTex[0],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
if ( ht > targ.maxhealth )
{
hw = (min(ht-targ.maxhealth,targ.maxhealth)*50.)/targ.maxhealth;
Screen.DrawTexture(EnemyHTex[1],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
if ( ht > targ.maxhealth*2 )
{
hw = (min(ht-targ.maxhealth*2,targ.maxhealth*3)*50.)/(targ.maxhealth*3);
Screen.DrawTexture(EnemyHTex[2],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
if ( ht > targ.maxhealth*5 )
{
hw = (min(ht-targ.maxhealth*5,targ.maxhealth*5)*50.)/(targ.maxhealth*5);
Screen.DrawTexture(EnemyHTex[3],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
}
}
if ( targ.mytarget && targ.mytarget.FindInventory("DivineSpriteEffect") )
{
double falph = clamp((ht-targ.maxhealth*10)/(targ.maxhealth*60.),0.,1.);
Screen.DrawTexture(EnemyHTex[5],false,(barpos.x+2*hs1)/hs1,(barpos.y+2*hs1)/hs1,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph*falph,DTA_LegacyRenderStyle,STYLE_Add);
ht = clamp(ct.intpl.GetValue(fractic),0,ct.maxhealth);
hw = (ht*50.)/ct.maxhealth;
Screen.DrawTexture(EnemyHTex[2],false,barpos.x+2,barpos.y+2,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowLeftF,ohw,DTA_WindowRightF,hw);
}
if ( ct.cummdamage <= 0 ) continue;
double calph = clamp(ct.cummspan-fractic,0.,20.)/20.;
Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_RED],barpos.x+3,barpos.y+10,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,calph*alph);
if ( ct.cummflash <= 0 ) continue;
double falph = max((ct.cummflash-FracTic)/15.,0.)**1.5;
Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_REDFLASH],barpos.x+3,barpos.y+10,String.Format("%d",ct.cummdamage),DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,falph*calph*alph,DTA_LegacyRenderStyle,STYLE_Add);
}
// floating kill scores and others
for ( int i=0; i<scoreobjs.Size(); i++ )
}
private void DrawNumbers( Vector3 viewvec, out bool projinit )
{
SWWMDamNum snum = hnd.damnums;
if ( !snum ) return;
do
{
let snum = scoreobjs[i];
if ( !snum ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+level.Vec3Diff(ViewPos,snum.pos));
// this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing
if ( !projinit )
{
projinit = true;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
}
Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
tag = String.Format("%+d",snum.score);
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs;
String tag = abs(snum.damage>=Actor.TELEFRAG_DAMAGE)?(snum.damage>0)?"-∞":"+∞":String.Format("%+d",-snum.damage);
double alph = clamp((snum.lifespan+fractic)/35.,0.,1.);
Vector2 fo;
int initspd = (128-snum.seed);
int boostup = 64+snum.seed2;
fo.x = (.05*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.8);
fo.y = -((snum.initialspan-(snum.lifespan-fractic))**1.5)+boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic));
Screen.DrawText(MiniHUDFontOutline,snum.tcolor,(vpos.x-fo.x)-(MiniHUDFontOutline.StringWidth(tag))/2,(vpos.y-fo.y)-(MiniHUDFontOutline.GetHeight())/2,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
while ( snum = snum.next );
}
private void DrawScores( Vector3 viewvec, out bool projinit )
{
SWWMScoreObj snum = hnd.scorenums;
if ( !snum ) return;
do
{
// this ensures that projection data isn't cached if there are no target array elements, to avoid needless GC thrashing
if ( !projinit )
{
projinit = true;
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
Screen.SetClipRect(projdata.viewx,projdata.viewy,projdata.vieww,projdata.viewh);
}
Vector3 tdir = level.Vec3Diff(ViewPos,snum.pos);
if ( viewvec dot tdir < 0 ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc)/hs1;
String tag = String.Format("%+d",snum.score);
double alph = clamp((snum.lifespan+fractic)/double(GameTicRate),0.,1.);
Vector2 fo = (0,0);
bool isscore = false;
if ( snum.damnum )
{
if ( snum.score < 0 )
{
// damage falls down
int initspd = (128-snum.seed);
int boostup = 64+snum.seed2;
fo.x = (.05*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.8);
fo.y = -((snum.initialspan-(snum.lifespan-fractic))**1.5)+boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic));
}
else
{
// health falls up (?)
int initspd = (128-snum.seed);
int boostup = 16+snum.seed2/4;
fo.x = (.15*initspd)*((snum.initialspan-(snum.lifespan-fractic))**.6);
fo.y = ((snum.initialspan-(snum.lifespan-fractic))**1.2)-boostup*sin((90./snum.initialspan)*(level.maptime+fractic-snum.starttic));
}
}
else
{
// score rises linearly
fo.y = snum.initialspan-(snum.lifespan-fractic);
isscore = true;
}
double hs0 = isscore?hs1:hs;
Vector2 ss0 = isscore?ss1:ss;
Screen.DrawText(mTinyFont,snum.tcolor,(vpos.x-hs0*(fo.x+mTinyFont.StringWidth(tag)/2.))/hs0,(vpos.y-hs0*(fo.y+(mTinyFont.GetHeight()/2.)))/hs0,tag,DTA_VirtualWidthF,ss0.x,DTA_VirtualHeightF,ss0.y,DTA_KeepRatio,true,DTA_Alpha,alph);
// score rises linearly
Vector2 fo = (0,snum.initialspan-(snum.lifespan-fractic));
Screen.DrawText(mTinyFontOutline,snum.tcolor,vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
// extra strings (if available)
fo.y += mTinyFont.GetHeight();
fo.y += mTinyFontOutline.GetHeight();
for ( int i=0; i<snum.xstr.Size(); i++ )
{
tag = snum.xstr[i];
if ( snum.xscore[i] == int.max ) tag.AppendFormat(" MAX");
else if ( snum.xscore[i] > 0 ) tag.AppendFormat(" x%d",snum.xscore[i]);
Screen.DrawText(mTinyFont,snum.xtcolor[i],(vpos.x-hs1*(fo.x+mTinyFont.StringWidth(tag)/2.))/hs1,(vpos.y-hs1*(fo.y+(mTinyFont.GetHeight()/2.)))/hs1,tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
fo.y += mTinyFont.GetHeight();
Screen.DrawText(mTinyFontOutline,snum.xtcolor[i],vpos.x-(fo.x+mTinyFontOutline.StringWidth(tag)/2.),vpos.y-(fo.y+(mTinyFontOutline.GetHeight()/2.)),tag,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
fo.y += mTinyFontOutline.GetHeight();
}
}
while ( snum = snum.next );
}
private void DrawTarget()
{
// don't draw when dead or with automap open
if ( (CPlayer.health <= 0) || automapactive ) return;
bool projinit = false;
Vector3 viewvec = SWWMUtility.Vec3FromAngles(viewrot.x,viewrot.y);
// points of interest
if ( level.allmap && swwm_pois ) DrawInterest(viewvec,projinit);
// sensed items
DrawItemSense(viewvec,projinit);
// targetting array
if ( swwm_targeter ) DrawTrackers(viewvec,projinit);
// floating kill scores and others
if ( swwm_damnums ) DrawNumbers(viewvec,projinit);
if ( swwm_scorenums ) DrawScores(viewvec,projinit);
Screen.ClearClipRect();
}
override void DrawMyPos()
{
String str = String.Format("(%d,%d,%d)",CPlayer.mo.pos.X,CPlayer.mo.pos.Y,CPlayer.mo.pos.Z);
Screen.DrawText(mTinyFont,Font.CR_GREEN,(ss.x-mTinyFont.StringWidth(str))/2,4,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTinyFontOutline,Font.CR_GREEN,(ss.x-mTinyFontOutline.StringWidth(str))/2,4,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
override void DrawAutomapHUD( double ticFrac )
@ -2377,8 +2235,8 @@ Class SWWMStatusBar : BaseStatusBar
if ( iof != -1 ) ln.Truncate(iof);
if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) ) str = ln;
else str = String.Format("%s - %s",level.mapname.MakeUpper(),ln);
Screen.DrawText(mBigFont,tclabel,xx-mBigFont.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += mBigFont.GetHeight()+4;
Screen.DrawText(mSmallFontOutline,tclabel,xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += mSmallFontOutline.GetHeight()+4;
}
if ( (level.total_monsters > 0) && am_showmonsters && !deathmatch )
{
@ -3171,7 +3029,7 @@ Class SWWMStatusBar : BaseStatusBar
override bool DrawPaused( int player )
{
let fnt = mBigFont?mBigFont:NewSmallFont;
let fnt = mSmallFontOutline?mSmallFontOutline:NewSmallFont;
let fnt2 = mSmallFont?mSmallFont:NewConsoleFont;
if ( swwm_fuzz )
{

View file

@ -1,14 +1,6 @@
// hud-related storage objects
// (used to be thinkers, but this might be lighter on performance)
Enum EScoreObjType
{
ST_Score,
ST_Damage,
ST_Health,
ST_Armor
};
// floating scores
Class SWWMScoreObj play
{
@ -21,65 +13,27 @@ Class SWWMScoreObj play
int lifespan, initialspan;
int starttic, seed, seed2;
SWWMScoreObj next;
bool damnum;
Actor acc;
static SWWMScoreObj Spawn( int score, Vector3 pos, int type = ST_Score, int tcolor = -1 )
static SWWMScoreObj Spawn( int score, Vector3 pos, int tcolor = -1 )
{
// early checks
if ( (type == ST_SCORE) && !swwm_scorenums ) return null;
else if ( (type > ST_SCORE) && !swwm_healthnums ) return null;
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return null;
return SpawnInternal(hnd,score,pos,type,tcolor);
return SpawnFromHandler(hnd,score,pos,tcolor);
}
static SWWMScoreObj SpawnFromHandler( SWWMHandler hnd, int score, Vector3 pos, int type = ST_Score, int tcolor = -1 )
{
// early checks
if ( (type == ST_SCORE) && !swwm_scorenums ) return null;
else if ( (type > ST_SCORE) && !swwm_healthnums ) return null;
return SpawnInternal(hnd,score,pos,type,tcolor);
}
private static SWWMScoreObj SpawnInternal( SWWMHandler hnd, int score, Vector3 pos, int type = ST_Score, int tcolor = -1 )
static SWWMScoreObj SpawnFromHandler( SWWMHandler hnd, int score, Vector3 pos, int tcolor = -1 )
{
let o = new("SWWMScoreObj");
o.score = score;
o.pos = pos;
o.lifespan = o.initialspan = 60;
if ( tcolor != -1 ) o.tcolor = tcolor;
else switch ( type )
{
case ST_Score:
o.tcolor = Font.CR_GOLD;
break;
case ST_Damage:
o.tcolor = Font.CR_RED;
break;
case ST_Health:
o.tcolor = Font.CR_GREEN;
break;
case ST_Armor:
o.tcolor = Font.CR_BLUE;
break;
}
else o.tcolor = Font.CR_GOLD;
o.starttic = level.maptime;
o.seed = Random[ScoreBits]();
o.seed2 = Random[ScoreBits]();
o.damnum = (type > ST_Score);
if ( o.damnum )
{
o.next = hnd.damnums;
hnd.damnums = o;
hnd.damnums_cnt++;
}
else
{
o.next = hnd.scorenums;
hnd.scorenums = o;
hnd.scorenums_cnt++;
}
o.next = hnd.scorenums;
hnd.scorenums = o;
return o;
}
@ -97,6 +51,51 @@ Class SWWMScoreObj play
}
}
// damage/health/armor numbers
Class SWWMDamNum play
{
int tcolor;
int damage;
Vector3 pos;
int lifespan, initialspan;
int starttic, seed, seed2;
SWWMDamNum next;
static SWWMDamNum Spawn( int damage, Vector3 pos, Name type = '' )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return null;
return SpawnFromHandler(hnd,damage,pos,type);
}
static SWWMDamNum SpawnFromHandler( SWWMHandler hnd, int damage, Vector3 pos, Name type = '' )
{
let o = new("SWWMDamNum");
o.damage = damage;
o.pos = pos;
o.tcolor = Font.FindFontColor("MiniRed");
if ( !hnd.usedamcolors ) hnd.usedamcolors = CVar.FindCVar('swwm_damnums_color');
if ( hnd.usedamcolors.GetBool() ) for ( int i=0; i<hnd.damtypes.Size(); i++ )
{
if ( hnd.damtypes[i] != type ) continue;
o.tcolor = Font.FindFontColor(hnd.damcolors[i]);
break;
}
o.lifespan = o.initialspan = 60;
o.starttic = level.maptime;
o.seed = Random[ScoreBits]();
o.seed2 = Random[ScoreBits]();
o.next = hnd.damnums;
hnd.damnums = o;
return o;
}
bool Tick()
{
lifespan--;
return (lifespan <= 0);
}
}
enum EInterestType
{
INT_Key,
@ -167,7 +166,6 @@ Class SWWMInterest play
i.pos = thekey?thekey.Vec3Offset(0,0,thekey.height/2):pos;
i.next = hnd.intpoints;
hnd.intpoints = i;
hnd.intpoints_cnt++;
return i;
}
@ -227,7 +225,6 @@ Class SWWMItemSense play
i.pos = item.Vec3Offset(0,0,item.height);
i.next = parent.itemsense;
parent.itemsense = i;
parent.itemsense_cnt++;
return i;
}
@ -250,196 +247,6 @@ Class SWWMItemSense play
}
}
// enemy combat tracker
Class SWWMCombatTracker play
{
Actor mytarget;
String mytag;
int updated, lasthealth, maxhealth;
DynamicValueInterpolator intp;
Vector3 pos, prevpos, oldpos, oldprev;
PlayerInfo myplayer;
SWWMCombatTracker next;
bool legged, mutated;
int tcnt;
double height;
int dbar;
bool bBOSS, bFRIENDLY;
bool firsthit;
bool bUpdateMorph;
String unmorphedtag;
void UpdateTag( SWWMHandler hnd )
{
if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') || (mytarget is 'Demolitionist')) )
{
String realtag = swwm_funtags?SWWMUtility.GetFunTag(hnd,mytarget,FallbackTag):mytarget.GetTag(FallbackTag);
if ( realtag == FallbackTag )
{
realtag = mytarget.GetClassName();
SWWMUtility.BeautifyClassName(realtag);
}
mytag = mytarget.player?(mytarget.player.mo!=mytarget)?String.Format(StringTable.Localize("$FN_VOODOO"),mytarget.player.GetUserName()):mytarget.player.GetUserName():((mytarget is 'PlayerPawn')&&(!mytarget.player))?StringTable.Localize("$FN_VOODOO_NP"):realtag;
}
else mytag = "";
}
static SWWMCombatTracker Spawn( SWWMHandler hnd, Actor target, bool update = false )
{
// NOTE: These are only ever called once a thing spawns, so we don't need to "check" if entries already exist
// this check will only be performed in "update mode", i.e. when called from the swwmupdatetrackers netevent,
// or when a monster is revived
SWWMCombatTracker t;
if ( update ) for ( t=hnd.trackers; t; t=t.next )
{
if ( t.mytarget != target ) continue;
return t;
}
t = new("SWWMCombatTracker");
t.mytarget = target;
t.UpdateTag(hnd);
if ( target.player )
{
t.lasthealth = target.health;
t.maxhealth = target.default.health;
}
else t.lasthealth = t.maxhealth = target.health;
t.updated = int.min;
t.height = target.height;
t.pos = level.Vec3Offset(target.pos,(0,0,t.height));
t.prevpos = level.Vec3Offset(target.prev,(0,0,t.height));
t.oldpos = target.pos;
t.oldprev = target.prev;
t.intp = DynamicValueInterpolator.Create(t.lasthealth,.5,1,100);
t.myplayer = target.player;
t.next = hnd.trackers;
t.bBOSS = target.bBOSS;
t.bFRIENDLY = target.IsFriend(players[consoleplayer].mo);
hnd.trackers = t;
hnd.trackers_cnt++;
return t;
}
bool Tick()
{
// is target gone or dead?
if ( !mytarget || (mytarget.Health <= 0) )
{
// we're done
if ( updated > level.maptime ) updated = level.maptime;
lasthealth = 0;
prevpos = pos; // prevent stuttering
intp.Update(lasthealth);
if ( level.maptime > updated+35 ) return true;
return false;
}
// don't update dormant targets
if ( mytarget.bDORMANT )
return false;
// only update height/position while alive
bool heightchanged = false;
if ( height != mytarget.height ) heightchanged = true;
height = mytarget.height;
if ( heightchanged || (mytarget.pos != oldpos) || (mytarget.prev != oldprev) )
{
oldpos = mytarget.pos;
oldprev = mytarget.prev;
pos = level.Vec3Offset(mytarget.pos,(0,0,height));
prevpos = level.Vec3Offset(mytarget.prev,(0,0,height));
}
if ( bUpdateMorph && !(mytarget is 'MorphedMonster') )
{
// reset our tag
mytag = unmorphedtag;
bUpdateMorph = false;
}
tcnt++;
if ( (tcnt == 1) && !mytarget.player )
{
// post-spawn health inflation check
if ( lasthealth > maxhealth )
{
maxhealth = lasthealth;
intp.Reset(lasthealth);
}
// post-spawn morph check
if ( (mytarget is 'MorphedMonster') && MorphedMonster(mytarget).UnmorphedMe )
{
// look for a previous tracker that has the same target as us
for ( SWWMCombatTracker t=next; t; t=t.next )
{
if ( t.mytarget != mytarget ) continue;
// change its tag and destroy ourselves (such is life)
t.bUpdateMorph = true;
t.unmorphedtag = t.mytag;
t.mytag = String.Format("%s (%s)",mytag,t.unmorphedtag);
return true;
}
}
}
if ( (tcnt == 6) && !mytarget.player )
{
// legendoom check
for ( Inventory i=mytarget.inv; i; i=i.inv )
{
if ( i.GetClassName() != "LDLegendaryMonsterToken" ) continue;
legged = true;
// adjust for health inflation
if ( lasthealth > maxhealth )
{
maxhealth = lasthealth;
intp.Reset(lasthealth);
}
}
}
if ( legged && !mutated )
{
// check inventory regularly to mark as mutated
for ( Inventory i=mytarget.inv; i; i=i.inv )
{
if ( i.GetClassName() != "LDLegendaryMonsterTransformed" ) continue;
mutated = true;
Console.Printf(StringTable.Localize("$SWWM_LTFORM"),mytag);
}
}
// voodoo dolls don't show as friendly
bFRIENDLY = mytarget.IsFriend(players[consoleplayer].mo);
if ( mytarget.Health < lasthealth ) firsthit = true;
lasthealth = mytarget.Health;
intp.Update(lasthealth);
// special update conditions
if ( dbar && !mytarget.player )
{
if ( (dbar == 2) && (lasthealth >= maxhealth) )
return false;
else if ( (dbar == 1) && !firsthit )
return false;
}
if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE && !mytarget.bCORPSE )
{
// players (but not voodoo dolls), always if friendly, otherwise only update if visible
if ( mytarget.player && (mytarget.player.mo == mytarget) )
{
if ( mytarget.IsFriend(players[consoleplayer].mo) ) updated = level.maptime+35;
else if ( players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
updated = level.maptime;
}
// friendlies within a set distance
else if ( mytarget.bFRIENDLY && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+35;
// enemies within a set distance that have us as target
else if ( mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+70;
// any visible enemies within one quarter of the set distance
else if ( players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
}
else if ( (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') )
{
// special stuff, only if visible
if ( players[consoleplayer].Camera.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
}
return false;
}
}
// ultralight trackers for certain things
Class SWWMSimpleTracker play
{
@ -539,7 +346,6 @@ Class SWWMSimpleTracker play
t.Update();
t.next = hnd.strackers;
hnd.strackers = t;
hnd.strackers_cnt++;
return t;
}
}

View file

@ -98,7 +98,6 @@ Class SWWMArmor : Armor abstract
int tgive = 0;
while ( (amount <= (MaxAmount-default.Amount)) && (Owner.CountInv(parent) > 0) )
{
SWWMScoreObj.Spawn(default.Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor);
Amount += default.Amount;
Owner.TakeInventory(parent,1);
// absorb the extra damage too
@ -142,7 +141,6 @@ Class SWWMSpareArmor : Inventory abstract
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_OVERLAP);
Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount);
SWWMHandler.ArmorFlash(Owner.PlayerNumber());
SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor);
return true;
}
return false;

View file

@ -75,7 +75,6 @@ Class SWWMHealth : Inventory abstract
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_OVERLAP);
SWWMHandler.HealthFlash(Owner.PlayerNumber());
Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount);
SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
AutoUseExtra(false);
return true;
}
@ -119,7 +118,6 @@ Class SWWMHealth : Inventory abstract
bool morethanonce = false;
while ( (Amount > 0) && (newdamage > 0) )
{
SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
newdamage = newdamage-GetDefaultByType(giveme).Amount;
if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount);
newdamage = max(0,newdamage);

View file

@ -294,11 +294,7 @@ Class KirinPlushGesture : SWWMItemGesture
s.pitch = FRandom[ExploS](-90,90);
}
int givehp = (Health<=25)?25:(Health<=50)?15:(Health<=100)?10:5;
if ( GiveBody(givehp,200) )
{
SWWMScoreObj.Spawn(givehp,Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Height/2),ST_Health);
SWWMHandler.DoFlash(self,Color(32,224,128,255),10);
}
if ( GiveBody(givehp,200) ) SWWMHandler.DoFlash(self,Color(32,224,128,255),10);
}
action void A_KirinGiggle()
{

View file

@ -55,7 +55,6 @@ Class RefresherRegen : Powerup
Super.DoEffect();
if ( !Owner || (Owner.health <= 0) || ((EffectTics-5)%175) ) return;
if ( !Owner.GiveBody(int(Strength),500) ) return;
SWWMScoreObj.Spawn(int(Strength),Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
SWWMHandler.DoFlash(Owner,Color(32,224,128,255),10);
Owner.A_StartSound("powerup/refresher",CHAN_ITEMEXTRA,CHANF_LOCAL|CHANF_OVERLAP,.4);
if ( Owner is 'Demolitionist' )

View file

@ -42,12 +42,8 @@ Class GrilledCheeseSandwich : Inventory
SWWMHandler.DoFlash(Owner,Color(64,255,255,64),10);
Owner.A_QuakeEx(9,9,9,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
Owner.GiveBody(1000,1000);
SWWMScoreObj.Spawn(1000,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
if ( Owner is 'Demolitionist' )
{
SWWMScoreObj.Spawn(200,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor);
SWWMScoreObj.Spawn(150,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor);
SWWMScoreObj.Spawn(250,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Armor);
let n = Owner.FindInventory("ArmorNugget");
if ( !n ) Owner.GiveInventory("ArmorNugget",GetDefaultByType("ArmorNugget").MaxAmount);
else n.Amount = n.MaxAmount;
@ -1088,8 +1084,7 @@ Class RagekitPower : Powerup
if ( !(level.maptime%30) )
{
SWWMHandler.DoFlash(Owner,Color(16,255,0,0),5);
if ( Owner.GiveBody(1,100) )
SWWMScoreObj.Spawn(1,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
Owner.GiveBody(1,100);
Owner.A_AlertMonsters(swwm_uncapalert?0:2000);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) && (swwm_mutevoice < 2) )
lastrage = SWWMHandler.AddOneliner("ragekit",2,5)+20;
@ -1121,8 +1116,7 @@ Class RagekitPower : Powerup
void DoHitFX()
{
if ( level.maptime <= lasteffect+5 ) return;
if ( Owner.GiveBody(5,100) )
SWWMScoreObj.Spawn(5,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
Owner.GiveBody(5,100);
Owner.A_AlertMonsters(swwm_uncapalert?0:5000);
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),10);
Owner.A_QuakeEx(8,8,8,Random[Rage](3,8),0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.);
@ -1165,8 +1159,7 @@ Class Ragekit : Inventory
{
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
if ( Owner.GiveBody(100,100) )
SWWMScoreObj.Spawn(100,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
Owner.GiveBody(100,100);
SWWMUtility.AchievementProgressInc("rage",1,Owner.player);
if ( !(Owner is 'Demolitionist') )
{
@ -3117,8 +3110,7 @@ Class SafetyTether : Inventory
}
SWWMHandler.DoFlash(Owner,Color(255,255,255,255),10);
SWWMHandler.DoFlash(Owner,Color(255,128,192,255),30);
if ( Owner.GiveBody(100,100) )
SWWMScoreObj.Spawn(100,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
Owner.GiveBody(100,100);
SWWMUtility.AchievementProgressInc("sneaky",1,Owner.player);
Amount--;
if ( Amount <= 0 ) DepleteOrDestroy();
@ -3577,7 +3569,6 @@ Class DivineSpriteEffect : Inventory
{
healcnt = 0;
Owner.GiveBody(1000,1000);
SWWMScoreObj.Spawn(1000,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
SWWMHandler.DoFlash(Owner,Color(40,255,255,255),20);
if ( Owner is 'Demolitionist' )
Demolitionist(Owner).lastbump *= 0.97;
@ -3586,7 +3577,6 @@ Class DivineSpriteEffect : Inventory
else if ( !(healcnt%5) )
{
Owner.GiveBody(500,10000);
SWWMScoreObj.Spawn(100,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),ST_Health);
SWWMHandler.DoFlash(Owner,Color(10,255,255,255),10);
Owner.A_StartSound("powerup/divinehit",CHAN_ITEMEXTRA,CHANF_OVERLAP);
}

View file

@ -203,6 +203,132 @@ Class SWWMFlyTracker : Inventory
}
}
// very lightweight combat tracker, more Souls-y
Class SWWMQuickCombatTracker : Inventory
{
int lifespan, lasthealth, maxhealth;
int laghealth[10];
int cummdamage, cummspan, cummflash; // please do not misread
int fadein; // used for the hud
double lvheight; // height while alive (used by the hud for positioning)
SmoothDynamicValueInterpolator intp;
SmoothLinearValueInterpolator intpl;
PlayerInfo myplayer; // for multiplayer compatibility (this breaks inventory logic, but alas...)
String mytag;
void UpdateTag( SWWMHandler hnd )
{
if ( Owner && (Owner.player || Owner.bISMONSTER || (Owner is 'BossBrain') || (Owner is 'SWWMHangingKeen') || (Owner is 'Demolitionist')) )
{
String realtag = swwm_funtags?SWWMUtility.GetFunTag(hnd,Owner,FallbackTag):Owner.GetTag(FallbackTag);
if ( realtag == FallbackTag )
{
realtag = Owner.GetClassName();
SWWMUtility.BeautifyClassName(realtag);
}
mytag = Owner.player?(Owner.player.mo!=Owner)?String.Format(StringTable.Localize("$FN_VOODOO"),Owner.player.GetUserName()):Owner.player.GetUserName():((Owner is 'PlayerPawn')&&(!Owner.player))?StringTable.Localize("$FN_VOODOO_NP"):realtag;
}
else mytag = "";
}
static SWWMQuickCombatTracker Update( SWWMHandler hnd, PlayerInfo p, Actor target, int damage = 0 )
{
SWWMQuickCombatTracker t = null;
for ( Inventory i=target.inv; i; i=i.inv )
{
if ( !(i is 'SWWMQuickCombatTracker') ) continue;
let tt = SWWMQuickCombatTracker(i);
if ( tt.myplayer != p ) continue;
t = tt;
break;
}
if ( t )
{
// re-fade in
if ( t.lifespan < 20 ) t.fadein = t.lifespan/4;
t.lifespan = max(t.lifespan,target.bISMONSTER?((damage>0)?700:70):((damage>0)?140:35));
if ( damage > 0 )
{
t.cummdamage += damage;
t.cummspan = target.bISMONSTER?120:30;
t.cummflash = 15;
}
return t;
}
t = SWWMQuickCombatTracker(Spawn("SWWMQuickCombatTracker"));
t.myplayer = p;
t.AttachToOwner(target);
// players always use SpawnHealth
if ( target.player ) t.maxhealth = target.SpawnHealth();
else t.maxhealth = max(target.health,target.SpawnHealth());
t.lasthealth = target.health;
int prevhealth = min(t.maxhealth,target.health+damage); // guessed health before damage was dealt
for ( int i=0; i<10; i++ ) t.laghealth[i] = prevhealth;
if ( damage > 0 )
{
t.cummdamage = damage;
t.cummspan = target.bISMONSTER?120:30;
t.cummflash = 15;
}
t.intp = SmoothDynamicValueInterpolator.Create(t.lasthealth,.5,1,100);
t.intpl = SmoothLinearValueInterpolator.Create(t.laghealth[9],max(1,t.maxhealth/20));
t.lifespan = target.bISMONSTER?((damage>0)?700:70):((damage>0)?140:35);
t.fadein = 0;
t.lvheight = target.Height;
t.UpdateTag(hnd);
return t;
}
// expiration, and interpolator updates
override void DoEffect()
{
Super.DoEffect();
// immediately expire if the owner is gone or has morphed
if ( !Owner || Owner.bUnmorphed )
{
Destroy();
return;
}
// cap lifespan if owner is dead
if ( Owner.Health <= 0 ) lifespan = min(lifespan,35);
else lvheight = Owner.Height;
if ( cummflash > 0 ) cummflash--;
if ( cummspan > 0 )
{
cummspan--;
if ( cummspan <= 0 ) cummdamage = 0;
}
if ( Owner.Health > lasthealth )
{
if ( !Owner.player && (Owner.Health > maxhealth) )
maxhealth = Owner.Health;
for ( int i=9; i>0; i-- )
laghealth[i] = Owner.Health;
}
laghealth[0] = lasthealth = Owner.Health;
intp.Update(lasthealth);
intpl.Update(laghealth[9]);
for ( int i=9; i>0; i-- )
laghealth[i] = laghealth[i-1];
if ( fadein < 5 ) fadein++;
lifespan--;
if ( lifespan <= 0 ) Destroy();
}
override void PreTravelled()
{
// expire immediately when switching maps
Destroy();
}
default
{
+INVENTORY.UNTOSSABLE;
+INVENTORY.UNDROPPABLE;
+INVENTORY.UNCLEARABLE;
}
}
// fractic-compatible interpolators, with double value
Class SmoothLinearValueInterpolator
{

View file

@ -217,10 +217,7 @@ Class SWWMGesture : SWWMWeapon
raging.DoHitFX();
}
if ( pt.target.GiveBody(healamt,pt.target.GetSpawnHealth()) )
{
SWWMScoreObj.Spawn(healamt,pt.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+pt.target.Height/2),ST_Health);
SWWMHandler.DoFlash(pt.target,Color(32,224,128,255),10);
}
}
let s = Demolitionist(self).mystats;
if ( s ) s.pats++;

View file

@ -122,10 +122,7 @@ Class LoveHeart : Actor
raging.DoHitFX();
}
if ( target.GiveBody(healamt,target.GetSpawnHealth()) )
{
SWWMScoreObj.Spawn(healamt,target.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+target.Height/2),ST_Health);
SWWMHandler.DoFlash(target,Color(32,224,128,255),10);
}
if ( SWWMUtility.IdentifyingDog(target) )
{
// befriend good doggo

View file

@ -5,10 +5,11 @@ Class SWWMHandler : EventHandler
{
transient int lastlock;
transient int lastpickuptic[MAXPLAYERS];
SWWMCombatTracker trackers;
SWWMScoreObj scorenums, damnums;
SWWMScoreObj scorenums;
SWWMDamNum damnums;
SWWMInterest intpoints;
int trackers_cnt, scorenums_cnt, damnums_cnt, intpoints_cnt;
Array<String> damtypes, damcolors;
transient CVar usedamcolors;
transient int slotstrictwarn;
transient ui String sswstr;
@ -103,6 +104,26 @@ Class SWWMHandler : EventHandler
bludtypes.Push(list[i]);
}
}
// read damnum colors
damtypes.Clear();
damcolors.Clear();
for ( int lmp = Wads.FindLump("DAMTYPES"); lmp != -1; lmp = Wads.FindLump("DAMTYPES",lmp+1) )
{
String dat = Wads.ReadLump(lmp);
Array<String> list;
// fucking Windows
dat.Replace("\r","");
list.Clear();
dat.Split(list,"\n");
for ( int i=0; i<list.Size(); i++ )
{
if ( (list[i].Length() == 0) || (list[i].Left(1) == "#") || (list[i].Left(1) == "") )
continue;
int spc = list[i].IndexOf(" ");
damtypes.Push(list[i].Left(spc));
damcolors.Push(list[i].Mid(spc+1));
}
}
// cache various services into the handler on register
// this dramatically reduces overhead by not having to use an iterator every time they're needed
// especially noticeable for fun tags, as they're looked up for every monster on map load
@ -158,6 +179,30 @@ Class SWWMHandler : EventHandler
Console.Printf(StringTable.Localize("$SWWM_NEWMISSION"));
}
}
for ( int i=0; i<legtrack.Size(); i++ )
{
// when a monster mutates, update its healthbar for all players
for ( int j=0; j<MAXPLAYERS; j++ )
{
if ( !playeringame[j] ) continue;
let t = SWWMQuickCombatTracker.Update(self,players[j],legtrack[i].Owner);
if ( t.myplayer == players[consoleplayer] ) Console.Printf(StringTable.Localize("$SWWM_LTFORM"),t.mytag);
}
legtrack.Delete(i--);
}
// healthbar on whatever the player is aiming at
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (players[i].Health <= 0) || !players[i].mo ) continue;
let t = players[i].mo.GetPointer(AAPTR_PLAYER_GETTARGET);
if ( t && t.bSHOOTABLE && (t.Health > 0) ) SWWMQuickCombatTracker.Update(self,players[i],t);
// keep healthbars updated for all friends of this player
for ( int j=0; j<MAXPLAYERS; j++ )
{
if ( !playeringame[j] || (players[j].Health <= 0) || !players[j].mo || !players[j].mo.IsFriend(players[i].mo) ) continue;
SWWMQuickCombatTracker.Update(self,players[j],players[i].mo);
}
}
OnelinerTick();
FlashTick();
ItemCountTrack();

View file

@ -77,7 +77,6 @@ Class Demolitionist : PlayerPawn
Vector3 pretelepos;
SWWMItemSense itemsense;
int itemsense_cnt;
int healcooldown, healtimer, oldhealth;
bool scriptedinvul;
@ -670,7 +669,6 @@ Class Demolitionist : PlayerPawn
if ( prev ) prev.next = next;
else itemsense = next;
itm.Destroy();
itemsense_cnt--;
}
else prev = itm;
itm = next;
@ -2551,7 +2549,6 @@ Class Demolitionist : PlayerPawn
lastbump *= 1.5;
SWWMHandler.DoFlash(self,Color(255,255,255,255),10);
SWWMHandler.DoFlash(self,Color(255,128,192,255),30);
SWWMScoreObj.Spawn(default.health,Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Height/2),ST_Health);
if ( special1 > 2 ) special1 = 0;
if ( (swwm_revivecooldown > 0) && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4) )
GiveInventory("ReviveCooldown",1);
@ -3312,7 +3309,6 @@ Class Demolitionist : PlayerPawn
si.Destroy();
si = next;
}
itemsense_cnt = 0;
SWWMMagItem mi = magitem;
while ( mi )
{

View file

@ -95,7 +95,6 @@ Class HealthOrb : SlayerOrb
int hp = int(ceil(abs(scale.x*10)));
mo.GiveBody(hp,100);
SWWMHandler.HealthFlash(np);
SWWMScoreObj.Spawn(hp,mo.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+mo.Height/2),ST_Health);
}
States
{
@ -127,7 +126,6 @@ Class ArmorOrb : SlayerOrb
}
if ( n.Amount < 100 ) n.Amount = min(n.Amount+hp,100);
SWWMHandler.ArmorFlash(np);
SWWMScoreObj.Spawn(hp,mo.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+mo.Height/2),ST_Armor);
}
States
{

View file

@ -389,7 +389,7 @@ Class BusterWall : Thinker
// damnums
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
for ( int i=0; i<bust.acchits.Size(); i++ )
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
SWWMDamNum.Spawn(bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),'Wallbust');
return true;
}
@ -624,7 +624,7 @@ Class BusterWall : Thinker
// damnums
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
for ( int i=0; i<bust.acchits.Size(); i++ )
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
SWWMDamNum.Spawn(bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),'Wallbust');
return true;
}
}