Add player path tracing in minimap.

This commit is contained in:
Mari the Deer 2023-12-07 23:17:27 +01:00
commit 13b2cb53b3
10 changed files with 166 additions and 4 deletions

View file

@ -89,6 +89,9 @@ server bool swwm_altclear = false; // use an alternate, less graphically demand
nosave bool swwm_mm_enable = true; // show a minimap below the score counter
nosave bool swwm_mm_missiles = true; // show projectiles (how revolutionary)
nosave bool swwm_mm_usecanvas = false; // use a canvas to draw the minimap, so its pixel density is consistent with the rest of the HUD
nosave noarchive bool swwm_mm_steptrace = false;// traces the player path in the minimap
nosave int swwm_mm_maxsteps = 5000; // how many steps to keep track of
nosave float swwm_mm_minstep = 32.; // minimum 2D distance between steps in map units
server noarchive bool swwm_iseriouslywanttoplaythiswithbd = false; // self-explanatory

View file

@ -197,6 +197,9 @@ SWWM_ALTCLEAR = "Alternate 100% FX";
SWWM_MM_ENABLE = "Show Minimap";
SWWM_MM_MISSILES = "Projectiles In Minimap";
SWWM_MM_USECANVAS = "Fixed Scale Minimap";
SWWM_MM_STEPTRACE = "Minimap Step Tracing";
SWWM_MM_MAXSTEPS = "Minimap Max Steps";
SWWM_MM_MINSTEP = "Minimap Step Size";
SWWM_AC_UNLOCKED = "Unlocked: ";
SWWM_AC_INCOMPLETE = "Incomplete: ";
SWWM_AC_UNDISCOVERED = "Undiscovered: ";
@ -288,6 +291,9 @@ TOOLTIP_SWWMACHIEVEMENTMENU = "View your achievements.";
TOOLTIP_SWWM_MM_ENABLE = "Displays a minimap on the top right corner of the screen.";
TOOLTIP_SWWM_MM_MISSILES = "Displays projectiles in the minimap. Can be toggled if this clutters too much.";
TOOLTIP_SWWM_MM_USECANVAS = "Rather than being drawn directly on-screen, the minimap will be drawn to a texture, maintaining the same pixel density as the rest of the HUD. Due to engine quirks, this causes the map to have a one-frame delay.";
TOOLTIP_SWWM_MM_STEPTRACE = "Traces the player's path on the minimap. Note: Previously recorded data will be erased when starting a new trace.";
TOOLTIP_SWWM_MM_MAXSTEPS = "Maximum player steps to hold in memory.";
TOOLTIP_SWWM_MM_MINSTEP = "Minimum distance in map units to count as an individual step.";
TOOLTIP_SWWMDEBUGMENU = "Don't touch this unless you know what you're doing.";
TOOLTIP_SWWM_DEBUGBLAST = "Shows radii of DoExplosion calls. Damaging explosions are green, with yellow for the hotspot. Non-damaging explosions are blue, with magenta for the hotspot.";
TOOLTIP_SWWM_DEBUGVIEW = "Shows collision, orientation and velocity of actors, as well as relationship lines to their target/tracer/master pointers (gold/orange/purple respectively).";

View file

