-- LUA Script - precede every function and global member with lowercase name of script + '_main' -- Default script - does nothing. local planes = {} function helicopter_init(e) end local function CloserThan(Ent, dist) local dX, dZ = Ent.x - g_PlayerPosX, Ent.z - g_PlayerPosZ return (dX*dX + dZ*dZ) < dist*dist end local function TimerExpired(p) return g_Time > p.timer end local function SetTimerSecs(p, val) if TimerExpired(p) then p.timer = g_Time + val * 1000 end end local rad = math.rad local sin = math.sin local cos = math.cos local random = math.random local function Rotate3D (x, y, z, xrot, yrot, zrot) function RotatePoint2D (x, y, Ang) -- Ang in radians local Sa, Ca = sin(Ang), cos(Ang) return x*Ca - y*Sa, x*Sa + y*Ca end local NX, NY, NZ = x, y, z -- X NZ, NY = RotatePoint2D (NZ, NY, -xrot) -- Y NX, NZ = RotatePoint2D (NX, NZ, -yrot) -- Z NY, NX = RotatePoint2D (NY, NX, -zrot) return NX, NY, NZ end local function MoveForward(e, Ent, val) -- change the vector to match 'forward' for the model, i.e. pointing up is 0,0,1 left would be -1,0,0 etc local vX, vY, vZ = Rotate3D (0, 0, 1, rad(Ent.anglex), rad(Ent.angley), rad(Ent.anglez)) local FPSrel = 60 / g_scheduler[e].frames_per_second ResetPosition(e, Ent.x + (val * vX * FPSrel), Ent.y, Ent.z + (val * vZ * FPSrel)); end local function MoveUp(e, Ent, val) local FPSrel = 60 / g_scheduler[e].frames_per_second ResetPosition(e, Ent.x, Ent.y + val * FPSrel, Ent.z) end function helicopter_main(e) scheduler(e) local Ent = g_Entity[e] if Ent == nil then return end local plane = planes[e] if plane == nil then planes[e] = {speed = 15 + random(1,5), -- set plane speed (16-20) turnspeed = 7, fallspeed = 15, takeoffspeed = 7, state = 'init', timer = 0, soundplaying = false }; return end Prompt(plane.state .. ", " .. Ent.health) -- initialization if plane.state == 'init' then plane.state = 'idle' GravityOff(e) elseif plane.state == 'idle' and CloserThan(Ent, 4000) then plane.state = 'takeoff' SetTimerSecs(plane, 3) -- taking off elseif plane.state == 'takeoff' and CloserThan(Ent, 4000) then if not TimerExpired(plane) then MoveUp(e, Ent, plane.takeoffspeed - plane.takeoffspeed * 0.7) else plane.state = 'takeoff2' SetTimerSecs(plane, 3) end elseif plane.state == 'takeoff2' then if not TimerExpired(plane) then MoveUp(e, Ent, plane.takeoffspeed) -- go at takeoff speed (another 3 secs) else if (Ent.y <= g_PlayerPosY + 500) then -- pull nose up and increase height (takeoff) while below player SendMessageF("setrotationx", e, -5) SendMessageF("setpositiony", e, Ent.y +5) else -- push nose down and set plane.state to fly when above player, also stop the timer SendMessageF("setrotationx",e,0); plane.state = 'fly' end MoveForward(e, Ent, plane.takeoffspeed + (plane.takeoffspeed * 0.7)) -- move at increased speed while taking off end -- flying straight elseif plane.state == 'fly' then if not CloserThan(Ent, 9000) then plane.state = 'turn' SetTimerSecs(plane, 10) else if not plane.soundplaying then -- LoopSound(e, 0) -- SetSoundVolume(80) plane.soundplaying = true end MoveForward(e, Ent, plane.speed) -- move at defined speed if (random(1, 10) == 2) then -- in 10% cases rotate plane a bit and change it's height RotateX(e, random(-10, 10)) RotateZ(e, random(-10, 10)) MoveUp(e, Ent, random(-10, 10)) end end -- turning elseif plane.state == 'turn' then MoveForward(e, Ent, plane.turnspeed) -- move at turning speed if not TimerExpired(plane) then RotateToPlayerSlowly(e, 2) else -- after 6 seconds do this: RotateToPlayerSlowly(e, 100) -- rotate to player plane.state = 'fly' -- set the plane into flying mode end -- falling down elseif plane.state == 'fall' then MoveForward(e, Ent, plane.fallspeed) -- move at falling speed RotateX(e, 3) -- rotate a bit RotateZ(e, 3) RotateY(e, 3) RotateX(e, 5) -- rotate a bit again RotateZ(e, 5) RotateY(e, 0) RotateX(e, 7) -- rotate a bit again RotateZ(e, 7) RotateY(e, 0) MoveUp(e, Ent, - 5); -- move down -- 3 -- if TimerExpired(plane) or Ent.y <= GetTerrainHeight(Ent.x, Ent.z) + 50 then -- after 3 seconds of falling down or if hit the ground plane.state = 'down' -- change plane.state to down plane.Timer = math.huge StopParticleEmitter(e) -- stop smoke SetEntityHealth(e, 0) end end -- if flying and is shot to death (while keeping entity alive), set falling plane.state and start smoke if (plane.state == 'fly' or plane.state == 'turn') and Ent.health < 100 then SetEntityHealth(e, 1000) plane.state = 'fall' StartParticleEmitter(e) StopSound(e, 0) SetTimerSecs(plane, 10) GravityOn(e) end end ---------------------------------------------------------------- -- Scheduler, this nifty little function keeps track of time. -- -- The main routine should be called every frame and returns -- -- a flag indicating whether the passed in time has expired -- -- since the last time it was called. -- -- If no time period is specified 100ms is used by default, -- -- i.e. 1/10th of a second. -- -- The caller can either ignore the return flag or use it to -- -- trigger time sensitive functionality. -- -- A global value giving the frames per second count is also -- -- generated, this can be used in script to make animations -- -- independent of frames. -- -- The reason this function is tied to an entity rather than -- -- being global is so that each entity can be triggered at a -- -- different time rather than all being triggered in the same -- -- frame. ---------------------------------------------------------------- g_scheduler = {} function scheduler(e, period) period = period or 100 -- defaults to tenths of a second (100ms) if g_Time == nil then return false end if g_scheduler[e] == nil then g_scheduler[e] = {frames_per_second = 60, period_accumulated = math.random(0, period), accumulated_time = 0, timer_value_last_frame = g_Time, frame_counter = 0}; -- 'test' mode sometimes doesn't clear Lua globals so if -- this appears to be the case initialise everything elseif g_scheduler[e].accumulated_time > 2000 then g_scheduler[e].frames_per_second = 60 g_scheduler[e].period_accumulated = math.random(0, period) g_scheduler[e].accumulated_time = 0 g_scheduler[e].timer_value_last_frame = g_Time g_scheduler[e].frame_counter = 0 end local entry = g_scheduler[e] local do_this_frame_flag = false entry.frame_counter = entry.frame_counter + 1 local time_since_last_frame = g_Time - entry.timer_value_last_frame if (entry.period_accumulated + time_since_last_frame) > period then entry.period_accumulated = (entry.period_accumulated + time_since_last_frame) - period; do_this_frame_flag = true else entry.period_accumulated = entry.period_accumulated + time_since_last_frame end if (entry.accumulated_time + time_since_last_frame) > 1000 then -- more than a second passed? entry.accumulated_time = (entry.accumulated_time + time_since_last_frame) - 1000; entry.frames_per_second = entry.frame_counter entry.frame_counter = 0 else entry.accumulated_time = entry.accumulated_time + time_since_last_frame end entry.timer_value_last_frame = entry.timer_value_last_frame + time_since_last_frame return do_this_frame_flag end