Source: Window_BattleLog.js

Window_BattleLog.js

//-----------------------------------------------------------------------------
// Window_BattleLog
//
// The window for displaying battle progress. No frame is displayed, but it is
// handled as a window for convenience.
/**
 * The window for displaying battle progress. No frame is displayed, but it is handled as a window for convenience.
 *
 * @class
 * @extends Window_Base
 */
function Window_BattleLog() {
    this.initialize(...arguments);
}

Window_BattleLog.prototype = Object.create(Window_Base.prototype);
Window_BattleLog.prototype.constructor = Window_BattleLog;

Window_BattleLog.prototype.initialize = function(rect) {
    Window_Base.prototype.initialize.call(this, rect);
    this.opacity = 0;
    this._lines = [];
    this._methods = [];
    this._waitCount = 0;
    this._waitMode = "";
    this._baseLineStack = [];
    this._spriteset = null;
    this.refresh();
};

/**
 * Associates the window with the spriteset
 *
 * @param {Spriteset_Battle} spriteset - The spriteset for the battle
 */
Window_BattleLog.prototype.setSpriteset = function(spriteset) {
    this._spriteset = spriteset;
};

/**
 * Get the max lines the window should handle
 *
 * @return {number} Max number of lines
 */
Window_BattleLog.prototype.maxLines = function() {
    return 10;
};

/**
 * Get the current number of lines in the window
 *
 * @return {number} The number of lines in the window
 */
Window_BattleLog.prototype.numLines = function() {
    return this._lines.length;
};

/**
 * Get the message speed in the window
 *
 * @return {number} The message speed
 */
Window_BattleLog.prototype.messageSpeed = function() {
    return 16;
};

/**
 * Check if the window is busy
 *
 * @return {boolean} True if the window is busy
 */
Window_BattleLog.prototype.isBusy = function() {
    return this._waitCount > 0 || this._waitMode || this._methods.length > 0;
};

/**
 * Updates the window
 */
Window_BattleLog.prototype.update = function() {
    if (!this.updateWait()) {
        this.callNextMethod();
    }
};

/**
 * Updates the wait of the window
 *
 * @return {boolean} True if updated
 */
Window_BattleLog.prototype.updateWait = function() {
    return this.updateWaitCount() || this.updateWaitMode();
};

/**
 * Updates the wait count
 *
 * @return {boolean} True if updated
 */
Window_BattleLog.prototype.updateWaitCount = function() {
    if (this._waitCount > 0) {
        this._waitCount -= this.isFastForward() ? 3 : 1;
        if (this._waitCount < 0) {
            this._waitCount = 0;
        }
        return true;
    }
    return false;
};

/**
 * Updates the wait mode
 *
 * @return {boolean} True if the window is waiting
 */
Window_BattleLog.prototype.updateWaitMode = function() {
    let waiting = false;
    switch (this._waitMode) {
        case "effect":
            waiting = this._spriteset.isEffecting();
            break;
        case "movement":
            waiting = this._spriteset.isAnyoneMoving();
            break;
    }
    if (!waiting) {
        this._waitMode = "";
    }
    return waiting;
};

/**
 * Sets the wait mode of the window. For example, "movement" if there is anyone moving on the spriteset
 *
 * @param {string} waitMode - The mode of the window's wait
 */
Window_BattleLog.prototype.setWaitMode = function(waitMode) {
    this._waitMode = waitMode;
};

/**
 * Calls the next method queued up in the window
 *
 * @throws Error if the method has no name or is not a valid method
 */
Window_BattleLog.prototype.callNextMethod = function() {
    if (this._methods.length > 0) {
        const method = this._methods.shift();
        if (method.name && this[method.name]) {
            this[method.name].apply(this, method.params);
        } else {
            throw new Error("Method not found: " + method.name);
        }
    }
};

/**
 * Check if the window should be fast forwarding
 *
 * @return {boolean} True if the window is fast forwarding
 */
Window_BattleLog.prototype.isFastForward = function() {
    return (
        Input.isLongPressed("ok") ||
        Input.isPressed("shift") ||
        TouchInput.isLongPressed()
    );
};

