"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostItemDischarge = void 0;
const isaac_typescript_definitions_1 = require("isaac-typescript-definitions");
const cachedEnumValues_1 = require("../../cachedEnumValues");
const cachedClasses_1 = require("../../core/cachedClasses");
const ModCallbackCustom_1 = require("../../enums/ModCallbackCustom");
const charge_1 = require("../../functions/charge");
const playerDataStructures_1 = require("../../functions/playerDataStructures");
const DefaultMap_1 = require("../DefaultMap");
const CustomCallback_1 = require("../private/CustomCallback");
const v = {
    run: {
        playersActiveItemMap: new DefaultMap_1.DefaultMap(() => new Map()),
        playersActiveChargeMap: new DefaultMap_1.DefaultMap(() => new Map()),
    },
    room: {
        playersBulbLastCollisionFrame: new Map(),
    },
};
class PostItemDischarge extends CustomCallback_1.CustomCallback {
    v = v;
    constructor() {
        super();
        this.callbacksUsed = [
            // 30
            [
                isaac_typescript_definitions_1.ModCallback.PRE_NPC_COLLISION,
                this.preNPCCollisionSucker,
                [isaac_typescript_definitions_1.EntityType.SUCKER],
            ],
        ];
        this.customCallbacksUsed = [
            [
                ModCallbackCustom_1.ModCallbackCustom.POST_PEFFECT_UPDATE_REORDERED,
                this.postPEffectUpdateReordered,
            ],
        ];
    }
    shouldFire = (fireArgs, optionalArgs) => {
        const [_player, collectibleType] = fireArgs;
        const [callbackCollectibleType] = optionalArgs;
        return (callbackCollectibleType === undefined ||
            callbackCollectibleType === collectibleType);
    };
    // ModCallback.PRE_NPC_COLLISION (30)
    // EntityType.SUCKER (61)
    preNPCCollisionSucker = (npc, collider) => {
        if (npc.Variant === isaac_typescript_definitions_1.SuckerVariant.BULB) {
            return this.preNPCCollisionBulb(npc, collider);
        }
        return undefined;
    };
    // ModCallback.PRE_NPC_COLLISION (30)
    // EntityType.SUCKER (61)
    preNPCCollisionBulb(_npc, collider) {
        this.checkPlayerCollidedWithBulb(collider);
        return undefined;
    }
    /**
     * The algorithm for detecting a discharge is checking if the current charge is less than the
     * charge on the previous frame. Thus, when a Bulb zaps a player and drains their charge, this
     * will be a false position, so Bulbs have to be handled.
     *
     * When Bulbs zap a player, they go to `NPCState.STATE_JUMP` for a frame. However, this only
     * happens on the frame after the player is discharged, which is too late to be of any use.
     *
     * Instead, we track the frames that Bulbs collide with players and assume that a collision means
     * a zap has occurred.
     */
    checkPlayerCollidedWithBulb(collider) {
        const player = collider.ToPlayer();
        if (player === undefined) {
            return;
        }
        const gameFrameCount = cachedClasses_1.game.GetFrameCount();
        (0, playerDataStructures_1.mapSetPlayer)(v.room.playersBulbLastCollisionFrame, player, gameFrameCount);
    }
    // ModCallbackCustom.POST_PEFFECT_UPDATE_REORDERED
    postPEffectUpdateReordered = (player) => {
        const activeItemMap = (0, playerDataStructures_1.defaultMapGetPlayer)(v.run.playersActiveItemMap, player);
        const chargeMap = (0, playerDataStructures_1.defaultMapGetPlayer)(v.run.playersActiveChargeMap, player);
        for (const activeSlot of cachedEnumValues_1.ACTIVE_SLOT_VALUES) {
            const currentActiveItem = player.GetActiveItem();
            let previousActiveItem = activeItemMap.get(activeSlot);
            if (previousActiveItem === undefined) {
                previousActiveItem = currentActiveItem;
            }
            activeItemMap.set(activeSlot, currentActiveItem);
            if (currentActiveItem !== previousActiveItem) {
                // The player swapped out their active item for something else, so it should be impossible
                // for the discharge callback to fire on this frame.
                continue;
            }
            const currentCharge = (0, charge_1.getTotalCharge)(player, activeSlot);
            let previousCharge = chargeMap.get(activeSlot);
            if (previousCharge === undefined) {
                previousCharge = currentCharge;
            }
            chargeMap.set(activeSlot, currentCharge);
            if (this.playerRecentlyCollidedWithBulb(player)) {
                continue;
            }
            if (currentCharge < previousCharge) {
                const collectibleType = player.GetActiveItem(activeSlot);
                this.fire(player, collectibleType, activeSlot);
            }
        }
    };
    /**
     * If the player collided with a Bulb on either this frame or the last frame, then assume a zap
     * has occurred. (We do not want to fire the discharge callback if this is the case.)
     */
    playerRecentlyCollidedWithBulb(player) {
        const gameFrameCount = cachedClasses_1.game.GetFrameCount();
        const bulbLastCollisionFrame = (0, playerDataStructures_1.mapGetPlayer)(v.room.playersBulbLastCollisionFrame, player);
        const collidedOnThisFrame = gameFrameCount === bulbLastCollisionFrame;
        const collidedOnLastFrame = gameFrameCount - 1 === bulbLastCollisionFrame;
        return collidedOnThisFrame || collidedOnLastFrame;
    }
}
exports.PostItemDischarge = PostItemDischarge;
