swwmgz_m/zscript/kbase/swwm_kbasetab_library.zsc

311 lines
9.6 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;
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 = "$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;
lists[i].selected = 0;
}
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()*13-(master.ws.y-50));
for ( int j=0; j<lists[i].items.Size(); j++ )
lists[i].items[j].ypos = j*13;
}
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;
// 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()*13-(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*13;
}
loresz = lore.ent.Size();
// append the notification for new lore
master.tmsg = StringTable.Localize("$SWWM_NEWLORE");
master.tmsgtic = gametic+70;
if ( (lwidth != olwidth) && 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)*13-(master.ws.y-50);
int maxy = lists[section].selected*13;
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 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();
}
}