/**
 * Pushes a method onto the method stack with arguments
 *
 * @param {string} methodName - The method name to push
 */
Window_BattleLog.prototype.push = function(methodName) {
    const methodArgs = Array.prototype.slice.call(arguments, 1);
    this._methods.push({ name: methodName, params: methodArgs });
};

/**
 * Clears the lines and base line stack
 */
Window_BattleLog.prototype.clear = function() {
    this._lines = [];
    this._baseLineStack = [];
    this.refresh();
};

/**
 * Starts the waiting process
 */
Window_BattleLog.prototype.wait = function() {
    this._waitCount = this.messageSpeed();
};

/**
 * Sets the wait mode for an effect wait
 */
Window_BattleLog.prototype.waitForEffect = function() {
    this.setWaitMode("effect");
};

/**
 * Sets the wait mode for a movement wait
 */
Window_BattleLog.prototype.waitForMovement = function() {
    this.setWaitMode("movement");
};

/**
 * Adds a new line of text to the window
 *
 * @param {string} text - The text to add
 */
Window_BattleLog.prototype.addText = function(text) {
    this._lines.push(text);
    this.refresh();
    this.wait();
};

/**
 * Pushes the base line to the stack
 */
Window_BattleLog.prototype.pushBaseLine = function() {
    this._baseLineStack.push(this._lines.length);
};

/**
 * Pops the base line and any lines afterwards
 */
Window_BattleLog.prototype.popBaseLine = function() {
    const baseLine = this._baseLineStack.pop();
    while (this._lines.length > baseLine) {
        this._lines.pop();
    }
};

/**
 * Waits for a new line
 */
Window_BattleLog.prototype.waitForNewLine = function() {
    let baseLine = 0;
    if (this._baseLineStack.length > 0) {
        baseLine = this._baseLineStack[this._baseLineStack.length - 1];
    }
    if (this._lines.length > baseLine) {
        this.wait();
    }
};

/**
 * Pops up damage on a target
 *
 * @param {Game_Battler} target - The target where the damage popup should display
 */
Window_BattleLog.prototype.popupDamage = function(target) {
    if (target.shouldPopupDamage()) {
        target.startDamagePopup();
    }
};

/**
 * Starts an action
 *
 * @param {Game_Battler} subject - The subject of the action
 * @param {Game_Action} action - The action to start
 */
Window_BattleLog.prototype.performActionStart = function(subject, action) {
    subject.performActionStart(action);
};

/**
 * Performs an action
 *
 * @param {Game_Battler} subject - The subject of the action
 * @param {Game_Action} action - The action to perform
 */
Window_BattleLog.prototype.performAction = function(subject, action) {
    subject.performAction(action);
};

/**
 * Ends an action
 *
 * @param {Game_Battler} subject - The subject of the action
 */
Window_BattleLog.prototype.performActionEnd = function(subject) {
    subject.performActionEnd();
};

/**
 * Performs damage
 *
 * @param {Game_Battler} target - The target that took damage
 */
Window_BattleLog.prototype.performDamage = function(target) {
    target.performDamage();
};

/**
 * Performs a miss
 *
 * @param {Game_Battler} target - The target of the miss
 */
Window_BattleLog.prototype.performMiss = function(target) {
    target.performMiss();
};

/**
 * Performs recovery
 *
 * @param {Game_Battler} target - The target that recovered
 */
Window_BattleLog.prototype.performRecovery = function(target) {
    target.performRecovery();
};

/**
 * Performs an evade
 *
 * @param {Game_Battler} target - The target that evaded
 */
Window_BattleLog.prototype.performEvasion = function(target) {
    target.performEvasion();
};

/**
 * Performs a magic evade
 *
 * @param {Game_Battler} target - The target that magic evaded
 */
Window_BattleLog.prototype.performMagicEvasion = function(target) {
    target.performMagicEvasion();
};

/**
 * Performs a counter
 *
 * @param {Game_Battler} target - The target that countered
 */
