Skip to content

Commit

Permalink
13 bonuses wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gtanczyk committed Aug 22, 2024
1 parent e561e0b commit 4e9e1a8
Show file tree
Hide file tree
Showing 15 changed files with 840 additions and 134 deletions.
74 changes: 71 additions & 3 deletions js13k2024/game/design/gameplay.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,75 @@
- Can be combined with other bonuses (e.g., Builder) for advanced strategies.
- Players must plan carefully to make the most of the limited duration.

### Teleport
- Instantly moves the player to another location on the grid.
- Useful for quick escapes or reaching distant areas.
- May be strategically placed to help or hinder the player's progress.

### Tsunami
- Gradually floods the grid with water over 13 steps.
- Effects:
- Step 1-6: Water starts to appear, slowing down player and monster movement slightly.
- Step 7-12: Water level rises, further slowing movement and making lower areas dangerous.
- Step 13: Full flood, eliminating all entities (player and monsters) not on elevated positions.
- Interactions:
- Climber bonus allows the player to survive on obstacles during a flood.
- Can be used strategically to eliminate multiple monsters at once.
- Changes the dynamics of the level, forcing players to seek higher ground.

### Monster
- Transforms the player into a monster for 13 steps.
- Effects:
- Player gains the appearance and some abilities of a monster.
- Existing monsters become vulnerable "players" during this time.
- The goal changes: eliminate all monster-players to win the level.
- If any monster-player reaches the original goal, it's game over.
- Strategic considerations:
- Completely changes the gameplay dynamic for a short period.
- Requires quick thinking and adaptation from the player.
- Can be used to clear the level of monsters if used skillfully.

### Slide
- Changes the player's movement to a continuous slide until hitting an obstacle or grid edge.
- Effects:
- Player moves in the chosen direction until stopped by an obstacle or the grid boundary.
- Allows for quick traversal of open areas.
- Can potentially slide past monsters without being caught.
- Strategic considerations:
- Requires careful planning to avoid sliding into dangerous situations.
- Can be combined with other bonuses for interesting effects (e.g., Slide + Crusher).
- Useful for quickly reaching distant bonuses or the goal.

### Sokoban
- Grants the player the ability to push obstacles.
- Effects:
- Player can move obstacles by pushing them.
- Pushed obstacles can crush monsters, eliminating them.
- Allows for reshaping the level layout.
- Strategic considerations:
- Can be used to create new paths or block existing ones.
- Pushing obstacles strategically can help manage monster movements.
- Requires spatial awareness and forward-thinking.

### Blaster
- Equips the player with a blaster that shoots in the direction of movement.
- Effects:
- When the player moves, a blast is fired in the same direction.
- Blasts can eliminate monsters in their path.
- Does not affect obstacles.
- Strategic considerations:
- Allows for offensive play against monsters.
- Requires planning to line up shots effectively.
- Can be used to clear a path to the goal.

## Scoring System

### Points
- Base points are awarded for completing a level.
- Bonus points are given for:
- Completing the level in fewer steps
- Effectively using bonuses
- Eliminating monsters (with new bonuses like Blaster or Monster transformation)

### Time Bonus
- A timer runs during gameplay.
Expand Down Expand Up @@ -118,6 +180,12 @@
4. Introduction of various bonus types one by one.
5. Combining multiple bonus types and increasing monster count.
6. Complex layouts requiring precise planning and execution of bonus usage.
7. The final level combining all learned mechanics in a challenging setup.

This gameplay design creates a tense, strategic experience that constantly reminds players of the "13" threat, embodying the theme of triskaidekaphobia while providing engaging and progressively challenging gameplay through 13 unique levels.
7. Introduction of new bonuses (Tsunami, Monster, Slide, Sokoban, Blaster) in later levels.
8. Levels designed to specifically challenge players with new bonus mechanics.
9. Combining new and old bonuses for complex puzzle solutions.
10. Increased monster spawn rate or initial count to raise difficulty.
11. Tighter time constraints for achieving higher scores.
12. Levels with limited bonuses, requiring perfect use of available resources.
13. The final level combining all learned mechanics in a challenging setup.

This gameplay design creates a tense, strategic experience that constantly reminds players of the "13" threat, embodying the theme of triskaidekaphobia while providing engaging and progressively challenging gameplay through 13 unique levels. The addition of new bonuses like Tsunami, Monster, Slide, Sokoban, and Blaster adds layers of complexity and strategic depth to the game, ensuring a rich and varied player experience.
4 changes: 3 additions & 1 deletion js13k2024/game/src/game-states/gameplay/animation-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ElectricalDischarge } from './gameplay-types';

export const MOVE_ANIMATION_DURATION = 250; // 1/4 second
export const TELEPORT_ANIMATION_DURATION = 500; // 1/2 second
export const BLASTER_SHOT_DURATION = 250; // 1/2 second

