From 2e0e5517ab8e8a2d18d85a2d03572a093b9abfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ta=C5=84czyk?= Date: Thu, 22 Aug 2024 23:55:06 +0300 Subject: [PATCH] isActiveBonus function instead of bonus state on player/game state props --- .../src/game-states/gameplay/game-logic.ts | 42 ++++++++----------- .../src/game-states/gameplay/game-render.ts | 9 ++-- .../game-states/gameplay/gameplay-types.ts | 11 ++--- .../game-states/gameplay/level-generator.ts | 7 ---- .../gameplay/levels/01-the-first-step.ts | 2 +- .../src/game-states/gameplay/monster-logic.ts | 5 ++- .../src/game-states/gameplay/move-utils.ts | 8 +++- .../gameplay/render/player-render.ts | 4 +- .../src/game-states/intro/game-preview.tsx | 7 ---- 9 files changed, 39 insertions(+), 56 deletions(-) diff --git a/js13k2024/game/src/game-states/gameplay/game-logic.ts b/js13k2024/game/src/game-states/gameplay/game-logic.ts index 501081e4..00dfb4a2 100644 --- a/js13k2024/game/src/game-states/gameplay/game-logic.ts +++ b/js13k2024/game/src/game-states/gameplay/game-logic.ts @@ -1,4 +1,13 @@ -import { GameState, Position, LevelConfig, Direction, BonusType, ActiveBonus, BlasterShot } from './gameplay-types'; +import { + GameState, + Position, + LevelConfig, + Direction, + BonusType, + ActiveBonus, + BlasterShot, + isActiveBonus, +} from './gameplay-types'; import { moveMonsters, checkCollision, @@ -62,7 +71,7 @@ export const doGameUpdate = (direction: Direction, gameState: GameState, levelCo ); if ( - gameState.crusherActive && + isActiveBonus(newGameState, BonusType.Crusher) && gameState.obstacles.find((obstacle) => isPositionEqual(newPosition, obstacle.position)) ) { newGameState.obstacles = gameState.obstacles.map((obstacle) => @@ -122,12 +131,13 @@ export const doGameUpdate = (direction: Direction, gameState: GameState, levelCo newGameState.player.position, levelConfig.gridSize, newGameState.obstacles, - newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.CapOfInvisibility), - newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.ConfusedMonsters), + isActiveBonus(newGameState, BonusType.CapOfInvisibility), + isActiveBonus(newGameState, BonusType.ConfusedMonsters), + isActiveBonus(newGameState, BonusType.Monster), ); // Handle Monster bonus - if (newGameState.player.isMonster) { + if (isActiveBonus(newGameState, BonusType.Monster)) { handleMonsterBonus(newGameState); } else { // Check for collisions with monsters @@ -139,7 +149,7 @@ export const doGameUpdate = (direction: Direction, gameState: GameState, levelCo } // Handle Blaster bonus - if (newGameState.player.hasBlaster) { + if (isActiveBonus(newGameState, BonusType.Blaster)) { handleBlasterShot(newGameState, direction, levelConfig); } newGameState.blasterShots = newGameState.blasterShots.filter( @@ -191,13 +201,8 @@ export const doGameUpdate = (direction: Direction, gameState: GameState, levelCo .map((bonus) => ({ ...bonus, duration: bonus.duration - 1 })) .filter((bonus) => bonus.duration > 0); - newGameState.builderActive = newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.Builder); - newGameState.crusherActive = newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.Crusher); - newGameState.player.isClimbing = newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.Climber); - newGameState.isSliding = newGameState.activeBonuses.some((bonus) => bonus.type === BonusType.Slide); - // Handle builder bonus - if (gameState.builderActive) { + if (isActiveBonus(newGameState, BonusType.Builder)) { const newObstacle = { position: oldPosition, creationTime: Date.now(), isRaising: true, isDestroying: false }; if ( !isPositionOccupied( @@ -236,13 +241,10 @@ export const applyBonus = (gameState: GameState, bonusType: BonusType) => { }); break; case BonusType.Crusher: - gameState.crusherActive = true; break; case BonusType.Builder: - gameState.builderActive = true; break; case BonusType.Climber: - gameState.player.isClimbing = true; break; case BonusType.Teleport: // Teleport is handled immediately when collected @@ -251,17 +253,13 @@ export const applyBonus = (gameState: GameState, bonusType: BonusType) => { gameState.tsunamiLevel = 1; break; case BonusType.Monster: - gameState.player.isMonster = true; break; case BonusType.Slide: gameState.isSliding = true; break; case BonusType.Sokoban: - // Sokoban logic is handled in movement break; case BonusType.Blaster: - gameState.player.hasBlaster = true; - gameState.player.blasterSteps = 13; break; } }; @@ -330,7 +328,7 @@ const performTeleportation = (gameState: GameState, teleportPoint: Position): vo const handleTsunamiEffect = (gameState: GameState): void => { gameState.tsunamiLevel++; if (gameState.tsunamiLevel >= 13) { - if (!gameState.player.isClimbing) { + if (!isActiveBonus(gameState, BonusType.Climber)) { gameState.gameEndingState = 'gameOver'; startGameOverAnimation(gameState); } @@ -408,10 +406,6 @@ const handleBlasterShot = (gameState: GameState, direction: Direction, levelConf const isHit = isMonsterOnBlasterPath(monster.position, start, end, direction); return !isHit; }); - - if (gameState.player.hasBlaster && gameState.player.blasterSteps!-- <= 0) { - gameState.player.hasBlaster = false; - } }; const isMonsterOnBlasterPath = ( diff --git a/js13k2024/game/src/game-states/gameplay/game-render.ts b/js13k2024/game/src/game-states/gameplay/game-render.ts index 79a46083..d3ccfdd3 100644 --- a/js13k2024/game/src/game-states/gameplay/game-render.ts +++ b/js13k2024/game/src/game-states/gameplay/game-render.ts @@ -1,4 +1,4 @@ -import { BonusType, GameState, LevelConfig } from './gameplay-types'; +import { BonusType, GameState, isActiveBonus, LevelConfig } from './gameplay-types'; import { drawObstacles, drawGoal } from './render/grid-objects-render'; import { drawGrid } from './render/grid-render'; import { drawPlayer } from './render/player-render'; @@ -97,7 +97,7 @@ export const drawGameState = ( drawTimeBombs(ctx, [sortedObject.obj], cellSize); break; case 'monster': - drawMonsters(ctx, [sortedObject.obj], cellSize, gameState.player.isMonster); + drawMonsters(ctx, [sortedObject.obj], cellSize, isActiveBonus(gameState, BonusType.Monster)); break; case 'player': drawPlayer( @@ -106,8 +106,9 @@ export const drawGameState = ( cellSize, gameState.activeBonuses.some((bonus) => bonus.type === BonusType.CapOfInvisibility), gameState.obstacles, - gameState.player.isMonster, - gameState.player.hasBlaster, + isActiveBonus(gameState, BonusType.Monster), + isActiveBonus(gameState, BonusType.Blaster), + isActiveBonus(gameState, BonusType.Climber), ); break; case 'goal': diff --git a/js13k2024/game/src/game-states/gameplay/gameplay-types.ts b/js13k2024/game/src/game-states/gameplay/gameplay-types.ts index 6bbea369..87f93bd1 100644 --- a/js13k2024/game/src/game-states/gameplay/gameplay-types.ts +++ b/js13k2024/game/src/game-states/gameplay/gameplay-types.ts @@ -38,13 +38,8 @@ export interface Player { previousPosition: Position; moveTimestamp: number; teleportTimestamp?: number; - isInvisible: boolean; isVictorious: boolean; isVanishing: boolean; - isClimbing: boolean; - isMonster: boolean; - hasBlaster: boolean; - blasterSteps: number | undefined; } export interface Obstacle { @@ -68,8 +63,6 @@ export interface GameState { explosions: Explosion[]; timeBombs: TimeBomb[]; landMines: Position[]; - crusherActive: boolean; - builderActive: boolean; score: number; gameEndingState: GameEndingState; tsunamiLevel: number; @@ -217,3 +210,7 @@ export interface EntityAnimationState { bounceOffset: number; tentacleAnimationFactor: number; } + +export function isActiveBonus(gameState: GameState, bonusType: BonusType) { + return gameState.activeBonuses.some((bonus) => bonus.type === bonusType); +} diff --git a/js13k2024/game/src/game-states/gameplay/level-generator.ts b/js13k2024/game/src/game-states/gameplay/level-generator.ts index 8b6e0117..8d8bfa2a 100644 --- a/js13k2024/game/src/game-states/gameplay/level-generator.ts +++ b/js13k2024/game/src/game-states/gameplay/level-generator.ts @@ -34,13 +34,8 @@ const createPlayer = (x: number, y: number): Player => ({ position: createPosition(x, y), previousPosition: createPosition(x, y), moveTimestamp: Date.now(), - isInvisible: false, isVictorious: false, isVanishing: false, - isClimbing: false, - isMonster: false, - hasBlaster: false, - blasterSteps: undefined, }); const createObstacle = (x: number, y: number): Obstacle => ({ @@ -62,8 +57,6 @@ const generateBaseState = (): GameState => ({ explosions: [], timeBombs: [], landMines: [], - crusherActive: false, - builderActive: false, score: 0, gameEndingState: 'none', tsunamiLevel: 0, diff --git a/js13k2024/game/src/game-states/gameplay/levels/01-the-first-step.ts b/js13k2024/game/src/game-states/gameplay/levels/01-the-first-step.ts index 2894e8fa..b1cecde4 100644 --- a/js13k2024/game/src/game-states/gameplay/levels/01-the-first-step.ts +++ b/js13k2024/game/src/game-states/gameplay/levels/01-the-first-step.ts @@ -17,7 +17,7 @@ export const generateLevel = (): [GameState, LevelConfig, string] => { state.goal = createPosition(6, 3); state.monsters = [createMonster(3, 0)]; state.obstacles = [createObstacle(2, 3), createObstacle(5, 2), createObstacle(5, 3)]; - state.bonuses = [createBonus(1, 3, BonusType.Tsunami)]; + state.bonuses = [createBonus(1, 3, BonusType.Builder)]; return [state, config, config.levelStory]; }; diff --git a/js13k2024/game/src/game-states/gameplay/monster-logic.ts b/js13k2024/game/src/game-states/gameplay/monster-logic.ts index 4ec0d5c2..b831cc9f 100644 --- a/js13k2024/game/src/game-states/gameplay/monster-logic.ts +++ b/js13k2024/game/src/game-states/gameplay/monster-logic.ts @@ -8,6 +8,7 @@ export const moveMonsters = ( obstacles: Obstacle[], isPlayerInvisible: boolean, isConfused: boolean, + isPlayerMonster: boolean, ): Monster[] => { const newPositions: Position[] = []; return monsters.map((monster) => { @@ -21,8 +22,8 @@ export const moveMonsters = ( } let newPosition: Position; - if (isConfused) { - // If monsters are confused, move in the opposite direction + if (isConfused || isPlayerMonster) { + // If monsters are confused, or player is the monster, move in the opposite direction newPosition = getConfusedPosition(monster.position, path[1] || monster.position, gridSize, obstacles, monsters); } else { newPosition = path.length > 1 ? path[1] : monster.position; diff --git a/js13k2024/game/src/game-states/gameplay/move-utils.ts b/js13k2024/game/src/game-states/gameplay/move-utils.ts index 8c26bb88..93ac9932 100644 --- a/js13k2024/game/src/game-states/gameplay/move-utils.ts +++ b/js13k2024/game/src/game-states/gameplay/move-utils.ts @@ -1,4 +1,4 @@ -import { BonusType, Direction, GameState, LevelConfig, Position } from './gameplay-types'; +import { BonusType, Direction, GameState, isActiveBonus, LevelConfig, Position } from './gameplay-types'; import { getArrowShape } from './render/move-arrows-render'; export const isPositionEqual = (pos1: Position, pos2: Position): boolean => pos1.x === pos2.x && pos1.y === pos2.y; @@ -95,7 +95,11 @@ export const isValidMove = ( ); // Allow movement onto obstacles if the player has the Climber bonus active - if (isObstaclePresent && !gameState.player.isClimbing && !gameState.crusherActive) { + if ( + isObstaclePresent && + !isActiveBonus(gameState, BonusType.Climber) && + !isActiveBonus(gameState, BonusType.Crusher) + ) { // Check if Sokoban bonus is active if (gameState.activeBonuses.some((bonus) => bonus.type === BonusType.Sokoban)) { const pushDirection = getDirectionFromPositions(gameState.player.position, newPosition); diff --git a/js13k2024/game/src/game-states/gameplay/render/player-render.ts b/js13k2024/game/src/game-states/gameplay/render/player-render.ts index 6dc60c1b..f02fdcfc 100644 --- a/js13k2024/game/src/game-states/gameplay/render/player-render.ts +++ b/js13k2024/game/src/game-states/gameplay/render/player-render.ts @@ -19,9 +19,9 @@ export const drawPlayer = ( obstacles: Obstacle[] = [], isMonster: boolean = false, hasBlaster: boolean = false, + isClimbing: boolean = false, ) => { - const { position, previousPosition, moveTimestamp, teleportTimestamp, isVanishing, isVictorious, isClimbing } = - player; + const { position, previousPosition, moveTimestamp, teleportTimestamp, isVanishing, isVictorious } = player; const isTeleporting = teleportTimestamp && Date.now() - teleportTimestamp < TELEPORT_ANIMATION_DURATION; const interpolatedPosition = isTeleporting diff --git a/js13k2024/game/src/game-states/intro/game-preview.tsx b/js13k2024/game/src/game-states/intro/game-preview.tsx index 5c6fe4a1..a51f0005 100644 --- a/js13k2024/game/src/game-states/intro/game-preview.tsx +++ b/js13k2024/game/src/game-states/intro/game-preview.tsx @@ -13,13 +13,8 @@ const createPreviewGameState = (): GameState => ({ position: { x: 2, y: 2 }, previousPosition: { x: 2, y: 2 }, moveTimestamp: Date.now(), - isInvisible: false, isVictorious: false, isVanishing: false, - isClimbing: false, - isMonster: false, - hasBlaster: false, - blasterSteps: undefined, }, goal: { x: 4, y: 4 }, obstacles: [ @@ -54,8 +49,6 @@ const createPreviewGameState = (): GameState => ({ explosions: [], timeBombs: [], landMines: [], - crusherActive: false, - builderActive: false, score: 0, gameEndingState: 'none', tsunamiLevel: 0,