Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/core/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import type {
IGlobalStateChangedData,
IObjectDeletedOrCreatedData,
IEntityTagsChangedData,
ICommandBlocked
ICommandBlocked,
IGameMap
} from "@interfaces";
import { EntityManager, UndoManager } from "@core";
import type {
Expand All @@ -29,7 +30,9 @@ import type {
CommandContext,
OnTagsChangesDecoratorsProperties,
OnUIEventDecoratorProperties,
OnConsoleKeyboardEventDecoratorProperites
OnConsoleKeyboardEventDecoratorProperites,
GeometryTypes,
GeometryToPosition
} from "@types";
import { BASE_FPS, BASE_MAX_COMMAND_EXECUTING_ON_TICK_LIMIT, isServer } from "@const";
import { BluePrintsFactory, EffectFactory, IteractionsFactory, QuestsFactory, SoundsFactory } from "@factories";
Expand Down Expand Up @@ -64,8 +67,8 @@ import {
import type { Entity, GameObject } from "@world";
import { ConflictResolverPlugin } from "@plugins";

export class Game implements IGame {
readonly options: IGameOptions;
export class Game<G extends GeometryTypes='2D'|'3D'> implements IGame<G> {
readonly options: IGameOptions<G>;

/**
* Flag indicates game start status
Expand Down Expand Up @@ -394,9 +397,9 @@ export class Game implements IGame {
}

public constructor(
options?: IInitGameOptions
options?: IInitGameOptions<G>
) {
const manager = new EntityManager([], this)
const manager = new EntityManager<G, GeometryToPosition<G>>([], this)

this.options = {
manager,
Expand Down Expand Up @@ -518,7 +521,7 @@ export class Game implements IGame {
return snapshot
}

public load(snapshot: ISnapshot, onLoad?: (game: Game) => void) {
public load(snapshot: ISnapshot<GeometryToPosition<G>>, onLoad?: (game: Game) => void) {
this.options.map.load(snapshot.objects)
this.options.manager.load(snapshot.entities)

Expand Down
34 changes: 17 additions & 17 deletions src/core/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import type {
IDeadData,
IEntityCreatedData
} from "@interfaces";
import type { GridPosition, Position } from "@types";
import type { GeometryToPosition, GeometryTypes, GridPosition, GridPosition3D, Position, Position3D } from "@types";
import { Entity, GameMap } from "@world";
import { checkCollisions, convertPositionToGridPosition } from "@utils";
import { FactoryKeys } from "@enums";

export class EntityManager implements Manager {
public readonly game: Game;
public readonly gameMap: GameMap;
public readonly grid = new Map<GridPosition, Set<Entity>>()
export class EntityManager<G extends GeometryTypes, T extends Position | Position3D = GeometryToPosition<G>> implements Manager<G, T> {
public readonly game: Game<G>;
public readonly gameMap: GameMap<G, T>;
public readonly grid = new Map<GridPosition | GridPosition3D, Set<Entity<G, T>>>()

public entities = new Map<number, Entity>()
public entities = new Map<number, Entity<G, T>>()

public load(rawEntity: ITarget[]) {
public load(rawEntity: ITarget<T>[]) {
this.entities = new Map(rawEntity.map((raw) => {
const entity = Entity.fromSnapshot(raw, this, this.gameMap, this.game.getFactory(FactoryKeys.EFFECTS))
const entity = Entity.fromSnapshot<T, G>(raw, this, this.gameMap, this.game.getFactory(FactoryKeys.EFFECTS))

return [entity.id, entity]
}))
Expand All @@ -30,7 +30,7 @@ export class EntityManager implements Manager {
* @param entity - Entity to add
* @returns { void }
*/
public addToGrid(entity: Entity): void {
public addToGrid(entity: Entity<G, T>): void {
const gridPosition = convertPositionToGridPosition(entity.position)

if (!this.grid.has(gridPosition)) this.grid.set(gridPosition, new Set([entity]))
Expand All @@ -42,7 +42,7 @@ export class EntityManager implements Manager {
* @param entity - Entity to delete
* @returns { void }
*/
public deleteFromGrid(entity: Entity): void {
public deleteFromGrid(entity: Entity<G, T>): void {
const gridPosition = convertPositionToGridPosition(entity.position)
const cell = this.grid.get(gridPosition)

Expand All @@ -59,7 +59,7 @@ export class EntityManager implements Manager {
* @param oldPosition - Entity old position
* @returns { void }
*/
public updateGrid(entity: Entity, oldPosition: Position): void {
public updateGrid(entity: Entity<G, T>, oldPosition: Position | Position3D): void {
const oldGrid = convertPositionToGridPosition(oldPosition)
const newGrid = convertPositionToGridPosition(entity.position)

Expand All @@ -77,8 +77,8 @@ export class EntityManager implements Manager {
}
}

public constructor(entities: Entity[], game: Game) {
this.gameMap = new GameMap(this, game)
public constructor(entities: Entity<G, T>[], game: Game<G>) {
this.gameMap = new GameMap<G, T>(this, game)
this.game = game
this.entities = new Map(entities.map(e => [e.id, e]))
}
Expand All @@ -87,7 +87,7 @@ export class EntityManager implements Manager {
return this.entities.get(id)
}

public create(target: ITarget) {
public create(target: ITarget<T>): Entity<G, T> {
const entity = new Entity(target, this, this.gameMap)

this.addToGrid(entity)
Expand All @@ -103,7 +103,7 @@ export class EntityManager implements Manager {
return entity
}

public update(id: number, target: Partial<ITarget>) {
public update(id: number, target: Partial<ITarget<T>>) {
const entity = this.get(id)

if (!entity) return undefined
Expand All @@ -114,7 +114,7 @@ export class EntityManager implements Manager {
entity.name = target.name ?? entity.name

if (target.position) {
this.updateGrid(target as Entity, entity.position)
this.updateGrid(target as Entity<G, T>, entity.position)

entity.position = target.position
}
Expand Down Expand Up @@ -148,7 +148,7 @@ export class EntityManager implements Manager {
entity.isDead = true
entity.dropInventory()

this.game.processEvent<IDeadData>('entityDead', {
this.game.processEvent<IDeadData<G, T>>('entityDead', {
entity,
eventTime: this.game.currentTick,
eventData: {
Expand Down
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
IGameMap,
IInitGameOptions,
} from "@interfaces";
import type { GeometryTypes } from "@types";

export * from "./const/index.js"
export * from "./core/index.js"
Expand All @@ -23,8 +24,8 @@ export * from "./plugins/index.js"
* @param options - Init game options
* @returns { [game: Game, manager: IEntityManager, map: IGameMap] } - Array with main game iteract objects
*/
export const createGame = (options?: IInitGameOptions): [game: Game, manager: IEntityManager, map: IGameMap] => {
const game = new Game(options)
export const createGame = <G extends GeometryTypes = '2D'>(options?: IInitGameOptions<G>): [game: Game, manager: IEntityManager<G>, map: IGameMap<G>] => {
const game = new Game<G>(options)

return [game, game.options.manager, game.options.map] as const
}
Expand Down
65 changes: 34 additions & 31 deletions src/interfaces/core/engine/classes-engine.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ import type {
AnyPosition,
CustomEventCallback,
EventCallback,
GeometryToPosition,
GeometryTypes,
MiddlewareFn,
Position,
Position3D,
Quad,
SnapshotCallback
} from "@types";
import type { Entity, GameObject } from "@world";

export interface IGame {
export interface IGame<G extends GeometryTypes = '2D'> {
/**
* Init game options
*/
readonly options: IGameOptions;
readonly options: IGameOptions<G>;

/**
* Subscribe to game event
Expand Down Expand Up @@ -107,7 +110,7 @@ export interface IGame {
* @param onLoad - Function will be executed after load snapshot
* @returns { boolean } - True if correct load, else false
*/
readonly load: (snapshot: ISnapshot, onLoad?: (game: Game) => void) => void;
readonly load: (snapshot: ISnapshot<GeometryToPosition<G>>, onLoad?: (game: Game) => void) => void;

/**
* Register a middleware
Expand Down Expand Up @@ -137,43 +140,43 @@ export interface IGame {
readonly stop: () => boolean;
}

export interface IEntityManager {
export interface IEntityManager<G extends GeometryTypes, T extends Position | Position3D = GeometryToPosition<G>> {
/**
* Game reference
*/
readonly game: IGame;
readonly game: IGame<G>;

/**
* Game map reference
*/
readonly gameMap: IGameMap;
readonly gameMap: IGameMap<G, T>;

/**
* Map of all game entities
*/
readonly entities: Map<number, Entity>;
readonly entities: Map<number, Entity<G, T>>;

/**
* Create Entity in world
* @param target - Entity data
* @returns { Entity } - Created entity. Entity can be not created, then executed entityCreatedCollision event
*/
readonly create: (target: ITarget) => Entity;
readonly create: (target: ITarget<T>) => Entity<G, T>;

/**
* Get one Entity by id.
* @param id - ID of Entity
* @returns { Entity | undefined } - Entity if founded, else undefined
*/
readonly get: (id: number) => Entity | undefined;
readonly get: (id: number) => Entity<G, T> | undefined;

/**
* Update one Entity by id
* @param id - ID of entity
* @param target - Updating plants
* @returns { Entity | undefined } - Updated Entity, undefined if not founded
*/
readonly update: (id: number, target: Partial<ITarget>) => Entity | undefined;
readonly update: (id: number, target: Partial<ITarget<T>>) => Entity<G, T> | undefined;

/**
* Delete one Entity by id
Expand Down Expand Up @@ -201,61 +204,61 @@ export interface IEntityManager {
* @param entities - Entities to load
* @returns { void }
*/
readonly load: (rawEntity: ITarget[]) => void;
readonly load: (rawEntity: ITarget<T>[]) => void;
}

export interface IGameMap {
export interface IGameMap<G extends GeometryTypes, T extends Position | Position3D = GeometryToPosition<G>> {
/**
* Entity Manager reference
*/
readonly manager: IEntityManager;
readonly manager: IEntityManager<G, T>;

/**
* Game reference
*/
readonly game: Game;
readonly game: Game<G>;

/**
* Map of all game objects
*/
readonly objects: Map<number, GameObject>;
readonly objects: Map<number, GameObject<T>>;

/**
* Get world objects in quad
* @param quad - Quad to search
* @param returnType - Type of return values
*/
getInQuad(quad: Quad, returnType?: 'ALL'): (Entity | GameObject)[];
getInQuad(quad: Quad, returnType: 'ENTITES'): Entity[];
getInQuad(quad: Quad, returnType: 'OBJECTS'): GameObject[];
getInQuad(quad: Quad, returnType: 'ALL' | 'ENTITES' | 'OBJECTS'): Entity[] | GameObject[] | (Entity | GameObject)[];
getInQuad(quad: Quad, returnType?: 'ALL'): (Entity<G, T> | GameObject<T>)[];
getInQuad(quad: Quad, returnType: 'ENTITES'): Entity<G, T>[];
getInQuad(quad: Quad, returnType: 'OBJECTS'): GameObject<T>[];
getInQuad(quad: Quad, returnType: 'ALL' | 'ENTITES' | 'OBJECTS'): Entity<G, T>[] | GameObject<T>[] | (Entity<G, T> | GameObject<T>)[];

/**
* Teleport one Entity to new position
* @param id - ID of Entity
* @param to - AnyPosition for TP
* @returns { Entity | false } - Entity if teleported, else false
*/
readonly teleport: (id: number, to: AnyPosition) => Entity | false;
readonly teleport: (id: number, to: AnyPosition) => Entity<G, T> | false;

/**
* Get all world objects in provided position
* @param position - Position to get objects
* @returns { (Entity | GameObject)[] } - Array of world objects
*/
getAllInPosition(position: Position, returnType?: 'ALL'): (Entity | GameObject)[];
getAllInPosition(position: Position, returnType: 'ENTITES'): Entity[];
getAllInPosition(position: Position, returnType: 'OBJECTS'): GameObject[];
getAllInPosition(position: Position, returnType: 'ALL' | 'ENTITES' | 'OBJECTS'): Entity[] | GameObject[] | (Entity | GameObject)[];
getAllInPosition(position: Position, returnType:'ALL' | 'ENTITES' | 'OBJECTS'): (Entity | GameObject)[];
getAllInPosition(position: Position | Position3D, returnType?: 'ALL'): (Entity<G, T> | GameObject<T>)[];
getAllInPosition(position: Position | Position3D, returnType: 'ENTITES'): Entity<G, T>[];
getAllInPosition(position: Position | Position3D, returnType: 'OBJECTS'): GameObject<T>[];
getAllInPosition(position: Position | Position3D, returnType: 'ALL' | 'ENTITES' | 'OBJECTS'): Entity<G, T>[] | GameObject<T>[] | (Entity<G, T> | GameObject<T>)[];
getAllInPosition(position: Position | Position3D, returnType:'ALL' | 'ENTITES' | 'OBJECTS'): (Entity<G, T> | GameObject<T>)[];

/**
* Create game object
* @param obj - Object info
* @param metadata - Object metadata
* @returns { GameObject } - GameObject, also generates object error events, if need
*/
readonly createObject: <T = any>(obj: IGameObject<T>, metadata?: T) => GameObject;
readonly createObject: <M = any>(obj: IGameObject<M, T>, metadata?: M) => GameObject<T>;

/**
* Delete one object by id
Expand All @@ -269,13 +272,13 @@ export interface IGameMap {
* @param id - ID of object
* @returns { GameObject | undefined } - GameObject if founded, else undefined
*/
readonly getObject: (id: number) => GameObject | undefined;
readonly getObject: (id: number) => GameObject<T> | undefined;

/**
* Get all Items on map
* @returns { (GameObject & IGameObject & IWorldItem)[] } - Array of Items
*/
readonly getAllItems: () => (GameObject & IGameObject & IWorldItem)[];
readonly getAllItems: () => (GameObject<T, G> & IGameObject<any, T> & IWorldItem<T>)[];

/**
* Checks a given object by ID is ok: exists, no collisions in position, exists in position
Expand All @@ -289,17 +292,17 @@ export interface IGameMap {
* @param objects - Objects to load
* @returns { void }
*/
readonly load: (rawObjects: IGameObject[]) => void;
readonly load: (rawObjects: IGameObject<any, T>[]) => void;

/**
* Apply effect to provided Quad area
* @param quad - Quad to apply effect
* @param effect - Effect to apply
* @param duration - Effect duration
* @param excludeId - Optional ID of entity, effect will not be applied to her
* @returns { Entity[] } - Array of entities founded in quad on applying effect
* @returns { Entity<T>[] } - Array of entities founded in quad on applying effect
*/
readonly applyEffectToQuad: (quad: Quad, effect: IGameEffect, duration: number, excludeId?: number) => Entity[];
readonly applyEffectToQuad: (quad: Quad, effect: IGameEffect, duration: number, excludeId?: number) => Entity<G, T>[];
}

export interface IDeligator {
Expand Down
Loading
Loading