Window_BattleLog.prototype.performCounter = function(target) {
    target.performCounter();
};

/**
 * Performs a reflection
 *
 * @param {Game_Battler} target - The target that reflected
 */
Window_BattleLog.prototype.performReflection = function(target) {
    target.performReflection();
};

/**
 * Performs a substitute
 *
 * @param {Game_Battler} substitute - The substitute
 * @param {Game_Battler} target - The original target
 */
Window_BattleLog.prototype.performSubstitute = function(substitute, target) {
    substitute.performSubstitute(target);
};

/**
 * Performs a collapse
 *
 * @param {Game_Battler} target - The target that collapsed
 */
Window_BattleLog.prototype.performCollapse = function(target) {
    target.performCollapse();
};

// prettier-ignore
/**
 * Shows an animation
 *
 * @param {Game_Battler} subject - The subject of the animation
 * @param {Game_Battler[]} targets - The target(s) of the animation
 * @param {number} animationId - The ID of the animation to show
 */
Window_BattleLog.prototype.showAnimation = function(
    subject, targets, animationId
) {
    if (animationId < 0) {
        this.showAttackAnimation(subject, targets);
    } else {
        this.showNormalAnimation(targets, animationId);
    }
};

/**
 * Shows a normal attack animation
 *
 * @param {Game_Battler} subject - The subject of the animation
 * @param {Game_Battler[]} targets - The target(s) of the animation
 */
Window_BattleLog.prototype.showAttackAnimation = function(subject, targets) {
    if (subject.isActor()) {
        this.showActorAttackAnimation(subject, targets);
    } else {
        this.showEnemyAttackAnimation(subject, targets);
    }
};

// prettier-ignore
/**
 * Shows an actor attack animation
 *
 * @param {Game_Battler} subject - The subject of the animation
 * @param {Game_Battler[]} targets - The target(s) of the animation
 */
Window_BattleLog.prototype.showActorAttackAnimation = function(
    subject, targets
) {
    this.showNormalAnimation(targets, subject.attackAnimationId1(), false);
    this.showNormalAnimation(targets, subject.attackAnimationId2(), true);
};

// prettier-ignore
/**
 * Shows an enemy attack animation
 */
Window_BattleLog.prototype.showEnemyAttackAnimation = function(
    /* subject, targets */
) {
    SoundManager.playEnemyAttack();
};

// prettier-ignore
/**
 * Shows a normal animation
 *
 * @param {Game_Battler[]} targets - The target(s) of the animation
 * @param {number} animationId - The ID of the animation to show
 * @param {boolean} mirror - If the animation should mirror
 */
Window_BattleLog.prototype.showNormalAnimation = function(
    targets, animationId, mirror
) {
    const animation = $dataAnimations[animationId];
    if (animation) {
        $gameTemp.requestAnimation(targets, animationId, mirror);
    }
};

/**
 * Refreshes the window
 */
Window_BattleLog.prototype.refresh = function() {
    this.drawBackground();
    this.contents.clear();
    for (let i = 0; i < this._lines.length; i++) {
        this.drawLineText(i);
    }
};

/**
 * Draws the window background
 */
Window_BattleLog.prototype.drawBackground = function() {
    const rect = this.backRect();
    const color = this.backColor();
    this.contentsBack.clear();
    this.contentsBack.paintOpacity = this.backPaintOpacity();
    this.contentsBack.fillRect(rect.x, rect.y, rect.width, rect.height, color);
    this.contentsBack.paintOpacity = 255;
};

/**
 * Gets the window background rectangle
 *
 * @return {Rectangle} The {@link Rectangle} that contains the window background
 */
Window_BattleLog.prototype.backRect = function() {
    const height = this.numLines() * this.itemHeight();
    return new Rectangle(0, 0, this.innerWidth, height);
};

/**
 * Gets a rectangle object for a given line
 *
 * @param {number} index - The line index
 * @return {Rectangle} The {@link Rectangle} that contains the line
 */
