-- LUA Script - precede every function and global member with lowercase name of script + '_main' local Q = require "scriptbank\\quatlib" local U = require "scriptbank\\utillib" local P = require "scriptbank\\physlib" local min = math.min local max = math.max local rad = math.rad local deg = math.deg local atan = math.atan2 local modf = math.modf local abs = math.abs local rand = math.random local sqrt = math.sqrt local function vCreate( x, y, z ) return { x = x, y = y, z = z } end local function vAdd( v1, v2 ) return { x = v1.x + v1.x, y = v1.y + v2.y, z = v1.z + v2.z } end local function vMul( v, mul ) return { x = v.x * mul, y = v.y * mul, z = v.z * mul } end local function vMag( v ) return sqrt( v.x * v.x + v.y * v.y + v.z * v.z ) end local function vRot( v, xA, yA, zA ) local x, y, z = U.Rotate3D( v.x, v.y, v.z, xA, yA, zA ) return vCreate( x, y, z ) end local function promptVec( v ) Prompt( modf( v.x * 100 ) / 100 .. ", " .. modf( v.y * 100 ) / 100 .. ", " .. modf( v.y * 100 ) / 100 ) end local namedEmitter = { cannon = { animSpd = 0.25, SR = 0, freq = -1, lifeMin = 2000, lifeMax = 2000, -- minX minY minZ maxX maxY maxZ offset = { 0, 0, 0, 0, 0, 0 }, speed = { 0, 0, 0, 0, 0, 0 }, rotate = { 0, 0 }, -- currently only Z rotate processed -- startMin startMax endMin endMax scale = { 20, 20, 40, 40 }, alpha = { 100, 100, 100, 100 } }, machinegun = { animSpd = 0.25, SR = 0, freq = -1, lifeMin = 2000, lifeMax = 2000, -- minX minY minZ maxX maxY maxZ offset = { 0, 0, 0, 0, 0, 0 }, speed = { 0, 0, 0, 0, 0, 0 }, rotate = { 0, 0 }, -- currently only Z rotate processed -- startMin startMax endMin endMax scale = { 10, 10, 20, 20 }, alpha = { 100, 100, 80, 80 } }, muzzleFlash = { animSpd = 4, SR = 0, freq = -1, lifeMin = 250, lifeMax = 250, -- minX minY minZ maxX maxY maxZ offset = { 0, 0, 0, 0, 0, 0 }, speed = { 0, 0, 0, 0, 0, 0 }, rotate = { 0, 0 }, -- currently only Z rotate processed -- startMin startMax endMin endMax scale = { 20, 20, 30, 30 }, alpha = { 100, 100, 60, 60 } }, bulletPuff = { animSpd = 0.5, SR = 0, freq = -1, lifeMin = 500, lifeMax = 500, -- minX minY minZ maxX maxY maxZ offset = { 0, 0, 0, 0, 0, 0 }, speed = { 0, 0, 0, 0, 0, 0 }, rotate = { 0, 0 }, -- currently only Z rotate processed -- startMin startMax endMin endMax scale = { 100, 200, 100, 200 }, alpha = { 100, 100, 100, 100 } } } -- caller must load image local function PE_CreateNamedEmitter( name, image, frames, object ) image = image or 1 -- default to smoke frames = frames or 64 local et = namedEmitter[ name ] if et == nil then return end local emitter = ParticlesGetFreeEmitter() if emitter == -1 then return end local pos, spd, scl = et.offset, et.speed, et.scale if object == nil then ParticlesAddEmitterEx( emitter, et.animSpd, et.SR, pos[1], pos[2], pos[3], pos[4], pos[5], pos[6], scl[1], scl[2], scl[3], scl[4], spd[1], spd[2], spd[3], spd[4], spd[5], spd[6], et.rotate[1], et.rotate[2], et.lifeMin, et.lifeMax, et.alpha[1], et.alpha[2], et.alpha[3], et.alpha[4], et.freq, 0, 0, image, frames ) else ParticlesAddEmitterEx( emitter, et.animSpd, et.SR, pos[1], pos[2], pos[3], pos[4], pos[5], pos[6], scl[1], scl[2], scl[3], scl[4], spd[1], spd[2], spd[3], spd[4], spd[5], spd[6], et.rotate[1], et.rotate[2], et.lifeMin, et.lifeMax, et.alpha[1], et.alpha[2], et.alpha[3], et.alpha[4], et.freq, object, 0, image, frames ) end return emitter end local function coinFlip() return rand() > 0.5 end --local mouse_spr = mouse_spr or CreateSprite( LoadImage( "scriptbank\\images\\mouse_cursor.png" )) --SetSpritePosition( mouse_spr, 200, 200 ) local spitfire = {} local controlEnt = nil local timeLastFrame = nil local timeDiff = 0 local propellerPos = { x = 0, y = 0.5, z = 124.0 } -- relative to fuselage local elevPos = { x = 0, y = 1.9, z = -289.8 } -- relative to fuselage local rudderPos = { x = 0, y = 23.9, z = -290.6 } -- relative to fuselage local wingPos = { x = 0, y = -26, z = 16 } -- relative to fuselage local rAileronPos = { x = 168.2, y = 15.0, z = -50.0 } -- relative to wingPos local lAileronPos = { x = -170.3, y = 15.0, z = -49.5 } -- relative to wingPos local rWheelPos = { x = 34, y = -5, z = 0 } -- relative to wingPos local lWheelPos = { x = -34, y = -5, z = 0 } -- relative to wingPos local tailWheelPos = { x = 0, y = -29.5, z = -297 } -- relative to fuselage -- cockpit stuff local stickPos = { x = 0, y = -8.5, z = 28.5 } -- relative to fuselage local IndicatorPositions = -- relative to fuselage { knotI = { x = 10.05, y = 18.2, z = 34.5 }, turnIt = { x = -4.64, y = 20.22, z = 33.3 }, turnIb = { x = -4.64, y = 19.72, z = 33.3 }, rpmI = { x = 4.95, y = 15.5, z = 33.3 }, noseI = { x = -0.95, y = 20.0, z = 33.4 }, climbI = { x = 13.05, y = 18.15, z = 34.5 }, altIl = { x = -4.64, y = 15.52, z = 33.3 }, altIs = { x = -4.64, y = 15.52, z = 33.4 }, dirI = { x = 0.14, y = 15.73, z = 33.5 }, arthI = { x = 5.00, y = 19.92, z = 33.7 } } local spitfireParts = { spitprop = { fld = 'prop' }, spitelev = { fld = 'elev' }, leftwing = { fld = 'lwing' }, rightwing = { fld = 'rwing' }, aileronleft = { fld = 'aleft' }, aileronright = { fld = 'aright' }, spitrudder = { fld = 'rudder' }, spitleftw = { fld = 'leftw' }, spitrightw = { fld = 'rightw' }, spitjs = { fld = 'stick' }, knots01 = { fld = 'knotI' }, turn01t = { fld = 'turnIt' }, turn01b = { fld = 'turnIb' }, rpm01 = { fld = 'rpmI' }, nose01 = { fld = 'noseI' }, climb01 = { fld = 'climbI' }, altlg01 = { fld = 'altIl' }, altsm01 = { fld = 'altIs' }, dirind01 = { fld = 'dirI' }, arthoez01 = { fld = 'arthI' } } local function getPart( name ) for k, v in pairs( spitfire ) do if v.name == name and not v.used then v.used = true return k end end end local function gotParts( e, h ) for k, v in pairs( spitfireParts ) do if h[ v.fld ] == nil then h[ v.fld ] = getPart( k ) if h[ v.fld ] == nil then PromptLocal( e, "Missing " .. k ) return false end end end return true end local samplesLoaded = false function spitfire_init_name( e, name ) Include( "utillib.lua" ) Include( "quatlib.lua" ) Include( "physlib.lua" ) spitfire[ e ] = { name = name, state = 'init' } --SetSpriteSize( mouse_spr, 2.5 ) --SetSpriteOffset( mouse_spr, 1.25, 2.1 ) --SetSpritePosition( mouse_spr, 200, 200 ) if not samplesLoaded then LoadGlobalSound( "audiobank\\Spitfire\\Gun_20MM_Left.wav", 0 ) LoadGlobalSound( "audiobank\\Spitfire\\Gun_20MM_Right.wav", 1 ) LoadGlobalSound( "audiobank\\Spitfire\\Gun_Machine_Left.wav", 2 ) LoadGlobalSound( "audiobank\\Spitfire\\Gun_Machine_Right.wav", 3 ) LoadGlobalSound( "audiobank\\Spitfire\\Airplane_Prop_Start.wav", 4 ) LoadGlobalSound( "audiobank\\Spitfire\\Airplane_Prop_Stop.wav", 5 ) LoadGlobalSound( "audiobank\\Spitfire\\Airplane_Prop_Run1.wav", 6 ) samplesLoaded = true end end local maxSpeed = 40 local playerPos1 = { xo = 0, yo = 25, zo = 0, ang = 0 } local playerPos2 = { xo = 0, yo = 100, zo = -500, ang = 10 } local function AttachPlayer( h, xa, ya, za ) local xo, yo, zo = U.Rotate3D( h.pp.xo, h.pp.yo, h.pp.zo, xa, ya, za ) cx, cy, cz = h.pos.x + xo, h.pos.y + yo, h.pos.z + zo SetCameraPosition ( 0, cx, cy, cz ) SetCameraAngle ( 0, deg( xa ) + h.pp.ang, deg( ya ), deg( za ) ) SetFreezePosition( h.pos.x, h.pos.y + 80, h.pos.z - 80 ) TransportToFreezePositionOnly() end local function posObject( ent, obj, x, y, z, xA, yA, zA ) CollisionOff ( ent ) PositionObject( obj, x, y, z ) RotateObject ( obj, deg( xA ), deg( yA ), deg( zA ) ) CollisionOn ( ent ) end local function positionLAileron( h, x, y, z, xA, yA, zA ) local ov = vRot( lAileronPos, xA, yA, zA ) local obj = spitfire[ h.aleft ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( h.rollReq / 2, rad( 5.5 ), rad( -4.4 ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h.aleft, obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local function positionRAileron( h, x, y, z, xA, yA, zA ) local ov = vRot( rAileronPos, xA, yA, zA ) local obj = spitfire[ h.aright ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( -h.rollReq / 2, rad( -5.5 ), rad( 4.4 ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h.aright, obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local function positionLWheel( h, x, y, z, xA, yA, zA ) local ov = vCreate( lWheelPos.x, lWheelPos.y, lWheelPos.z ) local ovr = vRot( ov, xA, yA, zA ) local obj = spitfire[ h.leftw ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( 0, 0, rad( -h.wAng ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h.leftw, obj, x + ovr.x, y + ovr.y, z + ovr.z, rxA, ryA, rzA ) ov.y = ov.y - 36 ov = vRot( ov, xA, yA, zA ) h.vlw = { x = x + ov.x, y = y + ov.y, z = z + ov.z } end local function positionRWheel( h, x, y, z, xA, yA, zA ) local ov = vCreate( rWheelPos.x, lWheelPos.y, lWheelPos.z ) local ovr = vRot( ov, xA, yA, zA ) local obj = spitfire[ h.rightw ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( 0, 0, rad( h.wAng ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h.rightw, obj, x + ovr.x, y + ovr.y, z + ovr.z, rxA, ryA, rzA ) ov.y = ov.y - 36 ov = vRot( ov, xA, yA, zA ) h.vrw = { x = x + ov.x, y = y + ov.y, z = z + ov.z } end local function positionWings( h, x, y, z, xA, yA, zA ) local ov = vRot( wingPos, xA, yA, zA ) local wx, wy, wz = x + ov.x, y + ov.y, z + ov.z local w = spitfire[ h.lwing ] posObject( h.lwing, w.obj, wx, wy, wz, xA, yA, zA ) w = spitfire[ h.rwing ] posObject( h.rwing, w.obj, wx, wy, wz, xA, yA, zA ) positionLAileron( h, wx, wy, wz, xA, yA, zA ) positionRAileron( h, wx, wy, wz, xA, yA, zA ) positionLWheel( h, wx, wy, wz, xA, yA, zA ) positionRWheel( h, wx, wy, wz, xA, yA, zA ) end local function positionProp( h, x, y, z, xA, yA, zA ) local ov = vRot( propellerPos, xA, yA, zA ) local obj = spitfire[ h.prop ].obj local rotQ = Q.Mul( h.quat, Q.FromEuler( 0, 0, rad( -h.pAng ) ) ) local rxA, ryA, rzA = Q.ToEuler( rotQ ) posObject( h.prop, obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local function positionElev( h, x, y, z, xA, yA, zA ) local ov = vRot( elevPos, xA, yA, zA ) local obj = spitfire[ h.elev ].obj local pQ = Q.Mul( h.quat, Q.FromEuler( -h.pitchReq / 2 , 0, 0 ) ) local pxA, pyA, pzA = Q.ToEuler( pQ ) posObject( h.elev, obj, x + ov.x, y + ov.y, z + ov.z, pxA, pyA, pzA ) end local function positionStick ( h, x, y, z, xA, yA, zA ) local ov = vRot( stickPos, xA, yA, zA ) local obj = spitfire[ h.stick ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( rad( h.pitchReq * 10 ), 0, rad( h.rollReq * 20 ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h.stick, obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local function positionRudder( h, x, y, z, xA, yA, zA ) local ov = vRot( rudderPos, xA, yA, zA ) local obj = spitfire[ h.rudder ].obj local rotQ = Q.Mul( h.quat, Q.FromEuler( 0, h.yaw / 3, 0 ) ) local rxA, ryA, rzA = Q.ToEuler( rotQ ) posObject( h.rudder, obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local function PositionIndicator( h, name, valueZ, valueY, valueX, x, y, z, xA, yA, zA ) local ov = vRot( IndicatorPositions[ name ], xA, yA, zA ) local obj = spitfire[ h[ name ] ].obj local rQ = Q.Mul( h.quat, Q.FromEuler( rad( valueX ), rad( valueY ), rad( valueZ ) ) ) local rxA, ryA, rzA = Q.ToEuler( rQ ) posObject( h[ name ], obj, x + ov.x, y + ov.y, z + ov.z, rxA, ryA, rzA ) end local LmouseClicked = false local RmouseClicked = false local peCannon = nil local peMachGun = nil local peMuzzleF = nil local peBulletP = nil local pe_image = pe_image or ParticlesLoadImage( "effectbank\\particles\\flare.dds", 10 ) local mf_image = mf_image or ParticlesLoadImage( "effectbank\\particles\\TEST.dds" , 11 ) local bp_image = bp_image or ParticlesLoadImage( "effectbank\\particles\\dust_impact.dds", 12 ) local bulletVel = 8 local cannonVel = 7 local Cbarrel1 = { x = -95, y = -20, z = 58 } local Cbarrel2 = { x = 95, y = -20, z = 58 } local Mbarrel1 = { x = -131, y = -18, z = 45 } local Mbarrel2 = { x = 131, y = -18, z = 45 } local CtoeInL = Q.FromEuler( rad( -3.0 ), rad( 0.5 ), 0 ) local CtoeInR = Q.FromEuler( rad( -3.0 ), rad( -0.5 ), 0 ) local MtoeInL = Q.FromEuler( rad( -3.0 ), rad( 0.8 ), 0 ) local MtoeInR = Q.FromEuler( rad( -3.0 ), rad( -0.8 ), 0 ) local function AddTarget( h, x, y, z, dist, vel, obj ) if #h.tgtList == 0 then h.tgtList[ 1 ] = { x = x, y = y, z = z, timer = h.timeNow + dist / ( vel ), obj = obj } return else for k, v in pairs( h.tgtList ) do if v == nil then h.tgtList[ k ] = { x = x, y = y, z = z, timer = h.timeNow + dist / vel, obj = obj } return end end end h.tgtList[ #h.tgtList + 1 ] = { x = x, y = y, z = z, timer = h.timeNow + dist / vel, obj = obj } end local function ProcessTargets( h ) for k, v in pairs( h.tgtList ) do if h.timeNow > v.timer then ParticlesSpawnParticle( peBulletP, v.x, v.y, v.z ) if v.obj ~= nil then SetEntityHealth( P.ObjectToEntity( v.obj ), 0 ) end h.tgtList[ k ] = nil end end end local function getRandQ( ang ) local rndP = rand(-1, 1) * rand() * ang local rndY = rand(-1, 1) * rand() * ang return Q.FromEuler( rad( rndP ), rad( rndY ), 0 ) end local function ShootWeapon( wTyp, h, toeIn, barrelPos, velocity, numExtraShots, xA, yA, zA ) local bax, bay, baz = Q.ToEuler( Q.Mul( Q.Mul( h.quat, toeIn ), getRandQ( 0.5 ) ) ) local bvX, bvY, bvZ = U.Rotate3D( 0, 0, velocity, bax, bay, baz ) ParticlesSetSpeed( wTyp, bvX, bvY, bvZ, bvX, bvY, bvZ ) local xo, yo, zo = U.Rotate3D ( barrelPos.x, barrelPos.y, barrelPos.z, xA, yA, zA ) local bx, by, bz = h.pos.x + xo, h.pos.y + yo, h.pos.z + zo ParticlesSpawnParticle( wTyp, bx, by, bz ) for i = 1, numExtraShots do off = i * 2 ParticlesSpawnParticle( wTyp, bx + bvX * off, by + bvY * off, bz + bvZ * off ) end local mv = vMul( h.vec, 0.05 ) ParticlesSetSpeed( peMuzzleF, mv.x, mv.y, mv.z, mv.x, mv.y, mv.z ) ParticlesSpawnParticle( peMuzzleF, bx, by, bz ) rayx, rayy, rayz = U.Rotate3D( 0, 0, 7000, bax, bay, baz ) local obj = IntersectAll( bx, by, bz, bx + rayx, by + rayy, bz + rayz , 0 ) if obj and obj ~= 0 then local x, y, z = GetIntersectCollisionX(), GetIntersectCollisionY(), GetIntersectCollisionZ() local tgtv = vCreate( x - bx, y - by, z - bz ) AddTarget( h, x, y, z, vMag( tgtv ), velocity, obj ) elseif RayTerrain( bx, by, bz, bx + rayx, by + rayy, bz + rayz ) == 1 then local x, y, z = GetRayCollisionX(), GetRayCollisionY(), GetRayCollisionZ() local tgtv = vCreate( x - bx, y - by, z - bz ) AddTarget( h, x, y, z, vMag( tgtv ), velocity ) end end local function getRealPitchAngle( xa, ya, za ) -- Euler radians -- first rotate a unit 'facing forward' vector by the Euler angles local xv, yv, zv = U.Rotate3D( 0, 0, 1, xa, ya, za ) -- now work out the angle from the y and z components of the result local ang = atan( yv, abs( zv ) ) if abs( zv ) > abs( xv ) then return ang, atan( xv, zv ) else return atan( yv, abs( xv ) ), atan( xv, zv ) end end local function getRealRollAngle( xa, ya, za, rya ) -- Euler radians, rya degrees -- first rotate a unit 'up facing' vector by the Euler angles local xv, yv, zv = U.Rotate3D( 0, 1, 0, xa, ya, za ) -- now work out the angle from the components of the result if rya > 45 and rya < 135 then return atan( -zv, abs( yv ) ) elseif rya <= 45 or rya >= 315 then return atan( xv, abs( yv ) ) elseif rya >= 135 and rya <= 225 then return atan( -xv, abs( yv ) ) else return atan( zv, abs( yv ) ) end end local function PositionPlane( h, xA, yA, zA, pp ) h.pos.x, h.pos.y, h.pos.z = h.pos.x + h.vec.x, h.pos.y + h.vec.y, h.pos.z + h.vec.z posObject( h.ent, h.obj, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) positionWings( h, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) positionProp( h, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) positionElev ( h, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) positionRudder( h, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) positionStick( h, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) local a1 = h.height / 1000 local a2 = ( h.height - modf( a1 ) * 1000 ) / 100 PositionIndicator( h, 'altIl' , -a2 * 36, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) PositionIndicator( h, 'altIs' , -a1 * 36, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) PositionIndicator( h, 'knotI' , -h.airSpd * 10, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) PositionIndicator( h, 'rpmI' , -h.revs * 8, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) PositionIndicator( h, 'climbI', -h.vec.y * 8, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) local rxa, rya = getRealPitchAngle( xA, yA, zA ) if rxa > 0 then PositionIndicator( h, 'noseI' , min( 42, deg( rxa ) ), 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) else PositionIndicator( h, 'noseI' , max( -42, deg( rxa ) ), 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) end rya = deg( rya ) if rya < 0 then rya = 360 + rya end local hdgD = ( rya - h.rya ) * 60 if hdgD > 360 then hdgD = hdgD - 360 end if hdgD < 0 then local hg = max( hdgD, -50 ) PositionIndicator( h, 'turnIb' , 180 + hg, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) else local hg = min( hdgD, 50 ) PositionIndicator( h, 'turnIb' , 180 + hg, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) end h.rya = rya PositionIndicator( h, 'turnIt', -h.roll * 35, 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) PositionIndicator( h, 'dirI', 0, h.rya, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) local rza = getRealRollAngle( xA, yA, zA, rya ) PositionIndicator( h, 'arthI', deg( rza ), 0, 0, h.pos.x, h.pos.y, h.pos.z, xA, yA, zA ) if pp then AttachPlayer( h, xA, yA, zA ) if g_MouseClick == 2 then if not RmouseClicked then RmouseClicked = true if coinFlip() then -- barrel 1 shot ShootWeapon( peCannon, h, CtoeInL, Cbarrel1, cannonVel, 2, xA, yA, zA ) PlayGlobalSound( 0 ) end if coinFlip() then -- barrel 2 shot ShootWeapon( peCannon, h, CtoeInR, Cbarrel2, cannonVel, 2, xA, yA, zA ) PlayGlobalSound( 1 ) end end else RmouseClicked = false end if g_MouseClick == 1 then if h.timeNow < h.gTimer then return end h.gTimer = h.timeNow + 40 if coinFlip() then -- barrel 1 shot ShootWeapon( peMachGun, h, MtoeInL, Mbarrel1, bulletVel, 1, xA, yA, zA ) PlayGlobalSound( 2 ) end if coinFlip() then ShootWeapon( peMachGun, h, MtoeInR, Mbarrel2, bulletVel, 1, xA, yA, zA ) PlayGlobalSound( 3 ) end end end end local EPressed = false function spitfire_main( e ) local h = spitfire[ e ] if h == nil then return end if h.state == 'done' then -- parts don't need to do any further processing return elseif h.state == 'init' then local Ent = g_Entity[ e ] if h.name == 'spitfuse' then SetAnimationFrame( e, 1 ) local x, y, z, xa, ya, za = GetObjectPosAng( Ent.obj ) h.ent = e h.timer = math.huge h.etimer = math.huge h.estate = 'off' -- off, start, run, stop h.gTimer = 0 h.obj = Ent.obj h.pos = { x = x, y = y, z = z } h.quat = Q.FromEuler( rad( xa ), rad( ya ), rad( za ) ) h.rya = 0 h.vec = vCreate( 0, 0, 0 ) h.yaw = 0 h.pitch = 0 h.roll = 0 h.pitchReq = 0 h.rollReq = 0 h.wAng = 0 h.height = 0 h.revs = 0 h.pAng = 0 h.airSpd = 0 h.tgtList = {} h.wow = true h.pp = playerPos1 h.state = 'build' if peCannon == nil then peCannon = PE_CreateNamedEmitter( 'cannon', pe_image, 64 ) ParticlesLoadEffect( "effectbank\\reloaded\\decal_basic_additive.fx", peCannon ) end if peMachGun == nil then peMachGun = PE_CreateNamedEmitter( 'machinegun', pe_image, 64 ) end if peMuzzleF == nil then peMuzzleF = PE_CreateNamedEmitter( 'muzzleFlash', mf_image, 64 ) end if peBulletP == nil then peBulletP = PE_CreateNamedEmitter( 'bulletPuff', bp_image, 16 ) end else h.obj = Ent.obj h.used = false h.state = 'done' end return elseif h.state == 'build' then if gotParts( e, h ) then h.state = 'idle' SetAnimationFrame( e, 0 ) end return end h.timeNow = g_Time if controlEnt == nil then controlEnt = e end if controlEnt == e then if timeLastFrame == nil then timeLastFrame = h.timeNow timeDiff = 1 else timeDiff = ( h.timeNow - timeLastFrame ) / 20 timeLastFrame = h.timeNow end end if h.state == 'start' then --Prompt( modf( h.rya ) .. ", " .. modf( h.revs * 100 ) / 100 ) if h.inCab then if g_KeyPressE == 1 then if not EPressed and h.height < 10 and h.estate == 'off' then EPressed = true SetCameraOverride( 0 ) DeactivateMouse() --SetSpritePosition( mouse_spr, 200, 200 ) SetPlayerWeapons( 1 ) h.inCab = false h.yaw = 0 h.wUp = false StopSound( e, 0 ) PushObject( h.obj ) h.state = 'idle' return end else EPressed = false end -- Player camera control if g_KeyPressR == 1 then if not RPressed then RPressed = true if h.pp == playerPos1 then h.pp = playerPos2 else h.pp = playerPos1 end end else RPressed = false end -- Undercarriage controls if g_KeyPressQ == 1 then if h.wAng <= 0 and not h.wow then h.wUp = true elseif h.wAng >= 90 and not h.wow then h.wUp = false end else if h.wUp then if h.wAng < 90 then h.wAng = h.wAng + 0.5 * timeDiff if h.wAng > 89 then SetAnimationFrame( e, 1 ) end end else if h.wAng > 0 then h.wAng = h.wAng - 0.5 * timeDiff if h.wAng < 1 then SetAnimationFrame( e, 0 ) end end end end -- Throttle control if g_KeyPressW == 1 or h.estate == 'start' then if h.estate == 'off' then h.estate = 'start' PlayGlobalSound( 4 ) SetGlobalSoundSpeed( 4, 38000 ) h.etimer = h.timeNow + 6000 h.revs = 0.7 elseif h.estate == 'start' then if h.timeNow < h.etimer then if h.revs < 6 then h.revs = h.revs + 0.05 * timeDiff end else h.estate = 'run' LoopGlobalSound( 6 ) SetGlobalSoundSpeed( 6, 38000 ) SetGlobalSoundVolume( 6, 99 ) end elseif h.estate == 'run' then if h.revs < maxSpeed then h.revs = h.revs + 0.07 * timeDiff SetGlobalSoundSpeed( 6, 38000 + h.revs * 1000 ) end end elseif g_KeyPressS == 1 or h.estate == 'stop' then if h.estate == 'run' then if h.revs > 6 then h.revs = h.revs - 0.07 * timeDiff SetGlobalSoundSpeed( 6, 38000 + h.revs * 1000 ) else h.etimer = h.timeNow + 5000 StopGlobalSound( 6 ) SetGlobalSoundVolume( 6, 0 ) PlayGlobalSound( 5 ) SetGlobalSoundSpeed( 5, 38000 ) h.estate = 'stop' end elseif h.estate == 'stop' then if h.timeNow < h.etimer then if h.revs > 0 then h.revs = h.revs - 0.01 * timeDiff else h.revs = 0 end else h.estate = 'off' h.revs = 0 end end end -- Propeller rotation if h.pAng < 360 then h.pAng = h.pAng + h.revs else h.pAng = h.pAng - 360 + h.revs end local xA, yA, zA = Q.ToEuler( h.quat ) PositionPlane( h, xA, yA, zA, true ) -- Flight controls local omx, omy = ( g_MouseX - 50 ) / 50, ( g_MouseY - 50 ) / 50 if abs( omx ) < 0.01 then omx = 0 end if abs( omy ) < 0.01 then omy = 0 end h.pitchReq = -omy * 0.75 h.rollReq = -omx local twv = vRot( tailWheelPos, xA, yA, zA ) twv = vCreate( h.pos.x + twv.x, h.pos.y + twv.y, h.pos.z + twv.z ) local terrainTW = GetTerrainHeight( twv.x, twv.z ) local terrainLW = GetTerrainHeight( h.vlw.x, h.vlw.z ) local terrainRW = GetTerrainHeight( h.vrw.x, h.vrw.z ) if h.vrw.y > terrainRW and h.vlw.y > terrainLW then if twv.y > terrainTW then h.wow = false -- Prompt( "Airborne : " .. modf( h.height ) .. ", " .. modf( h.airSpd ) ) elseif twv.y < terrainTW then local twAmnt = terrainTW - twv.y h.pitch = h.pitch + rad( twAmnt / 10 ) h.pitchReq = h.pitch end else local rwAmnt = terrainRW - h.vrw.y local lwAmnt = terrainLW - h.vlw.y h.wow = true -- Prompt( "Landed : " .. modf( h.height ) .. ", " .. modf( h.airSpd ) ) end --if h.pos.y > terrain then -- h.vec.y = -1 --else -- h.vec.y = 0 --end -- h.height = h.height + h.vec.y if h.pitch < h.pitchReq then h.pitch = h.pitch + 0.02 elseif h.pitch > h.pitchReq then h.pitch = h.pitch - 0.02 end if h.roll < h.rollReq then h.roll = h.roll + 0.02 elseif h.roll > h.rollReq then h.roll = h.roll - 0.02 end -- Rudder control if g_KeyPressA == 1 then if h.yaw < 1 then h.yaw = h.yaw + 0.02 * timeDiff end elseif g_KeyPressD == 1 then if h.yaw > -1 then h.yaw = h.yaw - 0.02 * timeDiff end else if h.yaw ~= 0 then h.yaw = h.yaw * 0.999 * timeDiff end end -- Centre controls (i.e. Panic button ) -- could also use to return to level flight if g_KeyPressC == 1 then h.yaw = 0 PositionMouse( GetDeviceWidth() / 2, GetDeviceHeight() / 2 ) end --h.airSpd = h.airSpd + 0.5 --if h.airSpd >= 360 then h.airSpd = 0 end -- initialise lift vector h.lv = vCreate( 0, h.airSpd / 10, 0 ) -- to do : flight math -- LiftVector - { x = 0, y = n, z = 0 } rotated with spitfire -- amplitude related to airspeed. -- -- GravityVector { x = 0, y = -1, z = 0 } constant (i.e. no rotation) -- if h.wUp then h.dv = vCreate( 0, 0, -h.airSpd * 0.26 ) else h.dv = vCreate( 0, 0, -h.airSpd * 0.36 ) end -- DragVector { opposite of sum of vectors } -- amplitude related to difference between -- AirspeedVector and ThrustVector -- -- initialise thrust vector -- ThrustVector { x = 0, y = 0, z = n } rotated with spitfire -- amplitude related to engine speed -- if h.revs > 8 then h.tv = vCreate( 0, 0, h.revs / 2 ) else h.tv = vCreate( 0, 0, 0 ) end h.vec = vAdd( h.tv, h.lv ) h.vec = vAdd( h.vec, h.dv ) h.vec = vRot( h.vec, xA, yA, zA ) h.vec.y = h.vec.y - 2 -- Gravity --promptVec( h.vec ) -- initialise sideslip vector h.sv = vCreate( 0, 0, 0 ) -- SideSlipVector { x = n, y= 0, z = 0 } -- amplitude related to roll angle -- -- Airspeed = root sum of vectors -- -- GroundSpeed = root of sum of x & z portion of sum of vectors -- --Prompt( modf( h.airSpd ) ) -- Rotation of aircraft if not h.wow or h.airSpd > 0 then local pitchQ = Q.FromEuler( rad( h.pitch * timeDiff * h.airSpd / 20 ), 0, 0 ) local rollQ = Q.FromEuler( 0, 0, rad( h.roll * timeDiff * h.airSpd / 20 ) ) h.quat = Q.Mul( h.quat, pitchQ ) h.quat = Q.Mul( h.quat, rollQ ) end if h.yaw ~= 0 then h.quat = Q.Mul( h.quat, Q.FromEuler( 0, -rad( h.yaw ) * timeDiff, 0 ) ) end -- end --local terrain = GetTerrainHeight( h.pos.x, h.pos.z ) --if h.pos.y > terrain then -- h.vec.y = -1 --else -- h.vec.y = 0 --end -- h.height = h.height + h.vec.y if h.wow then if h.vec.y < 0 then h.vec.y = 0 h.height = 0 end else h.height = h.height + h.vec.y / 2 end h.airSpd = vMag( h.vec ) ProcessTargets( h ) --CheckForCollision( h.obj ) end elseif h.state == 'idle' then local xA, yA, zA = Q.ToEuler( h.quat ) PositionPlane( h, xA, yA, zA ) if U.PlayerLookingNear( e, 300, 180 ) then Prompt( "E to fly me" ) if g_KeyPressE == 1 then if not EPressed then PositionMouse( GetDeviceWidth() / 2, GetDeviceHeight() / 2 ) ActivateMouse() LoopNon3DSound( e, 0 ) EPressed = true SetPlayerWeapons( 0 ) SetCameraOverride( 3 ) h.inCab = true h.yaw = 0 h.state = 'start' return end else EPressed = false end end end end