"use strict";
// This emulates the vanilla versus screen that shows up when you enter a boss room.
Object.defineProperty(exports, "__esModule", { value: true });
exports.versusScreenPostRender = exports.playVersusScreenAnimation = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedClasses_1 = require("../../../../core/cachedClasses");
const array_1 = require("../../../../functions/array");
const bosses_1 = require("../../../../functions/bosses");
const roomData_1 = require("../../../../functions/roomData");
const string_1 = require("../../../../functions/string");
const ui_1 = require("../../../../functions/ui");
const utils_1 = require("../../../../functions/utils");
const versusScreen_1 = require("../../../../functions/versusScreen");
const versusScreenBackgroundColors_1 = require("../../../../objects/versusScreenBackgroundColors");
const versusScreenDirtSpotColors_1 = require("../../../../objects/versusScreenDirtSpotColors");
const constants_1 = require("./constants");
const v_1 = require("./v");
const DEFAULT_STAGE_ID = isaac_typescript_definitions_1.StageID.BASEMENT;
const VERSUS_SCREEN_ANIMATION_NAME = "Scene";
/** The layers range from 0 to 13. */
const NUM_VERSUS_SCREEN_ANM2_LAYERS = 14;
/** Corresponds to "resources/gfx/ui/boss/versusscreen.anm2". */
var VersusScreenLayer;
(function (VersusScreenLayer) {
    VersusScreenLayer[VersusScreenLayer["BACKGROUND"] = 0] = "BACKGROUND";
    VersusScreenLayer[VersusScreenLayer["FRAME"] = 1] = "FRAME";
    /** The boss dirt spot. */
    VersusScreenLayer[VersusScreenLayer["BOSS_SPOT"] = 2] = "BOSS_SPOT";
    /** The player dirt spot. */
    VersusScreenLayer[VersusScreenLayer["PLAYER_SPOT"] = 3] = "PLAYER_SPOT";
    VersusScreenLayer[VersusScreenLayer["BOSS_PORTRAIT"] = 4] = "BOSS_PORTRAIT";
    VersusScreenLayer[VersusScreenLayer["PLAYER_PORTRAIT"] = 5] = "PLAYER_PORTRAIT";
    VersusScreenLayer[VersusScreenLayer["PLAYER_NAME"] = 6] = "PLAYER_NAME";
    VersusScreenLayer[VersusScreenLayer["BOSS_NAME"] = 7] = "BOSS_NAME";
    VersusScreenLayer[VersusScreenLayer["VS_TEXT"] = 8] = "VS_TEXT";
    VersusScreenLayer[VersusScreenLayer["BOSS_DOUBLE"] = 9] = "BOSS_DOUBLE";
    VersusScreenLayer[VersusScreenLayer["DT_TEXT"] = 10] = "DT_TEXT";
    VersusScreenLayer[VersusScreenLayer["OVERLAY"] = 11] = "OVERLAY";
    /**
     * We only need to render either the normal player portrait layer or the alternate player portrait
     * layer. Rendering both will cause the player not to shake.
     */
    VersusScreenLayer[VersusScreenLayer["PLAYER_PORTRAIT_ALT"] = 12] = "PLAYER_PORTRAIT_ALT";
    VersusScreenLayer[VersusScreenLayer["BOSS_PORTRAIT_GROUND"] = 13] = "BOSS_PORTRAIT_GROUND";
    VersusScreenLayer[VersusScreenLayer["BOSS_PORTRAIT_2_GROUND"] = 14] = "BOSS_PORTRAIT_2_GROUND";
})(VersusScreenLayer || (VersusScreenLayer = {}));
/** These are the non-special layers that we will render last. */
const OTHER_ANM2_LAYERS = (0, array_1.arrayRemove)((0, utils_1.eRange)(NUM_VERSUS_SCREEN_ANM2_LAYERS), VersusScreenLayer.BACKGROUND, VersusScreenLayer.BOSS_SPOT, VersusScreenLayer.PLAYER_SPOT, VersusScreenLayer.OVERLAY, VersusScreenLayer.PLAYER_PORTRAIT_ALT);
const VANILLA_VERSUS_PLAYBACK_SPEED = 0.5;
/** We lazy load the sprite when first needed. */
const versusScreenSprite = Sprite();
/**
 * We lazy load the sprite when first needed.
 *
 * Unfortunately, we must split the background layer into an entirely different sprite so that we
 * can color it with the `Color` field.
 */
const versusScreenBackgroundSprite = Sprite();
/**
 * We lazy load the sprite when first needed.
 *
 * Unfortunately, we must split the dirt layer into an entirely different sprite so that we can
 * color it with the `Color` field.
 */
