Library partially implemented (mouse support pending).

This commit is contained in:
Mari the Deer 2022-02-13 23:13:14 +01:00
commit d3725d855e
52 changed files with 1672 additions and 957 deletions

View file

@ -325,21 +325,16 @@ Class DemolitionistMenuInvItem : DemolitionistMenuListItem
Class DemolitionistMenuLoreItem : DemolitionistMenuListItem
{
SWWMLore ent;
bool bActive; // currently being read
DemolitionistMenuLoreItem Init( DemolitionistMenu master, SWWMLore e )
{
Super.Init(master,"");
ent = e;
label = StringTable.Localize(ent.tag);
return self;
}
// return true if this entry hasn't been read yet
// used for the "unread" indicators on the top bar
bool IsUnread()
{
return !ent.read;
}
// marks this entry as read
void MarkRead()
{
@ -347,9 +342,11 @@ Class DemolitionistMenuLoreItem : DemolitionistMenuListItem
EventHandler.SendNetworkEvent(String.Format("swwmmarkloreread.%s",ent.tag),consoleplayer);
}
String GetText()
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
{
return StringTable.Localize(ent.text);
String str = label;
if ( !ent.read ) str = "\cf‼\c-"..label;
Screen.DrawText(smallfont,bActive?Font.CR_FIRE:selected?Font.CR_WHITE:Font.CR_DARKGRAY,master.origin.x+pos.x,master.origin.y+pos.y,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}

View file

@ -4,6 +4,7 @@ Class DemolitionistMenuTextBox ui
DemolitionistMenu master;
String txt;
BrokenLines l;
TextureID img;
int ofs;
int x, w;
bool scrollbar;
@ -32,6 +33,32 @@ Class DemolitionistMenuTextBox ui
}
return self;
}
// allow changing contents without re-creating the class
void Reinit( String txt, int x = 0, int w = 0 )
{
String oldtxt = self.txt;
self.txt = txt;
self.x = x;
int oldw = self.w;
if ( w == 0 ) self.w = 640-x;
else self.w = w;
// break lines again (if changed)
if ( (self.w == oldw) && (self.txt == oldtxt) ) return;
if ( l ) l.Destroy();
l = smallfont.BreakLines(self.txt,self.w-12);
int h = l.Count()*13;
if ( h > (master.ws.y-34) )
{
l.Destroy();
l = smallfont.BreakLines(self.txt,self.w-20);
scrollbar = true;
maxofs = int((l.Count()*13)-(master.ws.y-34));
}
// restrict scroll (if changed)
if ( ofs > maxofs ) smofs = ofs = maxofs;
// reset scroll if text is different
if ( self.txt != oldtxt ) smofs = ofs = 0;
}
override void OnDestroy()
{
l.Destroy();
@ -70,6 +97,13 @@ Class DemolitionistMenuTextBox ui
double yy = 17-smofs;
int miny = int((master.origin.y+17)*master.hs);
int maxy = int((master.origin.y+(master.ws.y-17))*master.hs);
// draw image if defined and visible
if ( img.IsValid() )
{
Vector2 imgsz = TexMan.GetScaledSize(img);
if ( (yy+imgsz.y >= 17) && (yy < master.ws.y) )
Screen.DrawTexture(img,false,master.origin.x+xx,master.origin.y+yy,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,miny,DTA_ClipBottom,maxy);
}
for ( int i=0; i<l.Count(); i++, yy+=13 )
{
// to save time, we skip lines that aren't visible

View file

@ -2,9 +2,310 @@
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();
}
}

View file

@ -344,6 +344,7 @@ Class SWWMTradeHistory : Thinker
enum ELoreTab
{
LORE_ITEM,
LORE_ENEMY,
LORE_PEOPLE,
LORE_LORE // lol
};
@ -351,6 +352,7 @@ enum ELoreTab
Class SWWMLore
{
String tag, text, assoc;
TextureID img;
int tab;
bool read;
}
@ -524,6 +526,7 @@ Class SWWMLoreLibrary : Thinker
if ( ttab ~== "People" ) e.tab = LORE_PEOPLE;
else if ( ttab ~== "Lore" ) e.tab = LORE_LORE;
else if ( ttab ~== "Item" ) e.tab = LORE_ITEM;
else if ( ttab ~== "Enemy" ) e.tab = LORE_ENEMY;
else
{
Console.Printf("Entry \"%s\" has an incorrect tab setting of \"%s\".",ref,ttab);
@ -537,6 +540,8 @@ Class SWWMLoreLibrary : Thinker
}
e.assoc = "$"..assoc;
e.read = false;
// image for certain entries
e.img = TexMan.CheckForTexture("graphics/KBase/PFP_"..ref..".png",TexMan.Type_Any);
// "new lore" message
if ( (level.maptime > 0) && (gametic > lastaddtic) && (myplayer == players[consoleplayer]) && (!menuactive || (menuactive == Menu.OnNoPause)) && (myplayer.mo is 'Demolitionist') )
Console.Printf(StringTable.Localize("$SWWM_NEWLORE"));