Skip to content

Commit 9877ea7

Browse files
committed
should have been in prev commit.
1 parent 12e9b63 commit 9877ea7

2 files changed

Lines changed: 554 additions & 0 deletions

File tree

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Flashlight Test</title>
7+
<style>
8+
html,
9+
body {
10+
height: 100%;
11+
margin: 0;
12+
background: #0b0b12;
13+
}
14+
#game {
15+
position: fixed;
16+
inset: 0;
17+
}
18+
.tip {
19+
position: fixed;
20+
left: 12px;
21+
top: 12px;
22+
color: #e7e7ee;
23+
font: 14px/1.3 Arial;
24+
opacity: 0.85;
25+
user-select: none;
26+
}
27+
.tip kbd {
28+
background: #1f2030;
29+
padding: 2px 6px;
30+
border-radius: 6px;
31+
border: 1px solid #2c2e44;
32+
}
33+
.tip .pill {
34+
background: #1d2b43;
35+
padding: 2px 6px;
36+
border-radius: 999px;
37+
border: 1px solid #2a3b5d;
38+
}
39+
</style>
40+
</head>
41+
<body>
42+
<div id="game"></div>
43+
<div class="tip">
44+
<div>
45+
<strong>WASD</strong> to move • Flashlight points at your cursor
46+
</div>
47+
<div style="margin-top: 6px">
48+
Press <kbd>R</kbd> to reroll obstacles
49+
</div>
50+
<div style="margin-top: 6px" class="pill">Pink = walls • Blue = you</div>
51+
</div>
52+
<div id="diag"></div>
53+
54+
<script type="module">
55+
// Pixi v8 compatible: no deprecated API, no invalid imports
56+
import {
57+
Application,
58+
Graphics,
59+
Container,
60+
Point,
61+
} from "https://cdn.jsdelivr.net/npm/pixi.js@8.x/dist/pixi.mjs";
62+
63+
// ---- PIXI APP ----
64+
const app = new Application();
65+
await app.init({
66+
backgroundAlpha: 0,
67+
antialias: true,
68+
resolution: Math.min(devicePixelRatio, 2),
69+
resizeTo: window,
70+
});
71+
document.getElementById("game").appendChild(app.canvas);
72+
73+
const world = new Container();
74+
app.stage.addChild(world);
75+
76+
const state = {
77+
speed: 260,
78+
coneLength: 520, // flashlight reach
79+
coneWidthDeg: 46, // full width in degrees
80+
obstacles: [],
81+
keys: new Set(),
82+
pointer: new Point(app.screen.width / 2, app.screen.height / 2),
83+
};
84+
85+
// Player (blue square)
86+
const player = new Graphics()
87+
.rect(0, 0, 28, 28)
88+
.fill({ color: 0x3b82f6 }); // blue
89+
player.pivot.set(14, 14);
90+
player.position.set(app.screen.width / 2, app.screen.height / 2);
91+
world.addChild(player);
92+
93+
// Obstacles (random pink rectangles)
94+
const obstaclesLayer = new Container();
95+
world.addChild(obstaclesLayer);
96+
97+
function rerollObstacles() {
98+
obstaclesLayer.removeChildren();
99+
state.obstacles.length = 0;
100+
const W = app.screen.width,
101+
H = app.screen.height;
102+
const count = Math.round((W * H) / 60000); // density scales with area
103+
for (let i = 0; i < count; i++) {
104+
const w = 50 + Math.random() * 140;
105+
const h = 40 + Math.random() * 120;
106+
const x = Math.random() * (W - w) + w / 2;
107+
const y = Math.random() * (H - h) + h / 2;
108+
// Avoid initial spawn too close to player
109+
const dx = x - player.x,
110+
dy = y - player.y;
111+
if (Math.hypot(dx, dy) < 120) {
112+
i--;
113+
continue;
114+
}
115+
116+
const r = new Graphics()
117+
.roundRect(0, 0, w, h, 8)
118+
.fill({ color: 0xff6bb3 }); // pink
119+
r.pivot.set(w / 2, h / 2);
120+
r.position.set(x, y);
121+
obstaclesLayer.addChild(r);
122+
state.obstacles.push(r);
123+
}
124+
}
125+
126+
rerollObstacles();
127+
128+
const visionMask = new Graphics();
129+
const darkness = new Graphics();
130+
darkness
131+
.rect(0, 0, app.screen.width, app.screen.height)
132+
.fill({ color: 0x000000, alpha: 0.95 });
133+
134+
darkness.setMask({ mask: visionMask, inverse: true });
135+
136+
app.stage.addChild(darkness, visionMask);
137+
138+
// ---- INPUT ----
139+
window.addEventListener("keydown", (e) => {
140+
const k = e.key.toLowerCase();
141+
if (
142+
[
143+
"w",
144+
"a",
145+
"s",
146+
"d",
147+
"arrowup",
148+
"arrowdown",
149+
"arrowleft",
150+
"arrowright",
151+
].includes(k)
152+
) {
153+
state.keys.add(k);
154+
}
155+
if (k === "r") rerollObstacles();
156+
});
157+
window.addEventListener("keyup", (e) =>
158+
state.keys.delete(e.key.toLowerCase())
159+
);
160+
161+
// Pointer tracking
162+
app.stage.eventMode = "static";
163+
app.stage.hitArea = app.screen; // screen-sized interactive area
164+
app.stage.on("pointermove", (e) => state.pointer.copyFrom(e.global));
165+
166+
// ---- UTILS ----
167+
function clamp(v, min, max) {
168+
return Math.max(min, Math.min(max, v));
169+
}
170+
171+
function updatePlayer(dt) {
172+
let vx = 0,
173+
vy = 0;
174+
if (state.keys.has("w") || state.keys.has("arrowup")) vy -= 1;
175+
if (state.keys.has("s") || state.keys.has("arrowdown")) vy += 1;
176+
if (state.keys.has("a") || state.keys.has("arrowleft")) vx -= 1;
177+
if (state.keys.has("d") || state.keys.has("arrowright")) vx += 1;
178+
if (vx || vy) {
179+
const l = Math.hypot(vx, vy) || 1;
180+
vx /= l;
181+
vy /= l;
182+
player.x += vx * state.speed * dt;
183+
player.y += vy * state.speed * dt;
184+
}
185+
// Stay on screen
186+
player.x = clamp(player.x, 14, app.screen.width - 14);
187+
player.y = clamp(player.y, 14, app.screen.height - 14);
188+
}
189+
190+
function redrawVisionMask() {
191+
const ang = Math.atan2(
192+
state.pointer.y - player.y,
193+
state.pointer.x - player.x
194+
);
195+
const half = (state.coneWidthDeg * Math.PI) / 180 / 2;
196+
const L = state.coneLength;
197+
198+
const leftX = player.x + Math.cos(ang - half) * L;
199+
const leftY = player.y + Math.sin(ang - half) * L;
200+
201+
visionMask.clear();
202+
203+
// console.log(ang, half, ang - half, ang + half);
204+
205+
// Full screen darkness
206+
visionMask
207+
.moveTo(player.x, player.y)
208+
.lineTo(leftX, leftY)
209+
.arc(player.x, player.y, L, ang - half, ang + half, false)
210+
.lineTo(player.x, player.y)
211+
.fill({ color: 0x00ffcc, alpha: 1 });
212+
// .cut(); // don't need anymore we are using inverse
213+
visionMask.circle(player.x, player.y, 36).fill({ color: 0x00ffcc, alpha: 1 })
214+
}
215+
216+
app.ticker.add(() => {
217+
const dt = app.ticker.deltaMS / 1000;
218+
updatePlayer(dt);
219+
redrawVisionMask();
220+
});
221+
222+
223+
224+
</script>
225+
</body>
226+
</html>

0 commit comments

Comments
 (0)