From e00c6ba0a9d1ecb1525630a34d0019798301075f Mon Sep 17 00:00:00 2001 From: Frank Force Date: Tue, 24 Sep 2024 20:28:56 -0500 Subject: [PATCH] gameplay tweaks --- code/debug.js | 2 +- code/game.js | 33 +++++++------------------- code/generative.js | 2 +- code/hud.js | 2 +- code/input.js | 23 ++++++++++-------- code/levels.js | 48 +++++++++++++++++++++++--------------- code/trackGen.js | 13 +++++++---- code/vehicle.js | 57 +++++++++++++++++++++++++++++++++------------ favicon.png | Bin 2973 -> 2825 bytes index.html | 40 +++++++++++++++---------------- 10 files changed, 124 insertions(+), 96 deletions(-) diff --git a/code/debug.js b/code/debug.js index 53e0b6a..b9d1f69 100644 --- a/code/debug.js +++ b/code/debug.js @@ -141,7 +141,7 @@ function debugUpdate() debugCapture = 1; if (keyWasPressed('KeyQ') && !freeCamMode) testDrive = !testDrive - if (keyWasPressed('KeyP')) + if (keyWasPressed('KeyU')) sound_win.play(); if (debug && keyWasPressed('KeyV')) spawnVehicle(playerVehicle.pos.z-1300) diff --git a/code/game.js b/code/game.js index 3afa56b..02dcd8c 100644 --- a/code/game.js +++ b/code/game.js @@ -26,9 +26,8 @@ Features */ -//devMode = 1 +//devMode = debugInfo = 1 //soundVolume = 0 -//debugInfo = 1 //debugGenerativeCanvas = 1 //autoPause = 0 @@ -63,7 +62,7 @@ const drawDistance = 1e3; // how many track segments to draw for scener const cameraPlayerOffset = vec3(0,680,1050); const checkpointTrackSegments = testQuick?1e3:4500; const checkpointDistance = checkpointTrackSegments*trackSegmentLength; -const startCheckpointTime = 50; +const startCheckpointTime = 45; const extraCheckpointTime = 40; const levelLerpRange = .1; const levelGoal = 10; @@ -102,10 +101,14 @@ function gameInit() mainContext = mainCanvas.getContext('2d'); const styleCanvas = 'position:absolute;' + // position - 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center + (clampAspectRatios?'top:50%;left:50%;transform:translate(-50%,-50%);':'') + // center (pixelate?' image-rendering: pixelated':''); // pixelated + glCanvas.style.cssText = mainCanvas.style.cssText = styleCanvas; + if (!clampAspectRatios) + document.body.style.margin = '0px'; + drawInit(); inputInit() initGenerative(); @@ -120,7 +123,7 @@ function gameStart() time = frame = frameTimeLastMS = averageFPS = frameTimeBufferMS = cameraOffset = checkpointTimeLeft = raceTime = playerLevel = playerWin = playerNewDistanceRecord = playerNewRecord = freeRide = checkpointSoundCount = 0; startCountdown = quickStart || testLevel ? 0 : 4; - worldHeading = titleScreenMode ? rand(7) : .9; + worldHeading = titleScreenMode ? rand(7) : .8; checkpointTimeLeft = startCheckpointTime; nextCheckpointDistance = checkpointDistance; startCountdownTimer = new Timer; @@ -250,25 +253,7 @@ function gameUpdateInternal() } } } - - // spawn in more vehicles - const playerIsSlow = titleScreenMode || playerVehicle.velocity.z < 20; - const trafficPosOffset = playerIsSlow? 0 : 18e4; // check in front/behind - const trafficLevel = (playerVehicle.pos.z+trafficPosOffset)/checkpointDistance; - const trafficLevelInfo = getLevelInfo(trafficLevel); - const trafficDensity = trafficLevelInfo.trafficDensity; - const maxVehicleCount = 10*trafficDensity; - if (trafficDensity) - if (vehicles.length!o.destroyed); + updateCars(); } function gameUpdate(frameTimeMS=0) diff --git a/code/generative.js b/code/generative.js index 42d6fdc..97cb264 100644 --- a/code/generative.js +++ b/code/generative.js @@ -598,7 +598,7 @@ function generateTetures() } else { - // city building + // y flippable city building color(BLACK); for(let i=19; i--;) { diff --git a/code/hud.js b/code/hud.js index 6823afb..696ef6b 100644 --- a/code/hud.js +++ b/code/hud.js @@ -131,7 +131,7 @@ function drawHUD() if (debugInfo&&!titleScreenMode) // mph { - const mph = playerVehicle.velocity.z>>1; + const mph = playerVehicle.velocity.z|0; const mphPos = vec3(.01,.95); drawHUDText(mph+' MPH', mphPos, .08, undefined,undefined,'left',900,'italic'); } diff --git a/code/input.js b/code/input.js index d57a32f..26c788b 100644 --- a/code/input.js +++ b/code/input.js @@ -1,7 +1,6 @@ 'use strict'; const gamepadsEnable = enhancedMode; -const gamepadDirectionEmulateStick = 1; const inputWASDEmulateDirection = enhancedMode; const allowTouch = enhancedMode; const isTouchDevice = allowTouch && window.ontouchstart !== undefined; @@ -11,23 +10,23 @@ const touchGamepadAlpha = .3; /////////////////////////////////////////////////////////////////////////////// // Input user functions -const keyIsDown = (key) => inputData[key] & 1; -const keyWasPressed = (key) => inputData[key] & 2 ? 1 : 0; +const keyIsDown = (key) => inputData[key] & 1; +const keyWasPressed = (key) => inputData[key] & 2 ? 1 : 0; const keyWasReleased = (key) => inputData[key] & 4 ? 1 : 0; const clearInput = () => inputData = []; -let mousePos = vec3(); -const mouseIsDown = keyIsDown; -const mouseWasPressed = keyWasPressed; +let mousePos; +const mouseIsDown = keyIsDown; +const mouseWasPressed = keyWasPressed; const mouseWasReleased = keyWasReleased; let isUsingGamepad; -const gamepadIsDown = (key, gamepad=0) => !!(gamepadData[gamepad][key] & 1); -const gamepadWasPressed = (key, gamepad=0) => !!(gamepadData[gamepad][key] & 2); +const gamepadIsDown = (key, gamepad=0) => !!(gamepadData[gamepad][key] & 1); +const gamepadWasPressed = (key, gamepad=0) => !!(gamepadData[gamepad][key] & 2); const gamepadWasReleased = (key, gamepad=0) => !!(gamepadData[gamepad][key] & 4); -const gamepadStick = (stick, gamepad=0) => +const gamepadStick = (stick, gamepad=0) => gamepadStickData[gamepad] ? gamepadStickData[gamepad][stick] || vec3() : vec3(); -const gamepadGetValue = (key, gamepad=0) => gamepadDataValues[gamepad][key]; +const gamepadGetValue = (key, gamepad=0) => gamepadDataValues[gamepad][key]; /////////////////////////////////////////////////////////////////////////////// // Input event handlers @@ -36,6 +35,9 @@ let inputData = []; // track what keys are down function inputInit() { + if (!js13kBuildLevel2) + mousePos = vec3(); + if (gamepadsEnable) { gamepadData = []; @@ -177,6 +179,7 @@ function gamepadsUpdate() isUsingGamepad ||= !i && button.pressed; } + const gamepadDirectionEmulateStick = 1; if (gamepadDirectionEmulateStick) { // copy dpad to left analog stick when pressed diff --git a/code/levels.js b/code/levels.js index f32cd36..74e64a0 100644 --- a/code/levels.js +++ b/code/levels.js @@ -17,6 +17,7 @@ function initLevelInfos() LI.horizonSpriteSize = .5; //LI.tunnel = trackSprites.tunnel2; // test tunnel LI.billboardChance = .3 // more billboards at start + LI.trafficDensity = .7; // less traffic start // mostly straight with few well defined turns or bumps LI.turnChance = .6; @@ -58,7 +59,7 @@ function initLevelInfos() LI.bumpFreqMin = .4; //LI.bumpFreqMax = .7; //LI.bumpScaleMin = 50; - LI.bumpScaleMax = 120; + LI.bumpScaleMax = 140; // Level 3 - desert - // has long straight thin roads and tunnel @@ -199,7 +200,7 @@ function initLevelInfos() LI.bumpFreqMin = .3; LI.bumpFreqMax = .6; LI.bumpScaleMin = 80; - LI.bumpScaleMax = 160; + LI.bumpScaleMax = 200; // Level 7 - graveyard - LI = new LevelInfo(level++, [ @@ -226,12 +227,10 @@ function initLevelInfos() // thin road over hills in graveyard //LI.turnChance = .5; - LI.turnMin = .1; - LI.turnMax = .7; - LI.bumpChance = .7; - LI.bumpFreqMin = .3; - LI.bumpFreqMax = .7; - LI.bumpScaleMin = 100; + LI.turnMax = .6; + LI.bumpChance = .6; + LI.bumpFreqMin = LI.bumpFreqMax = .7; + LI.bumpScaleMin = 80; //LI.bumpScaleMax = 150; // Level 8 - jungle - dirt road, many trees @@ -267,9 +266,9 @@ function initLevelInfos() LI.turnMax = .3; // lots of slight turns LI.bumpChance = 1; LI.bumpFreqMin = .4; - LI.bumpFreqMax = .8; + LI.bumpFreqMax = .6; LI.bumpScaleMin = 10; - LI.bumpScaleMax = 60; + LI.bumpScaleMax = 80; // Level 9 - strange area LI = new LevelInfo(level++, [ @@ -297,7 +296,7 @@ function initLevelInfos() LI.bumpFreqMin = .5; LI.bumpFreqMax = .9; LI.bumpScaleMin = 100; - //LI.bumpScaleMax = 150; + LI.bumpScaleMax = 200; // Level 10 - mountains - hilly, rocks on sides LI = new LevelInfo(level++, [ @@ -306,7 +305,7 @@ function initLevelInfos() trackSprites.grass_flower1, trackSprites.rock_huge2, trackSprites.rock_huge, - ], trackSprites.tree_pink, trackSprites.horizon_mountains); + ], trackSprites.tree_pink); LI.trackSideRate = 21; LI.skyColorTop = hsl(.2,1,.9); LI.skyColorBottom = hsl(.55,1,.5); @@ -314,14 +313,19 @@ function initLevelInfos() LI.groundColor = hsl(.1,.5,.7); LI.cloudColor = hsl(0,0,1,.5); LI.tunnel = trackSprites.tunnel1; - LI.sunHeight = .6; - LI.horizonSpriteSize = .5; + if (js13kBuildLevel2) + LI.horizonSpriteSize = 0; + else + { + LI.sunHeight = .6; + LI.horizonSprite = trackSprites.horizon_mountains + LI.horizonSpriteSize = .5; + } // mountains, most difficult level LI.turnChance = .8; //LI.turnMin = 0; - LI.turnMax = 1; - LI.bumpChance = 1; + LI.turnMax = LI.bumpChance = 1; LI.bumpFreqMin = .3; LI.bumpFreqMax = .9; //LI.bumpScaleMin = 50; @@ -335,15 +339,21 @@ function initLevelInfos() trackSprites.grass_plain, trackSprites.tree_oak, trackSprites.tree_bush, - ], trackSprites.tree_oak, trackSprites.horizon_mountains); + ], trackSprites.tree_oak); LI.sceneryListBias = 1; LI.groundColor = hsl(.2,.3,.5); LI.trackSideRate = LI.billboardChance = 0; LI.bumpScaleMin = 1e3; // hill in the distance // match settings to previous level - LI.sunHeight = .6; - LI.horizonSpriteSize = .5; + if (js13kBuildLevel2) + LI.horizonSpriteSize = 0; + else + { + LI.sunHeight = .6; + LI.horizonSprite = trackSprites.horizon_mountains + LI.horizonSpriteSize = .5; + } } const getLevelInfo = (level) => testLevelInfo || levelInfoList[level|0] || levelInfoList[0]; diff --git a/code/trackGen.js b/code/trackGen.js index d09ad29..8c70ee6 100644 --- a/code/trackGen.js +++ b/code/trackGen.js @@ -138,7 +138,7 @@ function initTrackSprites() trackSprites.telephonePole = new TrackSprite(vec3(0,4),1800,0,0,.03,0); //trackSprites.parts_girder = new TrackSprite(vec3(0,6),500,0,.05,30,0); trackSprites.telephonePole.shadowScale = .3; - trackSprites.grave_stone = new TrackSprite(vec3(2,6),500,.4,.05,.5,0); + trackSprites.grave_stone = new TrackSprite(vec3(2,6),500,.3,.05,.5,0); trackSprites.grave_stone.lightnessRandomness = .5; trackSprites.light_tunnel = new TrackSprite(vec3(0,0),200,0,0,0,0); trackSprites.light_tunnel.shadowScale = 0; @@ -149,9 +149,6 @@ function initTrackSprites() trackSprites.horizon_city.lightnessRandomness = .15; trackSprites.horizon_city.colorHSL = vec3(1); // vary color - trackSprites.horizon_mountains = new TrackSprite(vec3(7,6)); - trackSprites.horizon_mountains.colorHSL = vec3(0, 0, .7); - trackSprites.horizon_mountains.canMirror = 0; trackSprites.horizon_islands = new TrackSprite(vec3(7,6)); trackSprites.horizon_islands.colorHSL = vec3(.25, .5, .6); trackSprites.horizon_islands.canMirror = 0; @@ -176,6 +173,12 @@ function initTrackSprites() trackSprites.horizon_weird = new TrackSprite(vec3(7,6)); trackSprites.horizon_weird.colorHSL = vec3(.7, .5, .6); trackSprites.horizon_weird.canMirror = 0; + if (!js13kBuildLevel2) + { + trackSprites.horizon_mountains = new TrackSprite(vec3(7,6)); + trackSprites.horizon_mountains.colorHSL = vec3(0, 0, .7); + trackSprites.horizon_mountains.canMirror = 0; + } } // a sprite that can be placed on the track @@ -483,7 +486,7 @@ function buildTrack() //turn = .5; height = 0; //turn = Math.sin(i/100)*.7; - //height = noise1D(i/29)*-1700;turn =0; // jumps test + //height = noise1D((i-50)/99)*2700;turn =0; // jumps test // create track segment const o = vec3(turn, height, i*trackSegmentLength); diff --git a/code/vehicle.js b/code/vehicle.js index 3f37e03..0d9894c 100644 --- a/code/vehicle.js +++ b/code/vehicle.js @@ -6,6 +6,28 @@ function drawCars() v.draw(); } +function updateCars() +{ + // spawn in more vehicles + const playerIsSlow = titleScreenMode || playerVehicle.velocity.z < 20; + const trafficPosOffset = playerIsSlow? 0 : 18e4; // check in front/behind + const trafficLevel = (playerVehicle.pos.z+trafficPosOffset)/checkpointDistance; + const trafficLevelInfo = getLevelInfo(trafficLevel); + const trafficDensity = trafficLevelInfo.trafficDensity; + const maxVehicleCount = 10*trafficDensity; + if (trafficDensity) + if (vehicles.length!o.destroyed); +} + function spawnVehicle(z) { if (!aiVehicles) @@ -61,7 +83,7 @@ class Vehicle { const levelInfo = getLevelInfo(this.pos.z/checkpointDistance); const lane = levelInfo.laneCount - 1 - this.lane; // flip side - return max(120,120 + lane*22); // faster on left + return max(120,120 + lane*20); // faster on left } getLaneOffset() @@ -108,10 +130,11 @@ class Vehicle { // slow down if behind if (v != this && v != playerVehicle) - if (this.pos.z < v.pos.z + 500 && this.pos.z > v.pos.z - 2e3) + if (this.pos.z < v.pos.z + (js13kBuildLevel2?0:500) && this.pos.z > v.pos.z - 2e3) if (abs(x-v.laneOffset) < 500) // lane space { - this.destroyed |= (this.pos.z >= v.pos.z); // get rid of overlaps + if (!js13kBuildLevel2) + this.destroyed |= (this.pos.z >= v.pos.z); // get rid of overlaps this.velocity.z = min(this.velocity.z, v.velocity.z++); // clamp velocity & push this.isBraking = 20; break; @@ -290,7 +313,7 @@ class PlayerVehicle extends Vehicle } } - const hitBump=(amount = .98)=> + const hitBump=(amount = .97)=> { this.velocity.z *= amount; if (this.bumpTime < 0) @@ -318,6 +341,8 @@ class PlayerVehicle extends Vehicle ++playerLevel; nextCheckpointDistance += checkpointDistance; checkpointTimeLeft += extraCheckpointTime; + if (enhancedMode) + checkpointTimeLeft = min(60,checkpointTimeLeft); if (playerLevel >= levelGoal && !gameOverTimer.isSet()) { @@ -354,8 +379,8 @@ class PlayerVehicle extends Vehicle if (v != this && d.x < s.x && d.z < s.z) { // collision - this.velocity.z = v.velocity.z*.8; - v.velocity.z = max(v.velocity.z, this.velocity.z*1.2); // push other car + this.velocity.z = v.velocity.z/2; + v.velocity.z = max(v.velocity.z, this.velocity.z*1.5); // push other car this.velocity.x = 99*sign(this.pos.x-v.pos.x); // push away from car playHitSound(); } @@ -414,7 +439,7 @@ class PlayerVehicle extends Vehicle { --this.engineTime; const f = sound_velocity; - sound_engine.play(.1,f*f/8e3+rand(.1)); + sound_engine.play(.1,f/40+rand(.1)); } // player settings @@ -423,9 +448,9 @@ class PlayerVehicle extends Vehicle const gravity = -3; // gravity to apply in y axis const lateralDamping = .5; // dampen player x speed const playerAccel = 1; // player acceleration - const playerBrake = 4; // player acceleration when braking + const playerBrake = 3; // player acceleration when braking const playerMaxSpeed = 200; // limit max player speed - const speedPercent = clamp(this.velocity.z/playerMaxSpeed); + const speedPercent = this.velocity.z/playerMaxSpeed; // update physics this.velocity.y += gravity; @@ -478,13 +503,15 @@ class PlayerVehicle extends Vehicle else if (playerInputGas) { // extra boost at low speeds - const lowSpeedPercent = percent(this.velocity.z, 100, 0)**2; + //const lowSpeedPercent = this.velocity.z**2/1e4; + const lowSpeedPercent = percent(this.velocity.z, 90, 0)**2; const accel = playerInputGas*playerAccel*lerp(speedPercent, 1, .5) - * lerp(lowSpeedPercent, 1, 7); + * lerp(lowSpeedPercent, 1, 6); // apply acceleration in angle of road - const accelVec = vec3(0,0,accel).rotateX(trackSegment.pitch); - this.velocity = this.velocity.add(accelVec); + //const accelVec = vec3(0,0,accel).rotateX(trackSegment.pitch); + //this.velocity = this.velocity.add(accelVec); + this.velocity.z += accel; } else if (this.velocity.z < 30) this.velocity.z *= .9; // slow to stop @@ -496,8 +523,8 @@ class PlayerVehicle extends Vehicle this.onGround = 0; } - { - // clamp z velocity + { + // dampen z velocity & clamp this.velocity.z = max(0, this.velocity.z*forwardDamping); // turning diff --git a/favicon.png b/favicon.png index 8d34d73638799bf7c911578b17c04ab4fb1dd41c..e735397483fca7010aa628535a2d759e8252bead 100644 GIT binary patch delta 2367 zcmV-F3BdN97l{_IzY)k+E2?j|-K~z}7#g}Q2T}2hgf9G`Hx6CB7Br{`3 z$Rdm=iBfV>Koe2*kA1w((uh#t%$Q3OmtlBG>Crd0XcgEumI-e&;loHS0_k^`QC95ZM$M_U+X z-7)>hPgXE%eU14S3{c&?vg@52B|uj?(DrSvRa!RCA`a{c8H9rzrgfMy_qopTw>cWeU@2sysj1-w`y zorYFm0s;gp0Vp*YlA(OM=Lo2Q{NS6A8x%$2$#;K0#4$@(Fg$oEGVd7JTXG_>8Mxvv zcd+{P@4*9dV2W#(o_z?**Rlvi91)QWvjP#tAQ=V3Sm3Dg@!5UI+Lh2We8)@ZJ^wq* z1fIF-OgQpL0D6|LVBZbr0D+y|858gN3|9^B=B%OL6P?ng$_r^Ix9wPGL0iIOfF)@t zB_Mwyi31pCcFe=yypsNN*ARgVcKrc>&(4|!KyURpX15M)02#3LnomGhGJ4+U0Jvx8 zK1yf!kQ}R9#CK+f5fPL?#90N1h#El*GX)%e?Khc!{#sV-T1OO!F7knn9`w3zGylSy zm<`-|HoR{D)@qC<`crJ>y%6HrXPpAZCA95UN)_#3~lEd;q*^E36I$D$nCc!d1l~WGERX zifV?4rmHB5m`{hd9u=Wlty6yC8;BIBroAd4qBsxEDSeer_5n+09*c^iqKG=wduW|) zq6pu+#!#ECJEqe_ASM;_KCNkMP8Fsc)CJT-E4(;M-MVwr7 ze1F4z@EH6Nc!!IzPx5oUra~K*OW5&!G)XEaoNT!adN`>|q zsYlNdGR3tEd%0-uHlSd?wo`v;kFPTjWCUb!!7M;%Q zU3Wtxr>`9bUvttl;g5Ul9@Cf+eaX)kFQPfRi=XWtVo!`XsWNGvl9EYZ1*>tx;PV{30Ne#!! z&6^MrN^N6YJGj07rC^RvZ+n0m9+fGs{>NPu7!B1k?vSwdzYaA`6#YN~5DI8PFZ3FwOYs z?#=XTJ4<9Pe`kbucO9KJXa-yEgM&G=S6dBg4%UDOOf_MA4@8*i=w`{<5t<=lF|b{_ z*|>HEW1spp-2{L7>PIo-jHQRC;E|zwZMO653neFX^`tgR1^xTF){Y9qJb}hCsa2W7 zf&DN!4tay4e$_>YIni=L1R=2FM_*>bw{y~mQ!j8!+aRY5K=&DEQjny6&TWHufcY~z zSzw^gJkYYMOv*?TY$B;(Glz)-P=u6@d_OO{RS485?ErrvL{uIBTzxsgd5&Io38~vU ze8do#bLTD795}vv4(`c8sLX+I5Uc=o(^6bCp;HF!7G6B&f5=7Z2-J`;@0G0u{V**A`~S|O~B*<7}*7FZM^)K z84a&Mplp9m5G~@w*uG{Z;^Ej2UXmJ(P~eHgC^W)B02bW!JM=7=0(=X2T8cG7)C8Ot zFtQyQQ|Xy^6bpV`2MnbS7 zP{8;;7#@av3}!8+>zDszW~Bp~yHLtQ=?F2fWAHa>M3hC#E~YIgSWYy^qUZns002ovPDHLkV1h!JSF8X4 delta 2516 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 edi2M>D)4_Dr1v3n Wild! 🚗🌴 - + - - - + + + - + @@ -27,18 +27,18 @@ document.body.style.backgroundColor = '#222'; } - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + \ No newline at end of file