swwmgz_m/zscript/kbase/swwm_kbase.zsc
Marisa Kirisame 1e82cf6b09 Remove the 'Mx. Worldwide' achievement, it can't work due to cvars being split between games.
Adjust how the 'Full Set' achievement is handled, for the same reasons.
Don't split the Demolitionist Menu help text, it just complicates code.
2021-03-24 18:10:17 +01:00

3622 lines
123 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 DemolitionistMenu : 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, Drawing[3], LoreSeparator, LoreSeparatorW, AchievementUnknown;
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
Array<LevelStat> filtered_lstats; // filtered if necessary + entry for current map
LevelStat curlstat;
Array<SWWMAchievement> achievements; // array created once, then sorted as needed
// 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];
// 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;
// to save on calls
bool nrftl, eviternity, hexdd;
// returns MPlus if we're playing in Japanese, otherwise returns the requested font
Font LangFont( Font req )
{
if ( language ~== "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);
Drawing[0] = TexMan.CheckForTexture("graphics/KBase/Drawing_Saya.png",TexMan.Type_MiscPatch);
Drawing[1] = TexMan.CheckForTexture("graphics/KBase/Drawing_Ibuki.png",TexMan.Type_MiscPatch);
Drawing[2] = TexMan.CheckForTexture("graphics/KBase/Drawing_Kirin.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("event swwmdemomenu");
mkey[0] = Bindings.NameKeys(ikey[0],0);
mkey[1] = Bindings.NameKeys(ikey[1],0);
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
nrftl = false;
eviternity = false;
hexdd = false;
if ( (gameinfo.gametype&GAME_Doom) && SWWMUtility.IsKnownMap() )
{
int clus = level.cluster;
if ( clus == 11 ) nrftl = true;
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);
}
// naive method to guess if this is sigil
else if ( (clus == 5) && (level.mapname.Left(2) == "E5") )
missionstr = String.Format("$SWWM_MISSION_SIGIL");
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 we came from the doom 1 episodes, use the alt mission string for Doom 2
bool fromdoomone = false;
for ( int i=0; i<csiz; i++ )
{
// also include sigil
if ( !eviternity && (stats.clustervisit[i] >= 5) && (stats.clustervisit[i] != 25) ) continue;
fromdoomone = true;
break;
}
if ( !eviternity && (clus == 5) && fromdoomone )
missionstr = "$SWWM_MISSION_DOOM5_FROMDOOM1";
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 ( !eviternity && (stats.clustervisit[i] == 5) && fromdoomone )
xstr = "$SWWM_MISSION_DOOM5_FROMDOOM1";
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() )
{
// detect deathkings in the most amazing way imaginable
int mapnamenum = level.mapname.Mid(3).ToInt(10);
if ( mapnamenum > 40 ) hexdd = true;
String gstr = hexdd?"HEXDD":"HEXEN";
missionstr = String.Format("$SWWM_MISSION_%s%d",gstr,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_%s%d",gstr,stats.clustervisit[i]);
if ( StringTable.Localize(missionstr) ~== missionstr.Mid(1) )
continue;
missionbacklog.Push(xstr);
}
}
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";
if ( hexdd )
{
// deathkings happen the day after
c_day = 25;
c_hour = 10;
c_minute = 28;
}
}
else if ( eviternity )
{
// June 10th 2150, 20:09 +09
// (June 10th 2150, 20:09 JST)
// Epoch: 5694145740
c_year = 2150;
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 ( nrftl )
{
// NRFTL just happens the next day after
c_day = 6;
c_hour = 15;
c_minute = 48;
}
}
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);
if ( !deathmatch && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4) ) 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 = swwm_lasttab;
for ( int i=0; i<tabs.Size(); i++ )
{
if ( tabs[i] != lasttab ) continue;
curtab = i;
break;
}
if ( tabs[curtab] == TAB_LIBRARY ) sel1 = swwm_lastloretab;
else if ( tabs[curtab] == TAB_STATS ) sel1 = swwm_laststattab;
// load achievements
SWWMUtility.LoadAchievements(achievements,true);
AchievementUnknown = TexMan.CheckForTexture("graphics/Achievements/AchievementUnknown.png",TexMan.Type_Any);
}
// 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/GameTicRate;
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.FindCVar('swwm_lasttab').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.FindCVar('swwm_lasttab').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 kstr;
if ( mkey[1] != "" ) kstr = mkey[0].."/"..mkey[1];
else kstr = mkey[0];
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
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 = (language~=="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, num = 22;
if ( sel1 == STAT_MAIN )
cnt = statlist.Size();
else if ( sel1 == STAT_KILLS )
cnt = sorted_mstats.Size();
else if ( sel1 == STAT_LEVEL )
cnt = filtered_lstats.Size();
else if ( sel1 == STAT_ACHIEVEMENT )
{
cnt = achievements.Size();
num = 7;
}
if ( (cnt > num) && (sel0 < cnt-num) )
{
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.FindCVar('swwm_lastloretab').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.FindCVar('swwm_laststattab').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.FindCVar('swwm_lastloretab').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.FindCVar('swwm_laststattab').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(abs(itm.Stamina)*(1.+.75*(amt-1)));
let cur = players[consoleplayer].mo.FindInventory(storelist[sel0]);
if ( sub )
{
if ( !cur ) return true;
if ( storelist[sel0] is 'Weapon' )
{
let w = Weapon(itm);
// subtract price of given ammo, as we're only selling the weapon itself
if ( w.AmmoType1 && (w.AmmoGive1 > 0) )
{
let am1 = GetDefaultByType(w.AmmoType1);
if ( am1.Stamina > 0 ) price -= int(am1.Stamina*(1.+.75*(w.AmmoGive1-1)));
}
// candygun is a special case for secondary ammo
if ( w.AmmoType2 && (w.AmmoGive2 > 0) && (storelist[sel0] is 'CandyGun') )
{
let am2 = GetDefaultByType(w.AmmoType2);
if ( am2.Stamina > 0 ) price -= int(am2.Stamina*(1.+.75*(w.AmmoGive2-1)));
}
}
// sell at half price
if ( sub ) price /= 2;
EventHandler.SendNetworkEvent(String.Format("swwmstoretake.%s",storelist[sel0].GetClassName()),consoleplayer,price,amt);
MenuSound("menu/buyinv");
return true;
}
if ( !SWWMCredits.CanTake(players[consoleplayer],price) )
{
MenuSound("menu/noinvuse");
tmsg = StringTable.Localize("$SWWM_STOREMUNS");
tmsgtic = gametic+70;
return true;
}
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);
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) && !(invlist[sel1] is 'HammerspaceEmbiggener'))
|| ((invlist[sel1] is 'CandyGunSpares') && !players[playerlist[sel0]].mo.FindInventory('CandyGun')) )
{
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_STORE )
{
sub = !sub;
MenuSound(sub?"menu/demosel":"menu/democlose");
}
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.FindCVar('swwm_lasttab').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;
}
}
return res;
}
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;
}
}
return res;
}
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;
}
}
return res;
}
// 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 )
{
if ( isrclick )
{
MenuEvent(MKEY_CLEAR,false);
return res;
}
// 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 ( sub )
{
int amt = (storelist[i] is 'CandyGun')?(players[consoleplayer].mo.CountInv("CandyGunSpares")+1):players[consoleplayer].mo.CountInv(storelist[i]);
if ( (amt > 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,def.GetTag());
else str = def.GetTag();
}
else 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;
}
}
return res;
}
// 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 = (language~=="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 = (language~=="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 )
{
if ( isrclick )
{
MenuEvent(MKEY_CLEAR,false);
return res;
}
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;
}
}
return res;
}
// 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?
String kstr;
if ( mkey[1] != "" ) kstr = mkey[0].."/"..mkey[1];
else kstr = mkey[0];
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 = (language~=="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.FindCVar('swwm_laststattab').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, num = 22;
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = filtered_lstats.Size();
else if ( sel1 == STAT_ACHIEVEMENT )
{
cnt = achievements.Size();
num = 7;
}
if ( cnt <= num ) return res; // no scrollbar
// calculate offset
int szr = cnt-num;
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 ( sub )
{
int amt = (storelist[i] is 'CandyGun')?(players[consoleplayer].mo.CountInv("CandyGunSpares")+1):players[consoleplayer].mo.CountInv(storelist[i]);
if ( (amt > 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,def.GetTag());
else str = def.GetTag();
}
else 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 = (language~=="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, num = 22;
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = filtered_lstats.Size();
else if ( sel1 == STAT_ACHIEVEMENT )
{
cnt = achievements.Size();
num = 7;
}
if ( cnt <= num ) return res; // no scrollbar
// calculate offset
int szr = cnt-num;
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
int vala = abs(a.default.Stamina);
int valb = abs(b.default.Stamina);
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
int vala = GetDefaultByType(pamoa).Stamina;
int valb = GetDefaultByType(pamob).Stamina;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
return slota > slotb;
}
}
else
{
// sort by unit value
int vala = abs(a.default.Stamina);
int valb = abs(b.default.Stamina);
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 = 4;
else if ( a is 'HammerspaceEmbiggener' ) ta = 3;
else if ( a is 'Ammo' ) ta = 2;
else if ( a is 'MagAmmo' ) ta = 1;
else if ( (a is 'PowerupGiver') || (a is 'AmmoFabricator') || da.bBIGPOWERUP ) ta = 5;
else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = 7;
else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = 6;
if ( b is 'Weapon' ) tb = 4;
else if ( b is 'HammerspaceEmbiggener' ) tb = 3;
else if ( b is 'Ammo' ) tb = 2;
else if ( b is 'MagAmmo' ) ta = 1;
else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || db.bBIGPOWERUP ) tb = 5;
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = 7;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = 6;
if ( ta == tb )
{
// sort by unit price
if ( abs(da.Stamina) == abs(db.Stamina) )
{
// sort alphabetically
return da.GetTag() > db.GetTag();
}
return abs(da.Stamina) > abs(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);
}
private bool CmpAchievement( SWWMAchievement a, SWWMAchievement b )
{
bool adone = !!(a.state.GetInt()), bdone = !!(b.state.GetInt());
double afactor = adone?1.:0., bfactor = bdone?1.:0.;
if ( a.progress )
{
int cur = clamp(a.progress.GetInt(),0,a.maval);
afactor = cur/double(a.maxval);
}
if ( b.progress )
{
int cur = clamp(b.progress.GetInt(),0,b.maxval);
bfactor = cur/double(b.maxval);
}
if ( adone && bdone )
{
// alphabetic sort
String tag1 = StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TAG"),
tag2 = StringTable.Localize("$SWWM_ACHIEVEMENT_"..b.basename.."_TAG");
return tag1 > tag2;
}
if ( !adone && !bdone )
{
// progress sort?
if ( afactor != bfactor )
return afactor < bfactor;
// alphabetic sort
String tag1 = StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TAG"),
tag2 = StringTable.Localize("$SWWM_ACHIEVEMENT_"..b.basename.."_TAG");
return tag1 > tag2;
}
// state sort
return bdone;
}
private int partition_achievements( Array<SWWMAchievement> a, int l, int h )
{
SWWMAchievement pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpAchievement(pv,a[j]) )
{
i++;
SWWMAchievement tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMAchievement tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_achievements( Array<SWWMAchievement> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_achievements(a,l,h);
qsort_achievements(a,l,p-1);
qsort_achievements(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 ( swwm_menupause ) menuactive = Menu.On;
else menuactive = Menu.OnNoPause;
// forcibly tick hud (mainly so interpolators can still update in the background)
if ( !multiplayer && (menuactive == Menu.On) )
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 ) continue;
invlist.Push(k);
}
// because we can't call GetReplacement from ui, let's wrangle something real ugly here to clean up replaced keys
for ( int i=0; i<invlist.Size(); i++ )
{
if ( !(invlist[i] is 'SWWMKey') ) continue;
// remove the key this replaces
Class<Key> pc = invlist[i].Species;
if ( !pc ) continue;
for ( int j=0; j<invlist.Size(); j++ )
{
if ( invlist[j].GetClass() != pc ) continue;
invlist.Delete(j);
j--;
if ( i >= j ) i--;
}
}
// 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 )
{
Class<Inventory> lastsel = null;
if ( storelist.Size() > 0 ) lastsel = storelist[sel0];
storelist.Clear();
storeunits.Clear();
// price-sorted buyables/sellables
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (Class<Inventory>)(AllActorClasses[i]);
if ( !type ) continue;
// no collectibles
if ( type is 'SWWMCollectible' ) continue;
// can't sell candygun spares
if ( sub && (type is 'CandyGunSpares') ) continue;
let cur = players[consoleplayer].mo.FindInventory(type);
// skip items we don't own or are depleted if selling
if ( sub && (!cur || (cur.Amount <= 0)) ) continue;
else if ( !sub )
{
// skip maxed items if buying
if ( cur && (cur.Amount >= cur.MaxAmount) ) continue;
// ignore ammo for weapons not owned if buying
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;
// items with negative stamina can only be sold
if ( (inv.Stamina < 0) && !sub ) continue;
int amt = inv.Amount;
int price = int(abs(inv.Stamina)*(1.+.75*(amt-1)));
if ( type is 'Ammo' )
{
int maxamt;
if ( sub ) maxamt = cur.Amount; // we can sell ALL our ammo
else maxamt = cur?(cur.MaxAmount-cur.Amount):inv.MaxAmount;
// get the largest affordable child pickup amount (that we need, or we can sell)
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(abs(inv.Stamina)*(1.+.75*(inv2.Amount-1)));
if ( (inv2.Amount > amt) && (inv2.Amount <= maxamt) && (sub || SWWMCredits.CanTake(players[consoleplayer],cprice)) )
{
price = cprice;
amt = inv2.Amount;
}
}
}
// nuggets can be bought/sold in bulk
else if ( type is 'HealthNuggetItem' )
{
let inv2 = GetDefaultByType(SWWMHealth(inv).giveme);
int maxamt;
if ( sub ) maxamt = cur.Amount;
else
{
maxamt = (inv2.MaxAmount-players[consoleplayer].Health);
maxamt += cur?(cur.MaxAmount-cur.Amount):inv.MaxAmount;
}
for ( int j=5; j<=20; j+=5 )
{
int cprice = int(inv.Stamina*(1.+.75*(j-1)));
if ( (j <= maxamt) && (sub || 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 maxamt;
if ( sub ) maxamt = cur.Amount;
else
{
maxamt = cur2?(cur2.MaxAmount-cur2.Amount):inv2.MaxAmount;
maxamt += cur?(cur.MaxAmount-cur.Amount):inv.MaxAmount;
}
for ( int j=5; j<=20; j+=5 )
{
int cprice = int(inv.Stamina*(1.+.75*(j-1)));
if ( (j <= maxamt) && (sub || 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 ( abs(inv.Stamina) > abs(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);
// shift back if the last selected item was removed
if ( lastsel && (storelist.Find(lastsel)) >= storelist.Size() )
sel0 = max(0,sel0-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
int oldnplayers = playerlist.Size();
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);
}
if ( (playerlist.Size() != oldnplayers) && sub && (sel0 != -1) )
{
// go back, the player list changed
MenuSound("menu/democlose");
sub = false;
ofs0 = sel0 = 0;
}
// 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*GameTicRate));
int tmin = ((level.totaltime-stats.lastspawn)/(60*GameTicRate))%60;
int tsec = ((level.totaltime-stats.lastspawn)/GameTicRate)%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,StringTable.Localize("$SWWM_UNIT_LITER"));
statlist.Push(str);
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSPEED"),(stats.topspeed*3600.*GameTicRate)/32000.,StringTable.Localize("$SWWM_UNIT_KPH"));
statlist.Push(str);
thour = (stats.airtime/(3600*GameTicRate));
tmin = (stats.airtime/(60*GameTicRate))%60;
tsec = (stats.airtime/GameTicRate)%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_STATPARRY"),stats.parries);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATPPARRY"),stats.pparries);
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_STATPATS"),stats.pats);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATKISS"),stats.smooch);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATFRIENDS"),stats.befriend);
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 == 'SWWMItemGesture' ) str = str..StringTable.Localize("$T_FROGGY");
else if ( stats.wstats[stats.favweapon].w == 'Weapon' ) str = str..StringTable.Localize("$SWWM_GRAVKILL");
else if ( stats.wstats[stats.favweapon].w == 'DoomWeapon' ) str = str..StringTable.Localize("$SWWM_PARRYKILL");
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);
}
else if ( (tabs[curtab] == TAB_STATS) && (sel1 == STAT_LEVEL) )
{
filtered_lstats.Clear();
bool shouldfilter = swwm_uniqstats;
for ( int i=0; i<stats.lstats.Size(); i++ )
{
for ( int j=0; j<filtered_lstats.Size(); j++ )
{
if ( !shouldfilter || ((filtered_lstats[j].mapname != stats.lstats[i].mapname) && (filtered_lstats[j].mapname != level.mapname)) ) continue;
filtered_lstats.Delete(j);
j--;
}
filtered_lstats.Push(stats.lstats[i]);
}
if ( !curlstat ) curlstat = new("LevelStat");
curlstat.hub = !!(level.clusterflags&level.CLUSTER_HUB);
curlstat.levelname = level.levelname;
int iof = curlstat.levelname.IndexOf(" - by: ");
if ( iof != -1 ) curlstat.levelname.Truncate(iof);
curlstat.mapname = level.mapname;
curlstat.kcount = level.killed_monsters;
curlstat.ktotal = level.total_monsters;
curlstat.icount = level.found_items;
curlstat.itotal = level.total_items;
curlstat.scount = level.found_secrets;
curlstat.stotal = level.total_secrets;
curlstat.time = level.maptime;
curlstat.par = level.partime;
curlstat.suck = level.sucktime;
filtered_lstats.Push(curlstat);
}
else if ( (tabs[curtab] == TAB_STATS) && (sel1 == STAT_ACHIEVEMENT) )
qsort_achievements(achievements,0,achievements.Size()-1);
// 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
// if it's a collectible or key, play the "hands up" sound
let w = (Class<Weapon>)(hnd.checklist[j].used);
if ( w )
{
// play if actually switching
if ( hnd.checklist[j].result )
{
String snd = GetDefaultByType(w).UpSound;
MenuSound(snd);
}
}
else if ( (hnd.checklist[j].used is 'SWWMCollectible') || (hnd.checklist[j].used is 'SWWMKey') )
MenuSound("demolitionist/handsup");
else
{
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 = (language~=="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 = (language~=="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 ( swwm_fuzz )
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 = (language~=="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 = filtered_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(filtered_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",filtered_lstats[i].scount,filtered_lstats[i].stotal);
len[1] = fnt2.StringWidth(str);
if ( len[1] > maxlen[1] ) maxlen[1] = len[1];
str = String.Format("I %d/%d",filtered_lstats[i].icount,filtered_lstats[i].itotal);
len[2] = fnt2.StringWidth(str);
if ( len[2] > maxlen[2] ) maxlen[2] = len[2];
str = String.Format("K %d/%d",filtered_lstats[i].kcount,filtered_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 = filtered_lstats[i].hub?filtered_lstats[i].levelname:String.Format("%s - %s",filtered_lstats[i].mapname.MakeUpper(),filtered_lstats[i].levelname);
if ( filtered_lstats[i] == curlstat ) str = "\cd▸\c- "..str;
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(filtered_lstats[i].time);
str = String.Format("%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt2,((filtered_lstats[i].suck>0)&&(sec>=(filtered_lstats[i].suck*3600)))?Font.CR_RED:(sec<=filtered_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",filtered_lstats[i].scount,filtered_lstats[i].stotal);
Screen.DrawText(fnt2,(filtered_lstats[i].scount>=filtered_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",filtered_lstats[i].icount,filtered_lstats[i].itotal);
Screen.DrawText(fnt2,(filtered_lstats[i].icount>=filtered_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",filtered_lstats[i].kcount,filtered_lstats[i].ktotal);
Screen.DrawText(fnt2,(filtered_lstats[i].kcount>=filtered_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 += 12;
yy = 26;
int cnt = achievements.Size();
int ofs = clamp(sel0,0,max(0,cnt-7));
let fnt2 = LangFont(MiniwiFont);
int barsz = (language~=="jp")?400:480;
int txtsz = (language~=="jp")?360:440;
bool obscure = (swwm_filterachievements==1);
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 360 ) break;
let a = achievements[i];
bool completed = !!a.state.GetInt();
bool hasprogress = (a.progress && a.progress.GetInt());
Screen.DrawTexture((!completed&&!hasprogress&&obscure)?AchievementUnknown:a.icon,false,origin.x+xx+1,origin.y+yy+1,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_FillColor,(!completed&&!hasprogress&&obscure)?Color(8,8,8):Color(16,16,16));
Screen.DrawTexture((!completed&&!hasprogress&&obscure)?AchievementUnknown:a.icon,false,origin.x+xx,origin.y+yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Desaturate,(!completed)*255,DTA_ColorOverlay,completed?Color(0,0,0,0):(hasprogress||!obscure)?Color(96,0,0,0):Color(192,0,0,0));
// progress bar
if ( a.progress )
{
int val = a.progress.GetInt();
val = clamp(val,0,a.maxval);
if ( completed ) str = SWWMUtility.BlockBar(a.maxval,a.maxval,barsz,Font.CR_DARKGREEN,Font.CR_BLACK);
else str = SWWMUtility.BlockBar(val,a.maxval,barsz,Font.CR_DARKGRAY,Font.CR_BLACK);
Screen.DrawText(TewiFont,Font.CR_UNTRANSLATED,origin.x+xx,origin.y+yy+34,str,DTA_Spacing,-5,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
if ( completed ) str = String.Format("%s / %s",SWWMUtility.ThousandsNum(a.maxval),SWWMUtility.ThousandsNum(a.maxval));
else str = String.Format("%s / %s",SWWMUtility.ThousandsNum(val),SWWMUtility.ThousandsNum(a.maxval));
int ox = (barsz-MiniwiFont.StringWidth(str))/2;
Screen.DrawText(MiniwiFont,completed?Font.CR_GREEN:hasprogress?Font.CR_WHITE:Font.CR_DARKGRAY,origin.x+xx+ox,origin.y+yy+37,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else
{
str = SWWMUtility.BlockBar(completed,1,barsz,Font.CR_DARKGREEN,Font.CR_BLACK);
Screen.DrawText(TewiFont,Font.CR_UNTRANSLATED,origin.x+xx,origin.y+yy+34,str,DTA_Spacing,-5,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
// the text itself
str = a.hasformat?String.Format(StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT"),a.maxval):StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT");
if ( !completed && !hasprogress && obscure ) SWWMUtility.ObscureText(str,(gametic/3)+i*2);
BrokenLines l = fnt2.BreakLines(str,txtsz);
str = StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TAG");
if ( !completed && !hasprogress && obscure ) SWWMUtility.ObscureText(str,(gametic/3)+i*2+1);
int oy = (32-(14+(9*l.Count())))/2;
Screen.DrawText(fnt,completed?Font.CR_GREEN:Font.CR_DARKGRAY,origin.x+xx+36,origin.y+yy+oy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
for ( int j=0; j<l.Count(); j++ )
Screen.DrawText(fnt2,completed?Font.CR_WHITE:Font.CR_BLACK,origin.x+xx+40,origin.y+yy+oy+14+j*9,l.StringAt(j),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 50;
}
// scrollbar
if ( cnt > 7 )
{
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-7;
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_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 = (language~=="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((language~=="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(sub?"$SWWM_NOSTORESELL":"$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 ( sub )
{
int amt = (storelist[i] is 'CandyGun')?(players[consoleplayer].mo.CountInv("CandyGunSpares")+1):players[consoleplayer].mo.CountInv(storelist[i]);
if ( (amt > 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,def.GetTag());
else str = def.GetTag();
}
else 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 ( sub )
{
int amt = (storelist[i] is 'CandyGun')?(players[consoleplayer].mo.CountInv("CandyGunSpares")+1):players[consoleplayer].mo.CountInv(storelist[i]);
if ( (amt > 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,def.GetTag());
else str = def.GetTag();
}
else 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 'MagAmmo' ) clscol = Font.CR_TAN;
else if ( (storelist[i] is 'BackpackItem') || (storelist[i] is 'HammerspaceEmbiggener') ) clscol = Font.CR_DARKBROWN;
else if ( storelist[i] is 'Ammo' ) clscol = Font.CR_BROWN;
else if ( (storelist[i] is 'PowerupGiver') || (storelist[i] is 'AmmoFabricator') || 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(abs(def.Stamina)*(1.+.75*(storeunits[i]-1)));
if ( sub && (storelist[i] is 'Weapon') )
{
let w = Weapon(def);
// subtract price of given ammo, as we're only selling the weapon itself
if ( w.AmmoType1 && (w.AmmoGive1 > 0) )
{
let am1 = GetDefaultByType(w.AmmoType1);
if ( am1.Stamina > 0 ) price -= int(am1.Stamina*(1.+.75*(w.AmmoGive1-1)));
}
// candygun is a special case for secondary ammo
if ( w.AmmoType2 && (w.AmmoGive2 > 0) && (storelist[i] is 'CandyGun') )
{
let am2 = GetDefaultByType(w.AmmoType2);
if ( am2.Stamina > 0 ) price -= int(am2.Stamina*(1.+.75*(w.AmmoGive2-1)));
}
}
// sell at half price
if ( sub ) price /= 2;
str = String.Format("¥%d",price);
len = TewiFont.StringWidth(str);
int clscol = Font.CR_FIRE;
if ( sub ) clscol = Font.CR_GREEN;
else 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*GameTicRate));
int tmin = (ent.timestamp/(60*GameTicRate))%60;
int tsec = (ent.timestamp/GameTicRate)%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)?msg4color:msg3color;
int thour = (bar.FullHistory[i].tic/(3600*GameTicRate));
int tmin = (bar.FullHistory[i].tic/(60*GameTicRate))%60;
int tsec = (bar.FullHistory[i].tic/GameTicRate)%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 )
{
if ( gameinfo.gametype&GAME_Hexen )
{
Screen.DrawTexture(Drawing[2],false,origin.x+20,origin.y+40,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
str = StringTable.Localize("$SWWM_KIRINPOEM");
BrokenLines l = fnt.BreakLines(str,300);
int mxlen = 0;
for ( int i=0; i<l.Count(); i++ )
{
if ( l.StringWidth(i) > mxlen ) mxlen = l.StringWidth(i);
xx = (i<l.Count()-1)?350:(350+mxlen-l.StringWidth(i));
Screen.DrawText(fnt,Font.CR_GOLD,origin.x+xx,origin.y+43+i*14,l.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
str = StringTable.Localize("$SWWM_FROMKIRIN");
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+350,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else if ( gameinfo.gametype&GAME_Heretic )
{
Screen.DrawTexture(Drawing[1],false,origin.x+20,origin.y+40,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
str = StringTable.Localize("$SWWM_CUTIECLUB");
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+(640-fnt.StringWidth(str))/2,origin.y+350,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
else
{
Screen.DrawTexture(Drawing[0],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_WHITE,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
String kstr;
if ( mkey[1] != "" ) kstr = mkey[0].."/"..mkey[1];
else kstr = mkey[0];
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
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);
}
}
}
}