@ -191,6 +191,9 @@ SWWM_ALTCLEAR = "Efecto Alternativo de 100%";
SWWM_MM_ENABLE = "Mostrar Minimapa";
SWWM_MM_MISSILES = "Proyectiles en Minimapa";
SWWM_MM_USECANVAS = "Minimapa a Escala Fija";
SWWM_MM_STEPTRACE = "Trazo de Pasos en Minimapa";
SWWM_MM_MAXSTEPS = "Máx. de Pasos en Minimapa";
SWWM_MM_MINSTEP = "Tam. de Pasos en Minimapa";
SWWM_AC_UNLOCKED = "Desbloqueados: ";
SWWM_AC_INCOMPLETE = "Incompletos: ";
SWWM_AC_UNDISCOVERED = "Sin descubrir: ";
@ -283,6 +286,9 @@ TOOLTIP_SWWMACHIEVEMENTMENU = "Revisa tus logros.";
TOOLTIP_SWWM_MM_ENABLE = "Muestra un minimapa en la esquina superior derecha de la pantalla.";
TOOLTIP_SWWM_MM_MISSILES = "Muestra proyectiles en el minimapa. Puede ser desactivado si causa problemas de visibilidad.";
TOOLTIP_SWWM_MM_USECANVAS = "En vez de dibujarse directamente a la pantalla, el mapa se dibujará en una texture, mantentiendo la misma densidad de píxel que el resto del HUD. Debido a peculiaridades del motor, esto causa que el mapa tenga un frame de retardo.";
TOOLTIP_SWWM_MM_STEPTRACE = "Traza el camino del jugador en el minimapa. Nota: Los datos anteriores serán borrados al comenzar un nuevo trazado.";
TOOLTIP_SWWM_MM_MAXSTEPS = "Pasos máximos a almacenar en memoria.";
TOOLTIP_SWWM_MM_MINSTEP = "Distancia mínima en unidades de mapa que cuente como un paso individual.";
TOOLTIP_SWWMDEBUGMENU = "No toques esto a menos que sepas lo que estás haciendo.";
TOOLTIP_SWWM_DEBUGBLAST = "Muestra el radio de funciones DoExplosion. Las explosiones con daño son verdes, con amarillo para su punto caliente. Las explosiones sin daño son azules, con magenta para su punto caliente.";
TOOLTIP_SWWM_DEBUGVIEW = "Muestra la colisión, orientación y velocidad de actores, junto con lineas de relación hacia sus punteros de objetivo/trazador/maestro (dorado/naranja/púrpura respectivamente).";

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r1043 \cu(Thu 7 Dec 18:12:07 CET 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r1043 \cu(2023-12-07 18:12:07)\c-";
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r1044 \cu(Thu 7 Dec 23:17:27 CET 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r1044 \cu(2023-12-07 23:17:27)\c-";

View file

@ -146,6 +146,9 @@ OptionMenu "SWWMOptionMenu"
Option "$SWWM_MM_ENABLE", "swwm_mm_enable", "YesNo"
Option "$SWWM_MM_MISSILES", "swwm_mm_missiles", "YesNo"
Option "$SWWM_MM_USECANVAS", "swwm_mm_usecanvas", "YesNo"
Option "$SWWM_MM_STEPTRACE", "swwm_mm_steptrace", "YesNo"
Slider "$SWWM_MM_MAXSTEPS", "swwm_mm_maxsteps", 100, 8000, 100, 0
Slider "$SWWM_MM_MINSTEP", "swwm_mm_minstep", 8, 256, 8, 0
Option "$SWWM_TARGET", "swwm_targeter", "SWWMTarget"
ScaleSliderFix "$SWWM_BARDIST", "swwm_bardist", 0, 4000, 100, "$SWWM_UNLIMITED"
Option "$SWWM_TARGETVAL", "swwm_targetvals", "YesNo"

View file

@ -46,6 +46,13 @@ Class SWWMHandler : EventHandler
// corruption cards stuff
ui bool incardmenu, cardmessaged;
// ring buffer for player path tracing in minimap
const MAX_TRACED_BUFSZ = 8192;
transient ui Vector2 traced_steps[MAX_TRACED_BUFSZ];
transient ui int traced_steppos, traced_stepnum;
transient ui Vector2 oldplayerpos;
transient ui bool do_trace_steps;
enum EProfileTimer
{
PT_WORLDTICK,

View file

@ -34,6 +34,63 @@ extend Class SWWMHandler
if ( !bar ) return;
bar.mm_zoom = min(1.,bar.mm_zoom+.25);
}
else if ( e.Name ~== "swwmstepclear" )
{
Console.Printf("Cleared %d steps.",traced_stepnum);
traced_steppos = 0;
traced_stepnum = 0;
}
else if ( e.Name ~== "swwmstepcount" )
{
double cmdist = 0.;
int startpos = traced_steppos-traced_stepnum;
if ( startpos < 0 ) startpos += MAX_TRACED_BUFSZ;
for ( int i=1; i<traced_stepnum; i++ )
{
int idx1 = (startpos+(i-1))%MAX_TRACED_BUFSZ;
int idx2 = (startpos+i)%MAX_TRACED_BUFSZ;
// skip if vectors are NaN, for safety
if ( (traced_steps[idx1] != traced_steps[idx1])
|| (traced_steps[idx2] != traced_steps[idx2]) ) continue;
double sdist = level.Vec2Diff(traced_steps[idx1],traced_steps[idx2]).length();
cmdist += sdist;
}
Console.Printf("%d steps, %g map units total.",traced_stepnum,cmdist);
}
}
private ui void CheckPlayerStep()
{
if ( swwm_mm_steptrace && !do_trace_steps )
{
// begin tracing now
traced_steppos = 0;
traced_stepnum = 0;
oldplayerpos = (double.nan,double.nan);
do_trace_steps = true;
}
else if ( !swwm_mm_steptrace && do_trace_steps )
{
// just stop tracing, don't touch the data
do_trace_steps = false;
}
}
private ui void DoPlayerStep( Vector2 playerpos, bool ignoremindist = false )
{
// ignore if current position is NaN
if ( playerpos != playerpos ) return;
// clamp into map bounds to avoid weirdness
playerpos.x = clamp(playerpos.x,-32768.,32767.);
playerpos.y = clamp(playerpos.y,-32768.,32767.);
double clamped_mdist = clamp(swwm_mm_minstep,8.,256.);
double mindistsq = clamped_mdist*clamped_mdist;
if ( !ignoremindist && (oldplayerpos == oldplayerpos) && ((oldplayerpos-playerpos).lengthsquared() < mindistsq) )
return;
oldplayerpos = playerpos;
traced_steps[traced_steppos] = playerpos;
traced_steppos = (traced_steppos+1)%MAX_TRACED_BUFSZ;
traced_stepnum = min(traced_stepnum+1,clamp(swwm_mm_maxsteps,2,MAX_TRACED_BUFSZ));
}
override void InterfaceProcess( ConsoleEvent e )
@ -92,6 +149,28 @@ extend Class SWWMHandler
GenericFlash gf = new("GenericFlash").Setup(flash_camera,flash_color,flash_duration);
StatusBar.AttachMessage(gf,0,BaseStatusBar.HUDMSGLayer_UnderHUD);
}
else if ( e.Name ~== "swwmsendplayerstep" )
{
CheckPlayerStep();
let demo = Demolitionist(players[consoleplayer].mo);
if ( !demo || !do_trace_steps ) return;
DoPlayerStep(demo.pos.xy);
}
else if ( e.Name ~== "swwmsendplayertele" )
{
CheckPlayerStep();
let demo = Demolitionist(players[consoleplayer].mo);
if ( !demo || !do_trace_steps ) return;
// send the position we had before teleporting
DoPlayerStep(demo.pretelepos.xy,true);
// to indicate that the player has teleported, we write a NaN into the buffer (yup)
oldplayerpos = (double.nan,double.nan);
traced_steps[traced_steppos] = (double.nan,double.nan);
traced_steppos = (traced_steppos+1)%MAX_TRACED_BUFSZ;
traced_stepnum = min(traced_stepnum+1,clamp(swwm_mm_maxsteps,2,MAX_TRACED_BUFSZ));
// send the post-teleport position
DoPlayerStep(demo.pos.xy,true);
}
}
override void NetworkProcess( ConsoleEvent e )

