Source: Window_Message.js

Window_Message.js

//-----------------------------------------------------------------------------
// Window_Message
//
// The window for displaying text messages.
/**
 * The window for displaying text messages.
 *
 * @class
 * @extends Window
 */
function Window_Message() {
    this.initialize(...arguments);
}

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

Window_Message.prototype.initialize = function(rect) {
    Window_Base.prototype.initialize.call(this, rect);
    this.openness = 0;
    this.initMembers();
};

/**
 * Initializes window variables
 */
Window_Message.prototype.initMembers = function() {
    this._background = 0;
    this._positionType = 2;
    this._waitCount = 0;
    this._faceBitmap = null;
    this._textState = null;
    this._goldWindow = null;
    this._nameBoxWindow = null;
    this._choiceListWindow = null;
    this._numberInputWindow = null;
    this._eventItemWindow = null;
    this.clearFlags();
};

/**
 * Associates the gold window
 *
 * @param {Window_Gold} goldWindow - The gold window to associate with this window
 */
Window_Message.prototype.setGoldWindow = function(goldWindow) {
    this._goldWindow = goldWindow;
};

/**
 * Associates the name box window
 *
 * @param {Window_NameBox} nameBoxWindow - The name box window to associate with this window
 */
Window_Message.prototype.setNameBoxWindow = function(nameBoxWindow) {
    this._nameBoxWindow = nameBoxWindow;
};

/**
 * Associates the choice list window
 *
 * @param {Window_ChoiceList} nameBoxWindow - The choice list window to associate with this window
 */
Window_Message.prototype.setChoiceListWindow = function(choiceListWindow) {
    this._choiceListWindow = choiceListWindow;
};

/**
 * Associates the number input window
 *
 * @param {Window_NumberInput} nameBoxWindow - The number input window to associate with this window
 */
Window_Message.prototype.setNumberInputWindow = function(numberInputWindow) {
    this._numberInputWindow = numberInputWindow;
};

/**
 * Associates the event item window
 *
 * @param {Window_EventItem} nameBoxWindow - The event item window to associate with this window
 */
Window_Message.prototype.setEventItemWindow = function(eventItemWindow) {
    this._eventItemWindow = eventItemWindow;
};
/**
 * Clears window flags
 */
Window_Message.prototype.clearFlags = function() {
    this._showFast = false;
    this._lineShowFast = false;
    this._pauseSkip = false;
};

Window_Message.prototype.update = function() {
    this.checkToNotClose();
    Window_Base.prototype.update.call(this);
    this.synchronizeNameBox();
    while (!this.isOpening() && !this.isClosing()) {
        if (this.updateWait()) {
            return;
        } else if (this.updateLoading()) {
            return;
        } else if (this.updateInput()) {
            return;
        } else if (this.updateMessage()) {
            return;
        } else if (this.canStart()) {
            this.startMessage();
        } else {
            this.startInput();
            return;
        }
    }
};

/**
 * Check if the window should instead stay open if it is closing
 */
Window_Message.prototype.checkToNotClose = function() {
    if (this.isOpen() && this.isClosing() && this.doesContinue()) {
        this.open();
    }
};

/**
 * Sync the name box window with this window
 */
Window_Message.prototype.synchronizeNameBox = function() {
    this._nameBoxWindow.openness = this.openness;
};

/**
 * Check if the window can start
 *
 * @return {boolean} True if the window can start
 */
Window_Message.prototype.canStart = function() {
    return $gameMessage.hasText() && !$gameMessage.scrollMode();
};

/**
 * Starts the message
 */
Window_Message.prototype.startMessage = function() {
    const text = $gameMessage.allText();
    const textState = this.createTextState(text, 0, 0, 0);
    textState.x = this.newLineX(textState);
    textState.startX = textState.x;
    this._textState = textState;
    this.newPage(this._textState);
    this.updatePlacement();
    this.updateBackground();
    this.open();
    this._nameBoxWindow.start();
};

/**
 * Get the x coordinate for a new line
 *
 * @param {Object} textState - The text state object
 * @return {number} The x coordinate of a new line
 */
Window_Message.prototype.newLineX = function(textState) {
    const faceExists = $gameMessage.faceName() !== "";
    const faceWidth = ImageManager.standardFaceWidth;
    const spacing = 20;
    const margin = faceExists ? faceWidth + spacing : 4;
    return textState.rtl ? this.innerWidth - margin : margin;
};

/**
 * Update window placements
 */
