Skip to content

Commit 7b6dc27

Browse files
feat(TerminalCanvas): implement pickup interaction and exit logic for improved gameplay experience
1 parent e43224d commit 7b6dc27

1 file changed

Lines changed: 118 additions & 31 deletions

File tree

src/components/terminal/TerminalCanvas.tsx

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,6 +2479,34 @@ export default function TerminalCanvas({
24792479
return bestIndex >= 0 ? { index: bestIndex, dist: bestDist } : null;
24802480
}, []);
24812481

2482+
const findNearestDoomPickupInFront = useCallback(
2483+
(radius: number, halfFov: number) => {
2484+
const player = doomPlayerRef.current;
2485+
let bestIndex = -1;
2486+
let bestDist = radius;
2487+
doomPickupsRef.current.forEach((pickup, index) => {
2488+
const dx = pickup.x - player.x;
2489+
const dy = pickup.y - player.y;
2490+
const dist = Math.hypot(dx, dy);
2491+
if (dist >= bestDist) {
2492+
return;
2493+
}
2494+
const angle = Math.atan2(dy, dx);
2495+
const delta = Math.atan2(
2496+
Math.sin(angle - player.angle),
2497+
Math.cos(angle - player.angle),
2498+
);
2499+
if (Math.abs(delta) > halfFov) {
2500+
return;
2501+
}
2502+
bestDist = dist;
2503+
bestIndex = index;
2504+
});
2505+
return bestIndex >= 0 ? { index: bestIndex, dist: bestDist } : null;
2506+
},
2507+
[],
2508+
);
2509+
24822510
const applyDoomPlayerDamage = useCallback(
24832511
(damage: number, flash = 2) => {
24842512
if (doomShieldRef.current > 0) {
@@ -2929,6 +2957,44 @@ export default function TerminalCanvas({
29292957
}, [doomText]);
29302958

29312959
const interactDoom = useCallback(() => {
2960+
if (doomGameOverRef.current) {
2961+
return false;
2962+
}
2963+
2964+
const player = doomPlayerRef.current;
2965+
const exit = doomExitRef.current;
2966+
if (exit && Math.hypot(exit.x - player.x, exit.y - player.y) < 1.05) {
2967+
const nextLevel = doomLevelRef.current + 1;
2968+
if (nextLevel < doomLevels.length) {
2969+
doomAmmoRef.current = Math.min(99, doomAmmoRef.current + 8);
2970+
doomHealthRef.current = Math.min(100, doomHealthRef.current + 10);
2971+
loadDoomLevel(nextLevel, false);
2972+
} else {
2973+
doomMessageRef.current = doomText("clear");
2974+
}
2975+
return true;
2976+
}
2977+
2978+
const pickupTarget = findNearestDoomPickupInFront(1.05, 0.55);
2979+
if (pickupTarget) {
2980+
const pickup = doomPickupsRef.current[pickupTarget.index];
2981+
if (pickup) {
2982+
const angle = Math.atan2(pickup.y - player.y, pickup.x - player.x);
2983+
const wallDist = castDoomDistance(
2984+
player,
2985+
angle,
2986+
pickupTarget.dist + 0.12,
2987+
);
2988+
if (wallDist + 0.05 >= pickupTarget.dist) {
2989+
applyDoomPickup(pickup);
2990+
doomPickupsRef.current = doomPickupsRef.current.filter(
2991+
(_, index) => index !== pickupTarget.index,
2992+
);
2993+
return true;
2994+
}
2995+
}
2996+
}
2997+
29322998
if (openDoomDoor()) {
29332999
return true;
29343000
}
@@ -2945,7 +3011,16 @@ export default function TerminalCanvas({
29453011
(_, index) => index !== nearest.index,
29463012
);
29473013
return true;
2948-
}, [applyDoomPickup, findNearestDoomPickup, openDoomDoor]);
3014+
}, [
3015+
applyDoomPickup,
3016+
castDoomDistance,
3017+
doomLevels.length,
3018+
doomText,
3019+
findNearestDoomPickup,
3020+
findNearestDoomPickupInFront,
3021+
loadDoomLevel,
3022+
openDoomDoor,
3023+
]);
29493024

29503025
const stepDoom = useCallback(() => {
29513026
const map = doomMapRef.current;
@@ -3049,7 +3124,7 @@ export default function TerminalCanvas({
30493124
door.open = clamp(next, 0, 1);
30503125
});
30513126

3052-
const autoPickup = findNearestDoomPickup(0.45);
3127+
const autoPickup = findNearestDoomPickup(0.6);
30533128
if (autoPickup) {
30543129
const pickup = doomPickupsRef.current[autoPickup.index];
30553130
if (pickup) {
@@ -3061,7 +3136,7 @@ export default function TerminalCanvas({
30613136
}
30623137

30633138
const pickups = doomPickupsRef.current;
3064-
const nearestPickup = findNearestDoomPickup(1.15);
3139+
const nearestPickup = findNearestDoomPickupInFront(1.15, 0.6);
30653140
let hint: string | null = null;
30663141
if (nearestPickup) {
30673142
const pickup = pickups[nearestPickup.index];
@@ -3094,22 +3169,10 @@ export default function TerminalCanvas({
30943169
exit &&
30953170
Math.hypot(exit.x - player.x, exit.y - player.y) < 1.1
30963171
) {
3097-
hint = doomText("exitReady");
3172+
hint = doomText("interactExit");
30983173
}
30993174
doomHintRef.current = hint;
31003175

3101-
if (exit && Math.hypot(exit.x - player.x, exit.y - player.y) < 0.6) {
3102-
const nextLevel = doomLevelRef.current + 1;
3103-
if (nextLevel < doomLevels.length) {
3104-
doomAmmoRef.current = Math.min(99, doomAmmoRef.current + 8);
3105-
doomHealthRef.current = Math.min(100, doomHealthRef.current + 10);
3106-
loadDoomLevel(nextLevel, false);
3107-
} else {
3108-
doomMessageRef.current = doomText("clear");
3109-
}
3110-
return;
3111-
}
3112-
31133176
const bomb = doomBombRef.current;
31143177
if (bomb?.active) {
31153178
bomb.x += bomb.vx;
@@ -3322,11 +3385,10 @@ export default function TerminalCanvas({
33223385
applyDoomPickup,
33233386
applyDoomPlayerDamage,
33243387
castDoomDistance,
3325-
doomLevels.length,
33263388
doomText,
33273389
findNearestDoomPickup,
3390+
findNearestDoomPickupInFront,
33283391
getDoomBootProgress,
3329-
loadDoomLevel,
33303392
perfTier,
33313393
triggerDoomExplosion,
33323394
]);
@@ -7273,6 +7335,8 @@ export default function TerminalCanvas({
72737335
ctx.fillStyle = "rgba(255,255,255,0.05)";
72747336
ctx.fillRect(pad + 1, textAreaY - 4, boxW - 2, 1);
72757337

7338+
const textFont = `${fontSize}px "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace`;
7339+
ctx.font = textFont;
72767340
const charWidth = Math.max(6, Math.ceil(ctx.measureText("M").width));
72777341
const visibleLines = Math.max(1, Math.floor(textAreaH / lineHeight));
72787342
const maxLineDigits = String(state.buffer.length).length;
@@ -7679,14 +7743,22 @@ export default function TerminalCanvas({
76797743
if (!textures) {
76807744
return null;
76817745
}
7682-
const v = (tileX * 31 + tileY * 17 + (tileX ^ tileY) * 13) % 3;
7683-
if (v === 1) {
7684-
return textures.tech;
7746+
const level = doomLevelRef.current;
7747+
const palette = [textures.brick, textures.tech, textures.stone];
7748+
const base = palette[level % palette.length] ?? textures.brick;
7749+
const accentA =
7750+
palette[(level + 1) % palette.length] ?? textures.tech;
7751+
const accentB =
7752+
palette[(level + 2) % palette.length] ?? textures.stone;
7753+
const key =
7754+
(tileX * 11 + tileY * 7 + (tileX ^ tileY) * 3 + level * 13) % 17;
7755+
if (key === 0) {
7756+
return accentA;
76857757
}
7686-
if (v === 2) {
7687-
return textures.stone;
7758+
if (key === 1) {
7759+
return accentB;
76887760
}
7689-
return textures.brick;
7761+
return base;
76907762
};
76917763
const now =
76927764
typeof performance !== "undefined" ? performance.now() : Date.now();
@@ -8277,7 +8349,10 @@ export default function TerminalCanvas({
82778349
: sprite.kind === "bomb"
82788350
? 0.34
82798351
: 0.48;
8280-
const spriteH = Math.max(10, Math.floor(baseH * scale));
8352+
const spriteH = Math.min(
8353+
Math.floor(viewH * 0.78),
8354+
Math.max(10, Math.floor(baseH * scale)),
8355+
);
82818356
const aspect =
82828357
sprite.kind === "pillar"
82838358
? 0.35
@@ -8380,7 +8455,10 @@ export default function TerminalCanvas({
83808455
: enemy.type === "spitter"
83818456
? 1.05
83828457
: 0.96;
8383-
const spriteH = Math.max(22, Math.floor(baseH * 0.92 * enemyScale));
8458+
const spriteH = Math.min(
8459+
Math.floor(viewH * 0.92),
8460+
Math.max(22, Math.floor(baseH * 0.92 * enemyScale)),
8461+
);
83848462
const spriteW = Math.max(
83858463
18,
83868464
Math.floor(spriteH * (enemy.type === "brute" ? 0.62 : 0.55)),
@@ -8592,9 +8670,12 @@ export default function TerminalCanvas({
85928670
}
85938671
const baseH = Math.min(viewH, Math.floor(viewH / dist));
85948672
const isRocket = projectile.kind === "rocket";
8595-
const size = Math.max(
8596-
isRocket ? 10 : 8,
8597-
Math.floor(baseH * (isRocket ? 0.22 : 0.16)),
8673+
const size = Math.min(
8674+
Math.floor(viewH * 0.28),
8675+
Math.max(
8676+
isRocket ? 10 : 8,
8677+
Math.floor(baseH * (isRocket ? 0.22 : 0.16)),
8678+
),
85988679
);
85998680
const top = Math.floor(
86008681
viewY + viewH * 0.48 - size / 2 + cameraBob * 0.2,
@@ -8693,7 +8774,10 @@ export default function TerminalCanvas({
86938774
const screenX =
86948775
viewX + (0.5 + delta / fov) * Math.max(1, viewW);
86958776
const spriteH = Math.min(viewH, Math.floor(viewH / dist));
8696-
const size = Math.max(6, Math.floor(spriteH * 0.15));
8777+
const size = Math.min(
8778+
Math.floor(viewH * 0.18),
8779+
Math.max(6, Math.floor(spriteH * 0.15)),
8780+
);
86978781
const top = Math.floor(viewY + (viewH - size) / 2);
86988782
ctx.fillStyle = "#f4d35e";
86998783
ctx.beginPath();
@@ -8724,7 +8808,10 @@ export default function TerminalCanvas({
87248808
const screenX =
87258809
viewX + (0.5 + delta / fov) * Math.max(1, viewW);
87268810
const spriteH = Math.min(viewH, Math.floor(viewH / dist));
8727-
const size = Math.max(16, Math.floor(spriteH * 0.35));
8811+
const size = Math.min(
8812+
Math.floor(viewH * 0.45),
8813+
Math.max(16, Math.floor(spriteH * 0.35)),
8814+
);
87288815
const top = Math.floor(viewY + (viewH - size) / 2);
87298816
ctx.fillStyle = "rgba(246,194,122,0.6)";
87308817
ctx.beginPath();

0 commit comments

Comments
 (0)