//-----------------------------------------------------------------------------
// Window_Base
//
// The superclass of all windows within the game.
/**
* The superclass of all windows within the game.
*
* @class
* @extends Window
*/
function Window_Base() {
this.initialize(...arguments);
}
Window_Base.prototype = Object.create(Window.prototype);
Window_Base.prototype.constructor = Window_Base;
Window_Base.prototype.initialize = function(rect) {
Window.prototype.initialize.call(this);
this.loadWindowskin();
this.checkRectObject(rect);
this.move(rect.x, rect.y, rect.width, rect.height);
this.updatePadding();
this.updateBackOpacity();
this.updateTone();
this.createContents();
this._opening = false;
this._closing = false;
this._dimmerSprite = null;
};
Window_Base.prototype.destroy = function(options) {
this.destroyContents();
if (this._dimmerSprite) {
this._dimmerSprite.bitmap.destroy();
}
Window.prototype.destroy.call(this, options);
};
/**
* Checks if the given rect parameter is a valid {@link Rectangle}
*
* @param {*} rect - The object to check for validity
* @return {boolean} True if the object is a valid rectangle
* @throws Error if the object is not a valid rectangle
*/
Window_Base.prototype.checkRectObject = function(rect) {
if (typeof rect !== "object" || !("x" in rect)) {
// Probably MV plugin is used
throw new Error("Argument must be a Rectangle");
}
};
/**
* Gets the height in pixels of one line
*
* @return {number} Height of a line
*/
Window_Base.prototype.lineHeight = function() {
return 36;
};
/**
* Gets the width in pixels of an item
*
* @return {number} Width of an item
*/
Window_Base.prototype.itemWidth = function() {
return this.innerWidth;
};
/**
* Gets the height in pixels of an item
*
* @return {number} Height of an item
*/
Window_Base.prototype.itemHeight = function() {
return this.lineHeight();
};
/**
* Gets the padding for an item
*
* @return {number} Amount of item padding
*/
Window_Base.prototype.itemPadding = function() {
return 8;
};
/**
* Creates a rectangle for an item of text
*
* @return {Rectangle} Rectangle that encompasses the text item
*/
Window_Base.prototype.baseTextRect = function() {
const rect = new Rectangle(0, 0, this.innerWidth, this.innerHeight);
rect.pad(-this.itemPadding(), 0);
return rect;
};
/**
* Loads the windowskin for the window
*/
Window_Base.prototype.loadWindowskin = function() {
this.windowskin = ImageManager.loadSystem("Window");
};
/**
* Updates the window's padding
*/
Window_Base.prototype.updatePadding = function() {
this.padding = $gameSystem.windowPadding();
};
/**
* Updates the window's background opacity
*/
Window_Base.prototype.updateBackOpacity = function() {
this.backOpacity = $gameSystem.windowOpacity();
};
/**
* Calculates the height needed for a given number of lines
*
* @param {number} numLines - The number of lines to calculate the height of
* @return {number} The height needed to fit the amount of lines
*/
Window_Base.prototype.fittingHeight = function(numLines) {
return numLines * this.itemHeight() + $gameSystem.windowPadding() * 2;
};
/**
* Updates the window's tone
*/
Window_Base.prototype.updateTone = function() {
const tone = $gameSystem.windowTone();
this.setTone(tone[0], tone[1], tone[2]);
};
/**
* Creates the contents of the window
*/
Window_Base.prototype.createContents = function() {
const width = this.contentsWidth();
const height = this.contentsHeight();
this.destroyContents();
this.contents = new Bitmap(width, height);
this.contentsBack = new Bitmap(width, height);
this.resetFontSettings();
};
/**
* Destroys the contents of the window
*/
Window_Base.prototype.destroyContents = function() {
if (this.contents) {
this.contents.destroy();
}
if (this.contentsBack) {
this.contentsBack.destroy();
}
};
/**
* Gets the width needed for the window contents
*
* @return {number} Width needed for the window contents
*/
Window_Base.prototype.contentsWidth = function() {
return this.innerWidth;
};
/**
* Gets the height needed for the window contents
*
* @return {number} Height needed for the window contents
*/
Window_Base.prototype.contentsHeight = function() {
return this.innerHeight;
};
/**
* Resets font settings to defaults
*/
Window_Base.prototype.resetFontSettings = function() {
this.contents.fontFace = $gameSystem.mainFontFace();
this.contents.fontSize = $gameSystem.mainFontSize();
this.resetTextColor();
};
/**
* Resets text and outline color to defaults
*/
Window_Base.prototype.resetTextColor = function() {
this.changeTextColor(ColorManager.normalColor());
this.changeOutlineColor(ColorManager.outlineColor());
};
Window_Base.prototype.update = function() {
Window.prototype.update.call(this);
this.updateTone();
this.updateOpen();
this.updateClose();
this.updateBackgroundDimmer();
};
/**
* Updates the opening effect
*/
Window_Base.prototype.updateOpen = function() {
if (this._opening) {
this.openness += 32;
if (this.isOpen()) {
this._opening = false;
}
}
};
/**
* Updates the closing effect
*/
Window_Base.prototype.updateClose = function() {
if (this._closing) {
this.openness -= 32;
if (this.isClosed()) {
this._closing = false;
}
}
};
/**
* Start opening the window
*/
Window_Base.prototype.open = function() {
if (!this.isOpen()) {
this._opening = true;
}
this._closing = false;
};
/**
* Start closing the window
*/
Window_Base.prototype.close = function() {
if (!this.isClosed()) {
this._closing = true;
}
this._opening = false;
};
/**
* Check if the window is opening
*
* @return {boolean} True if window is currently opening
*/
Window_Base.prototype.isOpening = function() {
return this._opening;
};
/**
* Check if the window is closing
*
* @return {boolean} True if window is currently closing
*/
Window_Base.prototype.isClosing = function() {
return this._closing;
};
/**
* Displays the window
*/
Window_Base.prototype.show = function() {
this.visible = true;
};
/**
* Stops displaying the window
*/
Window_Base.prototype.hide = function() {
this.visible = false;
};
/**
* Activates the window
*/
Window_Base.prototype.activate = function() {
this.active = true;
};
/**
* Deactivates the window
*/
Window_Base.prototype.deactivate = function() {
this.active = false;
};
/**
* Gets the system text color
*
* @return {string} The system text color
*/
Window_Base.prototype.systemColor = function() {
return ColorManager.systemColor();
};
/**
* Gets the opacity to use for a translucent effect
*
* @return {number} The opacity to use for a translucent effect
*/
Window_Base.prototype.translucentOpacity = function() {
return 160;
};
/**
* Changes the text color of text drawn to the window
*
* @param {string} color - The new color to draw text in
*/
Window_Base.prototype.changeTextColor = function(color) {
this.contents.textColor = color;
};
/**
* Changes the text outline color of text drawn to the window
*
* @param {string} color - The new outline color to use
*/
Window_Base.prototype.changeOutlineColor = function(color) {
this.contents.outlineColor = color;
};
/**
* Changes the opacity of what is painted to the contents of the window
*
* @param {boolean} enabled - True = use full opacity | False = use translucent opacity
*/
Window_Base.prototype.changePaintOpacity = function(enabled) {
this.contents.paintOpacity = enabled ? 255 : this.translucentOpacity();
};
/**
* Draws a rectangle of given width/height at given x/y coordinates
*
* @param {number} x - The X coordinate of the upper left corner of the rectangle
* @param {number} y - The Y coordinate of the upper left corner of the rectangle
* @param {number} width - The width in pixels of the rectangle
* @param {number} height - The height in pixels of the rectangle
*/
Window_Base.prototype.drawRect = function(x, y, width, height) {
const outlineColor = this.contents.outlineColor;
const mainColor = this.contents.textColor;
this.contents.fillRect(x, y, width, height, outlineColor);
this.contents.fillRect(x + 1, y + 1, width - 2, height - 2, mainColor);
};
/**
* Draws text to the window's contents. Does not support text codes in the text, but will fit the text within the maximum width without overflowing.
*
* @param {string} text - The text to draw
* @param {number} x - The x coordinate to begin drawing the text
* @param {number} y - The y coordinate to begin drawing the text
* @param {number} maxWidth - The maximum width in pixels of the text
* @param {string} align - The alignment of the text
*/
Window_Base.prototype.drawText = function(text, x, y, maxWidth, align) {
this.contents.drawText(text, x, y, maxWidth, this.lineHeight(), align);
};
/**
* Calculates the width needed for a given string of text. Does not support text codes.
*
* @param {string} text - The text to calculate the width for
* @return {number} The width of the text
*/
Window_Base.prototype.textWidth = function(text) {
return this.contents.measureTextWidth(text);
};
/**
* Draws text to the window's contents. Does support text codes.
*
* @param {string} text - The text to draw
* @param {number} x - The x coordinate to draw the text
* @param {number} y - The y coordinate to draw the text
* @param {number} width - The width of the text
* @return {number} The width of the drawn text
*/
Window_Base.prototype.drawTextEx = function(text, x, y, width) {
this.resetFontSettings();
const textState = this.createTextState(text, x, y, width);
this.processAllText(textState);
return textState.outputWidth;
};
/**
* Calculates the width needed for a given string of text. Does support text codes.
*
* @param {string} text - The text to calculate the width for
* @return {{width: number, height: number}} The width and height of the text
*/
Window_Base.prototype.textSizeEx = function(text) {
this.resetFontSettings();
const textState = this.createTextState(text, 0, 0, 0);
textState.drawing = false;
this.processAllText(textState);
return { width: textState.outputWidth, height: textState.outputHeight };
};
/**
* Creates a text state object for drawing text with text codes
*
* @param {string} text - The text to draw
* @param {number} x - The x coordinate to draw the text
* @param {number} y - The y coordinate to draw the text
* @param {number} width - The width of the text
* @return {Object} The text state object
*/
Window_Base.prototype.createTextState = function(text, x, y, width) {
const rtl = Utils.containsArabic(text);
const textState = {};
textState.text = this.convertEscapeCharacters(text);
textState.index = 0;
textState.x = rtl ? x + width : x;
textState.y = y;
textState.width = width;
textState.height = this.calcTextHeight(textState);
textState.startX = textState.x;
textState.startY = textState.y;
textState.rtl = rtl;
textState.buffer = this.createTextBuffer(rtl);
textState.drawing = true;
textState.outputWidth = 0;
textState.outputHeight = 0;
return textState;
};
/**
* Processes all text within a text state object
*
* @param {Object} textState - The text state to process
*/
Window_Base.prototype.processAllText = function(textState) {
while (textState.index < textState.text.length) {
this.processCharacter(textState);
}
this.flushTextState(textState);
};
/**
* Flushes the text state
*
* @param {Object} textState - The text state to flush
*/
Window_Base.prototype.flushTextState = function(textState) {
const text = textState.buffer;
const rtl = textState.rtl;
const width = this.textWidth(text);
const height = textState.height;
const x = rtl ? textState.x - width : textState.x;
const y = textState.y;
if (textState.drawing) {
this.contents.drawText(text, x, y, width, height);
}
textState.x += rtl ? -width : width;
textState.buffer = this.createTextBuffer(rtl);
const outputWidth = Math.abs(textState.x - textState.startX);
if (textState.outputWidth < outputWidth) {
textState.outputWidth = outputWidth;
}
textState.outputHeight = y - textState.startY + height;
};
/**
* Creates a text buffer
*
* @param {boolean} rtl - If the text is right to left (such as arabic text)
* @return {string} The buffer for text embedding either right to left or left to right
*/
Window_Base.prototype.createTextBuffer = function(rtl) {
// U+202B: RIGHT-TO-LEFT EMBEDDING
return rtl ? "\u202B" : "";
};
/**
* Converts escape code characters in text, for example \v[x]
*
* @param {string} text - The text to convert
* @return {string} Text with escape characters converted
*/
Window_Base.prototype.convertEscapeCharacters = function(text) {
/* eslint no-control-regex: 0 */
text = text.replace(/\\/g, "\x1b");
text = text.replace(/\x1b\x1b/g, "\\");
while (text.match(/\x1bV\[(\d+)\]/gi)) {
text = text.replace(/\x1bV\[(\d+)\]/gi, (_, p1) =>
$gameVariables.value(parseInt(p1))
);
}
text = text.replace(/\x1bN\[(\d+)\]/gi, (_, p1) =>
this.actorName(parseInt(p1))
);
text = text.replace(/\x1bP\[(\d+)\]/gi, (_, p1) =>
this.partyMemberName(parseInt(p1))
);
text = text.replace(/\x1bG/gi, TextManager.currencyUnit);
return text;
};
/**
* Gets an actor name from their id
*
* @param {number} n - Actor id
* @return {string} The actor's name
*/
Window_Base.prototype.actorName = function(n) {
const actor = n >= 1 ? $gameActors.actor(n) : null;
return actor ? actor.name() : "";
};
/**
* Gets an actor name from their position in the party
*
* @param {number} n - Position in the party
* @return {string} The actor's name
*/
Window_Base.prototype.partyMemberName = function(n) {
const actor = n >= 1 ? $gameParty.members()[n - 1] : null;
return actor ? actor.name() : "";
};
/**
* Processes a character in a text state at the current index of the text state
*
* @param {Object} textState - The text state object for processing a character
*/
Window_Base.prototype.processCharacter = function(textState) {
const c = textState.text[textState.index++];
if (c.charCodeAt(0) < 0x20) {
this.flushTextState(textState);
this.processControlCharacter(textState, c);
} else {
textState.buffer += c;
}
};
/**
* Processes a control character within a text state
*
* @param {Object} textState - The text state object with the character
* @param {string} c - The character to process
*/
Window_Base.prototype.processControlCharacter = function(textState, c) {
if (c === "\n") {
this.processNewLine(textState);
}
if (c === "\x1b") {
const code = this.obtainEscapeCode(textState);
this.processEscapeCharacter(code, textState);
}
};
/**
* Processing for a new line
*
* @param {Object} textState - The text state object
*/
Window_Base.prototype.processNewLine = function(textState) {
textState.x = textState.startX;
textState.y += textState.height;
textState.height = this.calcTextHeight(textState);
};
/**
* Gets an escape code
*
* @param {Object} textState - The text state object
* @return {string} The escape code, will be empty string if none
*/
Window_Base.prototype.obtainEscapeCode = function(textState) {
const regExp = /^[$.|^!><{}\\]|^[A-Z]+/i;
const arr = regExp.exec(textState.text.slice(textState.index));
if (arr) {
textState.index += arr[0].length;
return arr[0].toUpperCase();
} else {
return "";
}
};
/**
* Gets an escape param
*
* @param {Object} textState - The text state object
* @return {int|string} The escape param, will be empty string if none
*/
Window_Base.prototype.obtainEscapeParam = function(textState) {
const regExp = /^\[\d+\]/;
const arr = regExp.exec(textState.text.slice(textState.index));
if (arr) {
textState.index += arr[0].length;
return parseInt(arr[0].slice(1));
} else {
return "";
}
};
/**
* Processes an escape character in a text state
*
* @param {string} code - The escape character's code
* @param {Object} textState - The text state object
*/
Window_Base.prototype.processEscapeCharacter = function(code, textState) {
switch (code) {
case "C":
this.processColorChange(this.obtainEscapeParam(textState));
break;
case "I":
this.processDrawIcon(this.obtainEscapeParam(textState), textState);
break;
case "PX":
textState.x = this.obtainEscapeParam(textState);
break;
case "PY":
textState.y = this.obtainEscapeParam(textState);
break;
case "FS":
this.contents.fontSize = this.obtainEscapeParam(textState);
break;
case "{":
this.makeFontBigger();
break;
case "}":
this.makeFontSmaller();
break;
}
};
/**
* Processes a color change
*
* @param {number} colorIndex - The index of the color to change to
*/
Window_Base.prototype.processColorChange = function(colorIndex) {
this.changeTextColor(ColorManager.textColor(colorIndex));
};
/**
* Processes drawing an icon
*
* @param {number} iconIndex - The index of the icon to draw
* @param {Object} textState - The text state object
*/
Window_Base.prototype.processDrawIcon = function(iconIndex, textState) {
const deltaX = ImageManager.standardIconWidth - ImageManager.iconWidth;
const deltaY = ImageManager.standardIconHeight - ImageManager.iconHeight;
if (textState.drawing) {
const x = textState.x + deltaX / 2 + 2;
const y = textState.y + deltaY / 2 + 2;
this.drawIcon(iconIndex, x, y);
}
textState.x += ImageManager.standardIconWidth + 4;
};
/**
* Makes the font size bigger by 12
*/
Window_Base.prototype.makeFontBigger = function() {
if (this.contents.fontSize <= 96) {
this.contents.fontSize += 12;
}
};
/**
* Makes the font size smaller by 12
*/
Window_Base.prototype.makeFontSmaller = function() {
if (this.contents.fontSize >= 24) {
this.contents.fontSize -= 12;
}
};
/**
* Calculates the height of the text in a text state
*
* @param {Object} textState - The text state object
* @return {string} The height of the text
*/
Window_Base.prototype.calcTextHeight = function(textState) {
const lineSpacing = this.lineHeight() - $gameSystem.mainFontSize();
const lastFontSize = this.contents.fontSize;
const lines = textState.text.slice(textState.index).split("\n");
const textHeight = this.maxFontSizeInLine(lines[0]) + lineSpacing;
this.contents.fontSize = lastFontSize;
return textHeight;
};
/**
* Gets the max font size in a line of text
*
* @param {string} line - The line of text
* @return {number} The largest font size seen in the line of text
*/
Window_Base.prototype.maxFontSizeInLine = function(line) {
let maxFontSize = this.contents.fontSize;
const regExp = /\x1b({|}|FS)(\[(\d+)])?/gi;
for (;;) {
const array = regExp.exec(line);
if (!array) {
break;
}
const code = String(array[1]).toUpperCase();
if (code === "{") {
this.makeFontBigger();
} else if (code === "}") {
this.makeFontSmaller();
} else if (code === "FS") {
this.contents.fontSize = parseInt(array[3]);
}
if (this.contents.fontSize > maxFontSize) {
maxFontSize = this.contents.fontSize;
}
}
return maxFontSize;
};
/**
* Draws an icon at x/y coordinates
*
* @param {number} iconIndex - The index of the icon to draw
* @param {number} x - The x coordinate
* @param {number} y - The y coordinate
*/
Window_Base.prototype.drawIcon = function(iconIndex, x, y) {
const bitmap = ImageManager.loadSystem("IconSet");
const pw = ImageManager.iconWidth;
const ph = ImageManager.iconHeight;
const sx = (iconIndex % 16) * pw;
const sy = Math.floor(iconIndex / 16) * ph;
this.contents.blt(bitmap, sx, sy, pw, ph, x, y);
};
// prettier-ignore
/**
* Draws a face at x/y coordinates with width/height
*
* @param {string} faceName - The name of the face sheet bitmap
* @param {number} faceIndex - The index of the face on the sheet
* @param {number} x - The x coordinate
* @param {number} y - The y coordinate
* @param {number} width - The width of the face
* @param {number} height - The height of the face
*/
Window_Base.prototype.drawFace = function(
faceName, faceIndex, x, y, width, height
) {
width = width || ImageManager.standardFaceWidth;
height = height || ImageManager.standardFaceHeight;
const bitmap = ImageManager.loadFace(faceName);
const pw = ImageManager.faceWidth;
const ph = ImageManager.faceHeight;
const sw = Math.min(width, pw);
const sh = Math.min(height, ph);
const dx = Math.floor(x + Math.max(width - pw, 0) / 2);
const dy = Math.floor(y + Math.max(height - ph, 0) / 2);
const sx = Math.floor((faceIndex % 4) * pw + (pw - sw) / 2);
const sy = Math.floor(Math.floor(faceIndex / 4) * ph + (ph - sh) / 2);
this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy);
};
// prettier-ignore
/**
* Draws a character at x/y coordinates
*
* @param {string} characterName - The name of the character sheet bitmap
* @param {number} characterIndex - The index of the character on the sheet
* @param {number} x - The x coordinate
* @param {number} y - The y coordinate
*/
Window_Base.prototype.drawCharacter = function(
characterName, characterIndex, x, y
) {
const bitmap = ImageManager.loadCharacter(characterName);
const big = ImageManager.isBigCharacter(characterName);
const pw = bitmap.width / (big ? 3 : 12);
const ph = bitmap.height / (big ? 4 : 8);
const n = big ? 0: characterIndex;
const sx = ((n % 4) * 3 + 1) * pw;
const sy = Math.floor(n / 4) * 4 * ph;
this.contents.blt(bitmap, sx, sy, pw, ph, x - pw / 2, y - ph);
};
/**
* Draws an item's name with its icon
*
* @param {Object} item - The item object to draw, must at least have iconIndex and name properties
* @param {number} x - The x coordinate
* @param {number} y - The y coordinate
* @param {number} width - The width available to draw the item name
*/
Window_Base.prototype.drawItemName = function(item, x, y, width) {
if (item) {
const iconY = y + (this.lineHeight() - ImageManager.iconHeight) / 2;
const delta = ImageManager.standardIconWidth - ImageManager.iconWidth;
const textMargin = ImageManager.standardIconWidth + 4;
const itemWidth = Math.max(0, width - textMargin);
this.resetTextColor();
this.drawIcon(item.iconIndex, x + delta / 2, iconY);
this.drawText(item.name, x + textMargin, y, itemWidth);
}
};
/**
* Draws a currency value
*
* @param {number} value - The amount of currency
* @param {string} unit - The currency unit
* @param {number} x - The x coordinate
* @param {number} y - The y coordinate
* @param {number} width - The width available to draw the item name
*/
Window_Base.prototype.drawCurrencyValue = function(value, unit, x, y, width) {
const unitWidth = Math.min(80, this.textWidth(unit));
this.resetTextColor();
this.drawText(value, x, y, width - unitWidth - 6, "right");
this.changeTextColor(ColorManager.systemColor());
this.drawText(unit, x + width - unitWidth, y, unitWidth, "right");
};
/**
* Sets the background type of the window
*
* @param {number} type - The type of background (0 = normal, 1 = dim, 2 = transparent)
*/
Window_Base.prototype.setBackgroundType = function(type) {
if (type === 0) {
this.opacity = 255;
} else {
this.opacity = 0;
}
if (type === 1) {
this.showBackgroundDimmer();
} else {
this.hideBackgroundDimmer();
}
};
/**
* Shows the background dimmer sprite
*/
Window_Base.prototype.showBackgroundDimmer = function() {
if (!this._dimmerSprite) {
this.createDimmerSprite();
}
const bitmap = this._dimmerSprite.bitmap;
if (bitmap.width !== this.width || bitmap.height !== this.height) {
this.refreshDimmerBitmap();
}
this._dimmerSprite.visible = true;
this.updateBackgroundDimmer();
};
/**
* Creates the background dimmer sprite
*/
Window_Base.prototype.createDimmerSprite = function() {
this._dimmerSprite = new Sprite();
this._dimmerSprite.bitmap = new Bitmap(0, 0);
this._dimmerSprite.x = -4;
this.addChildToBack(this._dimmerSprite);
};
/**
* Hides the background dimmer sprite
*/
Window_Base.prototype.hideBackgroundDimmer = function() {
if (this._dimmerSprite) {
this._dimmerSprite.visible = false;
}
};
/**
* Updates the background dimmer sprite
*/
Window_Base.prototype.updateBackgroundDimmer = function() {
if (this._dimmerSprite) {
this._dimmerSprite.opacity = this.openness;
}
};
/**
* Refreshes the background dimmer sprite
*/
Window_Base.prototype.refreshDimmerBitmap = function() {
if (this._dimmerSprite) {
const bitmap = this._dimmerSprite.bitmap;
const w = this.width > 0 ? this.width + 8 : 0;
const h = this.height;
const m = this.padding;
const c1 = ColorManager.dimColor1();
const c2 = ColorManager.dimColor2();
bitmap.resize(w, h);
bitmap.gradientFillRect(0, 0, w, m, c2, c1, true);
bitmap.fillRect(0, m, w, h - m * 2, c1);
bitmap.gradientFillRect(0, h - m, w, m, c1, c2, true);
this._dimmerSprite.setFrame(0, 0, w, h);
}
};
/**
* Plays the cursor sound effect
*/
Window_Base.prototype.playCursorSound = function() {
SoundManager.playCursor();
};
/**
* Plays the ok sound effect
*/
Window_Base.prototype.playOkSound = function() {
SoundManager.playOk();
};
/**
* Plays the buzzer sound effect
*/
Window_Base.prototype.playBuzzerSound = function() {
SoundManager.playBuzzer();
};