swwmgz_m/zscript/hud/swwm_hud_target.zsc
Marisa the Magician 964dcf84f9 Various UI changes:
* Add optional numeric values to healthbars, like in the standalone mod.
 * Remove minimap themes, keep only the Demolitionist color set.
 * Draw keys in the minimap using their respective colors.
 * Don't send AutoAutosave tokens to the minimap.
 * Tweak the minimap color of shootable actors so they look less like usable lines.
 * Use smaller font for cumulative damage on boss healthbars.
2023-08-17 20:13:16 +02:00

398 lines
21 KiB
Text

// Targeter
extend Class SWWMStatusBar
{
static private string FormatDist( double dist )
{
double meters = dist/32.;
if ( meters > 1000. ) return String.Format("\cj%d\cc%s",int(meters/1000.),StringTable.Localize("$SWWM_UNIT_KILOMETER"));
return String.Format("\cj%d\cc%s",int(meters),StringTable.Localize("$SWWM_UNIT_METER"));
}
private void DrawInterest( Vector3 viewvec, out bool projinit )
{
String tag;
SWWMInterest poi = hnd.intpoints;
if ( !poi ) 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,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)/hs2;
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(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 int GetItemFontColor( SWWMitemSense s )
{
let col = s.scoreitem?Font.CR_YELLOW:Font.CR_WHITE;
let i = (s.item is 'SWWMRespawnTimer')?s.item.tracer:s.item;
if ( i is 'Weapon' ) col = s.vipitem?Font.FindFontColor('VIPGold'):Font.CR_GOLD;
else if ( i is 'MagAmmo' ) col = s.vipitem?Font.FindFontColor('VIPTan'):Font.CR_TAN;
else if ( (i is 'BackpackItem') || (i is 'HammerspaceEmbiggener') ) col = Font.CR_DARKBROWN;
else if ( i is 'Ammo' ) col = s.vipitem?Font.FindFontColor('VIPBrown'):Font.CR_BROWN;
else if ( (i is 'PowerupGiver') || (i is 'AmmoFabricator') || Inventory(i).bBIGPOWERUP ) col = s.vipitem?Font.FindFontColor('VIPPurple'):Font.CR_PURPLE;
else if ( (i is 'Health') || (i is 'HealthPickup') || (i is 'SWWMHealth') ) col = Font.CR_RED;
else if ( (i is 'Armor') || (i is 'SWWMSpareArmor') ) col = Font.CR_GREEN;
else if ( i is 'PuzzleItem' ) col = Font.CR_LIGHTBLUE;
else if ( i is 'Key' ) col = Font.CR_UNTRANSLATED;
else if ( i is 'SWWMCollectible' ) col = Font.CR_PURPLE;
return col;
}
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,GetItemFontColor(s),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;
bool onlymonsters = (swwm_targeter >= 2);
bool onlyfriends = (swwm_targeter >= 3);
bool drawvalues = swwm_targetvals;
int fadedist = swwm_bardist;
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 if max health is zero (SOMEHOW can happen)
if ( ct.maxhealth <= 0 ) continue;
// ignore player trackers unless voodoo dolls
if ( ct.Owner.player && (ct.Owner.player.mo == ct.Owner) ) continue;
// ignore local player or camera
if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue;
// ignore trackers not of this player
if ( ct.myplayer != CPlayer ) continue;
// ignore non-monsters if filtering monsters
// easy check since they have empty tags
if ( onlymonsters && (ct.mytag == "") ) continue;
// ignore enemies if filtering friends
if ( onlyfriends && (!ct.Owner.IsFriend(CPlayer.mo) || ct.Owner.player) ) 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;
// ignore trackers that are too far away
double dist = tdir.length();
if ( (fadedist > 0) && (dist > fadedist*1.5) ) 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;
if ( fadedist > 0 )
{
double df = 1.-(clamp((dist-fadedist)/fadedist,0.,.5)*2.);
alph *= df;
}
Vector2 barpos = vpos-(27,7);
if ( drawvalues )
{
String val = String.Format("%d %d",ct.maxhealth,ct.maxhealth);
int valw = MiniHUDFontOutline.StringWidth(val);
val = String.Format("%d \c[MiniWhite]%d",ct.lasthealth,ct.maxhealth);
int ofsw = valw-MiniHUDFontOutline.StringWidth(val);
int col = (ct.lasthealth>ct.maxhealth)?mhudfontcol[MCR_BRASS]:(ct.lasthealth>=(ct.maxhealth/2))?mhudfontcol[MCR_WHITE]:(ct.lasthealth>=(ct.maxhealth/10))?mhudfontcol[MCR_RED]:mhudfontcol[MCR_SAYAHUD];
Screen.DrawText(MiniHUDFontOutline,col,vpos.x-int((valw/2)-ofsw),barpos.y-(MiniHUDFontOutline.GetHeight()+2),val,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawChar(MiniHUDFontOutline,mhudfontcol[MCR_IBUKIHUD],vpos.x-3,barpos.y-(MiniHUDFontOutline.GetHeight()+2),0x2F,DTA_VirtualWidthF,ss1.x,DTA_VirtualHeightF,ss1.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
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 ( 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
}
// voodoo dolls aren't friends
if ( ct.Owner.IsFriend(CPlayer.mo) && !ct.Owner.player ) tag = "\cg♥\c- "..tag.." \cg♥\c-";
int ofsh = drawvalues?(MiniHUDFontOutline.GetHeight()+4):2;
Screen.DrawText(fnt,Font.CR_WHITE,vpos.x-fnt.StringWidth(tag)/2,barpos.y-(fnt.GetHeight()+ofsh),tag,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);
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,(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,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":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 if max health is zero (SOMEHOW can happen)
if ( ct.maxhealth <= 0 ) continue;
// ignore non-player trackers and voodoo dolls
if ( !ct.Owner.player || (ct.Owner.player.mo != ct.Owner) ) continue;
// ignore local player or camera
if ( (ct.Owner == CPlayer.mo) || (ct.Owner == cam) ) continue;
// ignore trackers not of this player
if ( ct.myplayer != CPlayer ) continue;
// ignore enemies if filtering friends
if ( onlyfriends && !ct.Owner.IsFriend(CPlayer.mo) ) 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 = 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;
}
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[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
{
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,(ct.cummdamage>=Actor.TELEFRAG_DAMAGE)?"∞":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);
}
}
private void DrawNumbers( Vector3 viewvec, out bool projinit )
{
SWWMDamNum snum = hnd.damnums;
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)/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.);
// 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 += 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(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();
}
}