diff --git a/graphics/GuidedX1.png b/graphics/GuidedX1.png new file mode 100644 index 0000000..c330d16 Binary files /dev/null and b/graphics/GuidedX1.png differ diff --git a/graphics/GuidedX2.png b/graphics/GuidedX2.png new file mode 100644 index 0000000..02cfa46 Binary files /dev/null and b/graphics/GuidedX2.png differ diff --git a/zscript.txt b/zscript.txt index 436f0c5..77c0177 100644 --- a/zscript.txt +++ b/zscript.txt @@ -2,6 +2,7 @@ version "3.4" #include "zscript/mk_matrix.zsc" #include "zscript/mk_coordutil.zsc" +#include "zscript/mk_quaternion.zsc" #include "zscript/utcommon.zsc" #include "zscript/impacthammer.zsc" #include "zscript/chainsaw.zsc" diff --git a/zscript/mk_quaternion.zsc b/zscript/mk_quaternion.zsc new file mode 100644 index 0000000..24631a5 --- /dev/null +++ b/zscript/mk_quaternion.zsc @@ -0,0 +1,97 @@ +/* + Quaternion math helper class. + (C)2018 Marisa Kirisame, UnSX Team. + Released under the GNU Lesser General Public License version 3 (or later). + See https://www.gnu.org/licenses/lgpl-3.0.txt for its terms. +*/ + +Class Quat +{ + protected double W, X, Y, Z; + + Quat init( double w, double x, double y, double z ) + { + self.W = w; + self.X = x; + self.Y = y; + self.Z = z; + return self; + } + + void copy( Quat q ) + { + W = q.W; + X = q.X; + Y = q.Y; + Z = q.Z; + } + + static Quat create( double w, double x, double y, double z ) + { + return new("Quat").init(w,x,y,z); + } + + static Quat create_axis( Vector3 axis, double theta ) + { + double scale = axis dot axis; + if ( scale < double.epsilon ) return Quat.create(1,0,0,0); + theta *= 0.5; + double f = sin(theta)/sqrt(scale); + return Quat.create(cos(theta),axis.x*f,axis.y*f,axis.z*f); + } + + static Quat create_euler( double pitch, double yaw, double roll ) + { + Quat zrot = Quat.create_axis((0,0,1),yaw); + Quat yrot = Quat.create_axis((0,1,0),pitch); + Quat xrot = Quat.create_axis((1,0,0),roll); + Quat sum = zrot.qmul(yrot); + sum = sum.qmul(xrot); + return sum; + } + + // copied here since Actor.Normalize180 is not (yet) clearscope + static double Normalize180( double ang ) + { + ang = ang%360; + ang = (ang+360)%360; + if ( ang > 180 ) ang -= 360; + return ang; + } + + double, double, double to_euler() + { + double stest = z*x-w*y; + double yawY = 2*(w*z+x*y); + double yawX = 1-2*(y*y+z*z); + double st = 0.4999995; + double pitch = 0; + double yaw = 0; + double roll = 0; + if ( stest <= -st ) + { + pitch = 90; + yaw = atan2(yawY,yawX); + roll = Normalize180(yaw+(2*atan2(x,w))); + } + else if ( stest > st ) + { + pitch = -90; + yaw = atan2(yawY,yawX); + roll = Normalize180(yaw+(2*atan2(x,w))); + } + else + { + pitch = -asin(2*stest); + yaw = atan2(yawY,yawX); + roll = atan2(2*(w*x+y*z),(1-2*(x*x+y*y))); + } + return pitch, yaw, roll; + } + + Quat qmul( Quat q ) + { + return Quat.create(w*q.w-x*q.x-y*q.y-z*q.z,w*q.x+x*q.w+y*q.z-z + *q.y,w*q.y+y*q.w+z*q.x-x*q.z,w*q.z+z*q.w+x*q.y-y*q.x); + } +} diff --git a/zscript/warheadlauncher.zsc b/zscript/warheadlauncher.zsc index 0fa59e4..4e793b7 100644 --- a/zscript/warheadlauncher.zsc +++ b/zscript/warheadlauncher.zsc @@ -355,15 +355,13 @@ Class WarShell : Actor Class GuidedWarShell : WarShell { - double lagangle, lagpitch, lagangle2, lagpitch2; - double guideangle, guidepitch, lastguideroll; + double lagangle, lagpitch, lagroll, lagangle2, lagpitch2, lagroll2, + guideangle, guidepitch, guideroll; override void PostBeginPlay() { Super.PostBeginPlay(); if ( target && target.player ) target.player.camera = self; - guideangle = angle; - guidepitch = pitch; } override void Tick() { @@ -385,22 +383,25 @@ Class GuidedWarShell : WarShell { lagangle = target.player.cmd.yaw/128.; lagpitch = -target.player.cmd.pitch/128.; - guideangle += lagangle2*0.95+lagangle*0.05; - guidepitch += lagpitch2*0.95+lagpitch*0.05; - guidepitch = Clamp(guidepitch,-89,89); - double guideroll = -lagangle2*15; - Vector3 dir = (cos(guideangle)*cos(guidepitch),sin(guideangle)*cos(guidepitch),-sin(guidepitch)); - destangle = atan2(dir.y,dir.x); - destpitch = asin(-dir.z); - double destroll = lastguideroll*0.9+guideroll*0.1; - A_SetAngle(destangle,SPF_INTERPOLATE); - A_SetPitch(destpitch,SPF_INTERPOLATE); - A_SetRoll(destroll,SPF_INTERPOLATE); + if ( target.player.cmd.buttons&BT_RELOAD ) lagroll = 5; + else if ( target.player.cmd.buttons&BT_ZOOM ) lagroll = -5; + else lagroll = 0; + guideangle = lagangle2*0.95+lagangle*0.05; + guidepitch = lagpitch2*0.95+lagpitch*0.05; + guideroll = lagroll2*0.95+lagroll*0.05; + Quat orient = Quat.create_euler(pitch,angle,roll); + Quat angles = Quat.create_euler(guidepitch,guideangle,guideroll); + orient = orient.qmul(angles); + double npitch, nangle, nroll; + [npitch, nangle, nroll] = orient.to_euler(); + A_SetAngle(nangle,SPF_INTERPOLATE); + A_SetPitch(npitch,SPF_INTERPOLATE); + A_SetRoll(nroll,SPF_INTERPOLATE); vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11; } lagangle2 = lagangle2*0.95+lagangle*0.05; lagpitch2 = lagpitch2*0.95+lagpitch*0.05; - lastguideroll = roll*0.98; + lagroll2 = lagroll2*0.95+lagroll*0.05; } States { @@ -439,25 +440,36 @@ Class RedeemerHUD : HUDMessageBase { Actor Camera; Vector3 ViewPos; - double ViewAngle, ViewPitch, ViewRoll; - TextureID reticle, mark, readout; + double ViewAngle, ViewPitch, ViewRoll, LagRoll, LagRoll2; + TextureID reticle1, reticle2, mark, readout; Font whfont; ThinkerIterator t; MidTracer tr; Array ta; + Shape2D rreticle; RedeemerHUD Init() { - reticle = TexMan.CheckForTexture("GuidedX",TexMan.Type_Any); + reticle1 = TexMan.CheckForTexture("GuidedX1",TexMan.Type_Any); + reticle2 = TexMan.CheckForTexture("GuidedX2",TexMan.Type_Any); mark = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any); readout = TexMan.CheckForTexture("Readout",TexMan.Type_Any); whfont = Font.GetFont('WHFONT'); t = ThinkerIterator.Create("Actor"); tr = new("MidTracer"); + rreticle = new("Shape2D"); + rreticle.PushCoord((0,0)); + rreticle.PushCoord((1,0)); + rreticle.PushCoord((0,1)); + rreticle.PushCoord((1,1)); + rreticle.PushTriangle(0,3,1); + rreticle.PushTriangle(0,2,3); return self; } override bool Tick() { + LagRoll = Quat.Normalize180(ViewRoll-LagRoll2); + LagRoll2 += Quat.Normalize180(LagRoll-LagRoll2)*0.1; // shootable targetting if ( CVar.GetCVar('flak_redeemerreadout',players[consoleplayer]).GetBool() ) { @@ -494,8 +506,21 @@ Class RedeemerHUD : HUDMessageBase Screen.DrawText(whfont,Font.CR_UNTRANSLATED,(ta[i].vpos.x-whfont.StringWidth(ta[i].diststr)/2)-12,ta[i].vpos.y+8,ta[i].diststr,DTA_RenderStyle,(1|2<<8|1<<16)); } } - // other stuff - Screen.DrawTexture(reticle,false,320,240,DTA_VirtualWidth,640,DTA_VirtualHeight,480,DTA_RenderStyle,(1|2<<8|1<<16)); + // reticle + Vector2 vs = (640,640/Screen.GetAspectRatio()); + Vector2 mid, siz; + [mid, siz] = Screen.VirtualToRealCoords(vs*0.5,(128,128),vs,false,false); + Vector2 verts[4]; + verts[0] = (-siz.x,-siz.y); + verts[1] = (siz.x,-siz.y); + verts[2] = (-siz.x,siz.y); + verts[3] = (siz.x,siz.y); + rreticle.Clear(Shape2D.C_Verts); + for ( int i=0; i<4; i++ ) + rreticle.PushVertex(mid+(verts[i].x*cos(LagRoll2)-verts[i].y*sin(LagRoll2),verts[i].x*sin(LagRoll2)+verts[i].y*cos(LagRoll2))); + Screen.DrawShape(reticle1,false,rreticle,DTA_RenderStyle,(1|2<<8|1<<16)); + Screen.DrawTexture(reticle2,false,vs.x*0.5,vs.y*0.5,DTA_VirtualWidthF,vs.x,DTA_VirtualHeightF,vs.y,DTA_KeepRatio,true,DTA_RenderStyle,(1|2<<8|1<<16)); + // faux assembly readout int numreadouts = Screen.GetHeight()/128+2; for ( int i=0; i