1073 lines
40 KiB
Text
1073 lines
40 KiB
Text
// Minimap and stats
|
|
extend Class SWWMStatusBar
|
|
{
|
|
// quicksort (player scores)
|
|
private int partition_playerscore( Array<PlayerInfo> 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<PlayerInfo> 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 FlushTopStuff()
|
|
{
|
|
ScoreInter.Reset(SWWMCredits.Get(CPlayer));
|
|
}
|
|
|
|
private void TickTopStuffInterpolators()
|
|
{
|
|
ScoreInter.Update(SWWMCredits.Get(CPlayer));
|
|
// 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<keyflash.Size(); i++ )
|
|
{
|
|
if ( keyflash[i].flashtime >= 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<Teams.Size(); i++ )
|
|
{
|
|
teamactive[i] = false;
|
|
teamscore[i] = 0;
|
|
}
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] ) continue;
|
|
int team = players[i].GetTeam();
|
|
if ( team != -1 )
|
|
{
|
|
teamactive[team] = true;
|
|
teamscore[team] += players[i].fragcount;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
playercount = 0;
|
|
int highscore = int.min;
|
|
tiedscore = false;
|
|
rank = 1;
|
|
for ( int i=0; i<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] ) continue;
|
|
playercount++;
|
|
if ( players[i] == CPlayer ) continue;
|
|
if ( players[i].fragcount > 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<MAXPLAYERS; i++ )
|
|
{
|
|
if ( !playeringame[i] ) continue;
|
|
sortplayers[j++] = players[i];
|
|
}
|
|
// sort players by score
|
|
qsort_playerscore(sortplayers,0,playercount-1);
|
|
if ( playercount <= 1 ) highscore = CPlayer.fragcount;
|
|
lead = CPlayer.fragcount-highscore;
|
|
}
|
|
|
|
// minimap helper code
|
|
private void GetMinimapColors()
|
|
{
|
|
mm_backcolor = "10 10 10";
|
|
mm_cdwallcolor = "30 50 70";
|
|
mm_efwallcolor = "80 a0 c0";
|
|
mm_fdwallcolor = "50 70 90";
|
|
mm_interlevelcolor = "ff 00 60";
|
|
mm_intralevelcolor = "00 60 ff";
|
|
mm_lockedcolor = "00 90 80";
|
|
mm_notseencolor = "20 20 30";
|
|
mm_portalcolor = "40 30 20";
|
|
mm_secretsectorcolor = "80 00 ff";
|
|
mm_secretwallcolor = "60 40 80";
|
|
mm_specialwallcolor = "ff a0 00";
|
|
mm_thingcolor = "ff ff ff";
|
|
mm_thingcolor_citem = "00 ff ff";
|
|
mm_thingcolor_friend = "80 ff a0";
|
|
mm_thingcolor_item = "ff c0 00";
|
|
mm_thingcolor_monster = "ff 60 40";
|
|
mm_thingcolor_ncmonster = "c0 40 20";
|
|
mm_thingcolor_shootable = "a0 30 10";
|
|
mm_thingcolor_vipitem = "80 60 ff";
|
|
mm_thingcolor_missile = "ff c0 40";
|
|
mm_tswallcolor = "30 20 40";
|
|
mm_unexploredsecretcolor = "40 00 80";
|
|
mm_wallcolor = "c0 e0 ff";
|
|
mm_yourcolor = "80 ff 00";
|
|
}
|
|
private bool ShouldDisplaySpecial( int special )
|
|
{
|
|
// thanks graf/randi/whoever
|
|
switch ( special )
|
|
{
|
|
// the following have (max_args < 0)
|
|
// but we can't know this from zscript, so they're hardcoded here
|
|
case Polyobj_StartLine:
|
|
case Polyobj_ExplicitLine:
|
|
case Transfer_WallLight:
|
|
case Sector_Attach3dMidtex:
|
|
case ExtraFloor_LightOnly:
|
|
case Sector_CopyScroller:
|
|
case Scroll_Texture_Left:
|
|
case Scroll_Texture_Right:
|
|
case Scroll_Texture_Up:
|
|
case Scroll_Texture_Down:
|
|
case Plane_Copy:
|
|
case Line_SetIdentification:
|
|
case Line_SetPortal:
|
|
case Sector_Set3DFloor:
|
|
case Sector_SetContents:
|
|
case Plane_Align:
|
|
case Static_Init:
|
|
case Transfer_Heights:
|
|
case Transfer_FloorLight:
|
|
case Transfer_CeilingLight:
|
|
case Scroll_Texture_Model:
|
|
case Scroll_Texture_Offsets:
|
|
case PointPush_SetForce:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private bool CheckWhichSpecial( int type, int special )
|
|
{
|
|
if ( type == 1 )
|
|
{
|
|
// check for teleport specials
|
|
if ( (special == Teleport)
|
|
|| (special == Teleport_NoFog)
|
|
|| (special == Teleport_ZombieChanger)
|
|
|| (special == Teleport_Line) )
|
|
return true;
|
|
return false;
|
|
}
|
|
if ( type == 2 )
|
|
{
|
|
// check for exit specials
|
|
if ( (special == Exit_Normal)
|
|
|| (special == Exit_Secret)
|
|
|| (special == Teleport_NewMap)
|
|
|| (special == Teleport_EndGame) )
|
|
return true;
|
|
return false;
|
|
}
|
|
// just check normal specials
|
|
if ( !special || (am_showtriggerlines == 0) ) return false;
|
|
if ( !ShouldDisplaySpecial(special) ) return false;
|
|
if ( special && (am_showtriggerlines >= 2) ) return true;
|
|
if ( (special == Door_Open)
|
|
|| (special == Door_Close)
|
|
|| (special == Door_CloseWaitOpen)
|
|
|| (special == Door_Raise)
|
|
|| (special == Door_Animated)
|
|
|| (special == Generic_Door) )
|
|
return false;
|
|
return true;
|
|
}
|
|
private bool CheckSectorAction( Sector s, int type, bool useonly )
|
|
{
|
|
for ( Actor act=s.SecActTarget; act; act=act.tracer )
|
|
{
|
|
if ( ((act.Health&(SectorAction.SECSPAC_Use|SectorAction.SECSPAC_UseWall)) || (false == useonly))
|
|
&& CheckWhichSpecial(type,act.special) && !act.bFRIENDLY )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
private bool RealLineSpecial( Line l, int type )
|
|
{
|
|
if ( l.activation&SPAC_PlayerActivate && CheckWhichSpecial(type,l.special) )
|
|
return true;
|
|
if ( CheckSectorAction(l.frontsector,type,!l.backsector) )
|
|
return true;
|
|
return (l.backsector && CheckSectorAction(l.backsector,type,false));
|
|
}
|
|
private bool CheckTriggerLine( Line l )
|
|
{
|
|
return RealLineSpecial(l,0);
|
|
}
|
|
private bool CheckTeleportLine( Line l )
|
|
{
|
|
return RealLineSpecial(l,1);
|
|
}
|
|
private bool CheckExitLine( Line l )
|
|
{
|
|
return RealLineSpecial(l,2);
|
|
}
|
|
private bool CmpFloorPlanes( Line l )
|
|
{
|
|
return (l.frontsector.floorplane.Normal == l.backsector.floorplane.Normal)
|
|
&& (l.frontsector.floorplane.D == l.backsector.floorplane.D);
|
|
}
|
|
private bool CmpCeilingPlanes( Line l )
|
|
{
|
|
return (l.frontsector.ceilingplane.Normal == l.backsector.ceilingplane.Normal)
|
|
&& (l.frontsector.ceilingplane.D == l.backsector.ceilingplane.D);
|
|
}
|
|
private int CheckSecret( Line l )
|
|
{
|
|
if ( !mm_secretsectorcolor || !mm_unexploredsecretcolor )
|
|
return 0;
|
|
if ( l.frontsector && (l.frontsector.flags&Sector.SECF_WASSECRET) )
|
|
{
|
|
if ( am_map_secrets && !(l.frontsector.flags&Sector.SECF_SECRET) ) return 1;
|
|
if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2;
|
|
}
|
|
if ( l.backsector && (l.backsector.flags&Sector.SECF_WASSECRET) )
|
|
{
|
|
if ( am_map_secrets && !(l.backsector.flags&Sector.SECF_SECRET) ) return 1;
|
|
if ( (am_map_secrets == 2) && !(l.flags&Line.ML_SECRET) ) return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
private bool CheckFFBoundary( Line l )
|
|
{
|
|
int fcount = l.frontsector.Get3DFloorCount(),
|
|
bcount = l.backsector.Get3DFloorCount();
|
|
// no 3D floors, no boundary
|
|
if ( !fcount && !bcount ) return false;
|
|
int fvalid = 0, bvalid = 0;
|
|
for ( int i=0; i<fcount; i++ )
|
|
{
|
|
F3DFloor ff = l.frontsector.Get3DFloor(i);
|
|
if ( (ff.flags&F3DFloor.FF_THISINSIDE) || !(ff.flags&F3DFloor.FF_EXISTS) || (ff.alpha == 0) )
|
|
continue;
|
|
fvalid++;
|
|
}
|
|
for ( int i=0; i<bcount; i++ )
|
|
{
|
|
F3DFloor ff = l.backsector.Get3DFloor(i);
|
|
if ( (ff.flags&F3DFloor.FF_THISINSIDE) || !(ff.flags&F3DFloor.FF_EXISTS) || (ff.alpha == 0) )
|
|
continue;
|
|
bvalid++;
|
|
}
|
|
// unmatched counts, there's definitely a boundary
|
|
if ( fvalid != bvalid ) return true;
|
|
for ( int i=0; i<fcount; i++ )
|
|
{
|
|
F3DFloor ff = l.frontsector.Get3DFloor(i);
|
|
if ( (ff.flags&F3DFloor.FF_THISINSIDE) || !(ff.flags&F3DFloor.FF_EXISTS) || (ff.alpha == 0) )
|
|
continue;
|
|
bool found = false;
|
|
for ( int j=0; j<bcount; j++ )
|
|
{
|
|
F3DFloor ff2 = l.backsector.Get3DFloor(j);
|
|
if ( (ff2.flags&F3DFloor.FF_THISINSIDE) || !(ff2.flags&F3DFloor.FF_EXISTS) || (ff2.alpha == 0) )
|
|
continue;
|
|
if ( (ff.model != ff2.model) || (ff.flags != ff2.flags) )
|
|
continue;
|
|
found = true;
|
|
break;
|
|
}
|
|
// at least one mismatch between sectors, there's a boundary for sure
|
|
if ( !found ) return true;
|
|
}
|
|
// no boundary here
|
|
return false;
|
|
}
|
|
|
|
private void DrawMapLines( 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;
|
|
int thisgroup = csec.portalgroup;
|
|
int numgroups = level.GetPortalGroupCount();
|
|
// draw overlays first
|
|
for ( int p=numgroups-1; p >= -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) && !level.allmap ) // allmap will reveal these
|
|
{
|
|
if ( am_cheat && l.backsector && mm_secretwallcolor )
|
|
col = mm_secretwallcolor;
|
|
else col = mm_wallcolor;
|
|
}
|
|
else if ( mm_interlevelcolor && CheckExitLine(l) )
|
|
col = mm_interlevelcolor;
|
|
else if ( mm_intralevelcolor && 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 ( mm_specialwallcolor && 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]);
|
|
Actor a;
|
|
while ( a = ai.Next() )
|
|
{
|
|
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<nidx; j++ ) tv[j].y *= -1;
|
|
// rotate by view
|
|
for ( int j=0; j<nidx; j++ ) tv[j] = Actor.RotateVector(tv[j],ViewRot.x-90);
|
|
bool visible, drawn;
|
|
Vector2 x0, x1;
|
|
// clip to frame
|
|
for ( int j=0; j<nidx; j++ )
|
|
{
|
|
if ( !closeshape && (j == nidx-1) ) break;
|
|
[visible, x0, x1] = SWWMUtility.LiangBarsky((-1,-1)*zoomclip,(1,1)*zoomclip,tv[j],tv[(j+1)%nidx]);
|
|
if ( visible )
|
|
{
|
|
// scale to minimap frame
|
|
x0 *= hsz/zoomclip;
|
|
x1 *= hsz/zoomclip;
|
|
if ( !bUseCanvas )
|
|
{
|
|
x0 *= hs;
|
|
x1 *= hs;
|
|
}
|
|
// offset to minimap center
|
|
x0 += basepos;
|
|
x1 += basepos;
|
|
// draw the line
|
|
if ( bUseCanvas ) mm_canvas.DrawLine(int(x0.x),int(x0.y),int(x1.x),int(x1.y),col,int(t.smoothalpha*255));
|
|
else if ( isportal ) Screen.DrawThickLine(int(x0.x),int(x0.y),int(x1.x),int(x1.y),max(1.,hs*.25),col,int(t.smoothalpha*255));
|
|
else Screen.DrawThickLine(int(x0.x),int(x0.y),int(x1.x),int(x1.y),max(1.,hs*.5),col,int(t.smoothalpha*255));
|
|
drawn = true;
|
|
}
|
|
}
|
|
if ( drawn )
|
|
{
|
|
double alph = clamp(((t.lastupdate+mtime)-level.maptime)/double(GameTicRate),0.,1.);
|
|
if ( t.isbeam ) alph *= t.target?(t.target.alpha/t.target.default.alpha):0.;
|
|
double theta = clamp(5.*FrameTime,0.,1.);
|
|
t.smoothalpha = SWWMUtility.Lerp(t.smoothalpha,alph,theta);
|
|
}
|
|
else t.smoothalpha = 0.;
|
|
}
|
|
}
|
|
|
|
private String OrdinalStr( int val, int gender )
|
|
{
|
|
String lstr = "SWWM_PLACE"..val.."_GENDER"..gender;
|
|
String str = StringTable.Localize("$"..lstr);
|
|
if ( str != lstr ) return str;
|
|
return StringTable.Localize("$SWWM_PLACE"..val);
|
|
}
|
|
|
|
private void DrawMinimap( int xx, int yy, bool smol )
|
|
{
|
|
int hsz = smol?HALFMAPSIZE_SMALL:HALFMAPSIZE;
|
|
// draws at fixed resolution
|
|
if ( swwm_mm_usecanvas )
|
|
{
|
|
if ( !mm_canvas ) mm_canvas = TexMan.GetCanvas("MMCANVAS");
|
|
if ( !mm_canvastex ) mm_canvastex = TexMan.CheckForTexture("MMCANVAS");
|
|
mm_canvas.Clear(0,0,hsz*2,hsz*2,mm_backcolor);
|
|
Vector2 basemappos = (hsz,hsz);
|
|
// draw dat stuff
|
|
DrawMapLines(basemappos,smol,true);
|
|
DrawMapThings(basemappos,smol,true);
|
|
DrawMapMarkers(basemappos,smol,true);
|
|
// finally, draw the player arrow
|
|
Vector2 tv[] = {(0,-4),(-3,2),(3,2)};
|
|
for ( int i=0; i<3; i++ ) mm_canvas.DrawLine(int(tv[i].x+hsz),int(tv[i].y+hsz),int(tv[(i+1)%3].x+hsz),int(tv[(i+1)%3].y+hsz),mm_yourcolor);
|
|
// HACK: don't draw before the first refresh of the canvas
|
|
// there is a one-frame delay for its contents to get updated when drawing in the HUD
|
|
if ( (level.maptime > 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);
|
|
// 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 && 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 || (fstats > 0) )
|
|
{
|
|
xx = int(ss.x-(xmargin+2));
|
|
String str;
|
|
if ( automapactive || (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 = mSmallFontOutline.BreakLines(ln,120);
|
|
if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) )
|
|
{
|
|
Screen.DrawText(mSmallFontOutline,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<ln_bl.Count(); i++ )
|
|
{
|
|
yy += mSmallFontOutline.GetHeight();
|
|
Screen.DrawText(mSmallFontOutline,tcvalue,xx-ln_bl.StringWidth(i),yy,ln_bl.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
str = level.mapname.MakeUpper()..": ";
|
|
int labelw = MiniHUDFontOutline.StringWidth(str);
|
|
int namew = ln_bl.StringWidth(0);
|
|
Screen.DrawText(MiniHUDFontOutline,tclabel,xx-(labelw+namew),yy+4,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
Screen.DrawText(mSmallFontOutline,tcvalue,xx-namew,yy,ln_bl.StringAt(0),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
for ( int i=1; i<ln_bl.Count(); i++ )
|
|
{
|
|
yy += mSmallFontOutline.GetHeight();
|
|
Screen.DrawText(mSmallFontOutline,tcvalue,xx-ln_bl.StringWidth(i),yy,ln_bl.StringAt(i),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
}
|
|
}
|
|
yy += mSmallFontOutline.GetHeight()+4;
|
|
}
|
|
if ( (level.total_monsters > 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<dcnt; j++ )
|
|
Screen.DrawChar(MiniHUDFontOutline,(level.killed_monsters>=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<dcnt; j++ )
|
|
Screen.DrawChar(MiniHUDFontOutline,(level.found_items>=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<dcnt; j++ )
|
|
Screen.DrawChar(MiniHUDFontOutline,(level.found_secrets>=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<teamscore.Size(); i++ )
|
|
{
|
|
if ( !teamactive[i] ) continue;
|
|
str = String.Format("\c[MiniDemoBlue]%s \c-%d",Teams[i].mName,teamscore[i]);
|
|
Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_WHITE],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
yy += mSmallFont.GetHeight();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// draw rank and spread like in UT
|
|
if ( tiedscore ) str = String.Format("\cy%s \c[MiniRed]%d\c[MiniSayaHUD]/\c[MiniRed]%d\c-",StringTable.Localize("$SWWM_DMRANK"),rank,playercount);
|
|
else str = String.Format("\c[MiniDemoBlue]%s \c-%d\c[MiniIbukiHUD]/\c-%d",StringTable.Localize("$SWWM_DMRANK"),rank,playercount);
|
|
Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_WHITE],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
yy += mSmallFont.GetHeight();
|
|
if ( lead > 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<min(3,playercount); i++ )
|
|
{
|
|
if ( sortplayers[i].fragcount < 0 ) str = String.Format("\c[MiniDemoBlue]#%d: \c-%s\c- \c[MiniSayaHUD](\c[MiniRed]%d\c[MiniSayaHUD])\c-",i+1,sortplayers[i].GetUserName(),sortplayers[i].fragcount);
|
|
else str = String.Format("\c[MiniDemoBlue]%s: \c-%s\c- \c[MiniIbukiHUD](\c[MiniWhite]%d\c[MiniIbukiHUD])\c-",OrdinalStr(i+1,sortplayers[i].GetGender()),sortplayers[i].GetUserName(),sortplayers[i].fragcount);
|
|
Screen.DrawText(mSmallFontOutline,mhudfontcol[MCR_WHITE],xx-mSmallFontOutline.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
yy += mSmallFont.GetHeight();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// draw key icons
|
|
Vector2 keypos = (ss.x-(xmargin+2),yy);
|
|
int colc = 0;
|
|
double colh = 0;
|
|
int n = Key.GetKeyTypeCount();
|
|
Array<Key> klist;
|
|
for ( int i=0; i<n; i++ )
|
|
{
|
|
let k = Key(CPlayer.mo.FindInventory(Key.GetKeyType(i)));
|
|
if ( !k || !k.Icon.IsValid() ) continue;
|
|
klist.Push(k);
|
|
}
|
|
int maxcolc = (gameinfo.gametype&GAME_DOOMCHEX)?6:4;
|
|
foreach ( k:klist )
|
|
{
|
|
// Hexen key icons aren't meant for this kind of HUD
|
|
TextureID icon = (k is 'HexenKey')?k.SpawnState.GetSpriteTexture(0):k.Icon;
|
|
Vector2 siz = TexMan.GetScaledSize(icon);
|
|
Screen.DrawTexture(icon,false,keypos.x-siz.x,keypos.y,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_TopLeft,true);
|
|
foreach ( f:keyflash )
|
|
{
|
|
if ( !(k is f.got) ) continue;
|
|
if ( !f.flashtime || (gametic >= 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
|
|
}
|
|
}
|