-- LUA Script - precede every function and global member with lowercase name of script + '_main' local U = require "scriptbank\\utillib" local Q = require "scriptbank\\quatlib" local rad = math.rad local deg = math.deg local abs = math.abs local rnd = math.random local sqrt = math.sqrt local modf = math.modf math.randomseed(os.time()) rnd(); rnd(); rnd() local new_moonlander_list = {} function new_moonlander_init( e ) Include( "utillib.lua" ) Include( "quatlib.lua" ) new_moonlander_list[ e ] = nil end local controlsState = { UThrust = false, FThrust = false, DThrust = false, LTurn = false, RTurn = false } local function DisplayCrashedML( e, de ) CollisionOff( e ) de.quat = Q.FromEuler( 0, 0, 0 ) DE_Show( e, 'Moonlander Crashed') DE_Update( e, de.x, de.y, de.z, de.quat ) local list = DE_GetEnts ( e, 'Moonlander Crashed') if #list > 0 then StartParticleEmitter ( list[1] ) -- assume only one 'crashed' entity present end -- move 'new' moonlander to random position de.x, de.z = U.RandomPos( 3000, de.x, de.z ) -- reposition moonlander ResetRotation( e, 0, 0, 0 ) ResetPosition( e, de.x, GetTerrainHeight( de.x, de.z ), de.z ) CollisionOn( e ) end local function AttachPlayer( de ) local ldquat = Q.FromEuler( 0, rad( de.playerAngle ), 0 ) ldquat = Q.Mul( de.quat, ldquat ) xa, ya, za = Q.ToEuler( ldquat ) local xo, yo, zo = U.Rotate3D( 0, 0, -800, xa, ya, za ) local y = GetTerrainHeight( de.x + xo, de.z + zo ) if de.y + yo > y then y = de.y + yo end SetFreezePosition( de.x + xo, y + 400, de.z + zo ) xa, ya, za = Q.ToEuler( Q.Mul( ldquat, Q.FromEuler( rad ( 15), 0, 0 ) ) ) ya = deg( ya ) -- avoid Euler gimble lock angles if abs( 90 - ya ) < 0.1 or abs( 90 + ya ) < 0.1 then ya = ya + 0.01 end SetFreezeAngle( deg( xa ), ya, deg( za ) ) TransportToFreezePosition() end local function GetControls( e, de ) local controls = {} if g_KeyPressE == 1 then DE_Show( e, 'Main' ) controlsState.UThrust = true controls[#controls + 1] = 'Main' elseif controlsState.UThrust then controlsState.UThrust = false DE_Hide( e, 'Main' ) end if g_KeyPressR == 1 then DE_Show( e, 'Down' ) controlsState.DThrust = true controls[#controls + 1] = 'Down' elseif controlsState.DThrust then controlsState.DThrust = false DE_Hide( e, 'Down' ) end if g_MouseClick == 1 then DE_Show( e, 'Fwrd' ) controlsState.FThrust = true controls[#controls + 1] = 'Fwrd' elseif controlsState.FThrust then controlsState.FThrust = false DE_Hide( e, 'Fwrd' ) end if g_MouseClick == 2 then DE_Show( e, 'Back' ) controlsState.RThrust = true controls[#controls + 1] = 'Back' elseif controlsState.RThrust then controlsState.RThrust = false DE_Hide( e, 'Back' ) end local mx = MouseMoveX() if mx < 0 then DE_Show( e, 'Left' ) controlsState.LTurn = true controls[#controls + 1] = 'Left' elseif controlsState.LTurn and g_Time > de.mouseTimer then controlsState.LTurn = false DE_Hide( e, 'Left' ) end if mx > 0 then DE_Show( e, 'Rght' ) controlsState.RTurn = true controls[#controls + 1] = 'Rght' elseif controlsState.RTurn and g_Time > de.mouseTimer then controlsState.RTurn = false DE_Hide( e, 'Rght' ) end return controls end local function AddVect( vv, x, y, z , val) return {x = vv.x + x * val, y = vv.y + y * val, z = vv.z + z * val} end local function VectDist( v ) return sqrt( v.x*v.x + v.y*v.y + v.z*v.z ) end function HitTerrain( de ) return GetTerrainHeight(de.x + 100, de.z + 100) >= de.y or GetTerrainHeight(de.x + 100, de.z - 100) >= de.y or GetTerrainHeight(de.x - 100, de.z + 100) >= de.y or GetTerrainHeight(de.x - 100, de.z - 100) >= de.y end local startPress = false function new_moonlander_main( e ) local de = new_moonlander_list[ e ] if de == nil then local Ent = g_Entity[e] -- model is forced to default rotation on purpose so that the -- jet positioning abgles work properly. new_moonlander_list[ e ] = { state = 'init', timer = math.huge, quat = Q.FromEuler( 0, 0, 0 ), x = Ent.x, y = Ent.y, z = Ent.z, vv = { x = 0, y = 0, z = 0}, velocity = 0, mouseTimer = math.huge, turnSpeed = 0, landed = true, playerAngle = -75 } return end if de.state == 'init' then G_MoonlanderJetsInitialisation( e ) DE_Register( e, 'Moonlander Crashed', { xo = 0, yo = 0, zo = 0, angQ = Q.FromEuler( 0, 0, 0 ) } ) de.state = 'idle' return elseif de.state == 'idle' then if U.PlayerLookingNear( e, 500, 100 ) then Prompt( "Press E to Fly Moonlander") if g_KeyPressE == 1 then startPressed = true de.state = 'play' -- need to put all this in a 'level init' function g_PlayerWobble = GetGamePlayerControlWobbleHeight() SetGamePlayerControlWobbleHeight( 0 ) de.turnSpeed = 0 de.velocity = 0 DE_Hide( e, 'Moonlander Crashed') local list = DE_GetEnts ( e, 'Moonlander Crashed') if #list > 0 then StopParticleEmitter ( list[1] ) -- assume only one 'crashed' entity present end else startPressed = false end end elseif de.state == 'play' then if startPressed and g_KeyPressE == 1 then return else startPressed = false end if g_KeyPressA == 1 then de.playerAngle = de.playerAngle + 0.47 end if g_KeyPressD == 1 then de.playerAngle = de.playerAngle - 0.47 end -- get existing Euler angles local xa, ya, za = Q.ToEuler( de.quat ) local con = GetControls( e, de ) for _,v in pairs( con ) do if v == 'Main' then -- figure out which way is 'UP' local ux, uy, uz = U.Rotate3D( 0, 1, 0, xa, ya, za ) de.vv = AddVect( de.vv, ux, uy, uz , 0.04) de.landed = false end if v == 'Down' then -- figure out which way is 'Down' local ux, uy, uz = U.Rotate3D( 0, -1, 0, xa, ya, za ) de.vv = AddVect( de.vv, ux, uy, uz , 0.01) end if v == 'Fwrd' then -- figure out which way is 'Forward' for the model local ux, uy, uz = U.Rotate3D( -1, 0, 0, xa, ya, za ) de.vv = AddVect( de.vv, ux, uy, uz , 0.02) end if v == 'Back' then -- figure out which way is 'Backward' for the model local ux, uy, uz = U.Rotate3D( 1, 0, 0, xa, ya, za ) de.vv = AddVect( de.vv, ux, uy, uz , 0.02) end if v == 'Left' then de.turnSpeed = de.turnSpeed - rnd() * 0.0005 de.mouseTimer = g_Time + 200 elseif v == 'Rght' then de.turnSpeed = de.turnSpeed + rnd() * 0.0005 de.mouseTimer = g_Time + 200 end end Prompt( modf( abs( de.turnSpeed ) * 10000) .. ", " .. modf( de.y - GetTerrainHeight( de.x, de.z ) ) .. ", " .. modf( de.velocity * 100 ) ) if de.landed or g_Time > de.mouseTimer and abs( de.turnSpeed ) < 0.001 then de.turnSpeed = 0 end -- process turning if de.turnSpeed ~= 0 then de.quat = Q.Mul( de.quat, Q.FromEuler( 0, de.turnSpeed, 0) ) end -- do some basic gravity style stuff here if not de.landed then de.vv.y = de.vv.y - 0.002 end -- calculate new position local nx, ny, nz = de.x + de.vv.x, de.y + de.vv.y, de.z + de.vv.z if de.vv.y > 0 or not HitTerrain( de ) then de.x = nx de.y = ny de.z = nz de.velocity = VectDist( de.vv ) else de.landed = true de.vv = { x = 0, y = 0, z = 0 } if de.velocity > 1 or abs(de.turnSpeed) * 10000 > 30 then -- crashed! PromptDuration( "!! CRASHED !!", 2000) de.state = 'idle' de.playerAngle = -89 DE_Hide( e ) -- hide all attached entities SetGamePlayerControlWobbleHeight( g_PlayerWobble ) DisplayCrashedML( e, de ) return end de.turnSpeed = 0 de.velocity = 0 end -- get new Euler angles local xa, ya, za = Q.ToEuler( de.quat ) -- reposition moonlander CollisionOff( e ) ResetRotation( e, deg( xa ), deg( ya ), deg( za ) ) ResetPosition( e, de.x, de.y, de.z ) CollisionOn( e ) -- update 'attached' decals DE_Update( e, de.x, de.y, de.z, de.quat ) AttachPlayer( de ) end end -- This all looks really daunting but it is quite simple. -- The following lists contain details for each nozzle position on the moonlander model -- (and there are a lot of them), x,y,z offsets from the models origin, the Euler angles -- at which to place jets and 'tweak' values to align the second rotated jet decal properly. -- (note: these tweak values depend oon the scale of the decal!) -- (note2: the Euler angles are only valid for the default mode rotation when first dropped -- into the editor) local MLNozzlesList = { -- main jets decals, i.e. 'lift' there are 8 nozzles so 16 jets in all ------------------------ #Decals { name = 'Main', tx = 0, ty = 0, tz = 2.5, pos = { xo = -70.1, yo = 8, zo = -12, angQ = Q.FromEuler( rad( 0), rad( 0 ), rad ( 142) ) } }, -- 2 { name = 'Main', tx = 0, ty = 0, tz = 2.5, pos = { xo = -70.1, yo = 8, zo = 12, angQ = Q.FromEuler( rad( 0), rad( 0 ), rad ( 142) ) } }, -- 4 { name = 'Main', tx = 2.5, ty = 0, tz = -2.5, pos = { xo = -14.8, yo = 8, zo = -72, angQ = Q.FromEuler( rad(-142), rad( 0 ), rad ( 0) ) } }, -- 6 { name = 'Main', tx = 2.5, ty = 0, tz = -2.5, pos = { xo = 10.1, yo = 8, zo = -72, angQ = Q.FromEuler( rad(-142), rad( 0 ), rad ( 0) ) } }, -- 8 { name = 'Main', tx = -5, ty = 0, tz = 2.5, pos = { xo = 74.1, yo = 8, zo = 12, angQ = Q.FromEuler( rad( 0), rad( 0 ), rad (-142) ) } }, -- 10 { name = 'Main', tx = -5, ty = 0, tz = 2.5, pos = { xo = 74.1, yo = 8, zo = -12, angQ = Q.FromEuler( rad( 0), rad( 0 ), rad (-142) ) } }, -- 12 { name = 'Main', tx = 2.5, ty = 0, tz = -2.5, pos = { xo = 10.1, yo = 8, zo = 72, angQ = Q.FromEuler( rad( 142), rad( 0 ), rad ( 0) ) } }, -- 14 { name = 'Main', tx = 2.5, ty = 0, tz = -2.5, pos = { xo = -14.8, yo = 8, zo = 72, angQ = Q.FromEuler( rad( 142), rad( 0 ), rad ( 0) ) } }, -- 16 -- 'forward' jets { name = 'Fwrd', tx = 0, ty = -1, tz = 1, pos = { xo = 82.0, yo = 87.5, zo = 0.2, angQ = Q.FromEuler( rad( 0), rad ( 0), rad (-90) ) } }, -- 2 { name = 'Fwrd', tx = 1, ty = 1, tz = 0, pos = { xo = 74.0, yo = 209.2, zo = -71, angQ = Q.FromEuler( rad(-90), rad (-45), rad ( 0) ) } }, -- 4 { name = 'Fwrd', tx = 0, ty = -1, tz = -1, pos = { xo = 74.0, yo = 208.9, zo = 55, angQ = Q.FromEuler( rad( 90), rad ( 45), rad ( 0) ) } }, -- 6 -- 'reverse' jets { name = 'Back', tx = 0, ty = 1, tz = 1, pos = { xo = -82, yo = 86.0, zo = -0.3, angQ = Q.FromEuler( rad( 0), rad ( 0), rad (90) ) } }, -- 2 { name = 'Back', tx = 1, ty = 1, tz = 0, pos = { xo = -50, yo = 209.0, zo = -71, angQ = Q.FromEuler( rad(-90), rad ( 45), rad ( 0) ) } }, -- 4 { name = 'Back', tx = 1, ty = -1, tz = 0, pos = { xo = -50, yo = 208.9, zo = 55, angQ = Q.FromEuler( rad( 90), rad (-45), rad ( 0) ) } }, -- 6 -- 'left' rotation jets { name = 'Left', tx = 0, ty = 1, tz = 0, pos = { xo = 66.5, yo = 87.2, zo = -15.0, angQ = Q.FromEuler( rad(-90), rad ( -8), rad ( 45) ) } }, -- 2 { name = 'Left', tx = 0, ty = -1, tz = 0, pos = { xo = -69.5, yo = 88.3, zo = 15.0, angQ = Q.FromEuler( rad( 90), rad ( -8), rad (-45) ) } }, -- 4 { name = 'Left', tx = 0, ty = -1, tz = 1, pos = { xo = -51.0, yo = 209.0, zo = -50.0, angQ = Q.FromEuler( rad( 90), rad (-45), rad ( 0) ) } }, -- 6 { name = 'Left', tx = 1, ty = 1, tz = 0, pos = { xo = 76.5, yo = 208.8, zo = 31.0, angQ = Q.FromEuler( rad(-90), rad (-45), rad ( 0) ) } }, -- 8 { name = 'Left', tx = 1, ty = -1, tz = 0, pos = { xo = -27.0, yo = 208.8, zo = 57.0, angQ = Q.FromEuler( rad( 90), rad ( 45), rad ( 0) ) } }, -- 10 { name = 'Left', tx = 0, ty = 1, tz = -1, pos = { xo = 52.0, yo = 209.0, zo = -72.5, angQ = Q.FromEuler( rad(-90), rad ( 45), rad ( 0) ) } }, -- 12 -- 'right' rotation jets { name = 'Rght', tx = 1, ty = 0, tz = 0, pos = { xo = 66.5, yo = 87.2, zo = 15.0, angQ = Q.FromEuler( rad( 90), rad ( 8), rad ( 45) ) } }, -- 2 { name = 'Rght', tx = 1, ty = 0, tz = 0, pos = { xo = -69.5, yo = 88.3, zo = -15.0, angQ = Q.FromEuler( rad(-90), rad ( 8), rad (-45) ) } }, -- 4 { name = 'Rght', tx = 1, ty = 1, tz = 1, pos = { xo = -28.0, yo = 209.0, zo = -73.0, angQ = Q.FromEuler( rad(-90), rad (-45), rad ( 0) ) } }, -- 6 { name = 'Rght', tx = 1, ty = -1, tz = 1, pos = { xo = 53.0, yo = 208.8, zo = 54.3, angQ = Q.FromEuler( rad( 90), rad (-45), rad ( 0) ) } }, -- 8 { name = 'Rght', tx = 1, ty = 1, tz = -1, pos = { xo = -50.0, yo = 208.8, zo = 34.0, angQ = Q.FromEuler( rad(-90), rad ( 45), rad ( 0) ) } }, -- 10 { name = 'Rght', tx = 1, ty = -1, tz = -1, pos = { xo = 75.5, yo = 208.8, zo = -48.5, angQ = Q.FromEuler( rad( 90), rad ( 45), rad ( 0) ) } }, -- 12 -- 'down' jets (first three are rear) { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = 64.5, yo = 221.0, zo = -60.5, angQ = Q.FromEuler( 0, 0, 0 ) } }, -- 2 { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = 64.5, yo = 221.0, zo = 44.5, angQ = Q.FromEuler( 0, 0, 0 ) } }, -- 4 { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = 66.0, yo = 100.0, zo = 0.2, angQ = Q.FromEuler( 0, 0, 0 ) } }, -- 6 --(second three are front) { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = -39.0, yo = 221.0, zo = 44.5, angQ = Q.FromEuler( 0, 0, 0 ) } }, -- 8 { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = -39.0, yo = 221.0, zo = -60.5, angQ = Q.FromEuler( 0, 0, 0 ) } }, -- 10 { name = 'Down', tx = 0.5, ty = 0, tz = 0.5, pos = { xo = -68.3, yo = 100.0, zo = -0.3, angQ = Q.FromEuler( 0, 0, 0 ) } } -- 12 } local MLJetScales = { Main = 300, Fwrd = 140, Back = 140, Left = 80, Rght = 80, Down = 100 } local rotyQ = Q.FromEuler( 0, rad(90), 0 ) -- Globals section function G_MoonlanderJetsInitialisation( e ) -- register for dynamic entities if DE_Register ~= nil then -- register steam decals for exhaust ports for _, v in pairs( MLNozzlesList ) do local pos = v.pos -- two jets per nozzle, second rotated 90 degrees DE_Register( e, v.name, pos, { min = 10, max = MLJetScales[ v.name ], frames=10 } ) DE_Register( e, v.name, { xo = pos.xo + v.tx, yo = pos.yo + v.ty, zo = pos.zo + v.tz, angQ = Q.Mul( pos.angQ, rotyQ ) }, { min = 10, max = MLJetScales[ v.name ], frames = 10 } ) end end end