<commit_before>
import RG from '../rg';
import {Menu, SelectionObject} from '../menu';
import {Keys} from '../keymap';
import {Cell} from '../map.cell';
import * as GoalsBattle from '../goals-battle';
import * as Cmd from '../cmd-player';
import * as Component from '../component/component';
import {BaseActor, SentientActor} from '../actor';
import {Random} from '../random';
import {Geometry} from '../geometry';

import {BrainBase} from './brain.base';
import {Brain} from './brain';
import {Memory} from './brain.memory';

import {PlayerCmdInput} from '../interfaces';

type BrainGoalOriented = import('./brain.goaloriented').BrainGoalOriented;
type ItemBase = import('../item').ItemBase;

const RNG = Random.getRNG();
const KeyMap = Keys.KeyMap;

const {
    ACTION_ALREADY_DONE,
    ACTION_ZERO_ENERGY } = Cmd;

const selectTargetMsg =
    'Select a target (all with "A"), then press "s" to choose it';
const lookCellMsg =
    'Select a target with movement keys, then press "s" to choose it';

const chatSelObject = player => {
    const msg = 'Select direction for chatting:';
    RG.gameMsg(msg);
    return {
        select: code => {
            const args = {
                dir: KeyMap.getDir(code),
                src: null
            };
            if (args.dir) {
                args.src = player;
                return () => {
                    const chatComp = new Component.Chat();
                    chatComp.setArgs(args);
                    player.add(chatComp);
                };
            }
            return null;
        },
        showMenu: () => false
    };
};


/* Memory object for the player .*/
export class MemoryPlayer extends Memory {

    private _player: SentientActor;

    constructor(player) {
        super();
        this._lastAttackedID = null;
        this._player = player;
    }

    /* Sets the last attacked actor. */
    public setLastAttacked(actor: BaseActor | number): void {
        if (Number.isInteger(actor as number)) {
            this._lastAttackedID = actor as number;
        }
        else if (actor) {
            this._lastAttackedID = (actor as BaseActor).getID();
        }
    }

    public getLastAttacked(): number {
        return this._lastAttackedID;
    }

    /* Returns true if the actor was the last attacked one. */
    public wasLastAttacked(actor: SentientActor): boolean {
        return this._lastAttackedID === actor.getID();
    }

    /* Returns true if the given actor is enemy of player. */
    public isEnemy(actor: BaseActor): boolean {
        if (actor.isPlayer()) {
            return false; // Needed for MindControl
        }
        if (actor.has('NonSentient')) {return false;}
        return actor.getBrain().getMemory().isEnemy(this._player);
    }

    public toJSON() {
        const json: any = {};
        if (!RG.isNullOrUndef([this._lastAttackedID])) {
            json.setLastAttacked = this._lastAttackedID;
        }
        return json;
    }

}


const S_IDLE = 'S_IDLE';
const S_TARGETING = 'S_TARGETING';
const S_LOOKING = 'S_LOOKING';

const FSM_NO_MATCH = 256;

/* A class to manage the targeting/looking state of the player. */
class TargetingFSM {

    public _brain: BrainPlayer;
    public _targetList: Cell[];
    public targetIndex: number;
    public _state: string;
    public selectedCells: Cell[] | null;

    constructor(brain) {
        this._brain = brain;
        this._targetList = [];
        this.targetIndex = -1;
        this._state = S_IDLE;
    }

    public getActor(): SentientActor {
        return this._brain._actor;
    }

    public isTargeting(): boolean {
        return this._state === S_TARGETING;
    }

    public isLooking(): boolean {
        return this._state === S_LOOKING;
    }

    public nextTarget(): void {
        if (this.hasTargets()) {
            ++this.targetIndex;
            if (this.targetIndex >= this._targetList.length) {
                this.targetIndex = 0;
            }
            this.setSelectedCells(this._targetList[this.targetIndex]);
        }
    }

    public startLooking(): void {
        this._state = S_LOOKING;
    }

    public stopLooking(): void {
        this._state = S_IDLE;
        this.selectedCells = null;
    }

