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

100% Statements 102/102
95.65% Branches 22/23
100% Functions 7/7
100% Lines 102/102

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 101 102 1031x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 1x 1x 21x 21x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 2x 2x 21x 21x 9x 9x 9x 9x 9x 9x 7x 7x 7x 7x 7x 6x 6x 3x 3x 3x 3x 3x 1x 3x 1x 1x 3x 6x 7x 9x 9x 9x 21x 21x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 21x 21x 3x 3x 3x 3x 3x 1x 1x 1x 3x 1x 1x 1x 3x 21x  
import { GameMap, PositionInterface } from "../../maps/index";
import { TileInterface } from "../../tiles/index";
 
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 (this.position != null && world != null && world.getMap() !== null) {
            const map: GameMap = world.getMap();
            const tile: TileInterface = map.getTile(this.position);
            const targets: Entity[] = this.filterTargetsAtPosition();
 
            if (tile !== null) {
                // if there is a target, attack
                if (targets.length !== 0) {
                    this.attack(targets[0], world);
                } else {
                    // else move
                    // eslint-disable-next-line no-lonely-if
                    if (tile.isWalkable) {
                        move.canMove = true;
                    } else if (tile.isDiggable) {
                        map.dig(this.position);
                    }
                }
            }
        }
 
        return move;
    }
 
    private filterTargetsAtPosition(): Entity[] {
        // eslint-disable-next-line compat/compat
        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 {
        // eslint-disable-next-line compat/compat
        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);
        }
    }
}