// Minimap and stats extend Class SWWMStatusBar { // quicksort (player scores) private int partition_playerscore( Array a, int l, int h ) { PlayerInfo pv = a[h]; int i = (l-1); for ( int j=l; j<=(h-1); j++ ) { if ( pv.fragcount < a[j].fragcount ) { i++; PlayerInfo tmp = a[j]; a[j] = a[i]; a[i] = tmp; } } PlayerInfo tmp = a[h]; a[h] = a[i+1]; a[i+1] = tmp; return i+1; } private void qsort_playerscore( Array a, int l, int h ) { if ( l >= h ) return; int p = partition_playerscore(a,l,h); qsort_playerscore(a,l,p-1); qsort_playerscore(a,p+1,h); } private void TickTopStuffInterpolators() { // stats flashing if ( level.killed_monsters > oldkills ) { oldkills = level.killed_monsters; killflash = gametic+25; } if ( level.found_items > olditems ) { olditems = level.found_items; itemflash = gametic+25; } if ( level.found_secrets > oldsecrets ) { oldsecrets = level.found_secrets; secretflash = gametic+25; } if ( level.total_monsters > oldtkills ) { oldtkills = level.total_monsters; tkillflash = gametic+25; } if ( level.total_items > oldtitems ) { oldtitems = level.total_items; titemflash = gametic+25; } if ( level.total_secrets > oldtsecrets ) { oldtsecrets = level.total_secrets; tsecretflash = gametic+25; } if ( level.total_monsters > 0 ) { int pkills = (level.killed_monsters*100)/level.total_monsters; if ( pkills != oldpkills ) pkillflash = gametic+25; oldpkills = pkills; } if ( level.total_items > 0 ) { int pitems = (level.found_items*100)/level.total_items; if ( pitems != oldpitems ) pitemflash = gametic+25; oldpitems = pitems; } if ( level.total_secrets > 0 ) { int psecrets = (level.found_secrets*100)/level.total_secrets; if ( psecrets != oldpsecrets ) psecretflash = gametic+25; oldpsecrets = psecrets; } // purge expired key flashes for ( int i=0; i= gametic ) continue; keyflash.Delete(i--); } // minimap zoom interpolation double desiredzoom = clamp(mm_zoom,.5,1.); if ( (minimapzoom != mm_zoom) || (oldminimapzoom != mm_zoom) ) { oldminimapzoom = minimapzoom; double diff = .1*(desiredzoom-minimapzoom); minimapzoom += diff; if ( abs(minimapzoom-desiredzoom) <= .01 ) minimapzoom = desiredzoom; } } private void TickTopStuff() { // deathmatch stuff if ( !deathmatch ) return; if ( teamplay ) { if ( teamactive.Size() != Teams.Size() ) teamactive.Resize(Teams.Size()); if ( teamscore.Size() != Teams.Size() ) teamscore.Resize(Teams.Size()); for ( int i=0; i CPlayer.fragcount ) rank += 1; else if ( players[i].fragcount == CPlayer.fragcount ) tiedscore = true; if ( players[i].fragcount > highscore ) highscore = players[i].fragcount; } if ( sortplayers.Size() != playercount ) sortplayers.Resize(playercount); for ( int i=0, j=0; i= -1; p-- ) { if ( p == thisgroup ) continue; foreach ( l : level.lines ) { if ( !(l.flags&Line.ML_MAPPED) && !level.allmap && !am_cheat ) continue; if ( (l.flags&Line.ML_DONTDRAW) && ((am_cheat == 0) || (am_cheat >= 4)) ) continue; Vector2 rv1 = l.v1.p-cpos, rv2 = l.v2.p-cpos; int lgroup; if ( l.sidedef[0].flags&Side.WALLF_POLYOBJ ) lgroup = level.PointInSector(l.v1.p+l.delta/2.).portalgroup; else lgroup = l.frontsector.portalgroup; bool isportal = ((numgroups>0)&&(lgroup!=thisgroup)); if ( lgroup == p ) { // portal displacement Vector2 pofs = level.GetDisplacement(lgroup,thisgroup); rv1 += pofs; rv2 += pofs; } else if ( (p != -1) || (lgroup != thisgroup) ) continue; Vector2 mid = (rv1+rv2)/2.; Vector2 siz = (abs(rv1.x-rv2.x),abs(rv1.y-rv2.y))/2.; if ( (((siz.x+zoomview)-abs(mid.x)) <= 0) || (((siz.y+zoomview)-abs(mid.y)) <= 0) ) continue; // flip Y rv1.y *= -1; rv2.y *= -1; // rotate by view rv1 = Actor.RotateVector(rv1,ViewRot.x-90); rv2 = Actor.RotateVector(rv2,ViewRot.x-90); // clip to frame bool visible; [visible, rv1, rv2] = SWWMUtility.LiangBarsky((-1,-1)*zoomclip,(1,1)*zoomclip,rv1,rv2); if ( !visible ) continue; // scale to minimap frame rv1 *= hsz/zoomclip; rv2 *= hsz/zoomclip; if ( !bUseCanvas ) { rv1 *= hs; rv2 *= hs; } // offset to minimap center rv1 += basepos; rv2 += basepos; // get the line color Color col = mm_wallcolor; if ( (l.flags&Line.ML_MAPPED) || am_cheat ) { int secwit = CheckSecret(l); int lock = SWWMUtility.GetLineLock(l); if ( (l.flags&Line.ML_SECRET) && (am_cheat || !level.allmap) ) // allmap will reveal these { if ( am_cheat && l.backsector ) col = mm_secretwallcolor; else col = (secwit==2)?mm_unexploredsecretcolor:(secwit==1)?mm_secretsectorcolor:mm_wallcolor; } else if ( CheckExitLine(l) ) col = mm_interlevelcolor; else if ( CheckTeleportLine(l) ) col = mm_intralevelcolor; else if ( (lock > 0) && (lock < 256) ) { let lcol = Key.GetMapColorForLock(lock); if ( !lcol ) { // "all keys" locks lack a color // so we cycle through the colors of all available keys if ( gameinfo.gametype&GAME_Doom ) { Color cols[3] = {0xFFFF0000,0xFF0000FF,0xFFFFFF00}; col = cols[int(((gametic+fractic)*3)/GameTicRate)%3]; } else if ( gameinfo.gametype&GAME_Heretic ) { Color cols[3] = {0xFFFFFF00,0xFF00FF00,0xFF0000FF}; col = cols[int(((gametic+fractic)*3)/GameTicRate)%3]; } else if ( gameinfo.gametype&GAME_Hexen ) { Color cols[11] = {0xFF969696,0xFFFFDA00,0xFF4040FF,0xFFFF8000,0xFF00FF00,0xFF2F97FF,0xFF9A98BC,0xFF9C4C00,0xFFFFD900,0xFF40FF40,0xFFFF4040}; col = cols[int(((gametic+fractic)*11)/GameTicRate)%11]; } else col = mm_lockedcolor; // fallback } else if ( lcol != -1 ) col = lcol; else col = mm_lockedcolor; } else if ( CheckTriggerLine(l) ) col = mm_specialwallcolor; else if ( secwit == 1 ) col = mm_secretsectorcolor; // locked doors and trigger lines take priority over secrets else if ( secwit == 2 ) col = mm_unexploredsecretcolor; else if ( l.frontsector && l.backsector ) { if ( !CmpFloorPlanes(l) ) col = mm_fdwallcolor; else if ( !CmpCeilingPlanes(l) ) col = mm_cdwallcolor; else if ( CheckFFBoundary(l) ) col = mm_efwallcolor; else { if ( (am_cheat == 0) || (am_cheat >= 4) ) continue; col = mm_tswallcolor; } } } else col = mm_notseencolor; // draw the line if ( isportal ) { col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); if ( bUseCanvas ) mm_canvas.DrawLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),col); else Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.25),col); } else { if ( bUseCanvas ) mm_canvas.DrawLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),col); else Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.5),col); } } } } private void DrawMapMarkers( Vector2 basepos, bool smol, bool bUseCanvas = false ) { double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; int hsz = smol?HALFMAPSIZE_SMALL:HALFMAPSIZE; Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); Sector csec = players[consoleplayer].Camera.CurSector; if ( !mi ) mi = ThinkerIterator.Create('MapMarker',Thinker.STAT_MAPMARKER); else mi.Reinit(); MapMarker m; while ( m = MapMarker(mi.Next()) ) { if ( m.bDORMANT ) continue; if ( m.args[1] && !(m.CurSector.moreflags&Sector.SECMF_DRAWN) ) continue; TextureID tx; if ( m.picnum.IsValid() ) tx = m.picnum; else tx = m.CurState.GetSpriteTexture(1); Vector2 sz = TexMan.GetScaledSize(tx); Vector2 scl; bool bCentered = (m is 'SWWMInterestMarker'); // seems to match automap scaling somewhat if ( m.Args[2] ) scl = (m.Scale/zoomlevel)*.15; else scl = m.Scale*.5; sz.x *= scl.x; sz.y *= scl.y; double radius = max(sz.x,sz.y); // naive, I know if ( m.args[0] ) { // oh bother, this will be dicks let ai = level.CreateActorIterator(m.args[0]); foreach ( a:ai ) { Vector2 rv = a.pos.xy-cpos; bool isportal = false; Sector sec = level.PointInSector(a.pos.xy); if ( sec.portalgroup != csec.portalgroup ) { isportal = true; // portal displacement rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); } if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) continue; // flip Y rv.y *= -1; // rotate by view rv = Actor.RotateVector(rv,ViewRot.x-90); // scale to minimap frame rv *= hsz/zoomclip; if ( !bUseCanvas ) rv *= hs; // offset to minimap center rv += basepos; // draw if ( bUseCanvas ) mm_canvas.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,scl.x,DTA_ScaleY,scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation,DTA_CenterOffset,bCentered); else Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation,DTA_CenterOffset,bCentered); } ai.Destroy(); continue; } Vector2 rv = m.pos.xy-cpos; bool isportal = false; Sector sec = level.PointInSector(m.pos.xy); if ( sec.portalgroup != csec.portalgroup ) { isportal = true; // portal displacement rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); } if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) continue; // flip Y rv.y *= -1; // rotate by view rv = Actor.RotateVector(rv,ViewRot.x-90); // scale to minimap frame rv *= hsz/zoomclip; if ( !bUseCanvas ) rv *= hs; // offset to minimap center rv += basepos; // draw if ( bUseCanvas ) mm_canvas.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,scl.x,DTA_ScaleY,scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation,DTA_CenterOffset,bCentered); else Screen.DrawTexture(tx,false,rv.x,rv.y,DTA_ColorOverlay,isportal?Color(128,mm_portalcolor.r,mm_portalcolor.g,mm_portalcolor.b):Color(0,0,0,0),DTA_ScaleX,hs*scl.x,DTA_ScaleY,hs*scl.y,DTA_LegacyRenderStyle,m.GetRenderStyle(),DTA_Alpha,m.Alpha,DTA_FillColor,m.FillColor,DTA_TranslationIndex,m.Translation,DTA_CenterOffset,bCentered); } } private void DrawMapThings( Vector2 basepos, bool smol, bool bUseCanvas = false ) { double zoomlevel = SWWMUtility.Lerp(oldminimapzoom,minimapzoom,FracTic); double zoomview = MAPVIEWDIST*zoomlevel, zoomclip = CLIPDIST*zoomlevel; int hsz = smol?HALFMAPSIZE_SMALL:HALFMAPSIZE; Vector2 cpos = SWWMUtility.LerpVector2(players[consoleplayer].Camera.prev.xy,players[consoleplayer].Camera.pos.xy,FracTic); Sector csec = players[consoleplayer].Camera.CurSector; bool drawmissiles = swwm_mm_missiles; for ( SWWMSimpleTracker t=hnd.strackers; t; t=t.next ) { if ( !drawmissiles && t.ismissile ) continue; if ( level.allmap && t.iskey ) continue; // don't draw keys over the actual markers they have Color col = mm_thingcolor; bool isitem = false; bool plainactor = false; Vector2 pos; double angle; double radius; if ( t.target ) { pos = SWWMUtility.LerpVector2(t.target.prev.xy,t.target.pos.xy,FracTic); angle = t.target.angle; radius = t.isybeam?(t.target.speed*cos(t.target.pitch-90)):t.isbeam?(t.target.speed*cos(t.target.pitch)):t.target.radius; } else { pos = t.pos.xy; angle = t.angle; radius = t.radius; } if ( t.isitem ) { if ( t.iskey ) col = t.keycolor; else if ( t.vipitem ) col = mm_thingcolor_vipitem; else if ( t.countitem ) col = mm_thingcolor_citem; else col = mm_thingcolor_item; isitem = true; } else if ( t.isplayer ) col = t.playercol; else if ( t.friendly ) col = mm_thingcolor_friend; else if ( t.countkill ) col = mm_thingcolor_monster; else if ( t.ismonster ) col = mm_thingcolor_ncmonster; else if ( t.ismissile ) col = mm_thingcolor_missile; else { if ( t.vipitem ) col = mm_thingcolor_vipitem; // chanceboxes else if ( t.shootable ) col = mm_thingcolor_shootable; plainactor = true; } int mtime = GameTicRate; if ( level.allmap && !t.expired && t.target ) mtime += GameTicRate*3; Vector2 rv = pos-cpos; bool isportal = false; Sector sec = level.PointInSector(pos); if ( sec.portalgroup != csec.portalgroup ) { isportal = true; // portal displacement rv += level.GetDisplacement(sec.portalgroup,csec.portalgroup); // and blend in the color too col = Color((col.r+mm_portalcolor.r*7)/8,(col.g+mm_portalcolor.g*7)/8,(col.b+mm_portalcolor.b*7)/8); } if ( (((radius+zoomview)-abs(rv.x)) <= 0) || (((radius+zoomview)-abs(rv.y)) <= 0) ) continue; Vector2 tv[8]; int nidx; bool closeshape = true; if ( t.isbeam ) { // oriented line nidx = 2; tv[0] = rv; tv[1] = rv+Actor.RotateVector((radius,0),angle); closeshape = false; } else if ( t.iskey ) { // key shape (a rhombus and an L, basically) nidx = 8; double crad = min(radius,10); // head (pointing north) for ( int i=0; i<5; i++ ) tv[i] = rv+(0,crad*.5)-Actor.RotateVector((0,crad*.5),i*90); // tail (pointing east) tv[5] = rv; tv[6] = rv+(0,-crad); tv[7] = rv+(crad*.5,-crad); closeshape = false; } else if ( isitem ) { // rhombus nidx = 4; double crad = min(radius,10); for ( int i=0; i<4; i++ ) tv[i] = rv+Actor.RotateVector((crad,0),i*90); } else if ( plainactor ) { // aabb box nidx = 4; tv[0] = rv+(-radius,-radius); tv[1] = rv+(radius,-radius); tv[2] = rv+(radius,radius); tv[3] = rv+(-radius,radius); } else { // oriented triangle nidx = 3; tv[0] = rv+Actor.RotateVector((radius,0),angle); tv[1] = rv+Actor.RotateVector((-radius*.5,radius*.7),angle); tv[2] = rv+Actor.RotateVector((-radius*.5,-radius*.7),angle); } // flip Y for ( int j=0; j 1) && mm_cvfirstdraw ) { // make sure we don't draw the whole thing if we're using the smaller scale Screen.DrawTexture(mm_canvastex,false,xx+2,yy+2,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true, DTA_SrcWidth,hsz*2,DTA_SrcHeight,hsz*2,DTA_DestWidth,hsz*2,DTA_DestHeight,hsz*2); } else Screen.Dim(mm_backcolor,1.,int((xx+2)*hs),int((yy+2)*hs),int(hsz*2*hs),int(hsz*2*hs)); mm_cvfirstdraw = true; return; } mm_cvfirstdraw = false; Vector2 basemappos = (xx+hsz+2,yy+hsz+2); Screen.Dim(mm_backcolor,1.,int((basemappos.x-hsz)*hs),int((basemappos.y-hsz)*hs),int(hsz*2*hs),int(hsz*2*hs)); Screen.SetClipRect(int((basemappos.x-hsz)*hs),int((basemappos.y-hsz)*hs),int(hsz*2*hs),int(hsz*2*hs)); // draw dat stuff DrawMapLines(basemappos*hs,smol); DrawMapThings(basemappos*hs,smol); DrawMapMarkers(basemappos*hs,smol); DrawTracedSteps(basemappos*hs,smol); // finally, draw the player arrow Vector2 tv[] = {(0,-4),(-3,2),(3,2)}; for ( int i=0; i<3; i++ ) tv[i] = (tv[i]+basemappos)*hs; for ( int i=0; i<3; i++ ) Screen.DrawThickLine(int(tv[i].x),int(tv[i].y),int(tv[(i+1)%3].x),int(tv[(i+1)%3].y),max(1.,hs*.5),mm_yourcolor); Screen.ClearClipRect(); } private void DrawTopStuff() { int xx = xmargin, yy = ymargin; // obviously, don't draw the minimap if the automap is open if ( !(automapactive && !viewactive) && swwm_mm_enable ) { bool smol = (min(ss.x,ss.y/.5625)<480); int hsz = smol?HALFMAPSIZE_SMALL:HALFMAPSIZE; xx = int(ss.x-(xmargin+(hsz+2)*2)); Screen.DrawTexture(MiniBox[smol],false,xx,yy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); DrawMinimap(xx,yy,smol); yy += ((hsz+2)*2)+5; } // draw stats and timer when automap is open int fstats = swwm_forcestats; bool pstats = swwm_percentstats; if ( (automapactive && !viewactive) || (fstats > 0) ) { xx = int(ss.x-(xmargin+2)); String str; if ( (automapactive && !viewactive) || (fstats > 1) ) { int label = am_showmaplabel; String ln = level.levelname; if ( ln.Left(1) == "$" ) ln = StringTable.Localize(ln); // level name may contain trailing whitespace due to DEHACKED nonsense, so strip it ln.StripRight(); if ( level.authorname == "" ) // the author name might be part of the level name, try to strip it { int iof; if ( ((iof = ln.RightIndexOf(" - by: ")) != -1) || ((iof = ln.RightIndexOf(" - by ")) != -1) || ((iof = ln.RightIndexOf(" - ")) != -1) ) ln.Truncate(iof); } // split level name into separate lines if it's too long // also, use a cache so we don't call BreakLines constantly if ( ln_bl && (ln != cached_ln) ) ln_bl.Destroy(); cached_ln = ln; if ( !ln_bl ) ln_bl = mSmallFontOutlineAlt.BreakLines(ln,120); if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) ) { Screen.DrawText(mSmallFontOutlineAlt,tcvalue,xx-ln_bl.StringWidth(0),yy,ln_bl.StringAt(0),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); for ( int i=1; i 0) && am_showmonsters && !deathmatch ) { int pct = (level.killed_monsters*100)/level.total_monsters; if ( pstats ) str = String.Format("\c"..tclabel_s.."K \c-%3d\c"..tcextra_s.."%%\c-",pct); else str = String.Format("\c"..tclabel_s.."K \c-%d\c"..tcextra_s.."/\c-%d",level.killed_monsters,level.total_monsters); int basew = MiniHUDFontOutline.StringWidth(str); if ( pstats ) { int dcnt = 2-int(Log10(clamp(pct,1,999))); for ( int j=0; j=level.total_monsters)?tccompl:tcvalue,(xx-basew)+8+j*4,yy,0x30,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,Color(160,0,0,0)); } Screen.DrawText(MiniHUDFontOutline,(level.killed_monsters>=level.total_monsters)?tccompl:tcvalue,xx-basew,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); if ( pstats ) { if ( pkillflash && (gametic < pkillflash) ) { double alph = max((pkillflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%3d%%",pct); int pctpos = str.IndexOf("%"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(pctpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } else { if ( killflash && (gametic < killflash) ) { double alph = max((killflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d/%d",level.killed_monsters,level.total_monsters); int slashpos = str.IndexOf("/"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } if ( tkillflash && (gametic < tkillflash) ) { double alph = max((tkillflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d",level.total_monsters); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } yy += MiniHUDFontOutline.GetHeight()+2; } if ( (level.total_items > 0) && am_showitems && !deathmatch ) { int pct = (level.found_items*100)/level.total_items; if ( pstats ) str = String.Format("\c"..tclabel_s.."I \c-%3d\c"..tcextra_s.."%%\c-",pct); else str = String.Format("\c"..tclabel_s.."I \c-%d\c"..tcextra_s.."/\c-%d",level.found_items,level.total_items); int basew = MiniHUDFontOutline.StringWidth(str); if ( pstats ) { int dcnt = 2-int(Log10(clamp(pct,1,999))); for ( int j=0; j=level.total_items)?tccompl:tcvalue,(xx-basew)+8+j*4,yy,0x30,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,Color(160,0,0,0)); } Screen.DrawText(MiniHUDFontOutline,(level.found_items>=level.total_items)?tccompl:tcvalue,xx-basew,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); if ( pstats ) { if ( pitemflash && (gametic < pitemflash) ) { double alph = max((pitemflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%3d%%",pct); int pctpos = str.IndexOf("%"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(pctpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } else { if ( itemflash && (gametic < itemflash) ) { double alph = max((itemflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d/%d",level.found_items,level.total_items); int slashpos = str.IndexOf("/"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } if ( titemflash && (gametic < titemflash) ) { double alph = max((titemflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d",level.total_items); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } yy += MiniHUDFontOutline.GetHeight()+2; } if ( (level.total_secrets > 0) && am_showsecrets && !deathmatch ) { int pct = (level.found_secrets*100)/level.total_secrets; if ( pstats ) str = String.Format("\c"..tclabel_s.."S \c-%3d\c"..tcextra_s.."%%\c-",pct); else str = String.Format("\c"..tclabel_s.."S \c-%d\c"..tcextra_s.."/\c-%d",level.found_secrets,level.total_secrets); int basew = MiniHUDFontOutline.StringWidth(str); if ( pstats ) { int dcnt = 2-int(Log10(clamp(pct,1,999))); for ( int j=0; j=level.total_secrets)?tccompl:tcvalue,(xx-basew)+8+j*4,yy,0x30,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,Color(160,0,0,0)); } Screen.DrawText(MiniHUDFontOutline,(level.found_secrets>=level.total_secrets)?tccompl:tcvalue,xx-basew,yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); if ( pstats ) { if ( psecretflash && (gametic < psecretflash) ) { double alph = max((psecretflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%3d%%",pct); int pctpos = str.IndexOf("%"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(pctpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } else { if ( secretflash && (gametic < secretflash) ) { double alph = max((secretflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d/%d",level.found_secrets,level.total_secrets); int slashpos = str.IndexOf("/"); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str.Left(slashpos),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } if ( tsecretflash && (gametic < tsecretflash) ) { double alph = max((tsecretflash-(gametic+FracTic))/25.,0.)**1.5; str = String.Format("%d",level.total_secrets); Screen.DrawText(MiniHUDFontOutline,mhudfontcol[MCR_FLASH],xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); } } yy += MiniHUDFontOutline.GetHeight()+2; } int sec; if ( am_showtime ) { sec = Thinker.Tics2Seconds(level.maptime); str = String.Format("\c"..tclabel_s.."T \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); Screen.DrawText(MiniHUDFontOutline,((level.sucktime>0)&&(sec>=(level.sucktime*3600)))?tcsucks:((level.partime>0)&&(sec<=level.partime))?tccompl:tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); yy += MiniHUDFontOutline.GetHeight()+2; } // don't show total time if it's equal to map time if ( am_showtotaltime && (level.totaltime != level.maptime) ) { sec = Thinker.Tics2Seconds(level.totaltime); str = String.Format("\c"..tclabel_s.."TT \c-%02d\c"..tcextra_s..":\c-%02d\c"..tcextra_s..":\c-%02d",sec/3600,(sec%3600)/60,sec%60); Screen.DrawText(MiniHUDFontOutline,tcvalue,xx-MiniHUDFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); yy += MiniHUDFontOutline.GetHeight()+2; } yy += 3; } if ( deathmatch ) { yy += 9; if ( playercount <= 1 ) return; xx = int(ss.x-(xmargin+2)); String str; if ( teamplay ) { // draw team scores for ( int i=0; i 0 ) str = String.Format("\c[MiniDemoBlue]%s \c-+%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); else str = String.Format("\c[MiniDemoBlue]%s \c-%d",StringTable.Localize("$SWWM_DMSPREAD"),lead); Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_RED],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); yy += mSmallFont.GetHeight()+3; // draw top 3 players for ( int i=0; i klist; for ( int i=0; i= f.flashtime) ) continue; double alph = max((f.flashtime-(gametic+FracTic))/25.,0.)**1.5; Screen.DrawTexture(icon,false,keypos.x-siz.x,keypos.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_TopLeft,true,DTA_ColorOverlay,0xFFFFC040,DTA_LegacyRenderStyle,STYLE_Add,DTA_Alpha,alph); break; } keypos.x -= siz.x+2; colh = max(colh,siz.y); if ( ++colc == maxcolc ) { keypos.x = ss.x-(xmargin+2); keypos.y += colh+2; colh = colc = 0; } } } override void DrawAutomapHUD( double ticFrac ) { // do nothing, DrawTopStuff handles this } }