Window_Message.prototype.updatePlacement = function() {
    const goldWindow = this._goldWindow;
    this._positionType = $gameMessage.positionType();
    this.y = (this._positionType * (Graphics.boxHeight - this.height)) / 2;
    if (goldWindow) {
        goldWindow.y = this.y > 0 ? 0 : Graphics.boxHeight - goldWindow.height;
    }
};

/**
 * Updates the window background
 */
Window_Message.prototype.updateBackground = function() {
    this._background = $gameMessage.background();
    this.setBackgroundType(this._background);
};

/**
 * Ends the message
 */
Window_Message.prototype.terminateMessage = function() {
    this.close();
    this._goldWindow.close();
    $gameMessage.clear();
};

/**
 * Updates the window wait
 *
 * @return {boolean} True if updated
 */
Window_Message.prototype.updateWait = function() {
    if (this._waitCount > 0) {
        this._waitCount--;
        return true;
    } else {
        return false;
    }
};

/**
 * Cancels waiting
 * @since 1.8.0
 */
Window_Message.prototype.cancelWait = function() {
    if ($gameSystem.isMessageSkipEnabled()) {
        this._waitCount = 0;
    }
};

/**
 * Update window loading
 *
 * @return {boolean} True if updated
 */
Window_Message.prototype.updateLoading = function() {
    if (this._faceBitmap) {
        if (this._faceBitmap.isReady()) {
            this.drawMessageFace();
            this._faceBitmap = null;
            return false;
        } else {
            return true;
        }
    } else {
        return false;
    }
};

/**
 * Update window input
 *
 * @return {boolean} True if updated
 */
Window_Message.prototype.updateInput = function() {
    if (this.isAnySubWindowActive()) {
        return true;
    }
    if (this.pause) {
        if (this.isTriggered()) {
            Input.update();
            this.pause = false;
            if (!this._textState) {
                this.terminateMessage();
            }
        }
        return true;
    }
    return false;
};

/**
 * Check if there is any sub window that is currently active
 *
 * @return {boolean} True if choice/num input/event item window is active
 */
Window_Message.prototype.isAnySubWindowActive = function() {
    return (
        this._choiceListWindow.active ||
        this._numberInputWindow.active ||
        this._eventItemWindow.active
    );
};

/**
 * Updates the message
 *
 * @return {boolean} True if updated
 */
Window_Message.prototype.updateMessage = function() {
    const textState = this._textState;
    if (textState) {
        while (!this.isEndOfText(textState)) {
            if (this.needsNewPage(textState)) {
                this.newPage(textState);
            }
            this.updateShowFast();
            this.processCharacter(textState);
            if (this.shouldBreakHere(textState)) {
                break;
            }
        }
        this.flushTextState(textState);
        if (this.isEndOfText(textState) && !this.isWaiting()) {
            this.onEndOfText();
        }
        return true;
    } else {
        return false;
    }
};

/**
 * Check if the message should break
 *
 * @param {Object} textState - The text state object
 * @return {boolean} True if message should break
 */
Window_Message.prototype.shouldBreakHere = function(textState) {
    if (this.canBreakHere(textState)) {
        if (!this._showFast && !this._lineShowFast) {
            return true;
        }
        if (this.isWaiting()) {
            return true;
        }
    }
    return false;
};

/**
 * Check if the message can break
 *
 * @param {Object} textState - The text state object
 * @return {boolean} True if message can break
 */
Window_Message.prototype.canBreakHere = function(textState) {
    if (!this.isEndOfText(textState)) {
        const c = textState.text[textState.index];
        if (c.charCodeAt(0) >= 0xdc00 && c.charCodeAt(0) <= 0xdfff) {
            // surrogate pair
            return false;
        }
        if (textState.rtl && c.charCodeAt(0) > 0x20) {
            return false;
        }
    }
    return true;
};

/**
 * Handling for when there is no more text
 */
Window_Message.prototype.onEndOfText = function() {
    if (!this.startInput()) {
        if (!this._pauseSkip) {
            this.startPause();
        } else {
            this.terminateMessage();
        }
    }
    this._textState = null;
};

/**
 * Starts input
 *
 * @return {boolean} True if input started
 */
Window_Message.prototype.startInput = function() {
    if ($gameMessage.isChoice()) {
        this._choiceListWindow.start();
        return true;
    } else if ($gameMessage.isNumberInput()) {
        this._numberInputWindow.start();
        return true;
    } else if ($gameMessage.isItemChoice()) {
        this._eventItemWindow.start();
        return true;
    } else {
        return false;
    }
};

/**
 * Check if input is triggered
 *
 * @return {boolean} True if input is triggered
 */
