swwmgz_m/zscript/swwm_kbase.zsc
Marisa Kirisame 6068047fe8 Fix typo in SUSAN lore entry.
Restore weapon precaching.
Add precaching for gesture items.
Fix broken physics of light actors.
Add light actor physics to confetti.
Fix composite items not keeping their tracers on travel.
Fix ground distance reporting meters when it should be showing kilometers.
Reattach player shadow on travel.
Fix tip 37 erroneously listing slots from 6 and up as FOILINVUL when it should be 7 and up.
2020-12-14 21:03:08 +01:00

3277 lines
109 KiB
Text

// internal "knowledge base" and more
Class MenuTransaction
{
enum ETransactionType
{
TT_ITEMUSE,
TT_ITEMDROP,
TT_ITEMSEND
};
int uid, type;
Class<Inventory> used;
bool result, usedup;
}
// FIXME this whole thing needs to be refactored eventually
Class SWWMKnowledgeBaseMenu : GenericMenu
{
enum EDemoTab
{
TAB_MISSION = 0,
TAB_STATS,
TAB_INVENTORY,
TAB_KEYS,
TAB_LIBRARY,
TAB_STORE,
TAB_TRADING,
TAB_CHAT,
TAB_SECRET,
TAB_HELP
};
enum EStatsTab
{
STAT_MAIN = 0,
STAT_KILLS,
STAT_LEVEL,
STAT_ACHIEVEMENT,
};
TextureID MainWindow, TabSeparator, WindowSeparator, WindowSeparatorH,
FancyBg, EasterEgg, LoreSeparator, LoreSeparatorW;
transient CVar lang, fuzz, pauseme;
transient Font TewiFont, MPlusFont, MiniwiFont, k6x8Font;
Array<int> tabs;
int curtab, oldtab;
// for scrolling
bool sub;
int sel0, sel1, sel2;
int ofs0, ofs1, ofs2; // usually equal to above, except when using mouse input
int drag; // when dragging with the mouse, which scroller to affect (1 = ofs0 and so on)
// mission
String missionstr;
Array<String> missionbacklog;
// stats
SWWMStats stats;
Array<String> statlist;
Array<MonsterKill> sorted_mstats; // sorted by killcount
// inventory lists
Array<Inventory> invlist;
// lore stuff
SWWMLoreLibrary lorelib;
Array<SWWMLore> lorelist;
int oldloresiz;
// store stuff
Array<Class<Inventory> > storelist;
Array<int> storeunits;
// trading
SWWMTradeHistory tradelib;
Array<int> playerlist;
// chat history total line count (includes breaks)
int chatlines;
// temporary bottom messages, such as "not enough money"
String tmsg;
int tmsgtic;
// menu keybind
int ikey[2];
String mkey[2];
// color for chat
CVar ccol, tcol;
// for checks (duh)
Array<MenuTransaction> checklist;
int lasttuid;
SWWMHandler hnd;
// 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;
// returns MPlus if we're playing in Japanese, otherwise returns the requested font
Font LangFont( Font req )
{
if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]);
if ( lang.GetString() ~== "jp" ) return (req==MiniwiFont)?k6x8Font:MPlusFont;
return req;
}
int GenTUID()
{
return lasttuid++;
}
override void Init( Menu parent )
{
Super.Init(parent);
if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) || !(players[consoleplayer].mo is 'Demolitionist') )
{
// can't open this menu outside of the game or if dead
// also can't if you're not the Demolitionist
EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer);
isclosing = true;
return;
}
TewiFont = Font.GetFont('TewiShaded');
MPlusFont = Font.GetFont('MPlusShaded');
MiniwiFont = Font.GetFont('MiniwiShaded');
k6x8Font = Font.GetFont('k6x8Shaded');
FancyBg = TexMan.CheckForTexture("graphics/tempbg.png",TexMan.Type_MiscPatch);
MainWindow = TexMan.CheckForTexture("graphics/KBase/MainWindow.png",TexMan.Type_MiscPatch);
TabSeparator = TexMan.CheckForTexture("graphics/KBase/TabSeparator.png",TexMan.Type_MiscPatch);
WindowSeparator = TexMan.CheckForTexture("graphics/KBase/WindowSeparator.png",TexMan.Type_MiscPatch);
WindowSeparatorH = TexMan.CheckForTexture("graphics/KBase/WindowSeparatorH.png",TexMan.Type_MiscPatch);
EasterEgg = TexMan.CheckForTexture("graphics/KBase/Drawing.png",TexMan.Type_MiscPatch);
LoreSeparator = TexMan.CheckForTexture("graphics/KBase/LibraryTabSeparator.png",TexMan.Type_MiscPatch);
LoreSeparatorW = TexMan.CheckForTexture("graphics/KBase/LibraryTabSeparatorJP.png",TexMan.Type_MiscPatch);
MenuSound("menu/demoopen");
[ikey[0], ikey[1]] = Bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu");
mkey[0] = Bindings.NameKeys(ikey[0],0);
mkey[1] = Bindings.NameKeys(ikey[1],0);
ccol = CVar.GetCVar("msg3color",players[consoleplayer]);
tcol = CVar.GetCVar("msg4color",players[consoleplayer]);
lorelib = SWWMLoreLibrary.Find(players[consoleplayer]);
oldloresiz = lorelib.ent.Size();
tradelib = SWWMTradeHistory.Find(players[consoleplayer]);
stats = SWWMStats.Find(players[consoleplayer]);
tmsg = "$SWWM_MAINCONTROLS";
tmsgtic = gametic+70;
lasttuid = Random[TUID]();
hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
// mission text
if ( (gameinfo.gametype&GAME_Doom) && SWWMUtility.IsKnownMap() )
{
int clus = level.cluster;
bool eviternity = SWWMUtility.IsEviternity();
if ( eviternity )
{
// we have to do some heavy lifting here because episodes don't match clusters
if ( level.levelnum <= 5 ) clus = 1;
else if ( level.levelnum <= 10 ) clus = 2;
else if ( level.levelnum <= 15 ) clus = 3;
else if ( level.levelnum <= 20 ) clus = 4;
else if ( level.levelnum <= 25 ) clus = 5;
else if ( level.levelnum <= 30 ) clus = 6;
else if ( level.levelnum <= 31 ) clus = 7;
else if ( level.levelnum <= 32 ) clus = 8;
missionstr = String.Format("$SWWM_MISSION_EVITERNITY%d",clus);
}
else missionstr = String.Format("$SWWM_MISSION_DOOM%d",clus);
int csiz = stats.clustervisit.Size();
if ( (csiz > 0) && stats.secretdone[csiz-1] )
{
String tmpstr = missionstr.."_SECRET";
if ( !(StringTable.Localize(tmpstr) ~== tmpstr.Mid(1)) )
missionstr = tmpstr;
}
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == clus) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format(eviternity?"$SWWM_MISSION_EVITERNITY%d":"$SWWM_MISSION_DOOM%d",stats.clustervisit[i]);
if ( stats.secretdone[i] )
{
String tmpstr = xstr.."_SECRET";
if ( !(StringTable.Localize(tmpstr) ~== tmpstr.Mid(1)) )
xstr = tmpstr;
}
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
continue;
missionbacklog.Push(xstr);
}
}
else if ( (gameinfo.gametype&GAME_Heretic) && SWWMUtility.IsKnownMap() )
{
missionstr = String.Format("$SWWM_MISSION_HERETIC%d",level.cluster);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
int csiz = stats.clustervisit.Size();
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == level.cluster) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format("$SWWM_MISSION_HERETIC%d",stats.clustervisit[i]);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
continue;
missionbacklog.Push(xstr);
}
}
else if ( (gameinfo.gametype&GAME_Hexen) && SWWMUtility.IsKnownMap() )
{
missionstr = String.Format("$SWWM_MISSION_HEXEN%d",level.cluster);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
missionstr = "$SWWM_MISSION_NONE";
int csiz = stats.clustervisit.Size();
bool firstskip = false;
for ( int i=csiz-1; i>=0; i-- )
{
if ( (stats.clustervisit[i] == level.cluster) && !firstskip )
continue;
firstskip = true;
String xstr = String.Format("$SWWM_MISSION_HEXEN%d",stats.clustervisit[i]);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
continue;
missionbacklog.Push(xstr);
}
}
else if ( gameinfo.gametype&GAME_Strife )
{
missionstr = players[consoleplayer].logtext; // just show the quest log
int csiz = stats.questbacklog.Size();
for ( int i=csiz-1; i>=0; i-- )
missionbacklog.Push(stats.questbacklog[i]);
}
else missionstr = "$SWWM_MISSION_NONE";
if ( gameinfo.gametype&GAME_Heretic )
{
// March 10th 2171, 17:34 JST
// Epoch: 6351554040
c_year = 2171;
c_month = 3;
c_day = 9;
c_hour = 17;
c_minute = 34;
c_tz = "JST";
}
else if ( gameinfo.gametype&GAME_Hexen )
{
// May 25th 2171, 16:41 JST
// Epoch: 6355438860
c_year = 2171;
c_month = 4;
c_day = 24;
c_hour = 16;
c_minute = 41;
c_tz = "JST";
}
else if ( gameinfo.gametype&GAME_Strife )
{
// September 13th 2173, 11:26 JST
// Epoch: 6425490360
c_year = 2173;
c_month = 7;
c_day = 12;
c_hour = 11;
c_minute = 26;
c_tz = "JST";
}
else if ( SWWMUtility.IsEviternity() )
{
// June 10th 2148, 20:09 +09
// (June 10th 2148, 20:09 JST)
// Epoch: 5631073740
c_year = 2148;
c_month = 5;
c_day = 9;
c_hour = 20;
c_minute = 9;
c_tz = "+09";
}
else // Doom
{
// June 6th 2148, 18:37 EDT
// (June 7th 2148, 07:37 JST)
// Epoch: 5630769420
c_year = 2148;
c_month = 5;
c_day = 5;
c_hour = 18;
c_minute = 37;
c_tz = "EDT";
}
if ( (missionstr != "$SWWM_MISSION_NONE") || (missionbacklog.Size() > 0) )
tabs.Push(TAB_MISSION);
tabs.Push(TAB_STATS);
tabs.Push(TAB_INVENTORY);
tabs.Push(TAB_KEYS);
tabs.Push(TAB_LIBRARY);
tabs.Push(TAB_STORE);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
tabs.Push(TAB_TRADING);
break;
}
let bar = SWWMStatusBar(StatusBar);
if ( bar && (bar.FullHistory.Size() > 0) )
tabs.Push(TAB_CHAT);
int lasttab = CVar.GetCVar('swwm_lasttab',players[consoleplayer]).GetInt();
for ( int i=0; i<tabs.Size(); i++ )
{
if ( tabs[i] != lasttab ) continue;
curtab = i;
break;
}
if ( tabs[curtab] == TAB_LIBRARY ) sel1 = CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).GetInt();
else if ( tabs[curtab] == TAB_STATS ) sel1 = CVar.GetCVar('swwm_laststattab',players[consoleplayer]).GetInt();
}
// please don't look at this
private String CrimeTime()
{
// we have to do things this way because dates in this mod go beyond 32-bit unix time
static const int days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
bool leap_year;
int y = c_year, m = c_month, d = c_day, h = c_hour, mn = c_minute;
int addtime = level.totaltime/Thinker.TICRATE;
while ( addtime > 0 )
{
leap_year = (!(y%4) && ((y%100) || !(y%400)));
if ( addtime >= 86400 )
{
addtime -= 86400;
d++;
int md = days_in_month[m];
if ( leap_year && (m == 1) ) md++;
if ( d >= md )
{
d = 0;
m++;
if ( m > 11 )
{
m = 0;
y++;
}
}
}
else if ( addtime >= 3600 )
{
addtime -= 3600;
h++;
if ( h > 23 )
{
h = 0;
d++;
int md = days_in_month[m];
if ( leap_year && (m == 1) ) md++;
if ( d >= md )
{
d = 0;
m++;
if ( m > 11 )
{
m = 0;
y++;
}
}
}
}
else if ( addtime >= 60 )
{
addtime -= 60;
mn++;
if ( mn > 59 )
{
mn = 0;
h++;
if ( h > 23 )
{
h = 0;
d++;
int md = days_in_month[m];
if ( leap_year && (m == 1) ) md++;
if ( d >= md )
{
d = 0;
m++;
if ( m > 11 )
{
m = 0;
y++;
}
}
}
}
}
else
{
// we finished counting
addtime = 0;
}
}
return String.Format("%04d-%02d-%02d %02d:%02d %s",y,m+1,d+1,h,mn,c_tz);
}
override bool MenuEvent( int mkey, bool fromcontroller )
{
Font fnt = LangFont(TewiFont);
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 )
{
MenuSound("misc/secret");
if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab);
tabs.Push(TAB_SECRET);
curtab = tabs.Size()-1;
sel0 = sel1 = sel2 = 0;
ofs0 = ofs1 = ofs2 = 0;
sub = false;
}
default:
kcode = 0;
break;
}
int maxtab = multiplayer?TAB_CHAT:TAB_STORE;
switch ( mkey )
{
case MKEY_BACK:
MenuSound("menu/democlose");
EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer);
Close();
return true;
case MKEY_PAGEUP:
MenuSound("menu/demotab");
if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab); // hide this tab
if ( curtab <= 0 ) curtab = (tabs.Size()-1);
else curtab--;
CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(tabs[curtab]);
sel0 = sel1 = sel2 = 0;
ofs0 = ofs1 = ofs2 = 0;
sub = false;
invlist.Clear();
lorelist.Clear();
storelist.Clear();
storeunits.Clear();
playerlist.Clear();
return true;
case MKEY_PAGEDOWN:
MenuSound("menu/demotab");
if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab); // hide this tab
if ( curtab < (tabs.Size()-1) ) curtab++;
else curtab = 0;
CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(tabs[curtab]);
sel0 = sel1 = sel2 = 0;
ofs0 = ofs1 = ofs2 = 0;
sub = false;
invlist.Clear();
lorelist.Clear();
storelist.Clear();
storeunits.Clear();
playerlist.Clear();
return true;
case MKEY_DOWN:
if ( tabs[curtab] == TAB_HELP )
{
String str = StringTable.Localize("$SWWM_HELPTXT");
if ( multiplayer ) str = str..StringTable.Localize("$SWWM_HELPTXT_MP");
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
if ( (l.Count() > 28) && (sel0 < l.Count()-28) )
{
MenuSound("menu/demoscroll");
sel0++;
}
}
else if ( tabs[curtab] == TAB_MISSION )
{
String str;
if ( sel1 == 0 ) str = StringTable.Localize(missionstr);
else str = StringTable.Localize(missionbacklog[sel1-1]);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
if ( (l.Count() > 28) && (sel0 < l.Count()-28) )
{
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
}
}
else if ( (tabs[curtab] == TAB_CHAT) && (StatusBar is 'SWWMStatusBar') && (sel0 > 0) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
}
else if ( (tabs[curtab] == TAB_LIBRARY) && (lorelist.Size() > 1) )
{
if ( sub )
{
String str = StringTable.Localize(lorelist[sel0].text);
int ofs = (lang.GetString()~=="jp")?212:132;
if ( lorelist.Size() > 26 ) ofs += 8;
BrokenLines l = fnt.BreakLines(str,635-ofs);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs);
if ( (l.Count() > 28) && (sel2 < l.Count()-28) )
{
MenuSound("menu/demoscroll");
ofs2 = ++sel2;
}
}
else
{
MenuSound("menu/demoscroll");
if ( sel0 >= lorelist.Size()-1 ) ofs0 = sel0 = 0;
else
{
sel0++;
// update ofs so selection stays on-screen
int ofs = max(ofs0-26,0);
if ( sel0 < ofs ) ofs0 = sel0+26;
else if ( sel0 > ofs+26 ) ofs0 = sel0;
}
}
}
else if ( ((tabs[curtab] == TAB_INVENTORY) || (tabs[curtab] == TAB_KEYS) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 1) )
{
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
if ( sel0 >= invlist.Size() ) ofs0 = sel0 = 0;
}
else if ( (tabs[curtab] == TAB_STORE) && (storelist.Size() > 1) )
{
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
if ( sel0 >= storelist.Size() ) ofs0 = sel0 = 0;
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub && (playerlist.Size() > 1) )
{
// scroll through player list
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
if ( sel0 >= playerlist.Size() ) ofs0 = sel0 = 0;
}
else if ( sub && (sel0 == -1) && (sel1 < tradelib.ent.Size()-28) )
{
// scroll through trading history
MenuSound("menu/demoscroll");
ofs1 = ++sel1;
}
}
else if ( tabs[curtab] == TAB_STATS )
{
int cnt = 0;
if ( sel1 == STAT_MAIN )
cnt = statlist.Size();
else if ( sel1 == STAT_KILLS )
cnt = sorted_mstats.Size();
else if ( sel1 == STAT_LEVEL )
cnt = stats.lstats.Size();
if ( (cnt > 22) && (sel0 < cnt-22) )
{
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
}
}
return true;
case MKEY_UP:
if ( (tabs[curtab] == TAB_HELP) && (sel0 > 0) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
}
else if ( (tabs[curtab] == TAB_MISSION) && (sel0 > 0) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
}
else if ( (tabs[curtab] == TAB_CHAT) && (StatusBar is 'SWWMStatusBar') && (sel0 < SWWMStatusBar(StatusBar).FullHistory.Size()-1) )
{
MenuSound("menu/demoscroll");
ofs0 = ++sel0;
}
else if ( (tabs[curtab] == TAB_LIBRARY) && (lorelist.Size() > 1) )
{
if ( sub )
{
if ( sel2 > 0 )
{
MenuSound("menu/demoscroll");
ofs2 = --sel2;
}
}
else
{
MenuSound("menu/demoscroll");
if ( sel0 <= 0 ) ofs0 = sel0 = lorelist.Size()-1;
else
{
sel0--;
// update ofs so selection stays on-screen
int ofs = max(ofs0-26,0);
if ( sel0 < ofs ) ofs0 = sel0+26;
else if ( sel0 > ofs+26 ) ofs0 = sel0;
}
}
}
else if ( ((tabs[curtab] == TAB_INVENTORY) || (tabs[curtab] == TAB_KEYS) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 1) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
if ( sel0 < 0 ) ofs0 = sel0 = invlist.Size()-1;
}
else if ( (tabs[curtab] == TAB_STORE) && (storelist.Size() > 1) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
if ( sel0 < 0 ) ofs0 = sel0 = storelist.Size()-1;
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub && (playerlist.Size() > 1) )
{
// scroll through player list
MenuSound("menu/demoscroll");
ofs0 = --sel0;
if ( sel0 < 0 ) ofs0 = sel0 = playerlist.Size()-1;
}
else if ( sub && (sel0 == -1) && (sel1 > 0) )
{
// scroll through trading history
MenuSound("menu/demoscroll");
ofs1 = --sel1;
}
}
else if ( (tabs[curtab] == TAB_STATS) && (sel0 > 0) )
{
MenuSound("menu/demoscroll");
ofs0 = --sel0;
}
return true;
case MKEY_RIGHT:
if ( (tabs[curtab] == TAB_MISSION) && (missionbacklog.Size() > 0) )
{
if ( sel1 < missionbacklog.Size() )
{
sel0 = 0;
sel1++;
MenuSound("menu/demoscroll");
}
}
else if ( ((tabs[curtab] == TAB_INVENTORY) || (tabs[curtab] == TAB_KEYS) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 21) )
{
int oldsel = sel0;
ofs0 = sel0 += 22;
if ( sel0 >= invlist.Size() ) ofs0 = sel0 = invlist.Size()-1;
if ( sel0 != oldsel ) MenuSound("menu/demoscroll");
}
else if ( (tabs[curtab] == TAB_STORE) && (storelist.Size() > 21) )
{
int oldsel = sel0;
ofs0 = sel0 += 22;
if ( sel0 >= storelist.Size() ) ofs0 = sel0 = storelist.Size()-1;
if ( sel0 != oldsel ) MenuSound("menu/demoscroll");
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 = 0;
sub = false;
ofs1 = ++sel1;
if ( sel1 > LORE_LORE ) ofs1 = sel1 = LORE_ITEM;
CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).SetInt(sel1);
}
else if ( (tabs[curtab] == TAB_TRADING) && !sub && (playerlist.Size() > 21) ) // lol is this ever going to happen
{
int oldsel = sel0;
ofs0 = sel0 += 22;
if ( sel0 >= playerlist.Size() ) ofs0 = sel0 = playerlist.Size()-1;
if ( sel0 != oldsel ) MenuSound("menu/demoscroll");
}
else if ( tabs[curtab] == TAB_STATS )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 = 0;
ofs1 = ++sel1;
if ( ofs1 > STAT_ACHIEVEMENT ) ofs1 = sel1 = STAT_MAIN;
CVar.GetCVar('swwm_laststattab',players[consoleplayer]).SetInt(sel1);
}
return true;
case MKEY_LEFT:
if ( (tabs[curtab] == TAB_MISSION) && (missionbacklog.Size() > 0) )
{
if ( sel1 > 0 )
{
sel0 = 0;
sel1--;
MenuSound("menu/demoscroll");
}
}
else if ( ((tabs[curtab] == TAB_INVENTORY) || (tabs[curtab] == TAB_KEYS) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 21) && (sel0-22 >= 0) )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 -= 22;
}
else if ( (tabs[curtab] == TAB_STORE) && (storelist.Size() > 21) && (sel0-22 >= 0) )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 -= 22;
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 = 0;
sub = false;
ofs1 = --sel1;
if ( sel1 < LORE_ITEM ) ofs1 = sel1 = LORE_LORE;
CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).SetInt(sel1);
}
else if ( (tabs[curtab] == TAB_TRADING) && !sub && (playerlist.Size() > 21) && (sel0-22 >= 0) ) // lol is this ever going to happen
{
ofs0 = sel0 -= 22;
MenuSound("menu/demoscroll");
}
else if ( tabs[curtab] == TAB_STATS )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 = 0;
ofs1 = --sel1;
if ( ofs1 < STAT_MAIN ) ofs1 = sel1 = STAT_ACHIEVEMENT;
CVar.GetCVar('swwm_laststattab',players[consoleplayer]).SetInt(sel1);
}
return true;
case MKEY_ENTER:
if ( (tabs[curtab] == TAB_INVENTORY) && (invlist.Size() > 0) && (sel0 < invlist.Size()) )
{
// can't use this
if ( (invlist[sel0] is 'Ammo') || (invlist[sel0] is 'MagAmmo') || (invlist[sel0] is 'SWWMCollectible')
|| (invlist[sel0] is 'HammerspaceEmbiggener') )
return true;
let t = new("MenuTransaction");
t.uid = GenTUID();
t.type = MenuTransaction.TT_ITEMUSE;
t.result = false;
// don't check weapons, always assume check succeeded
if ( invlist[sel0] is 'Weapon' ) t.result = true;
checklist.Push(t);
EventHandler.SendNetworkEvent(String.Format("swwmuseitem.%s",invlist[sel0].GetClassName()),consoleplayer,t.uid);
}
else if ( (tabs[curtab] == TAB_KEYS) && (invlist.Size() > 0) && (sel0 < invlist.Size()) )
{
// can't use this
if ( !(invlist[sel0] is 'SWWMKey') && !(invlist[sel0] is 'SWWMCollectible') )
return true;
let t = new("MenuTransaction");
t.uid = GenTUID();
t.type = MenuTransaction.TT_ITEMUSE;
t.result = true; // always true
checklist.Push(t);
EventHandler.SendNetworkEvent(String.Format("swwmuseitem.%s",invlist[sel0].GetClassName()),consoleplayer,t.uid);
}
else if ( (tabs[curtab] == TAB_STORE) && (storelist.Size() > 0) && (sel0 < storelist.Size()) )
{
let itm = GetDefaultByType(storelist[sel0]);
let amt = storeunits[sel0];
int price = int(itm.Stamina*(1.+.75*(amt-1)));
if ( !SWWMCredits.CanTake(players[consoleplayer],price) )
{
MenuSound("menu/noinvuse");
tmsg = StringTable.Localize("$SWWM_STOREMUNS");
tmsgtic = gametic+70;
return true;
}
let cur = players[consoleplayer].mo.FindInventory(storelist[sel0]);
int camt, max;
if ( cur )
{
camt = cur.Amount;
max = cur.MaxAmount;
}
else
{
camt = 0;
max = itm.MaxAmount;
}
if ( camt >= max )
{
MenuSound("menu/noinvuse");
tmsg = StringTable.Localize("$SWWM_STOREFULL");
tmsgtic = gametic+70;
return true;
}
EventHandler.SendNetworkEvent(String.Format("swwmstoregive.%s",storelist[sel0].GetClassName()),consoleplayer,price,amt);
if ( (camt+amt) >= max ) ofs1 = sel0 = max(0,sel0-1);
MenuSound("menu/buyinv");
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
if ( sub )
{
MenuSound("menu/democlose");
sub = false;
}
else if ( (lorelist.Size() > 0) && (sel0 < lorelist.Size()) )
{
// mark as read
if ( !lorelist[sel0].read )
EventHandler.SendNetworkEvent(String.Format("swwmmarkloreread.%s",lorelist[sel0].tag),consoleplayer);
MenuSound("menu/demosel");
sub = true;
ofs2 = sel2 = 0;
}
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub && (playerlist.Size() > 0) )
{
// pick a player
MenuSound("menu/demosel");
sub = true;
ofs1 = sel1 = 0;
}
else if ( sub && (sel0 != -1) && (invlist.Size() > 0) )
{
// cannot trade these
if ( invlist[sel1].bUNDROPPABLE || invlist[sel1].bUNTOSSABLE )
{
MenuSound("menu/noinvuse");
tmsg = StringTable.Localize("$SWWM_TRADEFAIL");
tmsgtic = gametic+70;
return true;
}
// trade item
let t = new("MenuTransaction");
t.uid = GenTUID();
t.type = MenuTransaction.TT_ITEMSEND;
t.result = false;
checklist.Push(t);
EventHandler.SendNetworkEvent(String.Format("swwmtrade.%s",invlist[sel1].GetClassName()),consoleplayer,playerlist[sel0],t.uid);
}
}
return true;
case MKEY_CLEAR:
if ( (tabs[curtab] == TAB_INVENTORY) && (invlist.Size() > 0) )
{
let t = new("MenuTransaction");
t.uid = GenTUID();
t.type = MenuTransaction.TT_ITEMDROP;
t.result = false;
checklist.Push(t);
EventHandler.SendNetworkEvent(String.Format("swwmdropitem.%s",invlist[sel0].GetClassName()),consoleplayer,t.uid);
}
else if ( (tabs[curtab] == TAB_LIBRARY) && sub )
{
MenuSound("menu/democlose");
sub = false;
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub )
{
// open trading history
MenuSound("menu/demosel");
sub = true;
ofs0 = sel0 = -1;
ofs1 = sel1 = 0;
}
else if ( sub )
{
// go back
MenuSound("menu/democlose");
sub = false;
ofs0 = sel0 = 0;
}
}
return true;
}
return Super.MenuEvent(mkey,fromcontroller);
}
private int rnd( double num )
{
return int(num+((num<0)?-.5:.5));
}
override bool MouseEvent( int type, int mx, int my )
{
bool res = Super.MouseEvent(type,mx,my);
Font fnt = LangFont(TewiFont);
String str;
double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.);
Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs;
Vector2 origin = (ss.x-640,ss.y-400)/2.;
Vector2 mpos = (mx/hs,my/hs)-origin;
int xx, yy, len;
if ( type == MOUSE_Click )
{
if ( (mpos.x < 0) || (mpos.x >= 641) || (mpos.y < 0) || (mpos.y >= 386) ) return res; // outside clickable area, ignore
else if ( mpos.y < 14 )
{
if ( isrclick ) return res;
// check which tab we're clicking
static const string tabnames[] =
{
"$SWWM_MISSTAB", "$SWWM_STATTAB", "$SWWM_INVTAB", "$SWWM_KEYTAB",
"$SWWM_KBASETAB", "$SWWM_STORETAB", "$SWWM_TRADETAB", "$SWWM_CHATTAB",
"$SWWM_SECRETTAB", "$SWWM_HELPTAB"
};
xx = 0;
for ( int i=0; i<tabs.Size(); i++ )
{
len = fnt.StringWidth(tabnames[tabs[i]])+6;
if ( (mpos.x >= xx) && (mpos.x < xx+len) )
{
if ( curtab == i ) break;
MenuSound("menu/demotab");
if ( tabs[curtab] >= TAB_SECRET )
{
tabs.Delete(curtab);
if ( curtab < i ) i--; // account for shortening
}
curtab = i;
CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(tabs[curtab]);
sel0 = sel1 = sel2 = 0;
ofs0 = ofs1 = ofs2 = 0;
sub = false;
invlist.Clear();
lorelist.Clear();
storelist.Clear();
storeunits.Clear();
playerlist.Clear();
break;
}
xx += len;
}
}
else if ( tabs[curtab] == TAB_MISSION )
{
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 )
{
int csiz = missionbacklog.Size();
if ( csiz <= 0 ) return res;
if ( isrclick && (sel1 < csiz) )
{
sel0 = 0;
sel1++;
MenuSound("menu/demoscroll");
}
else if ( !isrclick && (sel1 > 0) )
{
sel0 = 0;
sel1--;
MenuSound("menu/demoscroll");
}
return res;
}
if ( !isrclick )
{
String str;
if ( sel1 == 0 ) str = StringTable.Localize(missionstr);
else str = StringTable.Localize(missionbacklog[sel1-1]);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
drag = 1;
}
}
else if ( tabs[curtab] == TAB_INVENTORY )
{
// check what item we clicked
if ( mpos.y < 377 )
{
if ( invlist.Size() <= 0 ) return res; // clicked nothing
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
xx = 9;
yy = 23;
int cols = 1;
for ( int i=ofs; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
// check boundary
if ( (mpos.x >= xx) && (mpos.x < xx+longest) && (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel0 != i ) MenuSound("menu/demoscroll");
sel0 = i;
ofs0 = (sel0/22)*22;
// drop on right click in inventory tab
if ( (tabs[curtab] == TAB_INVENTORY) && isrclick ) MenuEvent(MKEY_CLEAR,false);
else if ( !isrclick ) MenuEvent(MKEY_ENTER,false);
return res;
}
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
}
else if ( !isrclick )
{
// check that scrollbar is present
if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
drag = 1;
}
}
else if ( (tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1) )
{
// check what item we clicked
if ( mpos.y < 377 )
{
if ( invlist.Size() <= 0 ) return res; // clicked nothing
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
int ofs = int(max(0,(ofs1/22)-(maxcol-1))*22);
xx = 9;
yy = 23;
int cols = 1;
for ( int i=ofs; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
// check boundary
if ( (mpos.x >= xx) && (mpos.x < xx+longest) && (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel1 != i ) MenuSound("menu/demoscroll");
sel1 = i;
ofs1 = (sel1/22)*22;
// drop on right click in inventory tab
if ( (tabs[curtab] == TAB_INVENTORY) && isrclick ) MenuEvent(MKEY_CLEAR,false);
else if ( !isrclick ) MenuEvent(MKEY_ENTER,false);
return res;
}
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
}
else if ( !isrclick )
{
// check that scrollbar is present
if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
drag = 1;
}
}
else if ( (tabs[curtab] == TAB_KEYS) && !isrclick )
{
// check what item we clicked
if ( mpos.y < 377 )
{
if ( invlist.Size() <= 0 ) return res; // clicked nothing
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectible' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
xx = 9;
yy = 23;
int cols = 1;
for ( int i=ofs; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
// check boundary
if ( (mpos.x >= xx) && (mpos.x < xx+longest) && (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel0 != i ) MenuSound("menu/demoscroll");
sel0 = i;
ofs0 = (sel0/22)*22;
// try to use it if it has any special behaviour
MenuEvent(MKEY_ENTER,false);
return res;
}
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
}
else if ( !isrclick )
{
// check that scrollbar is present
if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
drag = 1;
}
}
else if ( (tabs[curtab] == TAB_STORE) && !isrclick )
{
// check what item we clicked
if ( mpos.y < 377 )
{
if ( storelist.Size() <= 0 ) return res; // clicked nothing
xx = 9;
yy = 23;
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
if ( storeunits[i] > 1 ) str = String.Format("%dx %s",storeunits[i],def.GetTag());
else str = def.GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+96);
totalcol = int(ceil(storelist.Size()/22.));
// gotta check both the tag and the price for clicking
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
int cols = 1;
for ( int i=ofs; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
// check boundary
if ( (mpos.x >= xx) && (mpos.x < xx+longest+80) && (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel0 != i ) MenuSound("menu/demoscroll");
sel0 = i;
ofs0 = (sel0/22)*22;
MenuEvent(MKEY_ENTER,false);
return res;
}
yy += 16;
if ( yy >= 370 )
{
xx += longest+96;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
}
else if ( !isrclick )
{
// check that scrollbar is present
if ( storelist.Size() <= 0 ) return res; // definitely no scrollbar
xx = 9;
yy = 23;
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
if ( storeunits[i] > 1 ) str = String.Format("%dx %s",storeunits[i],def.GetTag());
else str = def.GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+96);
totalcol = int(ceil(storelist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
drag = 1;
}
}
else if ( (tabs[curtab] == TAB_LIBRARY) && !isrclick )
{
double midp = (lang.GetString()~=="jp")?206:126;
if ( mpos.x < midp )
{
if ( mpos.y < 28 )
{
// switch category
if ( mpos.x < midp/2. ) MenuEvent(MKEY_LEFT,false);
else MenuEvent(MKEY_RIGHT,false);
}
else
{
// check what item we clicked
int xx = 3;
int yy = 32;
int ofs = max(0,ofs0-26);
for ( int i=ofs; i<lorelist.Size(); i++ )
{
if ( yy > 370 ) break;
// check boundary
if ( (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel0 != i ) MenuSound("menu/demoscroll");
if ( (sel0 != i) || !sub )
{
sel0 = i;
sub = false;
MenuEvent(MKEY_Enter,false);
}
return res;
}
yy += 13;
}
}
}
else if ( mpos.x < midp+8 )
{
// check that scrollbar is present
if ( lorelist.Size() <= 27 ) return res; // no scrollbar
// calculate offset
int szr = lorelist.Size()-27;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step ) step += 26;
if ( step != ofs0 ) MenuSound("menu/demoscroll");
ofs0 = step;
drag = 1;
}
else if ( sub && (mpos.x >= 632) )
{
// check that scrollbar is present
str = StringTable.Localize(lorelist[sel0].text);
int ofs = (lang.GetString()~=="jp")?212:132;
if ( lorelist.Size() > 26 ) ofs += 8;
BrokenLines l = fnt.BreakLines(str,635-ofs);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel2 ) MenuSound("menu/demoscroll");
ofs2 = sel2 = step;
drag = 3;
}
}
else if ( (tabs[curtab] == TAB_TRADING) && isrclick )
{
MenuEvent(MKEY_CLEAR,false);
}
else if ( (tabs[curtab] == TAB_TRADING) && !isrclick )
{
if ( !sub )
{
// check what item we clicked
if ( mpos.y < 377 )
{
if ( playerlist.Size() <= 0 ) return res; // clicked nothing
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<playerlist.Size(); i++ )
{
str = players[playerlist[i]].GetUserName();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(playerlist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
xx = 9;
yy = 23;
int cols = 1;
for ( int i=ofs; i<playerlist.Size(); i++ )
{
str = players[playerlist[i]].GetUserName();
len = fnt.StringWidth(str);
// check text boundary
if ( (mpos.x >= xx) && (mpos.x < xx+len+1) && (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel0 != i ) MenuSound("menu/demoscroll");
ofs0 = sel0 = i;
MenuEvent(MKEY_Enter,false);
return res;
}
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
}
else if ( !isrclick )
{
// check that scrollbar is present (will it ever be???)
if ( playerlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<playerlist.Size(); i++ )
{
str = players[playerlist[i]].GetUserName();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(playerlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
drag = 1;
}
}
else if ( sel0 == -1 )
{
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 ) return res;
if ( tradelib.ent.Size() <= 28 ) return res; // no scrollbar
// calculate offset
int szr = tradelib.ent.Size()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
drag = 1;
}
}
else if ( (tabs[curtab] == TAB_CHAT) && !isrclick )
{
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 ) return res;
let bar = SWWMStatusBar(StatusBar);
if ( !bar || (bar.FullHistory.Size() <= 1) ) return res; // no scrollbar
// calculate offset
int szr = bar.FullHistory.Size()-1;
int step = szr-clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
drag = 1;
}
else if ( (tabs[curtab] == TAB_HELP) && !isrclick )
{
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 ) return res;
int k1, k2;
[k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu");
String kstr = bindings.NameKeys(k1,k2);
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
drag = 1;
}
else if ( (tabs[curtab] == TAB_STATS) && !isrclick )
{
double midp = (lang.GetString()~=="jp")?206:126;
if ( mpos.x < midp )
{
// check what item we clicked
int xx = 9;
int yy = 23;
for ( int i=0; i<=STAT_ACHIEVEMENT; i++ )
{
// check boundary
if ( (mpos.y >= yy) && (mpos.y < yy+14) )
{
if ( sel1 != i )
{
MenuSound("menu/demoscroll");
ofs0 = sel0 = 0;
sel1 = i;
CVar.GetCVar('swwm_laststattab',players[consoleplayer]).SetInt(sel1);
}
return res;
}
yy += 16;
}
return res;
}
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 ) return res;
int cnt = 0;
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = stats.lstats.Size();
if ( cnt <= 28 ) return res; // no scrollbar
// calculate offset
int szr = cnt-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
drag = 1;
}
}
else if ( (type == MOUSE_Move) && drag ) // scroll dragging
{
if ( tabs[curtab] == TAB_MISSION )
{
String str;
if ( sel1 == 0 ) str = StringTable.Localize(missionstr);
else str = StringTable.Localize(missionbacklog[sel1-1]);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
}
else if ( (tabs[curtab] == TAB_INVENTORY) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) )
{
// check that scrollbar is present
if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
}
else if ( tabs[curtab] == TAB_KEYS )
{
// check that scrollbar is present
if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectible' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
}
else if ( tabs[curtab] == TAB_STORE )
{
// check that scrollbar is present
if ( storelist.Size() <= 0 ) return res; // definitely no scrollbar
xx = 9;
yy = 23;
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
if ( storeunits[i] > 1 ) str = String.Format("%dx %s",storeunits[i],def.GetTag());
else str = def.GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+96);
totalcol = int(ceil(storelist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
if ( drag == 1 )
{
// check that scrollbar is present
if ( lorelist.Size() <= 27 ) return res; // no scrollbar
// calculate offset
int szr = lorelist.Size()-27;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step ) step += 26;
if ( step != ofs0 ) MenuSound("menu/demoscroll");
ofs0 = step;
}
else if ( drag == 3 )
{
// check that scrollbar is present
str = StringTable.Localize(lorelist[sel0].text);
int ofs = (lang.GetString()~=="jp")?212:132;
if ( lorelist.Size() > 26 ) ofs += 8;
BrokenLines l = fnt.BreakLines(str,635-ofs);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel2 ) MenuSound("menu/demoscroll");
ofs2 = sel2 = step;
}
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub )
{
// check that scrollbar is present (will it ever be???)
if ( playerlist.Size() <= 0 ) return res; // definitely no scrollbar
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<playerlist.Size(); i++ )
{
str = players[playerlist[i]].GetUserName();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
if ( maxcol >= totalcol ) return res; // no scrollbar
// calculate offset
int szr = totalcol-maxcol;
int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr);
if ( step > 0 ) step += (maxcol-1);
if ( step != (ofs0/22) ) MenuSound("menu/demoscroll");
ofs0 = step*22;
}
else if ( sel0 == -1 )
{
if ( tradelib.ent.Size() <= 28 ) return res; // no scrollbar
// calculate offset
int szr = tradelib.ent.Size()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
}
}
else if ( tabs[curtab] == TAB_CHAT )
{
let bar = SWWMStatusBar(StatusBar);
if ( !bar || (bar.FullHistory.Size() <= 1) ) return res; // no scrollbar
// calculate offset
int szr = bar.FullHistory.Size()-1;
int step = szr-clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
}
else if ( tabs[curtab] == TAB_HELP )
{
int k1, k2;
[k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu");
String kstr = bindings.NameKeys(k1,k2);
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
else return res; // no scrollbar
// calculate offset
int szr = l.Count()-28;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
}
else if ( tabs[curtab] == TAB_STATS )
{
int cnt = 0;
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = stats.lstats.Size();
if ( cnt <= 22 ) return res; // no scrollbar
// calculate offset
int szr = cnt-22;
int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr);
if ( step != sel0 ) MenuSound("menu/demoscroll");
ofs0 = sel0 = step;
}
}
else if ( type == MOUSE_Release ) drag = 0; // stop dragging any active scrolls
return res;
}
private bool CmpInventory( Inventory a, Inventory b )
{
int ta = 0, tb = 0;
if ( a is 'Weapon' ) ta = 2;
else if ( (a is 'BackpackItem') || (a is 'HammerspaceEmbiggener') ) ta = 3;
else if ( (a is 'Ammo') ) ta = 4;
else if ( (a is 'MagAmmo') ) ta = 5;
else if ( (a is 'PowerupGiver') || (a is 'AmmoFabricator') || a.bBIGPOWERUP ) ta = -3;
else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = -2;
else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = -1;
else if ( a is 'PuzzleItem' ) ta = 1;
if ( b is 'Weapon' ) tb = 2;
else if ( (b is 'BackpackItem') || (b is 'HammerspaceEmbiggener') ) tb = 3;
else if ( (b is 'Ammo') ) tb = 4;
else if ( (b is 'MagAmmo') ) tb = 5;
else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || b.bBIGPOWERUP ) tb = -3;
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = -2;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = -1;
else if ( b is 'PuzzleItem' ) tb = 1;
if ( ta == tb )
{
if ( a is 'Weapon' )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(Weapon(a).GetClass());
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(Weapon(b).GetClass());
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
return (Weapon(a).SlotPriority <= Weapon(b).SlotPriority);
return slota > slotb;
}
else if ( a is 'Ammo' )
{
Class<Weapon> usesa, usesb;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let w = (Class<Weapon>)(AllActorClasses[i]);
if ( !w ) continue;
let def = GetDefaultByType(w);
if ( w is 'SWWMWeapon' )
{
if ( SWWMWeapon(def).UsesAmmo(Ammo(a).GetClass()) )
usesa = w;
if ( SWWMWeapon(def).UsesAmmo(Ammo(b).GetClass()) )
usesb = w;
}
else
{
if ( (def.AmmoType1 == Ammo(a).GetClass()) || (def.AmmoType2 == Ammo(a).GetClass()) )
usesa = w;
if ( (def.AmmoType1 == Ammo(b).GetClass()) || (def.AmmoType2 == Ammo(b).GetClass()) )
usesb = w;
}
if ( usesa && usesb )
break;
}
if ( usesa && usesb )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(usesa);
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(usesb);
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
{
// sort by unit value
// (special, those with zero value go last)
int vala = a.default.Stamina;
if ( vala == 0 ) vala = int.max;
int valb = b.default.Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
return slota > slotb;
}
}
else if ( a is 'MagAmmo' )
{
// oh boy this one gets complicated
Class<Ammo> pamoa, pamob;
pamoa = MagAmmo(a).ParentAmmo;
pamob = MagAmmo(b).ParentAmmo;
Class<Weapon> usesa, usesb;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let w = (Class<Weapon>)(AllActorClasses[i]);
if ( !w ) continue;
let def = GetDefaultByType(w);
if ( w is 'SWWMWeapon' )
{
if ( SWWMWeapon(def).UsesAmmo(pamoa) )
usesa = w;
if ( SWWMWeapon(def).UsesAmmo(pamob) )
usesb = w;
}
else
{
if ( (def.AmmoType1 == pamoa) || (def.AmmoType2 == pamoa) )
usesa = w;
if ( (def.AmmoType1 == pamob) || (def.AmmoType2 == pamob) )
usesb = w;
}
if ( usesa && usesb )
break;
}
if ( usesa && usesb )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(usesa);
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(usesb);
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
{
// sort by unit value
// (special, those with zero value go last)
int vala = GetDefaultByType(pamoa).Stamina;
if ( vala == 0 ) vala = int.max;
int valb = GetDefaultByType(pamob).Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
return slota > slotb;
}
}
else
{
// sort by unit value
// (special, those with zero value go last)
int vala = a.default.Stamina;
if ( vala == 0 ) vala = int.max;
int valb = b.default.Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
}
return ta > tb;
}
private bool CmpInventoryClass( Class<Inventory> a, Class<Inventory> b )
{
int ta = 0, tb = 0;
let da = GetDefaultByType(a);
let db = GetDefaultByType(b);
if ( a is 'Weapon' ) ta = 2;
else if ( a is 'Ammo' ) ta = 1;
else if ( (a is 'PowerupGiver') || da.bBIGPOWERUP ) ta = 3;
else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = 5;
else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = 4;
if ( b is 'Weapon' ) tb = 2;
else if ( b is 'Ammo' ) tb = 1;
else if ( (b is 'PowerupGiver') || db.bBIGPOWERUP ) tb = 3;
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = 5;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = 4;
if ( ta == tb )
{
// sort by unit price
if ( da.Stamina == db.Stamina )
{
// sort alphabetically
return da.GetTag() > db.GetTag();
}
return da.Stamina > db.Stamina;
}
return ta < tb;
}
private int partition_inventory( Array<Inventory> a, int l, int h )
{
Inventory pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInventory(pv,a[j]) )
{
i++;
Inventory tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Inventory tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_inventory( Array<Inventory> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_inventory(a,l,h);
qsort_inventory(a,l,p-1);
qsort_inventory(a,p+1,h);
}
private int partition_store( Array<Class<Inventory> > a, Array<int> b, int l, int h )
{
Class<Inventory> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInventoryClass(pv,a[j]) )
{
i++;
Class<Inventory> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
int tmpi = b[j];
b[j] = b[i];
b[i] = tmpi;
}
}
Class<Inventory> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
int tmpi = b[h];
b[h] = b[i+1];
b[i+1] = tmpi;
return i+1;
}
private void qsort_store( Array<Class<Inventory> > a, Array<int> b, int l, int h )
{
if ( l >= h ) return;
int p = partition_store(a,b,l,h);
qsort_store(a,b,l,p-1);
qsort_store(a,b,p+1,h);
}
private bool CmpMonsterKill( MonsterKill a, MonsterKill b )
{
if ( a.kills == b.kills )
{
// sort by name instead
String taga, tagb;
taga = GetDefaultByType(a.m).GetTag();
tagb = GetDefaultByType(b.m).GetTag();
// beautify if there's no tag
if ( taga == FallbackTag )
{
taga = a.m.GetClassName();
SWWMUtility.BeautifyClassName(taga);
}
if ( tagb == FallbackTag )
{
tagb = a.m.GetClassName();
SWWMUtility.BeautifyClassName(tagb);
}
return taga > tagb;
}
return a.kills < b.kills;
}
private int partition_mstats( Array<MonsterKill> a, int l, int h )
{
MonsterKill pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpMonsterKill(pv,a[j]) )
{
i++;
MonsterKill tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
MonsterKill tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_mstats( Array<MonsterKill> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_mstats(a,l,h);
qsort_mstats(a,l,p-1);
qsort_mstats(a,p+1,h);
}
private int partition_lore( Array<SWWMLore> a, int l, int h )
{
SWWMLore pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( StringTable.Localize(pv.tag) > StringTable.Localize(a[j].tag) )
{
i++;
SWWMLore tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMLore tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_lore( Array<SWWMLore> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_lore(a,l,h);
qsort_lore(a,l,p-1);
qsort_lore(a,p+1,h);
}
private bool CmpCollectible( Inventory a, Inventory b )
{
if ( a.Stamina == b.Stamina )
{
// alphabetic
return a.GetTag() > b.GetTag();
}
// descending value
return a.default.Stamina < b.default.Stamina;
}
private int partition_collectible( Array<Inventory> a, int l, int h )
{
Inventory pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpCollectible(pv,a[j]) )
{
i++;
Inventory tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Inventory tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_collectible( Array<Inventory> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_collectible(a,l,h);
qsort_collectible(a,l,p-1);
qsort_collectible(a,p+1,h);
}
override void Ticker()
{
Super.Ticker();
if ( (players[consoleplayer].Health <= 0) || isclosing )
{
// ded (or force close)
EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer);
Close();
return;
}
if ( !pauseme ) pauseme = CVar.GetCVar('swwm_menupause',players[consoleplayer]);
if ( pauseme.GetBool() ) menuactive = Menu.On;
else menuactive = Menu.OnNoPause;
// forcibly tick hud (mainly so interpolators can still update in the background)
if ( !multiplayer && (menuactive == Menu.On) )
StatusBar.Tick();
if ( tabs[curtab] == TAB_INVENTORY )
{
invlist.Clear();
// alphabetically sorted inventory
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
{
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectible') && !(inv is 'MagAmmo') || (inv is 'SWWMCollectible') ) continue;
// no hidden weapons
if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).bHIDEINMENU ) continue;
String tag = inv.GetTag();
bool greater = false;
for ( int i=0; i<invlist.Size(); i++ )
{
String tag2 = invlist[i].GetTag();
if ( tag > tag2 ) continue;
greater = true;
invlist.Insert(i,inv);
break;
}
if ( greater ) continue;
invlist.Push(inv);
}
// re-sort by category
qsort_inventory(invlist,0,invlist.Size()-1);
// crop selection
if ( sel0 >= invlist.Size() ) sel0 = max(0,invlist.Size()-1);
}
else if ( (tabs[curtab] == TAB_STATS) && (sel1 == STAT_KILLS) )
{
sorted_mstats.Copy(stats.mstats);
qsort_mstats(sorted_mstats,0,sorted_mstats.Size()-1);
}
else if ( tabs[curtab] == TAB_KEYS )
{
invlist.Clear();
// keys sorted by their index
int n = Key.GetKeyTypeCount();
for ( int i=0; i<n; i++ )
{
let k = Key(players[consoleplayer].mo.FindInventory(Key.GetKeyType(i)));
if ( k ) invlist.Push(k);
}
// collectibles, sorted by name
Array<Inventory> cols;
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
{
if ( inv is 'SWWMCollectible' )
cols.Push(inv);
}
qsort_collectible(cols,0,cols.Size()-1);
invlist.Append(cols);
// crop selection
if ( sel0 >= invlist.Size() ) sel0 = max(0,invlist.Size()-1);
}
else if ( tabs[curtab] == TAB_STORE )
{
storelist.Clear();
storeunits.Clear();
// price-sorted buyables
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (Class<Inventory>)(AllActorClasses[i]);
if ( !type ) continue;
// no collectibles
if ( type is 'SWWMCollectible' ) continue;
// skip maxed items
let cur = players[consoleplayer].mo.FindInventory(type);
if ( cur && (cur.Amount >= cur.MaxAmount) ) continue;
// ignore ammo for weapons not owned
bool notownedammo = false;
if ( type is 'Ammo' )
{
notownedammo = true;
let amo = (Class<Ammo>)(type);
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.inv )
{
if ( !(inv is 'Weapon') ) continue;
if ( (Weapon(inv).AmmoType1 == amo) || (Weapon(inv).AmmoType2 == amo) )
{
notownedammo = false;
break;
}
if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).UsesAmmo(amo) )
{
notownedammo = false;
break;
}
}
}
if ( notownedammo ) continue;
let inv = GetDefaultByType(type);
// skip unimplemented weapons
if ( type is 'Weapon' )
{
let ready = inv.FindState("Ready");
if ( !ready || !ready.ValidateSpriteFrame() ) continue;
}
// ignore child ammos
if ( (type is 'Ammo') && (type.GetParentClass() != 'Ammo') ) continue;
if ( inv.Stamina <= 0 ) continue;
int amt = inv.Amount;
int price = int(inv.Stamina*(1.+.75*(amt-1)));
if ( type is 'Ammo' )
{
int maxneed = cur?(cur.MaxAmount-cur.Amount):inv.MaxAmount;
// get the largest affordable child pickup amount (that we need)
for ( int j=0; j<AllActorClasses.Size(); j++ )
{
let type2 = (Class<Ammo>)(AllActorClasses[j]);
if ( !type2 || (type2.GetParentClass() != type) ) continue;
let inv2 = GetDefaultByType(type2);
int cprice = int(inv.Stamina*(1.+.75*(inv2.Amount-1)));
if ( (inv2.Amount > amt) && (inv2.Amount <= maxneed) && SWWMCredits.CanTake(players[consoleplayer],cprice) )
{
price = cprice;
amt = inv2.Amount;
}
}
}
// nuggets can be bought in bulk
else if ( type is 'HealthNuggetItem' )
{
let inv2 = GetDefaultByType(SWWMHealth(inv).giveme);
int maxneed = (inv2.MaxAmount-players[consoleplayer].Health);
for ( int j=5; j<=20; j+=5 )
{
int cprice = int(inv.Stamina*(1.+.75*(j-1)));
if ( (j <= maxneed) && SWWMCredits.CanTake(players[consoleplayer],cprice) )
{
price = cprice;
amt = j;
}
}
}
else if ( type is 'ArmorNuggetItem' )
{
let inv2 = GetDefaultByType(SWWMSpareArmor(inv).giveme);
let cur2 = players[consoleplayer].mo.FindInventory(SWWMSpareArmor(inv).giveme);
int maxneed = cur2?(cur2.MaxAmount-cur2.Amount):inv2.MaxAmount;
for ( int j=5; j<=20; j+=5 )
{
int cprice = int(inv.Stamina*(1.+.75*(j-1)));
if ( (j <= maxneed) && SWWMCredits.CanTake(players[consoleplayer],cprice) )
{
price = cprice;
amt = j;
}
}
}
// sort by unit price
bool greater = false;
for ( int j=0; j<storelist.Size(); j++ )
{
let inv2 = GetDefaultByType(storelist[j]);
if ( inv.Stamina > inv2.Stamina ) continue;
greater = true;
storelist.Insert(j,type);
storeunits.Insert(j,amt);
break;
}
if ( greater ) continue;
storelist.Push(type);
storeunits.Push(amt);
}
// re-sort by category
qsort_store(storelist,storeunits,0,storelist.Size()-1);
// crop selection
if ( sel0 >= storelist.Size() ) sel0 = max(0,storelist.Size()-1);
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
// remember last position/size
SWWMLore oldone = null;
int oldsiz = lorelist.Size();
if ( lorelist.Size() > 0 ) oldone = lorelist[sel0];
// lore lore lore
lorelist.Clear();
for ( int i=0; i<lorelib.ent.Size(); i++ )
{
if ( lorelib.ent[i].tab != sel1 ) continue;
lorelist.Push(lorelib.ent[i]);
}
// sort
qsort_lore(lorelist,0,lorelist.Size()-1);
// readjust
if ( oldone && (lorelist.Size() != oldsiz) )
{
int idx = lorelist.Find(oldone);
if ( (idx > 0) && (idx < lorelist.Size()) )
sel0 = idx;
// update ofs so selection stays on-screen
int ofs = max(ofs0-26,0);
if ( sel0 < ofs ) ofs0 = sel0+26;
else if ( sel0 > ofs+26 ) ofs0 = sel0;
}
// add "new entries" message
if ( lorelib.ent.Size() != oldloresiz )
{
tmsg = StringTable.Localize("$SWWM_NEWLORE");
tmsgtic = gametic+70;
}
oldloresiz = lorelib.ent.Size();
}
else if ( tabs[curtab] == TAB_TRADING )
{
// build player list
playerlist.Clear();
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
playerlist.Push(i);
}
// build inventory list if open
if ( sub && (sel0 != -1) )
{
invlist.Clear();
// alphabetically sorted inventory
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
{
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectible') && !(inv is 'MagAmmo') ) continue;
// no hidden weapons
if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).bHIDEINMENU ) continue;
String tag = inv.GetTag();
bool greater = false;
for ( int i=0; i<invlist.Size(); i++ )
{
String tag2 = invlist[i].GetTag();
if ( tag > tag2 ) continue;
greater = true;
invlist.Insert(i,inv);
break;
}
if ( greater ) continue;
invlist.Push(inv);
}
// re-sort by category
qsort_inventory(invlist,0,invlist.Size()-1);
// crop selection
if ( sel1 >= invlist.Size() ) sel1 = max(0,invlist.Size()-1);
}
}
else if ( (tabs[curtab] == TAB_STATS) && (sel1 == STAT_MAIN) )
{
statlist.Clear();
// wish I could use macros for this
int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE));
int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60;
int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60;
String str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
statlist.Push(str);
if ( stats.grounddist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.airdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.swimdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.teledist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATTELE"),stats.teledist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATTELE"),stats.teledist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBOOST"),stats.boostcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDASH"),stats.dashcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSTOMP"),stats.stompcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFUEL"),stats.fuelusage/60.,StringTable.Localize("$SWWM_UNIT_LITER"));
statlist.Push(str);
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSPEED"),stats.topspeed*32000./(3600*Thinker.TICRATE),StringTable.Localize("$SWWM_UNIT_KPH"));
statlist.Push(str);
thour = (stats.airtime/(3600*Thinker.TICRATE));
tmin = (stats.airtime/(60*Thinker.TICRATE))%60;
tsec = (stats.airtime/Thinker.TICRATE)%60;
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATAIRTIME"),thour,tmin,tsec);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATWPONCH"),stats.wponch);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBUSTS"),stats.busts);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBUTTS"),stats.buttslams);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATITEMS"),stats.items);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSECRETS"),stats.secrets);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATKILLS"),stats.kills);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDEATHS"),stats.deaths);
statlist.Push(str);
if ( stats.hdamagedealt > 0 ) str = str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDDEALT"),stats.hdamagedealt,stats.damagedealt);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDDEALT"),stats.damagedealt);
statlist.Push(str);
if ( stats.hdamagetaken > 0 ) str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.hdamagetaken,stats.damagetaken);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.damagetaken);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTDEALT"),stats.topdealt);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTTAKEN"),stats.toptaken);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill);
statlist.Push(str);
str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP"));
if ( stats.favweapon == -1 ) str = str.."N/A";
else if ( stats.wstats[stats.favweapon].w == 'SWWMWeapon' ) str = str..StringTable.Localize("$SWWM_YOURSELF");
else if ( stats.wstats[stats.favweapon].w == 'SWWMGesture' ) str = str..StringTable.Localize("$SWWM_DOKIDOKI");
else if ( stats.wstats[stats.favweapon].w == 'Weapon' ) str = str..StringTable.Localize("$SWWM_GRAVKILL");
else
{
let def = GetDefaultByType(stats.wstats[stats.favweapon].w);
str = str..def.GetTag();
}
statlist.Push(str);
if ( stats.hhiscore > 0 ) str = String.Format("\cx%s\cu¥\c-%d%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hhiscore,stats.hiscore);
else str = String.Format("\cx%s\cu¥\c-%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hiscore);
statlist.Push(str);
}
// ui->play transaction checks
for ( int i=0; i<checklist.Size(); i++ )
{
bool deleteme = false;
for ( int j=0; j<hnd.checklist.Size(); j++ )
{
if ( hnd.checklist[j].uid != checklist[i].uid ) continue;
if ( checklist[i].type == MenuTransaction.TT_ITEMUSE )
{
// check if use succeeded
if ( checklist[i].result || hnd.checklist[j].result )
{
// only play use sound if game isn't frozen
if ( !multiplayer && (menuactive == Menu.On) )
{
// if it's a weapon, play the select sound
let w = (Class<Weapon>)(hnd.checklist[j].used);
if ( w )
{
// play if actually switching
if ( hnd.checklist[j].result )
{
// gz 4.5pre workaround (expects a Name now)
String snd = GetDefaultByType(w).UpSound;
MenuSound(snd);
}
}
else
{
// gz 4.5pre workaround (expects a Name now)
String snd = GetDefaultByType(hnd.checklist[j].used).UseSound;
MenuSound(snd);
}
}
}
else
{
tmsg = StringTable.Localize("$SWWM_INVFAIL");
tmsgtic = gametic+70;
MenuSound("menu/noinvuse");
}
}
else if ( checklist[i].type == MenuTransaction.TT_ITEMDROP )
{
// check if drop succeeded
if ( !hnd.checklist[j].result )
{
tmsg = StringTable.Localize("$SWWM_INVNDROP");
tmsgtic = gametic+70;
MenuSound("menu/noinvuse");
}
}
else if ( checklist[i].type == MenuTransaction.TT_ITEMSEND )
{
// check if trade succeeded
if ( !hnd.checklist[j].result )
{
tmsg = StringTable.Localize("$SWWM_TRADEFULL");
tmsgtic = gametic+70;
MenuSound("menu/noinvuse");
}
else MenuSound("menu/demosel");
}
EventHandler.SendNetworkEvent("swwmcleartransaction",checklist[i].uid,consoleplayer);
deleteme = true;
break;
}
if ( !deleteme ) continue;
checklist.Delete(i);
i--;
}
}
override bool OnUiEvent( UIEvent ev )
{
switch ( ev.type )
{
case UIEvent.Type_KeyDown:
if ( ev.keychar == UiEvent.Key_F1 )
{
if ( tabs[curtab] == TAB_HELP )
{
tabs.Delete(curtab);
curtab = oldtab;
}
else
{
if ( tabs[curtab] >= TAB_SECRET )
{
tabs.Delete(curtab);
curtab--;
}
oldtab = curtab;
tabs.Push(TAB_HELP);
curtab = tabs.Size()-1;
}
sel0 = sel1 = sel2 = 0;
ofs0 = ofs1 = ofs2 = 0;
sub = false;
invlist.Clear();
lorelist.Clear();
storelist.Clear();
storeunits.Clear();
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;
}
if( (ikey[0] && (ev.keystring == mkey[0])) || (ikey[1] && (ev.keystring == mkey[1])) )
{
MenuSound("menu/democlose");
EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer);
Close();
return true;
}
break;
case UIEvent.Type_WheelDown:
if ( (tabs[curtab] == TAB_LIBRARY) && sub && (lorelist.Size() > 1) )
{
// special handling, need to check which side is being scrolled
double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.);
Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs;
Vector2 origin = (ss.x-640,ss.y-400)/2.;
Vector2 mpos = (curmouse/hs)-origin;
double midp = (lang.GetString()~=="jp")?206:126;
if ( ((lorelist.Size() > 27) && (mpos.x < midp+8)) || (mpos.x < midp) )
{
MenuSound("menu/democlose");
sub = false;
}
}
return MenuEvent(MKEY_DOWN,false);
break;
case UIEvent.Type_WheelUp:
if ( (tabs[curtab] == TAB_LIBRARY) && sub && (lorelist.Size() > 1) )
{
// special handling, need to check which side is being scrolled
double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.);
Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs;
Vector2 origin = (ss.x-640,ss.y-400)/2.;
Vector2 mpos = (curmouse/hs)-origin;
double midp = (lang.GetString()~=="jp")?206:126;
if ( ((lorelist.Size() > 27) && (mpos.x < midp+8)) || (mpos.x < midp) )
{
MenuSound("menu/democlose");
sub = false;
}
}
return MenuEvent(MKEY_UP,false);
break;
case UIEvent.Type_LButtonDown:
isrclick = false;
// copy over what base menus do for L click
// but ignore back button, since we don't use it
bool lres = MouseEvent(MOUSE_Click,ev.MouseX,ev.MouseY);
if ( lres ) SetCapture(true);
return lres;
break;
case UIEvent.Type_RButtonDown:
isrclick = true;
// same as L click
bool rres = MouseEvent(MOUSE_Click,ev.MouseX,ev.MouseY);
if ( rres ) SetCapture(true);
return rres;
break;
case UIEvent.Type_LButtonUp:
case UIEvent.Type_RButtonUp:
// copy over what base menus do for release
// but ignore back button, since we don't use it
if ( mMouseCapture )
{
SetCapture(false);
return MouseEvent(MOUSE_Release,ev.MouseX,ev.MouseY);
}
break;
case UIEvent.Type_MouseMove:
// store coords beforehand
curmouse = (ev.MouseX,ev.MouseY);
if ( mMouseCapture || (m_use_mouse == 1) )
return MouseEvent(MOUSE_Move,ev.MouseX,ev.MouseY);
break;
}
return false;
}
override void Drawer()
{
if ( isclosing ) return;
Font fnt = LangFont(TewiFont);
String str;
double hs;
if ( (Screen.GetWidth() < 640) || (Screen.GetHeight() < 400) ) hs = max(min(floor(Screen.GetWidth()/320.),floor(Screen.GetHeight()/200.)),1.)/2.;
else hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.);
Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs;
Vector2 origin = (ss.x-640,ss.y-400)/2.;
if ( !fuzz ) fuzz = CVar.GetCvar('swwm_fuzz',players[consoleplayer]);
if ( fuzz.GetBool() )
Screen.DrawTexture(FancyBg,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,.5);
Screen.DrawTexture(MainWindow,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw tabs
double xx = 3, yy = 0;
static const string tabnames[] =
{
"$SWWM_MISSTAB", "$SWWM_STATTAB", "$SWWM_INVTAB", "$SWWM_KEYTAB",
"$SWWM_KBASETAB", "$SWWM_STORETAB", "$SWWM_TRADETAB", "$SWWM_CHATTAB",
"$SWWM_SECRETTAB", "$SWWM_HELPTAB"
};
static const string ltabnames[] =
{
"$SWWM_LORETAB0", "$SWWM_LORETAB1", "$SWWM_LORETAB2"
};
static const string stabnames[] =
{
"$SWWM_STATTAB0", "$SWWM_STATTAB1", "$SWWM_STATTAB2", "$SWWM_STATTAB3"
};
for ( int i=0; i<tabs.Size(); i++ )
{
str = StringTable.Localize(tabnames[tabs[i]]);
Screen.DrawText(fnt,(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 += fnt.StringWidth(str)+2;
Screen.DrawTexture(TabSeparator,false,origin.x+xx,origin.y+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += 4;
}
int muns1, muns2;
[muns1, muns2] = SWWMCredits.Get(players[consoleplayer]);
str = "\cg¥\c-";
if ( muns2 > 0 ) str.AppendFormat("%d",muns2);
str.AppendFormat("%09d",muns1);
xx = 637-TewiFont.StringWidth(str);
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw bottom messages
yy = 386;
if ( gametic < tmsgtic ) str = StringTable.Localize(tmsg);
else str = CrimeTime();
xx = 4;
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
str = "DemolitionOS v1.0";
xx = 637-fnt.StringWidth(str);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw contents
if ( tabs[curtab] == TAB_MISSION )
{
String str;
if ( sel1 == 0 ) str = StringTable.Localize(missionstr);
else str = StringTable.Localize(missionbacklog[sel1-1]);
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
xx = 6;
yy = 17;
int ofs = clamp(sel0,0,max(0,l.Count()-28));
for ( int i=ofs; i<l.Count(); i++ )
{
if ( yy >= 370 ) break;
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,sel1?Color(96,0,0,0):Color(0,0,0,0));
yy += 13;
}
int csiz = missionbacklog.Size();
// scrollbar
if ( l.Count() > 28 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = l.Count()-28;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// page count
if ( csiz > 0 )
{
str = String.Format("%d \cf/\c- %d",sel1+1,csiz+1);
Screen.DrawText(MiniwiFont,Font.CR_FIRE,(origin.x+629)-MiniwiFont.StringWidth(str),origin.y+375,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( csiz > 0 )
{
str = String.Format("%d \cf/\c- %d",sel1+1,csiz+1);
Screen.DrawText(MiniwiFont,Font.CR_FIRE,(origin.x+637)-MiniwiFont.StringWidth(str),origin.y+375,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_STATS )
{
// categories
xx = 9;
yy = 23;
int twidth = (lang.GetString()~=="jp")?194:114;
for ( int i=0; i<=STAT_ACHIEVEMENT; i++ )
{
str = StringTable.Localize(stabnames[i]);
Screen.DrawText(fnt,(i==sel1)?Font.CR_FIRE:Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
}
xx += twidth+2;
Screen.DrawTexture(WindowSeparator,false,origin.x+xx,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// stat contents
if ( sel1 == STAT_MAIN )
{
xx += 9;
yy = 23;
int cnt = statlist.Size();
if ( cnt <= 0 )
{
str = StringTable.Localize("$SWWM_NOSTAT");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+int((634-xx)-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
int ofs = clamp(sel0,0,max(0,cnt-22));
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 370 ) break;
str = statlist[i];
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
}
// scrollbar
if ( cnt > 22 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = cnt-22;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( sel1 == STAT_KILLS )
{
xx += 9;
yy = 23;
int cnt = sorted_mstats.Size();
if ( cnt <= 0 )
{
str = StringTable.Localize("$SWWM_NOSTAT");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+int((634-xx)-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
int ofs = clamp(sel0,0,max(0,cnt-22));
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 370 ) break;
str = GetDefaultByType(sorted_mstats[i].m).GetTag(FallbackTag);
// beautify if there's no tag
if ( str == FallbackTag )
{
str = sorted_mstats[i].m.GetClassName();
SWWMUtility.BeautifyClassName(str);
}
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
str = String.Format("%d",sorted_mstats[i].kills);
int len = fnt.StringWidth(str);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+((cnt>22)?623:631)-len,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
}
// scrollbar
if ( cnt > 22 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = cnt-22;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( sel1 == STAT_LEVEL )
{
xx += 9;
yy = 23;
int cnt = stats.lstats.Size();
if ( cnt <= 0 )
{
str = StringTable.Localize("$SWWM_NOSTAT");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+int((634-xx)-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
int ofs = clamp(sel0,0,max(0,cnt-22));
let fnt2 = LangFont(MiniwiFont);
// calc lengths
int len[4], maxlen[4];
for ( int i=0; i<4; i++ ) maxlen[i] = 0;
for ( int i=0; i<cnt; i++ )
{
int sec = Thinker.Tics2Seconds(stats.lstats[i].time);
str = String.Format("T %02d:%02d:%02d",sec/3600,(sec%3600)/60,sec%60);
len[0] = fnt2.StringWidth(str);
if ( len[0] > maxlen[0] ) maxlen[0] = len[0];
str = String.Format("S %d/%d",stats.lstats[i].scount,stats.lstats[i].stotal);
len[1] = fnt2.StringWidth(str);
if ( len[1] > maxlen[1] ) maxlen[1] = len[1];
str = String.Format("I %d/%d",stats.lstats[i].icount,stats.lstats[i].itotal);
len[2] = fnt2.StringWidth(str);
if ( len[2] > maxlen[2] ) maxlen[2] = len[2];
str = String.Format("K %d/%d",stats.lstats[i].kcount,stats.lstats[i].ktotal);
len[3] = fnt2.StringWidth(str);
if ( len[3] > maxlen[3] ) maxlen[3] = len[3];
}
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 370 ) break;
str = stats.lstats[i].hub?stats.lstats[i].levelname:String.Format("%s - %s",stats.lstats[i].mapname.MakeUpper(),stats.lstats[i].levelname);
bool smallname = fnt.StringWidth(str)>(620-(xx+maxlen[3]+maxlen[2]+maxlen[1]+maxlen[0]+24));
if ( smallname ) yy += 2;
Screen.DrawText(smallname?fnt2:fnt,Font.CR_FIRE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
if ( !smallname ) yy += 2;
int ox = ((cnt>22)?623:631);
int sec = Thinker.Tics2Seconds(stats.lstats[i].time);
str = String.Format("%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt2,(sec>=3600)?Font.CR_RED:(sec<=stats.lstats[i].par)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[0],origin.y+yy,"T",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[0]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].scount,stats.lstats[i].stotal);
Screen.DrawText(fnt2,(stats.lstats[i].scount>=stats.lstats[i].stotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[1],origin.y+yy,"S",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[1]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].icount,stats.lstats[i].itotal);
Screen.DrawText(fnt2,(stats.lstats[i].icount>=stats.lstats[i].itotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[2],origin.y+yy,"I",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[2]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].kcount,stats.lstats[i].ktotal);
Screen.DrawText(fnt2,(stats.lstats[i].kcount>=stats.lstats[i].ktotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[3],origin.y+yy,"K",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 14;
}
// scrollbar
if ( cnt > 22 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = cnt-22;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( sel1 == STAT_ACHIEVEMENT )
{
xx += 9;
yy = 23;
str = StringTable.Localize("$SWWM_COMINGSOON");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+int((631-xx)-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( (tabs[curtab] == TAB_INVENTORY) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) )
{
if ( invlist.Size() <= 0 )
{
str = StringTable.Localize("$SWWM_NOINV");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
xx = 9;
yy = 23;
int longest, len;
int maxcol, totalcol, cols = 1;
// first pass, calculate longest item name
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
for ( int i=ofs; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
if ( invlist[i] is 'Ammo' ) str = String.Format("(%d/%d) %s",invlist[i].Amount,invlist[i].MaxAmount,invlist[i].GetTag());
else if ( (invlist[i].Amount > 1) || (!(invlist[i] is 'PuzzleItem') && !(invlist[i] is 'Weapon') && (invlist[i].MaxAmount > 1)) ) str = String.Format("%dx %s",invlist[i].Amount,invlist[i].GetTag());
else str = invlist[i].GetTag();
int clscol = Font.CR_WHITE;
if ( invlist[i] is 'Weapon' ) clscol = Font.CR_GOLD;
else if ( invlist[i] is 'MagAmmo' ) clscol = Font.CR_TAN;
else if ( (invlist[i] is 'BackpackItem') || (invlist[i] is 'HammerspaceEmbiggener') ) clscol = Font.CR_DARKBROWN;
else if ( invlist[i] is 'Ammo' ) clscol = Font.CR_BROWN;
else if ( (invlist[i] is 'PowerupGiver') || (invlist[i] is 'AmmoFabricator') || invlist[i].bBIGPOWERUP ) clscol = Font.CR_PURPLE;
else if ( (invlist[i] is 'Health') || (invlist[i] is 'HealthPickup') || (invlist[i] is 'SWWMHealth') ) clscol = Font.CR_RED;
else if ( (invlist[i] is 'Armor') || (invlist[i] is 'SWWMSpareArmor') ) clscol = Font.CR_GREEN;
else if ( invlist[i] is 'PuzzleItem' ) clscol = Font.CR_LIGHTBLUE;
else if ( invlist[i] is 'SWWMCollectible' ) clscol = Font.CR_FIRE;
Screen.DrawText(fnt,clscol,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
if ( maxcol < totalcol )
{
// draw scrollbar
int szr = totalcol-maxcol;
xx = floor((ofs/22.)*(630./szr))+2.;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_KEYS )
{
if ( invlist.Size() <= 0 )
{
str = StringTable.Localize("$SWWM_NOKEYS");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
xx = 9;
yy = 23;
int longest = 0, len;
int maxcol, totalcol, cols = 1;
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectible' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+24);
totalcol = int(ceil(invlist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectible' )
str.AppendFormat(" \cj(\cg¥\cf%d\cj)\c-",invlist[i].default.Stamina);
Screen.DrawText(fnt,(invlist[i] is 'SWWMCollectible')?Font.CR_PURPLE:Font.CR_UNTRANSLATED,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
if ( maxcol < totalcol )
{
// draw scrollbar
int szr = totalcol-maxcol;
xx = floor((ofs/22.)*(630./szr))+2.;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_LIBRARY )
{
xx = 3;
yy = 14;
// draw the category
int twidth = (lang.GetString()~=="jp")?200:120;
str = StringTable.Localize(ltabnames[sel1]);
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,"<",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx+twidth-6,origin.y+yy,">",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+(twidth-fnt.StringWidth(str))/2,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
// draw "unread" indicators on sides
int ltab = sel1-1;
if ( ltab < 0 ) ltab = 2;
int rtab = sel1+1;
if ( rtab > 2 ) rtab = 0;
bool lunread = false, runread = false;
for ( int i=0; i<lorelib.ent.Size(); i++ )
{
if ( lorelib.ent[i].tab == ltab ) lunread |= !lorelib.ent[i].read;
if ( lorelib.ent[i].tab == rtab ) runread |= !lorelib.ent[i].read;
}
if ( lunread ) Screen.DrawText(TewiFont,Font.CR_GOLD,origin.x+xx+6,origin.y+yy,"‼",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
if ( runread ) Screen.DrawText(TewiFont,Font.CR_GOLD,origin.x+xx+twidth-12,origin.y+yy,"‼",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture((lang.GetString()~=="jp")?LoreSeparatorW:LoreSeparator,false,origin.x,origin.y+27,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 18;
// draw the current entries
int ofs = max(0,ofs0-26);
for ( int i=ofs; i<lorelist.Size(); i++ )
{
if ( yy > 370 ) break;
str = StringTable.Localize(lorelist[i].tag);
int len = fnt.StringWidth(str);
Screen.DrawText(fnt,(i==sel0)?Font.CR_FIRE:Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
if ( !lorelist[i].read )
Screen.DrawText(TewiFont,Font.CR_GOLD,origin.x+xx+len,origin.y+yy," ‼",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 13;
}
xx += twidth+2;
Screen.DrawTexture(WindowSeparator,false,origin.x+xx,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += 3;
// scrollbar
if ( lorelist.Size() > 27 )
{
int szr = lorelist.Size()-27;
yy = floor((ofs)*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += 5;
Screen.DrawTexture(WindowSeparator,false,origin.x+xx,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx += 3;
}
xx += 4;
// draw the entry text (if open)
if ( !sub )
{
str = StringTable.Localize("$SWWM_LOREUNSEL");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+int((634-xx)-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
str = StringTable.Localize(lorelist[sel0].text);
BrokenLines l = fnt.BreakLines(str,int(635-xx));
if ( l.Count() > 28 ) l = fnt.BreakLines(str,int(626-xx));
yy = 17;
ofs = clamp(sel2,0,max(0,l.Count()-28));
for ( int i=ofs; i<l.Count(); i++ )
{
if ( yy > 370 ) break;
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 13;
}
// scrollbar
if ( l.Count() > 28 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = l.Count()-28;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_STORE )
{
if ( storelist.Size() <= 0 )
{
str = StringTable.Localize("$SWWM_NOSTORE");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
// draw all the stuff in the store
xx = 9;
yy = 23;
int longest = 0, len;
int maxcol, totalcol;
for ( int i=0; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
if ( (storeunits[i] > 1) || (storelist[i] is 'Ammo') ) str = String.Format("%dx %s",storeunits[i],def.GetTag());
else str = def.GetTag();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
maxcol = 622/(longest+96);
totalcol = int(ceil(storelist.Size()/22.));
int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22);
int cols = 1;
for ( int i=ofs; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
if ( (storeunits[i] > 1) || (storelist[i] is 'Ammo') ) str = String.Format("%dx %s",storeunits[i],def.GetTag());
else str = def.GetTag();
int clscol = Font.CR_WHITE;
if ( storelist[i] is 'Weapon' ) clscol = Font.CR_GOLD;
else if ( storelist[i] is 'Ammo' ) clscol = Font.CR_BROWN;
else if ( (storelist[i] is 'PowerupGiver') || def.bBIGPOWERUP ) clscol = Font.CR_PURPLE;
else if ( (storelist[i] is 'Health') || (storelist[i] is 'HealthPickup') || (storelist[i] is 'SWWMHealth') ) clscol = Font.CR_RED;
else if ( (storelist[i] is 'Armor') || (storelist[i] is 'SWWMSpareArmor') ) clscol = Font.CR_GREEN;
Screen.DrawText(fnt,clscol,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
xx += longest+96;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
xx = 9;
yy = 23;
cols = 1;
for ( int i=ofs; i<storelist.Size(); i++ )
{
if ( !storelist[i] ) continue;
let def = GetDefaultByType(storelist[i]);
int price = int(def.Stamina*(1.+.75*(storeunits[i]-1)));
str = String.Format("¥%d",price);
len = TewiFont.StringWidth(str);
int clscol = Font.CR_FIRE;
if ( (price > muns1) && (muns2 <= 0) ) clscol = Font.CR_BLACK;
Screen.DrawText(TewiFont,clscol,origin.x+xx+longest+80-len,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
xx += longest+96;
yy = 23;
cols++;
if ( cols > maxcol ) break;
}
}
if ( maxcol < totalcol )
{
// draw scrollbar
int szr = totalcol-maxcol;
xx = floor((ofs/22.)*(630./szr))+2.;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_TRADING )
{
if ( !sub )
{
if ( playerlist.Size() <= 0 )
{
str = StringTable.Localize("$SWWM_NOTRADE");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
xx = 9;
yy = 23;
int longest = 0, len;
int ofs = int(floor(max(0,sel0-44)/22.)*22);
int cols = 1;
for ( int i=ofs; i<playerlist.Size(); i++ )
{
str = players[playerlist[i]].GetUserName();
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
xx += longest+24;
yy = 23;
longest = 0;
cols++;
if ( cols > 3 ) break;
}
}
if ( playerlist.Size() > 65 )
{
// draw scrollbar
int szr = (playerlist.Size()/22)-2;
xx = floor((ofs/22)*(630./szr))+2;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+373,"▬",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture(WindowSeparatorH,false,origin.x,origin.y+377,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( sub )
{
if ( sel0 == -1 )
{
if ( tradelib.ent.Size() <= 0 )
{
str = StringTable.Localize("$SWWM_NOTRADEHIST");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
xx = 6;
yy = 17;
int ofs = clamp(sel1,0,max(0,tradelib.ent.Size()-28));
for ( int i=ofs; i<tradelib.ent.Size(); i++ )
{
if ( yy >= 370 ) break;
let ent = tradelib.ent[tradelib.ent.Size()-(i+1)];
int thour = (ent.timestamp/(3600*Thinker.TICRATE));
int tmin = (ent.timestamp/(60*Thinker.TICRATE))%60;
int tsec = (ent.timestamp/Thinker.TICRATE)%60;
str = String.Format("\cu[\cc%02d\cu:\cc%02d\cu:\cc%02d\cu]\c- ",thour,tmin,tsec);
str.AppendFormat("\cd%s %s\cd: \cj%dx \cf%s\c-",ent.type?StringTable.Localize("$SWWM_TRADEFROM"):StringTable.Localize("$SWWM_TRADETO"),ent.other,ent.amt,GetDefaultByType(ent.what).GetTag());
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 13;
}
// scrollbar
if ( tradelib.ent.Size() > 28 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = tradelib.ent.Size()-28;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
}
}
else if ( tabs[curtab] == TAB_CHAT )
{
let bar = SWWMStatusBar(StatusBar);
if ( !bar || (bar.FullHistory.Size() <= 0) )
{
str = StringTable.Localize("$SWWM_NOCHAT");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+(400-fnt.GetHeight())/2,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
int margin = TewiFont.StringWidth("[00:00:00] ");
int ofs = bar.FullHistory.Size()-(1+sel0);
xx = 3;
yy = 379;
for ( int i=ofs; i>=0; i-- )
{
int col = (bar.FullHistory[i].type==PRINT_TEAMCHAT)?tcol.GetInt():ccol.GetInt();
int thour = (bar.FullHistory[i].tic/(3600*Thinker.TICRATE));
int tmin = (bar.FullHistory[i].tic/(60*Thinker.TICRATE))%60;
int tsec = (bar.FullHistory[i].tic/Thinker.TICRATE)%60;
str = String.Format("\cu[\c-%02d\cu:\c-%02d\cu:\c-%02d\cu]\c- ",thour,tmin,tsec);
BrokenLines l = fnt.BreakLines(bar.FullHistory[i].str,626-margin);
double by = yy-13*l.Count();
if ( by < 17 ) break;
Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+by,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
for ( int j=0; j<l.Count(); j++ )
Screen.DrawText(fnt,col,origin.x+xx+margin,origin.y+by+j*13,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy = by;
}
// scrollbar
if ( bar.FullHistory.Size() > 1 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = bar.FullHistory.Size()-1;
yy = floor((szr-sel0)*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
else if ( tabs[curtab] == TAB_SECRET )
{
Screen.DrawTexture(EasterEgg,false,origin.x+20,origin.y+40,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
str = StringTable.Localize("$SWWM_TODEMO");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+350,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else if ( tabs[curtab] == TAB_HELP )
{
// help tab
int k1, k2;
[k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu");
String kstr = bindings.NameKeys(k1,k2);
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
if ( multiplayer ) str = str..StringTable.Localize("$SWWM_HELPTXT_MP");
BrokenLines l = fnt.BreakLines(str,629);
if ( l.Count() > 28 ) l = fnt.BreakLines(str,620);
xx = 6;
yy = 17;
int ofs = clamp(sel0,0,max(0,l.Count()-28));
for ( int i=ofs; i<l.Count(); i++ )
{
if ( yy >= 370 ) break;
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 13;
}
// scrollbar
if ( l.Count() > 28 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = l.Count()-28;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
}
}
}