Skip to content

Commit

Permalink
Add basic second level support
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminbours committed Dec 17, 2023
1 parent 08b869d commit a264122
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 37 deletions.
2 changes: 2 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@
## To fix

- manage disconnect event while in game
- Fix issue when you finished first level and then you go with your team mate in the second one
- Fix what it seems to be physics difference between client and server on level 2
8 changes: 7 additions & 1 deletion back/src/socket/socket.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SocketEvent,
GameState,
PositionLevel,
ProjectionLevel,
FLOOR,
TimeSyncPayload,
PhysicLoop,
Expand Down Expand Up @@ -249,6 +250,8 @@ export class SocketGateway {
switch (players[0].player.selectedLevel) {
case Levels.CRACK_THE_DOOR:
return new PositionLevel();
case Levels.LEARN_TO_FLY:
return new ProjectionLevel();
}
})();

Expand Down Expand Up @@ -300,7 +303,10 @@ export class SocketGateway {
this.registerGameLoop(game.id, level);
}

registerGameLoop = (gameId: number, level: PositionLevel) => {
registerGameLoop = (
gameId: number,
level: PositionLevel | ProjectionLevel,
) => {
// TODO: The following variable declared here and accessible in the process
// input queue closure are potential memory leaks.
// Let's try to declare them only once somewhere else, or to update
Expand Down
5 changes: 3 additions & 2 deletions front/app/Game/levels/levels.controller.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// vendors
import { Scene, Vector3 } from 'three';
// our libs
import { Levels } from '@benjaminbours/composite-core';
import { Levels, ProjectionLevel } from '@benjaminbours/composite-core';
// local
import { LightPlayer, ShadowPlayer, Player } from '../Player';
import { PositionLevelWithGraphic } from './PositionLevelWithGraphic';

export default class LevelController {
public levels: {
[Levels.CRACK_THE_DOOR]: PositionLevelWithGraphic;
[Levels.LEARN_TO_FLY]?: PositionLevelWithGraphic;
[Levels.LEARN_TO_FLY]: ProjectionLevel;
[Levels.THE_HIGH_SPHERES]?: PositionLevelWithGraphic;
};

Expand All @@ -18,6 +18,7 @@ export default class LevelController {
this.levels = {
// [Levels.]: new TestLevel(),
[Levels.CRACK_THE_DOOR]: new PositionLevelWithGraphic(),
[Levels.LEARN_TO_FLY]: new ProjectionLevel(),
};
}

Expand Down
2 changes: 1 addition & 1 deletion front/app/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export function Menu({
id: Levels.LEARN_TO_FLY,
name: 'Learn to fly',
img: '/learn_to_fly.png',
disabled: true,
disabled: false,
},
{
id: Levels.THE_HIGH_SPHERES,
Expand Down
35 changes: 28 additions & 7 deletions packages/core/lib/GameState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ export enum Levels {
}

interface Level {
doors: {
[key: string]: number[];
};
end_level: number[];
}

export interface PositionLevelState extends Level {
doors: {
[key: string]: number[];
};
id: Levels.CRACK_THE_DOOR;
}

export interface ProjectionLevelState extends Level {
id: Levels.LEARN_TO_FLY;
}

interface OtherLevelState extends Level {
id: Levels.LEARN_TO_FLY;
}

export type LevelState = PositionLevelState | OtherLevelState;
export type LevelState =
| PositionLevelState
| ProjectionLevelState
| OtherLevelState;

// orders of properties are very important here
export class RedisGameState {
Expand All @@ -43,6 +50,22 @@ export class RedisGameState {
) {}

static parseGameState(state: GameState) {
// TODO: Remove code duplication, function is copy pasted from apply world update
const isPositionLevel = (
value: LevelState,
): value is PositionLevelState =>
Boolean((value as PositionLevelState).doors);

const doors: [string, string] = (() => {
if (isPositionLevel(state.level)) {
return [
state.level.doors.ground.join(),
state.level.doors.roof.join(),
];
}
return ['', ''];
})();

return new RedisGameState(
String(state.level.id),
String(state.players[1].position.x),
Expand All @@ -56,8 +79,7 @@ export class RedisGameState {
String(state.lastValidatedInput),
String(state.game_time),
state.level.end_level.join(),
(state.level as PositionLevelState).doors.ground.join(),
(state.level as PositionLevelState).doors.roof.join(),
...doors,
);
}
}
Expand Down Expand Up @@ -98,7 +120,6 @@ export class GameState {
return {
id: level,
end_level: parseActivators(state.end_level),
doors: {},
};
}
})() as LevelState;
Expand Down
52 changes: 52 additions & 0 deletions packages/core/lib/levels/ProjectionLevel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// vendors
import { Group, Object3D, Vector3 } from 'three';
// local
import {
ElementName,
createArchGroup,
createWall,
createWallDoor,
positionOnGrid,
} from './levels.utils';
import { InteractiveArea } from '../elements/InteractiveArea';
import { Levels, ProjectionLevelState } from '../GameState';

export class ProjectionLevel extends Group {
public collidingElements: Object3D[] = [];
public interactiveElements: any[] = [];
public name = 'projection-level';

public startPosition = {
light: new Vector3(10, 20, 0), // start level
shadow: new Vector3(200, 20, 0),
};

public state: ProjectionLevelState = {
id: Levels.LEARN_TO_FLY,
end_level: [],
};

constructor() {
super();
const wallBlockingLeftPath = createWall(
new Vector3(4, 2, 0),
new Vector3(-2, 0, 2),
new Vector3(0, 90, 0),
);
this.add(wallBlockingLeftPath);
this.collidingElements.push(wallBlockingLeftPath);

const arches = [
createArchGroup(1, new Vector3(4, 0, 0)),
createArchGroup(3, new Vector3(5, 0, 0)),
createArchGroup(2, new Vector3(2, 0, 0)),
createArchGroup(3, new Vector3(10, 0, 0)),
];

arches.forEach((arch) => {
this.add(arch);
// TODO: Add only the platform to the list of colliding elements
this.collidingElements.push(arch);
});
}
}
1 change: 1 addition & 0 deletions packages/core/lib/levels/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './levels.utils';
export * from './PositionLevel';
export * from './ProjectionLevel';
67 changes: 41 additions & 26 deletions packages/core/lib/physics/movementHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import {
MovableComponentState,
Side,
} from '../types';
import { GameState } from '../GameState';
import {
GameState,
LevelState,
Levels,
PositionLevelState,
} from '../GameState';
import { computeVelocityX } from './velocity';
import { INearestObjects } from './raycaster';
import { AREA_DOOR_OPENER_SUFFIX, ElementName } from '../levels';
Expand Down Expand Up @@ -224,36 +229,46 @@ function applyWorldUpdate(
collisionResult: INearestObjects,
context: Context,
) {
let doorNameActivating: string | undefined = undefined;
if (collisionResult.down && isTouchingDoorOpener(collisionResult.down)) {
const elem = collisionResult.down.object.parent as InteractiveArea;
doorNameActivating = `${elem.name.replace(
`_${AREA_DOOR_OPENER_SUFFIX}`,
'',
)}`;
if (gameState.level.doors[doorNameActivating].indexOf(side) === -1) {
gameState.level.doors[doorNameActivating].push(side);
const isPositionLevel = (value: LevelState): value is PositionLevelState =>
Boolean((value as PositionLevelState).doors);

if (isPositionLevel(gameState.level)) {
let doorNameActivating: string | undefined = undefined;
if (
collisionResult.down &&
isTouchingDoorOpener(collisionResult.down)
) {
const elem = collisionResult.down.object.parent as InteractiveArea;
doorNameActivating = `${elem.name.replace(
`_${AREA_DOOR_OPENER_SUFFIX}`,
'',
)}`;
if (
gameState.level.doors[doorNameActivating].indexOf(side) === -1
) {
gameState.level.doors[doorNameActivating].push(side);
}
}
}

for (const key in gameState.level.doors) {
const activators = gameState.level.doors[key];
for (const key in gameState.level.doors) {
const activators = gameState.level.doors[key];

// if this door opener is not the one we are currently activating
// remove us from the list of activators
if (key !== doorNameActivating) {
const index = activators.indexOf(side);
if (index !== -1) {
activators.splice(index, 1);
// if this door opener is not the one we are currently activating
// remove us from the list of activators
if (key !== doorNameActivating) {
const index = activators.indexOf(side);
if (index !== -1) {
activators.splice(index, 1);
}
}
}

if (context === 'server') {
const wallDoor = obstacles.find(
(e) => e.name === ElementName.WALL_DOOR(key),
);
if (wallDoor) {
updateDoor(wallDoor, activators.length > 0);
if (context === 'server') {
const wallDoor = obstacles.find(
(e) => e.name === ElementName.WALL_DOOR(key),
);
if (wallDoor) {
updateDoor(wallDoor, activators.length > 0);
}
}
}
}
Expand Down

0 comments on commit a264122

Please sign in to comment.