swwmgz_m/zscript/kbase/swwm_kbase.zsc

4174 lines
137 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;
}
Class MenuAchievement
{
String tag, txt;
TextureID icon;
CVar state, progress;
int num, maxval;
}
// 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<MenuAchievement> 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
LoadAchievements();
}
// this is very spaghetti, sorry
private void LoadAchievements()
{
// cvar assigned
static const Name vars[] =
{
'swwm_achievement_gcsandwich',
'swwm_achievement_hdoom',
'swwm_achievement_onestanding',
'swwm_achievement_oopsie',
'swwm_achievement_ghost',
'swwm_achievement_clonk',
'swwm_achievement_allitems',
'swwm_achievement_conga',
'swwm_achievement_explogun',
'swwm_achievement_gravity',
'swwm_achievement_flight',
'swwm_achievement_cliffyb',
'swwm_achievement_par',
'swwm_achievement_oneguy',
'swwm_achievement_golden',
'swwm_achievement_gib',
'swwm_achievement_barrier',
'swwm_achievement_bossdash',
'swwm_achievement_sneeze',
'swwm_achievement_fuel',
'swwm_achievement_slayer',
'swwm_achievement_broccolidoccoli',
'swwm_achievement_dime',
'swwm_achievement_stomp',
'swwm_achievement_cheat',
'swwm_achievement_god',
'swwm_achievement_thruwall',
'swwm_achievement_mashiro',
'swwm_achievement_lead',
'swwm_achievement_love',
'swwm_achievement_butts',
'swwm_achievement_plush',
'swwm_achievement_bustin',
'swwm_achievement_moth',
'swwm_achievement_iwad',
'swwm_achievement_brake',
'swwm_achievement_rage',
'swwm_achievement_abort',
'swwm_achievement_parry',
'swwm_achievement_allkills',
'swwm_achievement_allsecrets',
'swwm_achievement_reflect',
'swwm_achievement_sekiro',
'swwm_achievement_wave',
'swwm_achievement_friend',
'swwm_achievement_shock',
'swwm_achievement_balls',
'swwm_achievement_refresh',
'swwm_achievement_step',
'swwm_achievement_sunny',
'swwm_achievement_thicc',
'swwm_achievement_everything',
'swwm_achievement_allcoll',
'swwm_achievement_slemg',
'swwm_achievement_dosh',
'swwm_achievement_jump',
'swwm_achievement_ezkill',
'swwm_achievement_ligma',
'swwm_achievement_yeet',
'swwm_achievement_mbf',
'swwm_achievement_lightning',
'swwm_achievement_dakka',
'swwm_achievement_roast',
'swwm_achievement_dab',
'swwm_achievement_hnd',
'swwm_achievement_gepgun',
'swwm_achievement_fuller',
'swwm_achievement_pene',
'swwm_achievement_acid',
'swwm_achievement_salt',
'swwm_achievement_mega',
'swwm_achievement_sanic',
'swwm_achievement_tele',
'swwm_achievement_bune',
'swwm_achievement_bonk',
'swwm_achievement_anom',
'swwm_achievement_anone',
'swwm_achievement_tetris',
'swwm_achievement_pong',
'swwm_achievement_snake',
'swwm_achievement_smb',
'swwm_achievement_tyrian',
'swwm_achievement_bof',
'swwm_achievement_wantdie'
};
// localized name
static const String names[] =
{
"$SWWM_ACHIEVEMENT_GCSANDWICH",
"$SWWM_ACHIEVEMENT_HDOOM",
"$SWWM_ACHIEVEMENT_ONESTANDING",
"$SWWM_ACHIEVEMENT_OOPSIE",
"$SWWM_ACHIEVEMENT_GHOST",
"$SWWM_ACHIEVEMENT_CLONK",
"$SWWM_ACHIEVEMENT_ALLITEMS",
"$SWWM_ACHIEVEMENT_CONGA",
"$SWWM_ACHIEVEMENT_EXPLOGUN",
"$SWWM_ACHIEVEMENT_GRAVITY",
"$SWWM_ACHIEVEMENT_FLIGHT",
"$SWWM_ACHIEVEMENT_CLIFFYB",
"$SWWM_ACHIEVEMENT_PAR",
"$SWWM_ACHIEVEMENT_ONEGUY",
"$SWWM_ACHIEVEMENT_GOLDEN",
"$SWWM_ACHIEVEMENT_GIB",
"$SWWM_ACHIEVEMENT_BARRIER",
"$SWWM_ACHIEVEMENT_BOSSDASH",
"$SWWM_ACHIEVEMENT_SNEEZE",
"$SWWM_ACHIEVEMENT_FUEL",
"$SWWM_ACHIEVEMENT_SLAYER",
"$SWWM_ACHIEVEMENT_BROCCOLIDOCCOLI",
"$SWWM_ACHIEVEMENT_DIME",
"$SWWM_ACHIEVEMENT_STOMP",
"$SWWM_ACHIEVEMENT_CHEAT",
"$SWWM_ACHIEVEMENT_GOD",
"$SWWM_ACHIEVEMENT_THRUWALL",
"$SWWM_ACHIEVEMENT_MASHIRO",
"$SWWM_ACHIEVEMENT_LEAD",
"$SWWM_ACHIEVEMENT_LOVE",
"$SWWM_ACHIEVEMENT_BUTTS",
"$SWWM_ACHIEVEMENT_PLUSH",
"$SWWM_ACHIEVEMENT_BUSTIN",
"$SWWM_ACHIEVEMENT_MOTH",
"$SWWM_ACHIEVEMENT_IWAD",
"$SWWM_ACHIEVEMENT_BRAKE",
"$SWWM_ACHIEVEMENT_RAGE",
"$SWWM_ACHIEVEMENT_ABORT",
"$SWWM_ACHIEVEMENT_PARRY",
"$SWWM_ACHIEVEMENT_ALLKILLS",
"$SWWM_ACHIEVEMENT_ALLSECRETS",
"$SWWM_ACHIEVEMENT_REFLECT",
"$SWWM_ACHIEVEMENT_SEKIRO",
"$SWWM_ACHIEVEMENT_WAVE",
"$SWWM_ACHIEVEMENT_FRIEND",
"$SWWM_ACHIEVEMENT_SHOCK",
"$SWWM_ACHIEVEMENT_BALLS",
"$SWWM_ACHIEVEMENT_REFRESH",
"$SWWM_ACHIEVEMENT_STEP",
"$SWWM_ACHIEVEMENT_SUNNY",
"$SWWM_ACHIEVEMENT_THICC",
"$SWWM_ACHIEVEMENT_EVERYTHING",
"$SWWM_ACHIEVEMENT_ALLCOLL",
"$SWWM_ACHIEVEMENT_SLEMG",
"$SWWM_ACHIEVEMENT_DOSH",
"$SWWM_ACHIEVEMENT_JUMP",
"$SWWM_ACHIEVEMENT_EZKILL",
"$SWWM_ACHIEVEMENT_LIGMA",
"$SWWM_ACHIEVEMENT_YEET",
"$SWWM_ACHIEVEMENT_MBF",
"$SWWM_ACHIEVEMENT_LIGHTNING",
"$SWWM_ACHIEVEMENT_DAKKA",
"$SWWM_ACHIEVEMENT_ROAST",
"$SWWM_ACHIEVEMENT_DAB",
"$SWWM_ACHIEVEMENT_HND",
"$SWWM_ACHIEVEMENT_GEPGUN",
"$SWWM_ACHIEVEMENT_FULLER",
"$SWWM_ACHIEVEMENT_PENE",
"$SWWM_ACHIEVEMENT_ACID",
"$SWWM_ACHIEVEMENT_SALT",
"$SWWM_ACHIEVEMENT_MEGA",
"$SWWM_ACHIEVEMENT_SANIC",
"$SWWM_ACHIEVEMENT_TELE",
"$SWWM_ACHIEVEMENT_BUNE",
"$SWWM_ACHIEVEMENT_BONK",
"$SWWM_ACHIEVEMENT_ANOM",
"$SWWM_ACHIEVEMENT_ANONE",
"$SWWM_ACHIEVEMENT_TETRIS",
"$SWWM_ACHIEVEMENT_PONG",
"$SWWM_ACHIEVEMENT_SNAKE",
"$SWWM_ACHIEVEMENT_SMB",
"$SWWM_ACHIEVEMENT_TYRIAN",
"$SWWM_ACHIEVEMENT_BOF",
"$SWWM_ACHIEVEMENT_WANTDIE"
};
// has number in txt
static const int number[] =
{
SA_GCSANDWICH,
0, // HDOOM
0, // ONESTANDING
0, // OOPSIE
SA_GHOST,
0, // CLONK
SA_ALLITEMS,
SA_CONGA,
SA_EXPLOGUN,
SA_GRAVITY,
SA_FLIGHT,
0, // CLIFFYB
SA_PAR,
0, // ONEGUY
SA_GOLDEN,
SA_GIB,
SA_BARRIER,
SA_BOSSDASH,
SA_SNEEZE,
SA_FUEL,
SA_SLAYER,
0, // BROCCOLIDOCCOLI
0, // DIME
SA_STOMP,
0, // CHEAT
0, // GOD
SA_THRUWALL,
0, // MASHIRO
SA_LEAD,
SA_LOVE,
SA_BUTTS,
SA_PLUSH,
SA_BUSTIN,
SA_MOTH,
0, // IWAD
SA_BRAKE,
SA_RAGE,
0, // ABORT
SA_PARRY,
SA_ALLKILLS,
SA_ALLSECRETS,
SA_REFLECT,
0, // SEKIRO
0, // WAVE
SA_FRIEND,
SA_SHOCK,
SA_BALLS,
SA_REFRESH,
SA_STEP,
SA_SUNNY,
SA_THICC,
0, // EVERYTHING
0, // ALLCOLL
SA_SLEMG,
0, // DOSH
SA_JUMP,
SA_EZKILL,
0, // LIGMA
0, // YEET
0, // MBF
0, // LIGHTNING
0, // DAKKA
SA_ROAST,
SA_DAB,
SA_HND,
SA_GEPGUN,
0, // FULLER
SA_PENE,
SA_ACID,
0, // SALT
0, // MEGA
SA_SANIC,
0, // TELE
SA_BUNE,
SA_BONK,
SA_ANOM,
0, // ANONE
0, // TETRIS
0, // PONG
0, // SNAKE
0, // SMB
0, // TYRIAN
0, // BOF
0 // WANTDIE
};
// is ignored (hide away achievements that can't be done yet)
static const bool ignoreme[] =
{
false, // GCSANDWICH
false, // HDOOM
false, // ONESTANDING
false, // OOPSIE
false, // GHOST
false, // CLONK
false, // ALLITEMS
false, // CONGA
false, // EXPLOGUN
false, // GRAVITY
false, // FLIGHT
false, // CLIFFYB
false, // PAR
false, // ONEGUY
false, // GOLDEN
false, // GIB
false, // BARRIER
false, // BOSSDASH
false, // SNEEZE
false, // FUEL
false, // SLAYER
false, // BROCCOLIDOCCOLI
false, // DIME
false, // STOMP
false, // CHEAT
false, // GOD
false, // THRUWALL
true, // MASHIRO
false, // LEAD
false, // LOVE
false, // BUTTS
false, // PLUSH
false, // BUSTIN
false, // MOTH
false, // IWAD
false, // BRAKE
false, // RAGE
false, // ABORT
false, // PARRY
false, // ALLKILLS
false, // ALLSECRETS
false, // REFLECT
false, // SEKIRO
false, // WAVE
false, // FRIEND
false, // SHOCK
false, // BALLS
false, // REFRESH
false, // STEP
false, // SUNNY
false, // THICC
false, // EVERYTHING
false, // ALLCOLL
false, // SLEMG
false, // DOSH
false, // JUMP
false, // EZKILL
false, // LIGMA
false, // YEET
false, // MBF
true, // LIGHTNING
true, // DAKKA
true, // ROAST
true, // DAB
true, // HND
true, // GEPGUN
true, // FULLER
true, // PENE
false, // ACID
false, // SALT
false, // MEGA
false, // SANIC
false, // TELE
false, // BUNE
false, // BONK
false, // ANOM
false, // ANONE
true, // TETRIS
true, // PONG
true, // SNAKE
true, // SMB
true, // TYRIAN
true, // BOF
false // WANTDIE
};
// progress tracking cvar (if any)
static const Name pvar[] =
{
'swwm_progress_gcsandwich',
'None', // HDOOM
'None', // ONESTANDING
'None', // OOPSIE
'swwm_progress_ghost',
'None', // CLONK
'swwm_progress_allitems',
'swwm_progress_conga',
'swwm_progress_explogun',
'swwm_progress_gravity',
'swwm_progress_flight',
'None', // CLIFFYB
'swwm_progress_par',
'None', // ONEGUY
'swwm_progress_golden',
'swwm_progress_gib',
'swwm_progress_barrier',
'swwm_progress_bossdash',
'swwm_progress_sneeze',
'swwm_progress_fuel',
'swwm_progress_slayer',
'None', // BROCCOLIDOCCOLI
'None', // DIME
'swwm_progress_stomp',
'None', // CHEAT
'None', // GOD
'swwm_progress_thruwall',
'None', // MASHIRO
'swwm_progress_lead',
'swwm_progress_love',
'swwm_progress_butts',
'swwm_progress_plush',
'swwm_progress_bustin',
'swwm_progress_moth',
'swwm_progress_iwad',
'swwm_progress_brake',
'swwm_progress_rage',
'None', // ABORT
'swwm_progress_parry',
'swwm_progress_allkills',
'swwm_progress_allsecrets',
'swwm_progress_reflect',
'None', // SEKIRO
'None', // WAVE
'swwm_progress_friend',
'swwm_progress_shock',
'swwm_progress_balls',
'swwm_progress_refresh',
'swwm_progress_step',
'swwm_progress_sunny',
'swwm_progress_thicc',
'None', // EVERYTHING
'swwm_progress_allcoll',
'swwm_progress_slemg',
'swwm_progress_dosh',
'swwm_progress_jump',
'swwm_progress_ezkill',
'None', // LIGMA
'None', // YEET
'None', // MBF
'None', // LIGHTNING
'swwm_progress_dakka',
'swwm_progress_roast',
'swwm_progress_dab',
'swwm_progress_hnd',
'swwm_progress_gepgun',
'None', // FULLER
'swwm_progress_pene',
'swwm_progress_acid',
'None', // SALT
'swwm_progress_mega',
'swwm_progress_sanic',
'None', // TELE
'swwm_progress_bune',
'swwm_progress_bonk',
'swwm_progress_anom',
'None', // ANONE
'None', // TETRIS
'None', // PONG
'None', // SNAKE
'None', // SMB
'None', // TYRIAN
'None', // BOF
'None' // WANTDIE
};
achievements.Clear();
for ( int i=0; i<vars.Size(); i++ )
{
if ( ignoreme[i] ) continue;
let ac = new("MenuAchievement");
ac.tag = names[i].."_TAG";
ac.txt = names[i].."_TXT";
ac.icon = TexMan.CheckForTexture(StringTable.Localize(names[i].."_PIC"),TexMan.Type_Any);
if ( !ac.icon.IsValid() ) ac.icon = TexMan.CheckForTexture("graphics/Achievements/AchievementNone.png",TexMan.Type_Any);
ac.state = CVar.FindCVar(vars[i]);
ac.progress = (pvar[i]!='None')?CVar.FindCVar(pvar[i]):null;
ac.maxval = ac.num = number[i];
// special cases
if ( vars[i] == 'swwm_achievement_allcoll' )
{
int nc = 0;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let c = (Class<SWWMCollectible>)(AllActorClasses[i]);
if ( !c || (c == 'SWWMCollectible') ) continue;
let def = GetDefaultByType(c);
// check that we can collect it in this IWAD
if ( !def.ValidGame() ) continue;
nc++;
}
ac.maxval = nc;
}
else if ( vars[i] == 'swwm_achievement_dakka' ) ac.maxval = 60;
else if ( vars[i] == 'swwm_achievement_dosh' ) ac.maxval = 1000000000;
else if ( vars[i] == 'swwm_achievement_mega' ) ac.maxval = 1000000;
else if ( vars[i] == 'swwm_achievement_iwad' ) ac.maxval = 7;
achievements.Push(ac);
}
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 str = StringTable.Localize("$SWWM_HELPTXT");
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
str = str..StringTable.Localize("$SWWM_HELPTXT_TRADING");
break;
}
let bar = SWWMStatusBar(StatusBar);
if ( bar && (bar.FullHistory.Size() > 0) )
str = str..StringTable.Localize("$SWWM_HELPTXT_CHAT");
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?
if ( mpos.x < 632 ) return res;
String kstr;
if ( mkey[1] != "" ) kstr = mkey[0].."/"..mkey[1];
else kstr = mkey[0];
str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
str = str..StringTable.Localize("$SWWM_HELPTXT_TRADING");
break;
}
let bar = SWWMStatusBar(StatusBar);
if ( bar && (bar.FullHistory.Size() > 0) )
str = str..StringTable.Localize("$SWWM_HELPTXT_CHAT");
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);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
str = str..StringTable.Localize("$SWWM_HELPTXT_TRADING");
break;
}
let bar = SWWMStatusBar(StatusBar);
if ( bar && (bar.FullHistory.Size() > 0) )
str = str..StringTable.Localize("$SWWM_HELPTXT_CHAT");
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( MenuAchievement a, MenuAchievement b )
{
bool adone = !!(a.state.GetInt()), bdone = !!(b.state.GetInt());
double afactor = adone?1.:0., bfactor = bdone?1.:0.;
if ( a.progress )
{
int val = a.progress.GetInt();
int mval = a.maxval;
if ( a.tag == "$SWWM_ACHIEVEMENT_IWAD_TAG" )
{
// special case
mval = 3;
int bits = val;
val = 0;
for ( int i=0; i<3; i++ ) if ( bits&(1<<i) ) val++;
}
int cur = min(val,mval);
afactor = cur/double(mval);
}
if ( b.progress )
{
int val = b.progress.GetInt();
int mval = b.maxval;
if ( b.tag == "$SWWM_ACHIEVEMENT_IWAD_TAG" )
{
// special case
mval = 3;
int bits = val;
val = 0;
for ( int i=0; i<3; i++ ) if ( bits&(1<<i) ) val++;
}
int cur = min(val,mval);
bfactor = cur/double(mval);
}
if ( adone && bdone )
{
// alphabetic sort
String tag1 = StringTable.Localize(a.tag),
tag2 = StringTable.Localize(b.tag);
return tag1 > tag2;
}
if ( !adone && !bdone )
{
// progress sort?
if ( afactor != bfactor )
return afactor < bfactor;
// alphabetic sort
String tag1 = StringTable.Localize(a.tag),
tag2 = StringTable.Localize(b.tag);
return tag1 > tag2;
}
// state sort
return bdone;
}
private int partition_achievements( Array<MenuAchievement> a, int l, int h )
{
MenuAchievement pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpAchievement(pv,a[j]) )
{
i++;
MenuAchievement tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
MenuAchievement tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_achievements( Array<MenuAchievement> 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;
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)?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)?Color(8,8,8):Color(16,16,16));
Screen.DrawTexture((!completed&&!hasprogress)?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?Color(96,0,0,0):Color(192,0,0,0));
// progress bar
if ( a.progress )
{
int val = a.progress.GetInt();
int mval = a.maxval;
if ( a.tag == "$SWWM_ACHIEVEMENT_IWAD_TAG" )
{
// special case
mval = 3;
int bits = val;
val = 0;
for ( int i=0; i<3; i++ ) if ( bits&(1<<i) ) val++;
}
val = clamp(val,0,mval);
if ( completed ) str = SWWMUtility.BlockBar(mval,mval,barsz,Font.CR_DARKGREEN,Font.CR_BLACK);
else str = SWWMUtility.BlockBar(val,mval,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(mval),SWWMUtility.ThousandsNum(mval));
else str = String.Format("%s / %s",SWWMUtility.ThousandsNum(val),SWWMUtility.ThousandsNum(mval));
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.num?String.Format(StringTable.Localize(a.txt),a.num):StringTable.Localize(a.txt);
if ( !completed && !hasprogress ) SWWMUtility.ObscureText(str,(gametic/3)+i*2);
BrokenLines l = fnt2.BreakLines(str,txtsz);
str = StringTable.Localize(a.tag);
if ( !completed && !hasprogress ) 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);
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || (i == consoleplayer) ) continue;
if ( deathmatch && (!teamplay || (players[i].GetTeam() != players[consoleplayer].GetTeam())) ) continue;
str = str..StringTable.Localize("$SWWM_HELPTXT_TRADING");
break;
}
let bar = SWWMStatusBar(StatusBar);
if ( bar && (bar.FullHistory.Size() > 0) )
str = str..StringTable.Localize("$SWWM_HELPTXT_CHAT");
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);
}
}
}
}