stinger_m/zscript/unrealhud.zsc
Marisa Kirisame 2d64db512f Various Sentry fixups and whatnot, plus a fancy explosion.
Sentry no longer causes a freeze if it runs out of ammo while still having a target.
Sentry now has to be recalled by using the item.
Hijacking a sentry now immediately assigns it to your inventory.
You can hijack rogue sentries (not owned by anyone).
Better placement check for forcefield and sentry spawning, no more "no room" messages when it can definitely fit.
Increased flak slug explosion sprite size, as it felt too small.
Eightball loads faster (slightly faster than vanilla but can't do anything about that).
ASMD combo no longer spawns amped explosions when it shouldn't.
Added a new flag to UnrealInventory, bDRAWSPECIAL, so the HUD displays the special1 as an amount. This is useful for items like the Sentry that use this variable to count ammo.
Increased rotation range for the Sentry so it's harder for enemies to sneak behind it from the sides.
Added debris to the explosions of flares and voice boxes.
2019-09-06 14:08:53 +02:00

849 lines
30 KiB
Text

// An almost 1:1 recreation of the Unreal 1 HUD
Class UnrealHUD : BaseStatusBar
{
double FracTic;
// for easy access to ammo slots
UnrealMainHandler hnd;
// Unreal HUD variables
Color RedColor, GreenColor, BlackColor;
int HudMode;
// Helpers
Vector2 scalev;
double CurX, CurY, ClipX, ClipY;
Color DrawColor;
ViewTracer vtracer;
Actor lastseen;
int lastseentic, Count;
// Fonts
Font LargeFont, LargeRedFont, UBigFont, MedFont, WhiteFont, TinyFont, TinyWhiteFont, TinyRedFont;
HUDFont mMapFont;
// Common Textures
TextureID HalfHud, HudLine, HudAmmo, IconHeal, IconSkul, IconSel, IconBase, KeyIcons[7];
// messaging stuff
String PickupMsg;
int PickupMsgTic;
String ShortMsg[4];
int ShortMsgTic[4];
int ShortMsgCol[4];
String MidPrintStr;
int MidPrintTic;
bool MidPrintLarge;
// 0.83 HUD stuff
String OldAmmo[19];
Class<Inventory> OldAmmoType[19];
String OldArmor[6];
Class<Inventory> OldArmorType[6];
String OldKeys[7];
HUDFont mOldDigits;
Font OldLargeFont, OldSmallFont;
// Translations
int RedIcon;
// Translator menu interaction
transient bool bTranslatorActive;
override void Init()
{
Super.Init();
SetSize(64,640,400);
// Initialize
Count = 0;
vtracer = new("ViewTracer");
RedColor = "FF 00 00";
GreenColor = "00 FF 00";
BlackColor = "00 00 00";
DrawColor = "FF FF FF";
LargeFont = Font.GetFont('ULargeFont');
LargeRedFont = Font.GetFont('ULargeRedFont');
UBigFont = Font.GetFont('UBigFont');
MedFont = Font.GetFont('UMedFont');
WhiteFont = Font.GetFont('UWhiteFont');
TinyFont = Font.GetFont('UTinyFont');
TinyWhiteFont = Font.GetFont('UTinyWhiteFont');
TinyRedFont = Font.GetFont('UTinyRedFont');
HalfHud = TexMan.CheckForTexture("HalfHud",TexMan.Type_Any);
HudLine = TexMan.CheckForTexture("HudLine",TexMan.Type_Any);
HudAmmo = TexMan.CheckForTexture("HudAmmo",TexMan.Type_Any);
IconHeal = TexMan.CheckForTexture("IconHeal",TexMan.Type_Any);
IconSkul = TexMan.CheckForTexture("IconSkul",TexMan.Type_Any);
IconSel = TexMan.CheckForTexture("IconSel",TexMan.Type_Any);
IconBase = TexMan.CheckForTexture("IconBase",TexMan.Type_Any);
RedIcon = Translation.GetID('RedIcon');
KeyIcons[0] = TexMan.CheckForTexture("I_KeyR",TexMan.Type_Any);
KeyIcons[1] = TexMan.CheckForTexture("I_KeyB",TexMan.Type_Any);
KeyIcons[2] = TexMan.CheckForTexture("I_KeyY",TexMan.Type_Any);
KeyIcons[3] = TexMan.CheckForTexture("I_SkullR",TexMan.Type_Any);
KeyIcons[4] = TexMan.CheckForTexture("I_SkullB",TexMan.Type_Any);
KeyIcons[5] = TexMan.CheckForTexture("I_SkullY",TexMan.Type_Any);
KeyIcons[6] = TexMan.CheckForTexture("I_KeyG",TexMan.Type_Any);
mOldDigits = HUDFont.Create(Font.GetFont('U083Digits'),26,Mono_CellLeft);
mMapFont = HUDFont.Create(WhiteFont);
OldLargeFont = Font.GetFont('UOldLargeFont');
OldSmallFont = Font.GetFont('UOldSmallFont');
OldAmmo[0] = "Disp083";
OldAmmo[1] = "Clip083";
OldAmmo[2] = "Tary083";
OldAmmo[3] = "Asmd083";
OldAmmo[4] = "Rokt083";
OldAmmo[5] = "Flak083";
OldAmmo[6] = "Razor083";
OldAmmo[7] = "Bio083";
OldAmmo[8] = "Rifle083";
OldAmmo[9] = "Mini083";
OldAmmo[10] = "Shell083";
OldAmmo[11] = "Impal083";
OldAmmo[12] = "Flame083";
OldAmmo[13] = "Tele083";
OldAmmo[14] = "Stun083";
OldAmmo[15] = "Big083";
OldAmmo[16] = "Smini083";
OldAmmo[17] = "Peace083";
OldAmmo[18] = "OLSMP083";
OldAmmoType[0] = "DefaultAmmo";
OldAmmoType[1] = "UMiniAmmo";
OldAmmoType[2] = "StingerAmmo";
OldAmmoType[3] = "AsmdAmmo";
OldAmmoType[4] = "URocketAmmo";
OldAmmoType[5] = "UFlakBox";
OldAmmoType[6] = "RazorAmmo";
OldAmmoType[7] = "UBioAmmo";
OldAmmoType[8] = "URifleAmmo";
OldAmmoType[9] = "UMinigun";
OldAmmoType[10] = "UShells";
OldAmmoType[11] = "ImpalerAmmo";
OldAmmoType[12] = "FlameAmmo";
OldAmmoType[13] = "UTranslocatorAmmo";
OldAmmoType[14] = "StunnerAmmo";
OldAmmoType[15] = "BigAmmo";
OldAmmoType[16] = "SMiniAmmo";
OldAmmoType[17] = "PeaceAmmo";
OldAmmoType[18] = "OLSMPAmmo";
OldArmor[0] = "Armor083";
OldArmor[1] = "Kev083";
OldArmor[2] = "Asb083";
OldArmor[3] = "Tox083";
OldArmor[4] = "Belt083";
OldArmor[5] = "Pbelt083";
OldArmorType[0] = "UArmor";
OldArmorType[1] = "KevlarSuit";
OldArmorType[2] = "AsbestosSuit";
OldArmorType[3] = "ToxinSuit";
OldArmorType[4] = "ShieldBelt";
OldArmorType[5] = "PowerShield";
OldKeys[0] = "Redk083";
OldKeys[1] = "Bluek083";
OldKeys[2] = "Goldk083";
OldKeys[3] = "Rskul083";
OldKeys[4] = "Bskul083";
OldKeys[5] = "Gskul083";
OldKeys[6] = "Grenk083";
}
override void Draw( int state, double TicFrac )
{
Super.Draw(state,TicFrac);
FracTic = TicFrac;
HudMode = CVar.GetCVar('stinger_hudmode',players[consoleplayer]).GetInt();
scalev.x = scalev.y = CVar.GetCVar('hud_scale',players[consoleplayer]).GetInt();
if ( scalev.x < 0 ) scalev.x = scalev.y = max(1,min(Screen.GetWidth()/640.,Screen.GetHeight()/480.)); // the typical behavior is scaling to 640x400 but we're expecting 4:3 here
else if ( scalev.x == 0 )
{
scalev.x = CleanXFac_1;
scalev.y = CleanYFac_1;
}
ClipX = Screen.GetWidth()/scalev.x;
ClipY = Screen.GetHeight()/scalev.y;
CurX = 0;
CurY = 0;
double lbottom = Screen.GetHeight()-32*scalev.y;
for ( Inventory i=CPlayer.mo.inv; i; i=i.inv )
if ( i is 'UnrealInventory' )
UnrealInventory(i).PreRender(lbottom);
if ( CPlayer.ReadyWeapon is 'UTWeapon' )
UTWeapon(CPlayer.ReadyWeapon).PreRender(lbottom);
if ( state == HUD_Fullscreen )
{
BeginHUD();
DrawUnrealHUD();
}
else if ( state == HUD_StatusBar )
{
BeginStatusBar();
DrawUnrealBar();
}
for ( Inventory i=CPlayer.mo.inv; i; i=i.inv )
if ( i is 'UnrealInventory' )
UnrealInventory(i).PostRender(lbottom);
if ( CPlayer.ReadyWeapon is 'UTWeapon' )
UTWeapon(CPlayer.ReadyWeapon).PostRender(lbottom);
DrawIdentifyInfo(state);
DrawMessages(state);
}
private void DrawNumberOf( Inventory i, double x, double y )
{
if ( (i.Amount <= 1) && !((i is 'UnrealInventory') || !UnrealInventory(i).bDRAWSPECIAL) ) return;
double TempX = CurX, TempY = CurY;
string itxt = String.Format("%d",((i is 'UnrealInventory')&&UnrealInventory(i).bDRAWSPECIAL)?i.special1:i.Amount);
CurX += 30;
CurY += 23;
CurX -= TinyRedFont.StringWidth(itxt);
Screen.DrawText(TinyRedFont,Font.CR_UNTRANSLATED,CurX,CurY,itxt,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurX = TempX;
CurY = TempY;
}
private void DrawIconValue( int n, bool bIsArmor = false )
{
if ( !HudMode || (HudMode == 3) ) return;
double TempX = CurX, TempY = CurY;
string itxt = String.Format("%d",n);
CurX -= 2;
CurY -= 6;
CurX -= TinyFont.StringWidth(itxt);
Screen.DrawText(TinyFont,Font.CR_UNTRANSLATED,CurX,CurY,itxt,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurX = TempX;
CurY = TempY;
}
private void DrawHudIcon( double x, double y, Inventory i, bool bRed )
{
if ( i.Icon.IsNull() ) return;
double width = CurX;
CurX = x;
CurY = y;
// scale to fit
Vector2 scl = TexMan.GetScaledSize(i.Icon);
double mscl = 32./max(scl.x,scl.y);
double dw = (ClipX/mscl), dh = (ClipY/mscl);
double dx = CurX/mscl, dy = CurY/mscl;
if ( bRed )
{
Screen.DrawTexture(IconBase,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_TranslationIndex,RedIcon);
Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_TranslationIndex,RedIcon,DTA_TopOffset,0,DTA_LeftOffset,0);
}
else
{
Screen.DrawTexture(IconBase,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
Screen.DrawTexture(i.Icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_TopOffset,0,DTA_LeftOffset,0);
}
if ( (i is 'UnrealInventory') && (UnrealInventory(i).DefaultCharge > 0) && (UnrealInventory(i).bActive || (UnrealInventory(i).Charge < UnrealInventory(i).DefaultCharge)) )
Screen.DrawTexture(HudLine,false,CurX+2,CurY+29,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_WindowRightF,Min(28.*(UnrealInventory(i).Charge/double(UnrealInventory(i).DefaultCharge)),28.));
else if ( (i is 'UTArmor') && ((HudMode == 0) || (HudMode == 3)) )
Screen.DrawTexture(HudLine,false,CurX+2,CurY+29,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_WindowRightF,Min(28.*(i.Amount/double(i.MaxAmount)),28.));
}
private void DrawFragCount( double x, double y )
{
CurX = X;
CurY = Y;
Screen.DrawTexture(IconSkul,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurX += 30;
CurY += 23;
string score = String.Format("%d",(deathmatch||teamplay)?CPlayer.fragcount:CPlayer.killcount);
CurX -= TinyWhiteFont.StringWidth(score);
Screen.DrawText(TinyWhiteFont,Font.CR_UNTRANSLATED,CurX,CurY,score,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
}
private void DrawInventory( double x, double y, bool bDrawOne = false, bool bNoWeapons = false )
{
bool bGotNext, bGotPrev, bGotSelected, bRed, bFlashTranslator;
Inventory Inv, Prev, Next, SelectedItem;
UTranslator translator;
double HalfHUDX, HalfHUDY;
double AmmoBarSize;
if ( (HudMode < 4) && !bNoWeapons ) // then draw HalfHUD
{
HalfHUDX = ClipX-64;
HalfHUDY = ClipY-32;
Screen.DrawTexture(HalfHUD,false,HalfHUDX,HalfHUDY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
}
if ( !CPlayer.mo.Inv ) return; // can this even happen?
bFlashTranslator = false;
bGotSelected = false;
bGotNext = false;
bGotPrev = false;
Prev = null;
Next = null;
SelectedItem = CPlayer.mo.InvSel;
for ( Inv=CPlayer.mo.Inv; Inv; Inv=Inv.Inv )
{
if ( bDrawOne ) break;
// if drawing more than one inventory, find next and previous items
if ( Inv == SelectedItem ) bGotSelected = true;
else if ( Inv.bINVBAR )
{
if ( bGotSelected )
{
if ( !bGotNext )
{
Next = Inv;
bGotNext = true;
}
else if ( !bGotPrev ) Prev = Inv;
}
else
{
if ( !Next ) Next = Prev;
Prev = Inv;
bGotPrev = true;
}
}
if ( Inv is 'UTranslator' ) translator = UTranslator(Inv);
}
// drawing weapon slots differently than Unreal 1 because we have better methods here
let cw = CPlayer.ReadyWeapon;
int cwslot = -1;
if ( cw && (cw.SlotNumber != -1) ) cwslot = cw.SlotNumber;
let pw = CPlayer.PendingWeapon;
int pwslot = -1;
if ( pw && (pw != WP_NOCHANGE) && (pw.SlotNumber != -1) ) pwslot = pw.SlotNumber;
Array<Ammo> aslots[10];
// clear the arrays before work
for ( int i=0; i<10; i++ ) aslots[i].Clear();
// we need the ammo slots array, if it isn't accessible then we're fucked
if ( !hnd ) hnd = UnrealMainHandler(EventHandler.Find("UnrealMainHandler"));
if ( hnd )
{
// populate the array
for ( int i=0; i<hnd.AmmoSlots.Size(); i++ )
{
let a = Ammo(CPlayer.mo.FindInventory(hnd.AmmoSlots[i].AmmoType));
if ( !a ) continue;
for ( int j=0; j<10; j++ )
{
if ( !hnd.AmmoSlots[i].UsedInSlot[j] ) continue;
aslots[j].Push(a);
}
}
}
// draw the slots
if ( (HudMode < 4) && !bNoWeapons )
{
for ( int i=0; i<10; i++ )
{
Font cfont = TinyFont;
if ( cwslot == i ) cfont = TinyWhiteFont;
int realslot = i?i:10;
CurX = HalfHUDX-3+realslot*6;
CurY = HalfHUDY+4;
if ( CPlayer.HasWeaponsInSlot(i) )
Screen.DrawText(cfont,Font.CR_UNTRANSLATED,CurX,CurY,String.Format("%d",i),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
// Draw ammo bar(s)
int nammo = 0;
for ( int j=0; j<aslots[i].Size(); j++ ) if ( aslots[i][j].Amount ) nammo++;
if ( nammo <= 0 ) continue;
int cbar = 0;
for ( int j=0; j<aslots[i].Size(); j++ )
{
let amo = aslots[i][j];
AmmoBarSize = 16*min(1.0,amo.Amount/double(amo.MaxAmount));
CurX = HalfHUDX-3+realslot*6+(4./nammo)*cbar;
CurY = HalfHUDY+29-AmmoBarSize;
if ( (AmmoBarSize < 8) && (amo.Amount < 10) && (amo.Amount > 0) && (nammo == 1) )
{
CurY -= 9;
Screen.DrawText(TinyRedFont,Font.CR_UNTRANSLATED,CurX,CurY,String.Format("%d",amo.Amount),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurY += 9;
}
DrawColor = Color(0,255,0);
if ( AmmoBarSize < 8 ) DrawColor = Color(255-int(AmmoBarSize)*30,int(AmmoBarSize)*30+40,0);
if ( !cw || (cw.Ammo1 != amo) ) DrawColor = Color(DrawColor.r/2,DrawColor.g/2,DrawColor.b/2);
if ( amo.Amount > 0 )
{
Screen.DrawTexture(HudAmmo,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_FillColor,BlackColor,DTA_DestWidthF,4./nammo,DTA_DestHeightF,AmmoBarSize);
Screen.DrawTexture(HudAmmo,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_AlphaChannel,true,DTA_FillColor,DrawColor,DTA_DestWidthF,4./nammo,DTA_DestHeightF,AmmoBarSize);
}
cbar++;
}
}
}
// flash translator icon
if ( translator ) bFlashTranslator = (translator.bNewMessage || translator.bNotNewMessage);
// draw the inventory bar
if ( (HUDMode == 5) || !SelectedItem ) return;
Count++;
if ( Count > 20 ) Count = 0;
if ( Prev && !Next && !bDrawOne ) x += 32; // this was missing from the original, causing a gap when there's only two items in the inventory
if ( Prev )
{
bRed = ((Prev is 'UnrealInventory') && UnrealInventory(Prev).bActive) || (Prev is 'Powerup') || ((Prev is 'UTranslator') && ((bTranslatorActive) || (bFlashTranslator && ((gametic%8)<4))));
DrawHudIcon(x,y,Prev,bRed);
if ( (Prev.MaxAmount > 1) || ((Prev is 'UnrealInventory') && UnrealInventory(Prev).bDRAWSPECIAL) ) DrawNumberOf(Prev,x,y);
}
bRed = ((SelectedItem is 'UnrealInventory') && UnrealInventory(SelectedItem).bActive) || (SelectedItem is 'Powerup') || ((SelectedItem is 'UTranslator') && ((bTranslatorActive) || (bFlashTranslator && ((gametic%8)<4))));
if ( !Next && !Prev && !bDrawOne ) DrawHudIcon(x+64,y,SelectedItem,bRed);
else DrawHudIcon(x+32,y,SelectedItem,bRed);
CurX = x+32;
if ( !Next && !Prev && !bDrawOne ) CurX = x+64;
CurY = y;
Screen.DrawTexture(IconSel,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_Alpha,0.5);
if ( (SelectedItem.MaxAmount > 1) || ((SelectedItem is 'UnrealInventory') && UnrealInventory(SelectedItem).bDRAWSPECIAL) ) DrawNumberOf(SelectedItem,CurX,y);
if ( Next )
{
bRed = ((Next is 'UnrealInventory') && UnrealInventory(Next).bActive) || (Next is 'Powerup') || ((Next is 'UTranslator') && ((bTranslatorActive) || (bFlashTranslator && ((gametic%8)<4))));
DrawHudIcon(x+64,y,Next,bRed);
if ( (Next.MaxAmount > 1) || ((Next is 'UnrealInventory') && UnrealInventory(Next).bDRAWSPECIAL) ) DrawNumberOf(Next,x+64,y);
}
}
private bool DrawArmor( double x, double y, bool bDrawOne = false, bool bCheckOnly = false )
{
bool hasdrawn = false;
int ArmorAmount = 0, CurAbs = 0;
Inventory Inv, BestArmor;
double XL, YL;
CurX = x;
CurY = y;
for ( Inv=CPlayer.mo.Inv; Inv; Inv=Inv.Inv )
{
if ( !(Inv is 'UTArmor') ) continue;
ArmorAmount += Inv.Amount;
if ( (Inv.Amount <= 0) || Inv.Icon.IsNull() ) continue;
if ( !bDrawOne )
{
hasdrawn = true;
if ( !bCheckOnly ) DrawHudIcon(CurX,y,Inv,false);
CurX += 32;
CurY += HudMode?29:27;
if ( !bCheckOnly ) DrawIconValue(Inv.Amount);
CurY -= HudMode?29:27;
}
else if ( UTArmor(Inv).absorb > CurAbs )
{
CurAbs = UTArmor(Inv).absorb;
BestArmor = Inv;
}
}
if ( bDrawOne && BestArmor )
{
hasdrawn = true;
if ( !bCheckOnly ) DrawHudIcon(CurX,Y,BestArmor,false);
CurX += 32;
CurY += HudMode?29:27;
if ( !bCheckOnly ) DrawIconValue(BestArmor.Amount);
CurY -= HudMode?29:27;
}
if ( (ArmorAmount > 0) && !HudMode )
{
hasdrawn = true;
if ( !bCheckOnly ) Screen.DrawText(LargeFont,Font.CR_UNTRANSLATED,CurX+2,Y,String.Format("%d",ArmorAmount),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
}
return hasdrawn;
}
private void DrawAmmo( double x, double y )
{
CurX = x;
CurY = y;
if ( !CPlayer.ReadyWeapon || !CPlayer.ReadyWeapon.Ammo1 ) return;
Font cfont = LargeFont;
if ( CPlayer.ReadyWeapon.Ammo1.Amount <= min(9,CPlayer.ReadyWeapon.Ammo1.MaxAmount/3) ) cfont = LargeRedFont;
if ( !HudMode )
{
CurX -= cfont.StringWidth(String.Format("%d",CPlayer.ReadyWeapon.Ammo1.Amount))+2;
Screen.DrawText(cfont,Font.CR_UNTRANSLATED,CurX,CurY,String.Format("%d",CPlayer.ReadyWeapon.Ammo1.Amount),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
}
CurX = x;
Screen.DrawTexture(IconBase,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
TextureID icon = CPlayer.ReadyWeapon.Icon.IsNull()?CPlayer.ReadyWeapon.Ammo1.Icon:CPlayer.ReadyWeapon.Icon;
// scale to fit
Vector2 scl = TexMan.GetScaledSize(icon);
double mscl = 32./max(scl.x,scl.y);
double dw = (ClipX/mscl), dh = (ClipY/mscl);
double dx = CurX/mscl, dy = CurY/mscl;
Screen.DrawTexture(icon,false,dx,dy,DTA_VirtualWidthF,dw,DTA_VirtualHeightF,dh,DTA_KeepRatio,true,DTA_TopOffset,0,DTA_LeftOffset,0);
CurX += 32;
CurY += 29;
DrawIconValue(CPlayer.ReadyWeapon.Ammo1.Amount);
CurX = X+2;
CurY = Y+29;
if ( (HudMode != 1) && (HudMode != 2) && (HudMode != 4) )
Screen.DrawTexture(HudLine,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_WindowRightF,Min(28.*(CPlayer.ReadyWeapon.Ammo1.Amount/double(CPlayer.ReadyWeapon.Ammo1.MaxAmount)),28.));
// TODO secondary ammo display
}
private void DrawHealth( double x, double y )
{
CurX = X;
CurY = Y;
Font cfont = LargeFont;
if ( CPlayer.mo.Health < 25 ) cfont = LargeRedFont;
Screen.DrawTexture(IconHeal,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurX += 32;
CurY += 29;
DrawIconValue(Max(0,CPlayer.mo.Health));
CurY -= 29;
if ( !HudMode ) Screen.DrawText(cfont,Font.CR_UNTRANSLATED,CurX+2,CurY,String.Format("%d",Max(0,CPlayer.mo.Health)),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
CurX = X+2;
CurY = Y+29;
if ( (HudMode != 1) && (HudMode != 2) && (HudMode != 4) )
Screen.DrawTexture(HudLine,false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_WindowRightF,Min(28.*(CPlayer.mo.Health/double(CPlayer.mo.SpawnHealth())),28.));
}
private void DrawIdentifyInfo( int state )
{
double lalpha = 2.0-((gametic+fractic)-lastseentic)/Thinker.TICRATE;
if ( !lastseen || (lalpha <= 0) ) return;
String cl1 = "DarkGreen", cl2 = "Green";
if ( deathmatch && (lastseen.player.GetTeam() < teams.size()) )
{
cl2 = teams[lastseen.player.GetTeam()].mName;
cl1 = String.Format("Dark%s",cl2);
}
String tname = String.Format("\c[%s]%s:\c[%s] %s",cl1,StringTable.Localize("$M_NAME"),cl2,lastseen.player.GetUserName());
let fnt = (state==HUD_Fullscreen)?WhiteFont:OldSmallFont;
CurX = (ClipX-fnt.StringWidth(tname))/2;
CurY = ClipY-54;
Screen.DrawText(fnt,Font.CR_UNTRANSLATED,CurX,CurY,tname,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_Alpha,lalpha/2.);
if ( !deathmatch || (lastseen.IsTeammate(CPlayer.mo)) )
{
CurY += 1.2*fnt.GetHeight();
tname = String.Format("\c[%s]%s:\c[%s] %d",cl1,StringTable.Localize("$M_HEALTH"),cl2,lastseen.Health);
Screen.DrawText(fnt,Font.CR_UNTRANSLATED,CurX,CurY,tname,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_Alpha,lalpha/2.);
}
}
private void DrawKeys( double x, double y, bool leftright = false )
{
if ( deathmatch ) return; // no need to draw in DM
if ( gameinfo.gametype&(GAME_Hexen|GAME_Strife) ) return; // no key display for these ATM (will do eventually)
CurX = x;
CurY = y;
int cnt = 0;
for ( int i=0; i<6; i++ )
{
if ( !CPlayer.mo.CheckKeys(i+1,false,true) ) continue;
Screen.DrawTexture(KeyIcons[((i==0)&&(gameinfo.gametype&GAME_Heretic))?6:i],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
cnt++;
if ( !(cnt%3) )
{
CurX = x;
CurY -= 16;
}
else CurX += leftright?16:-16;
}
}
private void DrawUnrealHUD()
{
if ( HudMode >= 5 )
{
// minimal hud
int ArmorAmount = 0;
for ( Inventory Inv=CPlayer.mo.Inv; Inv; Inv=Inv.Inv )
{
if ( !(Inv is 'UTArmor') ) continue;
ArmorAmount += Inv.Amount;
}
int AmmoAmount1 = 0, AmmoAmount2 = 0;
Ammo amo1, amo2;
[amo1, amo2] = GetCurrentAmmo();
if ( amo1 )
{
AmmoAmount1 = amo1.Amount;
if ( amo2 ) AmmoAmount2 = amo2.Amount;
}
String str;
if ( amo1 && amo2 && (amo2 != amo1) ) str = String.Format(StringTable.Localize("$S_MINHUD2"),CPlayer.Health,(deathmatch||teamplay)?CPlayer.fragcount:CPlayer.killcount,AmmoAmount2,AmmoAmount1);
else str = String.Format(StringTable.Localize("$S_MINHUD"),CPlayer.Health,(deathmatch||teamplay)?CPlayer.fragcount:CPlayer.killcount,AmmoAmount1);
Screen.DrawText(OldSmallFont,Font.CR_WHITE,(Screen.GetWidth()-OldSmallFont.StringWidth(str)*CleanXFac_1)/2,CleanYFac_1,str,DTA_CleanNoMove_1,true);
return;
}
if ( ClipX < 320 ) HudMode = 4;
// Draw Armor
if ( HudMode < 2 ) DrawArmor(0,0);
else if ( (HudMode == 3) || (HudMode == 2) ) DrawArmor(0,ClipY-32);
else if ( HudMode == 4 ) DrawArmor(ClipX-64,ClipY-64,true);
// Draw Ammo
if ( HudMode != 4 ) DrawAmmo(ClipX-96,ClipY-32);
else DrawAmmo(ClipX-32,ClipY-32);
// Draw Health
if ( HudMode < 2 ) DrawHealth(0,ClipY-32);
else if ( (HudMode == 3) || (HudMode == 2) ) DrawHealth(ClipX-128,ClipY-32);
else if ( HudMode == 4 ) DrawHealth(ClipX-64,ClipY-32);
// Display Inventory
if ( HudMode < 2 ) DrawInventory(ClipX-96,0);
else if ( HudMode == 3 ) DrawInventory(ClipX-96,ClipY-64);
else if ( HudMode == 4 ) DrawInventory(ClipX-64,ClipY-64,true);
else if ( HudMode == 2 ) DrawInventory(ClipX/2-64,ClipY-32);
if ( deathmatch )
{
// Display Frag count
if ( HudMode < 3 ) DrawFragCount(ClipX-32,ClipY-64);
else if ( HudMode == 3 ) DrawFragCount(0,ClipY-64);
else if ( HudMode == 4 ) DrawFragCount(ClipX-96,ClipY-32);
}
else
{
// Display Keys
if ( HudMode < 3 ) DrawKeys(ClipX-16,ClipY-48);
else if ( HudMode == 3 ) DrawKeys(0,ClipY-48,true);
else if ( HudMode == 4 ) DrawKeys(ClipX-80,ClipY-16);
}
}
private void DrawUnrealBar()
{
// 0.83 status bar, just for funsies
DrawImage("Bar083",(0,336),DI_ITEM_OFFSETS);
// extra widescreen filler
bool first = true;
double base = -128;
double rx, dummy;
do
{
[rx, dummy, dummy] = StatusBarToRealCoords(base,0,HorizontalResolution);
DrawImage(first?"BarL083":"BarM083",(base,336),DI_ITEM_OFFSETS);
first = false;
base -= 128;
} while ( rx >= -128 );
first = true;
base = 640;
do
{
[rx, dummy, dummy] = StatusBarToRealCoords(base,0,HorizontalResolution);
DrawImage(first?"BarR083":"BarM083",(base,336),DI_ITEM_OFFSETS);
first = false;
base += 128;
} while ( rx < Screen.GetWidth() );
static const float slotofs[] = {525, 84, 128, 173, 216, 259, 349, 392, 436, 481};
static const float keyofs[] = {187, 211, 235, 379, 403, 427};
for ( int i=0; i<10; i++ )
{
if ( !CPlayer.HasWeaponsInSlot(i) ) continue;
bool used = (CPlayer.ReadyWeapon&&(CPlayer.ReadyWeapon.SlotNumber==i));
DrawImage("Slot083",(slotofs[i],342),DI_ITEM_OFFSETS,used?1.:.8);
}
DrawString(mOldDigits,FormatNumber(CPlayer.health,3),(358,366),DI_TEXT_ALIGN_RIGHT);
int ArmorAmount = 0, CurAbs = -1;
Inventory BestArmor = null;
for ( Inventory Inv=CPlayer.mo.Inv; Inv; Inv=Inv.Inv )
{
if ( !(Inv is 'UTArmor') ) continue;
ArmorAmount += Inv.Amount;
if ( Inv.Amount <= 0 ) continue;
if ( UTArmor(Inv).absorb > CurAbs )
{
CurAbs = UTArmor(Inv).absorb;
BestArmor = Inv;
}
}
for ( int i=0; i<6; i++ )
{
if ( !(BestArmor is OldArmorType[i]) ) continue;
DrawImage(OldArmor[i],(4,340),DI_ITEM_OFFSETS);
break;
}
if ( ArmorAmount ) DrawString(mOldDigits,FormatNumber(ArmorAmount,3),(167,366),DI_TEXT_ALIGN_RIGHT);
Inventory Ammo1, Ammo2;
[Ammo1, Ammo2] = GetCurrentAmmo();
if ( Ammo1 )
{
if ( Ammo1.Amount ) DrawString(mOldDigits,FormatNumber(Ammo1.Amount,3),(549,366),DI_TEXT_ALIGN_RIGHT);
for ( int i=0; i<19; i++ )
{
// match by ammo
if ( (OldAmmoType[i] is 'Ammo') && !(Ammo1 is OldAmmoType[i]) ) continue;
// match by weapon
if ( (OldAmmoType[i] is 'Weapon') && !(CPlayer.ReadyWeapon is OldAmmoType[i]) ) continue;
DrawImage(OldAmmo[i],(560,336),DI_ITEM_OFFSETS);
break;
}
}
for ( int i=0; i<6; i++ )
{
if ( !CPlayer.mo.CheckKeys(i+1,false,true) ) continue;
DrawImage(OldKeys[((i==0)&&(gameinfo.gametype&GAME_Heretic))?6:i],(keyofs[i],366),DI_ITEM_OFFSETS);
}
if ( HudMode > 5 ) return;
// Draw frags in DM
if ( deathmatch ) DrawFragCount(ClipX-32,0);
// Need to draw the inventory bar
DrawInventory(ClipX-(deathmatch?128:96),0,false,true);
}
override void Tick()
{
Super.Tick();
CPlayer.inventorytics = 0;
vtracer.ignore = CPlayer.mo;
vtracer.trace(CPlayer.mo.Vec2OffsetZ(0,0,CPlayer.viewz),CPlayer.mo.CurSector,(cos(CPlayer.mo.angle)*cos(CPlayer.mo.pitch),sin(CPlayer.mo.angle)*cos(CPlayer.mo.pitch),-sin(CPlayer.mo.pitch)),1000,0);
if ( vtracer.Results.HitType != TRACE_HitActor ) return;
lastseen = vtracer.Results.HitActor;
lastseentic = gametic;
}
override void FlushNotify()
{
for ( int i=0; i<4; i++ )
{
ShortMsg[i] = "";
ShortMsgTic[i] = int.min;
}
PickupMsg = "";
PickupMsgTic = int.min;
MidPrintStr = "";
MidPrintTic = int.min;
}
override bool ProcessNotify( EPrintLevel printlevel, String outline )
{
if ( printlevel == PRINT_LOW )
{
// set pickup message
PickupMsg = outline;
PickupMsgTic = gametic+50;
return true;
}
else
{
// put message into queue
for ( int i=3; i>0; i-- )
{
ShortMsg[i] = ShortMsg[i-1];
ShortMsgTic[i] = ShortMsgTic[i-1];
ShortMsgCol[i] = ShortMsgCol[i-1];
}
if ( printlevel == PRINT_MEDIUM ) ShortMsgCol[0] = Font.CR_RED;
else if ( (printlevel == PRINT_CHAT) || (printlevel == PRINT_TEAMCHAT) ) ShortMsgCol[0] = Font.CR_GREEN;
else ShortMsgCol[0] = Font.CR_WHITE;
ShortMsg[0] = outline;
ShortMsgTic[0] = gametic+70;
return true;
}
return false;
}
override bool ProcessMidPrint( Font fnt, String msg, bool bold )
{
if ( !fnt || (fnt == SmallFont) || (fnt == OriginalSmallFont) || (fnt == NewSmallFont) )
{
MidPrintStr = msg;
MidPrintLarge = false;
MidPrintTic = gametic+70;
return true;
}
else if ( fnt == BigFont )
{
MidPrintStr = msg;
MidPrintLarge = true;
MidPrintTic = gametic+70;
return true;
}
return false;
}
override bool DrawChat( String txt )
{
int xpos = 4*CleanXFac_1;
int ypos = ((screenblocks<=10)||automapactive)?GetTopOfStatusBar():(Screen.GetHeight()-((screenblocks>11)?0:int(32*scalev.y)));
ypos -= (WhiteFont.GetHeight()+4)*CleanYFac_1;
String fullstr = String.Format("(> Say %s%s",txt,WhiteFont.GetCursor());
// cut out until it fits
while ( (WhiteFont.StringWidth(fullstr) > CleanWidth_1) && fullstr.Length() > 7 )
fullstr.Remove(7,1);
Screen.DrawText(WhiteFont,Font.CR_GREEN,xpos,ypos,fullstr,DTA_CleanNoMove_1,true);
return true;
}
private void DrawMessages( int state )
{
int xpos, ypos;
if ( (MidPrintStr.Length() > 0) && (MidPrintTic > gametic) )
{
let mfnt = MidPrintLarge?UBigFont:WhiteFont;
xpos = (Screen.GetWidth()-mfnt.StringWidth(MidPrintStr)*CleanXFac_1)/2;
ypos = 96*CleanYFac_1;
Screen.DrawText(mfnt,Font.FindFontColor('UTHudText'),xpos,ypos,MidPrintStr,DTA_CleanNoMove_1,true,DTA_Alpha,clamp((MidPrintTic-gametic+fractic)*0.05,0,1),DTA_LegacyRenderStyle,STYLE_Add);
}
if ( PickupMsgTic > gametic )
{
xpos = (Screen.GetWidth()-WhiteFont.StringWidth(PickupMsg)*CleanXFac_1)/2;
if ( state == HUD_Statusbar ) ypos = GetTopOfStatusBar()-21*CleanYFac_1;
else ypos = Screen.GetHeight()-41*CleanYFac_1;
Screen.DrawText(WhiteFont,Font.CR_WHITE,xpos,ypos,PickupMsg,DTA_CleanNoMove_1,true,DTA_Alpha,clamp((PickupMsgTic-gametic+fractic)*0.05,0,1),DTA_LegacyRenderStyle,STYLE_Add);
}
// draw messages
xpos = 4*CleanXFac_1;
ypos = 4*CleanYFac_1;
if ( (state == HUD_Fullscreen) && (HudMode < 2) && DrawArmor(0,0,false,true) ) ypos += int(32*scalev.y);
for ( int i=3; i>=0; i-- )
{
if ( ShortMsgTic[i] < gametic ) continue;
let lines = WhiteFont.BreakLines(ShortMsg[i],CleanWidth_1/2);
for ( int j=0; j<lines.Count(); j++ )
{
Screen.DrawText(WhiteFont,ShortMsgCol[i],xpos,ypos,lines.StringAt(j),DTA_CleanNoMove_1,true);
ypos += (WhiteFont.GetHeight()+2)*CleanYFac_1;
}
}
}
override void DrawAutomapHUD( double ticFrac )
{
int crdefault = Font.CR_GREY;
int highlight = Font.CR_RED;
let scale = GetHUDScale();
double textdist = 8./scale.Y;
int height = WhiteFont.GetHeight();
String printtext;
int SCREENWIDTH = screen.GetWidth();
BeginHUD();
let y = textdist;
let width = WhiteFont.StringWidth("00:00:00");
double tmp, hres;
[tmp,tmp,hres] = StatusbarToRealCoords(0,0,HorizontalResolution);
double swidth = 0;
double ltop = 0, rtop = 0;
if ( (HudMode < 5) && CPlayer.mo.InvSel )
rtop += (32*scalev.y)/scale.Y;
double cbottom = GetTopOfStatusBar()-textdist;
int protrusion = GetProtrusion(swidth/hres);
[tmp,tmp,hres] = StatusbarToRealCoords(0,0,protrusion);
width += int((swidth-hres)/scale.X);
if ( am_showtime )
{
printtext = level.TimeFormatted();
DrawString(mMapFont,level.TimeFormatted(),(-textdist-width,y+rtop),0,crdefault);
y += height;
}
if ( am_showtotaltime ) DrawString(mMapFont,level.TimeFormatted(true),(-textdist-width,y+rtop),0,crdefault);
if ( !deathmatch )
{
y = textdist;
if ( am_showmonsters )
{
DrawString(mMapFont,String.Format("%s\34%c %d/%d",Stringtable.Localize("$AM_MONSTERS"),crdefault+65,level.killed_monsters,level.total_monsters),(textdist,y+ltop),0,highlight);
y += height;
}
if ( am_showsecrets )
{
DrawString(mMapFont,String.Format("%s\34%c %d/%d",Stringtable.Localize("$AM_SECRETS"),crdefault+65,level.found_secrets,level.total_secrets),(textdist,y+ltop),0,highlight);
y += height;
}
if ( am_showitems ) DrawString(mMapFont,String.Format("%s\34%c %d/%d",Stringtable.Localize("$AM_ITEMS"),crdefault+65,level.found_items,level.total_items),(textdist,y+ltop),0,highlight);
}
String mapname = level.FormatMapName(crdefault);
BrokenLines lines = WhiteFont.BreakLines(mapname,int(SCREENWIDTH/scale.X));
int numlines = lines.Count();
int finalwidth = int(WhiteFont.StringWidth(lines.StringAt(numlines-1))*scale.X);
[tmp,tmp,hres] = StatusbarToRealCoords(0,0,HorizontalResolution);
protrusion = GetProtrusion(finalwidth/hres);
[tmp,tmp,tmp,hres] = StatusbarToRealCoords(0,0,0,protrusion);
y = (cbottom-hres)/scale.Y-height*numlines;
for ( int i = 0; i < numlines; i++ )
{
DrawString(mMapFont,lines.StringAt(i),(0,y+ltop),DI_TEXT_ALIGN_CENTER|DI_SCREEN_HCENTER|DI_SCREEN_TOP,highlight);
y += height;
}
}
}