Rework how level stats are handled.

(Note: this WILL break old saves)
This commit is contained in:
Mari the Deer 2023-12-24 15:02:10 +01:00
commit 98b5ebecac
10 changed files with 195 additions and 93 deletions

View file

@ -102,6 +102,7 @@ extend Class SWWMHandler
}
else if ( level.info.flags2&LEVEL2_RESETINVENTORY && !e.IsReturn )
s.CleanGotWeapons(); // clean up the "got weapons" so their obtain lines are played again
s.PreloadLevelStats();
// reset some vars
multilevel[e.playernumber] = 0;
spreecount[e.playernumber] = 0;

View file

@ -99,10 +99,6 @@ Class SWWMStaticHandler : StaticEventHandler
SWWMHandler.ClearAllShaders();
EventHandler.SendInterfaceEvent(consoleplayer,"swwmflushhud");
EventHandler.SendInterfaceEvent(consoleplayer,"swwmaprcheck");
// quick fix for old savegames (to be removed in 1.3 release)
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd && (hnd.sbounds.Size() <= 0) )
hnd.PrecalculateSectorBounds();
if ( !e.IsSaveGame ) return;
// save version checker
tainted = false;

View file

@ -10,7 +10,6 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
int ofs[4], maxofs[4];
double smofs[4];
bool drag;
LevelStat clstat;
override DemolitionistMenuTab Init( DemolitionistMenu master )
{
@ -29,36 +28,15 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
stats = Demolitionist(players[consoleplayer].mo)?Demolitionist(players[consoleplayer].mo).mystats:null;
if ( !stats ) return Super.Init(master);
// because these types of stats don't actually change while the menu is open, we can initialize their lists ONLY ONCE here
if ( swwm_uniqstats )
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++ )
{
foreach ( s:stats.lstats )
{
if ( s.mapname ~== level.mapname ) continue;
for ( int j=0; j<lists[2].items.Size(); j++ )
{
if ( !(DemolitionistMenuMapStatItem(lists[2].items[j]).s.mapname ~== s.mapname) ) continue;
lists[2].items[j].Destroy();
lists[2].items.Delete(j);
j--;
}
lists[2].items.Push(new("DemolitionistMenuMapStatItem").Init(master,s));
}
if ( DemolitionistMenuMapStatItem(lists[2].items[i]).s.mapname != level.mapname ) continue;
lists[2].selected = i;
break;
}
else
{
foreach ( s:stats.lstats )
lists[2].items.Push(new("DemolitionistMenuMapStatItem").Init(master,s));
}
clstat = new("LevelStat");
clstat.hub = !!(level.clusterflags&level.CLUSTER_HUB);
clstat.mapname = level.mapname;
clstat.levelname = level.levelname;
int iof = clstat.levelname.IndexOf(" - by: ");
if ( iof != -1 ) clstat.levelname.Truncate(iof);
clstat.par = level.partime;
clstat.suck = level.sucktime;
lists[2].items.Push(new("DemolitionistMenuMapStatItem").Init(master,clstat));
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;
foreach ( inf:master.shnd.achievementinfo )
@ -338,46 +316,18 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
break;
case 2:
maxofs[2] = int(16*lists[2].items.Size()-(master.ws.y-46));
// update current stats
clstat.kcount = level.killed_monsters;
clstat.ktotal = level.total_monsters;
clstat.icount = level.found_items;
clstat.itotal = level.total_items;
clstat.scount = level.found_secrets;
clstat.stotal = level.total_secrets;
clstat.time = level.maptime;
// 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;
foreach ( i:lists[2].items )
for ( int i=0; i<lists[2].items.Size(); i++ )
{
let l = DemolitionistMenuMapStatItem(i);
let l = DemolitionistMenuMapStatItem(lists[2].items[i]);
l.width = w;
// and calculate "max lengths"
int sec = Thinker.Tics2Seconds(l.s.time);
str = String.Format("T %02d:%02d:%02d",sec/3600,(sec%3600)/60,sec%60);
len[0] = master.mTinyFont.StringWidth(str);
if ( len[0] > maxlen[0] ) maxlen[0] = len[0];
if ( l.s.stotal > 0 )
{
str = String.Format("S %d/%d",l.s.scount,l.s.stotal);
len[1] = master.mTinyFont.StringWidth(str);
if ( len[1] > maxlen[1] ) maxlen[1] = len[1];
}
if ( l.s.itotal > 0 )
{
str = String.Format("I %d/%d",l.s.icount,l.s.itotal);
len[2] = master.mTinyFont.StringWidth(str);
if ( len[2] > maxlen[2] ) maxlen[2] = len[2];
}
if ( l.s.ktotal > 0 )
{
str = String.Format("K %d/%d",l.s.kcount,l.s.ktotal);
len[3] = master.mTinyFont.StringWidth(str);
if ( len[3] > maxlen[3] ) maxlen[3] = len[3];
}
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 )
@ -626,37 +576,113 @@ Class DemolitionistMenuMapStatItem : DemolitionistMenuListItem
return width;
}
void GetLengths( bool selected, out int tlen, out int slen, out int ilen, out 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 = label;
if ( selected ) str = "\cd▸\c- "..str;
bool smallname = master.mSmallFont.StringWidth(str)>(width-(maxlen[3]+maxlen[2]+maxlen[1]+maxlen[0]+24));
Screen.DrawText(smallname?master.mTinyFont:master.mSmallFont,Font.CR_FIRE,master.origin.x+pos.x,master.origin.y+pos.y+smallname*2,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
Screen.DrawText(smallname?master.mTinyFont:master.mSmallFont,(selected||s.visited)?Font.CR_FIRE:Font.CR_DARKGRAY,master.origin.x+pos.x,master.origin.y+pos.y+smallname*2,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
if ( !selected && !s.visited ) return;
double xx = pos.x+width;
double yy = pos.y+2;
int sec = Thinker.Tics2Seconds(s.time);
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,((s.suck>0)&&(sec>=(s.suck*3600)))?Font.CR_RED:(sec<=s.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,((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 ( s.stotal > 0 )
if ( stotal > 0 )
{
str = String.Format("%d\cu/\c-%d",s.scount,s.stotal);
Screen.DrawText(master.mTinyFont,(s.scount>=s.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);
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 ( s.itotal > 0 )
if ( itotal > 0 )
{
str = String.Format("%d\cu/\c-%d",s.icount,s.itotal);
Screen.DrawText(master.mTinyFont,(s.icount>=s.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);
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 ( s.ktotal > 0 )
if ( ktotal > 0 )
{
str = String.Format("%d\cu/\c-%d",s.kcount,s.ktotal);
Screen.DrawText(master.mTinyFont,(s.kcount>=s.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);
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);
}
}

View file

@ -42,11 +42,13 @@ Class SWWMStatScreen : StatusScreen
TewiFont = Font.GetFont('TewiFont');
TewiFontOutline = Font.GetFont('TewiFontOutline');
MiniwiFont = Font.GetFont('MiniwiFont');
// support for old author text style
int iof;
for ( int i=0; i<=1; i++ )
{
// level name may contain trailing whitespace due to DEHACKED nonsense, so strip it
lnametexts[i].StripRight();
if ( authortexts[i] != "" ) continue;
// support for old author text style
if ( (iof = lnametexts[i].RightIndexOf(" - by: ")) != -1 ) // 20 heretics, spooktober
{
authortexts[i] = lnametexts[i].Mid(iof+7);
@ -62,8 +64,6 @@ Class SWWMStatScreen : StatusScreen
authortexts[i] = lnametexts[i].Mid(iof+3);
lnametexts[i].Truncate(iof);
}
// level name may contain trailing whitespace due to DEHACKED nonsense, so strip it
lnametexts[i].StripRight();
}
tipalpha = -1.;
hs = CleanXFac_1;

View file

@ -67,7 +67,8 @@ Class MonsterKill
Class LevelStat
{
bool hub;
bool hub, visited;
int cluster;
String levelname, mapname;
int kcount, ktotal;
int icount, itotal;
@ -212,13 +213,87 @@ Class SWWMStats : SWWMStaticThinker
}
}
private LevelStat FindLevelStats( String mapname )
{
foreach ( ls:lstats )
{
if ( ls.mapname != mapname ) continue;
return ls;
}
return null;
}
void PreloadLevelStats()
{
// pre-adds all unvisited levels from the current cluster
int nlevels = LevelInfo.GetLevelInfoCount();
for ( int i=0; i<nlevels; i++ )
{
let li = LevelInfo.GetLevelInfo(i);
if ( !li.isValid() || !LevelInfo.MapExists(li.mapname)
|| (li.cluster != level.cluster)
|| FindLevelStats(li.mapname) )
continue;
let ls = new("LevelStat");
// we can automatically assume that all levels from the same cluster will have this flag as well
ls.hub = !!(level.clusterflags&level.CLUSTER_HUB);
ls.visited = false;
ls.cluster = li.cluster;
ls.levelname = li.LookupLevelName();
// level name may contain trailing whitespace due to DEHACKED nonsense, so strip it
ls.levelname.StripRight();
if ( li.authorname == "" ) // the author name might be part of the level name, try to strip it
{
int iof;
if ( ((iof = ls.levelname.RightIndexOf(" - by: ")) != -1) || ((iof = ls.levelname.RightIndexOf(" - by ")) != -1) || ((iof = ls.levelname.RightIndexOf(" - ")) != -1) )
ls.levelname.Truncate(iof);
}
ls.mapname = li.mapname;
// append after last entry whose cluster is lower or equal to our own
// or just push otherwise
int lastent = -1, i = 0;
foreach ( ls:lstats )
{
if ( ls.cluster <= li.cluster )
lastent = i;
i++;
}
if ( lastent != -1 ) lstats.Insert(lastent+1,ls);
else lstats.Push(ls);
}
}
void AddLevelStats()
{
let ls = new("LevelStat");
// overwrite any existing entry
let ls = FindLevelStats(level.mapname);
if ( ls )
{
ls.visited = true;
ls.kcount = level.killed_monsters;
ls.ktotal = level.total_monsters;
ls.icount = level.found_items;
ls.itotal = level.total_items;
ls.scount = level.found_secrets;
ls.stotal = level.total_secrets;
ls.time = level.maptime;
ls.par = level.partime;
ls.suck = level.sucktime;
return;
}
ls = new("LevelStat");
ls.hub = !!(level.clusterflags&level.CLUSTER_HUB);
ls.visited = true;
ls.cluster = level.cluster;
ls.levelname = level.levelname;
int iof = ls.levelname.IndexOf(" - by: ");
if ( iof != -1 ) ls.levelname.Truncate(iof);
// level name may contain trailing whitespace due to DEHACKED nonsense, so strip it
ls.levelname.StripRight();
if ( level.authorname == "" ) // the author name might be part of the level name, try to strip it
{
int iof;
if ( ((iof = ls.levelname.RightIndexOf(" - by: ")) != -1) || ((iof = ls.levelname.RightIndexOf(" - by ")) != -1) || ((iof = ls.levelname.RightIndexOf(" - ")) != -1) )
ls.levelname.Truncate(iof);
}
ls.mapname = level.mapname;
ls.kcount = level.killed_monsters;
ls.ktotal = level.total_monsters;
@ -229,7 +304,17 @@ Class SWWMStats : SWWMStaticThinker
ls.time = level.maptime;
ls.par = level.partime;
ls.suck = level.sucktime;
lstats.Push(ls);
// append to last entry whose cluster is lower or equal to our own
// or just push otherwise
int lastent = -1, i = 0;
foreach ( ls:lstats )
{
if ( ls.cluster <= level.cluster )
lastent = i;
i++;
}
if ( lastent != -1 ) lstats.Insert(lastent,ls);
else lstats.Push(ls);
}
void AddWeaponKill( Actor inflictor, Actor victim, Name damagetype )