"use strict";
// This file handles drawing the walls and floors for custom stages.
Object.defineProperty(exports, "__esModule", { value: true });
exports.setCustomStageBackdrop = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../../../../core/cachedClasses");
const constants_1 = require("../../../../core/constants");
const LadderSubTypeCustom_1 = require("../../../../enums/LadderSubTypeCustom");
const array_1 = require("../../../../functions/array");
const entitiesSpecific_1 = require("../../../../functions/entitiesSpecific");
const rng_1 = require("../../../../functions/rng");
const roomShape_1 = require("../../../../functions/roomShape");
const string_1 = require("../../../../functions/string");
const utils_1 = require("../../../../functions/utils");
const ReadonlySet_1 = require("../../../../types/ReadonlySet");
const constants_2 = require("./constants");
var BackdropKind;
(function (BackdropKind) {
    /** The "N" stands for narrow rooms. */
    BackdropKind["N_FLOOR"] = "nFloors";
    /** The "L" stands for L rooms. */
    BackdropKind["L_FLOOR"] = "lFloors";
    BackdropKind["WALL"] = "walls";
    BackdropKind["CORNER"] = "corners";
})(BackdropKind || (BackdropKind = {}));
/** This is created by the vanilla Basement files. */
const DEFAULT_BACKDROP = {
    nFloors: [`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/backdrop/nfloor.png`],
    lFloors: [`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/backdrop/lfloor.png`],
    walls: [`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/backdrop/wall.png`],
    corners: [`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/backdrop/corner.png`],
};
const ROOM_SHAPE_WALL_ANM2_LAYERS = {
    [isaac_typescript_definitions_1.RoomShape.SHAPE_1x1]: 44,
    [isaac_typescript_definitions_1.RoomShape.IH]: 36,
    [isaac_typescript_definitions_1.RoomShape.IV]: 28,
    [isaac_typescript_definitions_1.RoomShape.SHAPE_1x2]: 58,
    [isaac_typescript_definitions_1.RoomShape.IIV]: 42,
    [isaac_typescript_definitions_1.RoomShape.SHAPE_2x1]: 63,
    [isaac_typescript_definitions_1.RoomShape.IIH]: 62,
    [isaac_typescript_definitions_1.RoomShape.SHAPE_2x2]: 63,
    [isaac_typescript_definitions_1.RoomShape.LTL]: 63,
    [isaac_typescript_definitions_1.RoomShape.LTR]: 63,
    [isaac_typescript_definitions_1.RoomShape.LBL]: 63,
    [isaac_typescript_definitions_1.RoomShape.LBR]: 63, // 12
};
// We don't use `as const` since we need the object to be indexable by all `RoomShape`.
// eslint-disable-next-line isaacscript/require-capital-const-assertions
const ROOM_SHAPE_WALL_EXTRA_ANM2_LAYERS = {
    [isaac_typescript_definitions_1.RoomShape.SHAPE_2x1]: 7,
    [isaac_typescript_definitions_1.RoomShape.SHAPE_2x2]: 21,
    [isaac_typescript_definitions_1.RoomShape.LTL]: 19,
    [isaac_typescript_definitions_1.RoomShape.LTR]: 19,
    [isaac_typescript_definitions_1.RoomShape.LBL]: 19,
    [isaac_typescript_definitions_1.RoomShape.LBR]: 19, // 12
};
const WALL_OFFSET = Vector(-80, -80);
/** Corresponds to "floor-backdrop.anm2". */
const L_FLOOR_ANM2_LAYERS = [16, 17];
/** Corresponds to "floor-backdrop.anm2". */
const N_FLOOR_ANM2_LAYERS = [18, 19];
/**
 * Normally, we would make a custom entity to represent a backdrop effect, but we don't want to
 * interfere with the "entities2.xml" file in end-user mods. Thus, we must select a vanilla effect
 * to masquerade as a backdrop effect.
 *
 * We arbitrarily choose a ladder for this purpose because it will not automatically despawn after
 * time passes, like most other effects.
 */