Window_BattleLog.prototype.lineRect = function(index) {
    const itemHeight = this.itemHeight();
    const padding = this.itemPadding();
    const x = padding;
    const y = index * itemHeight;
    const width = this.innerWidth - padding * 2;
    const height = itemHeight;
    return new Rectangle(x, y, width, height);
};

/**
 * Gets the background color
 *
 * @return {string} The background color
 */
Window_BattleLog.prototype.backColor = function() {
    return "#000000";
};

/**
 * Gets the background opacity
 *
 * @return {number} The opacity of the background
 */
Window_BattleLog.prototype.backPaintOpacity = function() {
    return 64;
};

/**
 * Draws a line's text at a given line index
 *
 * @param {number} index - The line index to draw
 */
Window_BattleLog.prototype.drawLineText = function(index) {
    const rect = this.lineRect(index);
    this.contents.clearRect(rect.x, rect.y, rect.width, rect.height);
    this.drawTextEx(this._lines[index], rect.x, rect.y, rect.width);
};

/**
 * Handling for battle turn start
 */
Window_BattleLog.prototype.startTurn = function() {
    this.push("wait");
};

/**
 * Handling for the start of an action
 *
 * @param {Game_Battler} subject - The subject of the action
 * @param {Game_Action} action - The action
 * @param {Game_Battler[]} targets - The target(s) of the action
 */
Window_BattleLog.prototype.startAction = function(subject, action, targets) {
    const item = action.item();
    this.push("performActionStart", subject, action);
    this.push("waitForMovement");
    this.push("performAction", subject, action);
    this.push("showAnimation", subject, targets.clone(), item.animationId);
    this.displayAction(subject, item);
};

/**
 * Handling for the end of an action
 *
 * @param {Game_Battler} subject - The subject of the action
 */
Window_BattleLog.prototype.endAction = function(subject) {
    this.push("waitForNewLine");
    this.push("clear");
    this.push("performActionEnd", subject);
};

/**
 * Handling for displaying the current state of a subject
 *
 * @param {Game_Battler} subject - The subject
 */
Window_BattleLog.prototype.displayCurrentState = function(subject) {
    const stateText = subject.mostImportantStateText();
    if (stateText) {
        this.push("addText", stateText.format(subject.name()));
        this.push("wait");
        this.push("clear");
    }
};

/**
 * Handling for displaying regeneration of a subject
 *
 * @param {Game_Battler} subject - The subject
 */
Window_BattleLog.prototype.displayRegeneration = function(subject) {
    this.push("popupDamage", subject);
};

/**
 * Handling for the display of an action
 *
 * @param {Game_Battler} subject - The subject of the action
 * @param {Object} item - The item of an action (skill or item)
 */
Window_BattleLog.prototype.displayAction = function(subject, item) {
    const numMethods = this._methods.length;
    if (DataManager.isSkill(item)) {
        this.displayItemMessage(item.message1, subject, item);
        this.displayItemMessage(item.message2, subject, item);
    } else {
        this.displayItemMessage(TextManager.useItem, subject, item);
    }
    if (this._methods.length === numMethods) {
        this.push("wait");
    }
};

/**
 * Displays an item message
 *
 * @param {string} fmt - The format of the message
 * @param {Game_Battler} subject - The subject
 * @param {Object} item - The item
 */
Window_BattleLog.prototype.displayItemMessage = function(fmt, subject, item) {
    if (fmt) {
        this.push("addText", fmt.format(subject.name(), item.name));
    }
};

/**
 * Displays a counter
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayCounter = function(target) {
    this.push("performCounter", target);
    this.push("addText", TextManager.counterAttack.format(target.name()));
};

/**
 * Displays a reflection
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayReflection = function(target) {
    this.push("performReflection", target);
    this.push("addText", TextManager.magicReflection.format(target.name()));
};

/**
 * Displays a substitute
 *
 * @param {Game_Battler} substitute - The substitute
 * @param {Game_Battler} target - The original target
 */
Window_BattleLog.prototype.displaySubstitute = function(substitute, target) {
    const substName = substitute.name();
    const text = TextManager.substitute.format(substName, target.name());
    this.push("performSubstitute", substitute, target);
    this.push("addText", text);
};

