Facebook
From ss, 4 Months ago, written in Lua.
Embed
Download Paste or View Raw
Hits: 8148
  1. -- Whole thing is still at very early stage of development, a lot might and possibly
  2. -- will change. Currently whole thing is limited to sort of original drifting mode
  3. -- level. Observe things that happen, draw some extra UI, score user,
  4. -- decide when session ends.
  5.  
  6. -- This mode in particular is meant for Track Day with AI Flood on large tracks. Set
  7. -- AIs to draw some slow cars, get yourself that Red Bull monstrousity and try to
  8. -- score some points.
  9.  
  10. -- Key points for future:
  11. -- • Integration with CM’s Quick Drive section, with settings and everything;
  12. -- • These modes might need to be able to force certain CSP parameters — here, for example,
  13. --   it should be AI flood parameters;
  14. -- • To ensure competitiveness, they might also need to collect some data, verify integrity
  15. --   and possibly record short replays?
  16. -- • Remote future: control scene, AIs, spawn extra geometry and so on.
  17.  
  18. -- Event configuration:
  19. local requiredSpeed = 80
  20.  
  21. -- The event sent to the server, which ScoreTrackerPlugin will read
  22. -- To send this event: msg{ Score = myScore, Multiplier = myMultiplier, Car = ac.getCarName(0) }
  23. local msg = ac.OnlineEvent({
  24.     ac.StructItem.key("overtakeScoreEnd"),
  25.     Score = ac.StructItem.int64(),
  26.     Multiplier = ac.StructItem.int32(),
  27.     Car = ac.StructItem.string(64),
  28. })
  29.  
  30. -- This function is called before event activates. Once it returns true, it’ll run:
  31. function script.prepare(dt)
  32.     ac.debug("speed", ac.getCarState(1).speedKmh)
  33.     return ac.getCarState(1).speedKmh > 60
  34. end
  35.  
  36. -- Event state:
  37. local timePassed = 0
  38. local totalScore = 0
  39. local comboMeter = 1
  40. local comboColor = 0
  41. local highestScore = 0
  42. local dangerouslySlowTimer = 0
  43. local carsState = {}
  44. local wheelsWarningTimeout = 0
  45.  
  46. function script.update(dt)
  47.     if timePassed == 0 then
  48.         addMessage("Let’s go!", 0)
  49.     end
  50.  
  51.     local player = ac.getCarState(1)
  52.     if player.engineLifeLeft < 1 then
  53.         if totalScore > highestScore then
  54.             highestScore = math.floor(totalScore)
  55.             ac.sendChatMessage("scored " .. totalScore .. " points.")
  56.             msg{ Score = highestScore, Multiplier = comboMeter, Car = ac.getCarName(0) }
  57.         end
  58.         totalScore = 0
  59.         comboMeter = 1
  60.         return
  61.     end
  62.  
  63.     timePassed = timePassed + dt
  64.  
  65.     local comboFadingRate = 0.5 * math.lerp(1, 0.1, math.lerpInvSat(player.speedKmh, 80, 200)) + player.wheelsOutside
  66.     comboMeter = math.max(1, comboMeter - dt * comboFadingRate)
  67.  
  68.     local sim = ac.getSimState()
  69.     while sim.carsCount > #carsState do
  70.         carsState[#carsState + 1] = {}
  71.     end
  72.  
  73.     if wheelsWarningTimeout > 0 then
  74.         wheelsWarningTimeout = wheelsWarningTimeout - dt
  75.     elseif player.wheelsOutside > 0 then
  76.         if wheelsWarningTimeout == 0 then
  77.         end
  78.         addMessage("Car is outside", -1)
  79.         wheelsWarningTimeout = 60
  80.     end
  81.  
  82.     if player.speedKmh < requiredSpeed then
  83.         if dangerouslySlowTimer > 3 then
  84.             if totalScore > highestScore then
  85.                 highestScore = math.floor(totalScore)
  86.                 ac.sendChatMessage("scored " .. totalScore .. " points.")
  87.                 msg{ Score = highestScore, Multiplier = comboMeter, Car = ac.getCarName(0) }
  88.             end
  89.             totalScore = 0
  90.             comboMeter = 1
  91.         else
  92.             if dangerouslySlowTimer == 0 then
  93.                 addMessage("Too slow!", -1)
  94.             end
  95.         end
  96.         dangerouslySlowTimer = dangerouslySlowTimer + dt
  97.         comboMeter = 1
  98.         return
  99.     else
  100.         dangerouslySlowTimer = 0
  101.     end
  102.  
  103.     for i = 1, ac.getSimState().carsCount do
  104.         local car = ac.getCarState(i)
  105.         local state = carsState[i]
  106.  
  107.         if car.pos:closerToThan(player.pos, 10) then
  108.             local drivingAlong = math.dot(car.look, player.look) > 0.2
  109.             if not drivingAlong then
  110.                 state.drivingAlong = false
  111.  
  112.                 if not state.nearMiss and car.pos:closerToThan(player.pos, 3) then
  113.                     state.nearMiss = true
  114.  
  115.                     if car.pos:closerToThan(player.pos, 2.5) then
  116.                         comboMeter = comboMeter + 3
  117.                         addMessage("Very close near miss!", 1)
  118.                     else
  119.                         comboMeter = comboMeter + 1
  120.                         addMessage("Near miss: bonus combo", 0)
  121.                     end
  122.                 end
  123.             end
  124.  
  125.             if car.collidedWith == 0 then
  126.                 addMessage("Collision", -1)
  127.                 state.collided = true
  128.  
  129.                 if totalScore > highestScore then
  130.                     highestScore = math.floor(totalScore)
  131.                     ac.sendChatMessage("scored " .. totalScore .. " points.")
  132.                     msg{ Score = highestScore, Multiplier = comboMeter, Car = ac.getCarName(0) }
  133.                 end
  134.                 totalScore = 0
  135.                 comboMeter = 1
  136.             end
  137.  
  138.             if not state.overtaken and not state.collided and state.drivingAlong then
  139.                 local posDir = (car.pos - player.pos):normalize()
  140.                 local posDot = math.dot(posDir, car.look)
  141.                 state.maxPosDot = math.max(state.maxPosDot, posDot)
  142.                 if posDot < -0.5 and state.maxPosDot > 0.5 then
  143.                     totalScore = totalScore + math.ceil(10 * comboMeter)
  144.                     comboMeter = comboMeter + 1
  145.                     comboColor = comboColor + 90
  146.                     addMessage("Overtake", comboMeter > 20 and 1 or 0)
  147.                     state.overtaken = true
  148.                 end
  149.             end
  150.         else
  151.             state.maxPosDot = -1
  152.             state.overtaken = false
  153.             state.collided = false
  154.             state.drivingAlong = true
  155.             state.nearMiss = false
  156.         end
  157.     end
  158. end
  159.  
  160. -- For various reasons, this is the most questionable part, some UI. I don’t really like
  161. -- this way though. So, yeah, still thinking about the best way to do it.
  162. local messages = {}
  163. local glitter = {}
  164. local glitterCount = 0
  165.  
  166. function addMessage(text, mood)
  167.     for i = math.min(#messages + 1, 4), 2, -1 do
  168.         messages[i] = messages[i - 1]
  169.         messages[i].targetPos = i
  170.     end
  171.     messages[1] = {text = text, age = 0, targetPos = 1, currentPos = 1, mood = mood}
  172.     if mood == 1 then
  173.         for i = 1, 60 do
  174.             local dir = vec2(math.random() - 0.5, math.random() - 0.5)
  175.             glitterCount = glitterCount + 1
  176.             glitter[glitterCount] = {
  177.                 color = rgbm.new(hsv(math.random() * 360, 1, 1):rgb(), 1),
  178.                 pos = vec2(80, 140) + dir * vec2(40, 20),
  179.                 velocity = dir:normalize():scale(0.2 + math.random()),
  180.                 life = 0.5 + 0.5 * math.random()
  181.             }
  182.         end
  183.     end
  184. end
  185.  
  186. local function updateMessages(dt)
  187.     comboColor = comboColor + dt * 10 * comboMeter
  188.     if comboColor > 360 then
  189.         comboColor = comboColor - 360
  190.     end
  191.     for i = 1, #messages do
  192.         local m = messages[i]
  193.         m.age = m.age + dt
  194.         m.currentPos = math.applyLag(m.currentPos, m.targetPos, 0.8, dt)
  195.     end
  196.     for i = glitterCount, 1, -1 do
  197.         local g = glitter[i]
  198.         g.pos:add(g.velocity)
  199.         g.velocity.y = g.velocity.y + 0.02
  200.         g.life = g.life - dt
  201.         g.color.mult = math.saturate(g.life * 4)
  202.         if g.life < 0 then
  203.             if i < glitterCount then
  204.                 glitter[i] = glitter[glitterCount]
  205.             end
  206.             glitterCount = glitterCount - 1
  207.         end
  208.     end
  209.     if comboMeter > 10 and math.random() > 0.98 then
  210.         for i = 1, math.floor(comboMeter) do
  211.             local dir = vec2(math.random() - 0.5, math.random() - 0.5)
  212.             glitterCount = glitterCount + 1
  213.             glitter[glitterCount] = {
  214.                 color = rgbm.new(hsv(math.random() * 360, 1, 1):rgb(), 1),
  215.                 pos = vec2(195, 75) + dir * vec2(40, 20),
  216.                 velocity = dir:normalize():scale(0.2 + math.random()),
  217.                 life = 0.5 + 0.5 * math.random()
  218.             }
  219.         end
  220.     end
  221. end
  222.  
  223. local speedWarning = 0
  224.     function script.drawUI()
  225.         local uiState = ac.getUiState()
  226.         updateMessages(uiState.dt)
  227.  
  228.         local speedRelative = math.saturate(math.floor(ac.getCarState(1).speedKmh) / requiredSpeed)
  229.         speedWarning = math.applyLag(speedWarning, speedRelative < 1 and 1 or 0, 0.5, uiState.dt)
  230.  
  231.         local colorDark = rgbm(0.4, 0.4, 0.4, 1)
  232.         local colorGrey = rgbm(0.7, 0.7, 0.7, 1)
  233.         local colorAccent = rgbm.new(hsv(speedRelative * 120, 1, 1):rgb(), 1)
  234.         local colorCombo =
  235.             rgbm.new(hsv(comboColor, math.saturate(comboMeter / 10), 1):rgb(), math.saturate(comboMeter / 4))
  236.  
  237.         local function speedMeter(ref)
  238.             ui.drawRectFilled(ref + vec2(0, -4), ref + vec2(180, 5), colorDark, 1)
  239.             ui.drawLine(ref + vec2(0, -4), ref + vec2(0, 4), colorGrey, 1)
  240.             ui.drawLine(ref + vec2(requiredSpeed, -4), ref + vec2(requiredSpeed, 4), colorGrey, 1)
  241.  
  242.             local speed = math.min(ac.getCarState(1).speedKmh, 180)
  243.             if speed > 1 then
  244.                 ui.drawLine(ref + vec2(0, 0), ref + vec2(speed, 0), colorAccent, 4)
  245.             end
  246.         end
  247.  
  248.         ui.beginTransparentWindow("overtakeScore", vec2(100, 100), vec2(400 * 0.5, 400 * 0.5))
  249.         ui.beginOutline()
  250.  
  251.         ui.pushStyleVar(ui.StyleVar.Alpha, 1 - speedWarning)
  252.         ui.pushFont(ui.Font.Main)
  253.         ui.text("Highest Score: " .. highestScore .. " pts")
  254.         ui.popFont()
  255.         ui.popStyleVar()
  256.  
  257.         ui.pushFont(ui.Font.Title)
  258.         ui.text(totalScore .. " pts")
  259.         ui.sameLine(0, 20)
  260.         ui.beginRotation()
  261.         ui.textColored(math.ceil(comboMeter * 10) / 10 .. "x", colorCombo)
  262.         if comboMeter > 20 then
  263.             ui.endRotation(math.sin(comboMeter / 180 * 3141.5) * 3 * math.lerpInvSat(comboMeter, 20, 30) + 90)
  264.         end
  265.         ui.popFont()
  266.         ui.endOutline(rgbm(0, 0, 0, 0.3))
  267.  
  268.         ui.offsetCursorY(20)
  269.         ui.pushFont(ui.Font.Main)
  270.         local startPos = ui.getCursor()
  271.         for i = 1, #messages do
  272.             local m = messages[i]
  273.             local f = math.saturate(4 - m.currentPos) * math.saturate(8 - m.age)
  274.             ui.setCursor(startPos + vec2(20 * 0.5 + math.saturate(1 - m.age * 10) ^ 2 * 50, (m.currentPos - 1) * 15))
  275.             ui.textColored(
  276.                 m.text,
  277.                 m.mood == 1 and rgbm(0, 1, 0, f) or m.mood == -1 and rgbm(1, 0, 0, f) or rgbm(1, 1, 1, f)
  278.             )
  279.         end
  280.         for i = 1, glitterCount do
  281.             local g = glitter[i]
  282.             if g ~= nil then
  283.                 ui.drawLine(g.pos, g.pos + g.velocity * 4, g.color, 2)
  284.             end
  285.         end
  286.         ui.popFont()
  287.         ui.setCursor(startPos + vec2(0, 4 * 30))
  288.  
  289.         ui.pushStyleVar(ui.StyleVar.Alpha, speedWarning)
  290.         ui.setCursorY(0)
  291.         ui.pushFont(ui.Font.Main)
  292.         ui.textColored("Keep speed above " .. requiredSpeed .. " km/h:", colorAccent)
  293.         speedMeter(ui.getCursor() + vec2(-9 * 0.5, 4 * 0.2))
  294.  
  295.         ui.popFont()
  296.         ui.popStyleVar()
  297.  
  298.         ui.endTransparentWindow()
  299.     end
  300.