Achievement Stats tab now fully functional.
Optimize menu item list drawing by skipping off-screen items.
This commit is contained in:
parent
b36b2f808c
commit
cce76b52ec
7 changed files with 213 additions and 14 deletions
BIN
graphics/Achievements/BarAchievementBase.png
Normal file
BIN
graphics/Achievements/BarAchievementBase.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 B |
BIN
graphics/Achievements/BarAchievementDone.png
Normal file
BIN
graphics/Achievements/BarAchievementDone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 B |
BIN
graphics/Achievements/BarAchievementProgress.png
Normal file
BIN
graphics/Achievements/BarAchievementProgress.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 B |
|
|
@ -1,3 +1,3 @@
|
|||
[default]
|
||||
SWWM_MODVER="\cyDEMOLITIONIST \cw1.2pre r141 \cu(Thu 10 Feb 13:38:58 CET 2022)\c-";
|
||||
SWWM_SHORTVER="\cw1.2pre r141 \cu(2022-02-10 13:38:58)\c-";
|
||||
SWWM_MODVER="\cyDEMOLITIONIST \cw1.2pre r142 \cu(Thu 10 Feb 21:23:31 CET 2022)\c-";
|
||||
SWWM_SHORTVER="\cw1.2pre r142 \cu(2022-02-10 21:23:31)\c-";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Class DemolitionistMenuList ui
|
|||
{
|
||||
Array<DemolitionistMenuListItem> items;
|
||||
int selected;
|
||||
DemolitionistMenu master;
|
||||
|
||||
override void OnDestroy()
|
||||
{
|
||||
|
|
@ -49,8 +50,19 @@ Class DemolitionistMenuList ui
|
|||
// draw all items with a specific offset and clip region
|
||||
void Drawer( Vector2 offset, int cliptop, int clipbottom, int clipleft, int clipright )
|
||||
{
|
||||
Vector2 pos, rpos, rsiz;
|
||||
for ( int i=0; i<items.Size(); i++ )
|
||||
items[i].Drawer((items[i].xpos,items[i].ypos)+offset,(i==selected),cliptop,clipbottom,clipleft,clipright);
|
||||
{
|
||||
pos = (items[i].xpos,items[i].ypos)+offset;
|
||||
rpos = (master.origin+pos)*master.hs;
|
||||
rsiz = (items[i].GetWidth(),items[i].GetHeight())*master.hs;
|
||||
// skip draw if out of bounds
|
||||
if ( rpos.y+rsiz.y < cliptop ) continue;
|
||||
if ( rpos.y > clipbottom ) continue;
|
||||
if ( rpos.x+rsiz.x < clipleft ) continue;
|
||||
if ( rpos.x > clipright ) continue;
|
||||
items[i].Drawer(pos,(i==selected),cliptop,clipbottom,clipleft,clipright);
|
||||
}
|
||||
// scrollbar drawing is handled by the tab, as the list itself is entirely unaware of its frame dimensions
|
||||
}
|
||||
}
|
||||
|
|
@ -123,8 +135,16 @@ Class DemolitionistMenuKillItem : DemolitionistMenuListItem
|
|||
return self;
|
||||
}
|
||||
|
||||
override int GetWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
|
||||
{
|
||||
// skip draw when out of bounds
|
||||
if ( (master.origin.y+pos.y > clipbottom) || (master.origin.y+pos.y+smallfont.GetHeight() < cliptop) )
|
||||
return;
|
||||
Screen.DrawText(smallfont,Font.CR_FIRE,master.origin.x+pos.x,master.origin.y+pos.y,label,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);
|
||||
String str = String.Format("%d",s.kills);
|
||||
Screen.DrawText(smallfont,Font.CR_WHITE,master.origin.x+pos.x+width-smallfont.StringWidth(str),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);
|
||||
|
|
@ -147,6 +167,11 @@ Class DemolitionistMenuMapStatItem : DemolitionistMenuListItem
|
|||
return self;
|
||||
}
|
||||
|
||||
override int GetWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
|
||||
{
|
||||
String str = label;
|
||||
|
|
@ -187,6 +212,9 @@ Class DemolitionistMenuMapStatItem : DemolitionistMenuListItem
|
|||
Class DemolitionistMenuAchievementItem : DemolitionistMenuListItem
|
||||
{
|
||||
SWWMAchievementInfo a;
|
||||
TextureID AchievementUnknown, BaseBox, BarTex[3];
|
||||
bool ShouldObscure;
|
||||
bool bHidden;
|
||||
int width;
|
||||
|
||||
DemolitionistMenuAchievementItem Init( DemolitionistMenu master, SWWMAchievementInfo a, int width = 0 )
|
||||
|
|
@ -194,8 +222,72 @@ Class DemolitionistMenuAchievementItem : DemolitionistMenuListItem
|
|||
Super.Init(master,"");
|
||||
self.a = a;
|
||||
self.width = width;
|
||||
AchievementUnknown = TexMan.CheckForTexture("graphics/Achievements/HiddenAchievement.png",TexMan.Type_Any);
|
||||
BaseBox = TexMan.CheckForTexture("graphics/Achievements/NoAchievement.png",TexMan.Type_Any);
|
||||
BarTex[0] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementBase.png",TexMan.Type_Any);
|
||||
BarTex[1] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementProgress.png",TexMan.Type_Any);
|
||||
BarTex[2] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementDone.png",TexMan.Type_Any);
|
||||
ShouldObscure = (swwm_filterachievements==1);
|
||||
Update();
|
||||
return self;
|
||||
}
|
||||
|
||||
// cache state/progress to not call it every frame
|
||||
void Update()
|
||||
{
|
||||
a.state = master.shnd.achievementstate.At(a.basename).ToInt();
|
||||
if ( !a.maxval ) return;
|
||||
a.val = master.shnd.achievementprogress.At(a.basename).ToInt();
|
||||
if ( !a.bitfield ) return;
|
||||
int val = 0;
|
||||
for ( int i=0; i<a.maxval; i++ )
|
||||
val += !!(a.val&(1<<i));
|
||||
a.val = val;
|
||||
}
|
||||
|
||||
override int GetWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
override int GetHeight()
|
||||
{
|
||||
return 50; // hardcoded
|
||||
}
|
||||
|
||||
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
|
||||
{
|
||||
if ( bHidden ) return;
|
||||
bool completed = !!a.state;
|
||||
bool hasprogress = (a.maxval && a.val);
|
||||
double xx = master.origin.x+pos.x;
|
||||
double yy = master.origin.y+pos.y;
|
||||
Screen.DrawTexture(BaseBox,false,xx+1,yy+1,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,DTA_FillColor,(!completed&&!hasprogress&&ShouldObscure)?Color(8,8,8):Color(16,16,16));
|
||||
Screen.DrawTexture((!completed&&!hasprogress&&ShouldObscure)?AchievementUnknown:a.icon,false,xx,yy,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,DTA_Desaturate,(!completed)*255,DTA_ColorOverlay,completed?Color(0,0,0,0):(hasprogress||!ShouldObscure)?Color(96,0,0,0):Color(192,0,0,0));
|
||||
Screen.DrawTexture(BarTex[0],false,xx+1,yy+36,DTA_DestWidthF,width,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,DTA_FillColor,Color(0,0,0));
|
||||
String str;
|
||||
if ( a.maxval && (!ShouldObscure || hasprogress) )
|
||||
{
|
||||
int val = clamp(a.val,0,a.maxval);
|
||||
if ( val < a.maxval ) Screen.DrawTexture(BarTex[0],false,xx,yy+35,DTA_DestWidthF,width,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);
|
||||
Screen.DrawTexture(BarTex[completed?2:1],false,xx,yy+35,DTA_DestWidthF,width*(val/double(a.maxval)),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);
|
||||
if ( completed ) str = String.Format("%s / %s",SWWMUtility.ThousandsNum(a.maxval),SWWMUtility.ThousandsNum(a.maxval));
|
||||
else str = String.Format("%s / %s",SWWMUtility.ThousandsNum(val),SWWMUtility.ThousandsNum(a.maxval));
|
||||
int ox = (width-smallfont2.StringWidth(str))/2;
|
||||
Screen.DrawText(smallfont2,completed?Font.CR_GREEN:Font.CR_WHITE,xx+ox,yy+37,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);
|
||||
}
|
||||
else Screen.DrawTexture(BarTex[completed?2:0],false,xx,yy+35,DTA_DestWidthF,width,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);
|
||||
str = a.hasformat?String.Format(StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT"),a.maxval):StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT");
|
||||
if ( !completed && !hasprogress && ShouldObscure ) SWWMUtility.ObscureText(str,(gametic/3)+ypos*2);
|
||||
BrokenLines l = smallfont2.BreakLines(str,width-40);
|
||||
str = StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TAG");
|
||||
if ( !completed && !hasprogress && ShouldObscure ) SWWMUtility.ObscureText(str,(gametic/3)+ypos*2+1);
|
||||
int oy = (32-(14+(9*l.Count())))/2;
|
||||
Screen.DrawText(smallfont,completed?Font.CR_GREEN:Font.CR_DARKGRAY,xx+36,yy+oy,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);
|
||||
for ( int i=0; i<l.Count(); i++ )
|
||||
Screen.DrawText(smallfont2,completed?Font.CR_WHITE:Font.CR_BLACK,xx+40,yy+oy+14+i*9,l.StringAt(i),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);
|
||||
l.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// inventory item
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
sname[i] = StringTable.Localize("$SWWM_STATTAB"..i);
|
||||
lwidth = max(lwidth,smallfont.StringWidth(sname[i]));
|
||||
lists[i] = new("DemolitionistMenuList");
|
||||
lists[i].master = master;
|
||||
lists[i].selected = -1;
|
||||
}
|
||||
lwidth += 16; // account for padding of 8px on each side
|
||||
|
|
@ -61,6 +62,8 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
lists[2].selected = lists[2].items.Size()-1; // so it shows the "current level" indicator
|
||||
for ( int i=0; i<lists[2].items.Size(); i++ )
|
||||
lists[2].items[i].ypos = i*16;
|
||||
for ( int i=0; i<master.shnd.achievementinfo.Size(); i++ )
|
||||
lists[3].items.Push(new("DemolitionistMenuAchievementItem").Init(master,master.shnd.achievementinfo[i]));
|
||||
return Super.Init(master);
|
||||
}
|
||||
override void OnDestroy()
|
||||
|
|
@ -130,6 +133,75 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
qsort_mstats(a,p+1,h);
|
||||
}
|
||||
|
||||
private bool CmpAchievement( SWWMAchievementInfo a, SWWMAchievementInfo b )
|
||||
{
|
||||
bool adone = !!(a.state), bdone = !!(b.state);
|
||||
double afactor = adone?1.:0., bfactor = bdone?1.:0.;
|
||||
if ( a.maxval )
|
||||
{
|
||||
int cur = 0;
|
||||
if ( a.bitfield )
|
||||
{
|
||||
for ( int i=0; i<a.maxval; i++ )
|
||||
cur += !!(a.val&(1<<i));
|
||||
cur = min(cur,a.maxval);
|
||||
}
|
||||
else cur = clamp(a.val,0,a.maxval);
|
||||
afactor = cur/double(a.maxval);
|
||||
}
|
||||
if ( b.maxval )
|
||||
{
|
||||
int cur = 0;
|
||||
if ( b.bitfield )
|
||||
{
|
||||
for ( int i=0; i<b.maxval; i++ )
|
||||
cur += !!(b.val&(1<<i));
|
||||
cur = min(cur,b.maxval);
|
||||
}
|
||||
else cur = clamp(b.val,0,b.maxval);
|
||||
bfactor = cur/double(b.maxval);
|
||||
}
|
||||
// sort by base index
|
||||
if ( adone && bdone ) return a.baseindex > b.baseindex;
|
||||
if ( !adone && !bdone )
|
||||
{
|
||||
// progress sort?
|
||||
if ( afactor != bfactor )
|
||||
return afactor < bfactor;
|
||||
// sort by base index
|
||||
return a.baseindex > b.baseindex;
|
||||
}
|
||||
// state sort
|
||||
return bdone;
|
||||
}
|
||||
|
||||
private int partition_achievements( Array<DemolitionistMenuListItem> a, int l, int h )
|
||||
{
|
||||
DemolitionistMenuListItem pv = a[h];
|
||||
int i = (l-1);
|
||||
for ( int j=l; j<=(h-1); j++ )
|
||||
{
|
||||
if ( CmpAchievement(DemolitionistMenuAchievementItem(pv).a,DemolitionistMenuAchievementItem(a[j]).a) )
|
||||
{
|
||||
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_achievements( Array<DemolitionistMenuListItem> a, int l, int h )
|
||||
{
|
||||
if ( l >= h ) return;
|
||||
int p = partition_achievements(a,l,h);
|
||||
qsort_achievements(a,l,p-1);
|
||||
qsort_achievements(a,p+1,h);
|
||||
}
|
||||
|
||||
override void Ticker()
|
||||
{
|
||||
if ( !stats ) return;
|
||||
|
|
@ -314,6 +386,36 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
l.maxlen[j] = maxlen[j];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// update achievement progress
|
||||
for ( int i=0; i<lists[3].items.Size(); i++ )
|
||||
DemolitionistMenuAchievementItem(lists[3].items[i]).Update();
|
||||
// sort achievements
|
||||
qsort_achievements(lists[3].items,0,lists[3].items.Size()-1);
|
||||
// set offsets (based on which ones should be hidden, too)
|
||||
int ay = 0;
|
||||
bool dohide = (swwm_filterachievements==2);
|
||||
for ( int i=0; i<lists[3].items.Size(); i++ )
|
||||
{
|
||||
let ai = DemolitionistMenuAchievementItem(lists[3].items[i]);
|
||||
let key = ai.a.basename;
|
||||
ai.bHidden = true;
|
||||
if ( (key == "everything") && !ai.a.state ) continue;
|
||||
if ( dohide && !ai.a.state && (!ai.a.maxval || !ai.a.val) ) continue;
|
||||
ai.bHidden = false;
|
||||
ai.ypos = ay;
|
||||
ay += 50;
|
||||
}
|
||||
// get max scroll
|
||||
maxofs[3] = int(ay-(master.ws.y-52));
|
||||
// update widths
|
||||
w = int(master.ws.x-(24+lwidth));
|
||||
if ( maxofs[3] > 0 ) w -= 8;
|
||||
for ( int i=0; i<lists[3].items.Size(); i++ )
|
||||
{
|
||||
let ai = DemolitionistMenuAchievementItem(lists[3].items[i]);
|
||||
ai.width = w;
|
||||
}
|
||||
}
|
||||
// update smooth scroll
|
||||
smofs[section] = (smofs[section]*.6)+(ofs[section]*.4);
|
||||
|
|
@ -440,9 +542,16 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
yy += 16;
|
||||
}
|
||||
master.DrawVSeparator(lwidth,14,master.ws.y-28);
|
||||
// achievement drawer has different margins
|
||||
if ( section == 3 )
|
||||
{
|
||||
// TODO achievement drawer has different margins
|
||||
xx = lwidth+12;
|
||||
yy = 26;
|
||||
int cliptop = int((master.origin.y+26)*master.hs);
|
||||
int clipbottom = int((master.origin.y+master.ws.y-26)*master.hs);
|
||||
int clipleft = int((master.origin.x+lwidth+12)*master.hs);
|
||||
int clipright = int((master.origin.x+master.ws.x-12)*master.hs);
|
||||
lists[section].Drawer((xx,yy-smofs[section]),cliptop,clipbottom,clipleft,clipright);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -453,14 +562,14 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
|
|||
int clipleft = int((master.origin.x+lwidth+9)*master.hs);
|
||||
int clipright = int((master.origin.x+master.ws.x-9)*master.hs);
|
||||
lists[section].Drawer((xx,yy-smofs[section]),cliptop,clipbottom,clipleft,clipright);
|
||||
if ( maxofs[section] > 0 )
|
||||
{
|
||||
xx = master.ws.x-8;
|
||||
master.DrawVSeparator(xx,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 ( maxofs[section] > 0 )
|
||||
{
|
||||
xx = master.ws.x-8;
|
||||
master.DrawVSeparator(xx,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@ Class SWWMAchievementMenu : GenericMenu
|
|||
// cache state/progress
|
||||
mItems[i].state = hnd.achievementstate.At(key).ToInt();
|
||||
if ( mItems[i].maxval )
|
||||
{
|
||||
mItems[i].val = hnd.achievementprogress.At(key).ToInt();
|
||||
}
|
||||
// check if we need to hide it
|
||||
if ( (key == "everything") && !mItems[i].state )
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue