Made visual recoil toggleable (and with configurable strength, too).

Cleaned up some code (mostly related to server CVars).
Proper angle interpolation.
Snap player to floor when walking down steps like UE does (looks kinda twitchy due to lack of view height smoothing).
This commit is contained in:
Marisa the Magician 2018-09-24 15:00:02 +02:00
commit 957f976b29
14 changed files with 80 additions and 75 deletions

View file

@ -216,9 +216,9 @@ Class BioGel : Actor
if ( globalfreeze || level.frozen ) return;
if ( !bNOGRAVITY )
{
A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
A_SetPitch(pitch+yawvel,SPF_INTERPOLATE);
roll += rollvel;
pitch += pitchvel;
pitch += yawvel;
if ( waterlevel > 0 )
{
vel.xy *= 0.98;
@ -288,6 +288,7 @@ Class BioGel : Actor
// align self to what surface was hit, currently does not support 3d floors + slopes properly
virtual void AlignSelf()
{
bINTERPOLATEANGLES = false;
bHITOWNER = true;
A_NoGravity();
A_Stop();
@ -538,6 +539,7 @@ Class BioGel : Actor
+MOVEWITHSECTOR;
+NODAMAGETHRUST;
+HITTRACER;
+INTERPOLATEANGLES;
}
States
{

View file

@ -118,6 +118,7 @@ Class UTRocket : Actor
+SEEKERMISSILE;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+INTERPOLATEANGLES;
}
override void PostBeginPlay()
{
@ -184,7 +185,7 @@ Class UTRocket : Actor
Spawn:
RCKT B 1
{
A_SetRoll(roll+30,SPF_INTERPOLATE);
roll += 30;
if ( invoker.ticcnt++ > 3 )
{
invoker.ticcnt = 0;
@ -194,8 +195,8 @@ Class UTRocket : Actor
if ( vel.length() > 45. ) vel = Vel.unit()*45.;
Vector3 dir = vel.unit();
if ( waterlevel <= 0 ) vel = dir*min(vel.length()+1,32);
A_SetAngle(atan2(dir.y,dir.x),SPF_INTERPOLATE);
A_SetPitch(asin(-dir.z),SPF_INTERPOLATE);
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
for ( int i=0; i<3; i++ )
{
let s = Spawn("UTSmoke",pos);
@ -242,9 +243,9 @@ Class UTGrenade : UTRocket
Spawn:
RCKT A 1
{
A_SetAngle(angle+anglevel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
A_Countdown();
}
Wait;

View file

@ -101,6 +101,7 @@ Class UTCasing : Actor
+MOVEWITHSECTOR;
+THRUACTORS;
+USEBOUNCESTATE;
+INTERPOLATEANGLES;
BounceType "Doom";
BounceFactor 0.65;
BounceSound "bullet/casing";
@ -133,8 +134,8 @@ Class UTCasing : Actor
Spawn:
PCAS A 1
{
A_SetAngle(angle+anglevel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
angle += anglevel;
pitch += pitchvel;
}
Loop;
Bounce:
@ -148,8 +149,8 @@ Class UTCasing : Actor
Death:
PCAS A -1
{
A_SetPitch(0);
A_SetRoll(FRandom[Junk](0,360));
pitch = 0;
roll = FRandom[Junk](0,360);
}
Stop;
}
@ -166,7 +167,7 @@ Class Enforcer : UTWeapon
override void PostRender( double lbottom )
{
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() ) return;
if ( !flak_enforcerreload ) return;
if ( Amount > 1 ) Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,lbottom-Screen.GetHeight()*0.01-confont.GetHeight()*2,String.Format("L Clip: %2d / 20\nR Clip: %2d / 20",slaveclipcount,clipcount));
else Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,lbottom-Screen.GetHeight()*0.01-confont.GetHeight(),String.Format("Clip: %2d / 20",clipcount));
}
@ -209,7 +210,7 @@ Class Enforcer : UTWeapon
if ( !weap || !player ) return;
if ( slave )
{
if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slaveclipcount < 5) ) A_PlaySound("enforcer/click",CHAN_6);
if ( flak_enforcerreload && (invoker.slaveclipcount < 5) ) A_PlaySound("enforcer/click",CHAN_6);
if ( (invoker.slaveclipcount <= 0) || (weap.Ammo1.Amount <= 0) )
{
invoker.slaverefire = 0;
@ -236,7 +237,7 @@ Class Enforcer : UTWeapon
}
else
{
if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.clipcount < 5) ) A_PlaySound("enforcer/click",CHAN_WEAPON);
if ( flak_enforcerreload && (invoker.clipcount < 5) ) A_PlaySound("enforcer/click",CHAN_WEAPON);
if ( (invoker.clipcount <= 0) || (weap.Ammo1.Amount <= 0) )
{
A_ClearRefire();
@ -273,12 +274,12 @@ Class Enforcer : UTWeapon
if ( slave )
{
invoker.slaveclipcount--;
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slaveclipcount <=0) ) invoker.slaveclipcount = (weap.Ammo1.Amount>0)?Min(20,weap.Ammo1.Amount):20;
if ( !flak_enforcerreload && (invoker.slaveclipcount <=0) ) invoker.slaveclipcount = (weap.Ammo1.Amount>0)?Min(20,weap.Ammo1.Amount):20;
}
else
{
invoker.clipcount--;
if ( !CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.clipcount <=0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(20,weap.Ammo1.Amount):20;
if ( !flak_enforcerreload && (invoker.clipcount <=0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(20,weap.Ammo1.Amount):20;
}
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,255,128,0),1);
@ -441,7 +442,7 @@ Class Enforcer : UTWeapon
TNT1 A 1
{
if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) A_Overlay(PSP_WEAPON,"Reload");
else if ( CVar.GetCVar('flak_enforcerreload').GetBool() && ((invoker.clipcount < min(20,invoker.Ammo1.Amount)) || (invoker.slaveclipcount < min(20,invoker.Ammo1.Amount))) ) A_WeaponReady(WRF_ALLOWRELOAD);
else if ( flak_enforcerreload && ((invoker.clipcount < min(20,invoker.Ammo1.Amount)) || (invoker.slaveclipcount < min(20,invoker.Ammo1.Amount))) ) A_WeaponReady(WRF_ALLOWRELOAD);
else A_WeaponReady();
if ( !invoker.slaveactive && (CountInv("Enforcer") > 1) ) A_Overlay(2,"LeftReady");
}
@ -454,7 +455,7 @@ Class Enforcer : UTWeapon
invoker.slaveactive = false;
A_Overlay(2,"LeftDeselect");
}
else if ( CVar.GetCVar('flak_enforcerreload').GetBool() && (invoker.slavereload || (invoker.slaveclipcount < 0)) ) A_Overlay(2,"LeftReload");
else if ( flak_enforcerreload && (invoker.slavereload || (invoker.slaveclipcount < 0)) ) A_Overlay(2,"LeftReload");
else if ( invoker.slavedown ) A_Overlay(2,"LeftDeselect");
else A_LeftWeaponReady();
}

View file

@ -174,6 +174,7 @@ Class FlakChunk : Actor
-BOUNCEAUTOOFF;
+CANBOUNCEWATER;
+SKYEXPLODE;
+INTERPOLATEANGLES;
Scale 0.3;
}
override void PostBeginPlay()
@ -224,9 +225,9 @@ Class FlakChunk : Actor
}
if ( trail ) trail.alpha = max(0,11-frame)/11.;
if ( InStateSequence(CurState,FindState("Death")) ) return;
A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
A_SetAngle(angle+pitchvel,SPF_INTERPOLATE);
roll += rollvel;
pitch += pitchvel;
angle += pitchvel;
}
action void A_HandleBounce()
{

View file

@ -42,6 +42,7 @@ Class MinigunTracer : Actor
+NOCLIP;
+NOGRAVITY;
+DONTSPLASH;
+INTERPOLATEANGLES;
}
override void Tick()
{
@ -55,9 +56,9 @@ Class MinigunTracer : Actor
}
dir = dir.unit();
SetOrigin(Vec3Offset(dir.x*160,dir.y*160,dir.z*160),true);
A_SetAngle(atan2(dir.y,dir.x),SPF_INTERPOLATE);
A_SetPitch(asin(-dir.z),SPF_INTERPOLATE);
A_SetRoll(roll+60,SPF_INTERPOLATE);
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
roll += 60;
}
States
{

View file

@ -268,8 +268,7 @@ Class UTInvisibilityX : Actor
Destroy();
return;
}
SetOrigin(target.pos,true);
A_SetAngle(target.angle,SPF_INTERPOLATE);
Warp(target,flags:WARPF_COPYINTERPOLATION|WARPF_NOCHECKPOSITION);
bInvisible = target.bInvisible;
}
States

View file