/**
 * Displays action results
 *
 * @param {Game_Battler} subject - The subject
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayActionResults = function(subject, target) {
    if (target.result().used) {
        this.push("pushBaseLine");
        this.displayCritical(target);
        this.push("popupDamage", target);
        this.push("popupDamage", subject);
        this.displayDamage(target);
        this.displayAffectedStatus(target);
        this.displayFailure(target);
        this.push("waitForNewLine");
        this.push("popBaseLine");
    }
};

/**
 * Displays action failure
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayFailure = function(target) {
    if (target.result().isHit() && !target.result().success) {
        this.push("addText", TextManager.actionFailure.format(target.name()));
    }
};

/**
 * Displays a critical
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayCritical = function(target) {
    if (target.result().critical) {
        if (target.isActor()) {
            this.push("addText", TextManager.criticalToActor);
        } else {
            this.push("addText", TextManager.criticalToEnemy);
        }
    }
};

/**
 * Displays damage
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayDamage = function(target) {
    if (target.result().missed) {
        this.displayMiss(target);
    } else if (target.result().evaded) {
        this.displayEvasion(target);
    } else {
        this.displayHpDamage(target);
        this.displayMpDamage(target);
        this.displayTpDamage(target);
    }
};

/**
 * Displays a miss
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayMiss = function(target) {
    let fmt;
    if (target.result().physical) {
        const isActor = target.isActor();
        fmt = isActor ? TextManager.actorNoHit : TextManager.enemyNoHit;
        this.push("performMiss", target);
    } else {
        fmt = TextManager.actionFailure;
    }
    this.push("addText", fmt.format(target.name()));
};

/**
 * Displays an evade
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayEvasion = function(target) {
    let fmt;
    if (target.result().physical) {
        fmt = TextManager.evasion;
        this.push("performEvasion", target);
    } else {
        fmt = TextManager.magicEvasion;
        this.push("performMagicEvasion", target);
    }
    this.push("addText", fmt.format(target.name()));
};

/**
 * Displays hp damage
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayHpDamage = function(target) {
    if (target.result().hpAffected) {
        if (target.result().hpDamage > 0 && !target.result().drain) {
            this.push("performDamage", target);
        }
        if (target.result().hpDamage < 0) {
            this.push("performRecovery", target);
        }
        this.push("addText", this.makeHpDamageText(target));
    }
};

/**
 * Displays mp damage
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayMpDamage = function(target) {
    if (target.isAlive() && target.result().mpDamage !== 0) {
        if (target.result().mpDamage < 0) {
            this.push("performRecovery", target);
        }
        this.push("addText", this.makeMpDamageText(target));
    }
};

/**
 * Displays tp damage
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayTpDamage = function(target) {
    if (target.isAlive() && target.result().tpDamage !== 0) {
        if (target.result().tpDamage < 0) {
            this.push("performRecovery", target);
        }
        this.push("addText", this.makeTpDamageText(target));
    }
};

/**
 * Displays states or buffs
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayAffectedStatus = function(target) {
    if (target.result().isStatusAffected()) {
        this.push("pushBaseLine");
        this.displayChangedStates(target);
        this.displayChangedBuffs(target);
        this.push("waitForNewLine");
        this.push("popBaseLine");
    }
};

/**
 * Displays auto affected status
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayAutoAffectedStatus = function(target) {
    if (target.result().isStatusAffected()) {
        this.displayAffectedStatus(target, null);
        this.push("clear");
    }
};

/**
 * Displays a change in state
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayChangedStates = function(target) {
    this.displayAddedStates(target);
    this.displayRemovedStates(target);
};

/**
 * Displays added states
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayAddedStates = function(target) {
    const result = target.result();
    const states = result.addedStateObjects();
    for (const state of states) {
        const stateText = target.isActor() ? state.message1 : state.message2;
        if (state.id === target.deathStateId()) {
            this.push("performCollapse", target);
        }
        if (stateText) {
            this.push("popBaseLine");
            this.push("pushBaseLine");
            this.push("addText", stateText.format(target.name()));
            this.push("waitForEffect");
        }
    }
};

/**
 * Displays removed states
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayRemovedStates = function(target) {
    const result = target.result();
    const states = result.removedStateObjects();
    for (const state of states) {
        if (state.message4) {
            this.push("popBaseLine");
            this.push("pushBaseLine");
            this.push("addText", state.message4.format(target.name()));
        }
    }
};

/**
 * Displays changed buffs
 *
 * @param {Game_Battler} target - The target
 */
