swwmgz_m/zscript/handler/swwm_handler_crosshair.zsc

195 lines
8.5 KiB
Text

// precise crosshair (now fully independent from weapons)
const MAX_CROSSHAIRS = 25;
Class SWWMCrosshairTracer : LineTracer
{
Actor ignoreme;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
extend Class SWWMHandler
{
transient ui SWWMCrosshairTracer ctr; // very simple crosshair tracer
transient ui int numcrosshairs; // how many crosshairs the current weapon has
transient ui Vector3 tpos[MAX_CROSSHAIRS]; // current trace positions in world space
transient ui Color tcol[MAX_CROSSHAIRS]; // current crosshair colors
transient ui SWWMProjectionData tprojdata; // cached Gutamatics projection data
transient ui Vector3 lagtndc[MAX_CROSSHAIRS]; // "lagged" NDC for crosshairs
transient ui bool tactive[MAX_CROSSHAIRS]; // denotes that the crosshair is "active" for drawing
transient ui double prevframe; // previous frame timestamp
ui Font TinyFont;
private ui void TraceCrosshairs( RenderEvent e )
{
// trace precise crosshair(s)
Vector3 traceofs[MAX_CROSSHAIRS];
let sw = SWWMWeapon(players[consoleplayer].ReadyWeapon);
if ( !sw )
{
numcrosshairs = 0;
for ( int i=0; i<MAX_CROSSHAIRS; i++ )
tactive[i] = false;
return;
}
numcrosshairs = clamp(sw.NumCrosshairs,1,MAX_CROSSHAIRS);
for ( int i=0; i<numcrosshairs; i++ )
{
traceofs[i] = sw.GetTraceOffset(i);
tactive[i] = true;
}
let mo = players[consoleplayer].mo;
if ( !ctr ) ctr = new("SWWMCrosshairTracer");
ctr.ignoreme = mo;
Vector3 x, y, z, ofs, origin;
Color col;
[x, y, z] = swwm_CoordUtil.GetAxes(e.ViewPitch,e.ViewAngle,e.ViewRoll);
int chp = crosshairhealth;
for ( int i=0; i<numcrosshairs; i++ )
{
origin = level.Vec3Offset(e.ViewPos,traceofs[i].x*x+traceofs[i].y*y+traceofs[i].z*z);
ctr.Trace(origin,level.PointInSector(origin.xy),x,10000.,0);
if ( chp >= 2 )
{
int hp = Clamp(mo.Health,0,200);
double sat = (hp<150)?1.:(1.-(hp-150)/100.);
Vector3 rgb = SWWMUtility.HSVtoRGB((hp/300.,sat,1.));
col = Color(int(rgb.x*255),int(rgb.y*255),int(rgb.z*255));
}
else if ( chp == 1 )
{
double hp = Clamp(mo.Health,0,100)/100.;
if ( hp <= 0 ) col = Color(255,0,0);
else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0);
else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0);
else col = Color(0,255,0);
}
else if ( (ctr.Results.HitType == TRACE_HitActor) && ctr.Results.HitActor.bSHOOTABLE )
{
// show target health, rather than our own
double hp = ctr.Results.HitActor.Health/double(ctr.Results.HitActor.GetSpawnHealth());
if ( hp <= 0 ) col = Color(255,0,0);
else if ( hp < .3 ) col = Color(255,int(hp*255/.3),0);
else if ( hp < .85 ) col = Color(int((.6-hp)*255/.3),255,0);
else col = Color(0,255,0);
}
else col = crosshaircolor;
if ( ctr.Results.HitType == TRACE_HitNone ) tpos[i] = level.Vec3Offset(origin,x*10000.);
else tpos[i] = ctr.Results.HitPos;
tcol[i] = col;
}
// copy over used slots to unused slots, so transition between weapons is smoother
int j = 0;
for ( int i=numcrosshairs; i<MAX_CROSSHAIRS; i++ )
{
tpos[i] = tpos[j];
tcol[i] = tcol[j];
j = (j+1)%numcrosshairs;
}
}
private ui void RenderCrosshairs( RenderEvent e )
{
double curframe = MSTimeF();
double frametime = (curframe-prevframe)/1000.;
double theta = clamp(15.*frametime,0.,.5); // naive, but whatever
// draw precise crosshair(s)
if ( automapactive || (players[consoleplayer].Camera != players[consoleplayer].mo) || (players[consoleplayer].cheats&CF_CHASECAM) || (numcrosshairs <= 0) ) return;
if ( !swwm_precisecrosshair ) return;
if ( crosshairforce ) return;
if ( !crosshairon && (swwm_precisecrosshair <= 1) ) return;
int cnum = abs(CVar.FindCVar('crosshair').GetInt());
if ( !cnum ) return;
String tn = String.Format("XHAIR%s%d",(Screen.GetWidth()<640)?"S":"B",cnum);
TextureID ctex = TexMan.CheckForTexture(tn,TexMan.Type_MiscPatch);
if ( !ctex.IsValid() ) ctex = TexMan.CheckForTexture(String.Format("XHAIR%s1",(Screen.GetWidth()<640)?"S":"B"),TexMan.Type_MiscPatch);
if ( !ctex.IsValid() ) ctex = TexMan.CheckForTexture("XHAIRS1",TexMan.Type_MiscPatch);
Vector2 ts = TexMan.GetScaledSize(ctex);
double cs = crosshairscale;
double sz = 1.;
if ( cs > 0. ) sz = Screen.GetHeight()*cs/200.;
if ( crosshairgrow ) sz *= StatusBar.CrosshairSize;
SWWMUtility.PrepareProjData(tprojdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov);
Vector2 actpos[MAX_CROSSHAIRS];
if ( !TinyFont ) TinyFont = Font.GetFont('TewiFontOutline');
int fh = TinyFont.GetHeight();
bool debug = swwm_debugchair;
int nact = 0;
for ( int i=0; i<MAX_CROSSHAIRS; i++ ) nact += tactive[i];
if ( debug ) Screen.DrawText(TinyFont,Font.CR_UNTRANSLATED,4,4,(nact!=numcrosshairs)?String.Format("\cf%d USED CROSSHAIRS \cd%d ACTIVE CROSSHAIRS\c-",numcrosshairs,nact):String.Format("\cf%d USED CROSSHAIRS\c-",numcrosshairs));
for ( int i=0; i<MAX_CROSSHAIRS; i++ )
{
int xpos = 4+320*(i%5);
int ypos = 4+fh*2+160*(i/5);
int fc = (i<numcrosshairs)?Font.CR_GOLD:Font.CR_GREEN;
Vector3 cpos = tpos[i];
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("CPOS: (%g,%g,%g)",cpos.x,cpos.y,cpos.z));
ypos += fh;
Color ccol = tcol[i];
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("CCOL: %08X",ccol));
ypos += fh;
Vector3 tdir = level.Vec3Diff(e.ViewPos,cpos);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("TDIR: (%g,%g,%g)",tdir.x,tdir.y,tdir.z));
ypos += fh;
// project
Vector3 ndc = SWWMUtility.ProjectPoint(tprojdata,e.ViewPos+tdir);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("NDC: (%g,%g,%g)",ndc.x,ndc.y,ndc.z));
ypos += fh;
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(tprojdata,ndc);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("VPOS: (%g,%g)",vpos.x,vpos.y));
ypos += fh;
if ( !prevframe ) lagtndc[i] = ndc;
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("LAGTNDC: (%g,%g,%g)",lagtndc[i].x,lagtndc[i].y,lagtndc[i].z));
ypos += fh;
if ( lagtndc[i].z >= 1. ) continue;
Vector2 oldvpos = SWWMUtility.NDCToViewport(tprojdata,lagtndc[i]);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("OLDVPOS: (%g,%g)",oldvpos.x,oldvpos.y));
ypos += fh;
lagtndc[i] = SWWMUtility.LerpVector3(lagtndc[i],ndc,theta);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("LAGTNDC: (%g,%g,%g)",lagtndc[i].x,lagtndc[i].y,lagtndc[i].z));
ypos += fh;
if ( lagtndc[i].z >= 1. ) continue;
Vector2 lagvpos = SWWMUtility.NDCToViewport(tprojdata,lagtndc[i]);
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("LAGVPOS: (%g,%g)",lagvpos.x,lagvpos.y));
ypos += fh;
if ( !tactive[i] ) continue;
// draw
int streak = int(max(abs(oldvpos.x-lagvpos.x),abs(oldvpos.y-lagvpos.y)));
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("STREAK: %d",streak));
ypos += fh;
double alph = 1.;
if ( i < numcrosshairs ) actpos[i] = lagvpos;
else
{
// unused crosshairs must "linger" until they merge with the ones that are drawn
int j = (i-numcrosshairs)%numcrosshairs;
double dist = (lagvpos-actpos[j]).length();
if ( (streak <= 0) && (dist < 1.) )
tactive[i] = false;
alph = clamp(dist/max(2,streak+2),0.,1.); // this should make the merge less jarring
}
if ( debug && tactive[i] ) Screen.DrawText(TinyFont,fc,xpos,ypos,String.Format("ALPH: %g",alph));
ypos += fh;
for ( int i=0; i<streak; i++ ) Screen.DrawTexture(ctex,false,int(SWWMUtility.lerp(oldvpos.x,lagvpos.x,i/double(streak))),int(SWWMUtility.lerp(oldvpos.y,lagvpos.y,i/double(streak))),DTA_DestWidthF,ts.x*sz,DTA_DestHeightF,ts.y*sz,DTA_AlphaChannel,true,DTA_FillColor,ccol,DTA_Alpha,((i*.5)/streak)*alph);
Screen.DrawTexture(ctex,false,int(lagvpos.x),int(lagvpos.y),DTA_DestWidthF,ts.x*sz,DTA_DestHeightF,ts.y*sz,DTA_AlphaChannel,true,DTA_FillColor,ccol,DTA_Alpha,alph);
}
prevframe = curframe;
}
}