@ -538,7 +538,7 @@ Class PulseGun : UTWeapon
override void PostRender( double lbottom )
{
if ( !CVar.GetCVar('flak_pulsereload').GetBool() ) return;
if ( !flak_pulsereload ) return;
Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,lbottom-Screen.GetHeight()*0.01-confont.GetHeight(),String.Format("Clip: %2d / 50",clipcount));
}
@ -556,7 +556,7 @@ Class PulseGun : UTWeapon
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
invoker.clipcount--;
if ( !CVar.GetCVar('flak_pulsereload').GetBool() && (invoker.clipcount <= 0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(50,weap.Ammo1.Amount):50;
if ( !flak_pulsereload && (invoker.clipcount <= 0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(50,weap.Ammo1.Amount):50;
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
UTMainHandler.DoSwing(self,(FRandom[Pulse](-1,-1),FRandom[Pulse](-1,1)),0.1,-0.02,3,SWING_Spring,0,2);
@ -617,7 +617,7 @@ Class PulseGun : UTWeapon
if ( weap.Ammo1.Amount <= 0 ) return;
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
invoker.clipcount--;
if ( !CVar.GetCVar('flak_pulsereload').GetBool() && (invoker.clipcount <=0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(50,weap.Ammo1.Amount):50;
if ( !flak_pulsereload && (invoker.clipcount <=0) ) invoker.clipcount = (weap.Ammo1.Amount>0)?Min(50,weap.Ammo1.Amount):50;
invoker.FireEffect();
UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
UTMainHandler.DoSwing(self,(FRandom[Pulse](-1,-1),FRandom[Pulse](-1,1)),0.3,-0.1,2,SWING_Spring,0,3);
@ -715,7 +715,7 @@ Class PulseGun : UTWeapon
PGNI A 1
{
A_CheckReload();
if ( CVar.GetCVar('flak_pulsereload').GetBool() )
if ( flak_pulsereload )
{
if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) return A_Jump(255,"Reload");
A_WeaponReady(WRF_ALLOWRELOAD);

View file

@ -38,9 +38,8 @@ Class Razor2Trail : Actor
return;
}
SetOrigin(target.pos,true);
A_SetAngle(target.angle,SPF_INTERPOLATE);
A_SetPitch(target.pitch,SPF_INTERPOLATE);
A_SetRoll(target.roll,SPF_INTERPOLATE);
angle = target.angle;
pitch = target.pitch;
alpha = target.vel.length()/target.speed;
}
States

View file

@ -625,7 +625,7 @@ Class SuperShockBeam : Actor
action void A_BeamExplode()
{
Spawn("SuperShockBeamLight",pos);
if ( !CVar.GetCVar('flak_classicsshock').GetBool() )
if ( !flak_classicsshock )
{
UTMainHandler.DoBlast(self,50,60000);
A_Explode(Random[ASMD](500,800),50);
@ -1297,7 +1297,7 @@ Class EnhancedShockRifle : UTWeapon
ASMF BCDEFGHIJJ 2;
Goto Idle;
AltFire:
ASMI A 0 A_JumpIf(CVar.GetCVar('flak_classicsshock').GetBool(),"Fire");
ASMI A 0 A_JumpIf(flak_classicsshock,"Fire");
ASMI A 0 A_JumpIfNoAmmo("DryFire");
ASMA A 1 A_SShockAlt();
ASMA BCDFGHIJ 2;

View file

@ -3,7 +3,7 @@ Class UTPlayer : DoomPlayer
bool lastground;
int lastgroundtic;
double lastvelz, prevvelz;
transient CVar footsteps, utmovement, doomspeed, doomaircontrol, nowalkdrop;
transient CVar footsteps;
Vector2 acceleration;
Vector3 acceleration3;
int last_fm, last_sm;
@ -183,7 +183,7 @@ Class UTPlayer : DoomPlayer
override void Tick()
{
Super.Tick();
if ( !player ) return;
if ( !player || (player.mo != self) ) return;
if ( !footsteps ) footsteps = CVar.GetCVar('flak_footsteps',players[consoleplayer]);
if ( !footsteps.GetBool() ) return;
double ang = level.time/(20*TICRATE/35.)*360.;
@ -220,16 +220,12 @@ Class UTPlayer : DoomPlayer
override void MovePlayer()
{
if ( !utmovement ) utmovement = CVar.GetCVar('flak_utmovement');
if ( !doomspeed ) doomspeed = CVar.GetCVar('flak_doomspeed');
if ( !doomaircontrol ) doomaircontrol = CVar.GetCVar('flak_doomaircontrol');
if ( !nowalkdrop ) nowalkdrop = CVar.GetCVar('flak_nowalkdrop');
bNODROPOFF = false;
if ( !utmovement.GetBool() )
if ( !flak_utmovement || !player || (player.mo != self) )
{
Super.MovePlayer();
return;
}
bNODROPOFF = false;
UserCmd cmd = player.cmd;
if ( player.turnticks )
{
@ -239,10 +235,14 @@ Class UTPlayer : DoomPlayer
else Angle += cmd.yaw*(360./65536.);
player.onground = (pos.z <= floorz) || bOnMobj || bMBFBouncer || (player.cheats & CF_NOCLIP2);
if ( player.onground ) lastgroundtic = gametic;
if ( (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) ) player.onground = true;
if ( !player.onground && (abs(pos.z-floorz) <= maxdropoffheight) && (player.jumptics == 0) )
{
SetOrigin(Vec2OffsetZ(0,0,floorz),true);
player.onground = true;
}
double friction = FrictionToUnreal();
double fs = TweakSpeeds(1.0,0.0);
if ( !doomspeed.GetBool() )
if ( !flak_doomspeed )
{
if ( cmd.buttons&BT_SPEED ) fs *= walkfactor;
}
@ -281,7 +281,7 @@ Class UTPlayer : DoomPlayer
{
if ( !waterlevel && (dodge.length() > 0) )
{
if ( doomspeed.GetBool() ) vel += dodge.unit()*(groundspeed_doomish*1.5)/TICRATE;
if ( flak_doomspeed ) vel += dodge.unit()*(groundspeed_doomish*1.5)/TICRATE;
else vel += dodge.unit()*(groundspeed*1.5)/TICRATE;
vel.z += dodgez/TICRATE;
bOnMobj = false;
@ -297,7 +297,7 @@ Class UTPlayer : DoomPlayer
}
else
{
if ( nowalkdrop.GetBool() )
if ( flak_nowalkdrop )
bNODROPOFF = ((acceleration.length() > double.epsilon) && (cmd.buttons&BT_SPEED));
// Hook in Unreal physics
Vector2 dir = (0,0);
@ -317,7 +317,7 @@ Class UTPlayer : DoomPlayer
}
vel.xy = vel.xy + acceleration/TICRATE;
double maxvel;
if ( doomspeed.GetBool() ) maxvel = groundspeed_doomish/TICRATE;
if ( flak_doomspeed ) maxvel = groundspeed_doomish/TICRATE;
else maxvel = groundspeed/TICRATE;
maxvel *= fs*doomfriction;
if ( vel.xy.length() > maxvel ) vel.xy = vel.xy.unit()*maxvel;
@ -344,9 +344,9 @@ Class UTPlayer : DoomPlayer
Vector2 acceldir = acceleration.unit();
acceleration = acceldir * Min(acceleration.length(), accelrate/TICRATE);
}
acceleration *= doomaircontrol.GetBool()?level.aircontrol:utaircontrol;
acceleration *= flak_doomaircontrol?level.aircontrol:utaircontrol;
double maxvel;
if ( doomspeed.GetBool() ) maxvel = (groundspeed_doomish*fs)/TICRATE;
if ( flak_doomspeed ) maxvel = (groundspeed_doomish*fs)/TICRATE;
else maxvel = (groundspeed*fs)/TICRATE;
// if new velocity is higher than ground speed, steer but don't increase it
if ( (vel.xy+acceleration/TICRATE).length() > maxvel )
@ -403,9 +403,7 @@ Class UTPlayer : DoomPlayer
}
override void CheckJump()
{
if ( !utmovement ) utmovement = CVar.GetCVar('flak_utmovement');
if ( !doomspeed ) doomspeed = CVar.GetCVar('flak_doomspeed');
if ( !utmovement.GetBool() )
if ( !flak_utmovement || !player || (player.mo != self) )
{
Super.CheckJump();
return;
@ -418,7 +416,7 @@ Class UTPlayer : DoomPlayer
else if ( level.IsJumpingAllowed() && player.onground && (player.jumpTics == 0) && (last_jump_held < gametic-1) )
{
double jumpvelz;
if ( doomspeed.GetBool() ) jumpvelz = jumpz;
if ( flak_doomspeed ) jumpvelz = jumpz;
else jumpvelz = utjumpz/TICRATE;
double jumpfac = 0;
for ( let p = Inv; p != null; p = p.Inv )
@ -772,6 +770,7 @@ Class UTChip : Actor
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+INTERPOLATEANGLES;
BounceType "Doom";
BounceFactor 0.3;
Gravity 0.7;
@ -803,9 +802,9 @@ Class UTChip : Actor
Spawn:
CHIP # 1
{
A_SetAngle(angle+anglevel,SPF_INTERPOLATE);
A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
}
Loop;
Bounce:
@ -1184,8 +1183,8 @@ Class Swinger : Thinker
switch ( cstate )
{
case STATE_Initial:
target.A_SetAngle(target.angle+dir.x*str,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch+dir.y*str,SPF_INTERPOLATE);
target.A_SetAngle(target.angle+dir.x*str*flak_swingerstrength,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch+dir.y*str*flak_swingerstrength,SPF_INTERPOLATE);
str += inc;
if ( ++cnt >= steps )
{
@ -1203,8 +1202,8 @@ Class Swinger : Thinker
}
break;
case STATE_Return:
target.A_SetAngle(target.angle-dir.x*str/rmul,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch-dir.y*str/rmul,SPF_INTERPOLATE);
target.A_SetAngle(target.angle-dir.x*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
target.A_SetPitch(target.pitch-dir.y*(str/rmul)*flak_swingerstrength,SPF_INTERPOLATE);
if ( ++cnt >= steps*rmul )
{
cnt = 0;
@ -1257,7 +1256,6 @@ Class UTMainHandler : StaticEventHandler
{
ui TextureID tex;
Array<QueuedFlash> flashes;
transient CVar nobosstelefrag;
override void CheckReplacement( ReplaceEvent e )
{
@ -1487,8 +1485,7 @@ Class UTMainHandler : StaticEventHandler
override void WorldThingSpawned( WorldEvent e )
{
if ( !nobosstelefrag ) nobosstelefrag = CVar.GetCVar('flak_nobosstelefrag');
if ( nobosstelefrag.GetBool() && e.Thing.bBOSS ) e.Thing.bNOTELEFRAG = true;
if ( flak_nobosstelefrag && e.Thing.bBOSS ) e.Thing.bNOTELEFRAG = true;
}
ui void StartMenu()
@ -1513,12 +1510,12 @@ Class UTMainHandler : StaticEventHandler
override void PlayerEntered( PlayerEvent e )
{
if ( CVar.GetCVar('flak_translocator').GetBool() )
if ( flak_translocator )
players[e.playernumber].mo.GiveInventory("Translocator",1);
}
override void PlayerRespawned( PlayerEvent e )
{
if ( CVar.GetCVar('flak_translocator').GetBool() )
if ( flak_translocator )
players[e.playernumber].mo.GiveInventory("Translocator",1);
}
@ -1526,7 +1523,7 @@ Class UTMainHandler : StaticEventHandler
{
if ( e.Name ~== "refreshtrans" )
{
if ( CVar.GetCVar('flak_translocator').GetBool() )
if ( flak_translocator )
{
for ( int i=0; i<MAXPLAYERS; i++ ) if ( playeringame[i] ) players[i].mo.GiveInventory("Translocator",1);
}
@ -1577,7 +1574,7 @@ Class UTMainHandler : StaticEventHandler
{
if ( e.Thing.bDONTGIB ) return;
// attach damage accumulator for corpses
if ( !CVar.GetCVar('flak_corpsedamage').GetBool() ) return;
if ( !flak_corpsedamage ) return;
let a = Actor.Spawn("ShredCorpseHitbox",e.Thing.pos);
a.target = e.Thing;
}
@ -1616,6 +1613,7 @@ Class UTMainHandler : StaticEventHandler
static void DoSwing( Actor target, Vector2 dir, double initial, double inc, int steps, int mode = 0, int delay = 0, double rmul = 1.0 )
{
if ( !flak_swingers ) return;
let s = new("Swinger");
s.ChangeStatNum(Thinker.STAT_USER);
s.target = target;

View file

@ -254,6 +254,7 @@ Class WarShell : Actor
+SKYEXPLODE;
+FORCERADIUSDMG;
+EXPLODEONWATER;
+INTERPOLATEANGLES;
}
override void PostBeginPlay()
{
@ -402,9 +403,9 @@ Class GuidedWarShell : WarShell
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);
angle = nangle;
pitch = npitch;
roll = nroll;
vel = (vel+(cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*0.8).unit()*11;
}
lagangle2 = lagangle2*0.95+lagangle*0.05;