- Try to get rid of all implicit casts from string to name, color or class. - Use FindClass where needed. - Used a map in a case where a dictionary was unneeded. - Use new bounce flags where needed. - Replace Legacy of Rust weapons/ammo.
926 lines
32 KiB
Text
926 lines
32 KiB
Text
// stats stab
|
|
|
|
Class DemolitionistStatsTab : DemolitionistMenuTab
|
|
{
|
|
DemolitionistMenuList lists[4];
|
|
int section;
|
|
String sname[4];
|
|
int lwidth;
|
|
SWWMStats stats;
|
|
int ofs[4], maxofs[4];
|
|
double smofs[4];
|
|
bool drag;
|
|
|
|
override DemolitionistMenuTab Init( DemolitionistMenu master )
|
|
{
|
|
title = StringTable.Localize("$SWWM_STATTAB");
|
|
section = 0;
|
|
lwidth = 0;
|
|
for ( int i=0; i<4; i++ )
|
|
{
|
|
sname[i] = StringTable.Localize("$SWWM_STATTAB"..i);
|
|
lwidth = max(lwidth,master.mSmallFont.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
|
|
stats = Demolitionist(players[consoleplayer].mo)?Demolitionist(players[consoleplayer].mo).mystats:null;
|
|
if ( !stats ) return Super.Init(master);
|
|
// the general stats are always the same in the same order, so init them all here in one go
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATUPTIME",stats,stat_uptime));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATONFOOT",stats,stat_onfoot));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATFLIGHT",stats,stat_flight));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATSWIM",stats,stat_swim));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATTELE",stats,stat_tele));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATBOOST",stats,stat_boost));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATDASH",stats,stat_dash));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATSTOMP",stats,stat_stomp));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATFUEL",stats,stat_fuel));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATSPEED",stats,stat_speed));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATAIRTIME",stats,stat_airtime));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATPARRY",stats,stat_parry));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATPPARRY",stats,stat_pparry));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATWPONCH",stats,stat_wponch));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATBUSTS",stats,stat_busts));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATBUTTS",stats,stat_butts));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATPATS",stats,stat_pats));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATKISS",stats,stat_kiss));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATFRIENDS",stats,stat_friends));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATITEMS",stats,stat_items));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATSECRETS",stats,stat_secrets));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATKILLS",stats,stat_kills));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATDEATHS",stats,stat_deaths));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATDDEALT",stats,stat_ddealt));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATDTAKEN",stats,stat_dtaken));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATTDEALT",stats,stat_tdealt));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATTTAKEN",stats,stat_ttaken));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATMKILL",stats,stat_mkill));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATSKILL",stats,stat_skill));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATFAVWEAP",stats,stat_favweap));
|
|
lists[0].items.Push(new('DemolitionistMenuStatItem').Init(master,"$SWWM_STATHISCORE",stats,stat_hiscore));
|
|
for ( int i=0; i<lists[0].items.Size(); i++ )
|
|
lists[0].items[i].ypos = i*16;
|
|
// because these types of stats don't actually change while the menu is open, we can initialize their lists ONLY ONCE here
|
|
foreach ( s:stats.lstats )
|
|
lists[2].items.Push(new('DemolitionistMenuMapStatItem').Init(master,s));
|
|
// find the current level
|
|
for ( int i=0; i<lists[2].items.Size(); i++ )
|
|
{
|
|
if ( DemolitionistMenuMapStatItem(lists[2].items[i]).s.mapname != level.mapname ) continue;
|
|
lists[2].selected = i;
|
|
break;
|
|
}
|
|
for ( int i=0; i<lists[2].items.Size(); i++ )
|
|
lists[2].items[i].ypos = i*16;
|
|
foreach ( inf:master.shnd.achievementinfo )
|
|
lists[3].items.Push(new('DemolitionistMenuAchievementItem').Init(master,inf));
|
|
return Super.Init(master);
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
for ( int i=0; i<4; i++ )
|
|
{
|
|
if ( !lists[i] ) continue;
|
|
lists[i].Destroy();
|
|
}
|
|
}
|
|
override void OnSelect()
|
|
{
|
|
section = master.shnd.menustate.Get("LastStatSection").ToInt();
|
|
smofs[section] = ofs[section];
|
|
}
|
|
override void OnDeselect()
|
|
{
|
|
master.shnd.menustate.Insert("LastStatSection",String.Format("%d",section));
|
|
smofs[section] = ofs[section];
|
|
}
|
|
|
|
private bool CmpMonsterKill( MonsterKill a, MonsterKill b )
|
|
{
|
|
if ( a.kills == b.kills )
|
|
{
|
|
// sort by name instead
|
|
String taga, tagb;
|
|
taga = GetDefaultByType(a.m).GetTag();
|
|
tagb = GetDefaultByType(b.m).GetTag();
|
|
// beautify if there's no tag
|
|
if ( taga == FallbackTag )
|
|
{
|
|
taga = a.m.GetClassName();
|
|
SWWMUtility.BeautifyClassName(taga);
|
|
}
|
|
if ( tagb == FallbackTag )
|
|
{
|
|
tagb = a.m.GetClassName();
|
|
SWWMUtility.BeautifyClassName(tagb);
|
|
}
|
|
return taga > tagb;
|
|
}
|
|
return a.kills < b.kills;
|
|
}
|
|
|
|
private int partition_mstats( Array<DemolitionistMenuListItem> a, int l, int h )
|
|
{
|
|
DemolitionistMenuListItem pv = a[h];
|
|
int i = (l-1);
|
|
for ( int j=l; j<=(h-1); j++ )
|
|
{
|
|
if ( CmpMonsterKill(DemolitionistMenuKillItem(pv).s,DemolitionistMenuKillItem(a[j]).s) )
|
|
{
|
|
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_mstats( Array<DemolitionistMenuListItem> a, int l, int h )
|
|
{
|
|
if ( l >= h ) return;
|
|
int p = partition_mstats(a,l,h);
|
|
qsort_mstats(a,l,p-1);
|
|
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);
|
|
}
|
|
|
|
// BEGIN stat functions
|
|
static ui string stat_uptime( SWWMStats s )
|
|
{
|
|
int thour = ((level.totaltime-s.lastspawn)/(3600*GameTicRate));
|
|
int tmin = ((level.totaltime-s.lastspawn)/(60*GameTicRate))%60;
|
|
int tsec = ((level.totaltime-s.lastspawn)/GameTicRate)%60;
|
|
return String.Format("%02d\cu:\c-%02d\cu:\c-%02d",thour,tmin,tsec);
|
|
}
|
|
static ui string stat_onfoot( SWWMStats s )
|
|
{
|
|
if ( s.grounddist > 32000. )
|
|
return String.Format("%g\cu%s\c-",s.grounddist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
|
|
return String.Format("%g\cu%s\c-",s.grounddist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
|
|
}
|
|
static ui string stat_flight( SWWMStats s )
|
|
{
|
|
if ( s.airdist > 32000. )
|
|
return String.Format("%g\cu%s\c-",s.airdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
|
|
return String.Format("%g\cu%s\c-",s.airdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
|
|
}
|
|
static ui string stat_swim( SWWMStats s )
|
|
{
|
|
if ( s.swimdist > 32000. )
|
|
return String.Format("%g\cu%s\c-",s.swimdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
|
|
return String.Format("%g\cu%s\c-",s.swimdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
|
|
}
|
|
static ui string stat_tele( SWWMStats s )
|
|
{
|
|
if ( s.teledist > 32000. )
|
|
return String.Format("%g\cu%s\c-",s.teledist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
|
|
return String.Format("%g\cu%s\c-",s.teledist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
|
|
}
|
|
static ui string stat_boost( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.boostcount);
|
|
}
|
|
static ui string stat_dash( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.dashcount);
|
|
}
|
|
static ui string stat_stomp( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.stompcount);
|
|
}
|
|
static ui string stat_fuel( SWWMStats s )
|
|
{
|
|
return String.Format("%g\cu%s\c-",s.fuelusage,StringTable.Localize("$SWWM_UNIT_LITER"));
|
|
}
|
|
static ui string stat_speed( SWWMStats s )
|
|
{
|
|
return String.Format("%g\cu%s\c-",(s.topspeed*3600.*GameTicRate)/32000.,StringTable.Localize("$SWWM_UNIT_KPH"));
|
|
}
|
|
static ui string stat_airtime( SWWMStats s )
|
|
{
|
|
int thour = (s.airtime/(3600*GameTicRate));
|
|
int tmin = (s.airtime/(60*GameTicRate))%60;
|
|
int tsec = (s.airtime/GameTicRate)%60;
|
|
return String.Format("%02d\cu:\c-%02d\cu:\c-%02d",thour,tmin,tsec);
|
|
}
|
|
static ui string stat_parry( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.parries);
|
|
}
|
|
static ui string stat_pparry( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.pparries);
|
|
}
|
|
static ui string stat_wponch( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.wponch);
|
|
}
|
|
static ui string stat_busts( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.busts);
|
|
}
|
|
static ui string stat_butts( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.buttslams);
|
|
}
|
|
static ui string stat_pats( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.pats);
|
|
}
|
|
static ui string stat_kiss( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.smooch);
|
|
}
|
|
static ui string stat_friends( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.befriend);
|
|
}
|
|
static ui string stat_items( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.items);
|
|
}
|
|
static ui string stat_secrets( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.secrets);
|
|
}
|
|
static ui string stat_kills( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.kills);
|
|
}
|
|
static ui string stat_deaths( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.deaths);
|
|
}
|
|
static ui string stat_ddealt( SWWMStats s )
|
|
{
|
|
if ( s.hdamagedealt > 0 )
|
|
return String.Format("%d%09d",s.hdamagedealt,s.damagedealt);
|
|
return String.Format("%d",s.damagedealt);
|
|
}
|
|
static ui string stat_dtaken( SWWMStats s )
|
|
{
|
|
if ( s.hdamagetaken > 0 )
|
|
return String.Format("%d%09d",s.hdamagetaken,s.damagetaken);
|
|
return String.Format("%d",s.damagetaken);
|
|
}
|
|
static ui string stat_tdealt( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.topdealt);
|
|
}
|
|
static ui string stat_ttaken( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.toptaken);
|
|
}
|
|
static ui string stat_mkill( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.mkill);
|
|
}
|
|
static ui string stat_skill( SWWMStats s )
|
|
{
|
|
return String.Format("%d",s.skill);
|
|
}
|
|
static ui string stat_favweap( SWWMStats s )
|
|
{
|
|
if ( s.favweapon == -1 ) return "N/A";
|
|
if ( s.wstats[s.favweapon].w == 'SWWMWeapon' ) return StringTable.Localize("$SWWM_YOURSELF");
|
|
if ( s.wstats[s.favweapon].w == 'SWWMGesture' ) return StringTable.Localize("$SWWM_DOKIDOKI");
|
|
if ( s.wstats[s.favweapon].w == 'SWWMItemGesture' ) return StringTable.Localize("$T_FROGGY");
|
|
if ( s.wstats[s.favweapon].w == 'Weapon' ) return StringTable.Localize("$SWWM_GRAVKILL");
|
|
if ( s.wstats[s.favweapon].w == 'DoomWeapon' ) return StringTable.Localize("$SWWM_PARRYKILL");
|
|
let def = GetDefaultByType(s.wstats[s.favweapon].w);
|
|
return def.GetTag();
|
|
}
|
|
static ui string stat_hiscore( SWWMStats s )
|
|
{
|
|
return String.Format("\cu¥\c-%09d",s.hiscore);
|
|
}
|
|
// END stat functions
|
|
|
|
override void Ticker()
|
|
{
|
|
if ( !stats ) return;
|
|
// update list contents
|
|
String str;
|
|
int w;
|
|
switch ( section )
|
|
{
|
|
case 0:
|
|
// not much to do here, they update themselves and are left-aligned
|
|
maxofs[0] = int(16*lists[0].items.Size()-(master.ws.y-46));
|
|
break;
|
|
case 1:
|
|
// theoretically we can assume that the monster stats list will never shrink in the middle of gameplay, only grow
|
|
// also the array only ever has elements pushed, never inserted in-between
|
|
// if this somehow breaks, I don't know what the hell can do that
|
|
if ( lists[1].items.Size() < stats.mstats.Size() )
|
|
{
|
|
int oldindex = lists[1].items.Size();
|
|
lists[1].items.Resize(stats.mstats.Size());
|
|
for ( int i=oldindex; i<lists[1].items.Size(); i++ )
|
|
lists[1].items[i] = new('DemolitionistMenuKillItem').Init(master,stats.mstats[i]);
|
|
}
|
|
maxofs[1] = int(16*lists[1].items.Size()-(master.ws.y-46));
|
|
// update widths
|
|
w = int(master.ws.x-(18+lwidth));
|
|
if ( maxofs[1] > 0 ) w -= 8;
|
|
foreach ( i:lists[1].items )
|
|
{
|
|
let k = DemolitionistMenuKillItem(i);
|
|
k.width = w;
|
|
}
|
|
// sort
|
|
qsort_mstats(lists[1].items,0,lists[1].items.Size()-1);
|
|
// update y positions after sorting
|
|
for ( int i=0; i<lists[1].items.Size(); i++ )
|
|
lists[1].items[i].ypos = i*16;
|
|
break;
|
|
case 2:
|
|
maxofs[2] = int(16*lists[2].items.Size()-(master.ws.y-46));
|
|
// update widths
|
|
w = int(master.ws.x-(18+lwidth));
|
|
if ( maxofs[2] > 0 ) w -= 8;
|
|
int len[4], maxlen[4];
|
|
for ( int i=0; i<4; i++ ) maxlen[i] = 0;
|
|
for ( int i=0; i<lists[2].items.Size(); i++ )
|
|
{
|
|
let l = DemolitionistMenuMapStatItem(lists[2].items[i]);
|
|
l.width = w;
|
|
// and calculate "max lengths"
|
|
l.GetLengths(lists[2].selected==i,len[0],len[1],len[2],len[3]);
|
|
for ( int j=0; j<4; j++ ) if ( len[j] > maxlen[j] ) maxlen[j] = len[j];
|
|
}
|
|
// second pass to propagate the "max lengths"
|
|
foreach ( i:lists[2].items )
|
|
{
|
|
let l = DemolitionistMenuMapStatItem(i);
|
|
for ( int j=0; j<4; j++ )
|
|
l.maxlen[j] = maxlen[j];
|
|
}
|
|
break;
|
|
case 3:
|
|
// update achievement progress
|
|
foreach ( i:lists[3].items )
|
|
DemolitionistMenuAchievementItem(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);
|
|
foreach ( i:lists[3].items )
|
|
{
|
|
let ai = DemolitionistMenuAchievementItem(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;
|
|
foreach ( i:lists[3].items )
|
|
{
|
|
let ai = DemolitionistMenuAchievementItem(i);
|
|
if ( ai.width != w ) ai.oldstr = ""; // force brokenlines to update to the new width
|
|
ai.width = w;
|
|
}
|
|
}
|
|
// 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();
|
|
}
|
|
// 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);
|
|
}
|
|
override void MenuInput( int key )
|
|
{
|
|
switch ( key )
|
|
{
|
|
case MK_LEFT:
|
|
master.MenuSound("menu/demoscroll");
|
|
smofs[section] = ofs[section];
|
|
section--;
|
|
if ( section < 0 ) section = 3;
|
|
smofs[section] = ofs[section];
|
|
break;
|
|
case MK_RIGHT:
|
|
master.MenuSound("menu/demoscroll");
|
|
smofs[section] = ofs[section];
|
|
section++;
|
|
if ( section > 3 ) section = 0;
|
|
smofs[section] = ofs[section];
|
|
break;
|
|
case MK_DOWN:
|
|
if ( Scroll(16) ) master.MenuSound("menu/demoscroll");
|
|
break;
|
|
case MK_UP:
|
|
if ( Scroll(-16) ) master.MenuSound("menu/demoscroll");
|
|
break;
|
|
}
|
|
}
|
|
override void MouseInput( Vector2 pos, int btn )
|
|
{
|
|
// global events
|
|
switch ( btn )
|
|
{
|
|
case MB_DRAG:
|
|
if ( drag ) SetOffset(pos.y);
|
|
break;
|
|
case MB_RELEASE:
|
|
drag = false;
|
|
break;
|
|
}
|
|
// mouse on left side
|
|
if ( pos.x < lwidth )
|
|
{
|
|
if ( btn != MB_LEFT ) return;
|
|
for ( int i=0; i<4; i++ )
|
|
{
|
|
if ( pos.x < 9 ) continue;
|
|
if ( pos.x > 9+master.mSmallFont.StringWidth(sname[i]) ) continue;
|
|
if ( pos.y < 23+16*i ) continue;
|
|
if ( pos.y > 36+16*i ) continue;
|
|
if ( section != i )
|
|
{
|
|
master.MenuSound("menu/demoscroll");
|
|
smofs[section] = ofs[section];
|
|
section = i;
|
|
smofs[section] = ofs[section];
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
switch ( btn )
|
|
{
|
|
case MB_LEFT:
|
|
// see if we're clicking the scrollbar (if it exists)
|
|
if ( (maxofs[section] > 0) && (pos.x > (master.ws.x-8)) )
|
|
{
|
|
SetOffset(pos.y);
|
|
master.MenuSound("menu/demoscroll");
|
|
drag = true;
|
|
break;
|
|
}
|
|
break;
|
|
case MB_WHEELUP:
|
|
if ( Scroll(-8) ) master.MenuSound("menu/demoscroll");
|
|
break;
|
|
case MB_WHEELDOWN:
|
|
if ( Scroll(8) ) master.MenuSound("menu/demoscroll");
|
|
break;
|
|
}
|
|
}
|
|
override void Drawer( double fractic )
|
|
{
|
|
if ( !stats ) return;
|
|
double xx = 9;
|
|
double yy = 23;
|
|
for ( int i=0; i<4; i++ )
|
|
{
|
|
Screen.DrawText(master.mSmallFont,(i==section)?Font.CR_FIRE:Font.CR_WHITE,master.origin.x+xx,master.origin.y+yy,sname[i],DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
yy += 16;
|
|
}
|
|
master.DrawVSeparator(lwidth,14,master.ws.y-28);
|
|
if ( lists[section].items.Size() == 0 )
|
|
{
|
|
String str = StringTable.Localize("$SWWM_NOSTAT");
|
|
xx = lwidth+int((master.ws.x-lwidth)-master.mSmallFont.StringWidth(str))/2;
|
|
yy = int(master.ws.y-master.mSmallFont.GetHeight())/2;
|
|
Screen.DrawText(master.mSmallFont,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);
|
|
return;
|
|
}
|
|
double ssmofs = (smofs[section]~==ofs[section])?smofs[section]:(smofs[section]*(1.-fractic)+((smofs[section]*.6)+(ofs[section]*.4))*fractic);
|
|
if ( section == 3 )
|
|
{
|
|
// achievement drawer has different margins
|
|
xx = lwidth+12;
|
|
yy = 26;
|
|
Screen.SetClipRect(int((master.origin.x+lwidth+12)*master.hs),int((master.origin.y+26)*master.hs),int((master.ws.x-(lwidth+24))*master.hs),int((master.ws.y-52)*master.hs));
|
|
lists[section].Drawer((xx,yy-ssmofs));
|
|
}
|
|
else
|
|
{
|
|
xx = lwidth+9;
|
|
yy = 23;
|
|
Screen.SetClipRect(int((master.origin.x+lwidth+9)*master.hs),int((master.origin.y+23)*master.hs),int((master.ws.x-(lwidth+18))*master.hs),int((master.ws.y-46)*master.hs));
|
|
lists[section].Drawer((xx,yy-ssmofs));
|
|
}
|
|
Screen.ClearClipRect();
|
|
if ( maxofs[section] > 0 )
|
|
{
|
|
xx = master.ws.x-8;
|
|
master.DrawVSeparator(xx,14,master.ws.y-28);
|
|
xx += 2;
|
|
yy = floor(ssmofs*((master.ws.y-39)/maxofs[section]))+14;
|
|
Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,"▮",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// general stat item
|
|
Class DemolitionistMenuStatItem : DemolitionistMenuListItem
|
|
{
|
|
SWWMStats s;
|
|
Function<ui string(SWWMStats)> fn;
|
|
String txt;
|
|
|
|
DemolitionistMenuStatItem Init( DemolitionistMenu master, String label, SWWMStats s, Function<ui string(SWWMStats)> fn )
|
|
{
|
|
Super.Init(master,label);
|
|
self.s = s;
|
|
self.fn = fn;
|
|
return self;
|
|
}
|
|
|
|
override int GetWidth()
|
|
{
|
|
String str = String.Format("%s%s",label,txt);
|
|
return master.mSmallFont.StringWidth(str);
|
|
}
|
|
|
|
override void Ticker()
|
|
{
|
|
// call update fn
|
|
txt = fn.call(s);
|
|
}
|
|
|
|
override void Drawer( Vector2 pos, bool selected )
|
|
{
|
|
String str = String.Format("\cx%s\c-%s",label,txt);
|
|
Screen.DrawText(master.mSmallFont,Font.CR_WHITE,master.origin.x+pos.x,master.origin.y+pos.y,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
|
|
// monster kill stat item
|
|
Class DemolitionistMenuKillItem : DemolitionistMenuListItem
|
|
{
|
|
MonsterKill s;
|
|
int width;
|
|
|
|
DemolitionistMenuKillItem Init( DemolitionistMenu master, MonsterKill s, int width = 0 )
|
|
{
|
|
Super.Init(master,"");
|
|
self.s = s;
|
|
self.width = width;
|
|
let m = GetDefaultByType(s.m);
|
|
self.label = m.GetTag(FallbackTag);
|
|
if ( self.label == FallbackTag )
|
|
{
|
|
self.label = m.GetClassName();
|
|
SWWMUtility.BeautifyClassName(self.label);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
override int GetWidth()
|
|
{
|
|
return width;
|
|
}
|
|
|
|
override void Drawer( Vector2 pos, bool selected )
|
|
{
|
|
Screen.DrawText(master.mSmallFont,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);
|
|
String str = String.Format("%d",s.kills);
|
|
Screen.DrawText(master.mSmallFont,Font.CR_WHITE,master.origin.x+pos.x+width-master.mSmallFont.StringWidth(str),master.origin.y+pos.y,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
|
|
// map stat item
|
|
Class DemolitionistMenuMapStatItem : DemolitionistMenuListItem
|
|
{
|
|
LevelStat s;
|
|
int width;
|
|
int maxlen[4];
|
|
|
|
DemolitionistMenuMapStatItem Init( DemolitionistMenu master, LevelStat s, int width = 0 )
|
|
{
|
|
Super.Init(master,"");
|
|
self.s = s;
|
|
self.width = width;
|
|
self.label = s.levelname;
|
|
return self;
|
|
}
|
|
|
|
override int GetWidth()
|
|
{
|
|
return width;
|
|
}
|
|
|
|
void GetLengths( bool selected, int &tlen, int &slen, int &ilen, int &klen )
|
|
{
|
|
int time, par, suck, stotal, scount, itotal, icount, ktotal, kcount;
|
|
if ( selected )
|
|
{
|
|
time = level.maptime;
|
|
par = level.partime;
|
|
suck = level.sucktime;
|
|
stotal = level.total_secrets;
|
|
scount = level.found_secrets;
|
|
itotal = level.total_items;
|
|
icount = level.found_items;
|
|
ktotal = level.total_monsters;
|
|
kcount = level.killed_monsters;
|
|
}
|
|
else
|
|
{
|
|
time = s.time;
|
|
par = s.par;
|
|
suck = s.suck;
|
|
stotal = s.stotal;
|
|
scount = s.scount;
|
|
itotal = s.itotal;
|
|
icount = s.icount;
|
|
ktotal = s.ktotal;
|
|
kcount = s.kcount;
|
|
}
|
|
int sec = Thinker.Tics2Seconds(time);
|
|
String str = String.Format("T %02d:%02d:%02d",sec/3600,(sec%3600)/60,sec%60);
|
|
tlen = master.mTinyFont.StringWidth(str);
|
|
if ( stotal > 0 )
|
|
{
|
|
str = String.Format("S %d/%d",scount,stotal);
|
|
slen = master.mTinyFont.StringWidth(str);
|
|
}
|
|
else slen = 0;
|
|
if ( itotal > 0 )
|
|
{
|
|
str = String.Format("I %d/%d",icount,itotal);
|
|
ilen = master.mTinyFont.StringWidth(str);
|
|
}
|
|
else ilen = 0;
|
|
if ( ktotal > 0 )
|
|
{
|
|
str = String.Format("K %d/%d",kcount,ktotal);
|
|
klen = master.mTinyFont.StringWidth(str);
|
|
}
|
|
else klen = 0;
|
|
}
|
|
|
|
override void Drawer( Vector2 pos, bool selected )
|
|
{
|
|
String str = s.mapname..": ";
|
|
if ( selected ) str = "\cd> \c-"..str;
|
|
int nlen = master.mTinyFont.StringWidth(str);
|
|
double xx = pos.x;
|
|
double yy = pos.y;
|
|
Screen.DrawText(master.mTinyFont,(selected||s.visited)?Font.CR_FIRE:Font.CR_DARKGRAY,master.origin.x+xx,master.origin.y+yy+2,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
xx += nlen;
|
|
str = s.levelname;
|
|
if ( !s.visited && !selected ) SWWMUtility.ObscureText(str,(Menu.MenuTime()/3)+ypos*2);
|
|
bool smallname = (nlen+master.mSmallFont.StringWidth(str))>(width-(maxlen[3]+maxlen[2]+maxlen[1]+maxlen[0]+24));
|
|
bool allclear = true;
|
|
if ( selected )
|
|
{
|
|
if ( (level.total_secrets > 0) && (level.found_secrets < level.total_secrets) ) allclear = false;
|
|
if ( (level.total_items > 0) && (level.found_items < level.total_items) ) allclear = false;
|
|
if ( (level.total_monsters > 0) && (level.killed_monsters < level.total_monsters) ) allclear = false;
|
|
}
|
|
else if ( s.visited )
|
|
{
|
|
if ( (s.stotal > 0) && (s.scount < s.stotal) ) allclear = false;
|
|
if ( (s.itotal > 0) && (s.icount < s.itotal) ) allclear = false;
|
|
if ( (s.ktotal > 0) && (s.kcount < s.ktotal) ) allclear = false;
|
|
}
|
|
else allclear = false;
|
|
Screen.DrawText(smallname?master.mTinyFont:master.mSmallFont,(selected||s.visited)?allclear?Font.CR_GOLD:Font.CR_WHITE:Font.CR_BLACK,master.origin.x+xx,master.origin.y+yy+smallname*2,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
if ( !selected && !s.visited ) return;
|
|
xx = pos.x+width;
|
|
yy = pos.y+2;
|
|
int time, par, suck, stotal, scount, itotal, icount, ktotal, kcount;
|
|
if ( selected )
|
|
{
|
|
time = level.maptime;
|
|
par = level.partime;
|
|
suck = level.sucktime;
|
|
stotal = level.total_secrets;
|
|
scount = level.found_secrets;
|
|
itotal = level.total_items;
|
|
icount = level.found_items;
|
|
ktotal = level.total_monsters;
|
|
kcount = level.killed_monsters;
|
|
}
|
|
else
|
|
{
|
|
time = s.time;
|
|
par = s.par;
|
|
suck = s.suck;
|
|
stotal = s.stotal;
|
|
scount = s.scount;
|
|
itotal = s.itotal;
|
|
icount = s.icount;
|
|
ktotal = s.ktotal;
|
|
kcount = s.kcount;
|
|
}
|
|
int sec = Thinker.Tics2Seconds(time);
|
|
str = String.Format("%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
|
|
Screen.DrawText(master.mTinyFont,((suck>0)&&(sec>=(suck*3600)))?Font.CR_RED:(sec<=par)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.mTinyFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
Screen.DrawText(master.mTinyFont,Font.CR_FIRE,master.origin.x+xx-maxlen[0],master.origin.y+yy,"T",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
if ( maxlen[0] > 0 ) xx -= maxlen[0]+8;
|
|
if ( stotal > 0 )
|
|
{
|
|
str = String.Format("%d\cu/\c-%d",scount,stotal);
|
|
Screen.DrawText(master.mTinyFont,(scount>=stotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.mTinyFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
Screen.DrawText(master.mTinyFont,Font.CR_FIRE,master.origin.x+xx-maxlen[1],master.origin.y+yy,"S",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
if ( maxlen[1] > 0 ) xx -= maxlen[1]+8;
|
|
if ( itotal > 0 )
|
|
{
|
|
str = String.Format("%d\cu/\c-%d",icount,itotal);
|
|
Screen.DrawText(master.mTinyFont,(icount>=itotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.mTinyFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
Screen.DrawText(master.mTinyFont,Font.CR_FIRE,master.origin.x+xx-maxlen[2],master.origin.y+yy,"I",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
if ( maxlen[2] > 0 ) xx -= maxlen[2]+8;
|
|
if ( ktotal > 0 )
|
|
{
|
|
str = String.Format("%d\cu/\c-%d",kcount,ktotal);
|
|
Screen.DrawText(master.mTinyFont,(kcount>=ktotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.mTinyFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
Screen.DrawText(master.mTinyFont,Font.CR_FIRE,master.origin.x+xx-maxlen[3],master.origin.y+yy,"K",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// achievement item
|
|
Class DemolitionistMenuAchievementItem : DemolitionistMenuListItem
|
|
{
|
|
SWWMAchievementInfo a;
|
|
TextureID AchievementUnknown, BaseBox, BarTex[3];
|
|
bool ShouldObscure;
|
|
bool bHidden;
|
|
int width;
|
|
BrokenLines l;
|
|
String oldstr;
|
|
|
|
DemolitionistMenuAchievementItem Init( DemolitionistMenu master, SWWMAchievementInfo a, int width = 0 )
|
|
{
|
|
Super.Init(master,"");
|
|
self.a = a;
|
|
self.width = width;
|
|
AchievementUnknown = TexMan.CheckForTexture("graphics/Achievements/HiddenAchievement.png");
|
|
BaseBox = TexMan.CheckForTexture("graphics/Achievements/NoAchievement.png");
|
|
BarTex[0] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementBase.png");
|
|
BarTex[1] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementProgress.png");
|
|
BarTex[2] = TexMan.CheckForTexture("graphics/Achievements/BarAchievementDone.png");
|
|
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 )
|
|
{
|
|
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_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_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_FillColor,Color(0,0,0));
|
|
String str;
|
|
if ( a.maxval && (!ShouldObscure || hasprogress) )
|
|
{
|
|
int val = a.val;
|
|
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);
|
|
Screen.DrawTexture(BarTex[completed?2:1],false,xx,yy+35,DTA_DestWidthF,width*(min(val,a.maxval)/double(a.maxval)),DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
str = String.Format("%s / %s",SWWMUtility.ThousandsNum(val),SWWMUtility.ThousandsNum(a.maxval));
|
|
int ox = (width-master.mTinyFont.StringWidth(str))/2;
|
|
Screen.DrawText(master.mTinyFont,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);
|
|
}
|
|
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);
|
|
str = a.hasformat?String.Format(StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT"),SWWMUtility.ThousandsNum(a.maxval)):StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TXT");
|
|
if ( str != oldstr )
|
|
{
|
|
if ( l ) l.Destroy();
|
|
l = master.mTinyFont.BreakLines(str,width-40);
|
|
oldstr = str;
|
|
}
|
|
str = StringTable.Localize("$SWWM_ACHIEVEMENT_"..a.basename.."_TAG");
|
|
if ( !completed && !hasprogress && ShouldObscure ) SWWMUtility.ObscureText(str,(Menu.MenuTime()/3)+ypos*2+1);
|
|
int oy = (32-(14+(9*l.Count())))/2;
|
|
Screen.DrawText(master.mSmallFont,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);
|
|
for ( int i=0; i<l.Count(); i++ )
|
|
{
|
|
str = l.StringAt(i);
|
|
if ( !completed && ShouldObscure ) SWWMUtility.ObscureText(str,(Menu.MenuTime()/3)+ypos*2+2+i);
|
|
Screen.DrawText(master.mTinyFont,completed?Font.CR_WHITE:Font.CR_BLACK,xx+40,yy+oy+14+i*9,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
}
|