swwmgz_m/zscript/swwm_hud.zsc
Marisa Kirisame af1df2e18d Allow selling excess weapons/powerups.
Change of plans for one DLC weapon choice.
Various small fixes and tweaks.
Doubled price of Ynykron, since it was too cheap.
Fixed Candygun spares not being picked up in coop.
Add option to toggle menu fuzz, for streamers concerned about bitrate.
Add MAP07 enemies to the vanilla boss enhancer.
2020-05-19 14:30:35 +02:00

1140 lines
45 KiB
Text

// The SWWM GZ HUD is mostly built on top of what I had already done for SWWM Z, also a bit of Dark Souls
Class MsgLine
{
String str;
int tic, type, rep;
}
Class SWWMStatusBar : BaseStatusBar
{
TextureID StatusTex, WeaponTex, ScoreTex[3], InventoryTex, ChatTex[6],
HealthTex[5], FuelTex, DashTex, EnemyBTex, EnemyHTex[5], GenericAmmoTex[3];
HUDFont mTewiFont, mMiniwiFont, mMPlusFont, mk6x8Font;
// "Full History" contains all messages since session start, nothing is flushed
// this can be accessed from a section of the knowledge base
Array<MsgLine> MainQueue, PickupQueue, FullHistory;
// sorted arrays of various elements
Array<SWWMInterest> intpoints;
Array<SWWMScoreObj> scoreobjs;
Array<SWWMCombatTracker> trackers;
// the event handler, holding all sorts of stuff
SWWMHandler hnd;
// the boss stuff handler, for the boss healthbar
SWWMVanillaBossHandler bosshnd;
// client cvars
transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol, targetter, healthnums, scorenums, scorebonus, targettag, lang, maxtarg, maxscore, maxdamns, hscale, bscale, nscale, sscale;
// shared stuff
Vector2 ss, hs;
Vector2 ssb, hsb;
Vector2 ssn, hsn;
Vector2 sss, hss;
int margin;
double FracTic;
int chatopen;
// shared from renderunderlay, needed for proper interpolation of some things
Vector3 viewpos, viewrot;
// libeye stuff
swwmLe__ProjScreen proj;
swwmLe__GLScreen gl_proj;
swwmLe__SWScreen sw_proj;
swwmLe__Viewport viewport;
bool can_project;
transient CVar cvar_renderer;
DynamicValueInterpolator HealthInter, ScoreInter, ScoreInter2, FuelInter, DashInter;
// if playing in Japanese, returns an alternate font of the same height
// Tewi -> MPlus
// Miniwi -> k6x8
Font LangFont( HUDFont req )
{
if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
if ( lang.GetString() ~== "jp" )
{
if ( req == mMiniwiFont ) return mk6x8Font.mFont;
return mMPlusFont.mFont;
}
return req.mFont;
}
override void FlushNotify()
{
// flush interpolators (useful since this virtual gets called
// when loading saves, too)
HealthInter.Reset(CPlayer.Health);
ScoreInter.Reset(SWWMCredits.Get(CPlayer));
FuelInter.Reset((CPlayer.mo is 'Demolitionist')?int(Demolitionist(CPlayer.mo).dashfuel):0);
DashInter.Reset((CPlayer.mo is 'Demolitionist')?int((40-Demolitionist(CPlayer.mo).dashcooldown)*3.):0);
if ( level.maptime <= 1 )
{
// flush ALL messages
MainQueue.Clear();
PickupQueue.Clear();
return;
}
// flush non-chat messages
for ( int i=0; i<MainQueue.Size(); i++ )
{
if ( MainQueue[i].type >= PRINT_CHAT ) continue;
MainQueue.Delete(i);
i--;
}
}
override bool ProcessMidPrint( Font fnt, String msg, bool bold )
{
// check for Korax lines
if ( msg == StringTable.Localize("$TXT_ACS_MAP02_11_AREYO") )
EventHandler.SendNetworkEvent("swwmkoraxline",0,consoleplayer);
else if ( msg == StringTable.Localize("$TXT_ACS_MAP13_11_MYSER") )
EventHandler.SendNetworkEvent("swwmkoraxline",1,consoleplayer);
else if ( msg == StringTable.Localize("$TXT_ACS_MAP22_29_ITHIN") )
EventHandler.SendNetworkEvent("swwmkoraxline",2,consoleplayer);
else if ( msg == StringTable.Localize("$TXT_ACS_MAP27_10_THENA") )
EventHandler.SendNetworkEvent("swwmkoraxline",3,consoleplayer);
else if ( msg == StringTable.Localize("$TXT_ACS_MAP35_14_TOFAC") )
EventHandler.SendNetworkEvent("swwmkoraxline",4,consoleplayer);
return false;
}
override bool ProcessNotify( EPrintLevel printlevel, String outline )
{
let m = new("MsgLine");
m.str = outline.Left(outline.Length()-1); // strip newline
m.type = printlevel;
m.tic = level.totaltime;
m.rep = 1;
// append chat messages to full history
if ( (printlevel == PRINT_CHAT) || (printlevel == PRINT_TEAMCHAT) )
FullHistory.Push(m);
// ignore during intermission
if ( gamestate != GS_LEVEL ) return false;
if ( (printlevel < PRINT_LOW) || (printlevel > PRINT_TEAMCHAT) ) return true; // we couldn't care less about these
if ( printlevel == PRINT_LOW )
{
// check if repeated
for ( int i=0; i<PickupQueue.Size(); i++ )
{
if ( PickupQueue[i].str != m.str ) continue;
// delete old one and add its repeats
m.rep += PickupQueue[i].rep;
PickupQueue.Delete(i);
break;
}
PickupQueue.Push(m);
}
else
{
// check if repeated
for ( int i=0; i<MainQueue.Size(); i++ )
{
if ( MainQueue[i].str != m.str ) continue;
// delete old one and add its repeats
m.rep += MainQueue[i].rep;
MainQueue.Delete(i);
break;
}
MainQueue.Push(m);
}
return true;
}
void PrepareProjection()
{
if ( !cvar_renderer ) cvar_renderer = CVar.GetCVar("vid_rendermode",players[consoleplayer]);
if ( !cvar_renderer )
{
can_project = proj = gl_proj;
return;
}
switch ( cvar_renderer.GetInt() )
{
case 0:
case 1:
proj = sw_proj;
break;
default:
proj = gl_proj;
break;
}
can_project = proj;
}
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);
}
override void Tick()
{
Super.Tick();
PrepareProjection();
if ( !chatduration ) chatduration = CVar.GetCVar('swwm_chatduration',players[consoleplayer]);
if ( !msgduration ) msgduration = CVar.GetCVar('swwm_msgduration',players[consoleplayer]);
if ( !pickduration ) pickduration = CVar.GetCVar('swwm_pickduration',players[consoleplayer]);
if ( !targetter ) targetter = CVar.GetCVar('swwm_targeter',players[consoleplayer]);
if ( !healthnums ) healthnums = CVar.GetCVar('swwm_healthnums',players[consoleplayer]);
if ( !scorenums ) scorenums = CVar.GetCVar('swwm_scorenums',players[consoleplayer]);
if ( !maxtarg ) maxtarg = CVar.GetCVar('swwm_maxtargets',players[consoleplayer]);
if ( !maxscore ) maxscore = CVar.GetCVar('swwm_maxscorenums',players[consoleplayer]);
if ( !maxdamns ) maxdamns = CVar.GetCVar('swwm_maxdamnums',players[consoleplayer]);
int maxtargetnum = max(0,maxtarg.GetInt());
int maxscorenum = max(0,maxscore.GetInt());
int maxdamnum = max(0,maxdamns.GetInt());
// prune old messages
for ( int i=0; i<PickupQueue.Size(); i++ )
{
if ( level.totaltime < (PickupQueue[i].tic+35*pickduration.GetInt()) ) continue;
PickupQueue.Delete(i);
i--;
}
for ( int i=0; i<MainQueue.Size(); i++ )
{
if ( (MainQueue[i].type <= PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+35*msgduration.GetInt())) ) continue;
else if ( (MainQueue[i].type > PRINT_HIGH) && (level.totaltime < (MainQueue[i].tic+35*chatduration.GetInt())) ) continue;
MainQueue.Delete(i);
i--;
}
// update interpolators
HealthInter.Update(CPlayer.health);
if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
int s1, s2;
[s1, s2] = SWWMCredits.Get(CPlayer);
ScoreInter.Update(s1);
if ( ScoreInter.GetValue() >= ScoreInter.mCurrentValue ) ScoreInter2.Update(s2);
let d = Demolitionist(CPlayer.mo);
if ( d )
{
FuelInter.Update(int(d.dashfuel));
DashInter.Update(int((40-d.dashcooldown)*3.));
}
else
{
FuelInter.Update(0);
DashInter.Update(0);
}
// let weapons update their own interpolators
if ( CPlayer.ReadyWeapon is 'SWWMWeapon' )
SWWMWeapon(CPlayer.ReadyWeapon).HudTick();
bool thesight = CPlayer.mo.FindInventory("Omnisight");
let cam = players[consoleplayer].camera;
Vector3 viewvec = (cos(viewrot.x)*cos(viewrot.y),sin(viewrot.x)*cos(viewrot.y),sin(-viewrot.y));
int sz;
if ( thesight )
{
// 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;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) 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 ( targetter.GetBool() )
{
// 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 player unless chasecamming
if ( (trk.mytarget == players[consoleplayer].mo) && (players[consoleplayer].Camera == players[consoleplayer].mo) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue;
if ( trk.myplayer && deathmatch ) continue; // no players in dm
int mtime = 35;
if ( thesight && (trk.lasthealth > 0) ) mtime += 105;
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;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) 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);
// cap if limited (must cap after sorting, though, otherwise it'll look weird)
if ( maxtargetnum && (trackers.Size() > maxtargetnum) )
{
int endo = trackers.Size()-maxtargetnum;
// we gotta push the frontmost bars to the start, due to the inverted draw order
for ( int i=maxtargetnum-1; i>=0; i-- )
trackers[i] = trackers[endo+i];
trackers.Resize(maxtargetnum);
}
}
else trackers.Clear();
// update floating scores, adding the scorenums first, then the damnums
int total_sz = 0;
if ( scorenums.GetBool() ) total_sz += maxscorenum?min(maxscorenum,hnd.scorenums_cnt):hnd.scorenums_cnt;
if ( healthnums.GetBool() ) total_sz += maxdamnum?min(maxdamnum,hnd.damnums_cnt):hnd.damnums_cnt;
if ( scoreobjs.Size() != total_sz )
scoreobjs.Resize(total_sz);
int i = 0;
if ( scorenums.GetBool() )
{
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;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
scoreobjs[i++] = scr;
}
}
if ( healthnums.GetBool() )
{
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;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) 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);
}
override void Init()
{
Super.Init();
SetSize(0,640,360);
StatusTex = TexMan.CheckForTexture("graphics/HUD/StatusBox.png",TexMan.Type_Any);
DashTex = TexMan.CheckForTexture("graphics/HUD/DashBar.png",TexMan.Type_Any);
FuelTex = TexMan.CheckForTexture("graphics/HUD/FuelBar.png",TexMan.Type_Any);
HealthTex[0] = TexMan.CheckForTexture("graphics/HUD/HealthBar0.png",TexMan.Type_Any);
HealthTex[1] = TexMan.CheckForTexture("graphics/HUD/HealthBar1.png",TexMan.Type_Any);
HealthTex[2] = TexMan.CheckForTexture("graphics/HUD/HealthBar2.png",TexMan.Type_Any);
HealthTex[3] = TexMan.CheckForTexture("graphics/HUD/HealthBar3.png",TexMan.Type_Any);
HealthTex[4] = TexMan.CheckForTexture("graphics/HUD/HealthBarS.png",TexMan.Type_Any);
ScoreTex[0] = TexMan.CheckForTexture("graphics/HUD/ScoreBoxL.png",TexMan.Type_Any);
ScoreTex[1] = TexMan.CheckForTexture("graphics/HUD/ScoreBoxM.png",TexMan.Type_Any);
ScoreTex[2] = TexMan.CheckForTexture("graphics/HUD/ScoreBoxR.png",TexMan.Type_Any);
WeaponTex = TexMan.CheckForTexture("graphics/HUD/WeaponBox.png",TexMan.Type_Any);
ChatTex[0] = TexMan.CheckForTexture("graphics/HUD/ChatBoxTop.png",TexMan.Type_Any);
ChatTex[1] = TexMan.CheckForTexture("graphics/HUD/ChatBoxLine.png",TexMan.Type_Any);
ChatTex[2] = TexMan.CheckForTexture("graphics/HUD/ChatBoxBottom.png",TexMan.Type_Any);
ChatTex[3] = TexMan.CheckForTexture("graphics/HUD/ChatBoxTop_Smol.png",TexMan.Type_Any);
ChatTex[4] = TexMan.CheckForTexture("graphics/HUD/ChatBoxLine_Smol.png",TexMan.Type_Any);
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);
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);
mTewiFont = HUDFont.Create("TewiShaded");
mMiniwiFont = HUDFont.Create("MiniwiShaded");
mMPlusFont = HUDFont.Create("MPlusShaded");
mk6x8Font = HUDFont.Create("k6x8Shaded");
HealthInter = DynamicValueInterpolator.Create(100,.1,1,100);
ScoreInter = DynamicValueInterpolator.Create(0,.1,1,999999999);
ScoreInter2 = DynamicValueInterpolator.Create(0,.1,1,999999999);
FuelInter = DynamicValueInterpolator.Create(120,.5,1,100);
DashInter = DynamicValueInterpolator.Create(120,.5,1,40);
gl_proj = new("swwmLe__GLScreen");
sw_proj = new("swwmLe__SWScreen");
PrepareProjection();
hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
}
static private string FormatDist( double dist )
{
double meters = dist/32.;
if ( meters > 1000. ) return String.Format("\cj%d\cckm",int(meters/1000.));
return String.Format("\cj%d\ccm",int(meters));
}
private void DrawTarget()
{
// don't draw when dead or with automap open
if ( (CPlayer.health <= 0) || automapactive ) return;
if ( !targettag ) targettag = CVar.GetCVar('swwm_targettags',players[consoleplayer]);
viewport.FromHud();
proj.CacheResolution();
proj.CacheFov(players[consoleplayer].fov);
proj.Reorient(ViewPos,ViewRot);
proj.BeginProjection();
Vector3 vdir = (cos(ViewRot.x)*cos(ViewRot.y),sin(ViewRot.x)*cos(ViewRot.y),-sin(ViewRot.y));
bool thesight = !!CPlayer.mo.FindInventory("Omnisight");
// points of interest
String tag;
if ( thesight )
{
for ( int i=0; i<intpoints.Size(); i++ )
{
let poi = intpoints[i];
if ( !poi ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,poi.pos);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag);
else if ( poi.type == INT_Exit )
{
if ( poi.trackedline.special == Exit_Secret )
tag = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_SEXIT"));
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
Font fnt = LangFont(mMiniwiFont);
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsb.x*fnt.StringWidth(tag)/2.)/hsb.x,(vpos.y-hsb.y*fnt.GetHeight()/2.)/hsb.y,tag,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsb.x*fnt.StringWidth(tag)/2.)/hsb.x,(vpos.y+hsb.y*fnt.GetHeight()/2.)/hsb.y,tag,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true);
}
}
// targetting array
for ( int i=0; i<trackers.Size(); i++ )
{
let targ = trackers[i];
if ( !targ ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,targ.prevpos*(1.-fractic)+targ.pos*fractic);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
tag = targ.mytag;
if ( targ.legged )
{
if ( StringTable.Localize("$SWWM_LEGPREFIX") == "R" ) tag = tag..StringTable.Localize("$SWWM_LEG");
else tag = StringTable.Localize("$SWWM_LEG")..tag;
}
int mtime = 35;
if ( thesight && (targ.lasthealth > 0) ) mtime += 105;
double alph = clamp(((targ.updated+mtime)-level.maptime)/35.,0.,1.);
Vector2 barsiz = TexMan.GetScaledSize(EnemyBTex);
barsiz.x *= hsb.x;
barsiz.y *= hsb.y;
Vector2 barpos = vpos-(barsiz/2.);
barpos.y -= 16.;
Font fnt = LangFont(mMiniwiFont);
if ( targettag.GetBool() || targ.myplayer && (tag != "") )
Screen.DrawText(fnt,Font.CR_WHITE,(barpos.x+barsiz.x/2.-(fnt.StringWidth(tag)*hsb.x)/2.)/hsb.x,(barpos.y-fnt.GetHeight()*hsb.y)/hsb.y,tag,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawTexture(EnemyBTex,false,barpos.x/hsb.x,barpos.y/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph);
int ht = clamp(targ.intp.GetValue(),0,targ.maxhealth*10);
int hw = int((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.DrawTexture(EnemyHTex[4],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw);
continue;
}
Screen.DrawTexture(EnemyHTex[0],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw);
if ( ht > targ.maxhealth )
{
hw = int((min(ht-targ.maxhealth,targ.maxhealth)*50.)/targ.maxhealth);
Screen.DrawTexture(EnemyHTex[1],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw);
}
if ( ht > targ.maxhealth*2 )
{
hw = int((min(ht-targ.maxhealth*2,targ.maxhealth*3)*50.)/(targ.maxhealth*3));
Screen.DrawTexture(EnemyHTex[2],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw);
}
if ( ht > targ.maxhealth*5 )
{
hw = int((min(ht-targ.maxhealth*5,targ.maxhealth*5)*50.)/(targ.maxhealth*5));
Screen.DrawTexture(EnemyHTex[3],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRight,hw);
}
}
if ( !scorebonus ) scorebonus = CVar.GetCVar('swwm_scorebonus',players[consoleplayer]);
// floating kill scores and others
for ( int i=0; i<scoreobjs.Size(); i++ )
{
let snum = scoreobjs[i];
if ( !snum ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,snum.pos);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
tag = String.Format("%+d",snum.score);
double alph = clamp((snum.lifespan+fractic)/35.,0.,1.);
Vector2 fo = (0,0);
bool isscore = false;
if ( snum.tcolor == Font.CR_RED )
{
// damage falls down
int initspd = (128-snum.seed);
if ( initspd >= 0 && initspd < 32 ) initspd = 32;
if ( initspd < 0 && initspd > -32 ) initspd = -32;
int boostup = 64+snum.seed2/2;
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 if ( (snum.tcolor == Font.CR_GREEN) || (snum.tcolor == Font.CR_BLUE) )
{
// health falls up (?)
int initspd = (128-snum.seed);
if ( initspd >= 0 && initspd < 32 ) initspd = 32;
if ( initspd < 0 && initspd > -32 ) initspd = -32;
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;
}
Font fnt = mMiniwiFont.mFont;
if ( isscore )
Screen.DrawText(fnt,snum.tcolor,(vpos.x-hss.x*(fo.x+fnt.StringWidth(tag)/2.))/hss.x,(vpos.y-hss.y*(fo.y+(fnt.GetHeight()/2.)))/hss.y,tag,DTA_VirtualWidthF,sss.x,DTA_VirtualHeightF,sss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
else
Screen.DrawText(fnt,snum.tcolor,(vpos.x-hsn.x*(fo.x+fnt.StringWidth(tag)/2.))/hsn.x,(vpos.y-hsn.y*(fo.y+(fnt.GetHeight()/2.)))/hsn.y,tag,DTA_VirtualWidthF,ssn.x,DTA_VirtualHeightF,ssn.y,DTA_KeepRatio,true,DTA_Alpha,alph);
// extra strings (if available)
if ( !scorebonus.GetBool() ) continue;
fo.y += fnt.GetHeight();
fnt = LangFont(mMiniwiFont);
for ( int i=0; i<snum.xcnt; 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(fnt,snum.xtcolor[i],(vpos.x-hss.x*(fo.x+fnt.StringWidth(tag)/2.))/hss.x,(vpos.y-hss.y*(fo.y+(fnt.GetHeight()/2.)))/hss.y,tag,DTA_VirtualWidthF,sss.x,DTA_VirtualHeightF,sss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
fo.y += fnt.GetHeight();
}
}
}
private void DrawScore()
{
if ( ScoreInter2.GetValue() > 0 )
{
int digits = 9;
digits += int(Log10(ScoreInter2.GetValue()))+1;
int xx = 19+6*digits;
Screen.DrawTexture(ScoreTex[0],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx -= 15;
for ( int i=0; i<digits; i++ )
{
Screen.DrawTexture(ScoreTex[1],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx -= 6;
}
Screen.DrawTexture(ScoreTex[2],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,ss.x-(margin+4+6*digits),margin+1,String.Format("%d",clamp(ScoreInter2.GetValue(),0,999999999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,ss.x-(margin+58),margin+1,String.Format("%09d",clamp(ScoreInter.GetValue(),0,999999999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else
{
int xx = 73;
Screen.DrawTexture(ScoreTex[0],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx -= 15;
for ( int i=0; i<9; i++ )
{
Screen.DrawTexture(ScoreTex[1],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx -= 6;
}
Screen.DrawTexture(ScoreTex[2],false,ss.x-(margin+xx),margin,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,ss.x-(margin+58),margin+1,String.Format("%09d",clamp(ScoreInter.GetValue(),0,999999999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
// in doom/heretic (draw key icons)
if ( !(gameinfo.gametype&(GAME_DOOMCHEX|GAME_HERETIC)) ) return;
Vector2 keypos = (ss.x-(margin+2),margin+19);
int colc = 0;
double colh = 0;
int n = Key.GetKeyTypeCount();
for ( int i=0; i<n; i++ )
{
let k = Key(CPlayer.mo.FindInventory(Key.GetKeyType(i)));
if ( !k || !k.Icon.IsValid() ) continue;
Vector2 siz = TexMan.GetScaledSize(k.Icon);
Screen.DrawTexture(k.Icon,false,keypos.x-siz.x,keypos.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
keypos.x -= siz.x+2;
colh = max(colh,siz.y);
if ( ++colc == 8 )
{
keypos.x = ss.x-(margin+2);
keypos.y += colh+2;
colh = colc = 0;
}
}
}
private void DrawInvIcon( Inventory i, double xx, double yy, double alpha = 1., bool forceamt = false, bool aspowerup = false )
{
if ( !i || !i.Icon.IsValid() ) return;
Vector2 scl = TexMan.GetScaledSize(i.Icon);
double mscl = 30./max(scl.x,scl.y);
double dw = (ss.x/mscl), dh = (ss.y/mscl);
double dx = (xx+(30-scl.x*mscl)/2)/mscl, dy = (yy+(30-scl.y*mscl)/2)/mscl;
if ( i is 'Powerup' )
{
Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,Powerup(i).IsBlinking()?alpha*.5:alpha,DTA_TopOffset,0,DTA_LeftOffset,0);
String nstr = String.Format("%ds",Powerup(i).EffectTics/Thinker.TICRATE);
int len = mTewiFont.mFont.StringWidth(nstr);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,(xx+30)-len,(yy+30)-11,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,Powerup(i).IsBlinking()?alpha*.5:alpha);
return;
}
if ( (i is 'SWWMLamp') && aspowerup )
{
Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,SWWMLamp(i).isBlinking()?alpha*.5:alpha,DTA_TopOffset,0,DTA_LeftOffset,0);
String nstr = String.Format("%d%%",SWWMLamp(i).Charge);
int len = mTewiFont.mFont.StringWidth(nstr);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,(xx+30)-len,(yy+30)-11,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,SWWMLamp(i).isBlinking()?alpha*.5:alpha);
return;
}
Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_Alpha,alpha,DTA_TopOffset,0,DTA_LeftOffset,0);
if ( (i.Amount > 1) || forceamt )
{
String nstr;
if ( (i.Amount > 99999) && !forceamt ) nstr = "99999";
else nstr = String.Format("%d",i.Amount);
int len = mTewiFont.mFont.StringWidth(nstr);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,(xx+30)-len,(yy+30)-11,nstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alpha);
}
}
private void DrawInventory()
{
// active items (armor / powerups)
double xx = margin+2;
double yy = ss.y-(margin+58);
if ( CPlayer.mo.InvSel && !isInventoryBarVisible() ) yy -= 34;
bool drewarmor = false;
for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv )
{
if ( (i.Amount <= 0) || (!(i is 'SWWMArmor') && !(i is 'BasicArmor')) ) continue;
DrawInvIcon(i,xx,yy,forceamt:true);
yy -= 34;
drewarmor = true;
}
yy = ss.y-(margin+58);
if ( drewarmor ) xx += 40;
else if ( CPlayer.mo.InvSel && !isInventoryBarVisible() ) yy -= 34;
for ( Inventory i=CPlayer.mo.Inv; i; i=i.Inv )
{
if ( (i is 'SWWMLamp') )
{
DrawInvIcon(i,xx,yy,aspowerup:true);
yy -= 34;
continue;
}
if ( !(i is 'Powerup') || (Powerup(i).EffectTics <= 0) || !(Powerup(i).Icon) ) continue;
DrawInvIcon(i,xx,yy);
yy -= 34;
}
// inventory box / bar
if ( !CPlayer.mo.InvSel ) return;
if ( isInventoryBarVisible() )
{
Array<Inventory> bar;
bar.Clear();
for ( Inventory i=CPlayer.mo.FirstInv(); i; i=i.NextInv() ) bar.Push(i);
int ps = bar.Find(CPlayer.mo.InvSel);
Inventory prev[2], next[2];
if ( bar.Size() > 1 )
{
if ( ps+1 >= bar.Size() ) next[0] = bar[0];
else next[0] = bar[ps+1];
if ( ps-1 < 0 ) prev[0] = bar[bar.Size()-1];
else prev[0] = bar[ps-1];
}
if ( bar.Size() > 2 )
{
if ( ps+2 >= bar.Size() ) next[1] = bar[(ps+2)-bar.Size()];
else next[1] = bar[ps+2];
if ( ps-2 < 0 ) prev[1] = bar[bar.Size()+(ps-2)];
else prev[1] = bar[ps-2];
}
xx = (ss.x-34)/2;
yy = (ss.y+64)/2;
Screen.DrawTexture(InventoryTex,false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
DrawInvIcon(CPlayer.mo.InvSel,xx+2,yy+2);
DrawInvIcon(prev[0],xx-32,yy+2,2./3.);
DrawInvIcon(prev[1],xx-66,yy+2,1./3.);
DrawInvIcon(next[0],xx+36,yy+2,2./3.);
DrawInvIcon(next[1],xx+70,yy+2,1./3.);
return;
}
Screen.DrawTexture(InventoryTex,false,margin,ss.y-(margin+60),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
DrawInvIcon(CPlayer.mo.InvSel,margin+2,ss.y-(margin+58));
}
private void DrawWeapon()
{
if ( CPlayer.ReadyWeapon is 'SWWMWeapon' ) SWWMWeapon(CPlayer.ReadyWeapon).DrawWeapon(FracTic,ss.x-margin,ss.y-(margin+28),hs,ss);
else if ( CPlayer.ReadyWeapon )
{
// generic display
double xx = ss.x-(margin+13), yy = ss.y-(margin+42);
int maxlen = 0;
String astr2, astr1;
if ( CPlayer.ReadyWeapon.Ammo2 ) astr2 = String.Format("%d",CPlayer.ReadyWeapon.Ammo2.Amount);
if ( CPlayer.ReadyWeapon.Ammo1 ) astr1 = String.Format("%d",CPlayer.ReadyWeapon.Ammo1.Amount);
maxlen = max(astr1.Length(),astr2.Length());
if ( CPlayer.ReadyWeapon.Ammo2 )
{
yy -= 14;
Screen.DrawTexture(GenericAmmoTex[2],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
for ( int i=0; i<maxlen; i++ )
{
xx -= 6;
Screen.DrawTexture(GenericAmmoTex[1],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
Screen.DrawTexture(GenericAmmoTex[0],false,xx-3,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,xx,yy,astr2,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 14;
}
if ( CPlayer.ReadyWeapon.Ammo1 )
{
Screen.DrawTexture(GenericAmmoTex[2],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
for ( int i=0; i<maxlen; i++ )
{
xx -= 6;
Screen.DrawTexture(GenericAmmoTex[1],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
Screen.DrawTexture(GenericAmmoTex[0],false,xx-3,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,xx,yy,astr1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
Screen.DrawTexture(WeaponTex,false,ss.x-(margin+60),ss.y-(margin+28),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
double xx = ss.x-(margin+57), yy = ss.y-(margin+28);
for ( int i=1; i<=10; i++ )
{
int ncolor = Font.CR_WHITE;
if ( !CPlayer.HasWeaponsInSlot(i%10) ) ncolor = Font.CR_DARKGRAY;
else if ( CPlayer.PendingWeapon && (CPlayer.PendingWeapon != WP_NOCHANGE) && (CPlayer.PendingWeapon.SlotNumber == (i%10)) ) ncolor = Font.CR_FIRE;
else if ( (!CPlayer.PendingWeapon || (CPlayer.PendingWeapon == WP_NOCHANGE)) && CPlayer.ReadyWeapon && (CPlayer.ReadyWeapon.SlotNumber == (i%10)) ) ncolor = Font.CR_FIRE;
else
{
bool hasammo = false;
for ( Inventory inv=CPlayer.mo.Inv; inv; inv=inv.Inv )
{
bool dummy;
int slot;
if ( inv is 'Weapon' ) [dummy, slot] = CPlayer.weapons.LocateWeapon(Weapon(inv).GetClass());
else continue;
if ( slot != (i%10) ) continue;
// CheckAmmo can't be called from ui, so we have to improvise
// for SWWM weapons I made a function for this at least
if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).ReportHUDAmmo() )
hasammo = true;
else if ( !(inv is 'SWWMWeapon') && ((!Weapon(inv).Ammo1 || (Weapon(inv).Ammo1.Amount > 0)) || (Weapon(inv).Ammo2 && (Weapon(inv).Ammo2.Amount > 0))) )
hasammo = true;
}
if ( !hasammo ) ncolor = Font.CR_RED;
}
Screen.DrawText(mTewiFont.mFont,ncolor,xx,yy,String.Format("%d",(i%10)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += 12;
if ( i == 5 )
{
xx = ss.x-(margin+57);
yy += 14;
}
}
}
private void DrawStatus()
{
Screen.DrawTexture(StatusTex,false,margin,ss.y-(margin+26),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
let d = Demolitionist(CPlayer.mo);
int dw = DashInter.GetValue();
double alph = .6;
if ( !d || (d.dashfuel > 20) || ((gametic%10) < 5) ) alph = 1.;
Screen.DrawTexture(DashTex,false,margin+2,ss.y-(margin+20),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,dw,DTA_Alpha,alph);
int fw = FuelInter.GetValue();
Screen.DrawTexture(FuelTex,false,margin+2,ss.y-(margin+24),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,fw);
int ht = clamp(HealthInter.GetValue(),0,10000);
int hw = min(ht,100);
if ( isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower") )
{
Screen.DrawTexture(HealthTex[4],false,margin+2,ss.y-(margin+14),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(mTewiFont.mFont,Font.CR_WHITE,margin+108,ss.y-(margin+16),String.Format("%03d",Random[HudStuff](0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
Screen.DrawTexture(HealthTex[0],false,margin+2,ss.y-(margin+14),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
if ( ht > 100 )
{
hw = min(ht-100,100);
Screen.DrawTexture(HealthTex[1],false,margin+2,ss.y-(margin+14),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
}
if ( ht > 200 )
{
hw = int(min(ht-200,300)/3.);
Screen.DrawTexture(HealthTex[2],false,margin+2,ss.y-(margin+14),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
}
if ( ht > 500 )
{
hw = int(min(ht-500,500)/5.);
Screen.DrawTexture(HealthTex[3],false,margin+2,ss.y-(margin+14),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
}
int hcolor = Font.CR_RED;
if ( ht > 500 ) hcolor = Font.CR_GOLD;
else if ( ht > 200 ) hcolor = Font.CR_PURPLE;
else if ( ht > 100 ) hcolor = Font.CR_CYAN;
Screen.DrawText(mTewiFont.mFont,hcolor,margin+108,ss.y-(margin+15),String.Format("%3d",clamp(ht,0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
private void DrawPickups()
{
if ( !pickduration ) pickduration = CVar.GetCVar('swwm_pickduration',players[consoleplayer]);
if ( !pickcol ) pickcol = CVar.GetCVar('msg0color',players[consoleplayer]);
if ( PickupQueue.Size() <= 0 ) return;
// reverse order since they're drawn bottom to top
int mend = max(0,PickupQueue.Size()-(maxpick.GetInt()));
double yy = ss.y-(margin+50);
if ( bosshnd && (bosshnd.alpha > 0.) ) yy -= int(25*clamp(bosshnd.alpha*2.,0.,1.));
for ( int i=PickupQueue.Size()-1; i>=mend; i-- )
{
String cstr = PickupQueue[i].str;
if ( PickupQueue[i].rep > 1 ) cstr.AppendFormat(" (x%d)",PickupQueue[i].rep);
int curtime = (PickupQueue[i].tic+35*pickduration.GetInt())-level.totaltime;
double alph = clamp(curtime/20.,0.,1.);
Font fnt = LangFont(mTewiFont);
BrokenLines l = fnt.BreakLines(cstr,int(ss.x*.75));
int maxlen = 0;
for ( int j=0; j<l.Count(); j++ )
{
int len = fnt.StringWidth(l.StringAt(j));
if ( len > maxlen ) maxlen = len;
}
int h = fnt.GetHeight();
double xx = (ss.x-maxlen)/2.;
Screen.Dim("Black",.8*alph,int((xx-6)*hs.x),int((yy-h*(l.Count()-1))*hs.y),int((maxlen+12)*hs.x),int((h*l.Count()+4)*hs.y));
for ( int j=l.Count()-1; j>=0; j-- )
{
int len = fnt.StringWidth(l.StringAt(j));
xx = (ss.x-len)/2.;
Screen.DrawText(fnt,pickcol.GetInt(),int(xx),yy+2,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
yy -= h;
}
yy -= 6;
}
}
private void DrawMessages()
{
if ( !chatduration ) chatduration = CVar.GetCVar('swwm_chatduration',players[consoleplayer]);
if ( !msgduration ) msgduration = CVar.GetCVar('swwm_msgduration',players[consoleplayer]);
if ( !obitcol ) obitcol = CVar.GetCVar('msg1color',players[consoleplayer]);
if ( !critcol ) critcol = CVar.GetCVar('msg2color',players[consoleplayer]);
if ( !chatcol ) chatcol = CVar.GetCVar('msg3color',players[consoleplayer]);
if ( !teamcol ) teamcol = CVar.GetCVar('msg4color',players[consoleplayer]);
if ( MainQueue.Size() <= 0 ) return;
int mstart = max(0,MainQueue.Size()-(maxchat[chatopen>=gametic].GetInt()));
double xx = margin, yy = margin;
bool smol = (ss.x<640);
Screen.DrawTexture(ChatTex[smol?3:0],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy++;
for ( int i=mstart; i<MainQueue.Size(); i++ )
{
int col = critcol.GetInt();
if ( MainQueue[i].type == PRINT_MEDIUM ) col = obitcol.GetInt();
else if ( MainQueue[i].type == PRINT_CHAT ) col = chatcol.GetInt();
else if ( MainQueue[i].type == PRINT_TEAMCHAT ) col = teamcol.GetInt();
String cstr = MainQueue[i].str;
if ( MainQueue[i].rep > 1 ) cstr.AppendFormat(" (x%d)",MainQueue[i].rep);
int curtime = MainQueue[i].tic-level.totaltime;
if ( MainQueue[i].type < PRINT_CHAT ) curtime += 35*msgduration.GetInt();
else curtime += 35*chatduration.GetInt();
double alph = clamp(curtime/20.,0.,1.);
Font fnt = LangFont(mTewiFont);
BrokenLines l = fnt.BreakLines(cstr,smol?211:361);
for ( int j=0; j<l.Count(); j++ )
{
Screen.DrawTexture(ChatTex[smol?4:1],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,col,xx+4,yy,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
yy += 13;
}
}
Screen.DrawTexture(ChatTex[smol?5:2],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
override bool DrawChat( String txt )
{
// ignore during intermission
if ( gamestate != GS_LEVEL ) return false;
chatopen = gametic+1; // have to add 1 because DrawChat is called after everything else
double xx = 2;
double yy = ss.y-14;
Screen.Dim("Black",.8,0,Screen.GetHeight()-int(15*hs.y),Screen.GetWidth(),int(15*hs.y));
String pname = players[consoleplayer].GetUserName();
// strip colors
SWWMUtility.StripColor(pname);
String fullstr = String.Format("\cq%s\cd@\cqdemolitionist%d\cn ~ \c-wall %s_",pname,consoleplayer+1,txt);
Font fnt = LangFont(mTewiFont);
// cut out to fit
int w = fnt.StringWidth(fullstr);
if ( w > ss.x-4 )
{
// draw trailing dots
Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,"...",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// shift back
xx -= w-(ss.x-4);
// draw trimmed
Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,fullstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ClipLeft,int(26*hs.x));
}
else Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,fullstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return true;
}
override void DrawPowerups()
{
// don't do anything
}
private void DrawDeath()
{
// death prompt
let demo = Demolitionist(CPlayer.mo);
if ( !demo || (CPlayer.Health > 0) ) return;
String str;
double alph;
int len;
double xx, yy;
Font fnt;
if ( demo.player.viewheight <= 6 )
{
Screen.Dim("Black",min(demo.deadtimer/80.,1.),0,0,Screen.GetWidth(),Screen.GetHeight());
if ( demo.revivefail > level.maptime )
{
Screen.Dim("Red",clamp((demo.revivefail-level.maptime)/60.,0.,.2),0,0,Screen.GetWidth(),Screen.GetHeight());
str = StringTable.Localize("$SWWM_REFAIL");
fnt = LangFont(mTewiFont);
len = fnt.StringWidth(str);
xx = (ss.x-len)/2.;
yy = ss.y-48;
if ( ((demo.revivefail-level.maptime)%16) < 8 )
Screen.DrawText(fnt,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
alph = clamp((demo.deadtimer-60)/60.,0.,1.);
str = String.Format(StringTable.Localize("$SWWM_URDED"),CPlayer.GetUserName());
fnt = LangFont(mTewiFont);
len = fnt.StringWidth(str);
xx = (ss.x-len)/2.;
yy = (ss.y-fnt.GetHeight()*4)/2.;
Screen.DrawText(fnt,Font.CR_RED,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
if ( multiplayer || level.AllowRespawn || sv_singleplayerrespawn || G_SkillPropertyInt(SKILLP_PlayerRespawn) )
{
alph = clamp((demo.deadtimer-90)/60.,0.,1.);
str = String.Format(StringTable.Localize("$SWWM_URDEDMP"),CPlayer.GetUserName());
fnt = LangFont(mTewiFont);
len = fnt.StringWidth(str);
xx = (ss.x-len)/2.;
yy = ss.y/2.;
Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
return;
}
alph = clamp((demo.deadtimer-140)/60.,0.,1.);
str = String.Format(StringTable.Localize("$SWWM_URDED2"),CPlayer.GetUserName());
fnt = LangFont(mTewiFont);
len = fnt.StringWidth(str);
xx = (ss.x-len)/2.;
yy = ss.y/2.;
Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
if ( !swwm_revive )
return;
alph = clamp((demo.deadtimer-160)/60.,0.,1.);
str = String.Format(StringTable.Localize("$SWWM_URDED3"),CPlayer.GetUserName());
fnt = LangFont(mTewiFont);
len = fnt.StringWidth(str);
xx = (ss.x-len)/2.;
yy = (ss.y+fnt.GetHeight()*2)/2.;
Screen.DrawText(fnt,Font.CR_WHITE,xx,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
}
}
override void Draw( int state, double TicFrac )
{
Super.Draw(state,TicFrac);
if ( (state != HUD_StatusBar) && (state != HUD_Fullscreen) ) return;
if ( !safezone ) safezone = CVar.GetCVar('swwm_hudmargin',players[consoleplayer]);
if ( !maxchat[0] ) maxchat[0] = CVar.GetCVar('swwm_maxshown',players[consoleplayer]);
if ( !maxchat[1] ) maxchat[1] = CVar.GetCVar('swwm_maxshownbig',players[consoleplayer]);
if ( !maxpick ) maxpick = CVar.GetCVar('swwm_maxpickup',players[consoleplayer]);
if ( !hscale ) hscale = CVar.GetCVar('swwm_hudscale',players[consoleplayer]);
if ( !bscale ) bscale = CVar.GetCVar('swwm_barscale',players[consoleplayer]);
if ( !nscale ) nscale = CVar.GetCVar('swwm_numscale',players[consoleplayer]);
if ( !sscale ) sscale = CVar.GetCVar('swwm_scrscale',players[consoleplayer]);
BeginHUD();
if ( hscale.GetInt() <= 0 ) hs = GetHUDScale();
else
{
hs.x = hscale.GetInt();
if ( Screen.GetWidth()/hs.x < 640. ) hs.x = max(1,floor(Screen.GetWidth()/640.));
hs.y = hs.x;
}
ss = (Screen.GetWidth()/hs.x,Screen.GetHeight()/hs.y);
margin = clamp(safezone.GetInt(),0,(ss.x<640)?10:40);
if ( bscale.GetInt() <= 0 ) hsb = GetHUDScale();
else
{
hsb.x = bscale.GetInt();
hsb.y = hsb.x;
}
ssb = (Screen.GetWidth()/hsb.x,Screen.GetHeight()/hsb.y);
if ( nscale.GetInt() <= 0 ) hsn = GetHUDScale();
else
{
hsn.x = nscale.GetInt();
hsn.y = hsn.x;
}
ssn = (Screen.GetWidth()/hsn.x,Screen.GetHeight()/hsn.y);
if ( sscale.GetInt() <= 0 ) hss = GetHUDScale();
else
{
hss.x = sscale.GetInt();
hss.y = hss.x;
}
sss = (Screen.GetWidth()/hss.x,Screen.GetHeight()/hss.y);
FracTic = TicFrac;
DrawTarget();
DrawScore();
DrawInventory();
DrawStatus();
DrawWeapon();
if ( !bosshnd ) bosshnd = SWWMVanillaBossHandler(EventHandler.Find("SWWMVanillaBossHandler"));
else bosshnd.DrawBossBar(self);
DrawPickups();
DrawDeath();
DrawMessages();
}
}