Only auto aim and healthbars
This commit is contained in:
1 parent
54c6327ac7
commit
a914bd74fe
14 files changed
+94
-88
No files matched your search
@@ -240,7 +240,6 @@ final class GameSimulator
|
|||||||
$keys = is_array($frame['keys'] ?? null) ? $frame['keys'] : [];
|
$keys = is_array($frame['keys'] ?? null) ? $frame['keys'] : [];
|
||||||
$input->keys = array_values(array_filter($keys, 'is_string'));
|
$input->keys = array_values(array_filter($keys, 'is_string'));
|
||||||
$input->fire = (bool) ($frame['fire'] ?? false);
|
$input->fire = (bool) ($frame['fire'] ?? false);
|
||||||
$input->autoAim = (bool) ($frame['autoAim'] ?? false);
|
|
||||||
|
|
||||||
$aim = $frame['aim'] ?? null;
|
$aim = $frame['aim'] ?? null;
|
||||||
if (is_array($aim)) {
|
if (is_array($aim)) {
|
||||||
@@ -265,10 +264,8 @@ final class GameSimulator
|
|||||||
$entry['engine']->tick($dt);
|
$entry['engine']->tick($dt);
|
||||||
|
|
||||||
if ($room->state === Room::STATE_OVER) {
|
if ($room->state === Room::STATE_OVER) {
|
||||||
$room->seq++;
|
|
||||||
$this->broadcastRoom($room);
|
$this->broadcastRoom($room);
|
||||||
} elseif ($this->frame % self::SNAPSHOT_EVERY === 0) {
|
} elseif ($this->frame % self::SNAPSHOT_EVERY === 0) {
|
||||||
$room->seq++;
|
|
||||||
$this->broadcastRoom($room);
|
$this->broadcastRoom($room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,6 +277,8 @@ final class GameSimulator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$room->seq++;
|
||||||
|
|
||||||
$players = [];
|
$players = [];
|
||||||
foreach ($room->players as $player) {
|
foreach ($room->players as $player) {
|
||||||
$players[] = $player->toSnapshot();
|
$players[] = $player->toSnapshot();
|
||||||
|
|||||||
@@ -14,18 +14,14 @@ final class InputState
|
|||||||
|
|
||||||
public bool $fire;
|
public bool $fire;
|
||||||
|
|
||||||
public bool $autoAim;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $keys = [],
|
array $keys = [],
|
||||||
?Vector2 $aim = null,
|
?Vector2 $aim = null,
|
||||||
bool $fire = false,
|
bool $fire = false,
|
||||||
bool $autoAim = false,
|
|
||||||
) {
|
) {
|
||||||
$this->keys = $keys;
|
$this->keys = $keys;
|
||||||
$this->aim = $aim ?? new Vector2(1.0, 0.0);
|
$this->aim = $aim ?? new Vector2(1.0, 0.0);
|
||||||
$this->fire = $fire;
|
$this->fire = $fire;
|
||||||
$this->autoAim = $autoAim;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(?Vector2 $aim = null): void
|
public function reset(?Vector2 $aim = null): void
|
||||||
@@ -33,6 +29,5 @@ final class InputState
|
|||||||
$this->keys = [];
|
$this->keys = [];
|
||||||
$this->aim = $aim ?? new Vector2(1.0, 0.0);
|
$this->aim = $aim ?? new Vector2(1.0, 0.0);
|
||||||
$this->fire = false;
|
$this->fire = false;
|
||||||
$this->autoAim = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-14
@@ -60,8 +60,6 @@ final class Player extends GameObject
|
|||||||
|
|
||||||
public bool $dead = false;
|
public bool $dead = false;
|
||||||
|
|
||||||
public bool $autoAim = false;
|
|
||||||
|
|
||||||
private float $cooldown = 0.0;
|
private float $cooldown = 0.0;
|
||||||
|
|
||||||
private Vector2 $aimDirection;
|
private Vector2 $aimDirection;
|
||||||
@@ -154,22 +152,19 @@ final class Player extends GameObject
|
|||||||
);
|
);
|
||||||
|
|
||||||
$aim = $this->input->aim;
|
$aim = $this->input->aim;
|
||||||
$autoAimActive = $this->input->autoAim || $this->autoAim;
|
|
||||||
$hasTarget = false;
|
$hasTarget = false;
|
||||||
if ($autoAimActive) {
|
$nearest = $this->nearestZombie();
|
||||||
$nearest = $this->nearestZombie();
|
if ($nearest !== null) {
|
||||||
if ($nearest !== null) {
|
$hasTarget = true;
|
||||||
$hasTarget = true;
|
$toEnemy = $nearest->pos->subV($this->pos);
|
||||||
$toEnemy = $nearest->pos->subV($this->pos);
|
if ($toEnemy->lengthSquared() > 1.0) {
|
||||||
if ($toEnemy->lengthSquared() > 1.0) {
|
$aim = $toEnemy;
|
||||||
$aim = $toEnemy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($aim->lengthSquared() > 1.0) {
|
if ($aim->lengthSquared() > 1.0) {
|
||||||
$this->aimDirection = $aim->divS($aim->length());
|
$this->aimDirection = $aim->divS($aim->length());
|
||||||
} elseif ($aim->lengthSquared() > 0.01 && $this->input->aim !== null && ! $autoAimActive) {
|
} elseif ($aim->lengthSquared() > 0.01 && $this->input->aim !== null) {
|
||||||
// Raw normalized direction from the client when not auto-aiming.
|
// Use the raw client direction when no auto-aim target is near.
|
||||||
$this->aimDirection = $aim->normalize();
|
$this->aimDirection = $aim->normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +172,7 @@ final class Player extends GameObject
|
|||||||
$this->damageCooldown = max(0.0, $this->damageCooldown - $dt);
|
$this->damageCooldown = max(0.0, $this->damageCooldown - $dt);
|
||||||
|
|
||||||
$this->cooldown -= $dt;
|
$this->cooldown -= $dt;
|
||||||
if ($this->cooldown <= 0.0 && ($this->input->fire || ($autoAimActive && $hasTarget))) {
|
if ($this->cooldown <= 0.0 && ($this->input->fire || $hasTarget)) {
|
||||||
$this->cooldown = $this->fireRate;
|
$this->cooldown = $this->fireRate;
|
||||||
$this->shoot();
|
$this->shoot();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ final class Room
|
|||||||
$this->spawnTimer = 0.0;
|
$this->spawnTimer = 0.0;
|
||||||
$this->pickupTimer = 8.0;
|
$this->pickupTimer = 8.0;
|
||||||
$this->events = [];
|
$this->events = [];
|
||||||
$this->seq++;
|
|
||||||
|
|
||||||
foreach ($this->entities->copy() as $entity) {
|
foreach ($this->entities->copy() as $entity) {
|
||||||
if ($entity instanceof Player) {
|
if ($entity instanceof Player) {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ final class GameInputController extends Controller
|
|||||||
'keys' => $keys,
|
'keys' => $keys,
|
||||||
'aim' => $aim,
|
'aim' => $aim,
|
||||||
'fire' => (bool) $request->input('fire', false),
|
'fire' => (bool) $request->input('fire', false),
|
||||||
'autoAim' => (bool) $request->input('autoAim', false),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Redis::rpush(sprintf('game:in:%s', $room), json_encode($frame, JSON_THROW_ON_ERROR));
|
Redis::rpush(sprintf('game:in:%s', $room), json_encode($frame, JSON_THROW_ON_ERROR));
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ final class PushClientEventToInputQueue
|
|||||||
? ['x' => (float) ($data['aim']['x'] ?? 0.0), 'y' => (float) ($data['aim']['y'] ?? 0.0)]
|
? ['x' => (float) ($data['aim']['x'] ?? 0.0), 'y' => (float) ($data['aim']['y'] ?? 0.0)]
|
||||||
: ['x' => 1.0, 'y' => 0.0],
|
: ['x' => 1.0, 'y' => 0.0],
|
||||||
'fire' => (bool) ($data['fire'] ?? false),
|
'fire' => (bool) ($data['fire'] ?? false),
|
||||||
'autoAim' => (bool) ($data['autoAim'] ?? false),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Redis::rpush(sprintf(self::INPUT_QUEUE, $roomId), json_encode($frame, JSON_THROW_ON_ERROR));
|
Redis::rpush(sprintf(self::INPUT_QUEUE, $roomId), json_encode($frame, JSON_THROW_ON_ERROR));
|
||||||
|
|||||||
@@ -213,6 +213,21 @@ html, body {
|
|||||||
color: #bbb;
|
color: #bbb;
|
||||||
min-height: 18px;
|
min-height: 18px;
|
||||||
}
|
}
|
||||||
|
.lobby-players-title {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.lobby-player {
|
||||||
|
padding: 3px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
.lobby-player.you {
|
||||||
|
color: #4fe3ff;
|
||||||
|
}
|
||||||
#gameover {
|
#gameover {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { GameObject } from '../engine/GameObject.js';
|
import { GameObject } from '../engine/GameObject.js';
|
||||||
import { Vector2D } from '../engine/Vector2D.js';
|
import { Vector2D } from '../engine/Vector2D.js';
|
||||||
import { Bullet } from './Bullet.js';
|
import { Bullet } from './Bullet.js';
|
||||||
import { Zombie } from './Zombie.js';
|
|
||||||
|
|
||||||
export class Player extends GameObject {
|
export class Player extends GameObject {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -23,7 +22,6 @@ export class Player extends GameObject {
|
|||||||
particles = null,
|
particles = null,
|
||||||
sounds = null,
|
sounds = null,
|
||||||
size = 24,
|
size = 24,
|
||||||
autoAim = false,
|
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
super(viewport.width / 2, viewport.height / 2);
|
super(viewport.width / 2, viewport.height / 2);
|
||||||
@@ -51,7 +49,6 @@ export class Player extends GameObject {
|
|||||||
this._recoil = new Vector2D(0, 0);
|
this._recoil = new Vector2D(0, 0);
|
||||||
this._damageCooldown = 0;
|
this._damageCooldown = 0;
|
||||||
this._time = 0;
|
this._time = 0;
|
||||||
this.autoAim = autoAim;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(dt) {
|
update(dt) {
|
||||||
@@ -81,15 +78,6 @@ export class Player extends GameObject {
|
|||||||
this.pos.y = Math.min(Math.max(this.pos.y, maxY), height - maxY);
|
this.pos.y = Math.min(Math.max(this.pos.y, maxY), height - maxY);
|
||||||
|
|
||||||
let aim = this.input.mouse.subV(this.pos);
|
let aim = this.input.mouse.subV(this.pos);
|
||||||
if (this.autoAim) {
|
|
||||||
const nearest = this._nearestZombie();
|
|
||||||
if (nearest) {
|
|
||||||
const toEnemy = nearest.pos.subV(this.pos);
|
|
||||||
if (toEnemy.lengthSqrt() > 1) {
|
|
||||||
aim = toEnemy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (aim.lengthSqrt() > 1) {
|
if (aim.lengthSqrt() > 1) {
|
||||||
this._aimDirection = aim.divS(aim.length());
|
this._aimDirection = aim.divS(aim.length());
|
||||||
}
|
}
|
||||||
@@ -104,21 +92,6 @@ export class Player extends GameObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_nearestZombie() {
|
|
||||||
let best = null;
|
|
||||||
let bestDistSq = Infinity;
|
|
||||||
for (const obj of this.container || []) {
|
|
||||||
if (!(obj instanceof Zombie)) continue;
|
|
||||||
const delta = obj.pos.subV(this.pos);
|
|
||||||
const distSq = delta.lengthSqrt();
|
|
||||||
if (distSq < bestDistSq) {
|
|
||||||
bestDistSq = distSq;
|
|
||||||
best = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
shoot() {
|
shoot() {
|
||||||
const aimAngle = Math.atan2(this._aimDirection.y, this._aimDirection.x);
|
const aimAngle = Math.atan2(this._aimDirection.y, this._aimDirection.x);
|
||||||
this._recoil = this._aimDirection.mulS(-this.recoilSpeed);
|
this._recoil = this._aimDirection.mulS(-this.recoilSpeed);
|
||||||
|
|||||||
+25
-28
@@ -46,7 +46,6 @@ window.addEventListener('orientationchange', resizeToWindow);
|
|||||||
resizeToWindow();
|
resizeToWindow();
|
||||||
|
|
||||||
const input = new Input(canvas, viewport);
|
const input = new Input(canvas, viewport);
|
||||||
const isMobile = input.isMobile;
|
|
||||||
|
|
||||||
const fullscreenButton = document.getElementById('fullscreen');
|
const fullscreenButton = document.getElementById('fullscreen');
|
||||||
const updateFullscreenLabel = () => {
|
const updateFullscreenLabel = () => {
|
||||||
@@ -87,9 +86,7 @@ const updateCamera = (cx, cy) => {
|
|||||||
camera.y = Math.min(Math.max(0, targetY), Math.max(0, WORLD_H - viewport.height));
|
camera.y = Math.min(Math.max(0, targetY), Math.max(0, WORLD_H - viewport.height));
|
||||||
};
|
};
|
||||||
|
|
||||||
let autoAim = isMobile;
|
|
||||||
let muted = false;
|
let muted = false;
|
||||||
let autoAimNotice = 0;
|
|
||||||
let muteNotice = 0;
|
let muteNotice = 0;
|
||||||
let gameStarted = false;
|
let gameStarted = false;
|
||||||
let lastEventSeq = -1;
|
let lastEventSeq = -1;
|
||||||
@@ -555,7 +552,6 @@ const Lobby = (() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
el.name.value = me.name;
|
el.name.value = me.name;
|
||||||
el.room.value = localStorage.getItem('zombies.room') || '';
|
|
||||||
|
|
||||||
const setStatus = (text, error = false) => {
|
const setStatus = (text, error = false) => {
|
||||||
el.status.textContent = text;
|
el.status.textContent = text;
|
||||||
@@ -579,7 +575,20 @@ const Lobby = (() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const my = players.find((p) => p.id === me.id) || null;
|
const my = players.find((p) => p.id === me.id) || null;
|
||||||
el.players.textContent = `In room: ${players.map((p) => `${p.name}${p.host ? ' (host)' : ''}${p.id === me.id ? ' (you)' : ''}`).join(', ')}`;
|
el.players.textContent = '';
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.className = 'lobby-players-title';
|
||||||
|
title.textContent = 'Players';
|
||||||
|
el.players.appendChild(title);
|
||||||
|
for (const p of players) {
|
||||||
|
const row = document.createElement('div');
|
||||||
|
row.className = 'lobby-player';
|
||||||
|
row.textContent = `${p.name}${p.id === me.id ? ' (you)' : ''}${p.host ? ' (host)' : ''}`;
|
||||||
|
if (p.id === me.id) {
|
||||||
|
row.classList.add('you');
|
||||||
|
}
|
||||||
|
el.players.appendChild(row);
|
||||||
|
}
|
||||||
el.roomCode.textContent = me.room;
|
el.roomCode.textContent = me.room;
|
||||||
showInRoom();
|
showInRoom();
|
||||||
if (players.length === 1) {
|
if (players.length === 1) {
|
||||||
@@ -917,11 +926,11 @@ const drawHud = (state, my, now, t) => {
|
|||||||
textRenderer.draw(renderer, `Score: ${my.score}`, 12, 12, { size: 20, fillStyle: '#fff' });
|
textRenderer.draw(renderer, `Score: ${my.score}`, 12, 12, { size: 20, fillStyle: '#fff' });
|
||||||
textRenderer.draw(renderer, `HP: ${my.hp}`, 12, 40, { size: 20, fillStyle: '#fff' });
|
textRenderer.draw(renderer, `HP: ${my.hp}`, 12, 40, { size: 20, fillStyle: '#fff' });
|
||||||
|
|
||||||
if (autoAim) {
|
if (my.dead && state.state === 'playing' && state.players.some((p) => p.id !== my.id && !p.dead)) {
|
||||||
const aaEntry = textRenderer.measure('AUTO-AIM', { size: 14, fillStyle: '#4fe3ff' });
|
const specEntry = textRenderer.measure('SPECTATING', { size: 20, fillStyle: '#8a8a8a' });
|
||||||
textRenderer.draw(renderer, 'AUTO-AIM', viewport.width - aaEntry.width - 12, 12, {
|
textRenderer.draw(renderer, 'SPECTATING', viewport.width / 2 - specEntry.width / 2, 60, {
|
||||||
size: 14,
|
size: 20,
|
||||||
fillStyle: '#4fe3ff',
|
fillStyle: '#8a8a8a',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -947,15 +956,6 @@ const drawHud = (state, my, now, t) => {
|
|||||||
fillStyle: '#fff',
|
fillStyle: '#fff',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (autoAimNotice > 0) {
|
|
||||||
autoAimNotice -= t;
|
|
||||||
const notice = autoAim ? 'Auto-aim ON' : 'Auto-aim OFF';
|
|
||||||
const entry = textRenderer.measure(notice, { size: 22, fillStyle: '#fff' });
|
|
||||||
textRenderer.draw(renderer, notice, viewport.width / 2 - entry.width / 2, viewport.height / 2 - 20, {
|
|
||||||
size: 22,
|
|
||||||
fillStyle: '#4fe3ff',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void now;
|
void now;
|
||||||
};
|
};
|
||||||
@@ -978,20 +978,18 @@ const gameLoop = (now) => {
|
|||||||
muted = sounds.toggleMuted();
|
muted = sounds.toggleMuted();
|
||||||
muteNotice = 1.2;
|
muteNotice = 1.2;
|
||||||
}
|
}
|
||||||
if (!isMobile && input.pressed.has('q')) {
|
|
||||||
input.clearPressed();
|
|
||||||
autoAim = !autoAim;
|
|
||||||
autoAimNotice = 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.clear(0, 0, 0, 1);
|
renderer.clear(0, 0, 0, 1);
|
||||||
|
|
||||||
const snapshot = networking.curr;
|
const snapshot = networking.curr;
|
||||||
const frame = snapshot ? interpolate(now) : null;
|
const frame = snapshot ? interpolate(now) : null;
|
||||||
const own = frame ? frame.players.find((p) => p.id === me.id) || null : null;
|
const own = frame ? frame.players.find((p) => p.id === me.id) || null : null;
|
||||||
if (own) {
|
const anchor = own
|
||||||
entities.setTarget(own.x, own.y);
|
? (own.dead ? frame.players.find((p) => p.id !== me.id && !p.dead) || own : own)
|
||||||
updateCamera(own.x, own.y);
|
: null;
|
||||||
|
if (anchor) {
|
||||||
|
entities.setTarget(anchor.x, anchor.y);
|
||||||
|
updateCamera(anchor.x, anchor.y);
|
||||||
} else {
|
} else {
|
||||||
updateCamera(WORLD_W / 2, WORLD_H / 2);
|
updateCamera(WORLD_W / 2, WORLD_H / 2);
|
||||||
}
|
}
|
||||||
@@ -1084,7 +1082,6 @@ const gameLoop = (now) => {
|
|||||||
keys: [...input.keys],
|
keys: [...input.keys],
|
||||||
aim: { x: mouse.x - own.x, y: mouse.y - own.y },
|
aim: { x: mouse.x - own.x, y: mouse.y - own.y },
|
||||||
fire: input.mouseDown,
|
fire: input.mouseDown,
|
||||||
autoAim,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
networking.stopSendingInput();
|
networking.stopSendingInput();
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ export class NetEntities {
|
|||||||
this._stampPlayer(view, snapshot, now);
|
this._stampPlayer(view, snapshot, now);
|
||||||
if (!snapshot.dead) {
|
if (!snapshot.dead) {
|
||||||
view.draw(renderer);
|
view.draw(renderer);
|
||||||
|
if (snapshot.name) {
|
||||||
|
this._drawName(snapshot, renderer, textRenderer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +113,27 @@ export class NetEntities {
|
|||||||
this.players.clear();
|
this.players.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_drawName(snapshot, renderer, textRenderer) {
|
||||||
|
const hp = snapshot.hp ?? 0;
|
||||||
|
const mhp = snapshot.mhp ?? hp;
|
||||||
|
const label = `${snapshot.name} HP: ${hp}/${mhp}`;
|
||||||
|
const opts = { size: 12, fillStyle: '#fff', bold: true };
|
||||||
|
const entry = textRenderer.measure(label, opts);
|
||||||
|
|
||||||
|
const barW = 40;
|
||||||
|
const barH = 4;
|
||||||
|
const barX = snapshot.x - barW / 2;
|
||||||
|
const barY = snapshot.y - PLAYER_SIZE / 2 - barH - 2;
|
||||||
|
renderer.drawTexture(renderer.whiteTexture, barX, barY, barW, barH, [0, 0, 0, 0.6]);
|
||||||
|
const ratio = mhp > 0 ? Math.max(0, Math.min(1, hp / mhp)) : 0;
|
||||||
|
if (ratio > 0) {
|
||||||
|
const color = ratio > 2 / 3 ? [0.3, 0.9, 0.35, 0.9] : ratio > 1 / 3 ? [0.95, 0.8, 0.2, 0.9] : [0.9, 0.25, 0.25, 0.9];
|
||||||
|
renderer.drawTexture(renderer.whiteTexture, barX, barY, barW * ratio, barH, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
textRenderer.draw(renderer, label, snapshot.x - entry.width / 2, barY - entry.height - 2, opts);
|
||||||
|
}
|
||||||
|
|
||||||
_stamp(view, entity, now) {
|
_stamp(view, entity, now) {
|
||||||
view.pos.x = entity.x;
|
view.pos.x = entity.x;
|
||||||
view.pos.y = entity.y;
|
view.pos.y = entity.y;
|
||||||
|
|||||||
@@ -140,13 +140,12 @@ export class NetworkClient {
|
|||||||
|
|
||||||
_flushInput() {
|
_flushInput() {
|
||||||
if (!this._input || !this._connected()) return;
|
if (!this._input || !this._connected()) return;
|
||||||
const { id, keys, aim, fire, autoAim } = this._input;
|
const { id, keys, aim, fire } = this._input;
|
||||||
this.socket.trigger('client-input', this._inputChannel(), {
|
this.socket.trigger('client-input', this._inputChannel(), {
|
||||||
id,
|
id,
|
||||||
keys,
|
keys,
|
||||||
aim,
|
aim,
|
||||||
fire,
|
fire,
|
||||||
autoAim,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ final class GameEngineTest extends TestCase
|
|||||||
'health' => 1,
|
'health' => 1,
|
||||||
]);
|
]);
|
||||||
$room->addPlayer($player);
|
$room->addPlayer($player);
|
||||||
$zombie = new Zombie(944.4, 540.0, $room->entities, $room, ['emergeTime' => 0.0]);
|
$zombie = new Zombie(944.4, 540.0, $room->entities, $room, ['emergeTime' => 0.0, 'health' => 20]);
|
||||||
$zombie->target = $player;
|
$zombie->target = $player;
|
||||||
$room->entities->append($zombie);
|
$room->entities->append($zombie);
|
||||||
$engine->tick(1.0 / 60.0);
|
$engine->tick(1.0 / 60.0);
|
||||||
|
|||||||
@@ -69,6 +69,18 @@ final class GameSimulatorTest extends TestCase
|
|||||||
$this->assertSame(['p1' => true, 'p2' => false], $hosts);
|
$this->assertSame(['p1' => true, 'p2' => false], $hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function lobby_snapshots_have_monotonic_seq(): void
|
||||||
|
{
|
||||||
|
$sim = $this->makeSimulator();
|
||||||
|
$sim->applyControl('room-a', ['type' => 'create', 'id' => 'p1', 'name' => 'A']);
|
||||||
|
$firstSeq = $sim->room('room-a')->seq;
|
||||||
|
|
||||||
|
$sim->applyControl('room-a', ['type' => 'join', 'id' => 'p2', 'name' => 'B']);
|
||||||
|
|
||||||
|
$this->assertGreaterThan($firstSeq, $sim->room('room-a')->seq);
|
||||||
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function start_transitions_to_playing(): void
|
public function start_transitions_to_playing(): void
|
||||||
{
|
{
|
||||||
@@ -190,7 +202,7 @@ final class GameSimulatorTest extends TestCase
|
|||||||
$sim = $this->makeSimulator();
|
$sim = $this->makeSimulator();
|
||||||
$sim->applyControl('room-a', ['type' => 'create', 'id' => 'p1', 'name' => 'A']);
|
$sim->applyControl('room-a', ['type' => 'create', 'id' => 'p1', 'name' => 'A']);
|
||||||
$sim->applyControl('room-a', ['type' => 'start', 'id' => 'p1']);
|
$sim->applyControl('room-a', ['type' => 'start', 'id' => 'p1']);
|
||||||
$sim->applyInput('room-a', ['id' => 'p1', 'keys' => [], 'aim' => ['x' => 1.0, 'y' => 0.0], 'fire' => true, 'autoAim' => true]);
|
$sim->applyInput('room-a', ['id' => 'p1', 'keys' => [], 'aim' => ['x' => 1.0, 'y' => 0.0], 'fire' => true]);
|
||||||
$sawZombie = false;
|
$sawZombie = false;
|
||||||
for ($i = 0; $i < 600; $i++) {
|
for ($i = 0; $i < 600; $i++) {
|
||||||
$sim->tick(1.0 / 60.0);
|
$sim->tick(1.0 / 60.0);
|
||||||
@@ -337,7 +349,7 @@ final class GameSimulatorTest extends TestCase
|
|||||||
$zombie = new Zombie($player->pos->x + 40.0, $player->pos->y, $room->entities, $room, ['emergeTime' => 0.0]);
|
$zombie = new Zombie($player->pos->x + 40.0, $player->pos->y, $room->entities, $room, ['emergeTime' => 0.0]);
|
||||||
$room->entities->append($zombie);
|
$room->entities->append($zombie);
|
||||||
|
|
||||||
$sim->applyInput('room-a', ['id' => 'p1', 'keys' => [], 'aim' => ['x' => 1.0, 'y' => 0.0], 'fire' => true, 'autoAim' => false]);
|
$sim->applyInput('room-a', ['id' => 'p1', 'keys' => [], 'aim' => ['x' => 1.0, 'y' => 0.0], 'fire' => true]);
|
||||||
|
|
||||||
$hit = false;
|
$hit = false;
|
||||||
$kills = false;
|
$kills = false;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ final class PlayerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$room = new Room('test');
|
$room = new Room('test');
|
||||||
$room->state = Room::STATE_PLAYING;
|
$room->state = Room::STATE_PLAYING;
|
||||||
$input = new InputState(fire: false, autoAim: true);
|
$input = new InputState(fire: false);
|
||||||
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
||||||
$room->addPlayer($player);
|
$room->addPlayer($player);
|
||||||
$zombie = new Zombie(300.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
|
$zombie = new Zombie(300.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
|
||||||
@@ -69,7 +69,7 @@ final class PlayerTest extends TestCase
|
|||||||
public function auto_aim_does_not_fire_without_target(): void
|
public function auto_aim_does_not_fire_without_target(): void
|
||||||
{
|
{
|
||||||
$room = new Room('test');
|
$room = new Room('test');
|
||||||
$input = new InputState(fire: false, autoAim: true);
|
$input = new InputState(fire: false);
|
||||||
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
||||||
$room->addPlayer($player);
|
$room->addPlayer($player);
|
||||||
$player->update(1.0 / 60.0);
|
$player->update(1.0 / 60.0);
|
||||||
@@ -163,7 +163,7 @@ final class PlayerTest extends TestCase
|
|||||||
public function auto_aim_tracks_nearest_zombie(): void
|
public function auto_aim_tracks_nearest_zombie(): void
|
||||||
{
|
{
|
||||||
$room = new Room('test');
|
$room = new Room('test');
|
||||||
$input = new InputState(fire: true, autoAim: true);
|
$input = new InputState(fire: true);
|
||||||
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
$player = new Player('p1', 'Tester', $input, $room->entities, $room, ['x' => 100.0, 'y' => 100.0]);
|
||||||
$room->addPlayer($player);
|
$room->addPlayer($player);
|
||||||
$zombie = new Zombie(200.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
|
$zombie = new Zombie(200.0, 100.0, $room->entities, $room, ['emergeTime' => 0.0]);
|
||||||
|
|||||||
Reference in new issue
Block a user