diff --git a/src/Core/Picture2D.ts b/src/Core/Picture2D.ts index bb966296..515099f5 100644 --- a/src/Core/Picture2D.ts +++ b/src/Core/Picture2D.ts @@ -189,14 +189,10 @@ class Picture2D extends Bitmap { { if (!this.empty && this.loaded && sw > 0 && sh > 0) { // Default values - x = x === null ? this.x : (positionResize ? ScreenResolution - .getScreenX(x) : x); - y = y === null ? this.y : (positionResize ? ScreenResolution - .getScreenY(y) : y); - w = w === null ? this.w * this.zoom : (this.stretch ? ScreenResolution - .getScreenX(w) : ScreenResolution.getScreenMinXY(w)); - h = h === null ? this.h * this.zoom : (this.stretch ? ScreenResolution - .getScreenY(h) : ScreenResolution.getScreenMinXY(h)); + x = x === null ? (this.stretch ? ScreenResolution.getScreenX(this.oX) : this.x) : (positionResize ? ScreenResolution.getScreenX(x) : x); + y = y === null ? (this.stretch ? ScreenResolution.getScreenY(this.oY) : this.y) : (positionResize ? ScreenResolution.getScreenY(y) : y); + w = w === null ? (this.stretch ? ScreenResolution.getScreenX(this.oW * this.zoom) : this.oW * this.zoom) : ScreenResolution.getScreenMinXY(w); + h = h === null ? (this.stretch ? ScreenResolution.getScreenY(this.oH * this.zoom) : this.oH * this.zoom) : ScreenResolution.getScreenMinXY(h); // Draw the image according to all parameters let angle = this.angle * Math.PI / 180; diff --git a/src/Core/Status.ts b/src/Core/Status.ts index 546202e4..cfbe339d 100644 --- a/src/Core/Status.ts +++ b/src/Core/Status.ts @@ -58,8 +58,7 @@ class Status { let totalWidth = l * ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize); let s: Status; if (l > 1) { - totalWidth += (l - 1) * ScreenResolution.getScreenMinXY(Constants - .MEDIUM_SPACE); + totalWidth += (l - 1) * ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE); } let xOffset: number = 0; switch (align) { @@ -74,10 +73,10 @@ class Status { for (let i = 0, l = statusList.length; i < l; i++) { s = statusList[i]; xOffset += ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize); - s.draw(x - totalWidth + xOffset + ScreenResolution.getScreenMinXY(i - * Constants.MEDIUM_SPACE) - ScreenResolution.getScreenMinXY(Datas - .Systems.iconsSize), y - ScreenResolution.getScreenMinXY(Datas - .Systems.iconsSize)); + s.draw( + x - totalWidth + xOffset + ScreenResolution.getScreenMinXY(i * Constants.MEDIUM_SPACE) - ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize), + y - ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize) + ); } } @@ -123,11 +122,17 @@ class Status { * @param {number} y - The y position */ draw(x: number, y: number) { - this.picture.draw({ x: x, y: y, sx: this.system.pictureIndexX * Datas - .Systems.iconsSize, sy: this.system.pictureIndexY * Datas.Systems - .iconsSize, sw: Datas.Systems.iconsSize, sh: Datas.Systems.iconsSize, - w: ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize), h: - ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize) }); + this.picture.draw( + { + x: x, + y: y, + sx: this.system.pictureIndexX * Datas.Systems.iconsSize, + sy: this.system.pictureIndexY * Datas.Systems.iconsSize, + sw: Datas.Systems.iconsSize, + sh: Datas.Systems.iconsSize, + w: Datas.Systems.iconsSize, + h: Datas.Systems.iconsSize + }); } } diff --git a/src/Core/WindowBox.ts b/src/Core/WindowBox.ts index 143851de..6ae1ac48 100644 --- a/src/Core/WindowBox.ts +++ b/src/Core/WindowBox.ts @@ -1,12 +1,12 @@ /* - RPG Paper Maker Copyright (C) 2017-2023 Wano + RPG Paper Maker Copyright (C) 2017-2023 Wano - RPG Paper Maker engine is under proprietary license. - This source code is also copyrighted. + RPG Paper Maker engine is under proprietary license. + This source code is also copyrighted. - Use Commercial edition for commercial use of your games. - See RPG Paper Maker EULA here: - http://rpg-paper-maker.com/index.php/eula. + Use Commercial edition for commercial use of your games. + See RPG Paper Maker EULA here: + http://rpg-paper-maker.com/index.php/eula. */ import { Bitmap } from "./Bitmap"; @@ -19,39 +19,40 @@ import { Platform, ScreenResolution, Utils } from "../Common"; * * @interface WindowBoxOptions */ -interface WindowBoxOptions { - /** - * The contents displayed inside the window. - * - * @type {Graphic.Base} - * @default null - * @memberof WindowBoxOption - */ - content?: Graphic.Base; - /** - * The window padding - * - * @type {number[]} - * @default [0,0,0,0] - * @memberof WindowBoxOption - */ - padding?: number[]; - /** - * If enabled the contents will be cut according to the padding size. - * - * @type {boolean} - * @default true - * @memberof WindowBoxOption - */ - limitContent?: boolean; - /** - * Indicate if selected. - * - * @type {boolean} - * @default false - * @memberof WindowBoxOption - */ - selected?: boolean; +interface WindowBoxOptions +{ + /** + * The contents displayed inside the window. + * + * @type {Graphic.Base} + * @default null + * @memberof WindowBoxOption + */ + content?: Graphic.Base; + /** + * The window padding + * + * @type {number[]} + * @default [0,0,0,0] + * @memberof WindowBoxOption + */ + padding?: number[]; + /** + * If enabled the contents will be cut according to the padding size. + * + * @type {boolean} + * @default true + * @memberof WindowBoxOption + */ + limitContent?: boolean; + /** + * Indicate if selected. + * + * @type {boolean} + * @default false + * @memberof WindowBoxOption + */ + selected?: boolean; } /** @@ -60,171 +61,194 @@ interface WindowBoxOptions { * @class WindowBox * @extends {Bitmap} */ -class WindowBox extends Bitmap { - - public static readonly NONE_PADDING = [0, 0, 0, 0]; - public static readonly VERY_SMALL_PADDING_BOX = [5, 5, 5, 5]; - public static readonly SMALL_PADDING_BOX = [10, 10, 10, 10]; - public static readonly MEDIUM_PADDING_BOX = [20, 20, 20, 20]; - public static readonly HUGE_PADDING_BOX = [30, 30, 30, 30]; - public static readonly DIALOG_PADDING_BOX = [30, 50, 30, 50]; - public static readonly SMALL_SLOT_PADDING = [10, 5, 10, 5]; - public static readonly SMALL_SLOT_HEIGHT = 30; - public static readonly LARGE_SLOT_WIDTH = 250; - public static readonly MEDIUM_SLOT_WIDTH = 200; - public static readonly SMALL_SLOT_WIDTH = 100; - public static readonly MEDIUM_SLOT_HEIGHT = 40; - public static readonly LARGE_SLOT_HEIGHT = 60; - - public content: Graphic.Base; - public padding: number[]; - public limitContent: boolean; - public bordersOpacity: number; - public backgroundOpacity: number; - public selected: boolean; - public bordersVisible: boolean; - public contentDimension: number[]; - public windowDimension: number[]; - - /** - * - * @param {number} x - The x coordinates - * @param {number} y - The y coordinates - * @param {number} w - The width coordinates - * @param {number} h - The height coordinates - * @param {WindowBoxOption} [options={}] - the window options - * @memberof WindowBox - */ - constructor(x: number, y: number, w: number, h: number, options: WindowBoxOptions = {}) { - super(x, y, w, h); - this.content = Utils.defaultValue(options.content, null); - this.padding = Utils.defaultValue(options.padding, [0, 0, 0, 0]); - this.limitContent = Utils.defaultValue(options.limitContent, true); - this.selected = Utils.defaultValue(options.selected, false); - this.updateDimensions(); - this.bordersOpacity = 1; - this.backgroundOpacity = 1; - this.bordersVisible = true; - } - - /** - * Set the x value. - * @param {number} x - The x value - */ - setX(x: number) { - super.setX(x); - if (this.padding) { - this.updateDimensions(); - } - } - - /** - * Set the y value. - * @param {number} y - The y value - */ - setY(y: number) { - super.setY(y); - if (this.padding) { - this.updateDimensions(); - } - } - - /** - * Set the w value. - * @param {number} w - The w value - */ - setW(w: number) { - super.setW(w); - if (this.padding) { - this.updateDimensions(); - } - } - - /** - * Set the h value. - * @param {number} h - The h value - */ - setH(h: number) { - super.setH(h); - if (this.padding) { - this.updateDimensions(); - } - } - - /** - * Update the content and window dimensions. - */ - updateDimensions() { - // Setting content dimensions - this.contentDimension = [ - ScreenResolution.getScreenX(this.oX + this.padding[0]), - ScreenResolution.getScreenY(this.oY + this.padding[1]), - ScreenResolution.getScreenX(this.oW - (2 * this.padding[2])), - ScreenResolution.getScreenY(this.oH - (2 * this.padding[3])) - ]; - - // Adjusting dimensions - this.windowDimension = [ - this.oX, - this.oY, - this.oW, - this.oH - ]; - } - - /** - * Update the content. - */ - update() { - if (this.content) { - this.content.update(); - } - } - - /** - * Draw the window. - * @param {boolean} [isChoice=false] - Indicate if this window box is used - * for a window choices - * @param {number[]} [windowDimension - = this.windowDimension] Dimensions - * of the window - * @param {number[]} [contentDimension - = this.contentDimension] Dimension - * of content - */ - draw(isChoice: boolean = false, windowDimension: number[] = this - .windowDimension, contentDimension: number[] = this.contentDimension) { - // Content behind - if (this.content) { - this.content.drawBehind(contentDimension[0], contentDimension[1], - contentDimension[2], contentDimension[3]); - } - - // Draw box - Datas.Systems.getCurrentWindowSkin().drawBox(windowDimension, this - .selected, this.bordersVisible); - - // Draw content - if (this.content) { - if (!isChoice && this.limitContent) { - Platform.ctx.save(); - Platform.ctx.beginPath(); - Platform.ctx.rect(contentDimension[0], contentDimension[1] - - ScreenResolution.getScreenY(this.padding[3] / 2), - contentDimension[2], contentDimension[3] + ScreenResolution - .getScreenY(this.padding[3])); - Platform.ctx.clip(); - } - if (isChoice) { - this.content.drawChoice(contentDimension[0], contentDimension[1], - contentDimension[2], contentDimension[3]); - } else { - this.content.draw(contentDimension[0], contentDimension[1], - contentDimension[2], contentDimension[3]); - } - if (!isChoice && this.limitContent) { - Platform.ctx.restore(); - } - } - } +class WindowBox extends Bitmap +{ + public static readonly NONE_PADDING = [0, 0, 0, 0]; + public static readonly VERY_SMALL_PADDING_BOX = [5, 5, 5, 5]; + public static readonly SMALL_PADDING_BOX = [10, 10, 10, 10]; + public static readonly MEDIUM_PADDING_BOX = [20, 20, 20, 20]; + public static readonly HUGE_PADDING_BOX = [30, 30, 30, 30]; + public static readonly DIALOG_PADDING_BOX = [30, 50, 30, 50]; + public static readonly SMALL_SLOT_PADDING = [10, 5, 10, 5]; + public static readonly SMALL_SLOT_HEIGHT = 30; + public static readonly LARGE_SLOT_WIDTH = 250; + public static readonly MEDIUM_SLOT_WIDTH = 200; + public static readonly SMALL_SLOT_WIDTH = 100; + public static readonly MEDIUM_SLOT_HEIGHT = 40; + public static readonly LARGE_SLOT_HEIGHT = 60; + + public content: Graphic.Base; + public padding: number[]; + public limitContent: boolean; + public bordersOpacity: number; + public backgroundOpacity: number; + public selected: boolean; + public bordersVisible: boolean; + public contentDimension: number[]; + public windowDimension: number[]; + + /** + * + * @param {number} x - The x coordinates + * @param {number} y - The y coordinates + * @param {number} w - The width coordinates + * @param {number} h - The height coordinates + * @param {WindowBoxOption} [options={}] - the window options + * @memberof WindowBox + */ + constructor(x: number, y: number, w: number, h: number, options: WindowBoxOptions = {}) + { + super(x, y, w, h); + this.content = Utils.defaultValue(options.content, null); + this.padding = Utils.defaultValue(options.padding, [0, 0, 0, 0]); + this.limitContent = Utils.defaultValue(options.limitContent, true); + this.selected = Utils.defaultValue(options.selected, false); + this.updateDimensions(); + this.bordersOpacity = 1; + this.backgroundOpacity = 1; + this.bordersVisible = true; + } + + /** + * Set the x value. + * @param {number} x - The x value + */ + setX(x: number) + { + super.setX(x); + if (this.padding) + this.updateDimensions(); + } + + /** + * Set the y value. + * @param {number} y - The y value + */ + setY(y: number) + { + super.setY(y); + if (this.padding) + this.updateDimensions(); + } + + /** + * Set the w value. + * @param {number} w - The w value + */ + setW(w: number) + { + super.setW(w); + if (this.padding) + this.updateDimensions(); + } + + /** + * Set the h value. + * @param {number} h - The h value + */ + setH(h: number) + { + super.setH(h); + if (this.padding) + this.updateDimensions(); + } + + /** + * Update the content and window dimensions. + */ + updateDimensions() + { + // Setting content dimensions + this.contentDimension = [ + this.oX + this.padding[0], + this.oY + this.padding[1], + this.oW - (2 * this.padding[2]), + this.oH - (2 * this.padding[3]) + ]; + + // Adjusting dimensions + this.windowDimension = [ + this.oX, + this.oY, + this.oW, + this.oH + ]; + } + + /** + * Update the content. + */ + update() + { + if (this.content) + this.content.update(); + } + + /** + * Draw the window. + * @param {boolean} [isChoice=false] - Indicate if this window box is used + * for a window choices + * @param {number[]} [windowDimension - = this.windowDimension] Dimensions + * of the window + * @param {number[]} [contentDimension - = this.contentDimension] Dimension + * of content + */ + draw(isChoice: boolean = false, windowDimension: number[] = this.windowDimension, contentDimension: number[] = this.contentDimension) + { + // Content behind + if (this.content) + { + this.content.drawBehind( + ScreenResolution.getScreenX(contentDimension[0]), + ScreenResolution.getScreenY(contentDimension[1]), + ScreenResolution.getScreenX(contentDimension[2]), + ScreenResolution.getScreenY(contentDimension[3]) + ); + } + + // Draw box + if (!!this.customWindowSkin) + this.customWindowSkin.drawBox(windowDimension, this.selected, this.bordersVisible) + else + Datas.Systems.getCurrentWindowSkin().drawBox(windowDimension, this.selected, this.bordersVisible); + + // Draw content + if (this.content) + { + if (!isChoice && this.limitContent) + { + Platform.ctx.save(); + Platform.ctx.beginPath(); + Platform.ctx.rect( + ScreenResolution.getScreenX(contentDimension[0]), + ScreenResolution.getScreenY(contentDimension[1] - this.padding[3] / 2), + ScreenResolution.getScreenX(contentDimension[2]), + ScreenResolution.getScreenY(contentDimension[3] + this.padding[3]) + ); + Platform.ctx.clip(); + } + if (isChoice) + { + this.content.drawChoice( + ScreenResolution.getScreenX(contentDimension[0]), + ScreenResolution.getScreenY(contentDimension[1]), + ScreenResolution.getScreenX(contentDimension[2]), + ScreenResolution.getScreenY(contentDimension[3]) + ); + } + else + { + this.content.draw( + ScreenResolution.getScreenX(contentDimension[0]), + ScreenResolution.getScreenY(contentDimension[1]), + ScreenResolution.getScreenX(contentDimension[2]), + ScreenResolution.getScreenY(contentDimension[3]) + ); + } + if (!isChoice && this.limitContent) + Platform.ctx.restore(); + } + } } export { WindowBox, WindowBoxOptions } \ No newline at end of file diff --git a/src/Core/WindowChoices.ts b/src/Core/WindowChoices.ts index 0a139430..57b922af 100644 --- a/src/Core/WindowChoices.ts +++ b/src/Core/WindowChoices.ts @@ -532,7 +532,7 @@ class WindowChoices extends Bitmap { this.isMouseInArrowDown = false; this.isMouseInArrowUp = false; // If inside the main window - if (this.currentSelectedIndex !== -1 && this.isInside(x, y)) { + if (this.currentSelectedIndex !== -1 && this.isInside(ScreenResolution.getScreenXReverse(x), ScreenResolution.getScreenYReverse(y))) { let index: number; // Check which window if (this.orientation === OrientationWindow.Horizontal) { @@ -616,8 +616,7 @@ class WindowChoices extends Bitmap { let index: number; for (let i = 0; i < this.size; i++) { index = i + this.currentSelectedIndex - offset; - this.listWindows[index].draw(true, this.listWindows[i] - .windowDimension, this.listWindows[i].contentDimension); + this.listWindows[index].draw(true, this.listWindows[i].windowDimension, this.listWindows[i].contentDimension); } // Draw arrows @@ -628,8 +627,7 @@ class WindowChoices extends Bitmap { if (this.currentSelectedIndex - offset > 0) { ws.drawArrowUp(arrowX, this.oY - arrowHeight - 1); } - if (this.currentSelectedIndex - offset < this.listWindows.length - this - .nbItemsMax) { + if (this.currentSelectedIndex - offset < this.listWindows.length - this.nbItemsMax) { ws.drawArrowDown(arrowX, this.oY + this.oH + 1); } } diff --git a/src/EventCommand/MoveObject.ts b/src/EventCommand/MoveObject.ts index bb3fda44..f3ddb988 100644 --- a/src/EventCommand/MoveObject.ts +++ b/src/EventCommand/MoveObject.ts @@ -722,7 +722,7 @@ class MoveObject extends Base { Record): Orientation | boolean { if (object) { - object.lookAt((Orientation.North + (this.isCameraOrientation ? Scene.Map.current.orientation + 2 : 0)) % 4); + object.lookAt((Orientation.North + (this.isCameraOrientation ? Scene.Map.current.orientation : 0)) % 4); return true; } return Orientation.North; @@ -739,7 +739,7 @@ class MoveObject extends Base { Record): Orientation | boolean { if (object) { - object.lookAt((Orientation.South + (this.isCameraOrientation ? Scene.Map.current.orientation + 2 : 0)) % 4); + object.lookAt((Orientation.South + (this.isCameraOrientation ? Scene.Map.current.orientation : 0)) % 4); return true; } return Orientation.South; @@ -756,7 +756,7 @@ class MoveObject extends Base { Record): Orientation | boolean { if (object) { - object.lookAt((Orientation.West + (this.isCameraOrientation ? Scene.Map.current.orientation + 2 : 0)) % 4); + object.lookAt((Orientation.West + (this.isCameraOrientation ? Scene.Map.current.orientation : 0)) % 4); return true; } return Orientation.West; @@ -773,7 +773,7 @@ class MoveObject extends Base { Record): Orientation | boolean { if (object) { - object.lookAt((Orientation.East + (this.isCameraOrientation ? Scene.Map.current.orientation + 2 : 0)) % 4); + object.lookAt((Orientation.East + (this.isCameraOrientation ? Scene.Map.current.orientation : 0)) % 4); return true; } return Orientation.East; diff --git a/src/EventCommand/ShowText.ts b/src/EventCommand/ShowText.ts index 88186a3b..1e65546a 100644 --- a/src/EventCommand/ShowText.ts +++ b/src/EventCommand/ShowText.ts @@ -132,6 +132,8 @@ class ShowText extends Base { } ( this.windowInterlocutor.content).setText(this .interlocutor.getValue()); + this.windowMain.updateDimensions(); + this.windowMain.content.update(); return 0; } diff --git a/src/Graphic/Message.ts b/src/Graphic/Message.ts index be8a84da..3075ed84 100644 --- a/src/Graphic/Message.ts +++ b/src/Graphic/Message.ts @@ -504,7 +504,7 @@ class Message extends Graphic.Base { offsetX = 0; break; case Align.Center: - offsetX = (w - this.totalWidths[j]) / 2; + offsetX = (w - this.totalWidths[j] - (this.faceset.empty ? 0 : ScreenResolution.getScreenX(Datas.Systems.facesetScalingWidth))) / 2; break; case Align.Right: offsetX = w - this.totalWidths[j]; diff --git a/src/Graphic/Player.ts b/src/Graphic/Player.ts index 8458f54e..8ccae75d 100644 --- a/src/Graphic/Player.ts +++ b/src/Graphic/Player.ts @@ -105,7 +105,7 @@ class Player extends Base { } } for (graphic of this.listStatistics) { - graphic.maxStatNamesLength = this.maxStatNamesLength; + graphic.maxStatNamesLength = this.maxStatNamesLength / ScreenResolution.getScreenMinXY(1); } // Faceset @@ -235,22 +235,17 @@ class Player extends Base { let hBattler = this.battler.h / Datas.Systems.battlersColumns; let owBattler = this.battler.oW / Datas.Systems.battlersFrames; let ohBattler = this.battler.oH / Datas.Systems.battlersColumns; - this.battlerRect.setCoords(x, y + yOffset - (hBattler * coef) - - ScreenResolution.getScreenMinXY(15), wBattler * coef, hBattler * coef); - this.battler.draw({ x: this.battlerRect.x, y: this.battlerRect.y, w: - owBattler * coef, h: ohBattler * coef, sx: this.battlerFrame.value * owBattler, - sy: 0, sw: owBattler, sh: ohBattler }); + this.battlerRect.setCoords(x, y + yOffset - ScreenResolution.getScreenY(ohBattler * coef) - ScreenResolution.getScreenMinXY(15), wBattler * coef, hBattler * coef); + this.battler.draw({ x: this.battlerRect.x, y: this.battlerRect.y, w: owBattler * coef, h: ohBattler * coef, sx: this.battlerFrame.value * owBattler, sy: 0, sw: owBattler, sh: ohBattler }); // Stats let xOffset = this.graphicName.textWidth; if (this.graphicStatShort) { - this.graphicStatShort.draw(x, y + yOffset - ScreenResolution - .getScreenMinXY(15), 0, 0); + this.graphicStatShort.draw(x, y + yOffset - ScreenResolution.getScreenMinXY(15), 0, 0); } if (this.displayNameLevel) { this.graphicName.draw(x, y + yOffset, 0, 0); - xOffset = this.graphicName.textWidth + ScreenResolution - .getScreenMinXY(Constants.MEDIUM_SPACE); + xOffset = this.graphicName.textWidth + ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE); this.graphicLevelName.draw(x + xOffset, y + yOffset, 0, 0); xOffset += this.graphicLevelName.textWidth; this.graphicLevel.draw(x + xOffset, y + yOffset, 0, 0); @@ -279,15 +274,21 @@ class Player extends Base { let ohBattler = this.battler.oH / Datas.Systems.battlersColumns; // Battler - this.battler.draw({ x: x + (ScreenResolution.getScreenMinXY(80) - - (wBattler * coef)) / 2, y: y, w: owBattler * coef, h: ohBattler - * coef, sx: this.battlerFrame.value * owBattler, sy: 0, sw: owBattler, - sh: ohBattler }); + this.battler.draw( + { + x: x + (80 - ScreenResolution.getScreenXReverse(owBattler * coef)) / 2, + y: y, + w: owBattler * coef, + h: ohBattler * coef, + sx: this.battlerFrame.value * owBattler, + sy: 0, + sw: owBattler, + sh: ohBattler + }); // Stats this.graphicName.draw(xCharacter, yName, 0, 0); - let xLevelName = xCharacter + this.graphicName.textWidth + ScreenResolution - .getScreenMinXY(Constants.MEDIUM_SPACE); + let xLevelName = xCharacter + this.graphicName.textWidth + ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE); this.graphicLevelName.draw(xLevelName, yName, 0, 0); let xLevel = xLevelName + this.graphicLevelName.textWidth; this.graphicLevel.draw(xLevel, yName, 0, 0); @@ -308,9 +309,7 @@ class Player extends Base { // Level up if (this.player.levelingUp) { - this.graphicLevelUp.draw(xLevel + this.graphicLevel.textWidth + - ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE), yName, - 0, 0); + this.graphicLevelUp.draw(xLevel + this.graphicLevel.textWidth + ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE), yName, 0, 0); } let yClass = yName + ScreenResolution.getScreenMinXY(15); @@ -318,9 +317,7 @@ class Player extends Base { let yExp = yClass + ScreenResolution.getScreenMinXY(29); if (this.graphicExpName !== null) { this.graphicExpName.draw(xCharacter, yExp, 0, 0); - this.graphicExp.draw(xCharacter + this.graphicExpName.textWidth + - ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE), yExp, - 0, 0); + this.graphicExp.draw(xCharacter + this.graphicExpName.textWidth + ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE), yExp, 0, 0); } } @@ -334,12 +331,10 @@ class Player extends Base { draw(x: number, y: number, w: number, h: number) { let wName = this.graphicName.textWidth; let wLevelName = this.graphicLevelName.textWidth; - let xLevelName = x + wName + ScreenResolution.getScreenMinXY(Constants - .MEDIUM_SPACE); + let xLevelName = x + wName + ScreenResolution.getScreenMinXY(Constants.MEDIUM_SPACE); let xLevel = xLevelName + wLevelName; let firstLineLength = xLevel + this.graphicLevel.textWidth; - let xOffset = this.reverse ? ScreenResolution.getScreenMinXY(Datas.Systems - .facesetScalingWidth) : 0; + let xOffset = this.reverse ? ScreenResolution.getScreenMinXY(Datas.Systems.facesetScalingWidth) : 0; // Name, level, status let yName = y + ScreenResolution.getScreenMinXY(10); @@ -358,11 +353,15 @@ class Player extends Base { } // Faceset - this.faceset.draw({ sx: this.player.getFacesetIndexX() * Datas.Systems - .facesetsSize, sy: this.player.getFacesetIndexY() * Datas.Systems - .facesetsSize, sw: Datas.Systems.facesetsSize, sh: Datas.Systems - .facesetsSize, w: Datas.Systems.facesetScalingWidth, h: Datas.Systems - .facesetScalingHeight }); + this.faceset.draw( + { + sx: this.player.getFacesetIndexX() * Datas.Systems.facesetsSize, + sy: this.player.getFacesetIndexY() * Datas.Systems.facesetsSize, + sw: Datas.Systems.facesetsSize, + sh: Datas.Systems.facesetsSize, + w: Datas.Systems.facesetScalingWidth, + h: Datas.Systems.facesetScalingHeight + }); } } diff --git a/src/Graphic/Save.ts b/src/Graphic/Save.ts index abddd747..27d877b4 100644 --- a/src/Graphic/Save.ts +++ b/src/Graphic/Save.ts @@ -83,9 +83,12 @@ class Save extends Base { } else { this.graphicTimer.draw(x, y, w, ScreenResolution.getScreenMinXY(20)); for (let i = 0, l = this.graphicPlayers.length; i < l; i++) { - this.graphicPlayers[i].drawCharacter(x + ScreenResolution - .getScreenMinXY(5 + (i * 115)), y + ScreenResolution - .getScreenMinXY(20), w, h); + this.graphicPlayers[i].drawCharacter( + x + ScreenResolution.getScreenMinXY(5 + (i * 115)), + y + ScreenResolution.getScreenMinXY(20), + w, + h + ); } } } diff --git a/src/Graphic/Statistic.ts b/src/Graphic/Statistic.ts index 6908bb59..249f7272 100644 --- a/src/Graphic/Statistic.ts +++ b/src/Graphic/Statistic.ts @@ -1,12 +1,12 @@ /* - RPG Paper Maker Copyright (C) 2017-2023 Wano + RPG Paper Maker Copyright (C) 2017-2023 Wano - RPG Paper Maker engine is under proprietary license. - This source code is also copyrighted. + RPG Paper Maker engine is under proprietary license. + This source code is also copyrighted. - Use Commercial edition for commercial use of your games. - See RPG Paper Maker EULA here: - http://rpg-paper-maker.com/index.php/eula. + Use Commercial edition for commercial use of your games. + See RPG Paper Maker EULA here: + http://rpg-paper-maker.com/index.php/eula. */ import { Graphic, Core, Datas, System } from "../index"; @@ -21,98 +21,106 @@ import { Utils, Constants, Enum, ScreenResolution } from "../Common"; */ class Statistic extends Base { - public player: Core.Player; - public statistic: System.Statistic; - public graphicName: Graphic.Text; - public graphicValue: Graphic.Text; - public pictureBar: System.Picture; - public maxStatNamesLength: number; + public player: Core.Player; + public statistic: System.Statistic; + public graphicName: Graphic.Text; + public graphicValue: Graphic.Text; + public pictureBar: System.Picture; + public maxStatNamesLength: number; - constructor(player: Core.Player, statistic: System.Statistic, offsetStat?: number) { - super(); - this.player = player; - this.statistic = statistic; - this.graphicName = new Graphic.Text(statistic.name() + Constants - .STRING_COLON); - this.maxStatNamesLength = 0; - if (Utils.isUndefined(offsetStat)) { - this.graphicName.measureText(); - if (this.graphicName.textWidth > this.maxStatNamesLength) { - this.maxStatNamesLength = this.graphicName.textWidth; - } - } else { - this.maxStatNamesLength = offsetStat; - } - let txt = Utils.numToString(this.player[statistic.abbreviation]); - if (!statistic.isFix) { - txt += Constants.STRING_SLASH + this.player[statistic.getMaxAbbreviation()]; - this.pictureBar = Datas.Pictures.get(Enum.PictureKind.Bars, this - .statistic.pictureBarID); - } - this.graphicValue = new Graphic.Text(txt); - } + constructor(player: Core.Player, statistic: System.Statistic, offsetStat?: number) { + super(); + this.player = player; + this.statistic = statistic; + this.graphicName = new Graphic.Text(statistic.name() + Constants + .STRING_COLON); + this.maxStatNamesLength = 0; + if (Utils.isUndefined(offsetStat)) { + this.graphicName.measureText(); + if (this.graphicName.textWidth > this.maxStatNamesLength) { + this.maxStatNamesLength = this.graphicName.textWidth; + } + } else { + this.maxStatNamesLength = offsetStat; + } + let txt = Utils.numToString(this.player[statistic.abbreviation]); + if (!statistic.isFix) { + txt += Constants.STRING_SLASH + this.player[statistic.getMaxAbbreviation()]; + this.pictureBar = Datas.Pictures.get(Enum.PictureKind.Bars, this + .statistic.pictureBarID); + } + this.graphicValue = new Graphic.Text(txt); + } - /** - * Set the font size and the final font. - * @param {number} fontSize - The new font size - */ - setFontSize(fontSize: number) { - this.graphicName.setFontSize(fontSize); - this.graphicValue.setFontSize(fontSize); - } + /** + * Set the font size and the final font. + * @param {number} fontSize - The new font size + */ + setFontSize(fontSize: number) { + this.graphicName.setFontSize(fontSize); + this.graphicValue.setFontSize(fontSize); + } - /** - * Update the graphics - */ - update() { - let txt = Utils.numToString(this.player[this.statistic.abbreviation]); - if (!this.statistic.isFix) { - txt += Constants.STRING_SLASH + this.player[this.statistic.getMaxAbbreviation()]; - } - this.graphicValue.setText(txt); - } + /** + * Update the graphics + */ + update() { + let txt = Utils.numToString(this.player[this.statistic.abbreviation]); + if (!this.statistic.isFix) { + txt += Constants.STRING_SLASH + this.player[this.statistic.getMaxAbbreviation()]; + } + this.graphicValue.setText(txt); + } - /** - * Drawing statistic bar. - * @param {number} x - The x position to draw graphic - * @param {number} y - The y position to draw graphic - * @param {number} w - The width dimention to draw graphic - * @param {number} h - The height dimention to draw graphic - */ - drawChoice(x: number, y: number, w: number, h: number) { - this.draw(x, y, w, h); - } + /** + * Drawing statistic bar. + * @param {number} x - The x position to draw graphic + * @param {number} y - The y position to draw graphic + * @param {number} w - The width dimention to draw graphic + * @param {number} h - The height dimention to draw graphic + */ + drawChoice(x: number, y: number, w: number, h: number) { + this.draw(x, y, w, h); + } - /** - * Drawing statistic bar. - * @param {number} x - The x position to draw graphic - * @param {number} y - The y position to draw graphic - * @param {number} w - The width dimention to draw graphic - * @param {number} h - The height dimention to draw graphic - */ - draw(x: number, y: number, w: number, h: number) { - let height = 0; - let offset = 0; - if (this.pictureBar && this.pictureBar.picture) { - this.pictureBar.picture.draw({x: x, y: y, sw: this.pictureBar.picture - .oW / 2, w: this.pictureBar.picture.oW / 2}); - let percent = this.player[this.statistic.abbreviation] / this.player - [this.statistic.getMaxAbbreviation()]; - this.pictureBar.picture.draw({x: x + ScreenResolution.getScreenMinXY( - this.pictureBar.borderLeft) , y: y, sx: (this.pictureBar.picture - .oW / 2) + this.pictureBar.borderLeft, sw: Math.ceil(((this - .pictureBar.picture.oW / 2) - (this.pictureBar.borderLeft + this - .pictureBar.borderRight)) * percent), w: Math.ceil(((this - .pictureBar.picture.oW / 2) - (this.pictureBar.borderLeft + this - .pictureBar.borderRight)) * percent)}); - height = this.pictureBar.picture.h; - offset = ScreenResolution.getScreenY(-5); - } - y += (height / 2) + offset; - this.graphicName.draw(x, y, 0, 0); - this.graphicValue.draw(x + this.maxStatNamesLength + ScreenResolution - .getScreenMinXY(10), y, 0, 0); - } + /** + * Drawing statistic bar. + * @param {number} x - The x position to draw graphic + * @param {number} y - The y position to draw graphic + * @param {number} w - The width dimention to draw graphic + * @param {number} h - The height dimention to draw graphic + */ + draw(x: number, y: number, w: number, h: number) { + let height = 0; + let offset = 0; + if (this.pictureBar && this.pictureBar.picture) + { + this.pictureBar.picture.draw( + { + x: x, + y: y, + sw: this.pictureBar.picture.oW / 2, + w: this.pictureBar.picture.oW / 2, + h: this.pictureBar.picture.h + }); + let percent = this.player[this.statistic.abbreviation] / this.player + [this.statistic.getMaxAbbreviation()]; + this.pictureBar.picture.draw( + { + x: x + ScreenResolution.getScreenMinXY(this.pictureBar.borderLeft), + y: y, + sx: (this.pictureBar.picture.oW / 2) + this.pictureBar.borderLeft, + sw: Math.ceil(((this.pictureBar.picture.oW / 2) - (this.pictureBar.borderLeft + this.pictureBar.borderRight)) * percent), + w: Math.ceil(((this.pictureBar.picture.oW / 2) - (this.pictureBar.borderLeft + this.pictureBar.borderRight)) * percent), + h: this.pictureBar.picture.h + }); + height = this.pictureBar.picture.h; + offset = ScreenResolution.getScreenY(-5); + } + y += (height / 2) + offset; + this.graphicName.draw(x, y, 0, 0); + this.graphicValue.draw(x + ScreenResolution.getScreenX(this.maxStatNamesLength) + 10, y, 0, 0); + } } export { Statistic } \ No newline at end of file diff --git a/src/Graphic/Text.ts b/src/Graphic/Text.ts index 466150c0..14dba02e 100644 --- a/src/Graphic/Text.ts +++ b/src/Graphic/Text.ts @@ -191,11 +191,10 @@ class Text extends Base { { // Correcting x and y according to alignment let xBack = x; - if (this.zoom !== 1) { - this.fontSize *= this.zoom; - this.updateFont(); - this.measureText(); - } + this.fontSize *= this.zoom; + this.updateFont(); + this.measureText(); + // Wrap text if != 0 if (this.lastW !== w && w !== 0) { this.lastW = w; @@ -248,10 +247,10 @@ class Text extends Base { Platform.ctx.strokeStyle = this.strokeColor.rgb; yOffset = 0; for (i = 0; i < l; i++) { - Platform.ctx.strokeText(this.lines[i], x - 1, y - 1 + yOffset); - Platform.ctx.strokeText(this.lines[i], x - 1, y + 1 + yOffset); - Platform.ctx.strokeText(this.lines[i], x + 1, y - 1 + yOffset); - Platform.ctx.strokeText(this.lines[i], x + 1, y + 1 + yOffset); + Platform.ctx.strokeText(this.lines[i], (x - 1), (y - 1 + yOffset)); + Platform.ctx.strokeText(this.lines[i], (x - 1), (y + 1 + yOffset)); + Platform.ctx.strokeText(this.lines[i], (x + 1), (y - 1 + yOffset)); + Platform.ctx.strokeText(this.lines[i], (x + 1), (y + 1 + yOffset)); yOffset += lineHeight; } } @@ -265,10 +264,8 @@ class Text extends Base { } // Fix font back - if (this.zoom !== 1) { - this.setFontSize(this.oFontSize); - this.measureText(); - } + this.setFontSize(this.oFontSize); + this.measureText(); } /** diff --git a/src/Graphic/TextIcon.ts b/src/Graphic/TextIcon.ts index 4a64607a..529c4752 100644 --- a/src/Graphic/TextIcon.ts +++ b/src/Graphic/TextIcon.ts @@ -84,8 +84,7 @@ class TextIcon extends Base { * @returns {number} */ getWidth(): number { - return ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize) + this - .space + this.graphicText.textWidth; + return ScreenResolution.getScreenMinXY(Datas.Systems.iconsSize) + this.space + this.graphicText.textWidth; } /** diff --git a/src/Manager/Collisions.ts b/src/Manager/Collisions.ts index 5bfd4b45..a9d701c4 100644 --- a/src/Manager/Collisions.ts +++ b/src/Manager/Collisions.ts @@ -1,12 +1,12 @@ /* - RPG Paper Maker Copyright (C) 2017-2023 Wano + RPG Paper Maker Copyright (C) 2017-2023 Wano - RPG Paper Maker engine is under proprietary license. - This source code is also copyrighted. + RPG Paper Maker engine is under proprietary license. + This source code is also copyrighted. - Use Commercial edition for commercial use of your games. - See RPG Paper Maker EULA here: - http://rpg-paper-maker.com/index.php/eula. + Use Commercial edition for commercial use of your games. + See RPG Paper Maker EULA here: + http://rpg-paper-maker.com/index.php/eula. */ import { Mathf, Constants, Enum } from "../Common"; @@ -21,1388 +21,1390 @@ import { THREE } from "../Globals"; */ class Collisions { - public static BB_MATERIAL = new THREE.MeshBasicMaterial(); - public static BB_MATERIAL_DETECTION = new THREE.MeshBasicMaterial(); - public static BB_EMPTY_MATERIAL = new THREE.MeshBasicMaterial({visible: false}); - public static BB_BOX = Collisions.createBox(); - public static BB_ORIENTED_BOX = Collisions.createOrientedBox(); - private static BB_BOX_DETECTION = Collisions.createBox(true); - public static BB_BOX_DEFAULT_DETECTION = Collisions.createBox(true); - public static currentCustomObject3D: THREE.Mesh = null; - - constructor() { - throw new Error("This is a static class"); - } - - /** - * Initialize necessary collisions. - * @static - */ - static initialize() { - this.BB_BOX_DETECTION.geometry.boundingBox = new THREE.Box3(); - } - - /** - * Create a box for bounding box. - * @static - * @returns {THREE.Mesh} - */ - static createBox(detection: boolean = false): THREE.Mesh { - let box = new THREE.Mesh(CustomGeometry.createBox(1, 1, 1), detection ? - this.BB_MATERIAL_DETECTION : this.BB_MATERIAL); - box['previousTranslate'] = [0, 0, 0]; - box['previousRotate'] = [0, 0, 0]; - box['previousScale'] = [1, 1, 1]; - box['previousIsFix'] = false; - box['previousCenter'] = 0; - return box; - } - - /** - * Create an oriented box for bounding box. - * @static - * @returns {THREE.Mesh} - */ - static createOrientedBox(): THREE.Mesh { - let box = new THREE.Mesh(CustomGeometry.createBox(1, 1, 1), this - .BB_MATERIAL); - box['previousTranslate'] = [0, 0, 0]; - box['previousScale'] = [1, 1, 1]; - box.geometry.rotateY(Math.PI / 4); - return box; - } - - /** - * Apply transform for lands bounding box. - * @static - * @param {THREE.Mesh} box - The mesh bounding box - * @param {number[]} boundingBox - The bounding box list parameters - */ - static applyBoxLandTransforms(box: THREE.Mesh, boundingBox: number[]) { - // Cancel previous geometry transforms - box.geometry.translate(-box['previousTranslate'][0], -box[ - 'previousTranslate'][1], -box['previousTranslate'][2]); - box.geometry.rotateZ(-box['previousRotate'][2] * Math.PI / 180.0); - box.geometry.rotateX(-box['previousRotate'][1] * Math.PI / 180.0); - box.geometry.rotateY(-box['previousRotate'][0] * Math.PI / 180.0); - box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] - [1], 1 / box['previousScale'][2]); - - // Update to the new ones - box.geometry.scale(boundingBox[3], 1, boundingBox[4]); - box.geometry.translate(boundingBox[0], boundingBox[1], boundingBox[2]); - - // Register previous transforms to current - box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[ - 2]]; - box['previousRotate'] = [0, 0, 0]; - box['previousScale'] = [boundingBox[3], 1, boundingBox[4]]; - - // Update geometry now - box.updateMatrixWorld(); - - // Compute bounding box manually - if (box.geometry.boundingBox === null) { - box.geometry.computeBoundingBox(); - } - } - - /** - * Apply transform for sprite bounding box. - * @static - * @param {THREE.Mesh} box - The mesh bounding box - * @param {number[]} boundingBox - The bounding box list parameters - */ - static applyBoxSpriteTransforms(box: THREE.Mesh, boundingBox: number[], - isFixSprite: boolean = false, center: number = 0) { - // Cancel previous geometry transforms - box.geometry.translate(-box['previousTranslate'][0], -box[ - 'previousTranslate'][1] + box['previousCenter'], -box['previousTranslate'][2]); - let geometry = box.geometry; - if (box['previousIsFix'] && (box['previousRotate'][1] !== 0 || box['previousRotate'][2] !==0)) { - geometry.rotate(-box['previousRotate'][2], Sprite.Z_AXIS, new Vector3(0, box['previousCenter'], 0)); - geometry.rotate(-box['previousRotate'][1], Sprite.X_AXIS, new Vector3(0, box['previousCenter'], 0)); - geometry.rotate(-box['previousRotate'][0], Sprite.Y_AXIS, new Vector3(0, box['previousCenter'], 0)); - } else { - box.geometry.rotateZ(-box['previousRotate'][2] * Math.PI / 180.0); - box.geometry.rotateX(-box['previousRotate'][1] * Math.PI / 180.0); - box.geometry.rotateY(-box['previousRotate'][0] * Math.PI / 180.0); - } - box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] - [1], 1 / box['previousScale'][2]); - - // Update to the new ones - box.geometry.scale(boundingBox[3], boundingBox[4], boundingBox[5]); - if (isFixSprite && (boundingBox[7] !== 0 || boundingBox[8] !==0)) { - geometry.rotate(boundingBox[6], Sprite.Y_AXIS, new Vector3(0, center, 0)); - geometry.rotate(boundingBox[7], Sprite.X_AXIS, new Vector3(0, center, 0)); - geometry.rotate(boundingBox[8], Sprite.Z_AXIS, new Vector3(0, center, 0)); - } else { - box.geometry.rotateY(boundingBox[6] * Math.PI / 180.0); - box.geometry.rotateX(boundingBox[7] * Math.PI / 180.0); - box.geometry.rotateZ(boundingBox[8] * Math.PI / 180.0); - } - box.geometry.translate(boundingBox[0], boundingBox[1] - center, boundingBox[2]); - - // Register previous transforms to current - box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[2]]; - box['previousRotate'] = [boundingBox[6], boundingBox[7], boundingBox[8]]; - box['previousScale'] = [boundingBox[3], boundingBox[4], boundingBox[5]]; - box['previousIsFix'] = isFixSprite; - box['previousCenter'] = center; - - // Update geometry now - box.updateMatrixWorld(); - - // Compute bounding box manually - if (box.geometry.boundingBox === null) { - box.geometry.computeBoundingBox(); - } - } - - /** - * Apply transform for oriented bounding box. - * @static - * @param {THREE.Mesh} box - The mesh bounding box - * @param {number[]} boundingBox - The bounding box list parameters - */ - static applyOrientedBoxTransforms(box: THREE.Mesh, boundingBox: - number[]) - { - let size = Math.floor(boundingBox[3] / Math.sqrt(2)); - - // Cancel previous geometry transforms - box.geometry.translate(-box['previousTranslate'][0], -box[ - 'previousTranslate'][1], -box['previousTranslate'][2]); - box.geometry.rotateY(-Math.PI / 4); - box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] - [1], 1 / box['previousScale'][2]); - - // Update to the new ones - box.geometry.scale(size, boundingBox[4], size); - box.geometry.rotateY(Math.PI / 4); - box.geometry.translate(boundingBox[0], boundingBox[1], boundingBox[2]); - - // Register previous transforms to current - box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[ - 2]]; - box['previousScale'] = [size, boundingBox[4], size]; - - // Update geometry now - box.updateMatrixWorld(); - - // Compute bounding box manually - if (box.geometry.boundingBox === null) { - box.geometry.computeBoundingBox(); - } - } - - /** - * Get a bounding box mesh for detection. Keep the same existing one or - * force creating a new one for cases you need several. - * @static - * @param {number} [force=false] - * @returns {THREE.Mesh} - */ - static getBBBoxDetection(force: boolean = false): THREE.Mesh { - if (Datas.Systems.showBB && !force) { - let box = Collisions.createBox(true); - this.BB_BOX_DETECTION = box; - box.geometry.boundingBox = new THREE.Box3(); - Scene.Map.current.scene.add(box); - setTimeout(() => { - Scene.Map.current.scene.remove(box); - }, 1); - } - return this.BB_BOX_DETECTION; - } - - /** - * Indicate if min and max are overlapping. - * @static - * @param {number} minA - * @param {number} maxA - * @param {number} minB - * @param {number} maxB - * @returns {boolean} - */ - static isOverlapping(minA: number, maxA: number, minB: number, maxB: number) - : boolean - { - let minOverlap = null; - let maxOverlap = null; - - // If B contain in A - if (minA <= minB && minB <= maxA) - { - if (minOverlap === null || minB < minOverlap) - { - minOverlap = minB; - } - } - if (minA <= maxB && maxB <= maxA) - { - if (maxOverlap === null || maxB > minOverlap) - { - maxOverlap = maxB; - } - } - - // If A contain in B - if (minB <= minA && minA <= maxB) - { - if (minOverlap === null || minA < minOverlap) - { - minOverlap = minA; - } - } - if (minB <= maxA && maxA <= maxB) - { - if (maxOverlap === null || maxA > minOverlap) - { - maxOverlap = maxA; - } - } - return (minOverlap !== null && maxOverlap !== null); - } - - /** - * Check collision between two OBB. - * @static - * @param {Core.CustomGeometry} shapeA - First shape - * @param {Core.CustomGeometry} shapeB - Second shape - * @param {boolean} deepCheck - if false, only check bounding box - * @returns {boolean} - */ - static obbVSobb(shapeA: CustomGeometry, shapeB: CustomGeometry, deepCheck = true): boolean - { - const bbIntersect = shapeA.boundingBox.intersectsBox(shapeB.boundingBox); - if (!deepCheck && bbIntersect) { - return true; - } - if (!bbIntersect) { - return false; - } - let facesA = shapeA.getNormals(); - let facesB = shapeB.getNormals(); - let verticesA = shapeA.getVertices(); - let verticesB = shapeB.getVertices(); - let lA = verticesA.length; - let lB = verticesB.length; - if (!this.checkNormals(facesA, verticesA, verticesB, lA, lB)) { - return false; - } - if (!this.checkNormals(facesB, verticesA, verticesB, lA, lB)) { - return false; - } - return true; - } - - /** - * Check the fnormals for OBB collision. - * @static - * @param {ArrayLike} normals - The normals to check - * @param {Vector3[]} verticesA - First vertices to check - * @param {Vector3[]} verticesB - Second vertices to check - * @param {number} lA - The first vertices length - * @param {number} lB - The second vertices length - * @returns {boolean} - */ - static checkNormals(normals: ArrayLike, verticesA: ArrayLike, - verticesB: ArrayLike, lA: number, lB: number): boolean - { - for (let i = 0, l = normals.length; i < l; i += 3) { - if (!this.overlapOnThisNormal(verticesA, verticesB, lA, lB, new - Vector3(normals[i], normals[i + 1], normals[i + 2]))) { - return false; - } - } - return true; - } - - /** - * Check if vertices overlap on one of the faces normal. - * @static - * @param {ArrayLike} verticesA - First vertices to check - * @param {ArrayLike} verticesB - Second vertices to check - * @param {number} lA - The first vertices length - * @param {number} lB - The second vertices length - * @param {Core.Vector3} normal - The face normal - * @returns {boolean} - */ - static overlapOnThisNormal(verticesA: ArrayLike, verticesB: - ArrayLike, lA: number, lB: number, normal: THREE - .Vector3): boolean - { - // We test each vertex of A - let minA = null; - let maxA = null; - let i: number, vertex: Vector3, buffer: number; - for (i = 0; i < lA; i += 3) { - vertex = new Vector3(verticesA[i], verticesA[i + 1], verticesA[i + 2]); - buffer = Mathf.orthogonalProjection(vertex, normal); - if (minA === null || buffer < minA) { - minA = buffer; - } - if (maxA === null || buffer > maxA) { - maxA = buffer; - } - } - - // We test each vertex of B - let minB = null; - let maxB = null; - for (i = 0; i < lB; i += 3) { - vertex = new Vector3(verticesB[i], verticesB[i + 1], verticesB[i + 2]); - buffer = Mathf.orthogonalProjection(vertex, normal); - if (minB === null || buffer < minB) { - minB = buffer; - } - if (maxB === null || buffer > maxB) { - maxB = buffer; - } - } - - // We test if there is overlaping - return this.isOverlapping(minA, maxA, minB, maxB); - } - - /** - * Check collision ray. - * @static - * @param {Vector3} positionBefore - The position before collision - * @param {Vector3} positionAfter - The position after collision - * @param {MapObject} object - The map object to test collision - * @returns {boolean} - */ - static checkRay(positionBefore: Vector3, positionAfter: Vector3, object: - MapObject, bbSettings: number[], reverseTestObjects: boolean = false): [boolean, number, Enum.Orientation] - { - let direction = new Vector3(); - direction.subVectors(positionAfter, positionBefore).normalize(); - let jpositionBefore = Position.createFromVector3(positionBefore); - let jpositionAfter = Position.createFromVector3(positionAfter); - let positionBeforePlus = new Vector3(); - let positionAfterPlus = new Vector3(); - let testedCollisions = []; - let yMountain = null; - - // Squares to inspect according to the direction of the object - let [startI, endI, startJ, endJ, startK, endK] = object.getSquaresBB(); - - // Test objects - if (reverseTestObjects) { - let result = this.checkObjectsRay(positionAfter, object); - if (result !== null) { - return result; - } - } - - // Check collision outside - let block = false; - let i: number, j: number, k: number, i2: number, j2: number, k2: number, - portion: Portion, mapPortion: MapPortion, result: [boolean, number, Enum.Orientation]; - for (i = startI; i <= endI; i++) { - for (j = startJ; j <= endJ; j++) { - for (k = startK; k <= endK; k++) { - positionAfterPlus.set(positionAfter.x + i * Datas.Systems - .SQUARE_SIZE, positionAfter.y + j * Datas.Systems - .SQUARE_SIZE, positionAfter.z + k * Datas.Systems - .SQUARE_SIZE); - portion = Scene.Map.current.getLocalPortion(Portion - .createFromVector3(positionAfterPlus)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - result = this.check(mapPortion, jpositionBefore - , new Position(jpositionAfter.x + i, jpositionAfter - .y + j, jpositionAfter.z + k), positionAfter, object - , direction, testedCollisions); - if (result[0] === null) { - // If not already climbing, be sure that the before position can colide with climbling sprite - if (!object.isClimbing) { - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBefore); - for (i2 = startI; i2 <= endI; i2++) { - for (j2 = startJ; j2 <= endJ; j2++) { - for (k2 = startK; k2 <= endK; k2++) { - positionBeforePlus.set(positionBefore.x + i2 * Datas.Systems - .SQUARE_SIZE, positionBefore.y + j2 * Datas.Systems - .SQUARE_SIZE, positionBefore.z + k2 * Datas.Systems - .SQUARE_SIZE); - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBeforePlus)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - let [b, y] = this.checkSprites(mapPortion, new Position(jpositionBefore.x + i2, jpositionBefore - .y + j2, jpositionBefore.z + k2), [], object); - // If before and after collides, get up! - if (b === null) { - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); - return [null, y, result[2]]; - } - } - } - } - } - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); - return [false, null, Enum.Orientation.None]; - } - return [result[0], result[1], result[2]]; - } - if (result[0]) { - block = true; - } - if (result[1] !== null) { - if (yMountain === null || yMountain < result[1]) { - yMountain = result[1]; - } - } - } - } - } - } - if (block && (yMountain === null)) { - return [true, null, Enum.Orientation.None]; - } - - // Test objects - if (!reverseTestObjects) { - let result = this.checkObjectsRay(positionAfter, object); - if (result !== null) { - return result; - } - } - - // Check empty square or square mountain height possible down - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( - positionAfter)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - let floors: number[]; - if (mapPortion) { - floors = mapPortion.squareNonEmpty[jpositionAfter.x % Constants - .PORTION_SIZE][jpositionAfter.z % Constants.PORTION_SIZE]; - let otherMapPortion = Scene.Map.current.getMapPortion(portion.x, portion.y + 1, portion.z); - if (otherMapPortion) { - floors = floors.concat(otherMapPortion.squareNonEmpty[jpositionAfter - .x %Constants.PORTION_SIZE][jpositionAfter.z % Constants - .PORTION_SIZE]); - } - if (yMountain === null && floors.indexOf(positionAfter.y) === -1) { - let l = floors.length; - if (l === 0) { - return [true, null, Enum.Orientation.None]; - } else { - let maxY = null; - let limitY = positionAfter.y - Datas.Systems - .mountainCollisionHeight.getValue(); - let temp: number; - for (i = 0; i < l; i++) { - temp = floors[i]; - if (temp <= (positionAfter.y + Datas.Systems - .mountainCollisionHeight.getValue()) && temp >= - limitY) - { - if (maxY === null) { - maxY = temp; - } else { - if (maxY < temp) { - maxY = temp; - } - } - } - } - if (maxY === null) { - // redo with before pos for going down two following height angled - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBefore)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - floors = mapPortion.squareNonEmpty[jpositionBefore.x % Constants - .PORTION_SIZE][jpositionBefore.z % Constants.PORTION_SIZE]; - let otherMapPortion = Scene.Map.current.getMapPortion(portion.x, portion.y + 1, portion.z); - if (otherMapPortion) { - floors = floors.concat(otherMapPortion.squareNonEmpty[jpositionBefore.x % - Constants.PORTION_SIZE][jpositionBefore.z % Constants - .PORTION_SIZE]); - } - if (yMountain === null && floors.indexOf(positionBefore.y) === -1) { - let l = floors.length; - if (l === 0) { - return [null, null, Enum.Orientation.None]; - } else { - let maxY = null; - let limitY = positionBefore.y - Datas.Systems - .mountainCollisionHeight.getValue(); - let temp: number; - for (i = 0; i < l; i++) { - temp = floors[i]; - if (temp <= (positionBefore.y + Datas.Systems - .mountainCollisionHeight.getValue()) && temp >= - limitY) - { - if (maxY === null) { - maxY = temp; - } else { - if (maxY < temp) { - maxY = temp; - } - } - } - } - if (maxY === null) { - if (object.orientation === object.previousOrientation) { - // If non empty square on front of object, then force move front - let positionFront = positionBefore.clone(); - switch (object.orientationEye) { - case Enum.Orientation.North: - positionFront.setZ(positionFront.z - (Datas.Systems.SQUARE_SIZE / 2)); - break; - case Enum.Orientation.South: - positionFront.setZ(positionFront.z + (Datas.Systems.SQUARE_SIZE / 2)); - break; - case Enum.Orientation.West: - positionFront.setX(positionFront.x - (Datas.Systems.SQUARE_SIZE / 2)); - break; - case Enum.Orientation.East: - positionFront.setX(positionFront.x + (Datas.Systems.SQUARE_SIZE / 2)); - break; - } - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionFront)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - floors = mapPortion.squareNonEmpty[Math.floor(positionFront.x / Datas.Systems.SQUARE_SIZE) % Constants.PORTION_SIZE][Math.floor(positionFront.z / Datas.Systems.SQUARE_SIZE) % Constants.PORTION_SIZE]; - if (floors.length > 0) { - for (let y of floors) { - if (y === positionFront.y) { - return [false, null, Enum.Orientation.None]; - } - } - } - } - } - // Check if climbing stuff in 1 px bottom - const positionBottom = positionBefore.clone(); - positionBottom.setY(positionBottom.y - 1); - for (i = startI; i <= endI; i++) { - for (j = startJ; j <= endJ; j++) { - for (k = startK; k <= endK; k++) { - positionBeforePlus.set(positionBefore.x + i * Datas.Systems - .SQUARE_SIZE, (positionBefore.y + j * Datas.Systems - .SQUARE_SIZE) - 1, positionBefore.z + k * Datas.Systems - .SQUARE_SIZE); - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBeforePlus)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - const jpositionBottom = Position.createFromVector3(positionBeforePlus); - const climbingUp = object.isClimbingUp; - object.isClimbingUp = false; - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBottom); - let [b, y, o] = this.checkSprites(mapPortion, jpositionBottom, [], object); - if (b === null) { - // Check if after moving the collision still occurs. If not, go down - const positionBottomAfter = positionAfter.clone(); - positionBottomAfter.setY(positionBottomAfter.y - 1); - for (i2 = startI; i2 <= endI; i2++) { - for (j2 = startJ; j2 <= endJ; j2++) { - for (k2 = startK; k2 <= endK; k2++) { - positionAfterPlus.set(positionAfter.x + i2 * Datas.Systems - .SQUARE_SIZE, (positionAfter.y + j2 * Datas.Systems - .SQUARE_SIZE) - 1, positionAfter.z + k2 * Datas.Systems - .SQUARE_SIZE); - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionAfterPlus)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - if (mapPortion) { - const jpositionBottomAfter = Position.createFromVector3(positionAfterPlus); - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBottomAfter); - b = this.checkSprites(mapPortion, jpositionBottomAfter, [], object)[0]; - if (b === null) { - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); - object.isClimbingUp = climbingUp; - return [null, null, Enum.Orientation.None]; - } - } - } - } - } - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); - object.isClimbingUp = climbingUp; - return [null, y, o]; - } - object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); - object.isClimbingUp = climbingUp; - } - } - } - } - return [true, null, Enum.Orientation.None]; - } else { - yMountain = maxY; - } - } - } - } - } else { - yMountain = maxY; - } - } - } - - // Check lands inside collisions - portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( - positionBefore)); - mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); - return [this.checkLandsInside(mapPortion, jpositionBefore, - jpositionAfter, direction), yMountain, Enum.Orientation.None]; - } - return [true, null, Enum.Orientation.None]; - } - - static checkObjectsRay(positionAfter: Vector3, object: MapObject): [boolean, - number, Enum.Orientation] { - // Check collision inside & with other objects - if (object !== Game.current.hero && object.checkCollisionObject(Game - .current.hero)) { - return [true, null, Enum.Orientation.None]; - } - - // Check objects collisions - let portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( - positionAfter)); - let i: number, j: number, mapPortion: MapPortion; - for (i = 0; i < 2; i++) { - for (j = 0; j < 2; j++) { - mapPortion = Scene.Map.current.getMapPortion(portion.x + i, - portion.y, portion.z + j); - if (mapPortion && this.checkObjects(mapPortion, object)) { - return [true, null, Enum.Orientation.None]; - } - } - } - return null; - } - - /** - * Check if there is a collision at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionBefore - The json position before collision - * @param {Position} jpositionAfter - The json position after collision - * @param {Vector3} positionAfter - The position after collision - * @param {MapObject} object - The map object collision test - * @param {Vector3} direction - The direction collision - * @param {StructMapElementCollision[]} testedCollisions - The object - * collisions that were already tested - * @returns {boolean} - */ - static check(mapPortion: MapPortion, jpositionBefore: Position, - jpositionAfter: Position, positionAfter: Vector3, object: - MapObject, direction: Vector3, testedCollisions: - StructMapElementCollision[]): [boolean, number, Enum.Orientation] - { - // Check sprites and climbing - let [isCollision, yMountain, o] = this.checkSprites(mapPortion, - jpositionAfter, testedCollisions, object); - // Climbing - if (isCollision || yMountain !== null) { - return [isCollision, yMountain, o]; - } - - // Check mountain collision first for elevation - [isCollision, yMountain] = this.checkMountains(mapPortion, jpositionAfter, - positionAfter, testedCollisions, object); - if (isCollision) { - return [isCollision, yMountain, Enum.Orientation.None]; - } - - // Check other tests - return [(this.checkLands(mapPortion, jpositionBefore, jpositionAfter, - object, direction, testedCollisions) || this.checkObjects3D( - mapPortion, jpositionAfter, positionAfter, testedCollisions, object)), - yMountain, Enum.Orientation.None]; - } - - /** - * Check if there is a collision with lands at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionBefore - The json position before collision - * @param {Position} jpositionAfter - The json position after collision - * @param {MapObject} object - The map object collision test - * @param {Vector3} direction - The direction collision - * @param {StructMapElementCollision[]} testedCollisions - The object - * collisions that were already tested - * @returns {boolean} - */ - static checkLands(mapPortion: MapPortion, jpositionBefore: Position, - jpositionAfter: Position, object: MapObject, direction: THREE - .Vector3, testedCollisions: StructMapElementCollision[]): boolean - { - let index = jpositionAfter.toIndex() - let lands = mapPortion.boundingBoxesLands[index]; - if (lands !== null) { - let objCollision: StructMapElementCollision, boundingBox: number[], - collision: CollisionSquare; - for (let i = 0, l = lands.length; i < l; i++) { - objCollision = lands[i]; - if (testedCollisions.indexOf(objCollision) === -1) { - testedCollisions.push(objCollision); - if (objCollision !== null) { - boundingBox = objCollision.b; - collision = objCollision.cs; - if (this.checkIntersectionLand(collision, boundingBox, - object) || this.checkDirections(jpositionBefore, - jpositionAfter, collision, boundingBox, direction, - object)) - { - return true; - } - } - } - } - } - return false; - } - - /** - * Check if there is a collision with lands with directions. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionBefore - The json position before collision - * @param {Position} jpositionAfter - The json position after collision - * @param {Vector3} direction - The direction collision - * @returns {boolean} - */ - static checkLandsInside(mapPortion: MapPortion, jpositionBefore: Position, - jpositionAfter: Position, direction: Vector3): boolean - { - let lands = mapPortion.boundingBoxesLands[jpositionBefore.toIndex()]; - if (lands !== null) { - let objCollision: StructMapElementCollision, collision: - CollisionSquare; - for (let i = 0, l = lands.length; i < l; i++) { - objCollision = lands[i]; - if (objCollision !== null) { - collision = objCollision.cs; - if (this.checkDirectionsInside(jpositionBefore, - jpositionAfter, collision, direction)) - { - return true; - } - } - } - } - return false; - } - - /** - * Check intersection between ray and an object. - * @static - * @param {StructMapElementCollision} collision - The collision object - * @param {number[]} boundingBox - The bounding box values - * @param {MapObject} object - The map object to check - * @returns {boolean} - */ - static checkIntersectionLand(collision: StructMapElementCollision, - boundingBox: number[], object: MapObject): boolean - { - if (collision !== null) { - return false; - } - this.applyBoxLandTransforms(this.BB_BOX, boundingBox); - return this.obbVSobb(object.currentBoundingBox.geometry, - this.BB_BOX.geometry); - } - - /** - * Check directions - * @static - * @param {Position} jpositionBefore - The json position before collision - * @param {Position} jpositionAfter - The json position after collision - * @param {StructMapElementCollision} collision - The collision object - * @param {number[]} boundingBox - The bounding box values - * @param {Vector3} direction - The direction collision - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkDirections(jpositionBefore: Position, jpositionAfter: Position, - collision: StructMapElementCollision, boundingBox: number[], direction: - Vector3, object: MapObject): boolean - { - if (collision === null) { - return false; - } - if (!jpositionBefore.equals(jpositionAfter)) { - if (this.checkIntersectionLand(null, boundingBox, object)) { - if (direction.x > 0) { - return !collision.left; - } - if (direction.x < 0) { - return !collision.right; - } - if (direction.z > 0) { - return !collision.top; - } - if (direction.z < 0) { - return !collision.bot; - } - } - } - return false; - } - - /** - * Check directions inside. - * @static - * @param {Position} jpositionBefore - The json position before collision - * @param {Position} jpositionAfter - The json position after collision - * @param {StructMapElementCollision} collision - The collision object - * @param {Vector3} direction - The direction collision - * @returns {boolean} - */ - static checkDirectionsInside(jpositionBefore: Position, jpositionAfter: - Position, collision: StructMapElementCollision, direction: THREE - .Vector3): boolean - { - if (collision === null) { - return false; - } - if (!jpositionBefore.equals(jpositionAfter)) { - if (direction.x > 0) { - return !collision.right; - } - if (direction.x < 0) { - return !collision.left; - } - if (direction.z > 0) { - return !collision.bot; - } - if (direction.z < 0) { - return !collision.top; - } - } - return false; - } - - /** - * Check if there is a collision with sprites at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionAfter - The json position after collision - * @param {StructMapElementCollision[]} testedCollisions - The object - * collisions that were already tested - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkSprites(mapPortion: MapPortion, jpositionAfter: - Position, testedCollisions: StructMapElementCollision[], object: - MapObject): [boolean, number, Enum.Orientation] - { - let sprites = mapPortion.boundingBoxesSprites[jpositionAfter.toIndex()]; - let tested = false; - if (sprites !== null) { - let objCollision: StructMapElementCollision; - for (let i = 0, l = sprites.length; i < l; i++) { - objCollision = sprites[i]; - if (testedCollisions.indexOf(objCollision) === -1) { - testedCollisions.push(objCollision); - if (this.checkIntersectionSprite(objCollision.b, objCollision.k, object)) { - if (objCollision.cl) { - const speed = object.speed.getValue() * MapObject - .SPEED_NORMAL * Manager.Stack.averageElapsedTime * - Datas.Systems.SQUARE_SIZE * Datas.Systems.climbingSpeed.getValue(); - const limitTop = objCollision.b[1] + Math.ceil(objCollision.b[4] / 2); - const limitBot = objCollision.b[1] - Math.ceil(objCollision.b[4] / 2); - const y = object.isClimbingUp ? Math.min(object.position.y + speed, limitTop) : Math.max(object.position.y - speed, limitBot); - if (y === object.position.y) { - continue; - } - let angle = objCollision.b[6]; - let force = false, front = false; - if (angle === 0 || angle === 180) { - force = true; - front = true; - } else if (angle === 90 || angle === 270) { - force = true; - } - return [null, y, object.getOrientationBetweenPosition(objCollision.l, force, front)]; - } - if (!object.isClimbing) { - tested = true; - } - } - } - } - } - return [tested, null, Enum.Orientation.None]; - } - - /** - * Check intersection between ray and an object. - * @static - * @param {number[]} boundingBox - The bounding box values - * @param {boolean} fix - Indicate if the sprite is fix or not - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkIntersectionSprite(boundingBox: number[], fix: boolean, object: - MapObject): boolean - { - if (boundingBox === null) { - return false; - } - if (fix) { - this.applyBoxSpriteTransforms(this.BB_BOX, boundingBox, true); - return this.obbVSobb(object.currentBoundingBox - .geometry, this.BB_BOX.geometry); - } else { - this.applyOrientedBoxTransforms(this.BB_ORIENTED_BOX, boundingBox); - return this.obbVSobb(object.currentBoundingBox - .geometry, this.BB_ORIENTED_BOX.geometry); - } - } - - /** - * Check if there is a collision with sprites at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionAfter - The json position after collision - * @param {THREE.Vector3} positionAfter - The position after collision - * @param {StructMapElementCollision[]} testedCollisions - The object - * collisions that were already tested - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkObjects3D(mapPortion: MapPortion, jpositionAfter: Position, - positionAfter: THREE.Vector3, testedCollisions: StructMapElementCollision[], - object: MapObject): boolean - { - let objects3D = mapPortion.boundingBoxesObjects3D[jpositionAfter.toIndex()]; - if (objects3D !== null) { - let objCollision: StructMapElementCollision; - for (let i = 0, l = objects3D.length; i < l; i++) { - objCollision = objects3D[i]; - if (testedCollisions.indexOf(objCollision) === -1) { - testedCollisions.push(objCollision); - if (objCollision.id) { - if (this.checkCustomObject3D(objCollision, object, - positionAfter)) { - return true; - } - } else { - if (this.checkIntersectionSprite(objCollision.b, - objCollision.k, object)) - { - return true; - } - } - } - } - } - return false; - } - - /** - * Check if there is a collision with custom object 3D collision. - * @static - * @param {StructMapElementCollision} objCollision - The object colision - * info to test - * @param {MapObject} object - The map object collision test - * @param {THREE.Vector3} positionAfter - The position after collision - * @returns {boolean} - */ - static checkCustomObject3D(objCollision: StructMapElementCollision, object: - MapObject, positionAfter: THREE.Vector3): boolean - { - // Remove previous - let mesh = Datas.Shapes.get(Enum.CustomShapeKind.Collisions, objCollision.id).mesh; - if (mesh !== this.currentCustomObject3D) { - Scene.Map.current.scene.remove(this.currentCustomObject3D); - this.currentCustomObject3D = mesh; - } - if (this.currentCustomObject3D) { - this.currentCustomObject3D.position.set(objCollision.l.x, objCollision.l.y, objCollision.l.z); - this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(1,0,0), objCollision.b[6]); - this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(0,1,0), objCollision.b[7]); - this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(0,0,1), objCollision.b[8]); - Scene.Map.current.scene.add(this.currentCustomObject3D); - if (Datas.Systems.showBB) { - this.currentCustomObject3D.material = this.BB_MATERIAL; - } else { - this.currentCustomObject3D.material = this.BB_EMPTY_MATERIAL; - } - let direction = positionAfter.clone().sub(object.position).normalize(); - if (this.checkIntersectionMeshes(object.currentBoundingBox, this - .currentCustomObject3D, direction)) { - return true; - } - } - return false; - } - - /** - * Check intersection between two complex meshes. - * @static - * @param {THREE.Mesh} meshA - The first mesh - * @param {THREE.Mesh} meshB - The second mesh - * @param {THREE.Vector3} direction - The meshA direction to mesh B - * @returns {boolean} - */ - static checkIntersectionMeshes(meshA: THREE.Mesh, meshB: THREE.Mesh, direction: THREE.Vector3): boolean { - let vertices = meshA.geometry.getVerticesVectors(); - let raycaster = new THREE.Raycaster(); - let directionNegate = direction.clone().negate(); - let collisionResults: THREE.Intersection[]; - for (let vertex of vertices) { - raycaster.set(vertex, direction); - collisionResults = raycaster.intersectObject(meshB); - if (collisionResults.length === 0) { - raycaster.set(vertex, directionNegate); - collisionResults = raycaster.intersectObject(meshB); - } - if (collisionResults.length > 0) { - raycaster.set(collisionResults[0].point, new THREE.Vector3(1,1,1)); - const intersects = raycaster.intersectObject(meshA); - if (intersects.length > 0) { // Points is in objet - return true; - } - } - } - return false; - } - - /** - * Check if there is a collision with mountains at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionAfter - The json position after collision - * @param {Vector3} positionAfter - The position after collision - * @param {StructMapElementCollision[]} testedCollisions - The object collisions that were - * already tested - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkMountains(mapPortion: MapPortion, jpositionAfter: - Position, positionAfter: Vector3, testedCollisions: - StructMapElementCollision[], object: MapObject): [boolean, number] - { - let yMountain = null; - let mountains = mapPortion.boundingBoxesMountains[jpositionAfter.toIndex()]; - let block = false; - let i: number, l: number, result: [boolean, boolean, number]; - if (mountains !== null) { - for (i = 0, l = mountains.length; i < l; i++) { - result = this.checkMountain(mapPortion, jpositionAfter, - positionAfter, testedCollisions, object, mountains[i], - yMountain, block); - if (result[0]) { - return [result[1], result[2]]; - } else { - block = result[1]; - yMountain = result[2]; - } - } - } - let j: number, m: number, objCollision: StructMapElementCollision[], - position: Position, mapPortionOverflow: MapPortion; - for (i = 0, l = mapPortion.overflowMountains.length; i < l; i++) { - position = mapPortion.overflowMountains[i]; - mapPortionOverflow = Scene.Map.current.getMapPortionFromPortion(Scene - .Map.current.getLocalPortion(position.getGlobalPortion())); - if (!mapPortionOverflow) { - continue; - } - objCollision = mapPortionOverflow.getObjectCollisionAt(position, - jpositionAfter, ElementMapKind.Mountains); - for (j = 0, m = objCollision.length; j < m; j++) { - result = this.checkMountain(mapPortion, jpositionAfter, - positionAfter, testedCollisions, object, objCollision[j], - yMountain, block); - if (result[0]) { - return [result[1], result[2]]; - } else { - block = result[1]; - yMountain = result[2]; - } - } - } - return [block && (yMountain === null), yMountain]; - } - - /** - * Check if there is a collision with mountains at this position. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionAfter - The json position after collision - * @param {Vector3} positionAfter - The position after collision - * @param {StructMapElementCollision[]} testedCollisions - The object - * collisions that were already tested - * @param {MapObject} object - The map object collision test - * @param {StructMapElementCollision} objCollision - The object collision - * @param {number} yMountain - The y mountain collision - * @param {boolean} block - The block mountain collision - * @returns {[boolean, boolean, number]} - */ - static checkMountain(mapPortion: MapPortion, jpositionAfter: Position, - positionAfter: Vector3, testedCollisions: - StructMapElementCollision[], object: MapObject, objCollision: - StructMapElementCollision, yMountain: number, block: boolean): [boolean, - boolean, number] - { - if (testedCollisions.indexOf(objCollision) === -1) { - testedCollisions.push(objCollision); - let result = this.checkIntersectionMountain(mapPortion, - jpositionAfter, positionAfter, objCollision, object); - if (result[0]) { - if (result[1] === null) { - return [true, result[0], result[1]]; - } else { - block = true; - } - } else if (result[1] !== null) { - if (yMountain === null || yMountain < result[1]) { - yMountain = result[1]; - } - } - } - return [false, block, yMountain]; - } - - /** - * Check intersection with a mountain. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {Position} jpositionAfter - The json position after collision - * @param {Vector3} positionAfter - The position after collision - * @param {StructMapElementCollision} objCollision - The object collision - * @param {MapObject} object - The map object collision test - * @returns {[boolean, number]} - */ - static checkIntersectionMountain(mapPortion: MapPortion, jpositionAfter: - Position, positionAfter: Vector3, objCollision: - StructMapElementCollision, object: MapObject): [boolean, number] - { - let mountain = objCollision.t; - let forceAlways = ( mountain.getSystem()).forceAlways(); - let forceNever = ( mountain.getSystem()).forceNever(); - let point = new Vector2(positionAfter.x, positionAfter.z); - let x = objCollision.l.x; - let y = objCollision.l.y; - let z = objCollision.l.z; - let w = objCollision.rw; - let h = objCollision.rh; - - // If not in the height, no test - if (positionAfter.y < y || positionAfter.y > y + h) { - return [false, null]; - } - - // if w = 0, check height - if (objCollision.rw === 0) { - let pass = forceNever || -(!forceAlways && ((y + h) <= (positionAfter - .y + Datas.Systems.mountainCollisionHeight.getValue()))); - if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems.SQUARE_SIZE - , z, z + Datas.Systems.SQUARE_SIZE)) - { - return pass ? [false, (positionAfter.y - y - h) === 0 ? null : y + h] : [true, null]; - - } else { - if (!pass) { - // Collide with BB (avoiding use of checkIntersectionSprite here for perfs issues) - let vertices = object.currentBoundingBox.geometry.getVertices(); - let vy = 0; - for (let i = 0, l = vertices.length; i < l; i += 3) { - vy = vertices[i + 1]; - if (vy >= y && vy <= y + h) { - point = new Vector2(vertices[i], vertices[i + 2]); - if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems - .SQUARE_SIZE, z, z + Datas.Systems.SQUARE_SIZE)) - { - return [true, null]; - } - } - } - } - } - } else { - // if w > 0, go like a slope - // Get coplanar points according to side - let ptA: Vector2, ptB: Vector2, ptC: Vector2, pA: - Vector3, pB: Vector3, pC: Vector3; - if (objCollision.left && !mountain.left) { - if (objCollision.top && !mountain.top) { - ptA = new Vector2(x - w, z); - ptB = new Vector2(x, z); - ptC = new Vector2(x, z - w); - if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { - pA = new Vector3(ptA.x, y, ptA.y); - pB = new Vector3(ptB.x, y + h, ptB.y); - pC = new Vector3(ptC.x, y, ptC.y); - } else { - return [false, null]; - } - } else if (objCollision.bot && !mountain.bot) { - ptA = new Vector2(x - w, z + Datas.Systems.SQUARE_SIZE); - ptB = new Vector2(x, z + Datas.Systems.SQUARE_SIZE); - ptC = new Vector2(x, z + Datas.Systems.SQUARE_SIZE + w); - if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { - pA = new Vector3(ptA.x, y, ptA.y); - pB = new Vector3(ptB.x, y + h, ptB.y); - pC = new Vector3(ptC.x, y, ptC.y); - } else { - return [false, null]; - } - } else { - if (Mathf.isPointOnRectangle(point, x - w, x, z, z + Datas - .Systems.SQUARE_SIZE)) - { - pA = new Vector3(x - w, y, z); - pB = new Vector3(x, y + h, z); - pC = new Vector3(x, y + h, z + Datas.Systems.SQUARE_SIZE); - } else { - return [false, null]; - } - } - } else if (objCollision.right && !mountain.right) { - if (objCollision.top && !mountain.top) { - ptA = new Vector2(x + Datas.Systems.SQUARE_SIZE, z - w); - ptB = new Vector2(x + Datas.Systems.SQUARE_SIZE, z); - ptC = new Vector2(x + Datas.Systems.SQUARE_SIZE + w, z); - if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { - pA = new Vector3(ptA.x, y, ptA.y); - pB = new Vector3(ptB.x, y + h, ptB.y); - pC = new Vector3(ptC.x, y, ptC.y); - } else { - return [false, null]; - } - } else if (objCollision.bot && !mountain.bot) { - ptA = new Vector2(x + Datas.Systems.SQUARE_SIZE, z + - Datas.Systems.SQUARE_SIZE + w); - ptB = new Vector2(x + Datas.Systems.SQUARE_SIZE, z + - Datas.Systems.SQUARE_SIZE); - ptC = new Vector2(x + Datas.Systems.SQUARE_SIZE + w, z - + Datas.Systems.SQUARE_SIZE); - if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { - pA = new Vector3(ptA.x, y, ptA.y); - pB = new Vector3(ptB.x, y + h, ptB.y); - pC = new Vector3(ptC.x, y, ptC.y); - } else { - return [false, null]; - } - } else { - if (Mathf.isPointOnRectangle(point, x + Datas.Systems - .SQUARE_SIZE, x + Datas.Systems.SQUARE_SIZE + w, z, z + - Datas.Systems.SQUARE_SIZE)) - { - pA = new Vector3(x + Datas.Systems.SQUARE_SIZE, y - + h, z + Datas.Systems.SQUARE_SIZE); - pB = new Vector3(x + Datas.Systems.SQUARE_SIZE, y - + h, z); - pC = new Vector3(x + Datas.Systems.SQUARE_SIZE + w - , y, z); - } else { - return [false, null]; - } - } - } else { - if (objCollision.top && !mountain.top) { - if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems - .SQUARE_SIZE, z - w, z)) - { - pA = new Vector3(x, y + h, z); - pB = new Vector3(x, y, z - w); - pC = new Vector3(x + Datas.Systems.SQUARE_SIZE, y, - z - w); - } else { - return [false, null]; - } - } else if (objCollision.bot && !mountain.bot) { - if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems - .SQUARE_SIZE, z + Datas.Systems.SQUARE_SIZE, z + Datas - .Systems.SQUARE_SIZE + w)) - { - pA = new Vector3(x + Datas.Systems.SQUARE_SIZE, y, - z + Datas.Systems.SQUARE_SIZE + w); - pB = new Vector3(x, y, z + Datas.Systems - .SQUARE_SIZE + w); - pC = new Vector3(x, y + h, z + Datas.Systems - .SQUARE_SIZE); - } else { - return [false, null]; - } - } else { - return [false, null]; - } - } - // Get the intersection point for updating mountain y - let plane = new THREE.Plane(); - let ray = new THREE.Ray(new Vector3(positionAfter.x, y, - positionAfter.z), new Vector3(0, 1, 0)); - let newPosition = new Vector3(); - plane.setFromCoplanarPoints(pA, pB, pC); - ray.intersectPlane(plane, newPosition); - - // If going down, check if there's a blocking floor - let jposition = (newPosition.y - positionAfter.y) < 0 ? new Position - (Math.floor(positionAfter.x / Datas.Systems.SQUARE_SIZE), Math - .ceil(positionAfter.y / Datas.Systems.SQUARE_SIZE), Math.floor( - positionAfter.z / Datas.Systems.SQUARE_SIZE)) : jpositionAfter; - mapPortion = Scene.Map.current.getMapPortionFromPortion(Scene.Map - .current.getLocalPortion(jposition.getGlobalPortion())); - let isFloor = mapPortion.boundingBoxesLands[jposition.toIndex()] - .length > 0; - if (isFloor && (newPosition.y - positionAfter.y) < 0) { - return [false, null]; - } - - // If angle limit, block - if (forceAlways || (!forceNever && mountain.angle > Datas.Systems - .mountainCollisionAngle.getValue())) - { - // Check if floor existing on top of the mountain angle - isFloor = jposition.y === jpositionAfter.y ? false : - mapPortion.boundingBoxesLands[jpositionAfter.toIndex()] - .length > 0; - return [!isFloor, null]; - } - return [!forceNever && (Math.abs(newPosition.y - positionAfter.y) > - Datas.Systems.mountainCollisionHeight.getValue()), newPosition.y]; - } - return [false, null]; - } - - /** - * Check collision with objects. - * @static - * @param {MapPortion} mapPortion - The map portion to check - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkObjects(mapPortion: MapPortion, object: MapObject): boolean { - let datas = Scene.Map.current.getObjectsAtPortion(mapPortion.portion); - return this.checkObjectsList(mapPortion.objectsList, object) || this - .checkObjectsList(datas.min, object) || this.checkObjectsList(datas - .mout, object); - } - - /** - * Check collision with objects. - * @static - * @param {MapObject[]} list - The map objects list to test - * @param {MapObject} object - The map object collision test - * @returns {boolean} - */ - static checkObjectsList(list: MapObject[], object: MapObject): boolean { - let obj: MapObject; - for (let i = 0, l = list.length; i < l; i++) { - obj = list[i]; - if (obj !== object) { - if (object.checkCollisionObject(obj)) { - return true; - } - } - } - return false; - } + public static BB_MATERIAL = new THREE.MeshBasicMaterial(); + public static BB_MATERIAL_DETECTION = new THREE.MeshBasicMaterial(); + public static BB_EMPTY_MATERIAL = new THREE.MeshBasicMaterial({visible: false}); + public static BB_BOX = Collisions.createBox(); + public static BB_ORIENTED_BOX = Collisions.createOrientedBox(); + private static BB_BOX_DETECTION = Collisions.createBox(true); + public static BB_BOX_DEFAULT_DETECTION = Collisions.createBox(true); + public static currentCustomObject3D: THREE.Mesh = null; + + constructor() { + throw new Error("This is a static class"); + } + + /** + * Initialize necessary collisions. + * @static + */ + static initialize() { + this.BB_BOX_DETECTION.geometry.boundingBox = new THREE.Box3(); + } + + /** + * Create a box for bounding box. + * @static + * @returns {THREE.Mesh} + */ + static createBox(detection: boolean = false): THREE.Mesh { + let box = new THREE.Mesh(CustomGeometry.createBox(1, 1, 1), detection ? + this.BB_MATERIAL_DETECTION : this.BB_MATERIAL); + box['previousTranslate'] = [0, 0, 0]; + box['previousRotate'] = [0, 0, 0]; + box['previousScale'] = [1, 1, 1]; + box['previousIsFix'] = false; + box['previousCenter'] = 0; + return box; + } + + /** + * Create an oriented box for bounding box. + * @static + * @returns {THREE.Mesh} + */ + static createOrientedBox(): THREE.Mesh { + let box = new THREE.Mesh(CustomGeometry.createBox(1, 1, 1), this + .BB_MATERIAL); + box['previousTranslate'] = [0, 0, 0]; + box['previousScale'] = [1, 1, 1]; + box.geometry.rotateY(Math.PI / 4); + return box; + } + + /** + * Apply transform for lands bounding box. + * @static + * @param {THREE.Mesh} box - The mesh bounding box + * @param {number[]} boundingBox - The bounding box list parameters + */ + static applyBoxLandTransforms(box: THREE.Mesh, boundingBox: number[]) { + // Cancel previous geometry transforms + box.geometry.translate(-box['previousTranslate'][0], -box[ + 'previousTranslate'][1], -box['previousTranslate'][2]); + box.geometry.rotateZ(-box['previousRotate'][2] * Math.PI / 180.0); + box.geometry.rotateX(-box['previousRotate'][1] * Math.PI / 180.0); + box.geometry.rotateY(-box['previousRotate'][0] * Math.PI / 180.0); + box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] + [1], 1 / box['previousScale'][2]); + + // Update to the new ones + box.geometry.scale(boundingBox[3], 1, boundingBox[4]); + box.geometry.translate(boundingBox[0], boundingBox[1], boundingBox[2]); + + // Register previous transforms to current + box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[ + 2]]; + box['previousRotate'] = [0, 0, 0]; + box['previousScale'] = [boundingBox[3], 1, boundingBox[4]]; + + // Update geometry now + box.updateMatrixWorld(); + + // Compute bounding box manually + if (box.geometry.boundingBox === null) { + box.geometry.computeBoundingBox(); + } + } + + /** + * Apply transform for sprite bounding box. + * @static + * @param {THREE.Mesh} box - The mesh bounding box + * @param {number[]} boundingBox - The bounding box list parameters + */ + static applyBoxSpriteTransforms(box: THREE.Mesh, boundingBox: number[], + isFixSprite: boolean = false, center: number = 0) { + // Cancel previous geometry transforms + box.geometry.translate(-box['previousTranslate'][0], -box[ + 'previousTranslate'][1] + box['previousCenter'], -box['previousTranslate'][2]); + let geometry = box.geometry; + if (box['previousIsFix'] && (box['previousRotate'][1] !== 0 || box['previousRotate'][2] !==0)) { + geometry.rotate(-box['previousRotate'][2], Sprite.Z_AXIS, new Vector3(0, box['previousCenter'], 0)); + geometry.rotate(-box['previousRotate'][1], Sprite.X_AXIS, new Vector3(0, box['previousCenter'], 0)); + geometry.rotate(-box['previousRotate'][0], Sprite.Y_AXIS, new Vector3(0, box['previousCenter'], 0)); + } else { + box.geometry.rotateZ(-box['previousRotate'][2] * Math.PI / 180.0); + box.geometry.rotateX(-box['previousRotate'][1] * Math.PI / 180.0); + box.geometry.rotateY(-box['previousRotate'][0] * Math.PI / 180.0); + } + box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] + [1], 1 / box['previousScale'][2]); + + // Update to the new ones + box.geometry.scale(boundingBox[3], boundingBox[4], boundingBox[5]); + if (isFixSprite && (boundingBox[7] !== 0 || boundingBox[8] !==0)) { + geometry.rotate(boundingBox[6], Sprite.Y_AXIS, new Vector3(0, center, 0)); + geometry.rotate(boundingBox[7], Sprite.X_AXIS, new Vector3(0, center, 0)); + geometry.rotate(boundingBox[8], Sprite.Z_AXIS, new Vector3(0, center, 0)); + } else { + box.geometry.rotateY(boundingBox[6] * Math.PI / 180.0); + box.geometry.rotateX(boundingBox[7] * Math.PI / 180.0); + box.geometry.rotateZ(boundingBox[8] * Math.PI / 180.0); + } + box.geometry.translate(boundingBox[0], boundingBox[1] - center, boundingBox[2]); + + // Register previous transforms to current + box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[2]]; + box['previousRotate'] = [boundingBox[6], boundingBox[7], boundingBox[8]]; + box['previousScale'] = [boundingBox[3], boundingBox[4], boundingBox[5]]; + box['previousIsFix'] = isFixSprite; + box['previousCenter'] = center; + + // Update geometry now + box.updateMatrixWorld(); + + // Compute bounding box manually + if (box.geometry.boundingBox === null) { + box.geometry.computeBoundingBox(); + } + } + + /** + * Apply transform for oriented bounding box. + * @static + * @param {THREE.Mesh} box - The mesh bounding box + * @param {number[]} boundingBox - The bounding box list parameters + */ + static applyOrientedBoxTransforms(box: THREE.Mesh, boundingBox: + number[]) + { + let size = Math.floor(boundingBox[3] / Math.sqrt(2)); + + // Cancel previous geometry transforms + box.geometry.translate(-box['previousTranslate'][0], -box[ + 'previousTranslate'][1], -box['previousTranslate'][2]); + box.geometry.rotateY(-Math.PI / 4); + box.geometry.scale(1 / box['previousScale'][0], 1 / box['previousScale'] + [1], 1 / box['previousScale'][2]); + + // Update to the new ones + box.geometry.scale(size, boundingBox[4], size); + box.geometry.rotateY(Math.PI / 4); + box.geometry.translate(boundingBox[0], boundingBox[1], boundingBox[2]); + + // Register previous transforms to current + box['previousTranslate'] = [boundingBox[0], boundingBox[1], boundingBox[ + 2]]; + box['previousScale'] = [size, boundingBox[4], size]; + + // Update geometry now + box.updateMatrixWorld(); + + // Compute bounding box manually + if (box.geometry.boundingBox === null) { + box.geometry.computeBoundingBox(); + } + } + + /** + * Get a bounding box mesh for detection. Keep the same existing one or + * force creating a new one for cases you need several. + * @static + * @param {number} [force=false] + * @returns {THREE.Mesh} + */ + static getBBBoxDetection(force: boolean = false): THREE.Mesh { + if (Datas.Systems.showBB && !force) { + let box = Collisions.createBox(true); + this.BB_BOX_DETECTION = box; + box.geometry.boundingBox = new THREE.Box3(); + Scene.Map.current.scene.add(box); + setTimeout(() => { + Scene.Map.current.scene.remove(box); + }, 1); + } + return this.BB_BOX_DETECTION; + } + + /** + * Indicate if min and max are overlapping. + * @static + * @param {number} minA + * @param {number} maxA + * @param {number} minB + * @param {number} maxB + * @returns {boolean} + */ + static isOverlapping(minA: number, maxA: number, minB: number, maxB: number) + : boolean + { + let minOverlap = null; + let maxOverlap = null; + + // If B contain in A + if (minA <= minB && minB <= maxA) + { + if (minOverlap === null || minB < minOverlap) + { + minOverlap = minB; + } + } + if (minA <= maxB && maxB <= maxA) + { + if (maxOverlap === null || maxB > minOverlap) + { + maxOverlap = maxB; + } + } + + // If A contain in B + if (minB <= minA && minA <= maxB) + { + if (minOverlap === null || minA < minOverlap) + { + minOverlap = minA; + } + } + if (minB <= maxA && maxA <= maxB) + { + if (maxOverlap === null || maxA > minOverlap) + { + maxOverlap = maxA; + } + } + return (minOverlap !== null && maxOverlap !== null); + } + + /** + * Check collision between two OBB. + * @static + * @param {Core.CustomGeometry} shapeA - First shape + * @param {Core.CustomGeometry} shapeB - Second shape + * @param {boolean} deepCheck - if false, only check bounding box + * @returns {boolean} + */ + static obbVSobb(shapeA: CustomGeometry, shapeB: CustomGeometry, deepCheck = true): boolean + { + const bbIntersect = shapeA.boundingBox.intersectsBox(shapeB.boundingBox); + if (!deepCheck && bbIntersect) { + return true; + } + if (!bbIntersect) { + return false; + } + let facesA = shapeA.getNormals(); + let facesB = shapeB.getNormals(); + let verticesA = shapeA.getVertices(); + let verticesB = shapeB.getVertices(); + let lA = verticesA.length; + let lB = verticesB.length; + if (!this.checkNormals(facesA, verticesA, verticesB, lA, lB)) { + return false; + } + if (!this.checkNormals(facesB, verticesA, verticesB, lA, lB)) { + return false; + } + return true; + } + + /** + * Check the fnormals for OBB collision. + * @static + * @param {ArrayLike} normals - The normals to check + * @param {Vector3[]} verticesA - First vertices to check + * @param {Vector3[]} verticesB - Second vertices to check + * @param {number} lA - The first vertices length + * @param {number} lB - The second vertices length + * @returns {boolean} + */ + static checkNormals(normals: ArrayLike, verticesA: ArrayLike, + verticesB: ArrayLike, lA: number, lB: number): boolean + { + for (let i = 0, l = normals.length; i < l; i += 3) { + if (!this.overlapOnThisNormal(verticesA, verticesB, lA, lB, new + Vector3(normals[i], normals[i + 1], normals[i + 2]))) { + return false; + } + } + return true; + } + + /** + * Check if vertices overlap on one of the faces normal. + * @static + * @param {ArrayLike} verticesA - First vertices to check + * @param {ArrayLike} verticesB - Second vertices to check + * @param {number} lA - The first vertices length + * @param {number} lB - The second vertices length + * @param {Core.Vector3} normal - The face normal + * @returns {boolean} + */ + static overlapOnThisNormal(verticesA: ArrayLike, verticesB: + ArrayLike, lA: number, lB: number, normal: THREE + .Vector3): boolean + { + // We test each vertex of A + let minA = null; + let maxA = null; + let i: number, vertex: Vector3, buffer: number; + for (i = 0; i < lA; i += 3) { + vertex = new Vector3(verticesA[i], verticesA[i + 1], verticesA[i + 2]); + buffer = Mathf.orthogonalProjection(vertex, normal); + if (minA === null || buffer < minA) { + minA = buffer; + } + if (maxA === null || buffer > maxA) { + maxA = buffer; + } + } + + // We test each vertex of B + let minB = null; + let maxB = null; + for (i = 0; i < lB; i += 3) { + vertex = new Vector3(verticesB[i], verticesB[i + 1], verticesB[i + 2]); + buffer = Mathf.orthogonalProjection(vertex, normal); + if (minB === null || buffer < minB) { + minB = buffer; + } + if (maxB === null || buffer > maxB) { + maxB = buffer; + } + } + + // We test if there is overlaping + return this.isOverlapping(minA, maxA, minB, maxB); + } + + /** + * Check collision ray. + * @static + * @param {Vector3} positionBefore - The position before collision + * @param {Vector3} positionAfter - The position after collision + * @param {MapObject} object - The map object to test collision + * @returns {boolean} + */ + static checkRay(positionBefore: Vector3, positionAfter: Vector3, object: + MapObject, bbSettings: number[], reverseTestObjects: boolean = false): [boolean, number, Enum.Orientation] + { + let direction = new Vector3(); + direction.subVectors(positionAfter, positionBefore).normalize(); + let jpositionBefore = Position.createFromVector3(positionBefore); + let jpositionAfter = Position.createFromVector3(positionAfter); + let positionBeforePlus = new Vector3(); + let positionAfterPlus = new Vector3(); + let testedCollisions = []; + let yMountain = null; + + // Squares to inspect according to the direction of the object + let [startI, endI, startJ, endJ, startK, endK] = object.getSquaresBB(); + + // Test objects + if (reverseTestObjects) { + let result = this.checkObjectsRay(positionAfter, object); + if (result !== null) { + return result; + } + } + + // Check collision outside + let block = false; + let i: number, j: number, k: number, i2: number, j2: number, k2: number, + portion: Portion, mapPortion: MapPortion, result: [boolean, number, Enum.Orientation]; + for (i = startI; i <= endI; i++) { + for (j = startJ; j <= endJ; j++) { + for (k = startK; k <= endK; k++) { + positionAfterPlus.set(positionAfter.x + i * Datas.Systems + .SQUARE_SIZE, positionAfter.y + j * Datas.Systems + .SQUARE_SIZE, positionAfter.z + k * Datas.Systems + .SQUARE_SIZE); + portion = Scene.Map.current.getLocalPortion(Portion + .createFromVector3(positionAfterPlus)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + result = this.check(mapPortion, jpositionBefore + , new Position(jpositionAfter.x + i, jpositionAfter + .y + j, jpositionAfter.z + k), positionAfter, object + , direction, testedCollisions); + if (result[0] === null) { + // If not already climbing, be sure that the before position can colide with climbling sprite + if (!object.isClimbing) { + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBefore); + for (i2 = startI; i2 <= endI; i2++) { + for (j2 = startJ; j2 <= endJ; j2++) { + for (k2 = startK; k2 <= endK; k2++) { + positionBeforePlus.set(positionBefore.x + i2 * Datas.Systems + .SQUARE_SIZE, positionBefore.y + j2 * Datas.Systems + .SQUARE_SIZE, positionBefore.z + k2 * Datas.Systems + .SQUARE_SIZE); + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBeforePlus)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + let [b, y] = this.checkSprites(mapPortion, new Position(jpositionBefore.x + i2, jpositionBefore + .y + j2, jpositionBefore.z + k2), [], object); + // If before and after collides, get up! + if (b === null) { + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); + return [null, y, result[2]]; + } + } + } + } + } + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); + return [false, null, Enum.Orientation.None]; + } + return [result[0], result[1], result[2]]; + } + if (result[0]) { + block = true; + } + if (result[1] !== null) { + if (yMountain === null || yMountain < result[1]) { + yMountain = result[1]; + } + } + } + } + } + } + if (block && (yMountain === null)) { + return [true, null, Enum.Orientation.None]; + } + + // Test objects + if (!reverseTestObjects) { + let result = this.checkObjectsRay(positionAfter, object); + if (result !== null) { + return result; + } + } + + // Check empty square or square mountain height possible down + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( + positionAfter)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + let floors: number[]; + if (mapPortion) { + floors = mapPortion.squareNonEmpty[jpositionAfter.x % Constants + .PORTION_SIZE][jpositionAfter.z % Constants.PORTION_SIZE]; + let otherMapPortion = Scene.Map.current.getMapPortion(portion.x, portion.y + 1, portion.z); + if (otherMapPortion) { + floors = floors.concat(otherMapPortion.squareNonEmpty[jpositionAfter + .x %Constants.PORTION_SIZE][jpositionAfter.z % Constants + .PORTION_SIZE]); + } + if (yMountain === null && floors.indexOf(positionAfter.y) === -1) { + let l = floors.length; + if (l === 0) { + return [true, null, Enum.Orientation.None]; + } else { + let maxY = null; + let limitY = positionAfter.y - Datas.Systems + .mountainCollisionHeight.getValue(); + let temp: number; + for (i = 0; i < l; i++) { + temp = floors[i]; + if (temp <= (positionAfter.y + Datas.Systems + .mountainCollisionHeight.getValue()) && temp >= + limitY) + { + if (maxY === null) { + maxY = temp; + } else { + if (maxY < temp) { + maxY = temp; + } + } + } + } + if (maxY === null) { + // redo with before pos for going down two following height angled + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBefore)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + floors = mapPortion.squareNonEmpty[jpositionBefore.x % Constants + .PORTION_SIZE][jpositionBefore.z % Constants.PORTION_SIZE]; + let otherMapPortion = Scene.Map.current.getMapPortion(portion.x, portion.y + 1, portion.z); + if (otherMapPortion) { + floors = floors.concat(otherMapPortion.squareNonEmpty[jpositionBefore.x % + Constants.PORTION_SIZE][jpositionBefore.z % Constants + .PORTION_SIZE]); + } + if (yMountain === null && floors.indexOf(positionBefore.y) === -1) { + let l = floors.length; + if (l === 0) { + return [null, null, Enum.Orientation.None]; + } else { + let maxY = null; + let limitY = positionBefore.y - Datas.Systems + .mountainCollisionHeight.getValue(); + let temp: number; + for (i = 0; i < l; i++) { + temp = floors[i]; + if (temp <= (positionBefore.y + Datas.Systems + .mountainCollisionHeight.getValue()) && temp >= + limitY) + { + if (maxY === null) { + maxY = temp; + } else { + if (maxY < temp) { + maxY = temp; + } + } + } + } + if (maxY === null) { + if (object.orientation === object.previousOrientation) { + // If non empty square on front of object, then force move front + let positionFront = positionBefore.clone(); + switch (object.orientationEye) { + case Enum.Orientation.North: + positionFront.setZ(positionFront.z - (Datas.Systems.SQUARE_SIZE / 2)); + break; + case Enum.Orientation.South: + positionFront.setZ(positionFront.z + (Datas.Systems.SQUARE_SIZE / 2)); + break; + case Enum.Orientation.West: + positionFront.setX(positionFront.x - (Datas.Systems.SQUARE_SIZE / 2)); + break; + case Enum.Orientation.East: + positionFront.setX(positionFront.x + (Datas.Systems.SQUARE_SIZE / 2)); + break; + } + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionFront)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + floors = mapPortion.squareNonEmpty[Math.floor(positionFront.x / Datas.Systems.SQUARE_SIZE) % Constants.PORTION_SIZE][Math.floor(positionFront.z / Datas.Systems.SQUARE_SIZE) % Constants.PORTION_SIZE]; + if (floors.length > 0) { + for (let y of floors) { + if (y === positionFront.y) { + return [false, null, Enum.Orientation.None]; + } + } + } + } + } + // Check if climbing stuff in 1 px bottom + const positionBottom = positionBefore.clone(); + positionBottom.setY(positionBottom.y - 1); + for (i = startI; i <= endI; i++) { + for (j = startJ; j <= endJ; j++) { + for (k = startK; k <= endK; k++) { + positionBeforePlus.set(positionBefore.x + i * Datas.Systems + .SQUARE_SIZE, (positionBefore.y + j * Datas.Systems + .SQUARE_SIZE) - 1, positionBefore.z + k * Datas.Systems + .SQUARE_SIZE); + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionBeforePlus)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + const jpositionBottom = Position.createFromVector3(positionBeforePlus); + const climbingUp = object.isClimbingUp; + object.isClimbingUp = false; + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBottom); + let [b, y, o] = this.checkSprites(mapPortion, jpositionBottom, [], object); + if (b === null) { + // Check if after moving the collision still occurs. If not, go down + const positionBottomAfter = positionAfter.clone(); + positionBottomAfter.setY(positionBottomAfter.y - 1); + for (i2 = startI; i2 <= endI; i2++) { + for (j2 = startJ; j2 <= endJ; j2++) { + for (k2 = startK; k2 <= endK; k2++) { + positionAfterPlus.set(positionAfter.x + i2 * Datas.Systems + .SQUARE_SIZE, (positionAfter.y + j2 * Datas.Systems + .SQUARE_SIZE) - 1, positionAfter.z + k2 * Datas.Systems + .SQUARE_SIZE); + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3(positionAfterPlus)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + if (mapPortion) { + const jpositionBottomAfter = Position.createFromVector3(positionAfterPlus); + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionBottomAfter); + b = this.checkSprites(mapPortion, jpositionBottomAfter, [], object)[0]; + if (b === null) { + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); + object.isClimbingUp = climbingUp; + return [null, null, Enum.Orientation.None]; + } + } + } + } + } + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); + object.isClimbingUp = climbingUp; + return [null, y, o]; + } + object.updateMeshBBPosition(object.currentBoundingBox, bbSettings, positionAfter); + object.isClimbingUp = climbingUp; + } + } + } + } + return [true, null, Enum.Orientation.None]; + } else { + yMountain = maxY; + } + } + } + } + } else { + yMountain = maxY; + } + } + } + + // Check lands inside collisions + portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( + positionBefore)); + mapPortion = Scene.Map.current.getMapPortionFromPortion(portion); + return [this.checkLandsInside(mapPortion, jpositionBefore, + jpositionAfter, direction), yMountain, Enum.Orientation.None]; + } + return [true, null, Enum.Orientation.None]; + } + + static checkObjectsRay(positionAfter: Vector3, object: MapObject): [boolean, + number, Enum.Orientation] { + // Check collision inside & with other objects + if (object !== Game.current.hero && object.checkCollisionObject(Game + .current.hero)) { + return [true, null, Enum.Orientation.None]; + } + + // Check objects collisions + let portion = Scene.Map.current.getLocalPortion(Portion.createFromVector3( + positionAfter)); + let i: number, j: number, mapPortion: MapPortion; + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + mapPortion = Scene.Map.current.getMapPortion(portion.x + i, + portion.y, portion.z + j); + if (mapPortion && this.checkObjects(mapPortion, object)) { + return [true, null, Enum.Orientation.None]; + } + } + } + return null; + } + + /** + * Check if there is a collision at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionBefore - The json position before collision + * @param {Position} jpositionAfter - The json position after collision + * @param {Vector3} positionAfter - The position after collision + * @param {MapObject} object - The map object collision test + * @param {Vector3} direction - The direction collision + * @param {StructMapElementCollision[]} testedCollisions - The object + * collisions that were already tested + * @returns {boolean} + */ + static check(mapPortion: MapPortion, jpositionBefore: Position, + jpositionAfter: Position, positionAfter: Vector3, object: + MapObject, direction: Vector3, testedCollisions: + StructMapElementCollision[]): [boolean, number, Enum.Orientation] + { + // Check sprites and climbing + let [isCollision, yMountain, o] = this.checkSprites(mapPortion, + jpositionAfter, testedCollisions, object); + // Climbing + if (isCollision || yMountain !== null) { + return [isCollision, yMountain, o]; + } + + // Check mountain collision first for elevation + [isCollision, yMountain] = this.checkMountains(mapPortion, jpositionAfter, + positionAfter, testedCollisions, object); + if (isCollision) { + return [isCollision, yMountain, Enum.Orientation.None]; + } + + // Check other tests + return [(this.checkLands(mapPortion, jpositionBefore, jpositionAfter, + object, direction, testedCollisions) || this.checkObjects3D( + mapPortion, jpositionAfter, positionAfter, testedCollisions, object)), + yMountain, Enum.Orientation.None]; + } + + /** + * Check if there is a collision with lands at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionBefore - The json position before collision + * @param {Position} jpositionAfter - The json position after collision + * @param {MapObject} object - The map object collision test + * @param {Vector3} direction - The direction collision + * @param {StructMapElementCollision[]} testedCollisions - The object + * collisions that were already tested + * @returns {boolean} + */ + static checkLands(mapPortion: MapPortion, jpositionBefore: Position, + jpositionAfter: Position, object: MapObject, direction: THREE + .Vector3, testedCollisions: StructMapElementCollision[]): boolean + { + let index = jpositionAfter.toIndex() + let lands = mapPortion.boundingBoxesLands[index]; + if (lands !== null) { + let objCollision: StructMapElementCollision, boundingBox: number[], + collision: CollisionSquare; + for (let i = 0, l = lands.length; i < l; i++) { + objCollision = lands[i]; + if (testedCollisions.indexOf(objCollision) === -1) { + testedCollisions.push(objCollision); + if (objCollision !== null) { + boundingBox = objCollision.b; + collision = objCollision.cs; + if (this.checkIntersectionLand(collision, boundingBox, + object) || this.checkDirections(jpositionBefore, + jpositionAfter, collision, boundingBox, direction, + object)) + { + return true; + } + } + } + } + } + return false; + } + + /** + * Check if there is a collision with lands with directions. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionBefore - The json position before collision + * @param {Position} jpositionAfter - The json position after collision + * @param {Vector3} direction - The direction collision + * @returns {boolean} + */ + static checkLandsInside(mapPortion: MapPortion, jpositionBefore: Position, + jpositionAfter: Position, direction: Vector3): boolean + { + let lands = mapPortion.boundingBoxesLands[jpositionBefore.toIndex()]; + if (lands !== null) { + let objCollision: StructMapElementCollision, collision: + CollisionSquare; + for (let i = 0, l = lands.length; i < l; i++) { + objCollision = lands[i]; + if (objCollision !== null) { + collision = objCollision.cs; + if (this.checkDirectionsInside(jpositionBefore, + jpositionAfter, collision, direction)) + { + return true; + } + } + } + } + return false; + } + + /** + * Check intersection between ray and an object. + * @static + * @param {StructMapElementCollision} collision - The collision object + * @param {number[]} boundingBox - The bounding box values + * @param {MapObject} object - The map object to check + * @returns {boolean} + */ + static checkIntersectionLand(collision: StructMapElementCollision, + boundingBox: number[], object: MapObject): boolean + { + if (collision !== null) { + return false; + } + this.applyBoxLandTransforms(this.BB_BOX, boundingBox); + return this.obbVSobb(object.currentBoundingBox.geometry, + this.BB_BOX.geometry); + } + + /** + * Check directions + * @static + * @param {Position} jpositionBefore - The json position before collision + * @param {Position} jpositionAfter - The json position after collision + * @param {StructMapElementCollision} collision - The collision object + * @param {number[]} boundingBox - The bounding box values + * @param {Vector3} direction - The direction collision + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkDirections(jpositionBefore: Position, jpositionAfter: Position, + collision: StructMapElementCollision, boundingBox: number[], direction: + Vector3, object: MapObject): boolean + { + if (collision === null) { + return false; + } + if (!jpositionBefore.equals(jpositionAfter)) { + if (this.checkIntersectionLand(null, boundingBox, object)) { + if (direction.x > 0) { + return !collision.left; + } + if (direction.x < 0) { + return !collision.right; + } + if (direction.z > 0) { + return !collision.top; + } + if (direction.z < 0) { + return !collision.bot; + } + } + } + return false; + } + + /** + * Check directions inside. + * @static + * @param {Position} jpositionBefore - The json position before collision + * @param {Position} jpositionAfter - The json position after collision + * @param {StructMapElementCollision} collision - The collision object + * @param {Vector3} direction - The direction collision + * @returns {boolean} + */ + static checkDirectionsInside(jpositionBefore: Position, jpositionAfter: + Position, collision: StructMapElementCollision, direction: THREE + .Vector3): boolean + { + if (collision === null) { + return false; + } + if (!jpositionBefore.equals(jpositionAfter)) { + if (direction.x > 0) { + return !collision.right; + } + if (direction.x < 0) { + return !collision.left; + } + if (direction.z > 0) { + return !collision.bot; + } + if (direction.z < 0) { + return !collision.top; + } + } + return false; + } + + /** + * Check if there is a collision with sprites at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionAfter - The json position after collision + * @param {StructMapElementCollision[]} testedCollisions - The object + * collisions that were already tested + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkSprites(mapPortion: MapPortion, jpositionAfter: + Position, testedCollisions: StructMapElementCollision[], object: + MapObject): [boolean, number, Enum.Orientation] + { + let sprites = mapPortion.boundingBoxesSprites[jpositionAfter.toIndex()]; + let tested = false; + if (sprites !== null) { + let objCollision: StructMapElementCollision; + for (let i = 0, l = sprites.length; i < l; i++) { + objCollision = sprites[i]; + if (testedCollisions.indexOf(objCollision) === -1) { + testedCollisions.push(objCollision); + if (this.checkIntersectionSprite(objCollision.b, objCollision.k, object)) { + if (objCollision.cl) { + const speed = object.speed.getValue() * MapObject + .SPEED_NORMAL * Manager.Stack.averageElapsedTime * + Datas.Systems.SQUARE_SIZE * Datas.Systems.climbingSpeed.getValue(); + const limitTop = objCollision.b[1] + Math.ceil(objCollision.b[4] / 2); + const limitBot = objCollision.b[1] - Math.ceil(objCollision.b[4] / 2); + const y = object.isClimbingUp ? Math.min(object.position.y + speed, limitTop) : Math.max(object.position.y - speed, limitBot); + if (y === object.position.y) { + continue; + } + let angle = objCollision.b[6]; + let force = false, front = false; + if (angle === 0 || angle === 180) { + force = true; + front = true; + } else if (angle === 90 || angle === 270) { + force = true; + } + return [null, y, object.getOrientationBetweenPosition(objCollision.l, force, front)]; + } + if (!object.isClimbing) { + tested = true; + } + } + } + } + } + return [tested, null, Enum.Orientation.None]; + } + + /** + * Check intersection between ray and an object. + * @static + * @param {number[]} boundingBox - The bounding box values + * @param {boolean} fix - Indicate if the sprite is fix or not + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkIntersectionSprite(boundingBox: number[], fix: boolean, object: + MapObject): boolean + { + if (boundingBox === null) { + return false; + } + if (fix) { + this.applyBoxSpriteTransforms(this.BB_BOX, boundingBox, true); + return this.obbVSobb(object.currentBoundingBox + .geometry, this.BB_BOX.geometry); + } else { + this.applyOrientedBoxTransforms(this.BB_ORIENTED_BOX, boundingBox); + return this.obbVSobb(object.currentBoundingBox + .geometry, this.BB_ORIENTED_BOX.geometry); + } + } + + /** + * Check if there is a collision with sprites at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionAfter - The json position after collision + * @param {THREE.Vector3} positionAfter - The position after collision + * @param {StructMapElementCollision[]} testedCollisions - The object + * collisions that were already tested + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkObjects3D(mapPortion: MapPortion, jpositionAfter: Position, + positionAfter: THREE.Vector3, testedCollisions: StructMapElementCollision[], + object: MapObject): boolean + { + let objects3D = mapPortion.boundingBoxesObjects3D[jpositionAfter.toIndex()]; + if (objects3D !== null) { + let objCollision: StructMapElementCollision; + for (let i = 0, l = objects3D.length; i < l; i++) { + objCollision = objects3D[i]; + if (!objCollision) + continue; + if (testedCollisions.indexOf(objCollision) === -1) { + testedCollisions.push(objCollision); + if (objCollision.id) { + if (this.checkCustomObject3D(objCollision, object, + positionAfter)) { + return true; + } + } else { + if (this.checkIntersectionSprite(objCollision.b, + objCollision.k, object)) + { + return true; + } + } + } + } + } + return false; + } + + /** + * Check if there is a collision with custom object 3D collision. + * @static + * @param {StructMapElementCollision} objCollision - The object colision + * info to test + * @param {MapObject} object - The map object collision test + * @param {THREE.Vector3} positionAfter - The position after collision + * @returns {boolean} + */ + static checkCustomObject3D(objCollision: StructMapElementCollision, object: + MapObject, positionAfter: THREE.Vector3): boolean + { + // Remove previous + let mesh = Datas.Shapes.get(Enum.CustomShapeKind.Collisions, objCollision.id).mesh; + if (mesh !== this.currentCustomObject3D) { + Scene.Map.current.scene.remove(this.currentCustomObject3D); + this.currentCustomObject3D = mesh; + } + if (this.currentCustomObject3D) { + this.currentCustomObject3D.position.set(objCollision.l.x, objCollision.l.y, objCollision.l.z); + this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(1,0,0), objCollision.b[6]); + this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(0,1,0), objCollision.b[7]); + this.currentCustomObject3D.setRotationFromAxisAngle(new THREE.Vector3(0,0,1), objCollision.b[8]); + Scene.Map.current.scene.add(this.currentCustomObject3D); + if (Datas.Systems.showBB) { + this.currentCustomObject3D.material = this.BB_MATERIAL; + } else { + this.currentCustomObject3D.material = this.BB_EMPTY_MATERIAL; + } + let direction = positionAfter.clone().sub(object.position).normalize(); + if (this.checkIntersectionMeshes(object.currentBoundingBox, this + .currentCustomObject3D, direction)) { + return true; + } + } + return false; + } + + /** + * Check intersection between two complex meshes. + * @static + * @param {THREE.Mesh} meshA - The first mesh + * @param {THREE.Mesh} meshB - The second mesh + * @param {THREE.Vector3} direction - The meshA direction to mesh B + * @returns {boolean} + */ + static checkIntersectionMeshes(meshA: THREE.Mesh, meshB: THREE.Mesh, direction: THREE.Vector3): boolean { + let vertices = meshA.geometry.getVerticesVectors(); + let raycaster = new THREE.Raycaster(); + let directionNegate = direction.clone().negate(); + let collisionResults: THREE.Intersection[]; + for (let vertex of vertices) { + raycaster.set(vertex, direction); + collisionResults = raycaster.intersectObject(meshB); + if (collisionResults.length === 0) { + raycaster.set(vertex, directionNegate); + collisionResults = raycaster.intersectObject(meshB); + } + if (collisionResults.length > 0) { + raycaster.set(collisionResults[0].point, new THREE.Vector3(1,1,1)); + const intersects = raycaster.intersectObject(meshA); + if (intersects.length > 0) { // Points is in objet + return true; + } + } + } + return false; + } + + /** + * Check if there is a collision with mountains at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionAfter - The json position after collision + * @param {Vector3} positionAfter - The position after collision + * @param {StructMapElementCollision[]} testedCollisions - The object collisions that were + * already tested + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkMountains(mapPortion: MapPortion, jpositionAfter: + Position, positionAfter: Vector3, testedCollisions: + StructMapElementCollision[], object: MapObject): [boolean, number] + { + let yMountain = null; + let mountains = mapPortion.boundingBoxesMountains[jpositionAfter.toIndex()]; + let block = false; + let i: number, l: number, result: [boolean, boolean, number]; + if (mountains !== null) { + for (i = 0, l = mountains.length; i < l; i++) { + result = this.checkMountain(mapPortion, jpositionAfter, + positionAfter, testedCollisions, object, mountains[i], + yMountain, block); + if (result[0]) { + return [result[1], result[2]]; + } else { + block = result[1]; + yMountain = result[2]; + } + } + } + let j: number, m: number, objCollision: StructMapElementCollision[], + position: Position, mapPortionOverflow: MapPortion; + for (i = 0, l = mapPortion.overflowMountains.length; i < l; i++) { + position = mapPortion.overflowMountains[i]; + mapPortionOverflow = Scene.Map.current.getMapPortionFromPortion(Scene + .Map.current.getLocalPortion(position.getGlobalPortion())); + if (!mapPortionOverflow) { + continue; + } + objCollision = mapPortionOverflow.getObjectCollisionAt(position, + jpositionAfter, ElementMapKind.Mountains); + for (j = 0, m = objCollision.length; j < m; j++) { + result = this.checkMountain(mapPortion, jpositionAfter, + positionAfter, testedCollisions, object, objCollision[j], + yMountain, block); + if (result[0]) { + return [result[1], result[2]]; + } else { + block = result[1]; + yMountain = result[2]; + } + } + } + return [block && (yMountain === null), yMountain]; + } + + /** + * Check if there is a collision with mountains at this position. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionAfter - The json position after collision + * @param {Vector3} positionAfter - The position after collision + * @param {StructMapElementCollision[]} testedCollisions - The object + * collisions that were already tested + * @param {MapObject} object - The map object collision test + * @param {StructMapElementCollision} objCollision - The object collision + * @param {number} yMountain - The y mountain collision + * @param {boolean} block - The block mountain collision + * @returns {[boolean, boolean, number]} + */ + static checkMountain(mapPortion: MapPortion, jpositionAfter: Position, + positionAfter: Vector3, testedCollisions: + StructMapElementCollision[], object: MapObject, objCollision: + StructMapElementCollision, yMountain: number, block: boolean): [boolean, + boolean, number] + { + if (testedCollisions.indexOf(objCollision) === -1) { + testedCollisions.push(objCollision); + let result = this.checkIntersectionMountain(mapPortion, + jpositionAfter, positionAfter, objCollision, object); + if (result[0]) { + if (result[1] === null) { + return [true, result[0], result[1]]; + } else { + block = true; + } + } else if (result[1] !== null) { + if (yMountain === null || yMountain < result[1]) { + yMountain = result[1]; + } + } + } + return [false, block, yMountain]; + } + + /** + * Check intersection with a mountain. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {Position} jpositionAfter - The json position after collision + * @param {Vector3} positionAfter - The position after collision + * @param {StructMapElementCollision} objCollision - The object collision + * @param {MapObject} object - The map object collision test + * @returns {[boolean, number]} + */ + static checkIntersectionMountain(mapPortion: MapPortion, jpositionAfter: + Position, positionAfter: Vector3, objCollision: + StructMapElementCollision, object: MapObject): [boolean, number] + { + let mountain = objCollision.t; + let forceAlways = ( mountain.getSystem()).forceAlways(); + let forceNever = ( mountain.getSystem()).forceNever(); + let point = new Vector2(positionAfter.x, positionAfter.z); + let x = objCollision.l.x; + let y = objCollision.l.y; + let z = objCollision.l.z; + let w = objCollision.rw; + let h = objCollision.rh; + + // If not in the height, no test + if (positionAfter.y < y || positionAfter.y > y + h) { + return [false, null]; + } + + // if w = 0, check height + if (objCollision.rw === 0) { + let pass = forceNever || -(!forceAlways && ((y + h) <= (positionAfter + .y + Datas.Systems.mountainCollisionHeight.getValue()))); + if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems.SQUARE_SIZE + , z, z + Datas.Systems.SQUARE_SIZE)) + { + return pass ? [false, (positionAfter.y - y - h) === 0 ? null : y + h] : [true, null]; + + } else { + if (!pass) { + // Collide with BB (avoiding use of checkIntersectionSprite here for perfs issues) + let vertices = object.currentBoundingBox.geometry.getVertices(); + let vy = 0; + for (let i = 0, l = vertices.length; i < l; i += 3) { + vy = vertices[i + 1]; + if (vy >= y && vy <= y + h) { + point = new Vector2(vertices[i], vertices[i + 2]); + if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems + .SQUARE_SIZE, z, z + Datas.Systems.SQUARE_SIZE)) + { + return [true, null]; + } + } + } + } + } + } else { + // if w > 0, go like a slope + // Get coplanar points according to side + let ptA: Vector2, ptB: Vector2, ptC: Vector2, pA: + Vector3, pB: Vector3, pC: Vector3; + if (objCollision.left && !mountain.left) { + if (objCollision.top && !mountain.top) { + ptA = new Vector2(x - w, z); + ptB = new Vector2(x, z); + ptC = new Vector2(x, z - w); + if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { + pA = new Vector3(ptA.x, y, ptA.y); + pB = new Vector3(ptB.x, y + h, ptB.y); + pC = new Vector3(ptC.x, y, ptC.y); + } else { + return [false, null]; + } + } else if (objCollision.bot && !mountain.bot) { + ptA = new Vector2(x - w, z + Datas.Systems.SQUARE_SIZE); + ptB = new Vector2(x, z + Datas.Systems.SQUARE_SIZE); + ptC = new Vector2(x, z + Datas.Systems.SQUARE_SIZE + w); + if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { + pA = new Vector3(ptA.x, y, ptA.y); + pB = new Vector3(ptB.x, y + h, ptB.y); + pC = new Vector3(ptC.x, y, ptC.y); + } else { + return [false, null]; + } + } else { + if (Mathf.isPointOnRectangle(point, x - w, x, z, z + Datas + .Systems.SQUARE_SIZE)) + { + pA = new Vector3(x - w, y, z); + pB = new Vector3(x, y + h, z); + pC = new Vector3(x, y + h, z + Datas.Systems.SQUARE_SIZE); + } else { + return [false, null]; + } + } + } else if (objCollision.right && !mountain.right) { + if (objCollision.top && !mountain.top) { + ptA = new Vector2(x + Datas.Systems.SQUARE_SIZE, z - w); + ptB = new Vector2(x + Datas.Systems.SQUARE_SIZE, z); + ptC = new Vector2(x + Datas.Systems.SQUARE_SIZE + w, z); + if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { + pA = new Vector3(ptA.x, y, ptA.y); + pB = new Vector3(ptB.x, y + h, ptB.y); + pC = new Vector3(ptC.x, y, ptC.y); + } else { + return [false, null]; + } + } else if (objCollision.bot && !mountain.bot) { + ptA = new Vector2(x + Datas.Systems.SQUARE_SIZE, z + + Datas.Systems.SQUARE_SIZE + w); + ptB = new Vector2(x + Datas.Systems.SQUARE_SIZE, z + + Datas.Systems.SQUARE_SIZE); + ptC = new Vector2(x + Datas.Systems.SQUARE_SIZE + w, z + + Datas.Systems.SQUARE_SIZE); + if (Mathf.isPointOnTriangle(point, ptA, ptB, ptC)) { + pA = new Vector3(ptA.x, y, ptA.y); + pB = new Vector3(ptB.x, y + h, ptB.y); + pC = new Vector3(ptC.x, y, ptC.y); + } else { + return [false, null]; + } + } else { + if (Mathf.isPointOnRectangle(point, x + Datas.Systems + .SQUARE_SIZE, x + Datas.Systems.SQUARE_SIZE + w, z, z + + Datas.Systems.SQUARE_SIZE)) + { + pA = new Vector3(x + Datas.Systems.SQUARE_SIZE, y + + h, z + Datas.Systems.SQUARE_SIZE); + pB = new Vector3(x + Datas.Systems.SQUARE_SIZE, y + + h, z); + pC = new Vector3(x + Datas.Systems.SQUARE_SIZE + w + , y, z); + } else { + return [false, null]; + } + } + } else { + if (objCollision.top && !mountain.top) { + if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems + .SQUARE_SIZE, z - w, z)) + { + pA = new Vector3(x, y + h, z); + pB = new Vector3(x, y, z - w); + pC = new Vector3(x + Datas.Systems.SQUARE_SIZE, y, + z - w); + } else { + return [false, null]; + } + } else if (objCollision.bot && !mountain.bot) { + if (Mathf.isPointOnRectangle(point, x, x + Datas.Systems + .SQUARE_SIZE, z + Datas.Systems.SQUARE_SIZE, z + Datas + .Systems.SQUARE_SIZE + w)) + { + pA = new Vector3(x + Datas.Systems.SQUARE_SIZE, y, + z + Datas.Systems.SQUARE_SIZE + w); + pB = new Vector3(x, y, z + Datas.Systems + .SQUARE_SIZE + w); + pC = new Vector3(x, y + h, z + Datas.Systems + .SQUARE_SIZE); + } else { + return [false, null]; + } + } else { + return [false, null]; + } + } + // Get the intersection point for updating mountain y + let plane = new THREE.Plane(); + let ray = new THREE.Ray(new Vector3(positionAfter.x, y, + positionAfter.z), new Vector3(0, 1, 0)); + let newPosition = new Vector3(); + plane.setFromCoplanarPoints(pA, pB, pC); + ray.intersectPlane(plane, newPosition); + + // If going down, check if there's a blocking floor + let jposition = (newPosition.y - positionAfter.y) < 0 ? new Position + (Math.floor(positionAfter.x / Datas.Systems.SQUARE_SIZE), Math + .ceil(positionAfter.y / Datas.Systems.SQUARE_SIZE), Math.floor( + positionAfter.z / Datas.Systems.SQUARE_SIZE)) : jpositionAfter; + mapPortion = Scene.Map.current.getMapPortionFromPortion(Scene.Map + .current.getLocalPortion(jposition.getGlobalPortion())); + let isFloor = mapPortion.boundingBoxesLands[jposition.toIndex()] + .length > 0; + if (isFloor && (newPosition.y - positionAfter.y) < 0) { + return [false, null]; + } + + // If angle limit, block + if (forceAlways || (!forceNever && mountain.angle > Datas.Systems + .mountainCollisionAngle.getValue())) + { + // Check if floor existing on top of the mountain angle + isFloor = jposition.y === jpositionAfter.y ? false : + mapPortion.boundingBoxesLands[jpositionAfter.toIndex()] + .length > 0; + return [!isFloor, null]; + } + return [!forceNever && (Math.abs(newPosition.y - positionAfter.y) > + Datas.Systems.mountainCollisionHeight.getValue()), newPosition.y]; + } + return [false, null]; + } + + /** + * Check collision with objects. + * @static + * @param {MapPortion} mapPortion - The map portion to check + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkObjects(mapPortion: MapPortion, object: MapObject): boolean { + let datas = Scene.Map.current.getObjectsAtPortion(mapPortion.portion); + return this.checkObjectsList(mapPortion.objectsList, object) || this + .checkObjectsList(datas.min, object) || this.checkObjectsList(datas + .mout, object); + } + + /** + * Check collision with objects. + * @static + * @param {MapObject[]} list - The map objects list to test + * @param {MapObject} object - The map object collision test + * @returns {boolean} + */ + static checkObjectsList(list: MapObject[], object: MapObject): boolean { + let obj: MapObject; + for (let i = 0, l = list.length; i < l; i++) { + obj = list[i]; + if (obj !== object) { + if (object.checkCollisionObject(obj)) { + return true; + } + } + } + return false; + } } export { Collisions }; \ No newline at end of file diff --git a/src/Manager/Plugins.ts b/src/Manager/Plugins.ts index 09dd25fb..48069e02 100644 --- a/src/Manager/Plugins.ts +++ b/src/Manager/Plugins.ts @@ -10,12 +10,10 @@ import { IO, Paths, Constants, Utils } from "../Common"; import { System } from "../index"; -type ClassMethod = (this:T["prototype"], ...args:Parameters) => ReturnType - /** @class * The class who handles plugins of RPG Paper Maker. * @static - * @author Nio Kasgami, Wano, Trico Everfire + * @author Nio Kasgami, Wano */ class Plugins { @@ -184,6 +182,8 @@ class Plugins { */ } + + /** * @static * @usage This function is used to inject/overwrite original class methods and variables. @@ -195,8 +195,8 @@ class Plugins { * @param overwrite (METHODS ONLY) Should call original method's code or overwrite original method. (DEFAULT: false) * @param loadBefore (METHODS ONLY) Should original method's code be executed before or after your code (NOTE: This is obviously disabled if param overwrite is set to true.) (DEFAULT: true) */ - static inject, TR = string, LM = NewableFunction>( - classObject: T, prototypeName: LT | TR | M, prototype:CXT + static inject( + classObject: T, prototypeName: LT | TR | M, prototype:T["prototype"][LT] | T[M] | LM, staticType :boolean = false, overwrite: boolean = false, loadOriginalBefore: boolean = true) { let TheAnyPrototype:any = prototype; //force any type, system will not accept otherwise! diff --git a/src/Scene/Battle.ts b/src/Scene/Battle.ts index 0060ad4f..72ed5672 100644 --- a/src/Scene/Battle.ts +++ b/src/Scene/Battle.ts @@ -583,8 +583,8 @@ class Battle extends Map { break; } } - this.sceneMap.camera.distance = this.mapCameraDistance; - this.sceneMap.camera.update(); + this.sceneMap.camera.distance = this.mapCameraDistance; + this.sceneMap.camera.update(); } /** diff --git a/src/Scene/ChangeLanguage.ts b/src/Scene/ChangeLanguage.ts index a7cbd230..b35890d6 100644 --- a/src/Scene/ChangeLanguage.ts +++ b/src/Scene/ChangeLanguage.ts @@ -133,6 +133,8 @@ class ChangeLanguage extends Base { .listOrder[this.windowChoicesMain.currentSelectedIndex]); Manager.Stack.translateAll(); this.step = 0; + this.createWindowBoxLanguage(); + this.createWindowBoxTop(); Manager.Stack.requestPaintHUD = true; return true; }, @@ -211,11 +213,10 @@ class ChangeLanguage extends Base { switch (this.step) { case 0: this.windowChoicesMain.onKeyPressed(key, this); - if (Datas.Keyboards.checkActionMenu(key)) { + if (Datas.Keyboards.checkActionMenu(key)) this.action(); - } else if (Datas.Keyboards.checkCancelMenu) { + else if (Datas.Keyboards.checkCancelMenu(key)) this.cancel(); - } break; case 1: this.windowChoicesConfirm.onKeyPressed(key, this); diff --git a/src/Scene/Map.ts b/src/Scene/Map.ts index 0c4ff4d4..fe36c285 100644 --- a/src/Scene/Map.ts +++ b/src/Scene/Map.ts @@ -1112,7 +1112,7 @@ class Map extends Base { super.update(); // Update camera hiding - if (Datas.Systems.moveCameraOnBlockView.getValue()) { + if (Game.current !== null && Datas.Systems.moveCameraOnBlockView.getValue()) { this.camera.forceNoHide = false; this.camera.hidingDistance = -1; let pointer = Manager.GL.toScreenPosition(this.camera.target.position @@ -1259,10 +1259,7 @@ class Map extends Base { onMouseUp(x: number, y: number) { if (!this.loading) { if (!ReactionInterpreter.blockingHero && !this.isBattleMap) { - Manager.Events.sendEvent(null, 2, 0, true, 6, [null, System - .DynamicValue.createNumber(x), System.DynamicValue - .createNumber(y), System.DynamicValue.createSwitch(Inputs - .mouseLeftPressed)], true, false); + Manager.Events.sendEvent(null, 2, 0, true, 6, [null, System.DynamicValue.createSwitch(Inputs.mouseLeftPressed)], true, false); } super.onMouseUp(x, y); } diff --git a/src/Scene/SaveLoadGame.ts b/src/Scene/SaveLoadGame.ts index cd331fcc..3c158516 100644 --- a/src/Scene/SaveLoadGame.ts +++ b/src/Scene/SaveLoadGame.ts @@ -41,7 +41,7 @@ class SaveLoadGame extends Base { this.gamesDatas.push(null); const newGame = new Game(i); await newGame.load(); - Game.current = newGame; + Game.current = newGame; this.initializeGame(Game.current); } Game.current = currentGame; @@ -56,7 +56,7 @@ class SaveLoadGame extends Base { this.windowChoicesSlots = new WindowChoices(10, 100, 100, 50, this .gamesDatas, { nbItemsMax: 6, - padding: WindowBox.NONE_PADDING + padding: [-15, -15, -15, -15] } ); this.windowBot = new WindowBox(20, ScreenResolution.SCREEN_Y - 50, diff --git a/src/Scene/TitleSettings.ts b/src/Scene/TitleSettings.ts index 47434e23..bf45602b 100644 --- a/src/Scene/TitleSettings.ts +++ b/src/Scene/TitleSettings.ts @@ -1,16 +1,16 @@ /* - RPG Paper Maker Copyright (C) 2017-2023 Wano + RPG Paper Maker Copyright (C) 2017-2023 Wano - RPG Paper Maker engine is under proprietary license. - This source code is also copyrighted. + RPG Paper Maker engine is under proprietary license. + This source code is also copyrighted. - Use Commercial edition for commercial use of your games. - See RPG Paper Maker EULA here: - http://rpg-paper-maker.com/index.php/eula. + Use Commercial edition for commercial use of your games. + See RPG Paper Maker EULA here: + http://rpg-paper-maker.com/index.php/eula. */ import { Base } from "./Base"; -import { Datas, Graphic, Manager } from "../index"; +import { Datas, Graphic, Manager, System } from "../index"; import { Picture2D, WindowBox, WindowChoices } from "../Core"; import { Enum, Constants, ScreenResolution, Inputs } from "../Common"; import PictureKind = Enum.PictureKind; @@ -22,137 +22,158 @@ import Align = Enum.Align; */ class TitleSettings extends Base { - public pictureBackground: Picture2D; - public windowSettings: WindowBox - public windowInformations: WindowBox; - public windowChoicesMain: WindowChoices; - public title: string; - - constructor(title: string) { - super(); - - this.title = title; - } - - /** - * Load async stuff. - */ - async load() { - // Creating background - if (Datas.TitlescreenGameover.isTitleBackgroundImage) { - this.pictureBackground = await Picture2D.createWithID(Datas - .TitlescreenGameover.titleBackgroundImageID, PictureKind - .TitleScreen, { cover: true }); - } - - // Creating windows - this.windowSettings = new WindowBox(Constants.HUGE_SPACE, Constants - .HUGE_SPACE, WindowBox.MEDIUM_SLOT_WIDTH, WindowBox - .LARGE_SLOT_HEIGHT, - { - content: new Graphic.Text(this.title, { align: Align.Center }), - padding: WindowBox.SMALL_SLOT_PADDING - } - ); - this.windowInformations = new WindowBox(Constants.HUGE_SPACE + WindowBox - .MEDIUM_SLOT_WIDTH + Constants.LARGE_SPACE, Constants.HUGE_SPACE, - ScreenResolution.SCREEN_X - (2 * Constants.HUGE_SPACE) - WindowBox - .MEDIUM_SLOT_WIDTH - Constants.LARGE_SPACE, WindowBox - .LARGE_SLOT_HEIGHT, - { - padding: WindowBox.SMALL_SLOT_PADDING - } - ); - this.windowChoicesMain = new WindowChoices(Constants.HUGE_SPACE, - Constants.HUGE_SPACE + WindowBox.LARGE_SLOT_HEIGHT + Constants - .LARGE_SPACE, ScreenResolution.SCREEN_X - (2 * Constants.HUGE_SPACE) - , WindowBox.MEDIUM_SLOT_HEIGHT, Datas.TitlescreenGameover - .getTitleSettingsCommandsContent(), - { - nbItemsMax: 9, - listCallbacks: Datas.TitlescreenGameover.getTitleSettingsCommandsActions(), - bordersInsideVisible: false - } - ); - this.windowInformations.content = this.windowChoicesMain - .getCurrentContent(); - - this.loading = false; - } - - /** - * Cancel the scene. - */ - cancel() { - Datas.Systems.soundCancel.playSound(); - Manager.Stack.pop(); - } - - /** - * Translate the scene if possible. - */ - translate() { - - } - - /** - * @inheritdoc - */ - update() { - this.windowChoicesMain.update(); - } - - /** - * Handle scene key pressed. - * @param {number} key - The key ID - */ - onKeyPressed(key: number) { - this.windowChoicesMain.onKeyPressed(key); - if (Datas.Keyboards.checkCancelMenu(key)) { - this.cancel(); - } - } - - /** - * Handle scene pressed and repeat key. - * @param {number} key - The key ID - * @returns {boolean} - */ - onKeyPressedAndRepeat(key: number): boolean { - this.windowChoicesMain.onKeyPressedAndRepeat(key); - this.windowInformations.content = this.windowChoicesMain - .getCurrentContent(); - return true; - } - - /** - * @inheritdoc - */ - onMouseMove(x: number, y: number) { - this.windowChoicesMain.onMouseMove(x, y); - } - - /** - * @inheritdoc - */ - onMouseUp(x: number, y: number) { - this.windowChoicesMain.onMouseUp(x, y); - if (Inputs.mouseRightPressed) { - this.cancel(); - } - } - - /** - * Draw the HUD scene. - */ - drawHUD() { - if (Datas.TitlescreenGameover.isTitleBackgroundImage) { - this.pictureBackground.draw(); - } - this.windowSettings.draw(); - this.windowInformations.draw(); - this.windowChoicesMain.draw(); - } + public pictureBackground: Picture2D; + public windowSettings: WindowBox + public windowInformations: WindowBox; + public windowChoicesMain: WindowChoices; + public title: string; + + constructor(title: string) { + super(); + + this.title = title; + } + + /** + * Load async stuff. + */ + async load() { + // Creating background + if (Datas.TitlescreenGameover.isTitleBackgroundImage) { + this.pictureBackground = await Picture2D.createWithID(Datas + .TitlescreenGameover.titleBackgroundImageID, PictureKind + .TitleScreen, { cover: true }); + } + + // Creating windows + this.windowSettings = new WindowBox(Constants.HUGE_SPACE, Constants + .HUGE_SPACE, WindowBox.MEDIUM_SLOT_WIDTH, WindowBox + .LARGE_SLOT_HEIGHT, + { + content: new Graphic.Text(this.title, { align: Align.Center }), + padding: WindowBox.SMALL_SLOT_PADDING + } + ); + this.windowInformations = new WindowBox(Constants.HUGE_SPACE + WindowBox + .MEDIUM_SLOT_WIDTH + Constants.LARGE_SPACE, Constants.HUGE_SPACE, + ScreenResolution.SCREEN_X - (2 * Constants.HUGE_SPACE) - WindowBox + .MEDIUM_SLOT_WIDTH - Constants.LARGE_SPACE, WindowBox + .LARGE_SLOT_HEIGHT, + { + padding: WindowBox.SMALL_SLOT_PADDING + } + ); + this.windowChoicesMain = new WindowChoices(Constants.HUGE_SPACE, + Constants.HUGE_SPACE + WindowBox.LARGE_SLOT_HEIGHT + Constants + .LARGE_SPACE, ScreenResolution.SCREEN_X - (2 * Constants.HUGE_SPACE) + , WindowBox.MEDIUM_SLOT_HEIGHT, Datas.TitlescreenGameover + .getTitleSettingsCommandsContent(), + { + nbItemsMax: 9, + listCallbacks: Datas.TitlescreenGameover.getTitleSettingsCommandsActions(), + bordersInsideVisible: false + } + ); + this.windowInformations.content = this.windowChoicesMain + .getCurrentContent(); + + this.loading = false; + } + + /** + * Cancel the scene. + */ + cancel() { + Datas.Systems.soundCancel.playSound(); + Manager.Stack.pop(); + } + + /** + * Translate the scene if possible. + */ + translate() { + this.windowChoicesMain = new WindowChoices(Constants.HUGE_SPACE, Constants.HUGE_SPACE + WindowBox.LARGE_SLOT_HEIGHT + Constants.LARGE_SPACE, ScreenResolution.SCREEN_X - (2 * Constants.HUGE_SPACE), WindowBox.MEDIUM_SLOT_HEIGHT, Datas.TitlescreenGameover.getTitleSettingsCommandsContent(), + { + nbItemsMax: 9, + listCallbacks: Datas.TitlescreenGameover.getTitleSettingsCommandsActions(), + bordersInsideVisible: false + }); + this.windowInformations.content = this.windowChoicesMain.getCurrentContent(); + var s = null; + const l = Datas.TitlescreenGameover.titleCommands; + for (var i = 0; i < l.length; i++) + if (l[i].kind === Enum.TitleCommandKind.Settings) + s = l[i].name(); + if (s !== null) + this.title = s; + this.windowSettings = new WindowBox(Constants.HUGE_SPACE, Constants + .HUGE_SPACE, WindowBox.MEDIUM_SLOT_WIDTH, WindowBox + .LARGE_SLOT_HEIGHT, { + content: new Graphic.Text(this.title, { align: Align.Center }), + padding: WindowBox.SMALL_SLOT_PADDING + }); + this.update(); + } + + /** + * @inheritdoc + */ + update() { + this.windowChoicesMain.update(); + } + + /** + * Handle scene key pressed. + * @param {number} key - The key ID + */ + onKeyPressed(key: number) { + this.windowChoicesMain.onKeyPressed(key); + if (Datas.Keyboards.checkCancelMenu(key)) { + this.cancel(); + } + } + + /** + * Handle scene pressed and repeat key. + * @param {number} key - The key ID + * @returns {boolean} + */ + onKeyPressedAndRepeat(key: number): boolean { + this.windowChoicesMain.onKeyPressedAndRepeat(key); + this.windowInformations.content = this.windowChoicesMain + .getCurrentContent(); + return true; + } + + /** + * @inheritdoc + */ + onMouseMove(x: number, y: number) { + this.windowChoicesMain.onMouseMove(x, y); + this.windowInformations.content = this.windowChoicesMain.getCurrentContent(); + } + + /** + * @inheritdoc + */ + onMouseUp(x: number, y: number) { + this.windowChoicesMain.onMouseUp(x, y); + if (Inputs.mouseRightPressed) { + this.cancel(); + } + } + + /** + * Draw the HUD scene. + */ + drawHUD() { + if (Datas.TitlescreenGameover.isTitleBackgroundImage) { + this.pictureBackground.draw(); + } + this.windowSettings.draw(); + this.windowInformations.draw(); + this.windowChoicesMain.draw(); + } } export { TitleSettings } \ No newline at end of file diff --git a/src/System/DynamicValue.ts b/src/System/DynamicValue.ts index bb0b213c..2cbdc07e 100644 --- a/src/System/DynamicValue.ts +++ b/src/System/DynamicValue.ts @@ -437,14 +437,15 @@ class DynamicValue extends System.Base { } return this.customStructure; case DynamicValueKind.CustomList: - if (deep) { - let list = []; - for (let v of this.customList) { - list.push(v.getValue(forceVariable, true)); - } - return list; - } - return this.customList; + this.value = this.value.replace(/{"id":0,"isList":true,"list":/g, ""); + this.value = this.value.replace(/{"id":0,"isProperty":false,"name":"","value":{/g, ""); + this.value = this.value.replace(/,"k":47,"v":null/g, ""); + this.value = this.value.replace(/"k":\d*,"v":/g, ""); + this.value = this.value.replace(/"customList":/g, ""); + this.value = this.value.replace(/,"name":""}/g, ""); + this.value = this.value.replace(/}},/g, ","); + this.value = this.value.replace(/}}]/g, "]"); + return JSON.parse(this.value); case DynamicValueKind.Vector2: return new Vector2(this.x.getValue(), this.y.getValue()); case DynamicValueKind.Vector3: diff --git a/src/System/MapProperties.ts b/src/System/MapProperties.ts index 2c559518..902e1e46 100644 --- a/src/System/MapProperties.ts +++ b/src/System/MapProperties.ts @@ -166,6 +166,7 @@ class MapProperties extends Base { this.skyboxGeometry = new THREE.BoxGeometry(size, size, size); this.skyboxMesh = new THREE.Mesh(this.skyboxGeometry, Datas.Systems .getSkybox(this.backgroundSkyboxID.getValue()).createTextures()); + this.skyboxMesh.renderOrder = -1; Scene.Map.current.scene.add(this.skyboxMesh); } diff --git a/src/System/WindowSkin.ts b/src/System/WindowSkin.ts index 7df6291e..8f84a9f8 100644 --- a/src/System/WindowSkin.ts +++ b/src/System/WindowSkin.ts @@ -91,11 +91,12 @@ class WindowSkin extends System.Base { * @param {number} [h=r[3]] - The h target * @param {number} [zoom=1.0] - The zoom to apply of target size */ - drawElement(r: number[], x: number, y: number, w: number = r[2], h: number = - r[3], zoom: number = 1.0, positionResize: boolean = true) + drawElement(r: number[], x: number, y: number, w: number = r[2], h: number = r[3], zoom: number = 1.0, positionResize: boolean = true) { - this.picture.draw({ x: x, y: y, w: w * zoom, h: h * zoom, sx: r[0], sy: - r[1], sw: r[2], sh: r[3], positionResize: positionResize }); + const wr = ScreenResolution.getDoubleScreenX(1); + const hr = ScreenResolution.getDoubleScreenY(1); + const m = Math.min(wr, hr); + this.picture.draw({ x: x, y: y, w: w * zoom * wr / m, h: h * zoom * hr / m, sx: r[0], sy: r[1], sw: r[2], sh: r[3], positionResize: positionResize }); } /** @@ -119,10 +120,12 @@ class WindowSkin extends System.Base { } } } else { - this.drawElement(background, rect[0] + this.borderTopLeft[2], rect[1 - ] + this.borderTopLeft[3], rect[2] - this.borderTopLeft[2] - - this.borderBotRight[2], rect[3] - this.borderTopLeft[3] - this - .borderBotRight[3]); + this.drawElement(background, + rect[0] + this.borderTopLeft[2], + rect[1] + this.borderTopLeft[3], + rect[2] - this.borderTopLeft[2] - this.borderBotRight[2], + rect[3] - this.borderTopLeft[3] - this.borderBotRight[3] + ); } }