View file

@ -733,6 +733,56 @@ extend Class SWWMStatusBar
else t.smoothalpha = 0.;
}
}
private void DrawTracedSteps( Vector2 basepos, bool smol, bool bUseCanvas = false )
{
if ( !hnd ) return;
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);
int startpos = hnd.traced_steppos-hnd.traced_stepnum;
int bufsz = SWWMHandler.MAX_TRACED_BUFSZ;
if ( startpos < 0 ) startpos += bufsz;
for ( int i=1; i<hnd.traced_stepnum; i++ )
{
int alph = int((i*255.)/hnd.traced_stepnum);
int idx1 = (startpos+(i-1))%bufsz;
int idx2 = (startpos+i)%bufsz;
// skip if vectors are NaN, for safety
if ( (hnd.traced_steps[idx1] != hnd.traced_steps[idx1])
|| (hnd.traced_steps[idx2] != hnd.traced_steps[idx2]) ) continue;
Vector2 rv1 = level.Vec2Diff(cpos,hnd.traced_steps[idx1]),
rv2 = level.Vec2Diff(cpos,hnd.traced_steps[idx2]);
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;
// draw the line
if ( bUseCanvas ) mm_canvas.DrawLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),mm_yourcolor,alph);
else Screen.DrawThickLine(int(rv1.x),int(rv1.y),int(rv2.x),int(rv2.y),max(1.,hs*.5),mm_yourcolor,alph);
}
}
private String OrdinalStr( int val, int gender )
{
@ -756,6 +806,7 @@ extend Class SWWMStatusBar
DrawMapLines(basemappos,smol,true);
DrawMapThings(basemappos,smol,true);
DrawMapMarkers(basemappos,smol,true);
DrawTracedSteps(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);
@ -779,6 +830,7 @@ extend Class SWWMStatusBar
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;

View file

@ -189,6 +189,12 @@ extend Class Demolitionist
if ( (gamestate != GS_LEVEL) || !player || (player.mo != self) || (freezetics > 0) ) return;
UpdateFace();
UpdateTags();
if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd )
{
if ( hasteleported ) hnd.SendInterfaceEvent(PlayerNumber(),"swwmsendplayertele");
else hnd.SendInterfaceEvent(PlayerNumber(),"swwmsendplayerstep");
}
if ( hasteleported )
{
// we just got teleported, don't count the travel distance
@ -261,7 +267,7 @@ extend Class Demolitionist
if ( vel dot vel > lagvel dot lagvel ) lagvel = lagvel*.8+vel*.2;
else lagvel = lagvel*.4+vel*.6;
double traveldist = level.Vec3Diff(oldpos,pos).length();
if ( (traveldist == double.infinity) || (traveldist == double.nan) )
if ( (traveldist == double.infinity) || (traveldist != traveldist) )
traveldist = 0.; // prevent glitches from breaking stats
if ( !player.onground || bNoGravity )
{

View file

@ -67,7 +67,7 @@ extend class SWWMUtility
if ( developer >= 2 ) Console.Printf("AchievementProgressIncDOuble: achievement '"..pvar.."' not found");
return;
}
if ( pval == "NaN" ) // this can happen, yup
if ( pval ~== "NaN" ) // this can happen, yup
pval = "0";
hnd.achievementprogress.Insert(pvar,String.Format("%g",pval.ToDouble()+inc));
}