// the keychain Class DemolitionistKeychainTab : DemolitionistMenuTab { DemolitionistMenuList invlist; int ofs, maxofs; double smofs; bool drag; private bool CmpCollectible( Inventory a, Inventory b ) { int ta = 0, tb = 0; if ( a is 'Key' ) ta = 1; if ( b is 'Key' ) tb = 1; if ( ta == tb ) { if ( a is 'Key' ) { // sort by "index" int n = Key.GetKeyTypeCount(); int ida = -1, idb = -1; for ( int i=0; i idb; } // collectible if ( a.Stamina == b.Stamina ) { // alphabetic return a.GetTag() > b.GetTag(); } // descending value return a.default.Stamina < b.default.Stamina; } return ta > tb; } private int partition_collectible( Array a, int l, int h ) { DemolitionistMenuListItem pv = a[h]; int i = (l-1); for ( int j=l; j<=(h-1); j++ ) { if ( CmpCollectible(DemolitionistMenuInvItem(pv).inv,DemolitionistMenuInvItem(a[j]).inv) ) { i++; DemolitionistMenuListItem tmp = a[j]; a[j] = a[i]; a[i] = tmp; } } DemolitionistMenuListItem 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); } override DemolitionistMenuTab Init( DemolitionistMenu master ) { title = StringTable.Localize("$SWWM_KEYTAB"); bHidden = deathmatch; return Super.Init(master); } override void OnDestroy() { Super.OnDestroy(); if ( invlist ) invlist.Destroy(); } override void OnSelect() { smofs = ofs; } override void OnDeselect() { smofs = ofs; } private bool FilterInventory( Inventory inv ) { if ( !inv || (inv.Owner != players[consoleplayer].mo) ) return true; if ( !(inv is 'Key') && !(inv is 'SWWMCollectible') ) return true; return false; } override void Ticker() { bool mustsort = false; bool skipsel = false; if ( !invlist ) { mustsort = true; skipsel = true; // initialize invlist = new("DemolitionistMenuList"); invlist.master = master; invlist.selected = 0; int j = 0; for ( Inventory i=players[consoleplayer].mo.inv; i; i=i.inv ) { if ( FilterInventory(i) ) continue; invlist.items.Push(new("DemolitionistMenuInvItem").Init(master,i)); } } else { // check if current entries must be deleted for ( int i=0; i i ) invlist.selected = max(0,invlist.selected-1); i--; } // check if new entries must be added for ( Inventory i=players[consoleplayer].mo.inv; i; i=i.inv ) { if ( FilterInventory(i) ) continue; // skip if it's already there bool skipme = false; for ( int j=0; j (master.ws.y-62) ) { xx += maxw+16; // pad yy = 0; } } maxofs = max(0,maxofs-int(master.ws.x-18)); if ( ofs > maxofs ) smofs = ofs = maxofs; // update smooth scroll smofs = (smofs*.6)+(ofs*.4); if ( abs(smofs-ofs) < (1./master.hs) ) smofs = ofs; // tick the list invlist.Ticker(); } // called when sending a scroll input // returns true if the position actually changed // speed: how many pixels to move (either back or forward) bool Scroll( int speed ) { if ( maxofs <= 0 ) return false; int oldofs = ofs; ofs = clamp(ofs+speed,0,maxofs); return (ofs != oldofs); } // called when clicking on our scrollbar // returns true if the position actually changed // x: relative click position bool SetOffset( double x ) { if ( maxofs <= 0 ) return false; int oldofs = ofs; ofs = clamp(int(round((x-5.)/((master.ws.x-10.)/maxofs))),0,maxofs); return (ofs != oldofs); } // called when keyboard scrolling void KBScroll() { if ( maxofs <= 0 ) return; int minx = (invlist.items[invlist.selected].xpos+invlist.items[invlist.selected].GetWidth())-int(master.ws.x-18); int maxx = invlist.items[invlist.selected].xpos; if ( ofs < minx ) ofs = clamp(minx,0,maxofs); else if ( ofs > maxx ) ofs = clamp(maxx,0,maxofs); } override void MenuInput( int key ) { if ( !invlist || (invlist.items.Size() <= 0) ) return; switch ( key ) { case MK_LEFT: int prev = max(0,invlist.selected-22); if ( prev == invlist.selected ) break; invlist.selected = prev; master.MenuSound("menu/demoscroll"); KBScroll(); break; case MK_RIGHT: int next = min(invlist.items.Size()-1,invlist.selected+22); if ( next == invlist.selected ) break; invlist.selected = next; master.MenuSound("menu/demoscroll"); KBScroll(); break; case MK_DOWN: if ( invlist.selected < invlist.items.Size()-1 ) { invlist.selected++; master.MenuSound("menu/demoscroll"); KBScroll(); } break; case MK_UP: if ( invlist.selected > 0 ) { invlist.selected--; master.MenuSound("menu/demoscroll"); KBScroll(); } break; case MK_ENTER: if ( invlist.selected >= invlist.items.Size() ) break; DemolitionistMenuInvItem(invlist.items[invlist.selected]).UseItem(); break; } } override void MouseInput( Vector2 pos, int btn ) { if ( !invlist || (invlist.items.Size() <= 0) ) return; switch ( btn ) { case MB_LEFT: // see if we're clicking the scrollbar (if it exists) if ( pos.y > (master.ws.y-21) ) { if ( SetOffset(pos.x) ) { master.MenuSound("menu/demoscroll"); drag = true; } break; } // find which element we clicked for ( int i=0; i