diff --git a/language.version b/language.version index e11253a01..ab9fe567d 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre r1 \cu(Sat 18 Sep 00:10:31 CEST 2021)\c-"; -SWWM_SHORTVER="\cw1.2pre r1 \cu(2021-09-18 00:10:31)\c-"; +SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre \cu(Sat 18 Sep 14:20:11 CEST 2021)\c-"; +SWWM_SHORTVER="\cw1.2pre \cu(2021-09-18 14:20:11)\c-"; diff --git a/sndinfo.txt b/sndinfo.txt index 0f8543ee1..b28001fe2 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -912,11 +912,15 @@ ynykron/wind sounds/ynykron/mc_vortexwind.ogg misc/secret sounds/menu/findsecret.ogg misc/keytry sounds/menu/failuse.ogg misc/w_pkup sounds/general/wpickup.ogg +misc/w_pkup_vip sounds/general/wpickup_vip.ogg misc/p_pkup sounds/general/ppickup.ogg +misc/p_pkup_vip sounds/general/ppickup_vip.ogg misc/i_pkup sounds/general/ipickup.ogg misc/k_pkup sounds/general/kpickup.ogg misc/ammo_pkup sounds/general/apickup.ogg misc/bullet_pkup sounds/general/bpickup.ogg +misc/ammo_pkup_vip sounds/general/apickup_vip.ogg +misc/bullet_pkup_vip sounds/general/bpickup_vip.ogg misc/armor_pkup sounds/general/spickup.ogg misc/health_pkup sounds/general/hpickup.ogg misc/spawn sounds/CORK.ogg diff --git a/sounds/general/apickup_vip.ogg b/sounds/general/apickup_vip.ogg new file mode 100644 index 000000000..13a568e3c Binary files /dev/null and b/sounds/general/apickup_vip.ogg differ diff --git a/sounds/general/bpickup_vip.ogg b/sounds/general/bpickup_vip.ogg new file mode 100644 index 000000000..be1a43764 Binary files /dev/null and b/sounds/general/bpickup_vip.ogg differ diff --git a/sounds/general/ppickup_vip.ogg b/sounds/general/ppickup_vip.ogg new file mode 100644 index 000000000..a69141e59 Binary files /dev/null and b/sounds/general/ppickup_vip.ogg differ diff --git a/sounds/general/wpickup_vip.ogg b/sounds/general/wpickup_vip.ogg new file mode 100644 index 000000000..a7ce956b7 Binary files /dev/null and b/sounds/general/wpickup_vip.ogg differ diff --git a/zscript.txt b/zscript.txt index ab1ad8f47..85011422f 100644 --- a/zscript.txt +++ b/zscript.txt @@ -61,6 +61,7 @@ version "4.6" #include "zscript/hud/swwm_hudextra.zsc" // kbase #include "zscript/kbase/swwm_kbase.zsc" +#include "zscript/kbase/swwm_kbase_priv.zsc" // items #include "zscript/items/swwm_baseitem.zsc" #include "zscript/items/swwm_basehealth.zsc" diff --git a/zscript/dlc1/swwm_ammoitems_dlc.zsc b/zscript/dlc1/swwm_ammoitems_dlc.zsc index 818f9b62b..f15c19439 100644 --- a/zscript/dlc1/swwm_ammoitems_dlc.zsc +++ b/zscript/dlc1/swwm_ammoitems_dlc.zsc @@ -394,6 +394,7 @@ Class GrandSpear : MagAmmo //$Icon ammo Tag "$T_GRANDSPEAR"; MagAmmo.PickupTag "GRANDSPEAR"; + Inventory.PickupSound "misc/bullet_pkup_vip"; Inventory.Icon "graphics/HUD/Icons/A_GrandSpear.png"; MagAmmo.ParentAmmo "GrandAmmo"; MagAmmo.ClipSize 5; @@ -419,6 +420,7 @@ Class GrandAmmo : SWWMAmmo //$Icon ammo Tag "$T_GRANDAMMO"; SWWMAmmo.PickupTag "GRANDAMMO"; + Inventory.PickupSound "misc/ammo_pkup_vip"; Stamina -1200000; Inventory.Icon "graphics/HUD/Icons/A_GrandAmmo.png"; Inventory.Amount 1; diff --git a/zscript/dlc1/swwm_hugeassrailgun.zsc b/zscript/dlc1/swwm_hugeassrailgun.zsc index dea7e9a4b..eb26fc682 100644 --- a/zscript/dlc1/swwm_hugeassrailgun.zsc +++ b/zscript/dlc1/swwm_hugeassrailgun.zsc @@ -17,6 +17,7 @@ Class GrandLance : SWWMWeapon //$Icon weapon Tag "$T_GRANDLANCE"; Inventory.PickupMessage "$T_GRANDLANCE"; + Inventory.PickupSound "misc/w_pkup_vip"; Obituary "$O_GRANDLANCE"; SWWMWeapon.Tooltip "$TT_GRANDLANCE"; Inventory.Icon "graphics/HUD/Icons/W_GrandLance.png"; diff --git a/zscript/dlc2/swwm_ammoitems_dlc2.zsc b/zscript/dlc2/swwm_ammoitems_dlc2.zsc index 816bea33c..41e8a24b7 100644 --- a/zscript/dlc2/swwm_ammoitems_dlc2.zsc +++ b/zscript/dlc2/swwm_ammoitems_dlc2.zsc @@ -262,6 +262,7 @@ Class UltimatePod : MagAmmo //$Icon ammo Tag "$T_ULTIMATEPOD"; MagAmmo.PickupTag "ULTIMATEPOD"; + Inventory.PickupSound "misc/bullet_pkup_vip"; Inventory.Icon "graphics/HUD/Icons/A_UltimatePod.png"; MagAmmo.ParentAmmo "UltimateAmmo"; MagAmmo.ClipSize 4; @@ -287,6 +288,7 @@ Class UltimateAmmo : SWWMAmmo //$Icon ammo Tag "$T_ULTIMATEAMMO"; SWWMAmmo.PickupTag "ULTIMATEAMMO"; + Inventory.PickupSound "misc/ammo_pkup_vip"; Stamina -2000000; Inventory.Icon "graphics/HUD/Icons/A_UltimateAmmo.png"; Inventory.Amount 1; diff --git a/zscript/dlc2/swwm_ultimate.zsc b/zscript/dlc2/swwm_ultimate.zsc index 55424efbb..220ff2fc8 100644 --- a/zscript/dlc2/swwm_ultimate.zsc +++ b/zscript/dlc2/swwm_ultimate.zsc @@ -16,6 +16,7 @@ Class RafanKos : SWWMWeapon //$Icon weapon Tag "$T_RAFANKOS"; Inventory.PickupMessage "$T_RAFANKOS"; + Inventory.PickupSound "misc/w_pkup_vip"; Obituary "$O_RAFANKOS"; SWWMWeapon.Tooltip "$TT_RAFANKOS"; Inventory.Icon "graphics/HUD/Icons/W_RafanKos.png"; diff --git a/zscript/items/swwm_ammoitems.zsc b/zscript/items/swwm_ammoitems.zsc index 5b14fb9b5..735b403f8 100644 --- a/zscript/items/swwm_ammoitems.zsc +++ b/zscript/items/swwm_ammoitems.zsc @@ -1090,6 +1090,7 @@ Class YnykronAmmo : SWWMAmmo //$Icon ammo Tag "$T_YNYKRONAMMO"; SWWMAmmo.PickupTag "YNYKRONAMMO"; + Inventory.PickupSound "misc/ammo_pkup_vip"; Stamina -3000000; Inventory.Icon "graphics/HUD/Icons/A_Ynykron.png"; Inventory.Amount 1; diff --git a/zscript/items/swwm_powerups.zsc b/zscript/items/swwm_powerups.zsc index ca62df053..92813af2a 100644 --- a/zscript/items/swwm_powerups.zsc +++ b/zscript/items/swwm_powerups.zsc @@ -2941,7 +2941,7 @@ Class Mykradvo : Inventory Tag "$T_MYKRADVO"; Stamina -1200000; Inventory.Icon "graphics/HUD/Icons/I_Mykradvo.png"; - Inventory.PickupSound "misc/p_pkup"; + Inventory.PickupSound "misc/p_pkup_vip"; Inventory.UseSound "mykradvo/arc"; Inventory.PickupMessage "$T_MYKRADVO"; Inventory.MaxAmount 3; @@ -3357,7 +3357,7 @@ Class AngerySigil : Inventory Tag "$T_DEVASTATION"; Stamina -1500000; Inventory.Icon "graphics/HUD/Icons/I_Devastation.png"; - Inventory.PickupSound "misc/p_pkup"; + Inventory.PickupSound "misc/p_pkup_vip"; Inventory.UseSound "powerup/devastation"; Inventory.PickupMessage "$T_DEVASTATION"; Inventory.MaxAmount 3; @@ -3625,7 +3625,7 @@ Class DivineSprite : Inventory Tag "$T_DIVINE"; Stamina -3600000; Inventory.Icon "graphics/HUD/Icons/I_Divine.png"; - Inventory.PickupSound "misc/p_pkup"; + Inventory.PickupSound "misc/p_pkup_vip"; Inventory.UseSound "powerup/divineuse"; Inventory.PickupMessage "$T_DIVINE"; Inventory.MaxAmount 3; diff --git a/zscript/kbase/OLD_swwm_kbase.zsc b/zscript/kbase/OLD_swwm_kbase.zsc new file mode 100644 index 000000000..375d4d789 --- /dev/null +++ b/zscript/kbase/OLD_swwm_kbase.zsc @@ -0,0 +1,3728 @@ +// internal "knowledge base" and more +Class MenuTransaction +{ + enum ETransactionType + { + TT_ITEMUSE, + TT_ITEMDROP, + TT_ITEMSEND + }; + + int uid, type; + Class used; + bool result, usedup; +} + +// FIXME this whole thing needs to be refactored eventually +Class DemolitionistMenu : GenericMenu +{ + enum EDemoTab + { + TAB_MISSION = 0, + TAB_STATS, + TAB_INVENTORY, + TAB_KEYS, + TAB_LIBRARY, + TAB_STORE, + TAB_TRADING, + TAB_CHAT, + TAB_SECRET, + TAB_HELP + }; + + enum EStatsTab + { + STAT_MAIN = 0, + STAT_KILLS, + STAT_LEVEL, + STAT_ACHIEVEMENT, + }; + + TextureID MainWindow, TabSeparator, WindowSeparator, WindowSeparatorH, + FancyBg, Drawing[3], LoreSeparator, LoreSeparatorW, AchievementUnknown; + transient Font TewiFont, MPlusFont, MiniwiFont, k6x8Font; + Array 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 missionbacklog; + // stats + SWWMStats stats; + Array statlist; + Array sorted_mstats; // sorted by killcount + Array filtered_lstats; // filtered if necessary + entry for current map + LevelStat curlstat; + Array achievements; // array created once, then sorted as needed + // inventory lists + Array invlist; + // lore stuff + SWWMLoreLibrary lorelib; + Array lorelist; + int oldloresiz; + // store stuff + Array > storelist; + Array storeunits; + // trading + SWWMTradeHistory tradelib; + Array 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 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 = StringTable.Localize("$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= 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 + hexdd = SWWMUtility.IsDeathkings(); + 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 ) + { + // April 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); + if ( !deathmatch ) + { + tabs.Push(TAB_KEYS); + tabs.Push(TAB_LIBRARY); + } + if ( !deathmatch && (G_SkillPropertyInt(SKILLP_ACSReturn) < 4) ) tabs.Push(TAB_STORE); + for ( int i=0; i 0) ) + tabs.Push(TAB_CHAT); + int lasttab = swwm_lasttab; + for ( int i=0; i 0 ) + { + leap_year = (!(y%4) && ((y%100) || !(y%400))); + if ( addtime >= 86400 ) + { + addtime -= 86400; + d++; + int md = days_in_month[m]; + if ( leap_year && (m == 1) ) md++; + if ( d >= md ) + { + d = 0; + m++; + if ( m > 11 ) + { + m = 0; + y++; + } + } + } + else if ( addtime >= 3600 ) + { + addtime -= 3600; + h++; + if ( h > 23 ) + { + h = 0; + d++; + int md = days_in_month[m]; + if ( leap_year && (m == 1) ) md++; + if ( d >= md ) + { + d = 0; + m++; + if ( m > 11 ) + { + m = 0; + y++; + } + } + } + } + else if ( addtime >= 60 ) + { + addtime -= 60; + mn++; + if ( mn > 59 ) + { + mn = 0; + h++; + if ( h > 23 ) + { + h = 0; + d++; + int md = days_in_month[m]; + if ( leap_year && (m == 1) ) md++; + if ( d >= md ) + { + d = 0; + m++; + if ( m > 11 ) + { + m = 0; + y++; + } + } + } + } + } + else + { + // we finished counting + addtime = 0; + } + } + return String.Format("%04d-%02d-%02d %02d:%02d %s",y,m+1,d+1,h,mn,c_tz); + } + + override bool MenuEvent( int mkey, bool fromcontroller ) + { + Font fnt = LangFont(TewiFont); + switch ( kcode ) + { + case 0: + case 1: + if ( mkey == MKEY_UP ) kcode++; + else kcode = 0; + break; + case 2: + case 3: + if ( mkey == MKEY_DOWN ) kcode++; + else kcode = 0; + break; + case 4: + case 6: + if ( mkey == MKEY_LEFT ) kcode++; + else kcode = 0; + break; + case 5: + case 7: + if ( mkey == MKEY_RIGHT ) kcode++; + else kcode = 0; + break; + case 10: + if ( mkey == MKEY_ENTER ) + { + MenuSound("misc/secret"); + if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab); + tabs.Push(TAB_SECRET); + curtab = tabs.Size()-1; + sel0 = sel1 = sel2 = 0; + ofs0 = ofs1 = ofs2 = 0; + sub = false; + } + default: + kcode = 0; + break; + } + int maxtab = multiplayer?TAB_CHAT:TAB_STORE; + switch ( mkey ) + { + case MKEY_BACK: + MenuSound("menu/democlose"); + EventHandler.SendNetworkEvent("swwmclearalltransactions",consoleplayer); + Close(); + return true; + case MKEY_PAGEUP: + MenuSound("menu/demotab"); + if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab); // hide this tab + if ( curtab <= 0 ) curtab = (tabs.Size()-1); + else curtab--; + CVar.FindCVar('swwm_lasttab').SetInt(tabs[curtab]); + sel0 = sel1 = sel2 = 0; + ofs0 = ofs1 = ofs2 = 0; + sub = false; + invlist.Clear(); + lorelist.Clear(); + storelist.Clear(); + storeunits.Clear(); + playerlist.Clear(); + return true; + case MKEY_PAGEDOWN: + MenuSound("menu/demotab"); + if ( tabs[curtab] >= TAB_SECRET ) tabs.Delete(curtab); // hide this tab + if ( curtab < (tabs.Size()-1) ) curtab++; + else curtab = 0; + CVar.FindCVar('swwm_lasttab').SetInt(tabs[curtab]); + sel0 = sel1 = sel2 = 0; + ofs0 = ofs1 = ofs2 = 0; + sub = false; + invlist.Clear(); + lorelist.Clear(); + storelist.Clear(); + storeunits.Clear(); + playerlist.Clear(); + return true; + case MKEY_DOWN: + if ( tabs[curtab] == TAB_HELP ) + { + String kstr; + if ( self.mkey[1] != "" ) kstr = self.mkey[0].."/"..self.mkey[1]; + else kstr = self.mkey[0]; + String str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr); + BrokenLines l = fnt.BreakLines(str,629); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); + if ( (l.Count() > 28) && (sel0 < l.Count()-28) ) + { + MenuSound("menu/demoscroll"); + sel0++; + } + } + else if ( tabs[curtab] == TAB_MISSION ) + { + String str; + if ( sel1 == 0 ) str = StringTable.Localize(missionstr); + else str = StringTable.Localize(missionbacklog[sel1-1]); + BrokenLines l = fnt.BreakLines(str,629); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); + if ( (l.Count() > 28) && (sel0 < l.Count()-28) ) + { + MenuSound("menu/demoscroll"); + ofs0 = ++sel0; + } + } + else if ( (tabs[curtab] == TAB_CHAT) && (StatusBar is 'SWWMStatusBar') && (sel0 > 0) ) + { + MenuSound("menu/demoscroll"); + ofs0 = --sel0; + } + else if ( (tabs[curtab] == TAB_LIBRARY) && (lorelist.Size() > 1) ) + { + if ( sub ) + { + String str = StringTable.Localize(lorelist[sel0].text); + int ofs = (language~=="jp")?212:132; + if ( lorelist.Size() > 26 ) ofs += 8; + BrokenLines l = fnt.BreakLines(str,635-ofs); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs); + if ( (l.Count() > 28) && (sel2 < l.Count()-28) ) + { + MenuSound("menu/demoscroll"); + ofs2 = ++sel2; + } + } + else + { + MenuSound("menu/demoscroll"); + if ( sel0 >= lorelist.Size()-1 ) ofs0 = sel0 = 0; + else + { + sel0++; + // update ofs so selection stays on-screen + int ofs = max(ofs0-26,0); + if ( sel0 < ofs ) ofs0 = sel0+26; + else if ( sel0 > ofs+26 ) ofs0 = sel0; + } + } + } + else if ( ((tabs[curtab] == TAB_INVENTORY) || (tabs[curtab] == TAB_KEYS)) && (invlist.Size() > 1) ) + { + MenuSound("menu/demoscroll"); + ofs0 = ++sel0; + if ( sel0 >= invlist.Size() ) ofs0 = sel0 = 0; + } + else if ( ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) && (invlist.Size() > 1) ) + { + MenuSound("menu/demoscroll"); + ofs1 = ++sel1; + if ( sel1 >= invlist.Size() ) ofs1 = sel1 = 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)) && (invlist.Size() > 1) ) + { + MenuSound("menu/demoscroll"); + ofs0 = --sel0; + if ( sel0 < 0 ) ofs0 = sel0 = invlist.Size()-1; + } + else if ( ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) && (invlist.Size() > 1) ) + { + MenuSound("menu/demoscroll"); + ofs1 = --sel1; + if ( sel1 < 0 ) ofs1 = sel1 = 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)) && (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_TRADING) && sub && (sel0 != -1)) && (invlist.Size() > 21) ) + { + int oldsel = sel1; + ofs1 = sel1 += 22; + if ( sel1 >= invlist.Size() ) ofs1 = sel1 = invlist.Size()-1; + if ( sel1 != 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)) && (invlist.Size() > 21) && (sel0-22 >= 0) ) + { + MenuSound("menu/demoscroll"); + ofs0 = sel0 -= 22; + } + else if ( ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) && (invlist.Size() > 21) && (sel1-22 >= 0) ) + { + MenuSound("menu/demoscroll"); + ofs1 = sel1 -= 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= 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 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= 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 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 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= 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 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 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= 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 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 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,SWWMUtility.GetAmmoTagClass(storelist[i])); + else str = def.GetTag(); + } + else if ( (storeunits[i] > 1) || (storelist[i] is 'Ammo') ) str = String.Format("%dx %s",storeunits[i],SWWMUtility.GetAmmoTagClass(storelist[i])); + 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= 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 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,SWWMUtility.GetAmmoTagClass(storelist[i])); + 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; + 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 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 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= 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 longest ) longest = len; + } + maxcol = 622/(longest+24); + totalcol = int(ceil(playerlist.Size()/22.)); + if ( maxcol >= totalcol ) return res; // no scrollbar + // calculate offset + int szr = totalcol-maxcol; + int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr); + if ( step > 0 ) step += (maxcol-1); + if ( step != (ofs0/22) ) MenuSound("menu/demoscroll"); + ofs0 = step*22; + drag = 1; + } + else if ( sel0 == -1 ) + { + // are we clicking where the scrollbar should be? + if ( mpos.x < 632 ) return res; + if ( tradelib.ent.Size() <= 28 ) return res; // no scrollbar + // calculate offset + int szr = tradelib.ent.Size()-28; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + drag = 1; + } + } + else if ( (tabs[curtab] == TAB_CHAT) && !isrclick ) + { + // are we clicking where the scrollbar should be? + if ( mpos.x < 632 ) return res; + let bar = SWWMStatusBar(StatusBar); + if ( !bar || (bar.FullHistory.Size() <= 1) ) return res; // no scrollbar + // calculate offset + int szr = bar.FullHistory.Size()-1; + int step = szr-clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + drag = 1; + } + else if ( (tabs[curtab] == TAB_HELP) && !isrclick ) + { + // are we clicking where the scrollbar should be? + String kstr; + if ( mkey[1] != "" ) kstr = mkey[0].."/"..mkey[1]; + else kstr = mkey[0]; + str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr); + BrokenLines l = fnt.BreakLines(str,629); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); + else return res; // no scrollbar + // calculate offset + int szr = l.Count()-28; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + drag = 1; + } + else if ( (tabs[curtab] == TAB_STATS) && !isrclick ) + { + double midp = (language~=="jp")?206:126; + if ( mpos.x < midp ) + { + // check what item we clicked + int xx = 9; + int yy = 23; + for ( int i=0; i<=STAT_ACHIEVEMENT; i++ ) + { + // check boundary + if ( (mpos.y >= yy) && (mpos.y < yy+14) ) + { + if ( sel1 != i ) + { + MenuSound("menu/demoscroll"); + ofs0 = sel0 = 0; + sel1 = i; + CVar.FindCVar('swwm_laststattab').SetInt(sel1); + } + return res; + } + yy += 16; + } + return res; + } + // are we clicking where the scrollbar should be? + if ( mpos.x < 632 ) return res; + int cnt = 0, num = 22; + if ( sel1 == STAT_MAIN ) cnt = statlist.Size(); + else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size(); + else if ( sel1 == STAT_LEVEL ) cnt = filtered_lstats.Size(); + else if ( sel1 == STAT_ACHIEVEMENT ) + { + cnt = achievements.Size(); + num = 7; + } + if ( cnt <= num ) return res; // no scrollbar + // calculate offset + int szr = cnt-num; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + drag = 1; + } + } + else if ( (type == MOUSE_Move) && drag ) // scroll dragging + { + if ( tabs[curtab] == TAB_MISSION ) + { + String str; + if ( sel1 == 0 ) str = StringTable.Localize(missionstr); + else str = StringTable.Localize(missionbacklog[sel1-1]); + BrokenLines l = fnt.BreakLines(str,629); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); + else return res; // no scrollbar + // calculate offset + int szr = l.Count()-28; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + } + else if ( (tabs[curtab] == TAB_INVENTORY) || ((tabs[curtab] == TAB_TRADING) && sub && (sel0 != -1)) ) + { + // check that scrollbar is present + if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar + int longest = 0, len; + int maxcol, totalcol; + for ( int i=0; i 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 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 1) || (storelist[i] is 'Ammo') ) str = String.Format("(%d/%d) %s",storeunits[i],amt,SWWMUtility.GetAmmoTagClass(storelist[i])); + else str = def.GetTag(); + } + else if ( (storeunits[i] > 1) || (storelist[i] is 'Ammo') ) str = String.Format("%dx %s",storeunits[i],SWWMUtility.GetAmmoTagClass(storelist[i])); + 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 longest ) longest = len; + } + maxcol = 622/(longest+24); + totalcol = int(ceil(invlist.Size()/22.)); + if ( maxcol >= totalcol ) return res; // no scrollbar + // calculate offset + int szr = totalcol-maxcol; + int step = clamp(rnd((mpos.x-5.)/(630./szr)),0,szr); + if ( step > 0 ) step += (maxcol-1); + if ( step != (ofs0/22) ) MenuSound("menu/demoscroll"); + ofs0 = step*22; + } + else if ( sel0 == -1 ) + { + if ( tradelib.ent.Size() <= 28 ) return res; // no scrollbar + // calculate offset + int szr = tradelib.ent.Size()-28; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + } + } + else if ( tabs[curtab] == TAB_CHAT ) + { + let bar = SWWMStatusBar(StatusBar); + if ( !bar || (bar.FullHistory.Size() <= 1) ) return res; // no scrollbar + // calculate offset + int szr = bar.FullHistory.Size()-1; + int step = szr-clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + } + else if ( tabs[curtab] == TAB_HELP ) + { + int k1, k2; + [k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu"); + String kstr = bindings.NameKeys(k1,k2); + str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr); + BrokenLines l = fnt.BreakLines(str,629); + if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); + else return res; // no scrollbar + // calculate offset + int szr = l.Count()-28; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + } + else if ( tabs[curtab] == TAB_STATS ) + { + int cnt = 0, num = 22; + if ( sel1 == STAT_MAIN ) cnt = statlist.Size(); + else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size(); + else if ( sel1 == STAT_LEVEL ) cnt = filtered_lstats.Size(); + else if ( sel1 == STAT_ACHIEVEMENT ) + { + cnt = achievements.Size(); + num = 7; + } + if ( cnt <= num ) return res; // no scrollbar + // calculate offset + int szr = cnt-num; + int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); + if ( step != sel0 ) MenuSound("menu/demoscroll"); + ofs0 = sel0 = step; + } + } + else if ( type == MOUSE_Release ) drag = 0; // stop dragging any active scrolls + return res; + } + + private bool CmpInventory( Inventory a, Inventory b ) + { + int ta = 0, tb = 0; + if ( a is 'Weapon' ) ta = 2; + else if ( (a is 'BackpackItem') || (a is 'HammerspaceEmbiggener') ) ta = 3; + else if ( a is 'Ammo' ) ta = 4; + else if ( a is 'MagAmmo' ) ta = 5; + else if ( (a is 'PowerupGiver') || (a is 'AmmoFabricator') || a.bBIGPOWERUP ) ta = -3; + else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = -2; + else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = -1; + else if ( a is 'PuzzleItem' ) ta = 1; + if ( b is 'Weapon' ) tb = 2; + else if ( (b is 'BackpackItem') || (b is 'HammerspaceEmbiggener') ) tb = 3; + else if ( b is 'Ammo' ) tb = 4; + else if ( b is 'MagAmmo' ) tb = 5; + else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || b.bBIGPOWERUP ) tb = -3; + else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = -2; + else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = -1; + else if ( b is 'PuzzleItem' ) tb = 1; + if ( ta == tb ) + { + if ( a is 'Weapon' ) + { + bool dummy; + int slota, slotb; + [dummy, slota] = players[consoleplayer].weapons.LocateWeapon(Weapon(a).GetClass()); + if ( slota == 0 ) slota = 10; + [dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(Weapon(b).GetClass()); + if ( slotb == 0 ) slotb = 10; + if ( slota == slotb ) + return (Weapon(a).SlotPriority <= Weapon(b).SlotPriority); + return slota > slotb; + } + else if ( a is 'Ammo' ) + { + Class usesa, usesb; + for ( int i=0; i)(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 pamoa, pamob; + pamoa = MagAmmo(a).ParentAmmo; + pamob = MagAmmo(b).ParentAmmo; + Class usesa, usesb; + for ( int i=0; i)(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 a, Class 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 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 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 > a, Array b, int l, int h ) + { + Class pv = a[h]; + int i = (l-1); + for ( int j=l; j<=(h-1); j++ ) + { + if ( CmpInventoryClass(pv,a[j]) ) + { + i++; + Class tmp = a[j]; + a[j] = a[i]; + a[i] = tmp; + int tmpi = b[j]; + b[j] = b[i]; + b[i] = tmpi; + } + } + Class 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 > a, Array 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 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 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 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 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 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 a, int l, int h ) + { + if ( l >= h ) return; + int p = partition_collectible(a,l,h); + qsort_collectible(a,l,p-1); + qsort_collectible(a,p+1,h); + } + + private bool CmpAchievement( SWWMAchievement a, SWWMAchievement b ) + { + bool adone = !!(a.state.GetInt()), bdone = !!(b.state.GetInt()); + double afactor = adone?1.:0., bfactor = bdone?1.:0.; + if ( a.progress ) + { + int cur = clamp(a.progress.GetInt(),0,a.maxval); + afactor = cur/double(a.maxval); + } + if ( b.progress ) + { + int cur = clamp(b.progress.GetInt(),0,b.maxval); + bfactor = cur/double(b.maxval); + } + // sort by base index + if ( adone && bdone ) return a.baseindex > b.baseindex; + if ( !adone && !bdone ) + { + // progress sort? + if ( afactor != bfactor ) + return afactor < bfactor; + // sort by base index + return a.baseindex > b.baseindex; + } + // state sort + return bdone; + } + + private int partition_achievements( Array a, int l, int h ) + { + SWWMAchievement pv = a[h]; + int i = (l-1); + for ( int j=l; j<=(h-1); j++ ) + { + if ( CmpAchievement(pv,a[j]) ) + { + i++; + SWWMAchievement tmp = a[j]; + a[j] = a[i]; + a[i] = tmp; + } + } + SWWMAchievement tmp = a[h]; + a[h] = a[i+1]; + a[i+1] = tmp; + return i+1; + } + private void qsort_achievements( Array 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 ( (gamestate != GS_LEVEL) || (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 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(); + if ( !deathmatch ) + { + // keys sorted by their index + int n = Key.GetKeyTypeCount(); + for ( int i=0; i pc = invlist[i].Species; + if ( !pc ) continue; + for ( int j=0; j= j ) i--; + } + } + } + // collectibles, sorted by name + Array 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 lastsel = null; + if ( storelist.Size() > 0 ) lastsel = storelist[sel0]; + storelist.Clear(); + storeunits.Clear(); + // price-sorted buyables/sellables + for ( int i=0; i)(AllActorClasses[i]); + if ( !type ) continue; + // no collectibles + if ( type is 'SWWMCollectible' ) continue; + // no barriers outside doom + if ( !(gameinfo.gametype&GAME_DOOM) && (type is 'EBarrier') ) continue; + // no gravity/tether outside raven + if ( !(gameinfo.gametype&GAME_RAVEN) && ((type is 'GravitySuppressor') || (type is 'SafetyTether')) ) 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)(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() != 'SWWMAmmo') ) 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[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 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 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