"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setEntityVelocities = exports.setEntityPositions = exports.getEntityVelocities = exports.getEntityPositions = exports.findFreePosition = exports.anyPlayerCloserThan = exports.anyEntityCloserThan = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../core/cachedClasses");
const constants_1 = require("../core/constants");
const entities_1 = require("./entities");
const entitiesSpecific_1 = require("./entitiesSpecific");
const playerIndex_1 = require("./playerIndex");
const players_1 = require("./players");
const MAX_FIND_FREE_POSITION_ATTEMPTS = 100;
function anyEntityCloserThan(entities, position, distance) {
    return entities.some((entity) => position.Distance(entity.Position) <= distance);
}
exports.anyEntityCloserThan = anyEntityCloserThan;
/**
 * Iterates over all players and checks if any player is close enough to the specified position.
 *
 * Note that this function does not consider players with a non-undefined parent, since they are not
 * real players (e.g. the Strawman Keeper).
 */
function anyPlayerCloserThan(position, distance) {
    const players = (0, playerIndex_1.getPlayers)();
    return players.some((player) => player.Position.Distance(position) <= distance);
}
exports.anyPlayerCloserThan = anyPlayerCloserThan;
/**
 * Helper function to get a room position that is not overlapping with a grid entity, a heaven door,
 * or a player. The `Room.FindFreePickupSpawnPosition` method will return locations that overlap
 * with heaven doors and partially overlap with players, if the thing being spawned is bigger than a
 * tile (like a Blood Donation Machine). Use this function instead if you want to account for those
 * specific situations.
 *
 * @param startingPosition The position to start searching from. If this position is not overlapping
 *                         with anything, then it will be returned.
 * @param avoidActiveEntities Optional. Default is false.
 * @param minimumDistance Optional. If specified, will ensure that the randomly generated position
 *                        is equal to or greater than the distance provided.
 */
function findFreePosition(startingPosition, avoidActiveEntities = false, minimumDistance) {
    const room = cachedClasses_1.game.GetRoom();
    const heavenDoors = (0, entitiesSpecific_1.getEffects)(isaac_typescript_definitions_1.EffectVariant.HEAVEN_LIGHT_DOOR, isaac_typescript_definitions_1.HeavenLightDoorSubType.HEAVEN_DOOR);
    for (let initialStep = 0; initialStep < MAX_FIND_FREE_POSITION_ATTEMPTS; initialStep++) {
        const position = room.FindFreePickupSpawnPosition(startingPosition, initialStep, avoidActiveEntities);
        const closePlayer = (0, players_1.getPlayerCloserThan)(position, constants_1.DISTANCE_OF_GRID_TILE);
        if (closePlayer !== undefined) {
            continue;
        }
        const isCloseHeavenDoor = anyEntityCloserThan(heavenDoors, position, constants_1.DISTANCE_OF_GRID_TILE);
        if (isCloseHeavenDoor) {
            continue;
        }
        if (minimumDistance !== undefined) {
            const distance = startingPosition.Distance(position);
            if (distance < minimumDistance) {
                continue;
            }
        }
        return position;
    }
    // We failed to find a free position in N iterations.
    return room.FindFreePickupSpawnPosition(startingPosition);
}
exports.findFreePosition = findFreePosition;
/**
 * Helper function to get a map containing the positions of every entity in the current room.
 *
 * This is useful for rewinding entity positions at a later time. Also see `setEntityPositions`.
 *
 * @param entities Optional. If provided, will only get the positions of the provided entities. Use
 *                 this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
 *                 multiple times.
 */
function getEntityPositions(entities) {
    if (entities === undefined) {
        entities = (0, entities_1.getEntities)();
    }
    const entityPositions = new Map();
    for (const entity of entities) {
        const ptrHash = GetPtrHash(entity);
        entityPositions.set(ptrHash, entity.Position);
    }
    return entityPositions;
}
exports.getEntityPositions = getEntityPositions;
/**
 * Helper function to get a map containing the velocities of every entity in the current room.
 *
 * This is useful for rewinding entity velocities at a later time. Also see `setEntityVelocities`.
 *
 * @param entities Optional. If provided, will only get the velocities of the provided entities. Use
 *                 this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
 *                 multiple times.
 */
function getEntityVelocities(entities) {
    if (entities === undefined) {
        entities = (0, entities_1.getEntities)();
    }
    const entityVelocities = new Map();
    for (const entity of entities) {
        const ptrHash = GetPtrHash(entity);
        entityVelocities.set(ptrHash, entity.Velocity);
    }
    return entityVelocities;
}
exports.getEntityVelocities = getEntityVelocities;
/**
 * Helper function to set the position of every entity in the room based on a map of positions. If
 * an entity is found that does not have matching element in the provided map, then that entity will
 * be skipped.
 *
 * This function is useful for rewinding entity positions at a later time. Also see
 * `getEntityPositions`.
 *
 * @param entityPositions The map providing the positions for every entity.
 * @param entities Optional. If provided, will only set the positions of the provided entities. Use
 *                 this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
 *                 multiple times.
 */
function setEntityPositions(entityPositions, entities) {
    if (entities === undefined) {
        entities = (0, entities_1.getEntities)();
    }
    for (const entity of entities) {
        const ptrHash = GetPtrHash(entity);
        const entityPosition = entityPositions.get(ptrHash);
        if (entityPosition !== undefined) {
            entity.Position = entityPosition;
        }
    }
}
exports.setEntityPositions = setEntityPositions;
/**
 * Helper function to set the velocity of every entity in the room based on a map of velocities. If
 * an entity is found that does not have matching element in the provided map, then that entity will
 * be skipped.
 *
 * This function is useful for rewinding entity velocities at a later time. Also see
 * `getEntityVelocities`.
 *
 * @param entityVelocities The map providing the velocities for every entity.
 * @param entities Optional. If provided, will only set the velocities of the provided entities. Use
 *                 this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
 *                 multiple times.
 */
function setEntityVelocities(entityVelocities, entities) {
    if (entities === undefined) {
        entities = (0, entities_1.getEntities)();
    }
    for (const entity of entities) {
        const ptrHash = GetPtrHash(entity);
        const entityVelocity = entityVelocities.get(ptrHash);
        if (entityVelocity !== undefined) {
            entity.Velocity = entityVelocity;
        }
    }
}
exports.setEntityVelocities = setEntityVelocities;