Window_BattleLog.prototype.displayChangedBuffs = function(target) {
    const result = target.result();
    this.displayBuffs(target, result.addedBuffs, TextManager.buffAdd);
    this.displayBuffs(target, result.addedDebuffs, TextManager.debuffAdd);
    this.displayBuffs(target, result.removedBuffs, TextManager.buffRemove);
};

/**
 * Displays buffs
 *
 * @param {Game_Battler} target - The target
 * @param {number[]} buffs - Array of paramIds that are buffed
 * @param {string} fmt - The message format
 */
Window_BattleLog.prototype.displayBuffs = function(target, buffs, fmt) {
    for (const paramId of buffs) {
        const text = fmt.format(target.name(), TextManager.param(paramId));
        this.push("popBaseLine");
        this.push("pushBaseLine");
        this.push("addText", text);
    }
};

/**
 * Gets the hp damage text to show
 *
 * @param {Game_Battler} target - The target
 * @return {string} HP damage text to show
 */
Window_BattleLog.prototype.makeHpDamageText = function(target) {
    const result = target.result();
    const damage = result.hpDamage;
    const isActor = target.isActor();
    let fmt;
    if (damage > 0 && result.drain) {
        fmt = isActor ? TextManager.actorDrain : TextManager.enemyDrain;
        return fmt.format(target.name(), TextManager.hp, damage);
    } else if (damage > 0) {
        fmt = isActor ? TextManager.actorDamage : TextManager.enemyDamage;
        return fmt.format(target.name(), damage);
    } else if (damage < 0) {
        fmt = isActor ? TextManager.actorRecovery : TextManager.enemyRecovery;
        return fmt.format(target.name(), TextManager.hp, -damage);
    } else {
        fmt = isActor ? TextManager.actorNoDamage : TextManager.enemyNoDamage;
        return fmt.format(target.name());
    }
};

/**
 * Gets the mp damage text to show
 *
 * @param {Game_Battler} target - The target
 * @return {string} MP damage text to show
 */
Window_BattleLog.prototype.makeMpDamageText = function(target) {
    const result = target.result();
    const damage = result.mpDamage;
    const isActor = target.isActor();
    let fmt;
    if (damage > 0 && result.drain) {
        fmt = isActor ? TextManager.actorDrain : TextManager.enemyDrain;
        return fmt.format(target.name(), TextManager.mp, damage);
    } else if (damage > 0) {
        fmt = isActor ? TextManager.actorLoss : TextManager.enemyLoss;
        return fmt.format(target.name(), TextManager.mp, damage);
    } else if (damage < 0) {
        fmt = isActor ? TextManager.actorRecovery : TextManager.enemyRecovery;
        return fmt.format(target.name(), TextManager.mp, -damage);
    } else {
        return "";
    }
};

/**
 * Gets the tp damage text to show
 *
 * @param {Game_Battler} target - The target
 * @return {string} TP damage text to show
 */
Window_BattleLog.prototype.makeTpDamageText = function(target) {
    const result = target.result();
    const damage = result.tpDamage;
    const isActor = target.isActor();
    let fmt;
    if (damage > 0) {
        fmt = isActor ? TextManager.actorLoss : TextManager.enemyLoss;
        return fmt.format(target.name(), TextManager.tp, damage);
    } else if (damage < 0) {
        fmt = isActor ? TextManager.actorGain : TextManager.enemyGain;
        return fmt.format(target.name(), TextManager.tp, -damage);
    } else {
        return "";
    }
};