laatste versie
This commit is contained in:
+26
-18
@@ -11,9 +11,11 @@ const ROAD_W = 420; // wegbreedte in wereld-eenheden (= px)
|
||||
const CAR_W = 24; // breedte auto
|
||||
const CAR_HALF_X = CAR_W / 2;
|
||||
const CAR_HALF_Y = 13; // halve verticale hitbox
|
||||
const CRUISE = 300; // kruissnelheid (u/s)
|
||||
const BOOST_BONUS = 175; // extra snelheid tijdens boost
|
||||
const LATERAL = 165; // laterale stuursnelheid (u/s)
|
||||
const CRUISE = 380; // kruissnelheid (u/s)
|
||||
const BOOST_BONUS = 220; // extra snelheid tijdens boost
|
||||
const LATERAL = 195; // laterale stuursnelheid bij toetsenbordstuur (u/s)
|
||||
const FOLLOW_RESPONSE = 12; // hoe snel de auto naar de vingerpositie glijdt
|
||||
const FOLLOW_MAX = 460; // maximale laterale volgsnelheid (u/s)
|
||||
const STEER_RESPONSE = 7; // hoe snel stuuruitslag inslingert (hoger = directer, lager = vloeiender)
|
||||
const OBJ_VH = 12; // verticale halve hoogte van obstakels
|
||||
const PICKUP_VH = 14; // verticale halve hoogte van power-ups
|
||||
@@ -79,7 +81,7 @@ class Game extends EventEmitter {
|
||||
finishTime: 0,
|
||||
rank: 0,
|
||||
boostUntil: 0, shieldUntil: 0, slipUntil: 0, slowUntil: 0,
|
||||
steer: 0, steerSm: 0,
|
||||
steer: 0, steerSm: 0, followX: null,
|
||||
joinedAt: Date.now(),
|
||||
};
|
||||
this.players.set(player.id, player);
|
||||
@@ -115,7 +117,7 @@ class Game extends EventEmitter {
|
||||
p.x = clamp(40 + Math.random() * (ROAD_W - 80), CAR_HALF_X, ROAD_W - CAR_HALF_X);
|
||||
p.d = 0; p.speed = 0; p.finished = false; p.finishTime = 0;
|
||||
p.boostUntil = p.shieldUntil = p.slipUntil = p.slowUntil = 0;
|
||||
p.steerSm = 0;
|
||||
p.steerSm = 0; p.followX = null;
|
||||
}
|
||||
this.track = null;
|
||||
}
|
||||
@@ -154,7 +156,7 @@ class Game extends EventEmitter {
|
||||
p.d = 0; p.speed = 0;
|
||||
p.finished = false; p.finishTime = 0;
|
||||
p.boostUntil = p.shieldUntil = p.slipUntil = p.slowUntil = 0;
|
||||
p.steerSm = 0;
|
||||
p.steerSm = 0; p.followX = null;
|
||||
});
|
||||
this.emit('race_start', this.track);
|
||||
}
|
||||
@@ -169,9 +171,10 @@ class Game extends EventEmitter {
|
||||
let d = 700;
|
||||
let puD = 1500;
|
||||
while (d < this.trackLength - 900) {
|
||||
const step = 250 + rng() * 190;
|
||||
// aantal geblokkeerde banen: nooit alle 3, zodat er altijd een doorgang is
|
||||
const blockCount = rng() < 0.18 ? 1 : 2;
|
||||
const step = 330 + rng() * 220;
|
||||
// aantal geblokkeerde banen: nooit alle 3, zodat er altijd een doorgang is;
|
||||
// vaker maar 1 baan geblokkeerd = minder drukke baan
|
||||
const blockCount = rng() < 0.45 ? 1 : 2;
|
||||
const idx = [0, 1, 2].sort(() => rng() - 0.5).slice(0, blockCount).sort((a, b) => a - b);
|
||||
for (const li of idx) {
|
||||
const type = rng() < 0.72 ? 'pylon' : 'oil';
|
||||
@@ -215,15 +218,20 @@ class Game extends EventEmitter {
|
||||
if (p.slowUntil > now) target = Math.min(target, 70);
|
||||
p.speed += (target - p.speed) * Math.min(1, dt * 2.2);
|
||||
|
||||
// Sturen: geleidelijk inslingeren (smoother) i.p.v. direct volle uitslag.
|
||||
// p.steer is het ruwe doel van de client, p.steerSm de opgerampte waarde.
|
||||
p.steerSm += (p.steer - p.steerSm) * Math.min(1, dt * STEER_RESPONSE);
|
||||
|
||||
// Sturen (olie = wegglijden)
|
||||
let mv = p.steerSm * LATERAL * dt;
|
||||
if (p.slipUntil > now) mv = p.steerSm * LATERAL * 0.3 * dt + (Math.random() - 0.5) * 260 * dt;
|
||||
p.x = clamp(p.x + mv + (p.slipUntil > now ? (Math.random() - 0.5) * 90 * dt : 0),
|
||||
CAR_HALF_X, ROAD_W - CAR_HALF_X);
|
||||
// Sturen: vinger-volgmodus (invoer 'x') = snel glijden naar de positie
|
||||
// onder je vinger; toetsenbordmodus (invoer 'steer') = geleidelijk sturen.
|
||||
if (p.followX != null) {
|
||||
let move = clamp((p.followX - p.x) * Math.min(1, dt * FOLLOW_RESPONSE),
|
||||
-FOLLOW_MAX * dt, FOLLOW_MAX * dt);
|
||||
if (p.slipUntil > now) move *= 0.35;
|
||||
p.x = clamp(p.x + move, CAR_HALF_X, ROAD_W - CAR_HALF_X);
|
||||
} else {
|
||||
p.steerSm += (p.steer - p.steerSm) * Math.min(1, dt * STEER_RESPONSE);
|
||||
let mv = p.steerSm * LATERAL * dt;
|
||||
if (p.slipUntil > now) mv = p.steerSm * LATERAL * 0.3 * dt + (Math.random() - 0.5) * 260 * dt;
|
||||
p.x = clamp(p.x + mv + (p.slipUntil > now ? (Math.random() - 0.5) * 90 * dt : 0),
|
||||
CAR_HALF_X, ROAD_W - CAR_HALF_X);
|
||||
}
|
||||
|
||||
p.d += p.speed * dt;
|
||||
if (p.d >= this.trackLength && !p.finished) {
|
||||
|
||||
+30
-5
@@ -3,6 +3,7 @@
|
||||
* Turbo Race - HTTP + WebSocket server
|
||||
* Serveert de statische frontend en draait de spelengine.
|
||||
* Host-eindpunt: verbind met ?host=<ADMIN_TOKEN> en druk op "s".
|
||||
* De host is toeschouwer: rijdt NIET zelf mee, maar volgt de koplopers live.
|
||||
*/
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
@@ -70,6 +71,20 @@ wss.on('connection', (ws) => {
|
||||
try { msg = JSON.parse(buf.toString()); } catch { return; }
|
||||
switch (msg.type) {
|
||||
case 'join': {
|
||||
const isHost = !!(msg.hostToken && ADMIN_TOKEN && String(msg.hostToken) === String(ADMIN_TOKEN));
|
||||
if (isHost) {
|
||||
// Host = toeschouwer: neemt geen spelerslot in, toont de koplopers.
|
||||
ws.isHost = true;
|
||||
send(ws, {
|
||||
type: 'joined',
|
||||
host: true,
|
||||
spectator: true,
|
||||
self: { name: 'Host', color: '#ffd93d' },
|
||||
snapshot: game.snapshot(),
|
||||
track: game.track,
|
||||
});
|
||||
break;
|
||||
}
|
||||
let player;
|
||||
try {
|
||||
player = game.addPlayer(msg.name, msg.color, msg.hostToken, ADMIN_TOKEN);
|
||||
@@ -81,6 +96,7 @@ wss.on('connection', (ws) => {
|
||||
type: 'joined',
|
||||
id: player.id,
|
||||
host: player.host,
|
||||
spectator: false,
|
||||
self: { name: player.name, color: player.color },
|
||||
snapshot: game.snapshot(),
|
||||
track: game.track,
|
||||
@@ -92,20 +108,29 @@ wss.on('connection', (ws) => {
|
||||
if (!id) break;
|
||||
const p = game.players.get(id);
|
||||
if (!p) break;
|
||||
p.steer = Math.max(-1, Math.min(1, Number(msg.steer) || 0));
|
||||
if (typeof msg.x === 'number') {
|
||||
p.followX = Math.max(12, Math.min(408, Number(msg.x))); // vinger volgen
|
||||
p.steer = 0;
|
||||
} else {
|
||||
p.steer = Math.max(-1, Math.min(1, Number(msg.steer) || 0));
|
||||
p.followX = null; // toetsenbord
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'host_start': {
|
||||
const id = sockToId.get(ws);
|
||||
const p = id && game.players.get(id);
|
||||
if (!p || !p.host) break;
|
||||
const allowed = ws.isHost || (() => {
|
||||
const id = sockToId.get(ws);
|
||||
const p = id && game.players.get(id);
|
||||
return !!(p && p.host);
|
||||
})();
|
||||
if (!allowed) break;
|
||||
if (game.hostStart()) broadcast({ type: 'phase', phase: 'countdown', hostStart: true });
|
||||
break;
|
||||
}
|
||||
case 'ready_again': {
|
||||
const id = sockToId.get(ws);
|
||||
const p = id && game.players.get(id);
|
||||
if (!p) break;
|
||||
if (!ws.isHost && !p) break;
|
||||
if (game.readyAgain()) broadcast({ type: 'phase', phase: 'lobby', reset: true });
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user