const versusScreenDirtSpotSprite = Sprite();
function playVersusScreenAnimation(customStage, disableAllSound, pause, runInNFrames) {
    const room = cachedClasses_1.game.GetRoom();
    const roomType = room.GetType();
    const roomCleared = room.IsClear();
    const hud = cachedClasses_1.game.GetHUD();
    if (roomType !== isaac_typescript_definitions_1.RoomType.BOSS) {
        return;
    }
    if (roomCleared) {
        return;
    }
    if (willVanillaVersusScreenPlay()) {
        // Since we are on an invalid stage, the versus screen will have a completely black background.
        // Revert to using the background from the default stage.
        const level = cachedClasses_1.game.GetLevel();
        level.SetStage(constants_1.DEFAULT_BASE_STAGE, constants_1.DEFAULT_BASE_STAGE_TYPE);
        runInNFrames.runNextGameFrame(() => {
            const futureLevel = cachedClasses_1.game.GetLevel();
            futureLevel.SetStage(constants_1.CUSTOM_FLOOR_STAGE, constants_1.CUSTOM_FLOOR_STAGE_TYPE);
        });
        return;
    }
    v_1.v.run.showingBossVersusScreen = true;
    pause.pause();
    hud.SetVisible(false);
    disableAllSound.disableAllSound(constants_1.CUSTOM_STAGE_FEATURE_NAME);
    // In vanilla, the "overlay.png" file has a white background. We must convert it to a PNG that
    // uses a transparent background in order for the background behind it to be visible. We use the
    // same "overlay.png" file as StageAPI uses for this purpose.
    if (!versusScreenSprite.IsLoaded()) {
        versusScreenSprite.Load("gfx/ui/boss/versusscreen.anm2", false);
        versusScreenSprite.ReplaceSpritesheet(VersusScreenLayer.OVERLAY, `${constants_1.ISAACSCRIPT_CUSTOM_STAGE_GFX_PATH}/overlay.png`);
    }
    // Player
    {
        const { namePNGPath, portraitPNGPath } = getPlayerPNGPaths();
        versusScreenSprite.ReplaceSpritesheet(VersusScreenLayer.PLAYER_NAME, namePNGPath);
        versusScreenSprite.ReplaceSpritesheet(VersusScreenLayer.PLAYER_PORTRAIT, portraitPNGPath);
    }
    // Boss
    {
        const { namePNGPath, portraitPNGPath } = getBossPNGPaths(customStage);
        const trimmedNamePNGPath = (0, string_1.removeCharactersBefore)(namePNGPath, "gfx/");
        versusScreenSprite.ReplaceSpritesheet(VersusScreenLayer.BOSS_NAME, trimmedNamePNGPath);
        const trimmedPortraitPNGPath = (0, string_1.removeCharactersBefore)(portraitPNGPath, "gfx/");
        versusScreenSprite.ReplaceSpritesheet(VersusScreenLayer.BOSS_PORTRAIT, trimmedPortraitPNGPath);
    }
    versusScreenSprite.LoadGraphics();
    if (!versusScreenBackgroundSprite.IsLoaded()) {
        versusScreenBackgroundSprite.Load("gfx/ui/boss/versusscreen.anm2", true);
    }
    let backgroundColor = versusScreenBackgroundColors_1.VERSUS_SCREEN_BACKGROUND_COLORS[DEFAULT_STAGE_ID];
    if (customStage.versusScreen?.backgroundColor !== undefined) {
        const { r, g, b, a } = customStage.versusScreen.backgroundColor;
        backgroundColor = Color(r, g, b, a);
    }
    versusScreenBackgroundSprite.Color = backgroundColor;
    if (!versusScreenDirtSpotSprite.IsLoaded()) {
        versusScreenDirtSpotSprite.Load("gfx/ui/boss/versusscreen.anm2", true);
    }
    let dirtSpotColor = versusScreenDirtSpotColors_1.VERSUS_SCREEN_DIRT_SPOT_COLORS[DEFAULT_STAGE_ID];
    if (customStage.versusScreen?.dirtSpotColor !== undefined) {
        const { r, g, b } = customStage.versusScreen.dirtSpotColor;
        dirtSpotColor = Color(r, g, b);
    }
    versusScreenDirtSpotSprite.Color = dirtSpotColor;
    for (const sprite of [
        versusScreenBackgroundSprite,
        versusScreenDirtSpotSprite,
        versusScreenSprite,
    ]) {
        sprite.Play(VERSUS_SCREEN_ANIMATION_NAME, true);
        sprite.PlaybackSpeed = VANILLA_VERSUS_PLAYBACK_SPEED;
    }
}
exports.playVersusScreenAnimation = playVersusScreenAnimation;
function willVanillaVersusScreenPlay() {
    const bosses = (0, bosses_1.getBosses)();
    return bosses.some((boss) => boss.GetBossID() !== 0);
}
/** Use the character of the 0th player. */
function getPlayerPNGPaths() {
    const player = Isaac.GetPlayer();
    const character = player.GetPlayerType();
    if (character === isaac_typescript_definitions_1.PlayerType.POSSESSOR) {
        error("Failed to get the player PNG paths since they are a possessor.");
    }
    const namePNGPath = (0, versusScreen_1.getCharacterNamePNGFilePath)(character);
    const portraitPNGPath = (0, versusScreen_1.getCharacterPortraitPNGFilePath)(character);
    return { namePNGPath, portraitPNGPath };
}
/** Use the boss of the first boss found. */
function getBossPNGPaths(customStage) {
    // Prefer the PNG paths specified by the end-user, if any.
    const paths = getBossPNGPathsCustom(customStage);
    if (paths !== undefined) {
        return paths;
    }
    // If this is not a vanilla boss, default to showing question marks.
    const bosses = (0, bosses_1.getBosses)();
    const firstBoss = bosses[0];
    const bossID = firstBoss === undefined ? 0 : firstBoss.GetBossID();
    if (bossID === 0) {
        const questionMarkPath = (0, versusScreen_1.getBossNamePNGFilePath)(isaac_typescript_definitions_1.BossID.BLUE_BABY);
        const namePNGPath = questionMarkPath;
        const portraitPNGPath = questionMarkPath;
        return { namePNGPath, portraitPNGPath };
    }
    // If this is a vanilla boss, it will have a boss ID, and we can use the corresponding vanilla
    // files.
    const namePNGPath = (0, versusScreen_1.getBossNamePNGFilePath)(bossID);
    const portraitPNGPath = (0, versusScreen_1.getBossPortraitPNGFilePath)(bossID);
    return { namePNGPath, portraitPNGPath };
}
function getBossPNGPathsCustom(customStage) {
    if (customStage.bossPool === undefined) {
        return undefined;
    }
    const roomSubType = (0, roomData_1.getRoomSubType)();
    const matchingBossEntry = customStage.bossPool.find((bossEntry) => bossEntry.subType === roomSubType);
    if (matchingBossEntry === undefined) {
        return undefined;
    }
    return matchingBossEntry.versusScreen;
}
function finishVersusScreenAnimation(pause, disableAllSound) {
    const hud = cachedClasses_1.game.GetHUD();
    v_1.v.run.showingBossVersusScreen = false;
    pause.unpause();
    hud.SetVisible(true);
    disableAllSound.enableAllSound(constants_1.CUSTOM_STAGE_FEATURE_NAME);
    // The sound effect only plays once the versus cutscene is over.
    cachedClasses_1.sfxManager.Play(isaac_typescript_definitions_1.SoundEffect.CASTLE_PORTCULLIS);
}
// ModCallback.POST_RENDER (2)
function versusScreenPostRender(pause, disableAllSound) {
    if (!v_1.v.run.showingBossVersusScreen) {
        return;
    }
    // We do not want to early return when the game is paused because we need to start displaying the
    // black screen as soon as the slide animation starts.
    if (versusScreenSprite.IsFinished(VERSUS_SCREEN_ANIMATION_NAME)) {
        finishVersusScreenAnimation(pause, disableAllSound);
        return;
    }
    const position = (0, ui_1.getScreenCenterPos)();
    // First, we render the background.
    versusScreenBackgroundSprite.RenderLayer(VersusScreenLayer.BACKGROUND, position);
    versusScreenBackgroundSprite.Update();
    // Second, we render the overlay.
    versusScreenSprite.RenderLayer(VersusScreenLayer.OVERLAY, position);
    // Third, we render the dirt.
    versusScreenDirtSpotSprite.RenderLayer(VersusScreenLayer.BOSS_SPOT, position);
    versusScreenDirtSpotSprite.RenderLayer(VersusScreenLayer.PLAYER_SPOT, position);
    versusScreenDirtSpotSprite.Update();
    // Lastly, we render everything else.
    for (const layerID of OTHER_ANM2_LAYERS) {
        versusScreenSprite.RenderLayer(layerID, position);
    }
    versusScreenSprite.Update();
}
exports.versusScreenPostRender = versusScreenPostRender;
