Skip to content

Commit

Permalink
feat: fleshing sub grid actor movements
Browse files Browse the repository at this point in the history
  • Loading branch information
hxtree committed Dec 18, 2024
1 parent 0e71df9 commit ca48063
Show file tree
Hide file tree
Showing 24 changed files with 556 additions and 245 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 50 additions & 34 deletions clients/player-client/src/core/GameEngine/GameEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { GameState } from '../../dtos/GameState.dto';
import { InputEventRecord } from '../../dtos/Player/InputEventRecord.dto';
import { DateTime, Duration } from 'luxon';
import { InputActionType } from '../../context/Input/InputActionType.type';
import { ActorOrientation } from '../../dtos/Actor/ActorDirection.type';
import { ActorOrientation } from '../../dtos/Actor/ActorOrientation.type';
import { Coordinate3d } from '../../dtos/Coordinate3d.dto';
import { WalkAction } from '../../dtos/Actions/WalkAction';

export type GameEngineProps = {
data: GameState;
Expand Down Expand Up @@ -78,66 +79,81 @@ export const GameEngine: React.FC<GameEngineProps> = props => {
// TODO automate this
const actorIndex = 0;

data.actors[actorIndex].animation.startTimestamp = DateTime.now();
data.actors[actorIndex].movement.isInMotion = true;
data.actors[actorIndex].movement.currentPosition =
data.actors[actorIndex].movement.targetPosition;
data.actors[actorIndex].movement.startTimestamp = DateTime.now();
data.actors[actorIndex].movement.movementDuration = Duration.fromObject({
seconds: 10,
});

switch (inputContext.state.key) {
case InputEventRecordKey.LEFT:
data.actors[actorIndex].animation.orientation =
ActorOrientation.NORTHWEST;
if (
isTraversable(data, {
z: data.actors[actorIndex].movement.targetPosition.z,
y: data.actors[actorIndex].movement.targetPosition.y,
x: data.actors[actorIndex].movement.targetPosition.x - 1,
z: data.actors[actorIndex].position.gridZ,
y: data.actors[actorIndex].position.gridY,
x: data.actors[actorIndex].position.gridX - 1,
})
) {
data.actors[actorIndex].movement.targetPosition.x--;
data.actors[actorIndex].addAction(
new WalkAction({
wait: Duration.fromObject({ seconds: 0 }),
act: Duration.fromObject({ seconds: 3 }),
recovery: Duration.fromObject({ seconds: 0 }),
direction: ActorOrientation.NORTHWEST,
frames: 10,
}),
);
}
break;
case InputEventRecordKey.RIGHT:
data.actors[actorIndex].animation.orientation =
ActorOrientation.SOUTHEAST;
if (
isTraversable(data, {
z: data.actors[actorIndex].movement.targetPosition.z,
y: data.actors[actorIndex].movement.targetPosition.y,
x: data.actors[actorIndex].movement.targetPosition.x + 1,
z: data.actors[actorIndex].position.gridZ,
y: data.actors[actorIndex].position.gridY,
x: data.actors[actorIndex].position.gridX + 1,
})
) {
data.actors[actorIndex].movement.targetPosition.x++;
data.actors[actorIndex].addAction(
new WalkAction({
wait: Duration.fromObject({ seconds: 0 }),
act: Duration.fromObject({ seconds: 1 }),
recovery: Duration.fromObject({ seconds: 0 }),
direction: ActorOrientation.SOUTHEAST,
frames: 10,
}),
);
}
break;
case InputEventRecordKey.UP:
data.actors[actorIndex].animation.orientation =
ActorOrientation.NORTHEAST;
if (
isTraversable(data, {
z: data.actors[actorIndex].movement.targetPosition.z,
y: data.actors[actorIndex].movement.targetPosition.y - 1,
x: data.actors[actorIndex].movement.targetPosition.x,
z: data.actors[actorIndex].position.gridZ,
y: data.actors[actorIndex].position.gridY - 1,
x: data.actors[actorIndex].position.gridX,
})
) {
data.actors[actorIndex].movement.targetPosition.y--;
data.actors[actorIndex].addAction(
new WalkAction({
wait: Duration.fromObject({ seconds: 0 }),
act: Duration.fromObject({ seconds: 1 }),
recovery: Duration.fromObject({ seconds: 0 }),
direction: ActorOrientation.NORTHEAST,
frames: 10,
}),
);
}
break;
case InputEventRecordKey.DOWN:
data.actors[actorIndex].animation.orientation =
ActorOrientation.SOUTHWEST;
if (
isTraversable(data, {
z: data.actors[actorIndex].movement.targetPosition.z,
y: data.actors[actorIndex].movement.targetPosition.y + 1,
x: data.actors[actorIndex].movement.targetPosition.x,
z: data.actors[actorIndex].position.gridZ,
y: data.actors[actorIndex].position.gridY + 1,
x: data.actors[actorIndex].position.gridX,
})
) {
data.actors[actorIndex].movement.targetPosition.y++;
data.actors[actorIndex].addAction(
new WalkAction({
wait: Duration.fromObject({ seconds: 0 }),
act: Duration.fromObject({ seconds: 1 }),
recovery: Duration.fromObject({ seconds: 0 }),
direction: ActorOrientation.SOUTHWEST,
frames: 10,
}),
);
}
break;
case InputEventRecordKey.DEBUG:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActorOrientation } from '../../../dtos/Actor/ActorDirection.type';
import { ActorOrientation } from '../../../dtos/Actor/ActorOrientation.type';

interface Position {
x: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { GRID_WIDTH } from './GridDimensions';
import { gridToCanvasCoordinate } from './IsometricTransformer';
import SpriteMap from '../draw/SpriteMap';
import { drawDiamond } from '../draw/DrawDiamond';
import { SpriteMapRecord } from '../../../dtos/SpriteMapRecord.dto';
import { SpriteMapRecord } from '../../../dtos/SpriteMap/SpriteMapRecord.dto';
import { drawDialogue } from '../draw/DrawDialogue';
import { drawCoordinates } from '../draw/DrawCoordinates';
import { kebabCase } from 'lodash';
import { Dialogue } from '../../../dtos/Dialogue.dto';
import { GridAnimation } from '../../../dtos/Grid/GridAnimation.dto';
import { Actor } from '../../../dtos/Actor/Actor.dto';
import { drawMeter } from '../draw/DrawMeter';
import { getPosition } from './getPosition';
import { GridField } from '../../../dtos/Grid/GridItem.dto';

export class IsometricRender {
Expand Down Expand Up @@ -144,12 +143,18 @@ export class IsometricRender {
}

private findActorsByPosition(position: Coordinate3D): Actor[] {
return this._actors.filter(
actor =>
actor.movement.renderPosition?.x === position.x &&
actor.movement.renderPosition?.y === position.y &&
actor.movement.renderPosition?.z === position.z,
);
return this._actors.filter(actor => {
const actorRenderPosition = actor.gridRenderPosition();
if (
actorRenderPosition.x === position.x &&
actorRenderPosition.y === position.y &&
actorRenderPosition.z === position.z
) {
actor.processActions();
return true;
}
return false;
});
}

private renderText(ctx: CanvasRenderingContext2D) {
Expand Down Expand Up @@ -255,18 +260,14 @@ export class IsometricRender {
const actors = this.findActorsByPosition({ x, y, z });

actors.forEach((actor: Actor) => {
const position = getPosition(vectors);
const position = actor.screenPosition(vectors);

this._spriteMaps['shadow'].draw(ctx, 1, position, 0.618);

const actorSpriteMapId = kebabCase(
`${actor.actorId}-${actor.animation?.currentAnimation || 'idle'}`,
);

this._spriteMaps[actorSpriteMapId].drawFrame(
this._spriteMaps[actor.spriteMapId].drawFrame(
ctx,
actor.animation.orientation,
actor.animation.currentFrame,
actor.orientation,
actor.spriteMapCurrentFrame,
position,
1,
);
Expand Down
34 changes: 34 additions & 0 deletions clients/player-client/src/dtos/Actions/Action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Duration } from 'luxon';
import { Actor } from '../Actor/Actor.dto';
import { SpriteMapActionId } from '../SpriteMap/SpriteMapType.type';

export abstract class Action {
waitDuration: Duration;
actDuration: Duration;
recoveryDuration: Duration;

public spriteMapId: SpriteMapActionId;
public progress: number;
public frames: number;

constructor(params: { wait?: Duration; act: Duration; recovery?: Duration }) {
this.waitDuration = params.wait ?? Duration.fromObject({ seconds: 0 });
this.actDuration = params.act ?? Duration.fromObject({ seconds: 0 });
this.recoveryDuration =
params.recovery ?? Duration.fromObject({ seconds: 0 });
}

isComplete(): boolean {
return this.progress === 100;
}

frameDuration(): Duration {
const duration = this.actDuration;
const divisor = this.frames;
const milliseconds = duration.as('milliseconds');
const dividedMilliseconds = milliseconds / divisor;
return Duration.fromMillis(dividedMilliseconds);
}

abstract actionRun(actor: Actor, targets?: Actor[]): Promise<boolean>;
}
66 changes: 66 additions & 0 deletions clients/player-client/src/dtos/Actions/WalkAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Duration } from 'luxon';
import { ActorOrientation } from '../Actor/ActorOrientation.type';
import { Actor } from '../Actor/Actor.dto';
import { Action } from './Action';
import { pause } from './pause';
import { SpriteMapActionId } from '../SpriteMap/SpriteMapType.type';

export class WalkAction extends Action {
direction: ActorOrientation;

constructor(params: {
wait: Duration;
act: Duration;
recovery: Duration;
direction: ActorOrientation;
frames: number;
}) {
super(params);
this.direction = params.direction;
this.frames = params.frames;

this.progress = 0;
this.spriteMapId = SpriteMapActionId.WALK;
}

async actionRun(actor: Actor): Promise<boolean> {
const executeRun = async (
resolve: (value: boolean | PromiseLike<boolean>) => void,
) => {
this.progress = 0;
let delta: { x?: number; y?: number } = {};

switch (this.direction) {
case ActorOrientation.NORTHEAST:
delta = { y: -0.1 };
break;
case ActorOrientation.NORTHWEST:
delta = { x: -0.1 };
break;
case ActorOrientation.SOUTHEAST:
delta = { x: 0.1 };
break;
case ActorOrientation.SOUTHWEST:
delta = { y: 0.1 };
break;
}

actor.spriteMapActionId = this.spriteMapId;
actor.orientation = this.direction;

for (let currentFrame = 0; currentFrame < this.frames; currentFrame++) {
actor.position.move(delta);
actor.spriteMapCurrentFrame = currentFrame;
this.progress = ((currentFrame + 1) / this.frames) * 100;
await pause(this.frameDuration());
}

actor.spriteMapCurrentFrame = 0;
actor.spriteMapActionId = SpriteMapActionId.IDLE;

resolve(true);
};

return new Promise(executeRun);
}
}
7 changes: 7 additions & 0 deletions clients/player-client/src/dtos/Actions/pause.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Duration } from 'luxon';

export function pause(duration: Duration): Promise<void> {
return new Promise(resolve =>
setTimeout(resolve, duration.as('milliseconds')),
);
}
Loading

0 comments on commit ca48063

Please sign in to comment.