// internal "knowledge base" and more Class SWWMKnowledgeBaseMenu : GenericMenu { enum EDemoTab { TAB_MISSION = 0, TAB_STATS, TAB_INVENTORY, TAB_KEYS, TAB_LIBRARY, TAB_STORE, TAB_TRADING, TAB_CHAT, TAB_SECRET, TAB_HELP }; TextureID MainWindow, TabSeparator, WindowSeparator, WindowSeparatorH, FancyBg, EasterEgg, LoreSeparator, LoreSeparatorW; transient CVar lang, fuzz; transient Font TewiFont, MPlusFont; 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) // stats SWWMStats stats; // 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]; // color for chat CVar ccol, tcol; // for checks (duh) Class lastuse; int lastuseamt; int checkuse; int checkdrop; int checksend; // seeeeecret int kcode; // crimey clock stuff int c_year, c_month, c_day, c_hour, c_minute; // mouse stuff Vector2 curmouse; bool isrclick; // returns MPlus if we're playing in Japanese, otherwise returns the requested font Font LangFont( Font req ) { if ( !lang ) lang = CVar.GetCVar('language',players[consoleplayer]); if ( lang.GetString() ~== "jp" ) return MPlusFont; return req; } override void Init( Menu parent ) { Super.Init(parent); if ( (gamestate != GS_LEVEL) || (players[consoleplayer].Health <= 0) ) { // can't open this menu outside of the game or if dead Close(); return; } TewiFont = Font.GetFont('TewiShaded'); MPlusFont = Font.GetFont('MPlusShaded'); FancyBg = TexMan.CheckForTexture("graphics/tempbg.png",TexMan.Type_MiscPatch); MainWindow = TexMan.CheckForTexture("graphics/KBase/MainWindow.png",TexMan.Type_MiscPatch); TabSeparator = TexMan.CheckForTexture("graphics/KBase/TabSeparator.png",TexMan.Type_MiscPatch); WindowSeparator = TexMan.CheckForTexture("graphics/KBase/WindowSeparator.png",TexMan.Type_MiscPatch); WindowSeparatorH = TexMan.CheckForTexture("graphics/KBase/WindowSeparatorH.png",TexMan.Type_MiscPatch); EasterEgg = TexMan.CheckForTexture("graphics/KBase/Drawing.png",TexMan.Type_MiscPatch); LoreSeparator = TexMan.CheckForTexture("graphics/KBase/LibraryTabSeparator.png",TexMan.Type_MiscPatch); LoreSeparatorW = TexMan.CheckForTexture("graphics/KBase/LibraryTabSeparatorJP.png",TexMan.Type_MiscPatch); curtab = CVar.GetCVar('swwm_lasttab',players[consoleplayer]).GetInt(); int maxtab = multiplayer?TAB_CHAT:TAB_STORE; if ( (curtab < TAB_MISSION) || (curtab > maxtab) ) curtab = TAB_MISSION; if ( curtab == TAB_LIBRARY ) sel1 = CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).GetInt(); MenuSound("menu/demoopen"); [ikey[0], ikey[1]] = Bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu"); mkey[0] = Bindings.NameKeys(ikey[0],0); mkey[1] = Bindings.NameKeys(ikey[1],0); ccol = CVar.GetCVar("msg3color",players[consoleplayer]); tcol = CVar.GetCVar("msg4color",players[consoleplayer]); lorelib = SWWMLoreLibrary.Find(players[consoleplayer]); oldloresiz = lorelib.ent.Size(); tradelib = SWWMTradeHistory.Find(players[consoleplayer]); stats = SWWMStats.Find(players[consoleplayer]); tmsg = "$SWWM_MAINCONTROLS"; tmsgtic = gametic+70; if ( gameinfo.gametype&GAME_Heretic ) { c_year = 2171; c_month = 3; c_day = 9; c_hour = 9; c_minute = 41; } else if ( gameinfo.gametype&GAME_Hexen ) { c_year = 2171; c_month = 4; c_day = 24; c_hour = 16; c_minute = 41; } else if ( gameinfo.gametype&GAME_Strife ) { c_year = 2173; c_month = 7; c_day = 12; c_hour = 11; c_minute = 26; } else // Doom { c_year = 2148; c_month = 5; c_day = 5; c_hour = 12; c_minute = 27; } } // please don't look at this private String CrimeTime() { static const int days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; bool leap_year; int y = c_year, m = c_month, d = c_day, h = c_hour, mn = c_minute; int addtime = level.totaltime/Thinker.TICRATE; while ( addtime > 0 ) { leap_year = (!(y%4) && ((y%100) || !(y%400))); if ( addtime >= 86400 ) { addtime -= 86400; d++; int md = days_in_month[m]; if ( leap_year && (m == 1) ) md++; if ( d >= md ) { d = 0; m++; if ( m > 11 ) { m = 0; y++; } } } else if ( addtime >= 3600 ) { addtime -= 3600; h++; if ( h > 23 ) { h = 0; d++; int md = days_in_month[m]; if ( leap_year && (m == 1) ) md++; if ( d >= md ) { d = 0; m++; if ( m > 11 ) { m = 0; y++; } } } } else if ( addtime >= 60 ) { addtime -= 60; mn++; if ( mn > 59 ) { mn = 0; h++; if ( h > 23 ) { h = 0; d++; int md = days_in_month[m]; if ( leap_year && (m == 1) ) md++; if ( d >= md ) { d = 0; m++; if ( m > 11 ) { m = 0; y++; } } } } } else { // we finished counting addtime = 0; } } return String.Format("%04d-%02d-%02d %02d:%02d",y,m+1,d+1,h,mn); } 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"); curtab = TAB_SECRET; 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"); Close(); return true; case MKEY_PAGEUP: MenuSound("menu/demotab"); if ( (curtab <= TAB_MISSION) || (curtab > maxtab) ) curtab = maxtab; else curtab--; CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(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 ( curtab < maxtab ) curtab++; else curtab = TAB_MISSION; CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(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 ( curtab == TAB_HELP ) { String str = StringTable.Localize("$SWWM_HELPTXT"); if ( multiplayer ) str = str..StringTable.Localize("$SWWM_HELPTXT_MP"); BrokenLines l = fnt.BreakLines(str,629); if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); if ( (l.Count() > 28) && (sel0 < l.Count()-28) ) { MenuSound("menu/demoscroll"); sel0++; } } else if ( curtab == TAB_MISSION ) { String str = StringTable.Localize("$SWWM_MISSION_NONE"); if ( gameinfo.gametype&GAME_Doom ) str = StringTable.Localize("$SWWM_MISSION_DOOM"); else if ( gameinfo.gametype&GAME_Heretic ) str = StringTable.Localize("$SWWM_MISSION_HERETIC"); else if ( gameinfo.gametype&GAME_Hexen ) str = StringTable.Localize("$SWWM_MISSION_HEXEN"); else if ( gameinfo.gametype&GAME_Strife ) str = StringTable.Localize(players[consoleplayer].logtext); 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 ( (curtab == TAB_CHAT) && (StatusBar is 'SWWMStatusBar') && (sel0 > 0) ) { MenuSound("menu/demoscroll"); ofs0 = --sel0; } else if ( (curtab == TAB_LIBRARY) && (lorelist.Size() > 1) ) { if ( sub ) { String str = StringTable.Localize(lorelist[sel0].text); int ofs = (lang.GetString()~=="jp")?212:132; if ( lorelist.Size() > 26 ) ofs += 8; BrokenLines l = fnt.BreakLines(str,635-ofs); if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs); if ( (l.Count() > 28) && (sel2 < l.Count()-28) ) { MenuSound("menu/demoscroll"); ofs2 = ++sel2; } } else { MenuSound("menu/demoscroll"); if ( sel0 >= lorelist.Size()-1 ) ofs0 = sel0 = 0; else { sel0++; // update ofs so selection stays on-screen int ofs = max(ofs0-26,0); if ( sel0 < ofs ) ofs0 = sel0+26; else if ( sel0 > ofs+26 ) ofs0 = sel0; } } } else if ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS) || ((curtab == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 1) ) { MenuSound("menu/demoscroll"); ofs0 = ++sel0; if ( sel0 >= invlist.Size() ) ofs0 = sel0 = 0; } else if ( (curtab == TAB_STORE) && (storelist.Size() > 1) ) { MenuSound("menu/demoscroll"); ofs0 = ++sel0; if ( sel0 >= storelist.Size() ) ofs0 = sel0 = 0; } else if ( 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; } } return true; case MKEY_UP: if ( (curtab == TAB_HELP) && (sel0 > 0) ) { MenuSound("menu/demoscroll"); ofs0 = --sel0; } else if ( (curtab == TAB_MISSION) && (sel0 > 0) ) { MenuSound("menu/demoscroll"); ofs0 = --sel0; } else if ( (curtab == TAB_CHAT) && (StatusBar is 'SWWMStatusBar') && (sel0 < SWWMStatusBar(StatusBar).FullHistory.Size()-1) ) { MenuSound("menu/demoscroll"); ofs0 = ++sel0; } else if ( (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 ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS) || ((curtab == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 1) ) { MenuSound("menu/demoscroll"); ofs0 = --sel0; if ( sel0 < 0 ) ofs0 = sel0 = invlist.Size()-1; } else if ( (curtab == TAB_STORE) && (storelist.Size() > 1) ) { MenuSound("menu/demoscroll"); ofs0 = --sel0; if ( sel0 < 0 ) ofs0 = sel0 = storelist.Size()-1; } else if ( 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; } } return true; case MKEY_RIGHT: if ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS) || ((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 ( (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 ( curtab == TAB_LIBRARY ) { MenuSound("menu/demoscroll"); ofs0 = sel0 = 0; sub = false; ofs1 = ++sel1; if ( sel1 > LORE_LORE ) ofs1 = sel1 = LORE_ITEM; CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).SetInt(sel1); } else if ( (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"); } return true; case MKEY_LEFT: if ( ((curtab == TAB_INVENTORY) || (curtab == TAB_KEYS) || ((curtab == TAB_TRADING) && sub && (sel0 != -1))) && (invlist.Size() > 21) && (sel0-22 >= 0) ) { MenuSound("menu/demoscroll"); ofs0 = sel0 -= 22; } else if ( (curtab == TAB_STORE) && (storelist.Size() > 21) && (sel0-22 >= 0) ) { MenuSound("menu/demoscroll"); ofs0 = sel0 -= 22; } else if ( curtab == TAB_LIBRARY ) { MenuSound("menu/demoscroll"); ofs0 = sel0 = 0; sub = false; ofs1 = --sel1; if ( sel1 < LORE_ITEM ) ofs1 = sel1 = LORE_LORE; CVar.GetCVar('swwm_lastloretab',players[consoleplayer]).SetInt(sel1); } else if ( (curtab == TAB_TRADING) && !sub && (playerlist.Size() > 21) && (sel0-22 >= 0) ) // lol is this ever going to happen { ofs0 = sel0 -= 22; MenuSound("menu/demoscroll"); } return true; case MKEY_ENTER: if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) && (sel0 < invlist.Size()) ) { // can't use this if ( invlist[sel0] is 'Ammo' ) return true; lastuse = invlist[sel0].GetClass(); lastuseamt = invlist[sel0].Amount; // don't check weapons (or the lamp) if ( !(invlist[sel0] is 'Weapon') && !(invlist[sel0] is 'SWWMLamp') ) checkuse = gametic+(multiplayer?5:1); EventHandler.SendNetworkEvent(String.Format("swwmuseitem.%s",invlist[sel0].GetClassName()),consoleplayer); } else if ( (curtab == TAB_STORE) && (storelist.Size() > 0) && (sel0 < storelist.Size()) ) { let itm = GetDefaultByType(storelist[sel0]); let amt = storeunits[sel0]; int price = int(itm.Stamina*(1.+.75*(amt-1))); if ( !SWWMCredits.CanTake(players[consoleplayer],price) ) { MenuSound("menu/noinvuse"); tmsg = StringTable.Localize("$SWWM_STOREMUNS"); tmsgtic = gametic+70; return true; } let cur = players[consoleplayer].mo.FindInventory(storelist[sel0]); int camt, max; if ( cur ) { camt = cur.Amount; max = cur.MaxAmount; } else { camt = 0; max = itm.MaxAmount; } if ( camt >= max ) { MenuSound("menu/noinvuse"); tmsg = StringTable.Localize("$SWWM_STOREFULL"); tmsgtic = gametic+70; return true; } EventHandler.SendNetworkEvent(String.Format("swwmstoregive.%s",storelist[sel0].GetClassName()),consoleplayer,price,amt); if ( (camt+amt) >= max ) ofs1 = sel0 = max(0,sel0-1); MenuSound("menu/buyinv"); } else if ( 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 ( 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) ) { // trade item lastuse = invlist[sel1].GetClass(); lastuseamt = invlist[sel1].Amount; checksend = gametic+(multiplayer?5:1); EventHandler.SendNetworkEvent(String.Format("swwmtrade.%s",invlist[sel1].GetClassName()),consoleplayer,playerlist[sel0]); } } return true; case MKEY_CLEAR: if ( (curtab == TAB_INVENTORY) && (invlist.Size() > 0) ) { lastuse = invlist[sel0].GetClass(); lastuseamt = invlist[sel0].Amount; checkdrop = gametic+(multiplayer?5:1); EventHandler.SendNetworkEvent(String.Format("swwmdropitem.%s",invlist[sel0].GetClassName()),consoleplayer); } else if ( (curtab == TAB_LIBRARY) && sub ) { MenuSound("menu/democlose"); sub = false; } else if ( 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" }; xx = 0; int maxtab = multiplayer?TAB_CHAT:TAB_STORE; for ( int i=TAB_MISSION; i<=maxtab; i++ ) { len = fnt.StringWidth(tabnames[i])+6; if ( (mpos.x >= xx) && (mpos.x < xx+len) ) { if ( curtab == i ) break; MenuSound("menu/demotab"); curtab = i; CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(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 ( (curtab == TAB_MISSION) && !isrclick ) { // are we clicking where the scrollbar should be? if ( mpos.x < 632 ) return res; String str = StringTable.Localize("$SWWM_MISSION_NONE"); if ( gameinfo.gametype&GAME_Doom ) str = StringTable.Localize("$SWWM_MISSION_DOOM"); else if ( gameinfo.gametype&GAME_Heretic ) str = StringTable.Localize("$SWWM_MISSION_HERETIC"); else if ( gameinfo.gametype&GAME_Hexen ) str = StringTable.Localize("$SWWM_MISSION_HEXEN"); else if ( gameinfo.gametype&GAME_Strife ) str = StringTable.Localize(players[consoleplayer].logtext); 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 ( 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].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 = (invlist.Size()/22)+1; 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 ( (curtab == TAB_INVENTORY) && isrclick ) MenuEvent(MKEY_CLEAR,false); else if ( !isrclick ) MenuEvent(MKEY_ENTER,false); return res; } yy += 16; if ( yy >= 370 ) { xx += longest+24; yy = 23; cols++; if ( cols > maxcol ) break; } } } else if ( !isrclick ) { // check that scrollbar is present if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar int longest = 0, len; int maxcol, totalcol; for ( int i=0; i 1) || (!(invlist[i] is 'PuzzleItem') && (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 = (invlist.Size()/22)+1; 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 ( (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].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 = (invlist.Size()/22)+1; 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 ( (curtab == TAB_INVENTORY) && isrclick ) MenuEvent(MKEY_CLEAR,false); else if ( !isrclick ) MenuEvent(MKEY_ENTER,false); return res; } yy += 16; if ( yy >= 370 ) { xx += longest+24; yy = 23; cols++; if ( cols > maxcol ) break; } } } else if ( !isrclick ) { // check that scrollbar is present if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar int longest = 0, len; int maxcol, totalcol; for ( int i=0; i 1) || (!(invlist[i] is 'PuzzleItem') && (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 = (invlist.Size()/22)+1; 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 ( (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 = (invlist.Size()/22)+1; 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; // do nothing, we just selected it return res; } yy += 16; if ( yy >= 370 ) { xx += longest+24; yy = 23; cols++; if ( cols > maxcol ) break; } } } else if ( !isrclick ) { // check that scrollbar is present if ( invlist.Size() <= 0 ) return res; // definitely no scrollbar int longest = 0, len; int maxcol, totalcol; for ( int i=0; i longest ) longest = len; } maxcol = 622/(longest+24); totalcol = (invlist.Size()/22)+1; 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 ( (curtab == TAB_STORE) && !isrclick ) { // check what item we clicked if ( mpos.y < 377 ) { if ( storelist.Size() <= 0 ) return res; // clicked nothing xx = 9; yy = 23; int longest = 0, len; int maxcol, totalcol; for ( int i=0; i 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 = (storelist.Size()/22)+1; // 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; } } } else if ( !isrclick ) { // check that scrollbar is present if ( storelist.Size() <= 0 ) return res; // definitely no scrollbar xx = 9; yy = 23; int longest = 0, len; int maxcol, totalcol; for ( int i=0; i 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 = (storelist.Size()/22)+1; 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 ( (curtab == TAB_LIBRARY) && !isrclick ) { double midp = (lang.GetString()~=="jp")?206:126; if ( mpos.x < midp ) { if ( mpos.y < 28 ) { // switch category if ( mpos.x < midp/2. ) MenuEvent(MKEY_LEFT,false); else MenuEvent(MKEY_RIGHT,false); } else { // check what item we clicked int xx = 3; int yy = 32; int ofs = max(0,ofs0-26); for ( int i=ofs; i 370 ) break; // check boundary if ( (mpos.y >= yy) && (mpos.y < yy+14) ) { if ( sel0 != i ) MenuSound("menu/demoscroll"); if ( (sel0 != i) || !sub ) { sel0 = i; sub = false; MenuEvent(MKEY_Enter,false); } return res; } yy += 13; } } } else if ( mpos.x < midp+8 ) { // check that scrollbar is present if ( lorelist.Size() <= 27 ) return res; // no scrollbar // calculate offset int szr = lorelist.Size()-27; int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); if ( step ) step += 26; if ( step != ofs0 ) MenuSound("menu/demoscroll"); ofs0 = step; drag = 1; } else if ( sub && (mpos.x >= 632) ) { // check that scrollbar is present str = StringTable.Localize(lorelist[sel0].text); int ofs = (lang.GetString()~=="jp")?212:132; if ( lorelist.Size() > 26 ) ofs += 8; BrokenLines l = fnt.BreakLines(str,635-ofs); if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs); else return res; // no scrollbar // calculate offset int szr = l.Count()-28; int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); if ( step != sel2 ) MenuSound("menu/demoscroll"); ofs2 = sel2 = step; drag = 3; } } else if ( (curtab == TAB_TRADING) && isrclick ) { MenuEvent(MKEY_Back,false); } else if ( (curtab == TAB_TRADING) && !isrclick ) { if ( !sub ) { // check what item we clicked if ( mpos.y < 377 ) { if ( playerlist.Size() <= 0 ) return res; // clicked nothing int longest = 0, len; int maxcol, totalcol; for ( int i=0; i longest ) longest = len; } maxcol = 622/(longest+24); totalcol = (playerlist.Size()/22)+1; 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; } } } else if ( !isrclick ) { // check that scrollbar is present (will it ever be???) if ( playerlist.Size() <= 0 ) return res; // definitely no scrollbar int longest = 0, len; int maxcol, totalcol; for ( int i=0; i longest ) longest = len; } maxcol = 622/(longest+24); totalcol = (playerlist.Size()/22)+1; 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 ( (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 ( (curtab == TAB_HELP) && !isrclick ) { // are we clicking where the scrollbar should be? if ( mpos.x < 632 ) return res; int k1, k2; [k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu"); String kstr = bindings.NameKeys(k1,k2); str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr); BrokenLines l = fnt.BreakLines(str,629); if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); else return res; // no scrollbar // calculate offset int szr = l.Count()-28; int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); if ( step != sel0 ) MenuSound("menu/demoscroll"); ofs0 = sel0 = step; drag = 1; } } else if ( (type == MOUSE_Move) && drag ) // scroll dragging { if ( curtab == TAB_MISSION ) { String str = StringTable.Localize("$SWWM_MISSION_NONE"); if ( gameinfo.gametype&GAME_Doom ) str = StringTable.Localize("$SWWM_MISSION_DOOM"); else if ( gameinfo.gametype&GAME_Heretic ) str = StringTable.Localize("$SWWM_MISSION_HERETIC"); else if ( gameinfo.gametype&GAME_Hexen ) str = StringTable.Localize("$SWWM_MISSION_HEXEN"); else if ( gameinfo.gametype&GAME_Strife ) str = StringTable.Localize(players[consoleplayer].logtext); 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 ( (curtab == TAB_INVENTORY) || ((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].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 = (invlist.Size()/22)+1; 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 ( 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 = (invlist.Size()/22)+1; 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 ( 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 ) 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 = (storelist.Size()/22)+1; 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 ( curtab == TAB_LIBRARY ) { if ( drag == 1 ) { // check that scrollbar is present if ( lorelist.Size() <= 27 ) return res; // no scrollbar // calculate offset int szr = lorelist.Size()-27; int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); if ( step ) step += 26; if ( step != ofs0 ) MenuSound("menu/demoscroll"); ofs0 = step; } else if ( drag == 3 ) { // check that scrollbar is present str = StringTable.Localize(lorelist[sel0].text); int ofs = (lang.GetString()~=="jp")?212:132; if ( lorelist.Size() > 26 ) ofs += 8; BrokenLines l = fnt.BreakLines(str,635-ofs); if ( l.Count() > 28 ) l = fnt.BreakLines(str,626-ofs); else return res; // no scrollbar // calculate offset int szr = l.Count()-28; int step = clamp(rnd((mpos.y-24.)/(353./szr)),0,szr); if ( step != sel2 ) MenuSound("menu/demoscroll"); ofs2 = sel2 = step; } } else if ( 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 = (invlist.Size()/22)+1; 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 ( 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 ( 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 ( 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 'Ammo') || (a is 'BackpackItem') || (a is 'HammerspaceEmbiggener') ) ta = 3; 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 'Ammo') || (b is 'BackpackItem') || (b is 'HammerspaceEmbiggener') ) tb = 3; 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; 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 = 2; else if ( a is 'Ammo' ) ta = 1; else if ( (a is 'PowerupGiver') || (a is 'AmmoFabricator') || da.bBIGPOWERUP ) ta = 3; else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = 5; else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = 4; if ( b is 'Weapon' ) tb = 2; else if ( b is 'Ammo' ) tb = 1; else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || db.bBIGPOWERUP ) tb = 3; else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = 5; else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = 4; 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); } override void Ticker() { Super.Ticker(); if ( players[consoleplayer].Health <= 0 ) { // ded Close(); return; } if ( !multiplayer && ((curtab == TAB_TRADING) || (curtab == TAB_CHAT)) ) { MenuSound("menu/demotab"); curtab = TAB_MISSION; CVar.GetCVar('swwm_lasttab',players[consoleplayer]).SetInt(curtab); sel0 = sel1 = sel2 = 0; ofs0 = ofs1 = ofs2 = 0; sub = false; invlist.Clear(); lorelist.Clear(); storelist.Clear(); storeunits.Clear(); playerlist.Clear(); } if ( 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 'SWWMGesture') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectable') ) 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); } else if ( curtab == TAB_KEYS ) { invlist.Clear(); // keys sorted by their index int n = Key.GetKeyTypeCount(); for ( int i=0; i)(AllActorClasses[i]); if ( !type ) continue; // no fabricators outside of hexen if ( !(gameinfo.gametype&GAME_Hexen) && (type is 'AmmoFabricator') ) continue; // skip maxed items let cur = players[consoleplayer].mo.FindInventory(type); if ( cur && (cur.Amount >= cur.MaxAmount) ) continue; // ignore ammo for weapons not owned bool notownedammo = false; if ( type is 'Ammo' ) { notownedammo = true; let amo = (Class)(type); for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.inv ) { if ( !(inv is 'Weapon') ) continue; if ( (Weapon(inv).AmmoType1 == amo) || (Weapon(inv).AmmoType2 == amo) ) { notownedammo = false; break; } if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).UsesAmmo(amo) ) { notownedammo = false; break; } } } if ( notownedammo ) continue; let inv = GetDefaultByType(type); // skip unimplemented weapons if ( type is 'Weapon' ) { let ready = inv.FindState("Ready"); if ( !ready || !ready.ValidateSpriteFrame() ) continue; } // ignore child ammos if ( (type is 'Ammo') && (type.GetParentClass() != 'Ammo') ) continue; if ( inv.Stamina <= 0 ) continue; int amt = inv.Amount; int price = int(inv.Stamina*(1.+.75*(amt-1))); if ( type is 'Ammo' ) { int maxneed = cur.MaxAmount-cur.Amount; // get the largest affordable child pickup amount (that we need) for ( int j=0; j)(AllActorClasses[j]); if ( !type2 || (type2.GetParentClass() != type) ) continue; let inv2 = GetDefaultByType(type2); int cprice = int(inv.Stamina*(1.+.75*(inv2.Amount-1))); if ( (inv2.Amount > amt) && (inv2.Amount <= maxneed) && (cprice <= SWWMCredits.Get(players[consoleplayer])) ) { price = cprice; amt = inv2.Amount; } } } // sort by unit price bool greater = false; for ( int j=0; j 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); } else if ( 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 ( curtab == TAB_TRADING ) { // build player list playerlist.Clear(); 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); } } // check if use succeeded if ( checkuse && (gametic > checkuse) ) { checkuse = 0; int amt = players[consoleplayer].mo.CountInv(lastuse); if ( amt == lastuseamt ) { tmsg = StringTable.Localize("$SWWM_INVFAIL"); tmsgtic = gametic+70; MenuSound("menu/noinvuse"); } else { MenuSound(GetDefaultByType(lastuse).UseSound); if ( (amt == 0) && (sel0 >= invlist.Size()) ) sel0 = max(0,sel0-1); } } // check if drop succeeded if ( checkdrop && (gametic > checkdrop) ) { checkdrop = 0; int amt = players[consoleplayer].mo.CountInv(lastuse); if ( amt == lastuseamt ) { tmsg = StringTable.Localize("$SWWM_INVNDROP"); tmsgtic = gametic+70; MenuSound("menu/noinvuse"); } else if ( (amt == 0) && (sel0 >= invlist.Size()) ) sel0 = max(0,sel0-1); } // check if trade succeeded if ( checksend && (gametic > checksend) ) { checksend = 0; int amt = players[consoleplayer].mo.CountInv(lastuse); if ( amt == lastuseamt ) { tmsg = StringTable.Localize("$SWWM_TRADEFULL"); tmsgtic = gametic+70; MenuSound("menu/noinvuse"); } else { MenuSound("menu/demosel"); if ( (amt == 0) && (sel1 >= invlist.Size()) ) sel1 = max(0,sel1-1); } } } override bool OnUiEvent( UIEvent ev ) { switch ( ev.type ) { case UIEvent.Type_KeyDown: if ( ev.keychar == UiEvent.Key_F1 ) { if ( curtab == TAB_HELP ) curtab = oldtab; else { oldtab = curtab; curtab = TAB_HELP; } 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"); Close(); return true; } break; case UIEvent.Type_WheelDown: if ( (curtab == TAB_LIBRARY) && sub && (lorelist.Size() > 1) ) { // special handling, need to check which side is being scrolled double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.); Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; Vector2 origin = (ss.x-640,ss.y-400)/2.; Vector2 mpos = (curmouse/hs)-origin; double midp = (lang.GetString()~=="jp")?206:126; if ( ((lorelist.Size() > 27) && (mpos.x < midp+8)) || (mpos.x < midp) ) { MenuSound("menu/democlose"); sub = false; } } return MenuEvent(MKEY_DOWN,false); break; case UIEvent.Type_WheelUp: if ( (curtab == TAB_LIBRARY) && sub && (lorelist.Size() > 1) ) { // special handling, need to check which side is being scrolled double hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.); Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; Vector2 origin = (ss.x-640,ss.y-400)/2.; Vector2 mpos = (curmouse/hs)-origin; double midp = (lang.GetString()~=="jp")?206:126; if ( ((lorelist.Size() > 27) && (mpos.x < midp+8)) || (mpos.x < midp) ) { MenuSound("menu/democlose"); sub = false; } } return MenuEvent(MKEY_UP,false); break; case UIEvent.Type_LButtonDown: isrclick = false; return Super.OnUIEvent(ev); break; case UIEvent.Type_RButtonDown: isrclick = true; // copy over what base menus do for L click int y = ev.MouseY; bool res = MouseEventBack(MOUSE_Click,ev.MouseX,y); if ( res ) y = -1; res |= MouseEvent(MOUSE_Click,ev.MouseX,y); if ( res ) SetCapture(true); return false; break; case UIEvent.Type_RButtonUp: // copy over what base menus do for L release if ( mMouseCapture ) { SetCapture(false); int y = ev.MouseY; bool res = MouseEventBack(MOUSE_Release,ev.MouseX,y); if ( res ) y = -1; res |= MouseEvent(MOUSE_Release,ev.MouseX,y); } return false; break; case UIEvent.Type_MouseMove: // store coords curmouse = (ev.MouseX,ev.MouseY); break; } return Super.OnUIEvent(ev); } override void Drawer() { Super.Drawer(); Font fnt = LangFont(TewiFont); String str; double hs; if ( (Screen.GetWidth() < 640) || (Screen.GetHeight() < 400) ) hs = max(min(floor(Screen.GetWidth()/320.),floor(Screen.GetHeight()/200.)),1.)/2.; else hs = max(min(floor(Screen.GetWidth()/640.),floor(Screen.GetHeight()/400.)),1.); Vector2 ss = (Screen.GetWidth(),Screen.GetHeight())/hs; Vector2 origin = (ss.x-640,ss.y-400)/2.; if ( !fuzz ) fuzz = CVar.GetCvar('swwm_fuzz',players[consoleplayer]); if ( fuzz.GetBool() ) Screen.DrawTexture(FancyBg,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,.5); Screen.DrawTexture(MainWindow,false,origin.x,origin.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); // draw tabs double xx = 3, yy = 0; static const string tabnames[] = { "$SWWM_MISSTAB", "$SWWM_STATTAB", "$SWWM_INVTAB", "$SWWM_KEYTAB", "$SWWM_KBASETAB", "$SWWM_STORETAB", "$SWWM_TRADETAB", "$SWWM_CHATTAB", "$SWWM_SECRETTAB", "$SWWM_HELPTAB" }; static const string ltabnames[] = { "$SWWM_LORETAB0", "$SWWM_LORETAB1", "$SWWM_LORETAB2" }; int maxtab = multiplayer?TAB_CHAT:TAB_STORE; int mx = maxtab; if ( (curtab > TAB_CHAT) ) mx++; for ( int i=0; i<=mx; i++ ) { str = StringTable.Localize(tabnames[(i>maxtab)?curtab:i]); Screen.DrawText(fnt,((i>maxtab)||(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 ( curtab == TAB_MISSION ) { if ( gameinfo.gametype&GAME_Doom ) str = StringTable.Localize("$SWWM_MISSION_DOOM"); else if ( gameinfo.gametype&GAME_Heretic ) str = StringTable.Localize("$SWWM_MISSION_HERETIC"); else if ( gameinfo.gametype&GAME_Hexen ) str = StringTable.Localize("$SWWM_MISSION_HEXEN"); else if ( gameinfo.gametype&GAME_Strife ) str = StringTable.Localize(players[consoleplayer].logtext); else str = StringTable.Localize("$SWWM_MISSION_NONE"); 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= 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 ( curtab == TAB_STATS ) { xx = 9; yy = 23; // wish I could use macros for this int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE)); int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60; int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60; str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec); 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; if ( stats.grounddist > 32000. ) str = String.Format("\cx%s\c-%g\cukm\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32000.); else str = String.Format("\cx%s\c-%g\cum\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32.); 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; if ( stats.airdist > 32000. ) str = String.Format("\cx%s\c-%g\cukm\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32000.); else str = String.Format("\cx%s\c-%g\cum\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32.); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBOOST"),stats.boostcount); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDASH"),stats.dashcount); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSTOMP"),stats.stompcount); 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; str = String.Format("\cx%s\c-%g\cul\c-",StringTable.Localize("$SWWM_STATFUEL"),stats.fuelusage/60.); 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; str = String.Format("\cx%s\c-%g\cukm/h\c-",StringTable.Localize("$SWWM_STATSPEED"),stats.topspeed*32000./(3600*Thinker.TICRATE)); 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; thour = (stats.airtime/(3600*Thinker.TICRATE)); tmin = (stats.airtime/(60*Thinker.TICRATE))%60; tsec = (stats.airtime/Thinker.TICRATE)%60; str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATAIRTIME"),thour,tmin,tsec); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATKILLS"),stats.kills); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDEATHS"),stats.deaths); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDDEALT"),stats.damagedealt); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.damagetaken); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTDEALT"),stats.topdealt); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTTAKEN"),stats.toptaken); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill); 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; str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill); 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; str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP")); if ( stats.favweapon == -1 ) str = str.."N/A"; else { let def = GetDefaultByType(stats.wstats[stats.favweapon].w); str = str..def.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 += 16; 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); 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; } else if ( (curtab == TAB_INVENTORY) || ((curtab == TAB_TRADING) && sub && (sel0 != -1)) ) { if ( invlist.Size() <= 0 ) { str = StringTable.Localize("$SWWM_NOINV"); Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-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 1) || (!(invlist[i] is 'PuzzleItem') && (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 = (invlist.Size()/22)+1; int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22); for ( int i=ofs; i 1) || (!(invlist[i] is 'PuzzleItem') && (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 'Ammo') || (invlist[i] is 'BackpackItem') || (invlist[i] is 'HammerspaceEmbiggener') ) 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; 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 ( curtab == TAB_KEYS ) { if ( invlist.Size() <= 0 ) { str = StringTable.Localize("$SWWM_NOKEYS"); Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-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 longest ) longest = len; } maxcol = 622/(longest+24); totalcol = (invlist.Size()/22)+1; int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22); for ( int i=0; i= 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 ( curtab == TAB_LIBRARY ) { xx = 3; yy = 14; // draw the category int twidth = (lang.GetString()~=="jp")?200:120; str = StringTable.Localize(ltabnames[sel1]); Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+yy,"<",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx+twidth-6,origin.y+yy,">",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+(twidth-fnt.StringWidth(str))/2,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); // draw "unread" indicators on sides int ltab = sel1-1; if ( ltab < 0 ) ltab = 2; int rtab = sel1+1; if ( rtab > 2 ) rtab = 0; bool lunread = false, runread = false; for ( int i=0; i 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+((634-xx)-fnt.StringWidth(str))/2.,(ss.y-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 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 ( curtab == TAB_STORE ) { if ( storelist.Size() <= 0 ) { str = StringTable.Localize("$SWWM_NOSTORE"); Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-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 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 = (storelist.Size()/22)+1; int ofs = int(max(0,(ofs0/22)-(maxcol-1))*22); int cols = 1; for ( int i=ofs; i 1) || (storelist[i] is 'Ammo') ) str = String.Format("%dx %s",storeunits[i],def.GetTag()); else str = def.GetTag(); int clscol = Font.CR_WHITE; if ( storelist[i] is 'Weapon' ) clscol = Font.CR_GOLD; else if ( storelist[i] is 'Ammo' ) clscol = Font.CR_BROWN; else if ( (storelist[i] is 'PowerupGiver') || (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 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 ( curtab == TAB_TRADING ) { if ( !sub ) { if ( playerlist.Size() <= 0 ) { str = StringTable.Localize("$SWWM_NOTRADE"); Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-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 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,(ss.x-fnt.StringWidth(str))/2.,(ss.y-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= 370 ) break; let ent = tradelib.ent[tradelib.ent.Size()-(i+1)]; int thour = (ent.timestamp/(3600*Thinker.TICRATE)); int tmin = (ent.timestamp/(60*Thinker.TICRATE))%60; int tsec = (ent.timestamp/Thinker.TICRATE)%60; str = String.Format("\cu[\cc%02d\cu:\cc%02d\cu:\cc%02d\cu]\c- ",thour,tmin,tsec); str.AppendFormat("\cd%s %s\cd: \cj%dx \cf%s\c-",ent.type?StringTable.Localize("$SWWM_TRADEFROM"):StringTable.Localize("$SWWM_TRADETO"),ent.other,ent.amt,GetDefaultByType(ent.what).GetTag()); Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); yy += 13; } // scrollbar if ( tradelib.ent.Size() > 28 ) { Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); xx = 634; int szr = tradelib.ent.Size()-28; yy = floor(ofs*(353./szr))+17; Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } } } } else if ( curtab == TAB_CHAT ) { let bar = SWWMStatusBar(StatusBar); if ( !bar || (bar.FullHistory.Size() <= 0) ) { str = StringTable.Localize("$SWWM_NOCHAT"); Screen.DrawText(fnt,Font.CR_FIRE,(ss.x-fnt.StringWidth(str))/2.,(ss.y-fnt.GetHeight())/2.,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); return; } int margin = TewiFont.StringWidth("[00:00:00] "); int ofs = bar.FullHistory.Size()-(1+sel0); xx = 3; yy = 379; for ( int i=ofs; i>=0; i-- ) { int col = (bar.FullHistory[i].type==PRINT_TEAMCHAT)?tcol.GetInt():ccol.GetInt(); int thour = (bar.FullHistory[i].tic/(3600*Thinker.TICRATE)); int tmin = (bar.FullHistory[i].tic/(60*Thinker.TICRATE))%60; int tsec = (bar.FullHistory[i].tic/Thinker.TICRATE)%60; str = String.Format("\cu[\c-%02d\cu:\c-%02d\cu:\c-%02d\cu]\c- ",thour,tmin,tsec); BrokenLines l = fnt.BreakLines(bar.FullHistory[i].str,626-margin); double by = yy-13*l.Count(); if ( by < 17 ) break; Screen.DrawText(TewiFont,Font.CR_WHITE,origin.x+xx,origin.y+by,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); for ( int j=0; j 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 ( curtab == TAB_SECRET ) { Screen.DrawTexture(EasterEgg,false,origin.x+20,origin.y+40,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); str = StringTable.Localize("$SWWM_TODEMO"); Screen.DrawText(fnt,Font.CR_FIRE,origin.x+(640-fnt.StringWidth(str))/2.,origin.y+350,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } else if ( curtab == TAB_HELP ) { // help tab int k1, k2; [k1, k2] = bindings.GetKeysForCommand("openmenu SWWMKnowledgeBaseMenu"); String kstr = bindings.NameKeys(k1,k2); str = String.Format(StringTable.Localize("$SWWM_HELPTXT"),kstr); if ( multiplayer ) str = str..StringTable.Localize("$SWWM_HELPTXT_MP"); BrokenLines l = fnt.BreakLines(str,629); if ( l.Count() > 28 ) l = fnt.BreakLines(str,620); xx = 6; yy = 17; int ofs = clamp(sel0,0,max(0,l.Count()-28)); for ( int i=ofs; i= 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); } } } }