export const calculateAnimationFactor = (): number => {
return Math.cos((Date.now() / 1000) * Math.PI);
Expand Down Expand Up @@ -30,8 +31,9 @@ export const interpolatePosition = (
currentPosition: Position,
previousPosition: Position,
moveTimestamp: number,
duration = MOVE_ANIMATION_DURATION,
): Position => {
const moveProgress = Math.min(Date.now() - moveTimestamp, MOVE_ANIMATION_DURATION) / MOVE_ANIMATION_DURATION;
const moveProgress = Math.min(Date.now() - moveTimestamp, duration) / duration;

return {
x: previousPosition.x + (currentPosition.x - previousPosition.x) * moveProgress,
Expand Down
110 changes: 110 additions & 0 deletions js13k2024/game/src/game-states/gameplay/bonus-effect-render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { BLASTER_SHOT_DURATION, interpolatePosition } from './animation-utils';
import { BlasterShot, Direction, GameState, Position } from './gameplay-types';
import { toIsometric } from './isometric-utils';

export const drawTsunamiEffect = (
ctx: CanvasRenderingContext2D,
gameState: GameState,
gridSize: number,
cellSize: number,
) => {
const { tsunamiLevel } = gameState;
const maxWaterHeight = cellSize * 0.8; // Maximum water height when tsunamiLevel reaches 13

ctx.save();
ctx.globalAlpha = 0.6; // Make the water slightly transparent

for (let y = 0; y < gridSize; y++) {
for (let x = 0; x < gridSize; x++) {
const { x: isoX, y: isoY } = toIsometric(x, y);
const waterHeight = (tsunamiLevel / 13) * maxWaterHeight;

ctx.fillStyle = `rgba(0, 100, 255, ${tsunamiLevel / 13})`; // Blue color with increasing opacity
ctx.beginPath();
ctx.moveTo(isoX, isoY);
ctx.lineTo(isoX + cellSize / 2, isoY + cellSize / 4);
ctx.lineTo(isoX, isoY + cellSize / 2);
ctx.lineTo(isoX - cellSize / 2, isoY + cellSize / 4);
ctx.closePath();
ctx.fill();

// Add wave effect
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(isoX - cellSize / 2, isoY + cellSize / 4 - waterHeight + Math.sin(Date.now() / 200 + x * 0.5) * 5);
ctx.lineTo(
isoX + cellSize / 2,
isoY + cellSize / 4 - waterHeight + Math.sin(Date.now() / 200 + (x + 1) * 0.5) * 5,
);
ctx.stroke();
}
}

ctx.restore();
};

export const drawSlideTrail = (ctx: CanvasRenderingContext2D, start: Position, end: Position) => {
const { x: startX, y: startY } = toIsometric(start.x, start.y);
const { x: endX, y: endY } = toIsometric(end.x, end.y);

ctx.save();
ctx.strokeStyle = 'rgba(0, 255, 255, 0.5)'; // Cyan color with transparency
ctx.lineWidth = 3;
ctx.setLineDash([5, 5]); // Create a dashed line effect

ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();

ctx.restore();
};

export const drawBlasterShot = (ctx: CanvasRenderingContext2D, shot: BlasterShot, cellSize: number) => {
const pos = interpolatePosition(shot.endPosition, shot.startPosition, shot.shotTimestamp, BLASTER_SHOT_DURATION);
const { x: isoX, y: isoY } = toIsometric(pos.x, pos.y);

ctx.save();
ctx.fillStyle = 'yellow';
ctx.strokeStyle = 'orange';
ctx.lineWidth = 2;

const shotSize = cellSize * 0.2;

ctx.beginPath();
ctx.arc(isoX, isoY, shotSize, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();

// Add a directional indicator
const arrowSize = cellSize * 0.3;
ctx.beginPath();
switch (shot.direction) {
case Direction.Up:
ctx.moveTo(isoX, isoY - arrowSize);
ctx.lineTo(isoX - arrowSize / 2, isoY);
ctx.lineTo(isoX + arrowSize / 2, isoY);
break;
case Direction.Down:
ctx.moveTo(isoX, isoY + arrowSize);
ctx.lineTo(isoX - arrowSize / 2, isoY);
ctx.lineTo(isoX + arrowSize / 2, isoY);
break;
case Direction.Left:
ctx.moveTo(isoX - arrowSize, isoY);
ctx.lineTo(isoX, isoY - arrowSize / 2);
ctx.lineTo(isoX, isoY + arrowSize / 2);
break;
case Direction.Right:
ctx.moveTo(isoX + arrowSize, isoY);
ctx.lineTo(isoX, isoY - arrowSize / 2);
ctx.lineTo(isoX, isoY + arrowSize / 2);
break;
}
ctx.closePath();
ctx.fill();
ctx.stroke();

ctx.restore();
};
Loading

0 comments on commit 4e9e1a8

Please sign in to comment.