swwmgz_m/zscript/kbase/swwm_kbasetab_library.zsc
Marisa Kirisame 8769496e36 Various tweaks to menu code.
Add "mission log" thinker for use in custom maps.
2022-03-11 18:24:17 +01:00

421 lines
12 KiB
Text

// THE LORE
Class DemolitionistLibraryTab : DemolitionistMenuTab
{
DemolitionistMenuList lists[4];
int section;
String sname[4];
int lwidth;
SWWMLoreLibrary lore;
int loresz;
int ofs[4], maxofs[4];
double smofs[4];
bool drag[2];
int sel[4];
DemolitionistMenuLoreItem active;
SWWMLore clore;
DemolitionistMenuTextBox ltext;
private int partition_lore( Array<DemolitionistMenuListItem> a, int l, int h )
{
DemolitionistMenuListItem pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( StringTable.Localize(DemolitionistMenuLoreItem(pv).ent.tag) > StringTable.Localize(DemolitionistMenuLoreItem(a[j]).ent.tag) )
{
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_lore( Array<DemolitionistMenuListItem> 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);
}
override DemolitionistMenuTab Init( DemolitionistMenu master )
{
title = StringTable.Localize("$SWWM_KBASETAB");
lore = SWWMLoreLibrary.Find(players[consoleplayer]);
section = 0;
lwidth = 0;
for ( int i=0; i<4; i++ )
{
sname[i] = StringTable.Localize("$SWWM_LORETAB"..i);
lwidth = max(lwidth,smallfont.StringWidth("<‼ "..sname[i].." ‼>")+6);
lists[i] = new("DemolitionistMenuList");
lists[i].master = master;
}
loresz = lore.ent.Size();
for ( int i=0; i<loresz; i++ )
{
let ent = lore.ent[i];
let le = new("DemolitionistMenuLoreItem").Init(master,ent);
lists[ent.tab].items.Push(le);
lwidth = max(lwidth,smallfont.StringWidth("‼"..le.label)+6);
}
for ( int i=0; i<4; i++ )
{
qsort_lore(lists[i].items,0,lists[i].items.Size()-1);
maxofs[i] = max(0,lists[i].items.Size()*14-int(master.ws.y-50));
for ( int j=0; j<lists[i].items.Size(); j++ )
lists[i].items[j].ypos = j*14;
}
return Super.Init(master);
}
override void OnDestroy()
{
for ( int i=0; i<4; i++ )
lists[i].Destroy();
if ( ltext ) ltext.Destroy();
}
override void OnSelect()
{
section = master.shnd.menustate.At("LastLoreSection").ToInt();
smofs[section] = ofs[section];
}
override void OnDeselect()
{
master.shnd.menustate.Insert("LastLoreSection",String.Format("%d",section));
smofs[section] = ofs[section];
}
override void Ticker()
{
// update library (if anything changes)
if ( lore.ent.Size() > loresz )
{
int olwidth = lwidth;
int olmaxofs = maxofs[section];
// append the new entries
for ( int i=loresz; i<lore.ent.Size(); i++ )
{
let ent = lore.ent[i];
let le = new("DemolitionistMenuLoreItem").Init(master,ent);
lists[ent.tab].items.Push(le);
lwidth = max(lwidth,smallfont.StringWidth("‼"..le.label)+6);
}
// re-sort the lists, ensuring we are still selecting the same entry
for ( int i=0; i<4; i++ )
{
if ( lists[i].items.Size() <= 0 ) continue;
let osel = lists[i].items[lists[i].selected];
qsort_lore(lists[i].items,0,lists[i].items.Size()-1);
let idx = lists[i].items.Find(osel);
if ( (idx != lists[i].items.Size()) && (idx != lists[i].selected) )
{
sel[i] = lists[i].selected = idx;
KBScroll();
}
maxofs[i] = max(0,lists[i].items.Size()*14-int(master.ws.y-50));
if ( ofs[i] > maxofs[i] ) smofs[i] = ofs[i] = maxofs[i];
for ( int j=0; j<lists[i].items.Size(); j++ )
lists[i].items[j].ypos = j*14;
}
loresz = lore.ent.Size();
// append the notification for new lore
master.tmsg = StringTable.Localize("$SWWM_NEWLORE");
master.tmsgtic = gametic+70;
if ( ((lwidth != olwidth) || (maxofs[section] != olmaxofs)) && clore && ltext )
ltext.Reinit(StringTable.Localize(clore.text),lwidth+((maxofs[section]>0)?8:0));
}
// update smooth scroll
smofs[section] = (smofs[section]*.6)+(ofs[section]*.4);
if ( abs(smofs[section]-ofs[section]) < (1./master.hs) ) smofs[section] = ofs[section];
// tick the current list
lists[section].Ticker();
// tick the text box
if ( ltext ) ltext.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[section] <= 0 ) return false;
int oldofs = ofs[section];
ofs[section] = clamp(ofs[section]+speed,0,maxofs[section]);
return (ofs[section] != oldofs);
}
// called when clicking on our scrollbar
// returns true if the position actually changed
// y: relative click position
bool SetOffset( double y )
{
if ( maxofs[section] <= 0 ) return false;
int oldofs = ofs[section];
ofs[section] = clamp(int(round((y-20.5)/((master.ws.y-41.)/maxofs[section]))),0,maxofs[section]);
return (ofs[section] != oldofs);
}
// called when keyboard scrolling
void KBScroll()
{
if ( maxofs[section] <= 0 ) return;
int miny = (lists[section].selected+1)*14-int(master.ws.y-50);
int maxy = lists[section].selected*14;
if ( ofs[section] < miny ) ofs[section] = clamp(miny,0,maxofs[section]);
else if ( ofs[section] > maxy ) ofs[section] = clamp(maxy,0,maxofs[section]);
}
override void MenuInput( int key )
{
switch ( key )
{
case MK_LEFT:
int prev = max(0,section-1);
while ( prev > 0 )
{
if ( lists[prev].items.Size() > 0 )
break;
prev--;
}
if ( prev == section ) break;
master.MenuSound("menu/demoscroll");
smofs[section] = ofs[section];
section = prev;
smofs[section] = ofs[section];
if ( clore && ltext )
ltext.Reinit(StringTable.Localize(clore.text),lwidth+((maxofs[section]>0)?8:0));
break;
case MK_RIGHT:
int next = min(3,section+1);
while ( next < 3 )
{
if ( lists[next].items.Size() > 0 )
break;
next++;
}
if ( next == section ) break;
master.MenuSound("menu/demoscroll");
smofs[section] = ofs[section];
section = next;
smofs[section] = ofs[section];
if ( clore && ltext )
ltext.Reinit(StringTable.Localize(clore.text),lwidth+((maxofs[section]>0)?8:0));
break;
case MK_DOWN:
if ( clore && ltext )
{
if ( ltext.Scroll(16) ) master.MenuSound("menu/demoscroll");
break;
}
if ( sel[section] < lists[section].items.Size()-1 )
{
lists[section].selected = ++sel[section];
master.MenuSound("menu/demoscroll");
KBScroll();
}
break;
case MK_UP:
if ( clore && ltext )
{
if ( ltext.Scroll(-16) ) master.MenuSound("menu/demoscroll");
break;
}
if ( sel[section] > 0 )
{
lists[section].selected = --sel[section];
master.MenuSound("menu/demoscroll");
KBScroll();
}
break;
case MK_ENTER:
if ( clore ) break;
if ( active ) active.bActive = false;
active = DemolitionistMenuLoreItem(lists[section].items[sel[section]]);
active.bActive = true;
clore = active.ent;
if ( !ltext ) ltext = new("DemolitionistMenuTextBox").Init(master,StringTable.Localize(clore.text),lwidth+((maxofs[section]>0)?8:0));
else ltext.Reinit(StringTable.Localize(clore.text),lwidth+((maxofs[section]>0)?8:0));
ltext.img = clore.img;
master.MenuSound("menu/demosel");
active.MarkRead();
break;
case MK_BACK:
if ( !clore ) break;
if ( active ) active.bActive = false;
clore = null;
master.MenuSound("menu/democlose");
break;
}
}
override void MouseInput( Vector2 pos, int btn )
{
// global events
switch ( btn )
{
case MB_DRAG:
if ( clore && ltext && drag[1] ) ltext.SetOffset(pos.y);
else if ( drag[0] ) SetOffset(pos.y);
break;
case MB_RELEASE:
drag[0] = drag[1] = false;
break;
}
// mouse on left side
if ( pos.x < (lwidth+((maxofs[section]>0)?8:0)) )
{
// see if we're clicking the scrollbar (if it exists)
if ( (btn == MB_LEFT) && (maxofs[section] > 0) && (pos.x >= lwidth) )
{
SetOffset(pos.y);
master.MenuSound("menu/demoscroll");
drag[0] = true;
return;
}
// mouse on the category sub-tab
if ( pos.y < 28 )
{
switch ( btn )
{
case MB_LEFT:
if ( pos.x < (lwidth/2) ) MenuInput(MK_LEFT);
else MenuInput(MK_RIGHT);
break;
case MB_WHEELUP:
MenuInput(MK_LEFT);
break;
case MB_WHEELDOWN:
MenuInput(MK_RIGHT);
break;
}
return;
}
switch ( btn )
{
case MB_LEFT:
// find which element we clicked
for ( int i=0; i<lists[section].items.Size(); i++ )
{
if ( !lists[section].items[i].CheckBounds(pos.x-3,(pos.y-32)+smofs[section]) )
continue;
sel[section] = lists[section].selected = i;
KBScroll();
if ( active && (lists[section].items[i] == active) )
MenuInput(MK_BACK);
else
{
if ( active ) active.bActive = false;
clore = null;
MenuInput(MK_ENTER);
}
break;
}
break;
case MB_WHEELUP:
if ( sel[section] > 0 )
{
lists[section].selected = --sel[section];
master.MenuSound("menu/demoscroll");
KBScroll();
}
break;
case MB_WHEELDOWN:
if ( sel[section] < lists[section].items.Size()-1 )
{
lists[section].selected = ++sel[section];
master.MenuSound("menu/demoscroll");
KBScroll();
}
break;
}
return;
}
if ( !clore || !ltext ) return;
switch ( btn )
{
case MB_LEFT:
// see if we're clicking the scrollbar (if it exists)
if ( ltext.scrollbar && (pos.x > (master.ws.x-8)) )
{
ltext.SetOffset(pos.y);
master.MenuSound("menu/demoscroll");
drag[1] = true;
}
break;
case MB_WHEELUP:
if ( ltext.Scroll(-8) ) master.MenuSound("menu/demoscroll");
break;
case MB_WHEELDOWN:
if ( ltext.Scroll(8) ) master.MenuSound("menu/demoscroll");
break;
}
}
override void Drawer()
{
double xx = 0;
double yy = 15;
String str = sname[section];
master.DrawVSeparator(lwidth,14,master.ws.y-28);
master.DrawHSeparator(0,28,lwidth);
Screen.DrawText(smallfont,Font.CR_FIRE,master.origin.x+(lwidth-smallfont.StringWidth(str))/2,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
bool hasprev = false;
bool hasunreadprev = false;
for ( int i=0; i<section; i++ )
{
if ( lists[i].items.Size() <= 0 )
continue;
hasprev = true;
for ( int j=0; j<lists[i].items.Size(); j++ )
{
if ( DemolitionistMenuLoreItem(lists[i].items[j]).ent.read ) continue;
hasunreadprev = true;
break;
}
}
bool hasnext = false;
bool hasunreadnext = false;
for ( int i=section+1; i<4; i++ )
{
if ( lists[i].items.Size() <= 0 )
continue;
hasnext = true;
for ( int j=0; j<lists[i].items.Size(); j++ )
{
if ( DemolitionistMenuLoreItem(lists[i].items[j]).ent.read ) continue;
hasunreadnext = true;
break;
}
}
if ( hasprev )
Screen.DrawText(smallfont,Font.CR_WHITE,master.origin.x+3,master.origin.y+yy,"<",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
if ( hasunreadprev )
Screen.DrawText(smallfont,Font.CR_GOLD,master.origin.x+9,master.origin.y+yy,"‼",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
if ( hasnext )
Screen.DrawText(smallfont,Font.CR_WHITE,master.origin.x+lwidth-9,master.origin.y+yy,">",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
if ( hasunreadnext )
Screen.DrawText(smallfont,Font.CR_GOLD,master.origin.x+lwidth-15,master.origin.y+yy,"‼",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
xx = 3;
yy = 32;
int cliptop = int((master.origin.y+32)*master.hs);
int clipbottom = int((master.origin.y+master.ws.y-18)*master.hs);
int clipleft = int((master.origin.x+3)*master.hs);
int clipright = int((master.origin.x+lwidth-3)*master.hs);
lists[section].Drawer((xx,yy-smofs[section]),cliptop,clipbottom,clipleft,clipright);
if ( maxofs[section] > 0 )
{
xx = lwidth;
master.DrawVSeparator(xx+8,14,master.ws.y-28);
xx += 2;
yy = floor(smofs[section]*((master.ws.y-39)/maxofs[section]))+14;
Screen.DrawText(smallfont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,"▮",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
if ( clore && ltext ) ltext.Drawer();
else
{
str = StringTable.Localize("$SWWM_LOREUNSEL");
int lwx = lwidth+((maxofs[section]>0)?8:0);
xx = lwx+int((master.ws.x-lwx)-smallfont.StringWidth(str))/2;
yy = int(master.ws.y-smallfont.GetHeight())/2;
Screen.DrawText(smallfont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
}
}