    public startTargeting(): void {
        this._state = S_TARGETING;
        this._targetList = this.getTargetList();
        this.targetIndex = this.getCellIndexToTarget(this._targetList);
        this.setSelectedCells(this._targetList[this.targetIndex]);
    }

    public cancelTargeting(): void {
        this._targetList = [];
        this._state = S_IDLE;
        this.selectedCells = null;
        this.targetIndex = -1;
    }

    public getTargetList(): Cell[] {
        const mapXY = {};
        const visibleCells = this._brain.getSeenCells();
        const actor = this._brain._actor;
        const enemyCells = RG.findEnemyCellForActor(
            actor, visibleCells);
        enemyCells.forEach(cell => {
            mapXY[cell.getX() + ',' + cell.getY()] = cell;
        });
        return Object.values(mapXY);
    }

    public prevTarget(): void {
        if (this.hasTargets()) {
            --this.targetIndex;
            if (this.targetIndex < 0) {
                this.targetIndex = this._targetList.length - 1;
            }
            this.setSelectedCells(this._targetList[this.targetIndex]);
        }
    }

    public setSelectedCells(cells: Cell | Cell[]): void {
        if (cells) {
            if (!Array.isArray(cells)) {
                const cell = cells as Cell;
                this.selectedCells = [cell];
                if (this.isTargeting()) {
                    const actor = this.getActor();
                    const [tx, ty] = [cell.getX(), cell.getY()];
                    const [ax, ay] = [actor.getX(), actor.getY()];
                    const path = Geometry.getBresenham(ax, ay, tx, ty);
                    const pathCells = path.map(xy => (
                        actor.getLevel().getMap().getCell(xy[0], xy[1])
                    ));
                    this.selectedCells = this.selectedCells.concat(pathCells);
                }
            }
            else {
                this.selectedCells = cells as Cell[];<commit_msg>Added NO_SELECTED_CELLS. Moved prevTarget() after nextTarget().<commit_after>
import RG from '../rg';
import {Menu, SelectionObject} from '../menu';
import {Keys} from '../keymap';
import {Cell} from '../map.cell';
import * as GoalsBattle from '../goals-battle';
import * as Cmd from '../cmd-player';
import * as Component from '../component/component';
import {BaseActor, SentientActor} from '../actor';
import {Random} from '../random';
import {Geometry} from '../geometry';

import {BrainBase} from './brain.base';
import {Brain} from './brain';
import {Memory} from './brain.memory';

import {PlayerCmdInput} from '../interfaces';

type BrainGoalOriented = import('./brain.goaloriented').BrainGoalOriented;
type ItemBase = import('../item').ItemBase;

const RNG = Random.getRNG();
const KeyMap = Keys.KeyMap;

const {
    ACTION_ALREADY_DONE,
    ACTION_ZERO_ENERGY } = Cmd;

const selectTargetMsg =
    'Select a target (all with "A"), then press "s" to choose it';
const lookCellMsg =
    'Select a target with movement keys, then press "s" to choose it';

const chatSelObject = player => {
    const msg = 'Select direction for chatting:';
    RG.gameMsg(msg);
    return {
        select: code => {
            const args = {
                dir: KeyMap.getDir(code),
                src: null
            };
            if (args.dir) {
                args.src = player;
                return () => {
                    const chatComp = new Component.Chat();
                    chatComp.setArgs(args);
                    player.add(chatComp);
                };
            }
            return null;
        },
        showMenu: () => false
    };
};


/* Memory object for the player .*/
export class MemoryPlayer extends Memory {

    private _player: SentientActor;

    constructor(player) {
        super();
        this._lastAttackedID = null;
        this._player = player;
    }

    /* Sets the last attacked actor. */
    public setLastAttacked(actor: BaseActor | number): void {
        if (Number.isInteger(actor as number)) {
            this._lastAttackedID = actor as number;
        }
        else if (actor) {
            this._lastAttackedID = (actor as BaseActor).getID();
        }
    }

    public getLastAttacked(): number {
        return this._lastAttackedID;
    }

    /* Returns true if the actor was the last attacked one. */
    public wasLastAttacked(actor: SentientActor): boolean {
        return this._lastAttackedID === actor.getID();
    }

    /* Returns true if the given actor is enemy of player. */
    public isEnemy(actor: BaseActor): boolean {
        if (actor.isPlayer()) {
            return false; // Needed for MindControl
        }
        if (actor.has('NonSentient')) {return false;}
        return actor.getBrain().getMemory().isEnemy(this._player);
    }

    public toJSON() {
        const json: any = {};
        if (!RG.isNullOrUndef([this._lastAttackedID])) {
            json.setLastAttacked = this._lastAttackedID;
        }
        return json;
    }

}


const S_IDLE = 'S_IDLE';
const S_TARGETING = 'S_TARGETING';
const S_LOOKING = 'S_LOOKING';

const FSM_NO_MATCH = 256;
const NO_SELECTED_CELLS = null;

/* A class to manage the targeting/looking state of the player. */
class TargetingFSM {

    public _brain: BrainPlayer;
    public _targetList: Cell[];
    public targetIndex: number;
    public _state: string;
    public selectedCells: Cell[] | null;

    constructor(brain) {
        this._brain = brain;
        this._targetList = [];
        this.targetIndex = -1;
        this._state = S_IDLE;
    }

    public getActor(): SentientActor {
        return this._brain._actor;
    }

    public isTargeting(): boolean {
        return this._state === S_TARGETING;
    }

    public isLooking(): boolean {
        return this._state === S_LOOKING;
    }

    public nextTarget(): void {
        if (this.hasTargets()) {
            ++this.targetIndex;
            if (this.targetIndex >= this._targetList.length) {
                this.targetIndex = 0;
            }
            this.setSelectedCells(this._targetList[this.targetIndex]);
        }
    }

    public prevTarget(): void {
        if (this.hasTargets()) {
            --this.targetIndex;
            if (this.targetIndex < 0) {
                this.targetIndex = this._targetList.length - 1;
            }
            this.setSelectedCells(this._targetList[this.targetIndex]);
        }
    }

    public startLooking(): void {
        this._state = S_LOOKING;
    }

    public stopLooking(): void {
        this._state = S_IDLE;
        this.selectedCells = NO_SELECTED_CELLS;
    }

    public startTargeting(): void {
        this._state = S_TARGETING;
        this._targetList = this.getTargetList();
        this.targetIndex = this.getCellIndexToTarget(this._targetList);
        this.setSelectedCells(this._targetList[this.targetIndex]);
    }

    public cancelTargeting(): void {
        this._targetList = [];
        this._state = S_IDLE;
        this.selectedCells = NO_SELECTED_CELLS;
        this.targetIndex = -1;
    }

    public getTargetList(): Cell[] {
        const mapXY = {};
        const visibleCells = this._brain.getSeenCells();
        const actor = this._brain._actor;
        const enemyCells = RG.findEnemyCellForActor(
            actor, visibleCells);
        enemyCells.forEach(cell => {
            mapXY[cell.getX() + ',' + cell.getY()] = cell;
        });
        return Object.values(mapXY);
    }


    public setSelectedCells(cells: Cell | Cell[]): void {
        if (cells) {
            if (!Array.isArray(cells)) {
                const cell = cells as Cell;
                this.selectedCells = [cell];
                if (this.isTargeting()) {
                    const actor = this.getActor();
                    const [tx, ty] = [cell.getX(), cell.getY()];
                    const [ax, ay] = [actor.getX(), actor.getY()];
                    const path = Geometry.getBresenham(ax, ay, tx, ty);
                    const pathCells = path.map(xy => (
                        actor.getLevel().getMap().getCell(xy[0], xy[1])
                    ));
                    this.selectedCells = this.selectedCells.concat(pathCells);
                }
            }
            else {
                this.selectedCells = cells as Cell[];
{'fraction_non_alphanumeric': 0.12339577520924672, 'fraction_numerical': 0.0015942606616181746, 'mean_word_length': 1.9807555238774055, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5194287', 'n_tokens_mistral': 3735, 'n_tokens_neox': 3541, 'n_words': 881}