const BACKDROP_EFFECT_VARIANT = isaac_typescript_definitions_1.EffectVariant.LADDER;
const BACKDROP_EFFECT_SUB_TYPE = LadderSubTypeCustom_1.LadderSubTypeCustom.CUSTOM_BACKDROP;
const BACKDROP_ROOM_TYPE_SET = new ReadonlySet_1.ReadonlySet([
    isaac_typescript_definitions_1.RoomType.DEFAULT,
    isaac_typescript_definitions_1.RoomType.BOSS,
    isaac_typescript_definitions_1.RoomType.MINI_BOSS,
]);
function setCustomStageBackdrop(customStage) {
    const room = cachedClasses_1.game.GetRoom();
    const roomType = room.GetType();
    const decorationSeed = room.GetDecorationSeed();
    const rng = (0, rng_1.newRNG)(decorationSeed);
    // We do not want to set the backdrop inside shops, Curse Rooms, and so on.
    if (!BACKDROP_ROOM_TYPE_SET.has(roomType)) {
        return;
    }
    spawnWallEntity(customStage, rng, false);
    spawnSecondWallEntity(customStage, rng);
    spawnFloorEntity(customStage, rng);
}
exports.setCustomStageBackdrop = setCustomStageBackdrop;
function getBackdropPNGPath(customStage, backdropKind, rng) {
    const backdrop = customStage.backdropPNGPaths ?? DEFAULT_BACKDROP;
    const pathArray = backdrop[backdropKind];
    const randomPath = (0, array_1.getRandomArrayElement)(pathArray, rng);
    return (0, string_1.removeCharactersBefore)(randomPath, "gfx/");
}
function spawnWallEntity(customStage, rng, isExtraWall) {
    const room = cachedClasses_1.game.GetRoom();
    const roomShape = room.GetRoomShape();
    // We spawn an effect instead of simply rendering a static sprite in order to emulate how vanilla
    // does this. (`EntityFlag.RENDER_WALL` is intended for this purpose.)
    const seed = 1;
    const wallEffect = (0, entitiesSpecific_1.spawnEffectWithSeed)(BACKDROP_EFFECT_VARIANT, BACKDROP_EFFECT_SUB_TYPE, constants_1.VectorZero, seed);
    wallEffect.AddEntityFlags(isaac_typescript_definitions_1.EntityFlag.RENDER_WALL);
    const sprite = wallEffect.GetSprite();
    sprite.Load(`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/wall-backdrop.anm2`, false);
    const wallLayersArray = isExtraWall
        ? ROOM_SHAPE_WALL_EXTRA_ANM2_LAYERS
        : ROOM_SHAPE_WALL_ANM2_LAYERS;
    const numWallLayers = wallLayersArray[roomShape];
    (0, utils_1.assertDefined)(numWallLayers, `Failed to get the layers when creating the backdrop for custom stage: ${customStage.name}`);
    if ((0, roomShape_1.isLRoomShape)(roomShape)) {
        const cornerPNGPath = getBackdropPNGPath(customStage, BackdropKind.CORNER, rng);
        sprite.ReplaceSpritesheet(0, cornerPNGPath);
    }
    for (const layerID of (0, utils_1.iRange)(1, numWallLayers)) {
        const wallPNGPath = getBackdropPNGPath(customStage, BackdropKind.WALL, rng);
        sprite.ReplaceSpritesheet(layerID, wallPNGPath);
    }
    const topLeftPos = room.GetTopLeftPos();
    const renderPos = topLeftPos.add(WALL_OFFSET);
    const modifiedOffset = renderPos.div(40).mul(26);
    wallEffect.SpriteOffset = modifiedOffset;
    sprite.LoadGraphics();
    const roomShapeName = isaac_typescript_definitions_1.RoomShape[roomShape];
    const animation = (0, string_1.trimPrefix)(roomShapeName, "SHAPE_");
    const modifiedAnimation = isExtraWall ? `${animation}X` : animation;
    sprite.Play(modifiedAnimation, true);
}
function spawnSecondWallEntity(customStage, rng) {
    const room = cachedClasses_1.game.GetRoom();
    const roomShape = room.GetRoomShape();
    const extraLayers = ROOM_SHAPE_WALL_EXTRA_ANM2_LAYERS[roomShape];
    const roomShapeHasExtraLayers = extraLayers !== undefined;
    if (roomShapeHasExtraLayers) {
        spawnWallEntity(customStage, rng, true);
    }
}
function spawnFloorEntity(customStage, rng) {
    const room = cachedClasses_1.game.GetRoom();
    const roomShape = room.GetRoomShape();
    // We spawn an effect instead of simply rendering a static sprite in order to emulate how vanilla
    // does this. (`EntityFlag.RENDER_FLOOR` is intended for this purpose.)
    const seed = 1;
    const floorEffect = (0, entitiesSpecific_1.spawnEffectWithSeed)(BACKDROP_EFFECT_VARIANT, 0, constants_1.VectorZero, seed);
    floorEffect.AddEntityFlags(isaac_typescript_definitions_1.EntityFlag.RENDER_FLOOR);
    const sprite = floorEffect.GetSprite();
    sprite.Load(`${constants_2.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/floor-backdrop.anm2`, false);
    const numFloorLayers = getNumFloorLayers(roomShape);
    if (numFloorLayers !== undefined) {
        for (const layerID of (0, utils_1.eRange)(numFloorLayers)) {
            // The wall spritesheet is used for the "normal" floors.
            const wallPNGPath = getBackdropPNGPath(customStage, BackdropKind.WALL, rng);
            sprite.ReplaceSpritesheet(layerID, wallPNGPath);
        }
    }
    else if ((0, roomShape_1.isLRoomShape)(roomShape)) {
        for (const layerID of L_FLOOR_ANM2_LAYERS) {
            const LFloorPNGPath = getBackdropPNGPath(customStage, BackdropKind.L_FLOOR, rng);
            sprite.ReplaceSpritesheet(layerID, LFloorPNGPath);
        }
    }
    else if ((0, roomShape_1.isNarrowRoom)(roomShape)) {
        for (const layerID of N_FLOOR_ANM2_LAYERS) {
            const NFloorPNGPath = getBackdropPNGPath(customStage, BackdropKind.N_FLOOR, rng);
            sprite.ReplaceSpritesheet(layerID, NFloorPNGPath);
        }
    }
    const topLeftPos = room.GetTopLeftPos();
    const renderPos = topLeftPos;
    const modifiedOffset = renderPos.div(40).mul(26); // The magic numbers are copied from StageAPI.
    floorEffect.SpriteOffset = modifiedOffset;
    sprite.LoadGraphics();
    const roomShapeName = isaac_typescript_definitions_1.RoomShape[roomShape];
    const animation = (0, string_1.trimPrefix)(roomShapeName, "SHAPE_");
    sprite.Play(animation, true);
}
function getNumFloorLayers(roomShape) {
    switch (roomShape) {
        case isaac_typescript_definitions_1.RoomShape.SHAPE_1x1: {
            return 4;
        }
        case isaac_typescript_definitions_1.RoomShape.SHAPE_1x2:
        case isaac_typescript_definitions_1.RoomShape.SHAPE_2x1: {
            return 8;
        }
        case isaac_typescript_definitions_1.RoomShape.SHAPE_2x2: {
            return 16;
        }
        default: {
            // We have explicit logic elsewhere to handle narrow rooms and L rooms.
            return undefined;
        }
    }
}
