#include "script/wheels.lua" #include "script/common.lua" #include "script/explosion.lua" local dustintesity = 0.1 local dusttimer = 0 local timetravelwind = Vec(0,0,15) local glitchEmmiter = nil isOutro = GetBoolParam("isOutro", false) function init() glitchEmmiter = FindShape("glitch", true) idleGlitchTimer = 0.0 startSpawnGlitch = false --Scene breach = FindLocations("breach", true) rockhit_pos = GetLocationTransform(FindLocation("rock_hit", true)).pos slowtime_trigger = FindTrigger("slowtime", true) --Time Machine machine_up = false machine_pitch = false timemachine_body = FindBody("time_machine", true) timemachine_shape = FindShape("machine_shape", true) machine_transform = GetBodyTransform(timemachine_body) start_machine_transform = GetBodyTransform(timemachine_body) machine_speed = 0.2 smoke_age = 0 isSmoke = false floating_speed = 0.0005 --timetravel lights travel_spark = FindLights("travel_spark", true) travel_lightintensity = 0.001 travel_flash = FindLight("travel_flash", true) rocket_light = FindLights("rocket_light", true) bridges = FindLocations("bridge", true) isBridge = false --Cutscene SetPostProcessingProperty("bloom", 2) camerpoint_pos = {} local camerapoints = FindLocations("camerpoint", true) for i = 1, #camerapoints do camerpoint_pos[i] = GetLocationTransform(camerapoints[i]).pos end scene_step = 1 scene_timer = 0 camera_step = 1 camera_timer = 0 local c_targets = FindLocation("camera_target", true) camera_target = GetLocationTransform(c_targets) local f_targets = FindLocation("final_camera", true) final_camera = GetLocationTransform(f_targets) route_trigger = FindTriggers("route_trigger", true) sparktrigger = 0 table.sort(route_trigger, sort) for i=1, #route_trigger do SetTag(route_trigger[i], "route_trigger", i+2) if HasTag(route_trigger[i], "sparks") then sparktrigger = i + 2 end end s_prev = VecLength(VecSub(GetTriggerTransform(route_trigger[1]).pos, GetTriggerTransform(route_trigger[2]).pos)) rotation_prev = VecLength(VecSub(GetTriggerTransform(route_trigger[1]).rot, GetTriggerTransform(route_trigger[2]).rot)) speed_coef = 1 rot_coef = 1 routemax = #route_trigger + 2 current_route_trigger = route_trigger[1] local dir = VecNormalize(VecSub(GetTriggerTransform(route_trigger[1]).pos, start_machine_transform.pos)) machine_dir = VecScale(dir, 0.4) trigger_start_distance = VecLength(VecSub(GetTriggerTransform(route_trigger[1]).pos, start_machine_transform.pos)) machine_start = GetBodyTransform(timemachine_body) --TimeSparks time_spark_appear = 23 time_spark_appeartimer = 0 time_sparks_count = 0 time_shining = FindLight("time_shining", true) time_sparks_trigger = FindTrigger("time_sparks_trigger", true) time_sparks = {light = {}, speed = {}, acceleration = {}, timesparkbody = {}, intensity = {}, sounded = {}} local timesparkslights = FindLights("travel_spark", true) for i = 1, #timesparkslights do time_sparks.light[i] = timesparkslights[i] time_sparks.speed[i] = Vec(0.15,0,-0.15) time_sparks.acceleration[i] = 1 time_sparks.timesparkbody[i] = GetShapeBody(GetLightShape(timesparkslights[i])) time_sparks.intensity[i] = false time_sparks.sounded[i] = false end spakles_sound_trigger = FindTrigger("spakles_sound", true) --Logo isShowLogo = false LogoAlpha = 0 LogoTransition = 0 fov = 90 testlerp = 0.01 camera_fov = FindLocation("camera_fov", true) camera_fovt = GetLocationTransform(camera_fov) if isOutro then camera_step = 2 camera_timer = 3 fov = 130 SetBool("savegame.mod.creditsdone", true) else SetBool("savegame.mod.backintime",true) end pressedTimer = 0.0 road = 0 lerpspeed = 0.1 slowdown = false --Sounds --Loop sound_car_loop = LoadLoop("MOD/sound/time_travel/car-loop.ogg") sound_travel_riser_loop = LoadLoop("MOD/sound/time_travel/time-travel-riser-loop.ogg", 100) sound_travel_wind_loop = LoadLoop("MOD/sound/time_travel/time-travel-wind-loop.ogg") sound_wild_west_ambience = LoadLoop("MOD/sound/time_travel/wild-west-ambience.ogg") --Samples sound_hit = LoadSound("MOD/sound/time_travel/break.ogg") sound_time_hole_swoosh = LoadSound("MOD/sound/time_travel/time-hole-swoosh.ogg") sound_time_hole_boom = LoadSound("MOD/sound/time_travel/time-hole-boom.ogg") sound_swoosh = LoadSound("MOD/sound/time_travel/rocks-swoosh.ogg") sound_bridge_crash = LoadSound("MOD/sound/time_travel/bridge-crash.ogg") sound_logo_sample = LoadSound("MOD/sound/time_travel/logo-sample.ogg") sound_particle0 = LoadSound("MOD/sound/time_travel/particle0.ogg") sound_particle1 = LoadSound("MOD/sound/time_travel/particle1.ogg") sound_particle2 = LoadSound("MOD/sound/time_travel/particle2.ogg") sound_particle3 = LoadSound("MOD/sound/time_travel/particle3.ogg") sounds_sparks = {sound_particle0, sound_particle1, sound_particle2, sound_particle3} end function sort(a, b) return GetTriggerTransform(a).pos[3] > GetTriggerTransform(b).pos[3] end function EndScene() if not isOutro then StartLevel("hub1", "MOD/hub.xml", "hub1") else StartLevel("main", "MOD/main.xml") end end function tick(dt) if pressedTimer > 0.0 then pressedTimer = pressedTimer - dt if checkInputs() then return EndScene() end end if checkInputs() then pressedTimer = 4.0 end if GetBool("game.mod.credits.finished") then return EndScene() end --Camera-- if not slowdown then if camera_step ~= 0 then camera_timer = camera_timer + dt/2.8 end if scene_step ~= 0 then scene_timer = scene_timer + dt/2.8 end else SetTimeScale(0.4) if camera_step ~= 0 then camera_timer = camera_timer + dt/44.8 end if scene_step ~= 0 then scene_timer = scene_timer + dt/44.8 end end if not isOutro then if not isShowLogo then PlayMusic("MOD/music/theme-intro.ogg") end if camera_step > 3 then PlayLoop(sound_wild_west_ambience) end if camera_step < 3 then PlayLoop(sound_travel_riser_loop, machine_transform.pos, 1) end PlayLoop(sound_car_loop, machine_transform.pos, 50) elseif isOutro then PlayMusic("MOD/music/theme-credits.ogg") end if lerpspeed > 0.13 then lerpspeed = lerpspeed - dt/28000 end if scene_step < 12 then if not slowdown then road = road + dt/(3.999 * lerpspeed) else road = road + dt/(7.999 * lerpspeed) end end if camera_step == 1 then PlayLoop(sound_travel_wind_loop) if camera_timer < 0.25 then local rot = QuatLookAt(camera_target.pos, machine_transform.pos) camera_target.rot = rot camera_target.pos = VecAdd(camera_target.pos, Vec(0,0,0.0025)) elseif camera_timer >= 0.25 and camera_timer < 1.5 then local rot = QuatLookAt(camera_target.pos, machine_transform.pos) camera_target.rot = rot camera_target.pos = VecAdd(camera_target.pos, Vec(0.005,0,0.001)) Travelling_Sparks_Appear(dt) elseif camera_timer >= 1.5 then camera_step = 2 camera_timer = 0 end elseif camera_step == 2 then if not isOutro then PlayLoop(sound_travel_wind_loop) end if camera_timer <= 1.5 then Travelling_Sparks_Appear(dt) end if camera_timer < 1.075 then camera_target.rot = QuatSlerp(camera_target.rot, camera_fovt.rot, camera_timer/100) camera_target.pos = VecLerp(camera_target.pos, camera_fovt.pos, camera_timer/100) elseif not isOutro then camera_target.rot = QuatSlerp(camera_target.rot, camera_fovt.rot, camera_timer/150) camera_target.pos = VecLerp(camera_target.pos, camera_fovt.pos, camera_timer/150) fov = fov + dt * 14.3 elseif isOutro then camera_target.rot = camera_fovt.rot camera_target.pos = camera_fovt.pos end if fov >= 175 and not isOutro then camera_timer = 0 camera_step = 3 scene_step = 2 end elseif camera_step == 3 then if fov > 90 then fov = fov - dt * 52.9 end local rot = QuatLookAt(camera_target.pos, machine_transform.pos) camera_target.rot = rot camera_target.pos = VecLerp(camera_target.pos, machine_transform.pos, camera_timer/13) elseif camera_step == 4 then local rot = QuatLookAt(camera_target.pos, machine_transform.pos) camera_target.rot = rot camera_target.pos = VecLerp(camera_target.pos, camerpoint_pos[1], camera_timer/2) if camera_timer > 0.1 then camera_timer = 0 camera_step = camera_step + 1 end elseif camera_step == 5 then local rot = QuatLookAt(camera_target.pos, machine_transform.pos) camera_target.rot = rot camera_target.pos = VecLerp(camera_target.pos, camerpoint_pos[2], camera_timer/2) if camera_timer > 0.35 then camera_timer = 0 camera_step = camera_step + 1 end elseif camera_step == 6 then camera_target.pos = VecLerp(camera_target.pos, final_camera.pos, camera_timer/100) camera_target.rot = QuatSlerp(camera_target.rot, final_camera.rot, camera_timer/50) if camera_timer > 3 then camera_timer = 0 camera_step = camera_step + 1 PlaySound(sound_logo_sample) StopMusic() isShowLogo = true end elseif camera_step == 7 then if camera_timer > 3 then StartLevel("hub1", "MOD/hub.xml", "hub1") camera_timer = 0 camera_step = camera_step + 1 end end SetCameraTransform(camera_target, fov) --Scene if scene_step == 1 then Travelling_Sparks(dt) if scene_timer >= 3 then scene_timer = 0 end elseif scene_step == 2 then travel_lightintensity = - 10 Travelling_Sparks(dt) if scene_timer > 0.3 then SetLightIntensity(travel_flash, 5) PlaySound(sound_time_hole_swoosh) PlaySound(sound_time_hole_boom) Delete(time_shining) for i = 1, #breach do local direction = Vec(0,0,1) MakeHole(GetLocationTransform(breach[i]).pos, 10, 10, 10) direction = TransformToParentVec(machine_transform, direction) Shockwave(direction, GetLocationTransform(breach[i]).pos, 250) Delete(breach[i]) end if IsHandleValid(time_sparks.timesparkbody[1]) then for i = 1, #time_sparks.timesparkbody do Delete(time_sparks.timesparkbody[i]) end end scene_step = 3 machine_start = GetBodyTransform(timemachine_body) end elseif scene_step == 3 then MoveIt(machine_dir) local distance = VecLength(VecSub(GetTriggerTransform(current_route_trigger).pos, GetBodyTransform(timemachine_body).pos)) machine_transform.rot = QuatSlerp(machine_start.rot, GetTriggerTransform(current_route_trigger).rot, ((trigger_start_distance-distance)/trigger_start_distance)) if scene_timer > 1 then SetPostProcessingProperty("bloom", 0) road = 0 scene_timer = 0 scene_step = 4 end elseif scene_step == 4 then if scene_timer > 0.05 and IsHandleValid(travel_flash) then Delete(travel_flash) end MoveIt(machine_dir) if scene_timer > 1 then road = 0 scene_step = 5 end elseif scene_step > 4 then if IsHandleValid(slowtime_trigger) and IsBodyInTrigger(slowtime_trigger, timemachine_body) then Delete(slowtime_trigger) slowdown = true end local distance = VecLength(VecSub(GetTriggerTransform(current_route_trigger).pos, GetBodyTransform(timemachine_body).pos)) if scene_step < 12 then machine_transform.rot = QuatSlerp(machine_start.rot, GetTriggerTransform(current_route_trigger).rot, road * 0.65 * rot_coef) --((trigger_start_distance-distance)/trigger_start_distance) else road = road + dt/1.35 machine_transform.rot = QuatSlerp(machine_start.rot, GetTriggerTransform(current_route_trigger).rot, road * speed_coef) end machine_transform.pos = VecLerp(machine_start.pos, GetTriggerTransform(current_route_trigger).pos, road * speed_coef) if scene_step == 8 then machine_transform.rot[2] = machine_transform.rot[2] - dt/0.7 machine_transform.rot[3] = machine_transform.rot[3] + dt/0.7 elseif scene_step == 9 then slowdown = true machine_transform.rot[2] = machine_transform.rot[2] - dt/0.933 machine_transform.rot[3] = machine_transform.rot[3] - dt/0.933 end SetBodyTransform(timemachine_body, machine_transform) end ---Route triggers works if tonumber(GetTagValue(current_route_trigger, "route_trigger")) ~= scene_step - 1 and scene_step > 3 then for i = 1, #route_trigger do if tonumber(GetTagValue(route_trigger[i], "route_trigger")) == scene_step - 1 then current_route_trigger = route_trigger[i] if scene_step > 3 and scene_step < 12 then local current_dist = VecLength(VecSub(GetTriggerTransform(route_trigger[i]).pos, GetTriggerTransform(route_trigger[i-1]).pos)) local current_rot = VecLength(VecSub(GetTriggerTransform(route_trigger[i]).rot, GetTriggerTransform(route_trigger[i-1]).rot)) speed_coef = s_prev/current_dist rot_coef = rotation_prev/current_rot s_prev = current_dist rotation_prev = current_rot end machine_speed = 0.2 if HasTag(current_route_trigger, "rockhit") then explosionSparksCPP(rockhit_pos, 50, 10) explosionDebrisCPP(rockhit_pos, 100, 10) ParticleReset() ParticleType("smoke") ParticleColor(0.8,0.58,0.37) ParticleRadius(0.85, 1) ParticleGravity(rnd(0, -1)) ParticleDrag(1.0) ParticleAlpha(1, 1, "constant", 0, 0.5) SpawnParticle(rockhit_pos, Vec(0,0,0), rnd(3,5)) startSpawnGlitch = true camera_timer = 0 camera_step = camera_step + 1 PlaySound(sound_swoosh) PlaySound(sound_hit) end end end end if HasTag(current_route_trigger, "route_bridge") and not isBridge then for i=1, #bridges do local bridge_pos = GetLocationTransform(bridges[i]).pos MakeHole(bridge_pos, 2, 2, 2) explosionDebrisCPP(bridge_pos, 100, 10) end PlaySound(sound_bridge_crash) isSmoke = true isBridge = true local direction = Vec(0,0,1) direction = TransformToParentVec(machine_transform, direction) Shockwave(direction, machine_transform.pos, 250) end if IsBodyInTrigger(current_route_trigger, timemachine_body) then if slowdown and HasTag(current_route_trigger, "route_bridge") then slowdown = false end scene_step = scene_step + 1 if scene_step == 17 then PlaySound(sound_time_hole_swoosh) end ---Find direction if scene_step > 4 then for i = 1, #route_trigger do if tonumber(GetTagValue(route_trigger[i], "route_trigger")) == scene_step - 1 then if scene_step == 10 and slowdown then slowdown = false end local dir = VecNormalize(VecSub(GetTriggerTransform(route_trigger[i]).pos, machine_transform.pos)) machine_dir = VecScale(dir, machine_speed * dt * 0.95) road = 0 machine_start = GetBodyTransform(timemachine_body) trigger_start_distance = VecLength(VecSub(GetTriggerTransform(route_trigger[i]).pos, machine_transform.pos)) end end end end idleGlitchTimer = idleGlitchTimer + dt if startSpawnGlitch then if idleGlitchTimer >= 1.5 then ParticleReset() ParticleType("plain") ParticleColor(0.72, 0.35, 0.95, 0, 0, 1) ParticleRadius(0.95) ParticleGravity(-5) ParticleAlpha(0.2, 0, "smooth", "fadeout") ParticleStretch(5.0) ParticleTile(3) ParticleSticky(1.0) ParticleEmissive(1, 0.1) ParticleCollide(1, 1, "constant") ParticleDrag(0.5) SpawnParticle(GetShapeWorldTransform(glitchEmmiter).pos, TransformToParentVec(GetShapeWorldTransform(glitchEmmiter), Vec(rnd(-6, 6), rnd(3, -3), -10)), 1) end if idleGlitchTimer >= 2.0 then idleGlitchTimer = 0.0 end end if isSmoke then smoke_age = smoke_age + dt if smoke_age < 9 then local q = 1.0 - smoke_age / 3 for i=1, 6 * q do local w = 0.8-q*0.6 local w2 = 1.0 local r = 0.1 + 0.5*q local v = VecAdd(Vec(0, q + q * 8, 0), rndVec(1*q)) local p = VecAdd(machine_transform.pos, rndVec(r*0.3)) ParticleReset() ParticleType("smoke") ParticleColor(0.15,0.15,0.15) ParticleRadius(0.3*r, r) ParticleGravity(rnd(0, -1)) ParticleDrag(1.0) ParticleAlpha(q, q, "constant", 0, 0.5) SpawnParticle(p, v, rnd(3,5)) end else smoke_age = 0 end end end function update(dt) if scene_step < 5 then Time_Travel_Dust(dt) end if scene_step < 2 then Machine_Float() SetBodyTransform(timemachine_body, machine_transform) end if not isOutro then if tonumber(GetTagValue(current_route_trigger, "route_trigger")) > sparktrigger then for i = 1, #rocket_light do Sparks(math.random(8,16), 5, GetLightTransform(rocket_light[i])) end end end end function Machine_Float() if machine_up then machine_transform.pos[2] = machine_transform.pos[2] + 0.002 else machine_transform.pos[2] = machine_transform.pos[2] - 0.002 end if machine_pitch then machine_transform.rot[2] = machine_transform.rot[2] + floating_speed machine_transform.rot[3] = machine_transform.rot[3] + floating_speed floating_speed = floating_speed * 0.999 else machine_transform.rot[2] = machine_transform.rot[2] - floating_speed machine_transform.rot[3] = machine_transform.rot[3] - floating_speed floating_speed = floating_speed * 0.999 end if (machine_transform.pos[2] > start_machine_transform.pos[2] + 0.1 and machine_up) or (machine_transform.pos[2] < start_machine_transform.pos[2] - 0.1 and not machine_up) then machine_up = not machine_up end if (machine_transform.rot[2] > start_machine_transform.rot[2] + 0.05 and machine_pitch) or (machine_transform.rot[2] < start_machine_transform.rot[2] - 0.05 and not machine_pitch) then machine_pitch = not machine_pitch floating_speed = 0.0003 end end function Sparks(count, vel, spark_pos) for i=1, count do local direction = VecNormalize(spark_pos.rot) v = VecScale(direction,rnd(1,5)) v[1] = v[1] * rnd(0.1,3) v[3] = v[3] * rnd(0.1,3) local life = rnd(0, 1) life = life*life * 5 ParticleReset() ParticleEmissive(5, 0, "easeout") ParticleGravity(-10) ParticleRadius(0.03, 0.0, "easein") ParticleColor(1, 0.4, 0.3) ParticleTile(4) SpawnParticle(spark_pos.pos, v, life) end end function Time_Travel_Dust(dt) dusttimer = dusttimer + 0.0166667/3.33 if dusttimer > dustintesity then dusttimer = 0 ParticleReset() ParticleType("plain") ParticleRadius(0.8) ParticleColor(0.48, 0.33, 0.68) ParticleAlpha(1, 1, "constant", 0.5, 0.5) ParticleCollide(0) ParticleTile(8) for i = 1, 400 do local life = 1.0 local p, d = getParticlePos(8, 30, life) SpawnParticle(p, timetravelwind, life) end ParticleReset() ParticleType("plain") ParticleRadius(0.02) ParticleGravity(rnd(-5,5)) ParticleColor(0.48, 0.33, 0.68) ParticleAlpha(1, 1, "constant", 0.5, 0.5) ParticleCollide(0) ParticleTile(4) for i = 1, 55 do local life = 1.0 local p, d = getParticlePos(6, 30, life) SpawnParticle(p, timetravelwind, life) end ParticleReset() ParticleType("plain") ParticleGravity(rnd(-5,5)) ParticleColor(0.48, 0.33, 0.68) ParticleAlpha(0.1, 0.1, "constant", 0.5, 0.5) ParticleCollide(0) ParticleTile(0) for i = 1, 54 do local life = 1.0 local p, d = getParticlePos(2, 30, life) ParticleRadius(d/5) SpawnParticle(p, timetravelwind, life) end end end function getParticlePos(rMin, rMax, life) local camPos = GetCameraTransform().pos local p = rndVec(1) local d = rnd(rMin, rMax) p = VecScale(VecNormalize(p), d) p = VecAdd(p, camPos) p = VecSub(p, VecScale(timetravelwind, life * 0.5)) return p, d end function Travelling_Sparks_Appear(dt) if time_spark_appear > 0 then time_spark_appeartimer = time_spark_appeartimer + dt/2.8 if time_spark_appeartimer > 0.17 then SetLightIntensity(travel_spark[time_spark_appear], travel_lightintensity) travel_lightintensity = travel_lightintensity * 1.5 time_sparks.intensity[time_spark_appear] = true time_spark_appear = time_spark_appear - 1 time_spark_appeartimer = 0 end end end function Travelling_Sparks(dt) local position = GetTriggerTransform(time_sparks_trigger).pos position[2] = position[2] + 2 for i=1, #travel_spark do if time_sparks.intensity[i] then local light_shape = GetLightShape(travel_spark[i]) local light_body = GetShapeBody(light_shape) local light_shape_pos = GetBodyTransform(light_body).pos local dir = VecNormalize(VecSub(position, light_shape_pos)) local new_pos = Transform(VecAdd(GetBodyTransform(light_body).pos, VecScale(dir, 0.03 * time_sparks.acceleration[i]))) time_sparks.acceleration[i] = time_sparks.acceleration[i] + dt * 1.2 SetBodyTransform(light_body, new_pos) if IsBodyInTrigger(spakles_sound_trigger, light_body) and not time_sparks.sounded[i] then local rnd_snd = math.random(1,4) PlaySound(sounds_sparks[rnd_snd]) time_sparks.sounded[i] = true end end end end function MoveIt(speed) machine_transform.pos = VecAdd(machine_transform.pos, speed) SetBodyTransform(timemachine_body, machine_transform) end function draw(dt) if isShowLogo then UiPush() if LogoAlpha < 1 then LogoAlpha = LogoAlpha + dt else if LogoTransition < 190 then LogoTransition = LogoTransition + dt*450 end end UiTranslate(UiCenter(), UiMiddle()-240+1 + LogoTransition) UiAlign("center middle") UiColor(1,1,1,LogoAlpha) UiImage("images/tclogo.png") UiTranslate(0, -1 - LogoTransition) UiImage("images/teardownlogo.png") UiPop() end if pressedTimer > 0.0 then UiPush() UiFont("bold.ttf", 24) local alpha = math.min(pressedTimer, 0.5) / 0.5 UiColor(1,1,1, alpha) UiTranslate(UiWidth()/2, UiHeight() - 110) UiAlign("center") UiText(GetTranslatedStringByKey("UI_TEXT_PRESS_AGAIN")) UiPop() end end function Shockwave(direction, wavepos, imp) local muzzle = Vec(0,1.2,0) muzzle = VecAdd(muzzle,VecAdd(wavepos,VecScale(direction,4.9))) local mi = VecAdd(muzzle, Vec(-25/2, -1, -25/2)) local ma = VecAdd(muzzle, Vec(25/2, 2, 25/2)) QueryRequire("physical dynamic") QueryRejectBody(body) local bodies = QueryAabbBodies(mi, ma) --Loop through bodies and push them for i=1,#bodies do local b = bodies[i] --Compute body center point and distance local bmi, bma = GetBodyBounds(b) local bc = VecLerp(bmi, bma, 0.5) local dir = VecSub(bc, muzzle) local dist = VecLength(dir) dir = VecScale(dir, 1.0/dist) --Get body mass local mass = GetBodyMass(b) --Make sure direction is always pointing slightly upwards dir[2] = 0.5 dir = VecNormalize(dir) --Compute how much velocity to add local massScale = 1 - math.min(mass/1000, 1.0) local distScale = 1 - math.min(dist/25, 1.0) local add = VecScale(dir, imp * massScale * distScale) ApplyBodyImpulse(b, bc, add) end end function checkInputs() local key = InputLastPressedKey() if key ~= "" then return true else return InputPressed("interact") or InputPressed("usetool") or InputPressed("jump") or InputPressed("pause") end end