#include "minimap.lua" #include "ui/ui_extensions.lua" function init() carNames = {GetTranslatedStringByKey("CARNAMES_DRAB_75"), GetTranslatedStringByKey("CARNAMES_CAYUSE_550"), GetTranslatedStringByKey("CARNAMES_TASKMASTER_4WD"), GetTranslatedStringByKey("CARNAMES_EURUS_IV_XXF"), GetTranslatedStringByKey("CARNAMES_CROWNZYGOT_XVC-ALPHA")} sndConfirm = LoadSound("MOD/sounds/confirm.ogg") sndReject = LoadSound("MOD/sounds/reject.ogg") sndWarning = LoadSound("MOD/sounds/warning-beep.ogg") sndPickup = LoadSound("MOD/sounds/pickup.ogg") sndRocket = LoadLoop("MOD/sounds/rocket.ogg") sndFail = LoadSound("MOD/sounds/fail.ogg") sndWin = LoadSound("MOD/sounds/win.ogg") sndReady = LoadSound("MOD/sounds/ready.ogg") sndStart = LoadSound("MOD/sounds/start.ogg") lastFrameInTrigger = 0 totalPassed = 0 wakeup = 0 raceTime = 20 startTimer = 5 startTimerInt = math.floor(startTimer) warningColorMod = 1 gameOver = false gameWin = false endFade = 0 endFadePosition = nil fixedCamTransform = nil power = 0.1 -- Thrust power nitro = 0 nitroInUse = false nitroShow = 0 screenBlur = 0 sceneBody = FindBody("scenebody",true) flags = FindBodies("flag",true) gateState = {} triggers = FindTriggers("gate",true) for i=1,#triggers do gateState[tonumber(GetTagValue(triggers[i], "gate"))] = 0 end powerups = FindBodies("powerup", true) powerUpIndicator = 1 powerUpIndicatorValues = {0,0,0} powerUpIndicatorTimers = {0,0,0} powerUpIndicatorYPos = {0,0,0} powerUpIndicatorColors = {{0,0,0},{0,0,0},{0,0,0}} powerupTimeout = 2 spacebarTimeout = 0 isDead = false roundCar = tonumber(GetTagValue(GetPlayerVehicle(),"car")) startCar = GetPlayerVehicle() if roundCar and HasKey("savegame.mod.besttime.car" .. roundCar) then firstRoundWithCar = false savedBest = GetFloat("savegame.mod.besttime.car" .. roundCar) else firstRoundWithCar = true savedBest = 0 end minimap_init() end function tick(dt) if wakeup < 2 then if wakeup == 0 then for i=1, #flags do SetBodyVelocity(flags[i], Vec(.1,.1,.1)) end end wakeup = wakeup + 1 return end local v = GetPlayerVehicle() -- ################################### -- # -- # Powerups -- # -- ################################### if not gameOver and not gameWin then for i = 1, #powerups do if startTimer <= 1 then -- No pickups unless game is started if not HasTag(powerups[i],"spent") then local pickupHit = false if v > 0 then QueryRejectBody(sceneBody) local powerupPos = VecAdd(GetBodyTransform(powerups[i]).pos,Vec(0,0.8,0)) -- Raise powerup origin from bottom to middle of powerup local hit, point, _, shape = QueryClosestPoint(powerupPos, 2) if hit and VecLength(VecSub(powerupPos, point)) < 1.2 then local shapesVehicleBody = GetBodyVehicle(GetShapeBody(shape)) if shapesVehicleBody == v then pickupHit = true end end else if VecLength(VecSub(GetPlayerTransform().pos, GetBodyTransform(powerups[i]).pos)) < 1.5 then pickupHit = true end end if pickupHit then local value = GetTagValue(powerups[i],"value") if GetTagValue(powerups[i],"powerup") == "boost" then if nitro <= 0 and v > 0 then SetValue("nitroShow", 1, "easeout", 0.5) end nitro = nitro + tonumber(value) PlaySound(sndPickup) local indicatorColor = {0,1,0} addPowerupIndicator(GetTranslatedStringByKey("POWERUP_NITRO," .. value), indicatorColor) end if GetTagValue(powerups[i],"powerup") == "time" then raceTime = raceTime + tonumber(value) PlaySound(sndPickup) local indicatorColor = {0,1,0} addPowerupIndicator(GetTranslatedStringByKey("POWERUP_TIME," .. value), indicatorColor) end SetTag(powerups[i],"spent") local vt = GetBodyTransform(powerups[i]) vt.pos[2] = vt.pos[2] - 25 SetBodyTransform(powerups[i],vt) end end end -- Rotate powerups local powerupT = GetBodyTransform(powerups[i]) powerupT.rot = QuatRotateQuat(powerupT.rot, QuatEuler(0,180 * dt,0)) SetBodyTransform(powerups[i],powerupT) end end if nitro > 1000 then nitro = 1000 end -- ################################### -- # -- # Nitro -- # More or less copied from Dennis Vehicle Booster mod -- # -- ################################### if not gameOver and not gameWin then local cur_input = LastInputDevice() if v > 0 and GetFloat("game.vehicle.health") > 0 then local vehicle = v -- v is redefined down in the code and I don't want to rename a lot of variables in lua code if nitro > 0 and nitroShow == 0 then SetValue("nitroShow", 1, "easeout", 0.5) end if nitro > 0 then SetTag(vehicle, "CustomVehicleAction", "loc@UI_NITRO_BOOST") if cur_input == UI_DEVICE_GAMEPAD then RemoveTag(vehicle, "CustomVehicleActionKey") else SetTag(vehicle, "CustomVehicleActionKey", "loc@RMB") end end --Compute tail point and back direction on vehicle in world space local t = GetVehicleTransform(v) local p = TransformToParentPoint(t, Vec(0, 0.5, 3)) local d = TransformToParentVec(t, Vec(0, 0, 1)) if ((cur_input == UI_DEVICE_MOUSE and InputDown("rmb")) or (cur_input ~= UI_DEVICE_MOUSE and InputDown("vehicleaction"))) and nitro > 0 then --Push the vehicle forwards local b = GetVehicleBody(v) v = GetBodyVelocity(b) v = VecAdd(v, VecScale(d, -power)) SetBodyVelocity(b, v) --Engaged particle effects local pvel = VecScale(v, 0.7) --SpawnParticle("fire", p, VecAdd(pvel, VecScale(d, 2)), 1.0, 0.5) local size = 1.0 ParticleReset() ParticleType("fire") ParticleDrag(0.001) ParticleGravity(0, 1) ParticleColor(1, 0.9, 0.8, 1, 0.5, 0.4) ParticleAlpha(0.5, 0.0) ParticleRadius(size * 0.1, size) ParticleEmissive(10, 0) SpawnParticle(p, VecAdd(pvel, VecScale(d, 2)), 0.5) --SpawnParticle("smoke", p, VecAdd(pvel, VecScale(d, 5)), 1, 3.0) size = 1.0 ParticleReset() ParticleType("smoke") ParticleDrag(0) ParticleGravity(-2, -4) ParticleColor(1, 1, 1) ParticleAlpha(0.5, 0.0, "linear", 0.05) ParticleRadius(0.5 * size, size) ParticleCollide(0, 1) SpawnParticle(p, VecAdd(pvel, VecScale(d, 5)), 3.0) --SpawnParticle("darksmoke", p, VecAdd(pvel, VecScale(d, 10)), 1.5, 1.0) size = 1.5 ParticleReset() ParticleType("darksmoke") ParticleDrag(0.001) ParticleGravity(0, -1) ParticleColor(0, 0, 0) ParticleAlpha(0.5, 0.0, "linear", 0.05) ParticleRadius(0.5 * size, size) ParticleCollide(0, 1) SpawnParticle(p, VecAdd(pvel, VecScale(d, 10)), 1.0) --Play sound PlayLoop(sndRocket, t.pos) -- Decrease nitro nitro = nitro - 1 if nitro <= 0 then RemoveTag(vehicle, "CustomVehicleAction") SetValue("nitroShow", 0, "easein", 0.5) end if not nitroInUse then SetValue("screenBlur", 0.5, "cosine", 3) end nitroInUse = true else if nitro > 0 then --Idle particle effect --SpawnParticle("fire", p, VecScale(d, 1), 0.3, 0.1) local size = 0.3 ParticleReset() ParticleType("fire") ParticleDrag(0.001) ParticleGravity(0, 1) ParticleColor(1, 0.9, 0.8, 1, 0.5, 0.4) ParticleAlpha(0.5, 0.0) ParticleRadius(size * 0.1, size) ParticleEmissive(10, 0) SpawnParticle(p, VecScale(d, 1), 0.1) --SpawnParticle("smoke", p, VecScale(d, 2), 0.5, 1.0) size = 0.5 ParticleReset() ParticleType("smoke") ParticleDrag(0) ParticleGravity(-2, -4) ParticleColor(1, 1, 1) ParticleAlpha(0.5, 0.0, "linear", 0.05) ParticleRadius(0.5 * size, size) ParticleCollide(0, 1) SpawnParticle(p, VecScale(d, 2), 1.0) end if nitroInUse then SetValue("screenBlur", 0, "cosine", 1) end nitroInUse = false end else if nitroShow == 1 then SetValue("nitroShow", 0, "easein", 0.5) end if nitroInUse then SetValue("screenBlur", 0, "cosine", 1) end nitroInUse = false end else if nitroShow == 1 then SetValue("nitroShow", 0, "easein", 0.5) end end -- ################################### -- # -- # Racing gates -- # -- ################################### local inTrigger = 0 if not gameOver and not gameWin then --if v > 0 then for i=1,#triggers do if IsVehicleInTrigger(triggers[i], v) or IsVehicleInTrigger(triggers[i], startCar) then if gateState[i] == 0 then if i == #triggers and totalPassed == 0 then --We just passed the finish gate, but now as the start line. else if i == 1 then gateState[i] = 1 SetTag(triggers[i],"uipos","green") totalPassed = totalPassed + 1 raceTime = raceTime + 15 local indicatorColor = {0,1,0} addPowerupIndicator(GetTranslatedStringByKey("CHECKPOINT") .. " " .. GetTranslatedStringByKey("POWERUP_TIME,15") ,indicatorColor) if totalPassed < #triggers then PlaySound(sndConfirm) end elseif i > 1 and gateState[i-1] == 1 then if i == #triggers and (not IsVehicleInTrigger(triggers[i], startCar) and v ~= startCar) then if lastFrameInTrigger == 0 then PlaySound(sndReject) local indicatorColor = {1,0,0} addPowerupIndicator(GetTranslatedStringByKey("YOU_MUST"),indicatorColor) end else gateState[i] = 1 SetTag(triggers[i],"uipos","green") totalPassed = totalPassed + 1 if i < #triggers then raceTime = raceTime + 15 end if totalPassed < #triggers then local indicatorColor = {0,1,0} addPowerupIndicator(GetTranslatedStringByKey("CHECKPOINT") .. " " .. GetTranslatedStringByKey("POWERUP_TIME,15") ,indicatorColor) PlaySound(sndConfirm) end end else if lastFrameInTrigger == 0 then PlaySound(sndReject) local indicatorColor = {1,0,0} addPowerupIndicator(GetTranslatedStringByKey("WRONG_CHECKPOINT"),indicatorColor) end end end end inTrigger = inTrigger + 1 end end --end end -- ################################### -- # -- # Game WIN -- # -- ################################### if (totalPassed == #triggers and not gameWin) then gameWin = true fixedCamTransform = GetCameraTransform() if v > 0 then endFadePosition = TransformToParentPoint(GetVehicleTransform(v), Vec(20, 30, 25)) else endFadePosition = TransformToParentPoint(GetPlayerTransform(), Vec(20, 30, 25)) end SetValue("endFade",1,"cosine",3) fixedCamTransform = GetCameraTransform() spacebarTimeout = GetTime() StopMusic() PlaySound(sndWin) end lastFrameInTrigger = inTrigger minimap_tick(dt) -- ################################### -- # -- # Game FAIL -- # -- ################################### if (raceTime <= 0 or GetPlayerHealth() <= 0)and not gameOver then gameOver = true if GetPlayerHealth() <= 0 and not (gameOver or gameWin) then isDead = true end fixedCamTransform = GetCameraTransform() if v > 0 then endFadePosition = TransformToParentPoint(GetVehicleTransform(v), Vec(20, 0, 25)) endFadePosition[2] = GetVehicleTransform(v).pos[2] + 30 else endFadePosition = TransformToParentPoint(GetPlayerTransform(), Vec(20, 0, 25)) endFadePosition[2] = GetPlayerTransform().pos[2] + 30 end SetValue("endFade",1,"cosine",3) spacebarTimeout = GetTime() StopMusic() PlaySound(sndFail) Explosion(GetPlayerPos(), 2) end local endPos, endRot if gameOver or gameWin then if v > 0 then endPos = VecLerp(fixedCamTransform.pos,endFadePosition,endFade) endRot = QuatSlerp(fixedCamTransform.rot,QuatLookAt(endPos, GetVehicleTransform(v).pos),endFade) else endPos = VecLerp(fixedCamTransform.pos,endFadePosition,endFade) endRot = QuatSlerp(fixedCamTransform.rot,QuatLookAt(endPos, GetPlayerTransform().pos),endFade) end SetCameraTransform(Transform(endPos,endRot)) end if not gameOver and not gameWin and startTimer <= 1 then raceTime = raceTime - dt end if startTimer > 0 then startTimer = startTimer - dt end for i = 1, #powerUpIndicatorValues do if powerUpIndicatorTimers[i] >= 0 then powerUpIndicatorTimers[i] = powerUpIndicatorTimers[i] - dt end end end function addPowerupIndicator(s,c) powerUpIndicatorValues[powerUpIndicator] = s powerUpIndicatorTimers[powerUpIndicator] = powerupTimeout powerUpIndicatorYPos[powerUpIndicator] = (UiHeight()/2) powerUpIndicatorColors[powerUpIndicator][1] = c[1] powerUpIndicatorColors[powerUpIndicator][2] = c[2] powerUpIndicatorColors[powerUpIndicator][3] = c[3] powerUpIndicator = powerUpIndicator + 1 if powerUpIndicator > #powerUpIndicatorValues then powerUpIndicator = 1 end end function update(dt) if startTimer > 0 and math.floor(startTimer) < startTimerInt then startTimerInt = math.floor(startTimer) if math.floor(startTimer) > 0 then PlaySound(sndReady) else PlaySound(sndStart) PlayMusic("MOD/sounds/drive_to_survive.ogg") end end if raceTime <= 5 and not (gameOver or gameWin) then if raceTime + dt*1.5 >= math.ceil(raceTime) then warningColorMod = 1 SetValue("warningColorMod", 0, "linear", 0.5) PlaySound(sndWarning) end else warningColorMod = 1 end minimap_update() end function draw(dt) if LastInputDevice() ~= UI_DEVICE_MOUSE then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end if startTimer > 0 then drawStart() -- Don't let the player drive unitl the 1 second left mark of the intro (When it says GetTranslatedStringByKey("DRIVE")) if startTimer > 1 then UiMakeInteractive() end else end if not gameOver and not gameWin then UiPush() UiFont("bold.ttf", 64) UiTranslate(UiCenter(), 60) UiAlign("center") UiTextShadow(0,0,0,0.5,1) local v = string.format("%02d", raceTime) if raceTime > 0 then v = v .. string.sub(string.format("%.2f", raceTime - v),2) else v = v .. string.sub(string.format("%.2f", raceTime - v),3) end if not gameOver then if not gameWin then UiColor(1,warningColorMod,warningColorMod) UiText(v) else UiColor(0,1,0) UiText(v) UiTranslate(0, 60) UiText("loc@UI_TEXT_NICE_DRIVING") end else UiColor(1,0,0) UiText("loc@UI_TEXT_GAME_OVER") end UiPop() UiBlur(screenBlur) UiPush() UiTranslate(0, UiHeight() - 220 * nitroShow) UiImage("MOD/images/nitro_tank.png") UiAlign("center middle") UiTranslate(107, 60) UiPush() UiTranslate(5, 5) UiRotate(-270 * (nitro/1000)) UiImage("MOD/images/meter_hand_shadow.png") UiPop() UiRotate(-270 * (nitro/1000)) UiImage("MOD/images/meter_hand.png") UiPop() -- Nitro boost text UiPush() UiTranslate(0, UiHeight() - 220 * nitroShow) UiAlign("middle center") UiFont("bold.ttf", 28) UiTranslate(104, 192) UiPush() local w, _ = UiGetTextSize(GetTranslatedStringByKey("UI_NITRO_BOOST")) local scale = 140 / w UiScale(scale) UiText("loc@UI_NITRO_BOOST") UiPop() UiPop() minimap_draw() end drawPowerupIndicators(dt) if gameOver then UiMakeInteractive() drawGameOver() elseif gameWin then UiMakeInteractive() drawGameWin() end end function drawStart() UiPush() UiTranslate(UiWidth()/2, UiHeight()/2) UiAlign("center middle") UiScale(startTimer % 1 * 10) UiFont("bold.ttf", 128) local alpha = startTimer % 1 UiColor(1,0,0, alpha) UiTextShadow(0,0,0,alpha/2,2) if math.floor(startTimer) > 0 then UiText(tostring(math.floor(startTimer))) else UiText("loc@UI_TEXT_DRIVE") end UiPop() end function drawPowerupIndicators(dt) for i = 1, #powerUpIndicatorValues do if powerUpIndicatorTimers[i] >= 0 then local previousIndex = i - 1 local nextIndex = i + 1 if previousIndex < 1 then previousIndex = #powerUpIndicatorValues end if nextIndex > #powerUpIndicatorValues then nextIndex = 1 end UiPush() UiAlign("center bottom") UiFont("bold.ttf", 48) powerUpIndicatorYPos[i] = powerUpIndicatorYPos[i] - 150 * dt UiTranslate(UiWidth()/2, powerUpIndicatorYPos[i]) local alpha = (powerUpIndicatorTimers[i]) if alpha > 1 then alpha = 1 end UiColor(powerUpIndicatorColors[i][1],powerUpIndicatorColors[i][2],powerUpIndicatorColors[i][3], alpha) UiTextShadow(0,0,0,alpha/2,2) UiText(powerUpIndicatorValues[i]) UiPop() end end end function drawGameOver() local winTitle = "" local winMessage = "" if isDead then winTitle = GetTranslatedStringByKey("WINTITLE_OH_NO!") winMessage = GetTranslatedStringByKey("WINMESSAGE_SORRY_BUT") else if not firstRoundWithCar then winTitle = GetTranslatedStringByKey("WINTITLE_CATASTROPHIC_FAILURE!") winMessage = GetTranslatedStringByKey("WINMESSAGE_I'M_SORRY") else winTitle = GetTranslatedStringByKey("WINTITLE_NICE_ATTEMPT_BUT_NO_CIGAR") winMessage = GetTranslatedStringByKey("WINMESSAGE_THE_FIRST") end end UiPush() UiTranslate(UiCenter(), UiHeight()-320) UiAlign("center") UiColor(1, 1, 1) local boxWidth = 480 if LastInputDevice() ~= UI_DEVICE_MOUSE then boxWidth = 650 end UiImageBox("MOD/images/sign-02.png", boxWidth, 260, 50, 50) UiFont("bold.ttf", 24) UiPush() local _, h1 = UiGetTextSize(winTitle) UiTranslate(0,30 + h1) UiText(winTitle) UiFont("regular.ttf", 24) _, h1 = UiGetTextSize(winMessage) UiTranslate(0, h1) UiWordWrap(boxWidth - 80) UiText(winMessage) UiPop() if GetTime() > spacebarTimeout + 2 then UiPush() UiFont("regular.ttf", 24) UiWordWrap(400) local _, h = UiGetTextSize(GetTranslatedStringByKey("loc@UI_TXT_RETRY_OR_SELECT,R,SPACE")) if h > 27 then -- UiGetTextSize is bugged h = 54 end if LastInputDevice() == UI_DEVICE_MOUSE then UiTranslate(0, 260 - h) UiText("loc@UI_TXT_RETRY_OR_SELECT,R,SPACE") else UiTranslate(0, 200) UiDrawHintsCentered({ {ico = "[[menu:menu_accept;iconsize=42,42]]", txt="loc@DRIVE_AGAIN"}, {ico = "[[menu:menu_cancel;iconsize=42,42]]", txt="loc@DRIVE_SELECT"} }) end UiPop() if InputPressed("space") or InputPressed("lmb") or InputPressed("menu_cancel") then StartLevel("", "MOD/main.xml","nointro") end if InputPressed("r") or InputPressed("rmb") or InputPressed("menu_accept") then StartLevel("", "MOD/track.xml","car" .. roundCar) end end UiPop() end function drawGameWin() local winTitle = "" local winMessage = "" if roundCar ~= nil then if not firstRoundWithCar then if raceTime > savedBest then winTitle = GetTranslatedStringByKey("WINTITLE_GREAT_WORK!_A_NEW_RECORD!") winMessage = GetTranslatedStringByKey("WINMESSAGE_YOU_BEAT") .. " " .. carNames[roundCar] .. " by " .. (string.format("%.2f", raceTime - savedBest)) .. " seconds!" SetFloat("savegame.mod.besttime.car" .. roundCar,raceTime) else winTitle = GetTranslatedStringByKey("WINTITLE_NICE_DRIVING!") winMessage = GetTranslatedStringByKey("WINMESSAGE_UNFORTUNATELY_SOMEONE") .. " " .. (string.format("%.2f", savedBest - raceTime)) .. " " .. GetTranslatedStringByKey("WINMESSAGE_BEAT_BY_SECONDS") .." " .. carNames[roundCar] end else winTitle = GetTranslatedStringByKey("WINTITLE_NICE_DONE!") winMessage = GetTranslatedStringByKey("WINMESSAGE_YOU_SET") .. " " .. carNames[roundCar] SetFloat("savegame.mod.besttime.car" .. roundCar,raceTime) end else winTitle = GetTranslatedStringByKey("WINTITLE_TESTING?") winMessage = GetTranslatedStringByKey("WINMESSAGE_SEEMS_LIKE") end UiPush() UiTranslate(UiCenter(), UiHeight()/2) UiAlign("center middle") UiFont("bold.ttf", 48) UiScale(endFade*6) UiColor(1, 1, 1) UiTextShadow(0,0,0,0.5,2) UiText(string.format("%.2f", raceTime)) UiPop() UiPush() UiTranslate(UiCenter(), UiHeight()-320) UiAlign("center") UiColor(1, 1, 1) local boxWidth = 480 if LastInputDevice() ~= UI_DEVICE_MOUSE then boxWidth = 650 end UiImageBox("MOD/images/sign-02.png", boxWidth, 260, 50, 50) UiFont("bold.ttf", 24) UiPush() local _, h1 = UiGetTextSize(winTitle) UiTranslate(0,30 + h1) UiText(winTitle) UiFont("regular.ttf", 24) _, h1 = UiGetTextSize(winMessage) UiTranslate(0, h1) UiWordWrap(boxWidth - 80) UiText(winMessage) UiPop() if GetTime() > spacebarTimeout + 2 then UiPush() UiFont("regular.ttf", 24) UiWordWrap(400) local _, h = UiGetTextSize(GetTranslatedStringByKey("loc@UI_TXT_RETRY_OR_SELECT,R,SPACE")) if h > 27 then -- UiGetTextSize is bugged h = 54 end if LastInputDevice() == UI_DEVICE_MOUSE then UiTranslate(0, 260 - h) UiText("loc@UI_TXT_RETRY_OR_SELECT,R,SPACE") else UiTranslate(0, 200) UiDrawHintsCentered({ {ico = "[[menu:menu_accept;iconsize=42,42]]", txt="loc@DRIVE_AGAIN"}, {ico = "[[menu:menu_cancel;iconsize=42,42]]", txt="loc@DRIVE_SELECT"} }) end if InputPressed("space") or InputPressed("lmb") or InputPressed("menu_cancel") then StartLevel("", "MOD/main.xml","nointro") end if InputPressed("r") or InputPressed("rmb") or InputPressed("menu_accept") then StartLevel("", "MOD/track.xml","car" .. roundCar) end UiPop() end UiPop() end