pacman-contribution-graph 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/assets/packman-demo.png +0 -0
- package/dist/pacman-contribution-graph.js +372 -341
- package/dist/pacman-contribution-graph.js.map +1 -1
- package/dist/pacman-contribution-graph.min.js +1 -1
- package/embeded/canvas.html +41 -0
- package/embeded/svg.html +18 -0
- package/index.html +79 -47
- package/package.json +3 -2
- package/server/api/contributions/route.ts.z +31 -0
- package/server/page.zts.z +13 -0
- package/src/assets/images/ghosts/blinky.png +0 -0
- package/src/assets/images/ghosts/clyde.png +0 -0
- package/src/assets/images/ghosts/inky.png +0 -0
- package/src/assets/images/ghosts/pinky.png +0 -0
- package/src/assets/images/ghosts/scared.png +0 -0
- package/src/canvas.ts +82 -94
- package/src/constants.ts +29 -2
- package/src/game.ts +144 -121
- package/src/index.ts +38 -23
- package/src/music-player.ts +13 -7
- package/src/store.ts +3 -2
- package/src/svg.ts +69 -78
- package/src/types.ts +7 -4
- package/src/utils.ts +7 -5
package/index.html
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="google-site-verification" content="ETILrgmBZ33RjlxJh-ybUFcIlIyKXyefMvxbxC6LmOU" />
|
|
6
7
|
<title>GitLab Contributions Pac-Man</title>
|
|
7
8
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" />
|
|
8
9
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
<body>
|
|
46
47
|
<nav class="navbar navbar-expand-lg fixed-top">
|
|
47
48
|
<div class="container">
|
|
48
|
-
<a class="navbar-brand" href="
|
|
49
|
+
<a class="navbar-brand" href="https://abozanona.github.io/pacman-contribution-graph/">Pac-Man Contributions</a>
|
|
49
50
|
<button
|
|
50
51
|
class="navbar-toggler"
|
|
51
52
|
type="button"
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
<div class="collapse navbar-collapse" id="navbarNav">
|
|
61
62
|
<ul class="navbar-nav ms-auto">
|
|
62
63
|
<li class="nav-item">
|
|
63
|
-
<a class="nav-link" href="https://
|
|
64
|
+
<a class="nav-link" href="https://github.com/abozanona/pacman-contribution-graph/" target="_blank"
|
|
64
65
|
><i class="fab fa-github"></i> GitHub</a
|
|
65
66
|
>
|
|
66
67
|
</li>
|
|
@@ -148,10 +149,6 @@
|
|
|
148
149
|
</div>
|
|
149
150
|
</div>
|
|
150
151
|
<button id="generate" class="btn btn-primary">Generate</button>
|
|
151
|
-
<div class="alert alert-info my-2">
|
|
152
|
-
<strong>Note:</strong> You might need to refresh the page after eachtime you change configuration. This is a known issue
|
|
153
|
-
that we're working on it
|
|
154
|
-
</div>
|
|
155
152
|
</div>
|
|
156
153
|
|
|
157
154
|
<div class="section">
|
|
@@ -162,6 +159,7 @@
|
|
|
162
159
|
<br />
|
|
163
160
|
<button id="downloadSvg" class="btn btn-success mt-3" style="display: none">Download SVG</button>
|
|
164
161
|
</div>
|
|
162
|
+
<div>Commits count: <span id="points">0</span></div>
|
|
165
163
|
</div>
|
|
166
164
|
|
|
167
165
|
<div class="section">
|
|
@@ -229,49 +227,43 @@
|
|
|
229
227
|
<h3>Method 1: NPM Package</h3>
|
|
230
228
|
<pre><code>npm install pacman-contribution-graph
|
|
231
229
|
|
|
232
|
-
import {
|
|
230
|
+
import { PacmanRenderer } from 'pacman-contribution-graph';
|
|
233
231
|
|
|
234
|
-
|
|
232
|
+
const pr = new PacmanRenderer({
|
|
235
233
|
platform: 'github',
|
|
236
234
|
username: 'yourusername',
|
|
237
235
|
canvas: document.getElementById('canvas'),
|
|
238
236
|
outputFormat: 'canvas',
|
|
239
237
|
gameTheme: 'github'
|
|
240
|
-
})
|
|
238
|
+
});
|
|
239
|
+
pr.start();
|
|
240
|
+
</code></pre>
|
|
241
241
|
|
|
242
242
|
<h3>Method 2: Script loading</h3>
|
|
243
243
|
<pre><code><script type="module">
|
|
244
|
-
import {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
console.log('GAME OVER');
|
|
254
|
-
setTimeout(() => {
|
|
255
|
-
console.log('Restarting');
|
|
256
|
-
initializeGameCanvas();
|
|
257
|
-
}, 3000);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
initializeGameCanvas();
|
|
244
|
+
import { PacmanRenderer } from 'https://cdn.jsdelivr.net/gh/abozanona/pacman-contribution-graph@1.0.1/dist/pacman-contribution-graph.min.js';
|
|
245
|
+
const pr = new PacmanRenderer({
|
|
246
|
+
platform: 'github',
|
|
247
|
+
username: 'yourusername',
|
|
248
|
+
canvas: document.getElementById('canvas'),
|
|
249
|
+
outputFormat: 'canvas',
|
|
250
|
+
gameTheme: 'github'
|
|
251
|
+
});
|
|
252
|
+
pr.start();
|
|
261
253
|
</script></code></pre>
|
|
262
254
|
|
|
263
255
|
<h3>Method 3: Iframe/Image Embedding</h3>
|
|
264
256
|
<pre>
|
|
265
257
|
<!-- For canvas -->
|
|
266
|
-
<code><iframe src="https://abozanona.github.io/pacman-contribution-graph
|
|
258
|
+
<code><iframe src="https://abozanona.github.io/pacman-contribution-graph/embeded/canvas.html?username=yourusername&platform=github"
|
|
267
259
|
width="800" height="600" frameborder="0"></iframe></code>
|
|
268
260
|
<!-- For SVG -->
|
|
269
|
-
<code><img src="https://abozanona.github.io/pacman-contribution-graph
|
|
261
|
+
<code><img src="https://abozanona.github.io/pacman-contribution-graph/embeded/svg.html?username=yourusername&platform=github" /></code>
|
|
270
262
|
</pre>
|
|
271
263
|
</div>
|
|
272
264
|
</div>
|
|
273
265
|
|
|
274
|
-
<footer class="bg-light py-3 mt-5">
|
|
266
|
+
<footer class="bg-light py-3 mt-5 invisible">
|
|
275
267
|
<div class="container text-center">
|
|
276
268
|
<div>
|
|
277
269
|
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
|
|
@@ -284,7 +276,7 @@ initializeGameCanvas();
|
|
|
284
276
|
|
|
285
277
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
|
286
278
|
<script type="module">
|
|
287
|
-
import {
|
|
279
|
+
import { PacmanRenderer } from './dist/pacman-contribution-graph.js';
|
|
288
280
|
|
|
289
281
|
const generateButton = document.getElementById('generate');
|
|
290
282
|
const usernameInput = document.getElementById('username');
|
|
@@ -300,6 +292,7 @@ initializeGameCanvas();
|
|
|
300
292
|
const downloadSvgButton = document.getElementById('downloadSvg');
|
|
301
293
|
const userAvatar = document.getElementById('userAvatar');
|
|
302
294
|
const userNotFound = document.getElementById('userNotFound');
|
|
295
|
+
const points = document.getElementById('points');
|
|
303
296
|
|
|
304
297
|
generateButton.disabled = true;
|
|
305
298
|
|
|
@@ -310,6 +303,7 @@ initializeGameCanvas();
|
|
|
310
303
|
});
|
|
311
304
|
|
|
312
305
|
usernameInput.addEventListener('input', debounce(checkUser, 500));
|
|
306
|
+
platformSelect.addEventListener('input', debounce(checkUser, 500));
|
|
313
307
|
|
|
314
308
|
function debounce(func, wait) {
|
|
315
309
|
let timeout;
|
|
@@ -333,24 +327,48 @@ initializeGameCanvas();
|
|
|
333
327
|
return;
|
|
334
328
|
}
|
|
335
329
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
330
|
+
if (platform == 'github') {
|
|
331
|
+
try {
|
|
332
|
+
const response = await fetch(`https://api.github.com/users/${username}`);
|
|
333
|
+
if (response.ok) {
|
|
334
|
+
const data = await response.json();
|
|
335
|
+
userAvatar.src = data.avatar_url;
|
|
336
|
+
userAvatar.style.display = 'inline';
|
|
337
|
+
userNotFound.style.display = 'none';
|
|
338
|
+
generateButton.disabled = false;
|
|
339
|
+
} else {
|
|
340
|
+
throw new Error('User not found');
|
|
341
|
+
}
|
|
342
|
+
} catch (error) {
|
|
343
|
+
userAvatar.style.display = 'none';
|
|
344
|
+
userNotFound.style.display = 'block';
|
|
345
|
+
generateButton.disabled = true;
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
try {
|
|
349
|
+
const response = await fetch(`https://gitlab.com/api/v4/users?username=${username}`);
|
|
350
|
+
if (response.ok) {
|
|
351
|
+
const data = await response.json();
|
|
352
|
+
if (!data.length) {
|
|
353
|
+
throw new Error('User not found');
|
|
354
|
+
}
|
|
355
|
+
userAvatar.src = data[0].avatar_url;
|
|
356
|
+
userAvatar.style.display = 'inline';
|
|
357
|
+
userNotFound.style.display = 'none';
|
|
358
|
+
generateButton.disabled = false;
|
|
359
|
+
} else {
|
|
360
|
+
throw new Error('User not found');
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
userAvatar.style.display = 'none';
|
|
364
|
+
userNotFound.style.display = 'block';
|
|
365
|
+
generateButton.disabled = true;
|
|
346
366
|
}
|
|
347
|
-
} catch (error) {
|
|
348
|
-
userAvatar.style.display = 'none';
|
|
349
|
-
userNotFound.style.display = 'block';
|
|
350
|
-
generateButton.disabled = true;
|
|
351
367
|
}
|
|
352
368
|
}
|
|
353
369
|
|
|
370
|
+
let pr = undefined;
|
|
371
|
+
|
|
354
372
|
generateButton.addEventListener('click', () => {
|
|
355
373
|
const config = {
|
|
356
374
|
platform: platformSelect.value,
|
|
@@ -365,19 +383,29 @@ initializeGameCanvas();
|
|
|
365
383
|
canvas.style.display = 'block';
|
|
366
384
|
svgContainer.style.display = 'none';
|
|
367
385
|
downloadSvgButton.style.display = 'none';
|
|
368
|
-
|
|
386
|
+
if (pr !== undefined) {
|
|
387
|
+
pr.stop();
|
|
388
|
+
}
|
|
389
|
+
pr = new PacmanRenderer({
|
|
369
390
|
...config,
|
|
370
391
|
canvas: canvas,
|
|
371
392
|
gameOverCallback: () => {
|
|
372
393
|
console.log('Game Over');
|
|
373
|
-
setTimeout(() =>
|
|
394
|
+
setTimeout(() => pr.start(), 3000);
|
|
395
|
+
},
|
|
396
|
+
pointsIncreasedCallback: (pointsSum) => {
|
|
397
|
+
points.innerText = pointsSum;
|
|
374
398
|
}
|
|
375
399
|
});
|
|
400
|
+
pr.start();
|
|
376
401
|
} else {
|
|
377
402
|
canvas.style.display = 'none';
|
|
378
403
|
svgContainer.style.display = 'block';
|
|
379
404
|
downloadSvgButton.style.display = 'inline-block';
|
|
380
|
-
|
|
405
|
+
if (pr !== undefined) {
|
|
406
|
+
pr.stop();
|
|
407
|
+
}
|
|
408
|
+
pr = new PacmanRenderer({
|
|
381
409
|
...config,
|
|
382
410
|
svgCallback: (blobUrl) => {
|
|
383
411
|
svgHolder.src = blobUrl;
|
|
@@ -385,8 +413,12 @@ initializeGameCanvas();
|
|
|
385
413
|
},
|
|
386
414
|
gameOverCallback: () => {
|
|
387
415
|
console.log('Game Over');
|
|
416
|
+
},
|
|
417
|
+
pointsIncreasedCallback: (pointsSum) => {
|
|
418
|
+
points.innerText = pointsSum;
|
|
388
419
|
}
|
|
389
420
|
});
|
|
421
|
+
pr.start();
|
|
390
422
|
}
|
|
391
423
|
});
|
|
392
424
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pacman-contribution-graph",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Generates a pacman game from a github or gitlab user contributions grid",
|
|
5
5
|
"repository": "github:abozanona/pacman-contribution-graph",
|
|
6
|
+
"homepage": "https://abozanona.github.io/pacman-contribution-graph/",
|
|
6
7
|
"type": "module",
|
|
7
|
-
"main": "
|
|
8
|
+
"main": "dist/pacman-contribution-graph.min.js",
|
|
8
9
|
"scripts": {
|
|
9
10
|
"build": "webpack --config webpack.prod.js",
|
|
10
11
|
"dev": "webpack --progress --config webpack.dev.js"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
export async function GET(request: Request) {
|
|
4
|
+
const { searchParams } = new URL(request.url);
|
|
5
|
+
const username = searchParams.get('username');
|
|
6
|
+
|
|
7
|
+
if (!username) {
|
|
8
|
+
return NextResponse.json({ error: 'Username is required' }, { status: 400 });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(`https://gitlab.com/users/${username}/calendar.json`);
|
|
13
|
+
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error('Failed to fetch contributions from GitLab');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const contributionsList = await response.json();
|
|
19
|
+
|
|
20
|
+
// Create a new response with CORS headers
|
|
21
|
+
const corsResponse = NextResponse.json(contributionsList);
|
|
22
|
+
corsResponse.headers.set('Access-Control-Allow-Origin', '*');
|
|
23
|
+
corsResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
24
|
+
corsResponse.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
25
|
+
|
|
26
|
+
return corsResponse;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Error fetching contributions:', error);
|
|
29
|
+
return NextResponse.json({ error: 'Failed to fetch contributions' }, { status: 500 });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
export default function Home() {
|
|
3
|
+
return (
|
|
4
|
+
<div className="p-8">
|
|
5
|
+
<h1 className="text-3xl font-bold mb-4">GitHub and GitLab API Routes</h1>
|
|
6
|
+
<p className="mb-4">Use the following API routes:</p>
|
|
7
|
+
<ul className="list-disc pl-8">
|
|
8
|
+
<li>/api/contributions?username=USERNAME</li>
|
|
9
|
+
</ul>
|
|
10
|
+
<p className="mt-4">Replace 'USERNAME' with an actual GitLab username.</p>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/canvas.ts
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { CELL_SIZE, GAP_SIZE, GRID_HEIGHT, GRID_WIDTH, PACMAN_COLOR, PACMAN_COLOR_DEAD, PACMAN_COLOR_POWERUP } from './constants';
|
|
1
|
+
import { CELL_SIZE, GAP_SIZE, GHOSTS, GRID_HEIGHT, GRID_WIDTH, PACMAN_COLOR, PACMAN_COLOR_DEAD, PACMAN_COLOR_POWERUP } from './constants';
|
|
2
2
|
import { MusicPlayer } from './music-player';
|
|
3
|
-
import {
|
|
3
|
+
import { StoreType } from './types';
|
|
4
4
|
import { Utils } from './utils';
|
|
5
5
|
|
|
6
|
-
const resizeCanvas = () => {
|
|
6
|
+
const resizeCanvas = (store: StoreType) => {
|
|
7
7
|
const canvasWidth = GRID_WIDTH * (CELL_SIZE + GAP_SIZE);
|
|
8
8
|
const canvasHeight = GRID_HEIGHT * (CELL_SIZE + GAP_SIZE) + 20; // Adding some space for months on top
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
store.config.canvas.width = canvasWidth;
|
|
11
|
+
store.config.canvas.height = canvasHeight;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
const drawGrid = () => {
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const drawGrid = (store: StoreType) => {
|
|
15
|
+
store.config.canvas.getContext('2d')!.fillStyle = Utils.getCurrentTheme(store).gridBackground;
|
|
16
|
+
store.config.canvas.getContext('2d')!.fillRect(0, 0, store.config.canvas.width, store.config.canvas.height);
|
|
17
17
|
|
|
18
18
|
for (let x = 0; x < GRID_HEIGHT; x++) {
|
|
19
19
|
for (let y = 0; y < GRID_WIDTH; y++) {
|
|
20
|
-
const intensity =
|
|
20
|
+
const intensity = store.grid[x][y].intensity;
|
|
21
21
|
if (intensity > 0) {
|
|
22
22
|
const adjustedIntensity = intensity < 0.2 ? 0.3 : intensity;
|
|
23
|
-
const color = Utils.hexToRGBA(Utils.getCurrentTheme().contributionBoxColor, adjustedIntensity);
|
|
24
|
-
|
|
23
|
+
const color = Utils.hexToRGBA(Utils.getCurrentTheme(store).contributionBoxColor, adjustedIntensity);
|
|
24
|
+
store.config.canvas.getContext('2d')!.fillStyle = color;
|
|
25
25
|
} else {
|
|
26
|
-
|
|
26
|
+
store.config.canvas.getContext('2d')!.fillStyle = Utils.getCurrentTheme(store).emptyContributionBoxColor;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
29
|
+
store.config.canvas
|
|
30
30
|
.getContext('2d')!
|
|
31
31
|
.roundRect(y * (CELL_SIZE + GAP_SIZE), x * (CELL_SIZE + GAP_SIZE) + 15, CELL_SIZE, CELL_SIZE, 5);
|
|
32
|
-
|
|
32
|
+
store.config.canvas.getContext('2d')!.fill();
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
store.config.canvas.getContext('2d')!.fillStyle = Utils.getCurrentTheme(store).textColor;
|
|
37
|
+
store.config.canvas.getContext('2d')!.font = '10px Arial';
|
|
38
|
+
store.config.canvas.getContext('2d')!.textAlign = 'center';
|
|
39
39
|
|
|
40
40
|
let lastMonth = '';
|
|
41
41
|
for (let y = 0; y < GRID_WIDTH; y++) {
|
|
42
|
-
if (
|
|
42
|
+
if (store.monthLabels[y] !== lastMonth) {
|
|
43
43
|
const xPos = y * (CELL_SIZE + GAP_SIZE) + CELL_SIZE / 2;
|
|
44
|
-
|
|
45
|
-
lastMonth =
|
|
44
|
+
store.config.canvas.getContext('2d')!.fillText(store.monthLabels[y], xPos, 10);
|
|
45
|
+
lastMonth = store.monthLabels[y];
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const drawPacman = () => {
|
|
51
|
-
const x =
|
|
52
|
-
const y =
|
|
50
|
+
const drawPacman = (store: StoreType) => {
|
|
51
|
+
const x = store.pacman.y * (CELL_SIZE + GAP_SIZE) + CELL_SIZE / 2;
|
|
52
|
+
const y = store.pacman.x * (CELL_SIZE + GAP_SIZE) + CELL_SIZE / 2 + 15;
|
|
53
53
|
const radius = CELL_SIZE / 2;
|
|
54
54
|
|
|
55
55
|
// Change Pac-Man's color to red if he's on power-up, dead, else yellow
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
} else if (
|
|
59
|
-
|
|
56
|
+
if (store.pacman.deadRemainingDuration) {
|
|
57
|
+
store.config.canvas.getContext('2d')!.fillStyle = PACMAN_COLOR_DEAD;
|
|
58
|
+
} else if (store.pacman.powerupRemainingDuration) {
|
|
59
|
+
store.config.canvas.getContext('2d')!.fillStyle = PACMAN_COLOR_POWERUP;
|
|
60
60
|
} else {
|
|
61
|
-
|
|
61
|
+
store.config.canvas.getContext('2d')!.fillStyle = PACMAN_COLOR;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const mouthAngle =
|
|
64
|
+
const mouthAngle = store.pacmanMouthOpen ? 0.35 * Math.PI : 0.1 * Math.PI;
|
|
65
65
|
|
|
66
66
|
let startAngle, endAngle;
|
|
67
|
-
switch (
|
|
67
|
+
switch (store.pacman.direction) {
|
|
68
68
|
case 'up':
|
|
69
69
|
startAngle = 1.5 * Math.PI + mouthAngle;
|
|
70
70
|
endAngle = 1.5 * Math.PI - mouthAngle;
|
|
@@ -84,99 +84,87 @@ const drawPacman = () => {
|
|
|
84
84
|
break;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
88
|
+
store.config.canvas.getContext('2d')!.arc(x, y, radius, startAngle, endAngle);
|
|
89
|
+
store.config.canvas.getContext('2d')!.lineTo(x, y);
|
|
90
|
+
store.config.canvas.getContext('2d')!.fill();
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
const drawGhosts = () => {
|
|
94
|
-
|
|
95
|
-
const x = ghost.y * (CELL_SIZE + GAP_SIZE)
|
|
96
|
-
const y = ghost.x * (CELL_SIZE + GAP_SIZE) +
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Store.config.canvas.getContext('2d')!.fillStyle = 'white';
|
|
106
|
-
Store.config.canvas.getContext('2d')!.beginPath();
|
|
107
|
-
Store.config.canvas.getContext('2d')!.arc(x - radius / 3, y - radius / 3, radius / 4, 0, Math.PI * 2);
|
|
108
|
-
Store.config.canvas.getContext('2d')!.arc(x + radius / 3, y - radius / 3, radius / 4, 0, Math.PI * 2);
|
|
109
|
-
Store.config.canvas.getContext('2d')!.fill();
|
|
110
|
-
|
|
111
|
-
Store.config.canvas.getContext('2d')!.fillStyle = 'black';
|
|
112
|
-
Store.config.canvas.getContext('2d')!.beginPath();
|
|
113
|
-
Store.config.canvas.getContext('2d')!.arc(x - radius / 3, y - radius / 3, radius / 8, 0, Math.PI * 2);
|
|
114
|
-
Store.config.canvas.getContext('2d')!.arc(x + radius / 3, y - radius / 3, radius / 8, 0, Math.PI * 2);
|
|
115
|
-
Store.config.canvas.getContext('2d')!.fill();
|
|
93
|
+
const drawGhosts = (store: StoreType) => {
|
|
94
|
+
store.ghosts.forEach((ghost) => {
|
|
95
|
+
const x = ghost.y * (CELL_SIZE + GAP_SIZE);
|
|
96
|
+
const y = ghost.x * (CELL_SIZE + GAP_SIZE) + 15;
|
|
97
|
+
const size = CELL_SIZE;
|
|
98
|
+
|
|
99
|
+
const ctx = store.config.canvas.getContext('2d')!;
|
|
100
|
+
if (ghost.scared) {
|
|
101
|
+
ctx.drawImage(GHOSTS['scared'].img, x, y, size, size);
|
|
102
|
+
} else {
|
|
103
|
+
ctx.drawImage(GHOSTS[ghost.name].img, x, y, size, size);
|
|
104
|
+
}
|
|
116
105
|
});
|
|
117
106
|
};
|
|
118
107
|
|
|
119
|
-
const renderGameOver = () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
const renderGameOver = (store: StoreType) => {
|
|
109
|
+
store.config.canvas.getContext('2d')!.fillStyle = Utils.getCurrentTheme(store).textColor;
|
|
110
|
+
store.config.canvas.getContext('2d')!.font = '20px Arial';
|
|
111
|
+
store.config.canvas.getContext('2d')!.textAlign = 'center';
|
|
112
|
+
store.config.canvas.getContext('2d')!.fillText('Game Over', store.config.canvas.width / 2, store.config.canvas.height / 2);
|
|
124
113
|
};
|
|
125
114
|
|
|
126
|
-
const drawSoundController = () => {
|
|
127
|
-
if (!
|
|
128
|
-
console.log('vvvv');
|
|
115
|
+
const drawSoundController = (store: StoreType) => {
|
|
116
|
+
if (!store.config.enableSounds) {
|
|
129
117
|
return;
|
|
130
118
|
}
|
|
131
119
|
|
|
132
120
|
const width = 30,
|
|
133
121
|
height = 30,
|
|
134
|
-
left =
|
|
122
|
+
left = store.config.canvas.width - width - 10,
|
|
135
123
|
top = 10;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
124
|
+
store.config.canvas.getContext('2d')!.fillStyle = `rgba(0, 0, 0, ${MusicPlayer.getInstance().isMuted ? 0.3 : 0.5})`;
|
|
125
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
126
|
+
store.config.canvas.getContext('2d')!.moveTo(left + 10, top + 10);
|
|
127
|
+
store.config.canvas.getContext('2d')!.lineTo(left + 20, top + 5);
|
|
128
|
+
store.config.canvas.getContext('2d')!.lineTo(left + 20, top + 25);
|
|
129
|
+
store.config.canvas.getContext('2d')!.lineTo(left + 10, top + 20);
|
|
130
|
+
store.config.canvas.getContext('2d')!.closePath();
|
|
131
|
+
store.config.canvas.getContext('2d')!.fill();
|
|
144
132
|
|
|
145
133
|
if (!MusicPlayer.getInstance().isMuted) {
|
|
146
|
-
|
|
147
|
-
|
|
134
|
+
store.config.canvas.getContext('2d')!.strokeStyle = `rgba(0, 0, 0, 0.4)`;
|
|
135
|
+
store.config.canvas.getContext('2d')!.lineWidth = 2;
|
|
148
136
|
|
|
149
137
|
// First wave
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
139
|
+
store.config.canvas.getContext('2d')!.arc(left + 25, top + 15, 5, 0, Math.PI * 2);
|
|
140
|
+
store.config.canvas.getContext('2d')!.stroke();
|
|
153
141
|
|
|
154
142
|
// Second wave
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
144
|
+
store.config.canvas.getContext('2d')!.arc(left + 25, top + 15, 8, 0, Math.PI * 2);
|
|
145
|
+
store.config.canvas.getContext('2d')!.stroke();
|
|
158
146
|
} else {
|
|
159
147
|
// Mute line
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
148
|
+
store.config.canvas.getContext('2d')!.strokeStyle = 'rgba(255, 0, 0, 0.6)';
|
|
149
|
+
store.config.canvas.getContext('2d')!.lineWidth = 3;
|
|
150
|
+
store.config.canvas.getContext('2d')!.beginPath();
|
|
151
|
+
store.config.canvas.getContext('2d')!.moveTo(left + 25, top + 5);
|
|
152
|
+
store.config.canvas.getContext('2d')!.lineTo(left + 5, top + 25);
|
|
153
|
+
store.config.canvas.getContext('2d')!.stroke();
|
|
166
154
|
}
|
|
167
155
|
};
|
|
168
156
|
|
|
169
|
-
const listenToSoundController = () => {
|
|
170
|
-
if (!
|
|
157
|
+
const listenToSoundController = (store: StoreType) => {
|
|
158
|
+
if (!store.config.enableSounds) {
|
|
171
159
|
return;
|
|
172
160
|
}
|
|
173
|
-
|
|
174
|
-
const rect =
|
|
161
|
+
store.config.canvas.addEventListener('click', function (event) {
|
|
162
|
+
const rect = store.config.canvas.getBoundingClientRect();
|
|
175
163
|
const x = event.clientX - rect.left,
|
|
176
164
|
y = event.clientY - rect.top;
|
|
177
165
|
const width = 30,
|
|
178
166
|
height = 30,
|
|
179
|
-
left =
|
|
167
|
+
left = store.config.canvas.width - width - 10,
|
|
180
168
|
top = 10;
|
|
181
169
|
|
|
182
170
|
if (x >= left && x <= left + this.width && y >= top && y <= top + this.height) {
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GameTheme, ThemeKeys } from './types';
|
|
1
|
+
import { GameTheme, GhostName, ThemeKeys } from './types';
|
|
2
2
|
|
|
3
3
|
export const CELL_SIZE = 20;
|
|
4
4
|
export const GAP_SIZE = 2;
|
|
@@ -7,7 +7,7 @@ export const GRID_HEIGHT = 7;
|
|
|
7
7
|
export const PACMAN_COLOR = 'yellow';
|
|
8
8
|
export const PACMAN_COLOR_POWERUP = 'red';
|
|
9
9
|
export const PACMAN_COLOR_DEAD = '#80808064';
|
|
10
|
-
export const
|
|
10
|
+
export const GHOST_NAMES: GhostName[] = ['blinky', 'clyde', 'inky', 'pinky'];
|
|
11
11
|
export const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
12
12
|
export const DELTA_TIME = 250;
|
|
13
13
|
export const PACMAN_DEATH_DURATION = 10;
|
|
@@ -38,3 +38,30 @@ export const GAME_THEMES: { [key in ThemeKeys]: GameTheme } = {
|
|
|
38
38
|
emptyContributionBoxColor: '#2d2d2d'
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
+
export const GHOSTS: { [key in GhostName | 'scared']: { imgDate: string; img: HTMLImageElement } } = {
|
|
42
|
+
blinky: {
|
|
43
|
+
imgDate:
|
|
44
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfUlEQVQ4T+2TUQ7AIAhDy/0PzQIRAqxmLtnn/DJPWypBAVkKKOMCyOQN7IRElLrcnIrDLNK4wVtxNbkb6Hq+jOcSbim6QVzKEstkw92gxVeFrMpqokix4wA+NvCOnvfArvcEbHoe2G9QmmhDMUc65p3xYC6q3zQPxtdl3NgF5QpL/b/rs3IAAAAASUVORK5CYIIA',
|
|
45
|
+
img: new Image()
|
|
46
|
+
},
|
|
47
|
+
clyde: {
|
|
48
|
+
imgDate:
|
|
49
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgUlEQVQ4T+2T0Q6AIAhFLx9sH1MfTIPCAeLKrcd8PHqP4JBQLN7BFacNlHkAs+AQcqIueBs2mVWjgtWwl4yCdrd/pHYLLlVEgR2yK0wy4SoI5TcGXU4wM+AEJQfwsUCuXngDOR4rqKbngf0C94gyFHmkbd4rbkxD/pv2jfR1Ky7sBNrzXbHpnBX+AAAAAElFTkSuQmCC',
|
|
50
|
+
img: new Image()
|
|
51
|
+
},
|
|
52
|
+
inky: {
|
|
53
|
+
imgDate:
|
|
54
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAg0lEQVQ4T+WTWxKAIAhFuQvK/a+jFoT5QAVxypn+6vMEx6sDIO/jk12OAMs1WDVOXV3UBW+bRVbTFMFu8yCZBExH/g26VHCXI0AJpKgdUCUrTlkwxE+FECdzS7HiJemXgvyeO29gE7jD8wDVFX4vSLNtR1q2z+OVlaZxTaXYrq7HbxYBS8VgMVrqzkEAAAAASUVORK5CYIIA',
|
|
55
|
+
img: new Image()
|
|
56
|
+
},
|
|
57
|
+
pinky: {
|
|
58
|
+
imgDate:
|
|
59
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAhklEQVQ4T+2T0Q2AIAwF281wC50Qt9DNagoptqVESfyUz4N3vJCCECxaD4o47gt6bsAo2IWUqAnehkUmbYpgNqwlvSCnur+dtnnAuYUVyCGJimTAi8DUzwmwOoGI7hYjDgAfC/jqiTfg47ZBND0P7BeoR+Sh8CMt8x5xYSWkv2nbcF834swuA/9u49Yy5bgAAAAASUVORK5CYIIA',
|
|
60
|
+
img: new Image()
|
|
61
|
+
},
|
|
62
|
+
scared: {
|
|
63
|
+
imgDate:
|
|
64
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAeUlEQVQ4T82TUQ6AMAhD7UX0/sdyF0GREVmDmTN+bH9r6Bs0A0t2VpFULwDrrfBkZFcA3YC3ZodViAFGzQHyP0B2w2NrB0/1AoDbHwLoQ5/nrw1OBuD5e/crbM9Aiz35njHWzpSB/m3+0r40mV41M8U19WJe3Uw/tQOKt08pUUbBEQAAAABJRU5ErkJgggAA',
|
|
65
|
+
img: new Image()
|
|
66
|
+
}
|
|
67
|
+
};
|