UNPKG

isaacscript-common

Version:

Helper functions and features for IsaacScript mods.

222 lines (221 loc) • 9.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.streakTextPostRender = streakTextPostRender; exports.streakTextGetShaderParams = streakTextGetShaderParams; exports.topStreakTextStart = topStreakTextStart; const isaac_typescript_definitions_1 = require("isaac-typescript-definitions"); const cachedEnumValues_1 = require("../../../../cachedEnumValues"); const cachedClasses_1 = require("../../../../core/cachedClasses"); const constants_1 = require("../../../../core/constants"); const UIStreakAnimation_1 = require("../../../../enums/private/UIStreakAnimation"); const frames_1 = require("../../../../functions/frames"); const ui_1 = require("../../../../functions/ui"); const v_1 = require("./v"); /** Corresponds to "resources/gfx/ui/ui_streak.anm2". */ const UI_STREAK_ANIMATION_END_FRAMES = { [UIStreakAnimation_1.UIStreakAnimation.NONE]: 0, [UIStreakAnimation_1.UIStreakAnimation.TEXT]: 69, [UIStreakAnimation_1.UIStreakAnimation.TEXT_STAY]: 1, }; /** This must match the name of the shader in "shaders.xml". */ const EMPTY_SHADER_NAME = "IsaacScript-RenderAboveHUD"; /** * The frame of the "Text" animation that corresponds to when it reaches the center of the screen * and stays put. */ const TEXT_STAY_FRAME = 8; /** The frame of the "Text" animation that corresponds to when it starts to move right. */ const TEXT_OUT_FRAME = 60; /** This matches the offset that the vanilla game uses; determined via trial and error. */ const STREAK_SPRITE_TOP_OFFSET = Vector(0, 47); /** This matches the offset that the vanilla game uses; determined via trial and error. */ const STREAK_SPRITE_BOTTOM_OFFSET = Vector(0, -48.25); /** * The offset from the bottom of the sprite that the rendered text should go; determined via trial * and error. */ const STREAK_TEXT_BOTTOM_Y_OFFSET = -9; /** * Corresponds to the vanilla value; determined via trial and error. * * 8 is too little and 9 has the vanilla text come out just slightly ahead, so we go with 9. */ const NUM_RENDER_FRAMES_MAP_HELD_BEFORE_STREAK_TEXT = 11; /** Taken from StageAPI. */ const TEXT_IN_ADJUSTMENTS = [-800, -639, -450, -250, -70, 10, 6, 3]; /** Taken from StageAPI. */ const TEXT_OUT_ADJUSTMENTS = [ 0, -5, -10, -15, -20, 144, 308, 472, 636, 800, ]; /** Taken from StageAPI. */ const TEXT_IN_SCALES = [ Vector(3, 0.2), Vector(2.6, 0.36), Vector(2.2, 0.52), Vector(1.8, 0.68), Vector(1.4, 0.84), Vector(0.95, 1.05), Vector(0.97, 1.03), Vector(0.98, 1.02), ]; /** Taken from StageAPI. */ const TEXT_OUT_SCALES = [ Vector(1, 1), Vector(0.99, 1.03), Vector(0.98, 1.05), Vector(0.96, 1.08), Vector(0.95, 1.1), Vector(1.36, 0.92), Vector(1.77, 0.74), Vector(2.18, 0.56), Vector(2.59, 0.38), Vector(3, 0.2), ]; // ModCallback.POST_RENDER (2) function streakTextPostRender() { // The top streak only plays when the player arrives on the floor (or continues a game from the // main menu.) checkEndTopStreakText(); // The bottom streak only plays when the player holds down the map button. trackMapInputPressed(); checkStartBottomStreakText(); checkEndBottomStreakText(); } function checkEndTopStreakText() { if (v_1.v.run.topStreakTextStartedRenderFrame === null || v_1.v.run.topStreakText.animation !== UIStreakAnimation_1.UIStreakAnimation.TEXT_STAY) { return; } const elapsedFrames = (0, frames_1.getElapsedRenderFramesSince)(v_1.v.run.topStreakTextStartedRenderFrame); if (elapsedFrames >= 115) { v_1.v.run.topStreakText.animation = UIStreakAnimation_1.UIStreakAnimation.TEXT; // We adjust by the frame backwards by an arbitrary amount to roughly align with the speed of // the vanilla animation. v_1.v.run.topStreakText.frame = TEXT_OUT_FRAME - 2; } } function trackMapInputPressed() { const gameFrameCount = cachedClasses_1.game.GetFrameCount(); for (const controllerIndex of cachedEnumValues_1.CONTROLLER_INDEX_VALUES) { const oldPushedMapFrame = v_1.v.run.controllerIndexPushingMapRenderFrame.get(controllerIndex); const isPushingMap = Input.IsActionPressed(isaac_typescript_definitions_1.ButtonAction.MAP, controllerIndex); if (isPushingMap) { if (oldPushedMapFrame === undefined) { v_1.v.run.controllerIndexPushingMapRenderFrame.set(controllerIndex, gameFrameCount); } } else { v_1.v.run.controllerIndexPushingMapRenderFrame.delete(controllerIndex); } } } /** * If the map input has been pressed down for long enough, play the animation where the level streak * slides in from the left. */ function checkStartBottomStreakText() { if (v_1.v.run.bottomStreakText.animation !== UIStreakAnimation_1.UIStreakAnimation.NONE) { return; } const pushedMapFrames = [ ...v_1.v.run.controllerIndexPushingMapRenderFrame.values(), ]; if (pushedMapFrames.length === 0) { return; } const earliestFrame = Math.min(...pushedMapFrames); const elapsedFrames = (0, frames_1.getElapsedGameFramesSince)(earliestFrame); if (elapsedFrames >= NUM_RENDER_FRAMES_MAP_HELD_BEFORE_STREAK_TEXT) { v_1.v.run.bottomStreakText.animation = UIStreakAnimation_1.UIStreakAnimation.TEXT; v_1.v.run.bottomStreakText.frame = 0; } } /** * If the map input has been released, play the animation where the level streak slides out to the * right. */ function checkEndBottomStreakText() { if (v_1.v.run.bottomStreakText.animation !== UIStreakAnimation_1.UIStreakAnimation.TEXT_STAY) { return; } const pushedMapFrames = [ ...v_1.v.run.controllerIndexPushingMapRenderFrame.values(), ]; if (pushedMapFrames.length === 0) { v_1.v.run.bottomStreakText.animation = UIStreakAnimation_1.UIStreakAnimation.TEXT; // We adjust by the frame backwards by an arbitrary amount to roughly align with the speed of // the vanilla animation. v_1.v.run.bottomStreakText.frame = TEXT_OUT_FRAME - 2; } } // ModCallback.GET_SHADER_PARAMS (22) function streakTextGetShaderParams(customStage, shaderName) { if (shaderName !== EMPTY_SHADER_NAME) { return; } const topCenterPos = (0, ui_1.getScreenTopCenterPos)(); const topStreakPosition = topCenterPos.add(STREAK_SPRITE_TOP_OFFSET); renderStreakText(customStage, v_1.v.run.topStreakText, topStreakPosition); const bottomCenterPos = (0, ui_1.getScreenBottomCenterPos)(); const bottomStreakPosition = bottomCenterPos.add(STREAK_SPRITE_BOTTOM_OFFSET); renderStreakText(customStage, v_1.v.run.bottomStreakText, bottomStreakPosition); } function renderStreakText(customStage, streakText, position) { if (streakText.animation === UIStreakAnimation_1.UIStreakAnimation.NONE) { return; } if (streakText.animation !== UIStreakAnimation_1.UIStreakAnimation.TEXT_STAY) { const { pauseFrame } = streakText; streakText.pauseFrame = !streakText.pauseFrame; if (!pauseFrame) { streakText.frame++; } } const endFrame = UI_STREAK_ANIMATION_END_FRAMES[streakText.animation]; if (streakText.frame > endFrame) { streakText.animation = UIStreakAnimation_1.UIStreakAnimation.NONE; streakText.frame = 0; return; } if (streakText.animation === UIStreakAnimation_1.UIStreakAnimation.TEXT && streakText.frame === TEXT_STAY_FRAME) { streakText.animation = UIStreakAnimation_1.UIStreakAnimation.TEXT_STAY; streakText.frame = 0; } const isPaused = cachedClasses_1.game.IsPaused(); if (isPaused) { return; } const font = cachedClasses_1.fonts.upheaval; const { name } = customStage; const numberSuffix = v_1.v.run.firstFloor ? "I" : "II"; const nameWithNumberSuffix = `${name} ${numberSuffix}`; const length = font.GetStringWidthUTF8(nameWithNumberSuffix); const centeredX = position.X - length / 2; let adjustment = 0; let scale = constants_1.VectorOne; if (streakText.animation === UIStreakAnimation_1.UIStreakAnimation.TEXT) { if (streakText.frame < TEXT_STAY_FRAME) { adjustment = TEXT_IN_ADJUSTMENTS[streakText.frame] ?? 0; scale = TEXT_IN_SCALES[streakText.frame] ?? constants_1.VectorOne; } else { const adjustedFrame = streakText.frame - TEXT_OUT_FRAME; adjustment = TEXT_OUT_ADJUSTMENTS[adjustedFrame] ?? 0; scale = TEXT_OUT_SCALES[adjustedFrame] ?? constants_1.VectorOne; } } const adjustedX = centeredX + adjustment; const adjustedY = position.Y + STREAK_TEXT_BOTTOM_Y_OFFSET; font.DrawStringScaled(nameWithNumberSuffix, adjustedX, adjustedY, scale.X, scale.Y, constants_1.KColorDefault); } function topStreakTextStart() { const level = cachedClasses_1.game.GetLevel(); const renderFrameCount = Isaac.GetFrameCount(); // Show the vanilla streak text, which will have a blank name because of the -1 floor. level.ShowName(false); // Initiate the animation for the custom text. v_1.v.run.topStreakText.animation = UIStreakAnimation_1.UIStreakAnimation.TEXT; v_1.v.run.topStreakText.frame = 0; v_1.v.run.topStreakTextStartedRenderFrame = renderFrameCount; }