swwmgz_m/zscript/swwm_hud.zsc

376 lines
15 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, InventoryTex, ChatTex[6],
HealthTex[4], FuelTex, DashTex;
HUDFont mTewiFont;
// "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;
Actor targetactors[40], scoreactors[60], keyactors[10];
Vector3 exitpoints[10], uselines[10];
// client cvars
transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol;
// shared stuff
Vector2 ss, hs;
int margin;
double FracTic;
int chatopen;
DynamicValueInterpolator HealthInter, ScoreInter, FuelInter, DashInter;
override void FlushNotify()
{
// 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 ProcessNotify( EPrintLevel printlevel, String outline )
{
let m = new("MsgLine");
m.str = outline.Left(outline.Length()-1); // strip newline
m.type = printlevel;
m.tic = gametic;
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;
}
override void Tick()
{
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]);
Super.Tick();
// prune old messages
for ( int i=0; i<PickupQueue.Size(); i++ )
{
if ( gametic < (PickupQueue[i].tic+pickduration.GetInt()) ) continue;
PickupQueue.Delete(i);
i--;
}
for ( int i=0; i<MainQueue.Size(); i++ )
{
if ( (MainQueue[i].type <= PRINT_HIGH) && (gametic < (MainQueue[i].tic+msgduration.GetInt())) ) continue;
else if ( (MainQueue[i].type > PRINT_HIGH) && (gametic < (MainQueue[i].tic+chatduration.GetInt())) ) continue;
MainQueue.Delete(i);
i--;
}
// update target actors
// update floating scores
// update interpolators
HealthInter.Update(CPlayer.health);
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd ) ScoreInter.Update(hnd.creditsbridge[CPlayer.mo.PlayerNumber()]);
else ScoreInter.Update(0);
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);
}
}
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);
ScoreTex = TexMan.CheckForTexture("graphics/HUD/ScoreBox.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);
mTewiFont = HUDFont.Create("TewiShaded");
HealthInter = DynamicValueInterpolator.Create(100,.1,1,100);
ScoreInter = DynamicValueInterpolator.Create(0,.1,1,1000);
FuelInter = DynamicValueInterpolator.Create(120,.5,1,100);
DashInter = DynamicValueInterpolator.Create(120,.5,1,40);
}
private void DrawTarget()
{
// omnisight: usable highlights
// omnisight: key locations
// targetting array
// floating kill scores
}
private void DrawScore()
{
Screen.DrawTexture(ScoreTex,false,ss.x-(margin+73),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);
}
private void DrawInventory()
{
// active items (armor / powerups)
// inventory box / bar
}
private void DrawWeapon()
{
if ( CPlayer.ReadyWeapon is 'SWWMWeapon' ) SWWMWeapon(CPlayer.ReadyWeapon).DrawWeapon(FracTic,ss.x-margin,ss.y-(margin+28),hs,ss);
else
{
// TODO generic display
}
Screen.DrawTexture(WeaponTex,false,ss.x-(margin+61),ss.y-(margin+29),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
double xx = ss.x-(margin+58), yy = ss.y-(margin+29);
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());
if ( (slot == (i%10)) && (!Weapon(inv).Ammo1 || (Weapon(inv).Ammo1.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+58);
yy += 14;
}
}
}
private void DrawStatus()
{
Screen.DrawTexture(StatusTex,false,margin,ss.y-(margin+27),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+21),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+25),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);
Screen.DrawTexture(HealthTex[0],false,margin+2,ss.y-(margin+15),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+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
}
if ( ht > 200 )
{
hw = int(min(ht-100,400)*0.25);
Screen.DrawTexture(HealthTex[2],false,margin+2,ss.y-(margin+15),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,hw);
}
if ( ht > 500 )
{
hw = int(min(ht-500,500)*0.2);
Screen.DrawTexture(HealthTex[2],false,margin+2,ss.y-(margin+15),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+16),String.Format("%3d",clamp(ht,0,999)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
private void DrawMessages()
{
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 ( !pickcol ) pickcol = CVar.GetCVar('msg0color',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]);
// common message area
if ( MainQueue.Size() > 0 )
{
int mstart = max(0,MainQueue.Size()-(1+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-gametic;
if ( MainQueue[i].type < PRINT_CHAT ) curtime += msgduration.GetInt();
else curtime += chatduration.GetInt();
double alph = clamp(curtime/20.,0.,1.);
BrokenLines l = mTewiFont.mFont.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(mTewiFont.mFont,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);
}
// pickup messages
if ( PickupQueue.Size() > 0 )
{
// reverse order since they're drawn bottom to top
int mend = max(0,PickupQueue.Size()-(1+maxpick.GetInt()));
double yy = ss.y-(margin+50);
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+pickduration.GetInt())-gametic;
double alph = clamp(curtime/20.,0.,1.);
BrokenLines l = mTewiFont.mFont.BreakLines(cstr,int(ss.x*.75));
int maxlen = 0;
for ( int j=0; j<l.Count(); j++ )
{
int len = mTewiFont.mFont.StringWidth(l.StringAt(j));
if ( len > maxlen ) maxlen = len;
}
int h = mTewiFont.mFont.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 = mTewiFont.mFont.StringWidth(l.StringAt(j));
xx = (ss.x-len)/2.;
Screen.DrawText(mTewiFont.mFont,pickcol.GetInt(),xx,yy+2,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
yy -= h;
}
yy -= 6;
}
}
}
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
for ( int i=0; i<pname.CodePointCount(); i++ )
{
int remlen = 0;
if ( pname.GetNextCodePoint(i) != 0x1C )
continue;
remlen++;
if ( pname.GetNextCodePoint(i+remlen) == 0x5B )
while ( pname.GetNextCodePoint(i+remlen) != 0x5D )
remlen++;
remlen++;
pname.Remove(i,remlen);
}
String fullstr = String.Format("\cq%s\cd@\cqdemolitionist%d\cn ~ \c-wall %s%s",pname,consoleplayer+1,txt,mTewiFont.mFont.GetCursor());
// cut out to fit
int w = mTewiFont.mFont.StringWidth(fullstr);
if ( w > ss.x-4 )
{
// draw trailing dots
Screen.DrawText(mTewiFont.mFont,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(mTewiFont.mFont,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(mTewiFont.mFont,Font.CR_WHITE,xx,yy,fullstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return true;
}
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]);
BeginHUD();
hs = GetHUDScale();
ss = (Screen.GetWidth()/hs.x,Screen.GetHeight()/hs.y);
margin = clamp(safezone.GetInt(),0,(ss.x<640)?10:40);
FracTic = TicFrac;
DrawTarget();
DrawScore();
DrawInventory();
DrawStatus();
DrawWeapon();
DrawMessages();
}
}