Window_Message.prototype.isTriggered = function() {
    return (
        Input.isRepeated("ok") ||
        Input.isRepeated("cancel") ||
        TouchInput.isRepeated()
    );
};

/**
 * Check if the message continues
 *
 * @return {boolean} True if message continues
 */
Window_Message.prototype.doesContinue = function() {
    return (
        $gameMessage.hasText() &&
        !$gameMessage.scrollMode() &&
        !this.areSettingsChanged()
    );
};

/**
 * Check if the message window settings are changed
 *
 * @return {boolean} True if message settings changed
 */
Window_Message.prototype.areSettingsChanged = function() {
    return (
        this._background !== $gameMessage.background() ||
        this._positionType !== $gameMessage.positionType()
    );
};

/**
 * Update for showing fast
 */
Window_Message.prototype.updateShowFast = function() {
    if (this.isTriggered()) {
        this._showFast = true;
    }
};

/**
 * Starts a new page
 *
 * @param {Object} textState - The text state object
 */
Window_Message.prototype.newPage = function(textState) {
    this.contents.clear();
    this.resetFontSettings();
    this.clearFlags();
    this.updateSpeakerName();
    this.loadMessageFace();
    textState.x = textState.startX;
    textState.y = 0;
    textState.height = this.calcTextHeight(textState);
};

/**
 * Updates the speaker name for the name box window
 */
Window_Message.prototype.updateSpeakerName = function() {
    this._nameBoxWindow.setName($gameMessage.speakerName());
};

/**
 * Loads the face image for the message window
 */
Window_Message.prototype.loadMessageFace = function() {
    this._faceBitmap = ImageManager.loadFace($gameMessage.faceName());
};

/**
 * Draws the face in the message window
 */
Window_Message.prototype.drawMessageFace = function() {
    const faceName = $gameMessage.faceName();
    const faceIndex = $gameMessage.faceIndex();
    const rtl = $gameMessage.isRTL();
    const width = ImageManager.standardFaceWidth;
    const height = this.innerHeight;
    const x = rtl ? this.innerWidth - width - 4 : 4;
    this.drawFace(faceName, faceIndex, x, 0, width, height);
};

Window_Message.prototype.processControlCharacter = function(textState, c) {
    Window_Base.prototype.processControlCharacter.call(this, textState, c);
    if (c === "\f") {
        this.processNewPage(textState);
    }
};

Window_Message.prototype.processNewLine = function(textState) {
    this._lineShowFast = false;
    Window_Base.prototype.processNewLine.call(this, textState);
    if (this.needsNewPage(textState)) {
        this.startPause();
    }
};

/**
 * Processes a new page
 *
 * @param {Object} textState - The text state object
 */
Window_Message.prototype.processNewPage = function(textState) {
    if (textState.text[textState.index] === "\n") {
        textState.index++;
    }
    textState.y = this.contents.height;
    this.startPause();
};

/**
 * Check if there is no more text in the text state
 *
 * @param {Object} textState - The text state object
 * @return {boolean} True if no more text
 */
Window_Message.prototype.isEndOfText = function(textState) {
    return textState.index >= textState.text.length;
};

/**
 * Check if the message needs a new page
 *
 * @param {Object} textState - The text state object
 * @return {boolean} True if a new page is needed
 */
Window_Message.prototype.needsNewPage = function(textState) {
    return (
        !this.isEndOfText(textState) &&
        textState.y + textState.height > this.contents.height
    );
};

Window_Message.prototype.processEscapeCharacter = function(code, textState) {
    switch (code) {
        case "$":
            this._goldWindow.open();
            break;
        case ".":
            this.startWait(15);
            break;
        case "|":
            this.startWait(60);
            break;
        case "!":
            this.startPause();
            break;
        case ">":
            this._lineShowFast = true;
            break;
        case "<":
            this._lineShowFast = false;
            break;
        case "^":
            this._pauseSkip = true;
            break;
        default:
            Window_Base.prototype.processEscapeCharacter.call(
                this,
                code,
                textState
            );
            break;
    }
};

/**
 * Starts a wait for the given count
 *
 * @param {number} count - The amount to wait for
 */
Window_Message.prototype.startWait = function(count) {
    this._waitCount = count;
};

/**
 * Starts a pause
 */
Window_Message.prototype.startPause = function() {
    this.startWait(10);
    this.pause = true;
};

/**
 * Check if the window is waiting
 *
 * @return {boolean} True if the window is waiting
 * @since Version 1.2.1
 */
Window_Message.prototype.isWaiting = function() {
    return this.pause || this._waitCount > 0;
};