All files / games/engine/src/systems movement.ts

100% Statements 37/37
92.59% Branches 25/27
100% Functions 8/8
100% Lines 37/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100  16x 16x 16x 16x 16x 16x         13x 13x 13x     1x     2x 2x 2x 1x 1x 1x           9x       9x 7x 7x 7x 7x   6x 3x       3x 1x   2x 1x         9x     7x 7x 7x             3x 3x       1x 1x       16x                                              
import { GameMap, PositionInterface } from "jga-games-maps";
 
import { TileInterface } from "jga-games-tiles";
 
import { ComponentName, isPositionComponent } from "../components";
 
import { isActor, Entity, isEntity } from "../entities";
import { World } from "../world";
import { DamageSystem } from "./damage";
import { System } from "./system";
 
export interface MoveInterface {
    canMove: boolean;
    newPosition: PositionInterface | null;
}
 
export class MovementSystem extends System {
    protected position: PositionInterface;
    protected targets: Map<string, Entity>;
 
    public constructor(entities: Map<string, Entity>, position: PositionInterface, targets: Map<string, Entity>) {
        super(entities, [ComponentName.MOVEABLE, ComponentName.POSITION]);
 
        this.position = position;
        this.targets = this.filterEntities(targets, [ComponentName.POSITION]);
    }
 
    public getPosition(): PositionInterface {
        return this.position;
    }
 
    public run(world: World): void {
        return this.entities.forEach((entity): void => {
            const move = this.canMove(world);
 
            if (move.canMove) {
                const positionComponent = entity.getComponent(ComponentName.POSITION);
 
                if (isPositionComponent(positionComponent)) {
                    positionComponent.setPosition(this.position);
                }
            }
        });
    }
 
    public canMove(world: World): MoveInterface {
        const move: MoveInterface = {
            canMove: false,
            newPosition: this.position,
        };
 
        if (null != this.position && null != world && null !== world.getMap()) {
            const map: GameMap = world.getMap();
            const tile: TileInterface = map.getTile(this.position);
            const targets: Entity[] = this.filterTargetsAtPosition();
 
            if (null !== tile) {
                // if there is a target, attack
                if (0 !== targets.length) {
                    this.attack(targets[0], world);
                } else {
                    // else move
                    if (tile.isWalkable) {
                        move.canMove = true;
                    } else if (tile.isDiggable) {
                        map.dig(this.position);
                    }
                }
            }
        }
 
        return move;
    }
 
    private filterTargetsAtPosition(): Entity[] {
        return Array.from(this.targets.values()).filter((entity) => {
            const entityPosition = entity.getComponent(ComponentName.POSITION);
 
            return isPositionComponent(entityPosition)
                ? this.position.x === entityPosition.getPosition().x &&
                      this.position.y === entityPosition.getPosition().y
                : false;
        });
    }
 
    private attack(target: Entity, world: World): void {
        const attacker = Array.from(this.entities.values())[0];
 
        if (
            isActor(attacker) &&
            isEntity(target) &&
            attacker.hasComponent(ComponentName.ATTACK) &&
            target.hasComponent(ComponentName.DAMAGEABLE)
        ) {
            const damageSystem = new DamageSystem(attacker, target);
            damageSystem.run(world);
        }
    }
}