Skip to content

Commit 106b7f0

Browse files
committed
fix games
1 parent ddaa89c commit 106b7f0

File tree

3 files changed

+153
-69
lines changed

3 files changed

+153
-69
lines changed

packages/builder/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/grant-explorer/src/components/GitcoinRunner.tsx

Lines changed: 146 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,54 @@ import React, { useState, useEffect } from "react";
22
import { getChains, stringToBlobUrl } from "common";
33
import { TChain } from "common";
44

5+
// Game Configuration
6+
const GAME_CONFIG = {
7+
// Player settings
8+
player: {
9+
radius: 20,
10+
initialX: 50,
11+
jumpForce: -15,
12+
gravity: 0.6,
13+
},
14+
15+
// Obstacle settings
16+
obstacles: {
17+
width: 40,
18+
minHeight: 40,
19+
maxNormalHeight: 60,
20+
minHighHeight: 100,
21+
maxHighHeight: 140,
22+
highObstacleChance: 0.4,
23+
minSpacing: 700,
24+
randomSpacingRange: 300,
25+
},
26+
27+
// Game physics
28+
physics: {
29+
initialSpeed: 3,
30+
speedIncrease: 4,
31+
speedIncreaseRate: 1000, // Lower = faster speed increase
32+
},
33+
34+
// Scoring
35+
scoring: {
36+
obstaclePoints: 100,
37+
survivalPointsPerSecond: 1,
38+
},
39+
40+
// Canvas settings
41+
canvas: {
42+
width: 400,
43+
height: 300,
44+
groundOffset: 30,
45+
},
46+
47+
// Cooldown
48+
cooldown: {
49+
duration: 3, // seconds
50+
},
51+
};
52+
553
type Obstacle = {
654
x: number;
755
width: number;
@@ -19,14 +67,14 @@ export function GitcoinRunner() {
1967
const [cooldown, setCooldown] = useState(false);
2068
const [cooldownTime, setCooldownTime] = useState(3);
2169
const playerRef = React.useRef({
22-
x: 50,
23-
y: 250, // Default position, will be updated in useEffect
70+
x: GAME_CONFIG.player.initialX,
71+
y: 250,
2472
dy: 0,
25-
radius: 20,
73+
radius: GAME_CONFIG.player.radius,
2674
isJumping: false,
2775
});
2876
const gameStateRef = React.useRef({
29-
gameSpeed: 2,
77+
gameSpeed: GAME_CONFIG.physics.initialSpeed,
3078
frameCount: 0,
3179
});
3280
const obstaclesRef = React.useRef<Obstacle[]>([]);
@@ -36,9 +84,8 @@ export function GitcoinRunner() {
3684

3785
function startJump() {
3886
const player = playerRef.current;
39-
4087
if (!player.isJumping) {
41-
player.dy = -10;
88+
player.dy = GAME_CONFIG.player.jumpForce;
4289
player.isJumping = true;
4390
}
4491
}
@@ -55,12 +102,36 @@ export function GitcoinRunner() {
55102
: 250;
56103
player.dy = 0;
57104
player.isJumping = false;
58-
gameState.gameSpeed = 2;
105+
gameState.gameSpeed = GAME_CONFIG.physics.initialSpeed;
59106
gameState.frameCount = 0;
60107
obstaclesRef.current = [];
61108
setGameStarted(true);
62109
}
63110

111+
function createObstacle() {
112+
const width = GAME_CONFIG.obstacles.width;
113+
const isHighObstacle =
114+
Math.random() < GAME_CONFIG.obstacles.highObstacleChance;
115+
const height = isHighObstacle
116+
? GAME_CONFIG.obstacles.minHighHeight +
117+
Math.random() *
118+
(GAME_CONFIG.obstacles.maxHighHeight -
119+
GAME_CONFIG.obstacles.minHighHeight)
120+
: GAME_CONFIG.obstacles.minHeight +
121+
Math.random() *
122+
(GAME_CONFIG.obstacles.maxNormalHeight -
123+
GAME_CONFIG.obstacles.minHeight);
124+
const iconIndex = Math.floor(Math.random() * chainIconsRef.current.length);
125+
126+
obstaclesRef.current.push({
127+
x: canvasRef.current?.width || 0,
128+
width,
129+
height,
130+
scored: false,
131+
iconIndex,
132+
});
133+
}
134+
64135
useEffect(() => {
65136
if (!canvasRef.current || !imagesLoaded) return;
66137

@@ -72,28 +143,8 @@ export function GitcoinRunner() {
72143
const obstacles = obstaclesRef.current;
73144
const gameState = gameStateRef.current;
74145
let animationId: number;
75-
const gravity = 0.25;
76-
const groundLevel = canvas.height - 30;
77-
78-
function createObstacle() {
79-
const width = 40;
80-
// Sometimes create higher obstacles that can be passed under
81-
const isHighObstacle = Math.random() < 0.3; // 30% chance for high obstacle
82-
const height = isHighObstacle
83-
? 80 + Math.random() * 40 // High obstacle: 80-120px
84-
: 40 + Math.random() * 20; // Normal obstacle: 40-60px
85-
const iconIndex = Math.floor(
86-
Math.random() * chainIconsRef.current.length
87-
);
88-
89-
obstacles.push({
90-
x: canvas.width,
91-
width,
92-
height,
93-
scored: false,
94-
iconIndex,
95-
});
96-
}
146+
const gravity = GAME_CONFIG.player.gravity;
147+
const groundLevel = canvas.height - GAME_CONFIG.canvas.groundOffset;
97148

98149
function drawGround(context: CanvasRenderingContext2D) {
99150
context.strokeStyle = "#666";
@@ -134,21 +185,38 @@ export function GitcoinRunner() {
134185

135186
function checkCollision() {
136187
return obstacles.some((obstacle) => {
137-
// Get player bounds
138-
const playerTop = player.y - player.radius;
139-
const playerBottom = player.y + player.radius;
140-
const playerLeft = player.x - player.radius;
141-
const playerRight = player.x + player.radius;
142-
143-
// Get obstacle bounds
144-
const obstacleLeft = obstacle.x;
145-
const obstacleRight = obstacle.x + obstacle.width;
146-
const obstacleTop = groundLevel - obstacle.height;
147-
148-
// Check if we're horizontally aligned with the obstacle
149-
if (playerRight > obstacleLeft && playerLeft < obstacleRight) {
150-
// Only collide if we hit from above or the sides
151-
return playerBottom > obstacleTop && playerTop < obstacleTop;
188+
const playerBox = {
189+
left: player.x - player.radius,
190+
right: player.x + player.radius,
191+
top: player.y - player.radius,
192+
bottom: player.y + player.radius,
193+
};
194+
195+
const obstacleBox = {
196+
left: obstacle.x,
197+
right: obstacle.x + obstacle.width,
198+
top: groundLevel - obstacle.height,
199+
bottom: groundLevel,
200+
};
201+
202+
// Check for horizontal overlap
203+
const horizontalOverlap =
204+
playerBox.right > obstacleBox.left &&
205+
playerBox.left < obstacleBox.right;
206+
207+
// If there's horizontal overlap, check for collision
208+
if (horizontalOverlap) {
209+
// For high obstacles (that can be passed under)
210+
if (obstacle.height > GAME_CONFIG.obstacles.maxNormalHeight) {
211+
// Only collide if we hit the top portion
212+
return (
213+
playerBox.bottom > obstacleBox.top &&
214+
playerBox.top < obstacleBox.top
215+
);
216+
} else {
217+
// For normal obstacles, collide if there's any vertical overlap
218+
return playerBox.bottom > obstacleBox.top;
219+
}
152220
}
153221

154222
return false;
@@ -159,17 +227,17 @@ export function GitcoinRunner() {
159227
obstacles.forEach((obstacle) => {
160228
if (!obstacle.scored && player.x > obstacle.x + obstacle.width) {
161229
obstacle.scored = true;
162-
setScore((prev) => prev + 100); // More points for obstacles
230+
setScore((prev) => prev + GAME_CONFIG.scoring.obstaclePoints);
163231
}
164232
});
165233

166-
// Update survival time based on frame count (60 frames = 1 second)
167234
if (gameStarted && !gameOver) {
168235
gameState.frameCount++;
169-
// Add 1 point every 60 frames (1 second)
170236
if (gameState.frameCount % 60 === 0) {
171237
setSurvivalTime(gameState.frameCount / 60);
172-
setScore((prev) => prev + 1);
238+
setScore(
239+
(prev) => prev + GAME_CONFIG.scoring.survivalPointsPerSecond
240+
);
173241
}
174242
}
175243
}
@@ -193,26 +261,28 @@ export function GitcoinRunner() {
193261

194262
// Draw start screen
195263
if (!gameStarted && !gameOver) {
196-
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
264+
ctx.fillStyle = "#ffffff";
197265
ctx.fillRect(0, 0, canvas.width, canvas.height);
198266

199267
// Title with glow
200-
ctx.fillStyle = "white";
268+
ctx.fillStyle = "#000000";
201269
ctx.textAlign = "center";
202-
ctx.shadowColor = "rgba(0, 255, 255, 0.5)";
270+
ctx.shadowColor = "rgba(0, 200, 255, 0.3)";
203271
ctx.shadowBlur = 15;
204272
ctx.font = "bold 36px Arial";
205273
ctx.fillText("Chain Hopper", canvas.width / 2, canvas.height / 2 - 20);
206274

207275
// Instructions
208276
ctx.shadowBlur = 5;
209277
ctx.font = "20px Arial";
278+
ctx.fillStyle = "#333333";
210279
ctx.fillText(
211280
"Jump across the chains!",
212281
canvas.width / 2,
213282
canvas.height / 2 + 10
214283
);
215284
ctx.font = "16px Arial";
285+
ctx.fillStyle = "#666666";
216286
ctx.fillText(
217287
"Press spacebar or tap to play",
218288
canvas.width / 2,
@@ -225,19 +295,19 @@ export function GitcoinRunner() {
225295

226296
// Draw game over screen
227297
if (gameOver) {
228-
// Semi-transparent overlay
229-
ctx.fillStyle = "rgba(0, 0, 0, 0.85)";
298+
// Light background
299+
ctx.fillStyle = "#ffffff";
230300
ctx.fillRect(0, 0, canvas.width, canvas.height);
231301

232-
// Draw fancy score box with gradient
302+
// Draw score box with subtle gradient
233303
const gradient = ctx.createLinearGradient(
234304
0,
235305
canvas.height / 2 - 100,
236306
0,
237307
canvas.height / 2 + 100
238308
);
239-
gradient.addColorStop(0, "rgba(255, 255, 255, 0.15)");
240-
gradient.addColorStop(1, "rgba(255, 255, 255, 0.05)");
309+
gradient.addColorStop(0, "rgba(0, 200, 255, 0.1)");
310+
gradient.addColorStop(1, "rgba(0, 200, 255, 0.05)");
241311
ctx.fillStyle = gradient;
242312
ctx.beginPath();
243313
ctx.roundRect(
@@ -250,16 +320,17 @@ export function GitcoinRunner() {
250320
ctx.fill();
251321

252322
// Game over text with shadow
253-
ctx.fillStyle = "white";
254-
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
323+
ctx.fillStyle = "#000000";
324+
ctx.shadowColor = "rgba(0, 200, 255, 0.3)";
255325
ctx.shadowBlur = 10;
256326
ctx.font = "bold 36px Arial";
257327
ctx.textAlign = "center";
258328
ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 30);
259329

260-
// Score and time with subtle glow
330+
// Score with subtle glow
261331
ctx.shadowBlur = 5;
262332
ctx.font = "28px Arial";
333+
ctx.fillStyle = "#333333";
263334
ctx.fillText(
264335
`Score: ${score}`,
265336
canvas.width / 2,
@@ -271,15 +342,15 @@ export function GitcoinRunner() {
271342

272343
if (cooldown) {
273344
ctx.font = "16px Arial";
274-
ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
345+
ctx.fillStyle = "#666666";
275346
ctx.fillText(
276347
`Wait ${cooldownTime} seconds...`,
277348
canvas.width / 2,
278349
canvas.height / 2 + 50
279350
);
280351
} else {
281352
ctx.font = "18px Arial";
282-
ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
353+
ctx.fillStyle = "#666666";
283354
ctx.fillText(
284355
"Press space to play again",
285356
canvas.width / 2,
@@ -299,8 +370,13 @@ export function GitcoinRunner() {
299370
player.isJumping = false;
300371
}
301372

302-
// Remove speed cap and make progression slower
303-
gameState.gameSpeed = 2 + (gameState.frameCount / 6000) * 3;
373+
// Update game speed
374+
gameState.gameSpeed =
375+
GAME_CONFIG.physics.initialSpeed +
376+
Math.min(
377+
gameState.frameCount / GAME_CONFIG.physics.speedIncreaseRate,
378+
GAME_CONFIG.physics.speedIncrease
379+
);
304380

305381
// Update obstacles
306382
obstacles.forEach((obstacle) => {
@@ -312,10 +388,13 @@ export function GitcoinRunner() {
312388
obstacles.shift();
313389
}
314390

315-
// Add new obstacles with much more space between them
391+
// Add new obstacles with randomized spacing
316392
if (
317393
obstacles.length === 0 ||
318-
obstacles[obstacles.length - 1].x < canvas.width - 800
394+
obstacles[obstacles.length - 1].x <
395+
canvas.width -
396+
(GAME_CONFIG.obstacles.minSpacing +
397+
Math.random() * GAME_CONFIG.obstacles.randomSpacingRange)
319398
) {
320399
createObstacle();
321400
}
@@ -488,8 +567,8 @@ export function GitcoinRunner() {
488567
<div className="text-center">
489568
<canvas
490569
ref={canvasRef}
491-
width={400}
492-
height={300}
570+
width={GAME_CONFIG.canvas.width}
571+
height={GAME_CONFIG.canvas.height}
493572
className="mx-auto border rounded-lg cursor-pointer"
494573
/>
495574
</div>

packages/grant-explorer/src/features/round/ViewCartPage/ViewCartPage.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Balance, BalanceMap } from "../../api/types";
1919
import { formatUnits } from "ethers/lib/utils";
2020
import GenericModal from "../../common/GenericModal";
2121
import SquidWidget, { SwapParams } from "./SquidWidget";
22+
import { GitcoinRunner } from "../../../components/GitcoinRunner";
2223

2324
export default function ViewCart() {
2425
const { projects, setCart, getVotingTokenForChain } = useCartStorage();
@@ -136,7 +137,10 @@ export default function ViewCart() {
136137
address: token.address,
137138
chainId: chainIdNumber,
138139
formattedAmount: Number(
139-
formatUnits(balance.value / BigInt(getMultiplier(chainIdNumber)), balance.decimals)
140+
formatUnits(
141+
balance.value / BigInt(getMultiplier(chainIdNumber)),
142+
balance.decimals
143+
)
140144
),
141145
};
142146
} catch (e) {
@@ -250,6 +254,7 @@ export default function ViewCart() {
250254
) : (
251255
<div className={"grid sm:grid-cols-3 gap-5 w-full"}>
252256
<div className="flex flex-col gap-5 sm:col-span-2 order-2 sm:order-1">
257+
<GitcoinRunner />
253258
{Object.keys(groupedCartProjects).map((chainId) => (
254259
<div key={Number(chainId)}>
255260
<CartWithProjects

0 commit comments

Comments
 (0)