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.
1140 lines
45 KiB
Text
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();
|
|
}
|
|
}
|