0,4($>S}WupUʁPi Y;]AsVl3 0.45 then return nil end local props = {} local rnd2 = (seed * 12.9898) % 1 props.h_scale = 0.7 + rnd2 local rnd3 = (seed * 78.233) % 1 props.dist_mult = 1.0 + (rnd3 * 2.5) return props end -- === AUDIO HELPER === -- Converts a number (0-95) into a note string like "C#4" -- This fixes the "invalid note" error function get_note_str(i) i = math.floor(i) -- Force integer if i < 0 then i = 0 end if i > 95 then i = 95 end local oct = math.floor(i / 12) local n = i % 12 local names = {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"} return names[n+1] .. oct end -- === AUDIO ENGINE === function update_sfx() local ranges = { {0.0, GEAR_LIMITS[1]}, {GEAR_LIMITS[1], GEAR_LIMITS[2]}, {GEAR_LIMITS[2], GEAR_LIMITS[3]}, {GEAR_LIMITS[3], maxspeed} } local r = ranges[gear] local current_range_width = r[2] - r[1] -- Calculate RPM (0.0 to 1.0) local rpm = (speed - r[1]) / current_range_width -- Add a little idle RPM if stopped if speed == 0 then rpm = 0.1 end -- Store for Gauge rpm_global = rpm -- Calculate Pitch local pitch_val = 20 + (rpm * 18) local note_str = get_note_str(pitch_val) if speed > 0.01 or btn(0) then -- Using string note to prevent errors sfx(0, note_str, -1, 3) else sfx(-1, "C-0", 0, 3) end end function TIC() -- 1. CONTROLS & PHYSICS local up_limits = GEAR_LIMITS local down_limits = {0.6, 1.3, 2.0} -- Auto-Shift if shift_timer == 0 then if gear == 1 and speed > up_limits[1] then gear=2; shift_timer=20 elseif gear == 2 and speed > up_limits[2] then gear=3; shift_timer=20 elseif gear == 3 and speed > up_limits[3] then gear=4; shift_timer=15 end end if gear == 4 and speed < down_limits[3] then gear = 3 end if gear == 3 and speed < down_limits[2] then gear = 2 end if gear == 2 and speed < down_limits[1] then gear = 1 end -- Input if btn(0) and speed < maxspeed then if shift_timer > 0 then shift_timer = shift_timer - 1 speed = speed * 0.99 else speed = speed + gear_accel[gear] fuel = fuel - 0.0005 end else if shift_timer > 0 then shift_timer = shift_timer - 1 end end if btn(1) and speed > 0 then speed = speed - 0.05 end if not btn(0) and speed > 0 then speed = speed - 0.01 end if speed < 0 then speed = 0 end if fuel < 0 then fuel = 1 end update_sfx() -- 2. POSITIONING local vibration = 0 if speed > 0 then vibration = math.sin(frame * (0.5 + speed)) * (speed * 0.6) end x = 150 + vibration -- 3. TRACK LOGIC if speed > 0 then track_timer = track_timer + 1 if track_timer > 300 then track_timer = 0 curve_index = curve_index + 1 if curve_index > #track_list then curve_index = 1 end target_k = track_list[curve_index] end end if k < target_k then k = k + 1 end if k > target_k then k = k - 1 end -- 4. DRAWING SCENE cls(8) -- Sky for i,s in pairs(stars) do local sx = (s.x + k/5) % 240 pix(sx, s.y, 12) end circ(190 + (k/8), 30, 8, 12) circ(186 + (k/8), 30, 6, 8) rect(0, 136-h, 240, h, 1) -- Road for i=0, q do local p = (i/q)^4 local sy = 136 - h + p * h local curve_x = math.cos(i * (90/q) / 180 * pi) * k local spread = ((136-h)/17) local center_x = 120 + curve_x + (120 - x) * p * spread local w = 10 + (600 * p) local col = 0 if i > 60 then col = 14 end if i > 130 then col = 13 end local stripe_z = math.floor(i - t) if col > 0 then line(center_x - w/2, sy, center_x + w/2, sy, col) if (stripe_z % 80 < 30) and i > 80 then local lw = w * 0.06; if lw < 1 then lw = 1 end rect(center_x - lw/2, sy, lw, 1, 12) end if i > 60 then local edge_col = 12 if (stripe_z % 20 < 10) then edge_col = 13 end line(center_x - w/2 - w*0.1, sy, center_x - w/2, sy, edge_col) line(center_x + w/2, sy, center_x + w/2.2 + w*0.1, sy, edge_col) end end local tree_z = math.floor(stripe_z) if tree_z % 3 == 0 then local props = get_tree_props(tree_z) if props and (col > 0 or i > 20) and sy < 140 then local tree_w = w * 1.2 local tree_h = tree_w * 3.0 * props.h_scale local offset_val = (w * 0.6) + (tree_w * props.dist_mult) local tc = 0 if i > 20 then tc = 7 end if i > 100 then tc = 6 end if tc > 0 then local tx = center_x - offset_val tri(tx, sy-tree_h, tx-tree_w/2, sy, tx+tree_w/2, sy, tc) local tx2 = center_x + offset_val tri(tx2, sy-tree_h, tx2-tree_w/2, sy, tx2+tree_w/2, sy, tc) end end end end -- 5. DASHBOARD local c_dash = 0 rect(0, 100, 240, 60, c_dash) -- Mirror rect(98, 22, 44, 20, c_dash) rect(99, 23, 42, 18, 14) local mx, my, mw, mh = 100, 24, 40, 16 clip(mx, my, mw, mh) rect(mx, my, mw, mh, 8) rect(mx, my+mh/2, mw, mh/2, 1) for j=0, 40 do local p = (j/40)^2 local sy = (my + mh/2) + p * (mh/2) local mr_w = 2 + (32 * p) local m_curve = k * 0.5 local cx = math.cos(j * (90/40) / 180 * pi) * m_curve local center = mx + mw/2 + cx local stripe_z = math.floor(j + t/2) if sy < (my + mh) then line(center - mr_w/2, sy, center + mr_w/2, sy, 13) if (stripe_z % 80 < 30) then pix(center, sy, 12) end if math.floor(stripe_z) % 3 == 0 then local props = get_tree_props(math.floor(stripe_z)) if props then local tree_w = mr_w * 1.5 local tree_h = tree_w * 3.0 * props.h_scale local offset_val = (mr_w * 0.6) + (tree_w * props.dist_mult) local tc = 0 if j > 5 then tc = 7 end if j > 30 then tc = 6 end if tc > 0 then local tx = center - offset_val tri(tx, sy-tree_h, tx-tree_w/2, sy, tx+tree_w/2, sy, tc) local tx2 = center + offset_val tri(tx2, sy-tree_h, tx2-tree_w/2, sy, tx2+tree_w/2, sy, tc) end end end end end clip() rect(119, 14, 2, 10, c_dash) -- === GAUGES LAYER === -- (Drawn before the wheel so they are behind it) local wheel_x, wheel_y, wheel_rad = 56, 126, 44 -- === STEERING WHEEL LAYER === -- Drawn LAST to cover the gauges properly local angle = (k / 200) * 1.5 circ(wheel_x, wheel_y, wheel_rad, 15) circ(wheel_x, wheel_y, wheel_rad-2, 14) circ(wheel_x, wheel_y, wheel_rad-5, c_dash) -- 3. RPM Gauge (New! Right Side) local rpm_x, rpm_y, rpm_r = 66, 108, 15 -- Move it slightly right to balance Speedometer (42 vs 70 relative to 56) circ(rpm_x, rpm_y, rpm_r, 14) circ(rpm_x, rpm_y, rpm_r-2, 0) -- RPM Ticks (Redline at end) for i=0, 6 do local ta = 3.14 + (i/6 * 3.14) local tick_col = 15 if i >= 5 then tick_col = 6 end -- Redline line(rpm_x + math.cos(ta) * 13, rpm_y + math.sin(ta) * 13, rpm_x + math.cos(ta) * 10, rpm_y + math.sin(ta) * 10, tick_col) end -- RPM Needle local rpm_disp = rpm_global if rpm_disp > 1 then rpm_disp = 1 + math.random(-1,1)*0.02 end -- Shake at redline local rpm_a = 3.6 + (rpm_disp * 2.2) line(rpm_x, rpm_y, rpm_x + math.cos(rpm_a) * 12, rpm_y + math.sin(rpm_a) * 12, 2) -- Orange needle -- print("RPM", rpm_x-5, rpm_y+4, 5, true) print("G", rpm_x-6, rpm_y+6, 11, true) print(gear, rpm_x, rpm_y+6, 12, true) -- 1. Speedometer (Left) local spd_x, spd_y, spd_r = 38, 118, 18 circ(spd_x, spd_y, spd_r, 14) circ(spd_x, spd_y, spd_r-2, 0) for i=0, 6 do local ta = 3.14 + (i/6 * 3.14) line(spd_x + math.cos(ta) * 17, spd_y + math.sin(ta) * 13, spd_x + math.cos(ta) * 13, spd_y + math.sin(ta) * 10, 13) end local speed_pct = speed / maxspeed local shake = 0 if shift_timer > 0 then shake = math.random(-1,1) * 0.1 end local needle_a = 3.6 + (speed_pct * 2.2) + shake line(spd_x, spd_y, spd_x + math.cos(needle_a) * 12, spd_y + math.sin(needle_a) * 12, 12) print(math.floor(speed*50), spd_x-10, spd_y+2, 10, false) -- 2. Gas Gauge (Middle/Left) local gas_x = 82 circ(gas_x, spd_y+3, 10, 14) circ(gas_x, spd_y+3, 9, 0) print("E", gas_x-7, spd_y+3, 2, true) print("F", gas_x+4, spd_y-2, 6, true) local gas_a = 3.6 + (fuel * 2.2) line(gas_x, spd_y+5, gas_x + math.cos(gas_a) * 9, spd_y + math.sin(gas_a) * 9, 12) -- Spokes circ(wheel_x, wheel_y, 6, 14) for i=0,2 do local sa = angle + (i * 2.094) + 1.57 line(wheel_x, wheel_y, wheel_x + math.cos(sa) * 35, wheel_y + math.sin(sa) * 35, 14) end rect(0, 126, 240, 20, c_dash) -- Pillars tri(0,0, 14,0, 0,136, c_dash) tri(240,0, 226,0, 240,136, c_dash) rect(0,0,240,15, c_dash) -- Update t = t + speed * 3.0 frame = frame + 1 end