swwmgz_m/zscript/kbase/swwm_kbase.zsc
Marisa the Magician 5488bfce5d Remove code built on incorrect assumptions about UI events.
That is, remove code for closing menus with the key that's bound to open them.
As it turns out, UIEvent.keystring isn't 1:1 with key binds for a command.
This SEEMINGLY worked since the Demolitionist Menu is by default bound to Q,
and pressing Q does send an UIEvent to the menu with the string "Q". But if,
for example, the menu had been bound to Tab, this would fall apart because then
the key string sent is "	" (a literal tab character).
If there is a way to do this properly, I don't know about it. I've looked
everywhere in GZDoom's code for a solution, something that would let me do what
I need, but alas, there is nothing there. Better to get rid of this in its
entirety than keep the flaky code in the mod until someone with a special
setup that breaks it shows up to complain.
2022-08-13 14:28:09 +02:00

608 lines
19 KiB
Text

// internal "knowledge base" and more
Class MenuTransaction
{
enum ETransactionType
{
TT_ITEMUSE,
TT_ITEMDROP
};
int uid, type;
Class<Inventory> used;
bool result, usedup;
}
enum EKMenuKey
{
MK_DOWN,
MK_UP,
MK_LEFT,
MK_RIGHT,
MK_ENTER,
MK_BACK
};
enum EKMouseButton
{
MB_LEFT,
MB_RIGHT,
MB_WHEELUP,
MB_WHEELDOWN,
MB_DRAG,
MB_RELEASE
}
Class DemolitionistMenu : GenericMenu
{
TextureID FancyBg, FrameTex, VSepTex, HSepTex;
// for resolution scaling and such
double hs;
Vector2 ss, ws, origin;
// temporary bottom messages, such as "not enough money"
String tmsg;
int tmsgtic;
// money owned, for store
int muns;
// other text
String clockstr, munstr;
// for checks (duh)
Array<MenuTransaction> checklist;
int lasttuid;
SWWMHandler hnd;
SWWMStaticHandler shnd;
// seeeeecret
int kcode;
// crimey clock stuff
int c_year, c_month, c_day, c_hour, c_minute;
String c_tz;
// mouse stuff
Vector2 curmouse;
bool isrclick;
// somehow Drawer can be called while closing prematurely, which is big bollocks
bool isclosing, forceclose;
// the tabs
Array<DemolitionistMenuTab> tabs;
int curtab;
int oldtab; // used for returning from help tab
// fonts
Font mSmallFont, mTinyFont;
// for open/close animation
double animtimer;
int GenTUID()
{
return lasttuid++;
}
// tab functions
int FindTabType( Class<DemolitionistMenuTab> t, bool nothidden = false )
{
for ( int i=0; i<tabs.Size(); i++ )
{
if ( !(tabs[i] is t) ) continue;
if ( nothidden && tabs[i].bHidden ) continue;
return i;
}
return -1;
}
int GetFirstTab()
{
for ( int i=0; i<tabs.Size(); i++ )
{
if ( tabs[i].bHidden ) continue;
return i;
}
return -1;
}
int GetLastTab()
{
for ( int i=tabs.Size()-1; i>=0; i-- )
{
if ( tabs[i].bHidden ) continue;
return i;
}
return -1;
}
int GetNextTab()
{
int lst = GetLastTab();
if ( lst == -1 ) return -1;
if ( curtab >= lst ) return GetFirstTab();
for ( int i=curtab+1; i<tabs.Size(); i++ )
{
if ( tabs[i].bHidden ) continue;
return i;
}
return -1;
}
int GetPrevTab()
{
int fst = GetFirstTab();
if ( fst == -1 ) return -1;
if ( curtab <= fst ) return GetLastTab();
for ( int i=curtab-1; i>=0; i-- )
{
if ( tabs[i].bHidden ) continue;
return i;
}
return -1;
}
private void DoClose()
{
EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer);
for ( int i=0; i<tabs.Size(); i++ )
{
if ( i == curtab )
{
shnd.menustate.Insert("LastTab",tabs[i].GetClassName());
tabs[i].OnDeselect();
}
tabs[i].Destroy();
}
}
override void Init( Menu parent )
{
Super.Init(parent);
Animated = DontDim = DontBlur = true;
// can't open this menu outside of the game or if dead
// also can't if you're not the Demolitionist
if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') )
{
animtimer = -1.;
isclosing = true;
return;
}
FancyBg = TexMan.CheckForTexture("graphics/tempbg.png",TexMan.Type_MiscPatch);
FrameTex = TexMan.CheckForTexture("graphics/KBase/FrameTex.png",TexMan.Type_MiscPatch);
VSepTex = TexMan.CheckForTexture("graphics/KBase/VSepTex.png",TexMan.Type_MiscPatch);
HSepTex = TexMan.CheckForTexture("graphics/KBase/HSepTex.png",TexMan.Type_MiscPatch);
mSmallFont = Font.GetFont('TewiFont');
mTinyFont = Font.GetFont('MiniwiFont');
// note that we can assume 640x400 will always be the smallest resolution allowed by gzdoom, but we still need to handle widescreen
double sw = max(Screen.GetWidth(),640);
double sh = max(Screen.GetHeight(),400);
hs = max(min(floor(sw/640.),floor(sh/266.)),1.);
ss = (sw,sh)/hs;
ws.x = 640.;
double th = 640*(ss.y/ss.x);
ws.y = round(th);
origin = (int(ss.x-ws.x)/2,int(ss.y-ws.y)/2);
MenuSound("menu/demoopen");
tmsg = StringTable.Localize("$SWWM_MAINCONTROLS");
tmsgtic = MenuTime()+50;
lasttuid = Random[TUID]();
hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
shnd = SWWMStaticHandler(StaticEventHandler.Find("SWWMStaticHandler"));
SetClock();
static const class<DemolitionistMenuTab> deftabs[] =
{
'DemolitionistMissionTab',
'DemolitionistStatsTab',
'DemolitionistInventoryTab',
'DemolitionistKeychainTab',
'DemolitionistLibraryTab',
'DemolitionistStoreTab',
'DemolitionistChatTab',
//'DemolitionistGameTab', // disabled until 1.5
'DemolitionistHelpTab',
'DemolitionistSecretTab'
};
for ( int i=0; i<deftabs.Size()-2; i++ )
tabs.Push(DemolitionistMenuTab(new(deftabs[i])).Init(self));
// custom tabs go before the help and secret tabs
for ( int i=0; i<AllClasses.Size(); i++ )
{
let ct = (Class<DemolitionistMenuTabCustom>)(AllClasses[i]);
if ( !ct || (ct.GetParentClass() != 'DemolitionistMenuTabCustom') ) continue;
tabs.Push(DemolitionistMenuTab(new(ct)).Init(self));
}
for ( int i=deftabs.Size()-2; i<deftabs.Size(); i++ )
tabs.Push(DemolitionistMenuTab(new(deftabs[i])).Init(self));
if ( shnd.menustate )
{
Class<DemolitionistMenuTab> saved = shnd.menustate.At("LastTab");
if ( saved ) curtab = FindTabType(saved,true);
}
else
{
shnd.menustate = Dictionary.Create();
curtab = -1;
}
if ( curtab == -1 ) curtab = GetFirstTab();
tabs[curtab].OnSelect();
}
override bool MenuEvent( int mkey, bool fromcontroller )
{
if ( isclosing || (animtimer < 1.) ) return false;
// pachinko code only handled if the tab lacks direct input
if ( !tabs[curtab].bDirectInput )
{
switch ( kcode )
{
case 0:
case 1:
if ( mkey == MKEY_UP ) kcode++;
else kcode = 0;
break;
case 2:
case 3:
if ( mkey == MKEY_DOWN ) kcode++;
else kcode = 0;
break;
case 4:
case 6:
if ( mkey == MKEY_LEFT ) kcode++;
else kcode = 0;
break;
case 5:
case 7:
if ( mkey == MKEY_RIGHT ) kcode++;
else kcode = 0;
break;
case 10:
if ( mkey == MKEY_ENTER )
{
int secret = FindTabType('DemolitionistSecretTab');
if ( curtab != secret )
{
MenuSound("misc/secret");
tabs[curtab].OnDeselect();
curtab = secret;
tabs[curtab].OnSelect();
}
}
default:
kcode = 0;
break;
}
}
switch ( mkey )
{
case MKEY_BACK:
isclosing = true;
MenuSound("menu/democlose");
return true;
case MKEY_PAGEDOWN:
int next = GetNextTab();
if ( next != curtab )
{
MenuSound("menu/demotab");
tabs[curtab].OnDeselect();
curtab = next;
tabs[curtab].OnSelect();
}
return true;
case MKEY_PAGEUP:
int prev = GetPrevTab();
if ( prev != curtab )
{
MenuSound("menu/demotab");
tabs[curtab].OnDeselect();
curtab = prev;
tabs[curtab].OnSelect();
}
return true;
case MKEY_DOWN:
tabs[curtab].MenuInput(MK_DOWN);
return true;
case MKEY_UP:
tabs[curtab].MenuInput(MK_UP);
return true;
case MKEY_LEFT:
tabs[curtab].MenuInput(MK_LEFT);
return true;
case MKEY_RIGHT:
tabs[curtab].MenuInput(MK_RIGHT);
return true;
case MKEY_ENTER:
tabs[curtab].MenuInput(MK_ENTER);
return true;
case MKEY_CLEAR:
tabs[curtab].MenuInput(MK_BACK);
return true;
}
return Super.MenuEvent(mkey,fromcontroller);
}
override void Ticker()
{
Super.Ticker();
if ( ((gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || isclosing) && (animtimer <= 0.) )
{
// ded (or force close)
DoClose();
Close();
return;
}
if ( swwm_menupause ) menuactive = Menu.On;
else menuactive = Menu.OnNoPause;
// forcibly tick hud (mainly so interpolators can still update in the background)
if ( !multiplayer && (menuactive == Menu.On) )
{
let bar = SWWMStatusBar(StatusBar);
if ( bar ) bar.TickInterpolators();
}
CheckTransactions();
// update time string
clockstr = CrimeTime();
// update money
muns = SWWMCredits.Get(players[consoleplayer]);
munstr = String.Format("\cg¥\c-%09d",muns);
if ( isclosing )
{
if ( animtimer >= 1. ) DontDim = DontBlur = true;
animtimer -= .1;
return;
}
if ( animtimer < 1. )
{
animtimer += .1;
if ( animtimer < 1. ) return; // ensure tabs tick before first draw
else DontDim = DontBlur = false;
}
if ( (tabs.Size() <= 0) || (curtab == -1) || !tabs[curtab] ) return;
tabs[curtab].Ticker();
}
override bool MouseEvent( int type, int mx, int my )
{
if ( isclosing || (animtimer < 1.) ) return false;
bool res = Super.MouseEvent(type,mx,my);
Vector2 mpos = (mx/hs,my/hs)-origin;
if ( type == MOUSE_Click )
{
// outside clickable area
if ( (mpos.x < 0) || (mpos.x > ws.x) || (mpos.y < 0) || (mpos.y > ws.y-14) ) return res;
else if ( mpos.y < 14 )
{
if ( isrclick ) return res;
// check which tab we're clicking
int xx = 0;
String str;
int len;
for ( int i=0; i<tabs.Size(); i++ )
{
if ( tabs[i].bHidden ) continue;
str = tabs[i].title;
len = mSmallFont.StringWidth(str)+10;
if ( (mpos.x >= xx) && (mpos.x < xx+len) )
{
if ( curtab == i ) break;
MenuSound("menu/demotab");
tabs[curtab].OnDeselect();
curtab = i;
tabs[curtab].OnSelect();
break;
}
xx += len;
}
return res;
}
else if ( (tabs.Size() <= 0) || (curtab == -1) || !tabs[curtab] ) return res;
tabs[curtab].MouseInput(mpos,isrclick?MB_RIGHT:MB_LEFT);
}
else if ( type == MOUSE_Move )
{
if ( (tabs.Size() <= 0) || (curtab == -1) || !tabs[curtab] ) return res;
tabs[curtab].MouseInput(mpos,MB_DRAG);
}
else if ( type == MOUSE_Release )
{
if ( (tabs.Size() <= 0) || (curtab == -1) || !tabs[curtab] ) return res;
tabs[curtab].MouseInput(mpos,MB_RELEASE);
}
return res;
}
override bool OnUiEvent( UIEvent ev )
{
if ( isclosing || (animtimer < 1.) ) return false;
if ( tabs[curtab].bDirectInput && ((ev.type == UIEvent.Type_KeyDown) || (ev.type == UIEvent.Type_KeyUp)) )
return tabs[curtab].DirectInput(ev);
switch ( ev.type )
{
case UIEvent.Type_KeyDown:
if ( ev.keychar == UiEvent.Key_F1 )
{
int help = FindTabType('DemolitionistHelpTab');
if ( curtab == help )
{
tabs[curtab].OnDeselect();
curtab = oldtab;
tabs[curtab].OnSelect();
}
else
{
int secret = FindTabType('DemolitionistSecretTab');
if ( curtab == secret ) oldtab = GetLastTab();
else oldtab = curtab;
tabs[curtab].OnDeselect();
curtab = help;
tabs[curtab].OnSelect();
}
MenuSound("menu/demotab");
return true;
}
switch ( kcode )
{
case 8:
if ( ev.keystring ~== "B" )
kcode++;
else kcode = 0;
break;
case 9:
if ( ev.keystring ~== "A" ) kcode++;
else kcode = 0;
break;
default:
kcode = 0;
break;
}
break;
case UIEvent.Type_WheelDown:
tabs[curtab].MouseInput((curmouse/hs)-origin,MB_WHEELDOWN);
return true;
case UIEvent.Type_WheelUp:
tabs[curtab].MouseInput((curmouse/hs)-origin,MB_WHEELUP);
return true;
case UIEvent.Type_LButtonDown:
{
isrclick = false;
bool res = MouseEvent(MOUSE_Click,ev.MouseX,ev.MouseY);
if ( res ) SetCapture(true);
return res;
}
case UIEvent.Type_RButtonDown:
{
isrclick = true;
bool res = MouseEvent(MOUSE_Click,ev.MouseX,ev.MouseY);
if ( res ) SetCapture(true);
return res;
}
case UIEvent.Type_LButtonUp:
case UIEvent.Type_RButtonUp:
if ( mMouseCapture )
{
SetCapture(false);
return MouseEvent(MOUSE_Release,ev.MouseX,ev.MouseY);
}
break;
case UIEvent.Type_MouseMove:
curmouse = (ev.MouseX,ev.MouseY);
if ( mMouseCapture || (m_use_mouse == 1) )
return MouseEvent(MOUSE_Move,ev.MouseX,ev.MouseY);
break;
}
return false;
}
// fundamental drawing functions (good god these are hacky)
void DrawFrame( double x, double y, double w, double h, bool shadow = true )
{
x += origin.x;
y += origin.y;
Screen.DrawTexture(FrameTex,false,x,y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,0.,DTA_SrcY,0.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,1);
if ( w > 2 ) Screen.DrawTexture(FrameTex,false,x+1,y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,1.,DTA_SrcY,0.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,int(w-2),DTA_DestHeight,1);
if ( h > 2 ) Screen.DrawTexture(FrameTex,false,x,y+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,0.,DTA_SrcY,1.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,int(h-2));
Screen.DrawTexture(FrameTex,false,(x+w)-1,y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcY,0.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,1);
Screen.DrawTexture(FrameTex,false,x,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,0.,DTA_SrcY,2.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,1);
if ( shadow )
{
if ( h > 2 ) Screen.DrawTexture(FrameTex,false,(x+w)-1,y+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcY,1.,DTA_SrcWidth,2.,DTA_SrcHeight,1.,DTA_DestWidth,2,DTA_DestHeight,int(h-2));
if ( w > 2 ) Screen.DrawTexture(FrameTex,false,x+1,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,1.,DTA_SrcY,2.,DTA_SrcWidth,1.,DTA_SrcHeight,2.,DTA_DestWidth,int(w-2),DTA_DestHeight,2);
Screen.DrawTexture(FrameTex,false,(x+w)-1,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcY,2.,DTA_SrcWidth,2.,DTA_SrcHeight,2.,DTA_DestWidth,2,DTA_DestHeight,2);
}
else
{
if ( h > 2 ) Screen.DrawTexture(FrameTex,false,(x+w)-1,y+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcY,1.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,int(h-2));
if ( w > 2 ) Screen.DrawTexture(FrameTex,false,x+1,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,1.,DTA_SrcY,2.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,int(w-2),DTA_DestHeight,1);
Screen.DrawTexture(FrameTex,false,(x+w)-1,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcY,2.,DTA_SrcWidth,1.,DTA_SrcHeight,1.,DTA_DestWidth,1,DTA_DestHeight,1);
}
}
// these ones thankfully only need three drawtexture calls each
void DrawVSeparator( double x, double y, double h )
{
x += origin.x;
y += origin.y;
Screen.DrawTexture(VSepTex,false,x-1,y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcY,0.,DTA_SrcHeight,1.,DTA_DestHeight,1);
Screen.DrawTexture(VSepTex,false,x-1,y+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcY,1.,DTA_SrcHeight,1.,DTA_DestHeight,int(h-2));
Screen.DrawTexture(VSepTex,false,x-1,(y+h)-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcY,2.,DTA_SrcHeight,1.,DTA_DestHeight,1);
}
void DrawHSeparator( double x, double y, double w )
{
x += origin.x;
y += origin.y;
Screen.DrawTexture(HSepTex,false,x,y-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,0.,DTA_SrcWidth,1.,DTA_DestWidth,1);
Screen.DrawTexture(HSepTex,false,x+1,y-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,1.,DTA_SrcWidth,1.,DTA_DestWidth,int(w-2));
Screen.DrawTexture(HSepTex,false,(x+w)-1,y-1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_SrcX,2.,DTA_SrcWidth,1.,DTA_DestWidth,1);
}
override void Drawer()
{
if ( animtimer < 0 ) return;
// animated frame (more math)
if ( isclosing || (animtimer < 1.) )
{
double intp, xfact, yfact;
if ( isclosing )
{
intp = animtimer-.1*System.GetTimeFrac();
xfact = clamp(intp*2.,0.,1.)**2.;
yfact = clamp(intp,0.,1.)**5.;
}
else
{
intp = animtimer+.1*System.GetTimeFrac();
xfact = clamp(intp*2,0.,1.)**2.;
yfact = clamp(intp,0.,1.)**5.;
}
double rwsx = int(SWWMUtility.Lerp(2,ws.x,xfact));
double rwsy = int(SWWMUtility.Lerp(2,ws.y,yfact));
if ( (rwsx == 2) && (rwsy == 2) ) return;
double rox = int(ss.x-rwsx)/2;
double roy = int(ss.y-rwsy)/2;
// copy the menu dim below the window during animations, so the transition looks smoother
Screen.Dim((dimamount<0)?Color("Black"):dimcolor,(dimamount<0)?.5:dimamount,int(rox*hs),int(roy*hs),int(rwsx*hs),int(rwsy*hs));
// draw the background and main frame
if ( swwm_fuzz )
{
// fuzz was designed for 16:10, so we'll have to extend it at taller ratios
int count = int(ceil(ws.y/400.));
Screen.SetClipRect(int(rox*hs),int(roy*hs),int(rwsx*hs),int(rwsy*hs));
for ( int i=0; i<count; i++ )
Screen.DrawTexture(FancyBg,false,origin.x,origin.y+400.*i,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,.5);
Screen.ClearClipRect();
}
Screen.Dim("Black",.8,int(rox*hs),int(roy*hs),int(rwsx*hs),int(rwsy*hs));
DrawFrame(rox-origin.x,roy-origin.y,rwsx,rwsy,true);
return;
}
// draw the background and main frame
if ( swwm_fuzz )
{
// fuzz was designed for 16:10, so we'll have to extend it at taller ratios
int count = int(ceil(ws.y/400.));
Screen.SetClipRect(int(origin.x*hs),int(origin.y*hs),int(ws.x*hs),int(ws.y*hs));
for ( int i=0; i<count; i++ )
Screen.DrawTexture(FancyBg,false,origin.x,origin.y+400.*i,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,.5);
Screen.ClearClipRect();
}
Screen.Dim("Black",.8,int(origin.x*hs),int(origin.y*hs),int(ws.x*hs),int(ws.y*hs));
DrawFrame(0,0,ws.x,ws.y,true);
// draw top and bottom separators
DrawHSeparator(0,14,ws.x);
DrawHSeparator(0,ws.y-14,ws.x);
double xx = 5, yy = 1;
String str;
// draw tab listing
for ( int i=0; i<tabs.Size(); i++ )
{
if ( tabs[i].bHidden ) continue;
str = tabs[i].title;
Screen.DrawText(mSmallFont,(curtab==i)?Font.CR_FIRE:Font.CR_DARKGRAY,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += mSmallFont.StringWidth(str)+10;
DrawVSeparator(xx-5,0,14);
}
// draw money
xx = 637-mSmallFont.StringWidth(munstr);
Screen.DrawText(mSmallFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,munstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy = ws.y-13;
// draw clock / messages
if ( MenuTime() < tmsgtic ) str = tmsg;
else str = clockstr;
xx = 4;
Screen.DrawText(mSmallFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw os version
str = "DemolitionOS v1.0";
xx = 637-mSmallFont.StringWidth(str);
Screen.DrawText(mSmallFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw tab contents
if ( (tabs.Size() <= 0) || (curtab == -1) || !tabs[curtab] ) return;
tabs[curtab].Drawer(System.GetTimeFrac());
}
}