/**
* @file MBS - FPLE MV
* @author Masked
* @version 1.5.0 2017-01-09
*/
/*:
* @plugindesc This plugin makes your game a first person labyrinth explorer.
*
* @author Masked
*
* @param View Distance
* @desc The amount of tiles ahead the player can see by default.
* @default 3
*
* @param Field Of View
* @desc The angle your camera covers
* @default 75
*
* @param Light Color
* @desc The hex code for the color used on the light.
* @default #ffffff
*
* @param Wall Terrain
* @desc The ID for wall tiles terrain tag on FPLE tilesets.
* @default 1
*
* @param Ceiling Region
* @desc The Region IDs for ceiled tiles, you can specify more than one region here by
* setting this to a list of region ids sepparated by spaces.
* @default 0
*
* @param 3D Anaglyph
* @desc Whether to use 3D cyan and red anaglyph to give your game depth. 1 = YES, 0 = NO.
* @default 0
*
* @param Texture Filter
* @desc The texture's filter type. (nearest/linear)
* @default linear
*
* @param Texture Format
* @desc The texture file format used. (png/jpg/dds/gif/...)
* @default png
*
* @param Texture Anisotropy
* @desc The amount of samples taken from textures. (usually a power of 2)
* @default 1
*
* @param Antialiasing
* @desc Whether to use antialiasing or not (expensive). 1 = YES, 0 = NO.
* @default 0
*
* @param Optimization Level
* @desc The level of optimization applied (degrading). (none/low/medium/high)
* @default none
*
* @help
* #
* First Person Labyrinth Explorer
* by Masked
*
* This plugin makes your game a kind of 3D so it looks like a first person
* labyrinth explorer.
*
*
* How to use
*
* 1. Setup
*
* I really recommend using the Demo as a base for creating your FPLE
* projects, so you can skip this part.
* If you create the project from scratch, however, you'll need to download
* babylon.js by yourself from http://cdn.babylonjs.com/babylon.js and
* save it at your project's "js/libs" folder.
* Then, edit your index.html and add this after line 14:
*
* Now, you're half a step nearer to be ready to go.
*
* 2. Textures
*
* Textures can be set creating a file named "{tileset}_{row}-{col}",
* replacing '{tileset}' by the FPLE tileset number on the database and
* '{row}' and '{col}' with, respectively, the row and column the tile you are
* texturing is on the tileset's A5 layer, and placing it at your game's
* img/textures folder.
* There's no maximum/minimum size for the textures, but keep in mind that
* bigger textures are more expensive and can cause some serious performance
* decrease, specially on older computers.
*
* 3. Tilesets
*
* Setting tilesets up for FPLE is quite simple: first, create the tileset
* and set just the A5 layer (which is, currently, the only one supported by
* FPLE). Then, you can set passability flags as you want, and mark wall
* tiles with the terrain tag you choose on your plugin settings.
* Note that the first tile at the first row is always invisible, so it's
* recommended you use a black tile or something like that.
*
* 4. Mapping
*
* Mapping is quite intuitive, just put wall tiles wherer you want to be walls
* and floor tiles where you don't, you can use the first tile at the first
* wall to create holes since it's invisible. Also, you may want your map to
* have a ceiling, if so, you just have to set the tiles you want with the
* ceil region you choose on the plugin settings.
*
* To activate FPLE on your map, just add "" (without quotes) to its notes.
* This is case sensitive! will not work.
*
* 5. Events
*
* There are two kinds of events in FPLE: sprite events and wall events.
*
* Sprite events are the ones you'll use for characters, vehicles, or whatever
* can be seen from different directions. This kind of event will change
* direction along with the camera, so that they're always pointing the same
* direction relative to the game "world".
* There's no special preparation needed to create a sprite event, these are
* default. Just place the event on the map as you would normally and you're
* ready to go.
*
* Sprite events aren't as good for stuff like moving walls or doors, though.
* For those, you'll want to use wall events. Wall events, as the name says,
* behave just like they were normal walls. That means their graphics will
* be the same no matter from what angle they're being seen. To create a wall
* event, you just have to put on its notes (the textbox right next to
* its name).
*
* 6. Plugin commands
*
* FPLE setResolution : Sets the FPLE canvas resolution (higher
* is better)
* FPLE setViewDistance : Sets the FPLE view distance in tile
* FPLE setLightColor : Changes the FPLE light color to the hex
* value given
*
* Examples:
* FPLE setResolution 0.5 : Sets the resolution at half
* FPLE setResolution v[42] : Sets the resolution to the value of
* variable 42
*
* FPLE setViewDistance 3 : Sets the view radius to 3 tiles
* FPLE setViewDistance v[42] : Sets the view radius to the value of
* variable 42
*
* FPLE setLightColor ff0000 : Sets the light color to Red
* FPLE setLightColor v[42] : Sets the light color to the value of
* variable 42 (that must be a string and
* be in hexadecimal format!)
*
*
* Credits
*
* - Masked, for creating;
* - babylon.js team (https://github.com/BabylonJS/Babylon.js/graphs/contributors),
* for their aweasomer library.
*/
"use strict";
//=============================================================================
// This plugin requires babylon.js (http://babylonjs.com/)
//=============================================================================
if (!BABYLON)
throw new error('FPLE: Unable to find babylon.js, please follow the' +
' plugin instructions and try again.');
if (!BABYLON.Engine.isSupported())
throw new error('FPLE: Browser not supported by BabylonJS.');
// Import for compatibility check
var Imported = Imported || {};
Imported['MBS - FPLE'] = 1.40;
// MBS module
var MBS = MBS || {};
MBS.FPLE = {};
// Globals
var $babylon, $fple;
//=============================================================================
// MBS.FPLE
//-----------------------------------------------------------------------------
// FPLE settings module
//=============================================================================
(function($) {
$.Filename = document.currentScript.src;
$.Name = decodeURI(/([^\/]+)\.js$/.exec($.Filename)[1]);
$.Params = PluginManager.parameters($.Name);
//-----------------------------------------------------------------------
// Parameters
//-----------------------------------------------------------------------
$.viewRadius = Number($.Params['View Distance']);
$.fov = Number($.Params['Field Of View']) / 100.0;
$.lightColor = $.Params['Light Color'];
$.wallTerrain = Number($.Params['Wall Terrain']);
$.ceilRegion = String($.Params['Ceiling Region']).split(/\s+/);
if ($.ceilRegion === '') $.ceilRegion = [];
for (var temp = 0; temp < $.ceilRegion.length; ++temp) {
$.ceilRegion[temp] =
parseInt($.ceilRegion[temp]);
};
$.anaglyph3d = !!Number($.Params['3D Anaglyph']);
$.textureFilter = $.Params['Texture Filter'];
$.textureFormat = $.Params['Texture Format'];
$.anisotropy = Number($.Params['Texture Anisotropy']);
$.antialias = !!Number($.Params['Antialiasing']);
$.optimization = $.Params['Optimization Level'];
//-----------------------------------------------------------------------
// Heights
//-----------------------------------------------------------------------
$.floorHeight = 0;
$.wallHeight = 1;
$.ceilHeight = 2;
$.cameraHeight = 1;
//-----------------------------------------------------------------------
// Image files format string
//-----------------------------------------------------------------------
$.textureFileFormat = "img/textures/%1_%2-%3%4.%5";
$.bumpTextureFileFormat = "img/bump/%1_%2-%3.%4";
$.parallaxFileFormat = "img/parallaxes/%1.%2";
//-----------------------------------------------------------------------
// Resizes the FPLE canvas to change the game resolution
//
// n : Multiplier
//-----------------------------------------------------------------------
$.setPixelRate = function(n) {
if (!Graphics._fple_renderer) return;
Graphics._fple_renderer.setSize(
Graphics.width * n,
Graphics.height * n
);
Graphics._updateFPLE();
};
//-----------------------------------------------------------------------
// Gets a nice cube with correctly oriented faces
//
// scene : Scene the cube is being used in
// updatable : Set to true if the cube will be updated
//-----------------------------------------------------------------------
$.createCube = function(scene) {
var uvs = $.CUBE_UVS;
var cube = BABYLON.Mesh.CreateBox('fple-cube', 1.0, scene);
cube.setVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
return cube;
};
//-----------------------------------------------------------------------
// Ffs, don't mess this array up
//
// Directions are relative to the direction you're facing when the
// game starts
//-----------------------------------------------------------------------
$.CUBE_UVS = [
0, 0, 1, 0, // Back
1, 1, 0, 1,
1, 1, 0, 1, // Front
0, 0, 1, 0,
0, 1, 0, 0, // Left
1, 0, 1, 1,
0, 1, 0, 0, // Right
1, 0, 1, 1,
1, 0, 1, 1, // Top
0, 1, 0, 0,
0, 1, 0, 0, // Bottom
1, 0, 1, 1,
];
})(MBS.FPLE);
//=============================================================================
// MBS.FPLE.Map
//-----------------------------------------------------------------------------
// FPLE Map object, converts $dataMap into a cubic map
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------
MBS.FPLE.Map = function() {
this.initialize.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Map initialization
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.initialize = function() {
this.clearCache();
this.refresh();
};
//-----------------------------------------------------------------------
// Updates this map
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.update = function(scene) {
if (!$fple) return;
this._updateEvents();
};
//-----------------------------------------------------------------------
// Clears the map cache
// Used just for creating '_cache' and/or reloading textures.
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.clearCache = function() {
this._cache = {
textures: {},
materials: {}
};
};
//-----------------------------------------------------------------------
// Extracts $dataMap data
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.refresh = function() {
this._data = [];
var width = $dataMap.width;
var height = $dataMap.height;
var data = $dataMap.data;
for (var x = 0; x < width; x++) {
this._data[x] = [];
for (var y = 0; y < height; y++) {
this._data[x][y] = data[y * width + x];
}
}
};
//-----------------------------------------------------------------------
// Gets a cached texture or loads it if it's hasn't been used yet.
//
// filename : The file from where to load the texture
// scene : The scene the texture is going to used into
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.getTexture = function(filename, scene) {
var cache = this._cache.textures[filename];
if (cache) return cache;
var texture;
while (!texture)
texture = new BABYLON.Texture(filename, scene, false);
texture.anisotropicFilteringLevel = MBS.FPLE.anisotropy;
if (MBS.FPLE.textureFilter.match(/^nearest$/i))
texture.updateSamplingMode(BABYLON.Texture.NEAREST_SAMPLINGMODE);
else
texture.updateSamplingMode(BABYLON.Texture.BILINEAR_SAMPLINGMODE);
this._cache.textures[filename] = texture;
return texture;
};
//-----------------------------------------------------------------------
// Gets a cached material or loads it if it's hasn't been used yet.
//
// tile_id : The ID of the tileset being used
// row : Tile row
// col : Tile column
// ceil : Whether the tile is a ceiling or not.
// true = ceiling, false = not ceiling. Easy.
// scene : FPLE scene object
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.getMaterial = function(tile_id, row, col, ceil, scene) {
var filename = MBS.FPLE.textureFileFormat.format(
tile_id, row, col, ceil ? '_ceil' : '', MBS.FPLE.textureFormat
);
if (this._cache.materials[filename])
return this._cache.materials[filename];
var texture;
while (!texture)
texture = this.getTexture(filename, scene);
var material = new BABYLON.StandardMaterial(filename, scene);
material.diffuseTexture = texture.clone();
material.diffuseTexture.hasAlpha = true;
material.specularColor = new BABYLON.Color3(0, 0, 0);
return material;
};
//-----------------------------------------------------------------------
// Adds this map's data to a babylonJS scene
//
// scene : The scene to add the objects into
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype.toScene = function(scene) {
this._applySkybox(scene);
this._applyEvents(scene);
this._applyTiles(scene);
};
//-----------------------------------------------------------------------
// Creates a cube to add into a FPLE scene
//
// name : The name given to the cube
// scene : Scene where the cube is going to be added
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._createCube = function(name, scene) {
var cube;
if (!this._cubeMesh) {
cube = MBS.FPLE.createCube(scene);
cube.convertToUnIndexedMesh();
this._cubeMesh = cube;
}
cube = this._cubeMesh.clone(name);
if (!scene.meshes.contains(cube))
scene.addMesh(cube);
return cube;
};
//-----------------------------------------------------------------------
// Creates an skybox for the babylonJS scene using the parallax image
//
// scene : The scene to add the cubes into
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._applySkybox = function(scene) {
if (!$gameMap.parallaxName())
return;
this._skybox = this._createCube("skyBox", scene);
this._skybox.sideOrientation = BABYLON.Mesh.DOUBLESIDE;
this._skybox.scaling = new BABYLON.Vector3(50.0, 50.0, 50.0);
this._skybox.position = new BABYLON.Vector3(-$gamePlayer._realX, 1, $gamePlayer._realY);
this._skybox.onBeforeDraw = function() {
this._skybox.position = $fple._camera.position;
}.bind(this);
var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
skyboxMaterial.backFaceCulling = false;
skyboxMaterial.disableLighting = true;
skyboxMaterial.emissiveTexture = this.getTexture(
MBS.FPLE.parallaxFileFormat.format($gameMap.parallaxName(), 'png'),
scene
);
skyboxMaterial.emissiveTexture.coordinatesMode = BABYLON.Texture.PROJECTION_MODE;
this._skybox.material = skyboxMaterial;
};
//-----------------------------------------------------------------------
// Adds this map's tiles to a babylonJS scene
//
// scene : The scene to add the cubes into
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._applyTiles = function(scene) {
var tile,
row,
col,
filename,
material,
cube,
n;
var cubes = [], materials = [];
for (var x = 0; x < this._data.length; x++) {
for (var y = 0; y < this._data[x].length; y++) {
tile = this._data[x][y];
col = (Math.floor(tile / 128) % 2 * 8 + tile % 8); // srsly?
row = (Math.floor(tile % 256 / 8) % 16); // wow, such formula
// The first tile at the first row is invisible
if (col === 0 && row === 0) continue;
material = this.getMaterial($dataMap.tilesetId, row, col, false, scene);
if (!materials.contains(material)) {
cubes.push([]);
materials.push(material);
}
n = materials.indexOf(material);
// Floor
cube = this._createCube('floor' + x + '-' + y, scene);
cube.position = new BABYLON.Vector3(-x, MBS.FPLE.floorHeight, y);
cubes[n].push(cube);
// Wall
if ($gameMap.terrainTag(x, y) === MBS.FPLE.wallTerrain) {
cube = this._createCube('wall' + x + '-' + y, scene);
cube.position = new BABYLON.Vector3(-x, MBS.FPLE.wallHeight, y);
cubes[n].push(cube);
}
// Ceiling
if (MBS.FPLE.ceilRegion.indexOf($gameMap.regionId(x, y)) >= 0)
{
material = this.getMaterial($dataMap.tilesetId, row, col, true, scene);
if (!materials.contains(material)) {
cubes.push([]);
materials.push(material);
}
n = materials.indexOf(material);
cube = this._createCube('ceil' + x + '-' + y, scene);
cube.position = new BABYLON.Vector3(-x, MBS.FPLE.ceilHeight, y);
cubes[n].push(cube);
}
}
}
for (var i = 0; i < cubes.length; i++)
this._buildMesh(cubes[i], materials[i]);
if ([].concat.apply([], cubes).length > 128)
scene.createOrUpdateSelectionOctree(8);
};
//-----------------------------------------------------------------------
// Builds a single mesh for floor, wall and ceiling cubes of the same
// material. This reduces lag.
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._buildMesh = function(cubes, material) {
var mesh = BABYLON.Mesh.MergeMeshes(cubes, true);
mesh.material = material;
return mesh;
};
//-----------------------------------------------------------------------
// Adds this map's events to a babylonJS scene
//
// scene : The scene to add the sprites into.
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._applyEvents = function(scene) {
this._events = [];
this._managers = {};
$gameMap.events().forEach(function (event) {
var charName = event.characterName();
if (!charName) return;
var filename = "img/characters/" + encodeURIComponent(charName) + ".png";
if (event.event().note.match(//i))
{
var cube = this._createCube("event" + event.id, scene);
cube.position = new BABYLON.Vector3(-event._realX, MBS.FPLE.cameraHeight, event._realY);
var texture = new BABYLON.Texture(filename, scene);
if (!ImageManager.isObjectCharacter(event.characterName()))
{
texture.uScale = 1 / 12.0;
texture.vScale = 1 / 8.0;
}
else
{
texture.uScale = 1 / 3.0;
texture.vScale = 1 / 4.0;
}
var material = new BABYLON.StandardMaterial("event" + event.id, scene);
material.diffuseColor = new BABYLON.Color3(1, 1, 1);
material.diffuseTexture = texture;
material.diffuseTexture.hasAlpha = true;
material.specularColor = new BABYLON.Color3(0, 0, 0);
cube.material = material;
cube._event = event;
this._events.push(cube);
}
else
{
var manager;
if (!this._managers[charName]) {
var img = ImageManager.loadCharacter(charName, 0);
img.addLoadListener(function() {
manager = new BABYLON.SpriteManager(
"ev_" + charName,
filename,
256,
new BABYLON.Size(img.width / 12.0, img.height / 8.0),
scene
);
this._managers[charName] = manager;
var sprite = new BABYLON.Sprite("event" + event.id, manager);
sprite._event = event;
this._events.push(sprite);
}.bind(this));
} else {
manager = this._managers[charName];
var sprite = new BABYLON.Sprite("event" + event.id, manager);
sprite._event = event;
this._events.push(sprite);
}
}
}.bind(this));
};
//-----------------------------------------------------------------------
// Updates this map's events
//-----------------------------------------------------------------------
MBS.FPLE.Map.prototype._updateEvents = function(scene) {
var playerPosition = new BABYLON.Vector3(-$gamePlayer._realX, MBS.FPLE.cameraHeight, $gamePlayer._realY);
var playerAngle = Math.round($fple._camera.rotation.y * 180 / Math.PI);
var vr2 = MBS.FPLE.viewRadius * MBS.FPLE.viewRadius;
var row = 48;
var col = 3;
var disposed = [];
this._events.forEach(function (eventObject) {
var event = eventObject._event;
if (event._erased)
{
eventObject.dispose(true, true);
disposed.push(eventObject);
return;
}
var charIndex = event.characterIndex();
var direction, offset;
if (!ImageManager.isObjectCharacter(event.characterName())) {
offset = row * Math.floor(charIndex / 4) + (charIndex % 4) * col;
if (event.isDirectionFixed() || event.event().note.match(//i))
direction = (event._direction / 2 - 1) % 4 * col * 4;
else
{
var eventAngle = ([180, 270, 90, 0])[event._direction / 2 - 1];
var relativeAngle = playerAngle - eventAngle;
relativeAngle %= 360;
while (relativeAngle < 0)
relativeAngle += 360;
var angles = [180, 90, 270, 0];
var closest = angles[0];
for (var i = 0; i < angles.length; i++)
if (Math.abs(relativeAngle - angles[i]) < Math.abs(relativeAngle - closest))
closest = angles[i];
var relativeDirection = angles.indexOf(closest) * 2 + 2;
direction = (relativeDirection / 2 - 1) % 4 * col * 4;
}
}
else
{
offset = charIndex;
direction = (event._direction / 2 - 1) % 4 * 3;
}
if (!event.event().note.match(//i))
{
var d2 = BABYLON.Vector3.DistanceSquared(eventObject.position, playerPosition);
if (d2 >= vr2)
eventObject.color = new BABYLON.Color4(0, 0, 0, 1);
else
eventObject.color = new BABYLON.Color4.FromHexString(MBS.FPLE.lightColor + 'ff');
eventObject.cellIndex = offset + direction + ([0, 1, 2, 1])[event._pattern % 4];
}
else
{
var cellRow = Math.floor((offset + direction + ([0, 1, 2, 1])[event._pattern % 4]) / 12);
var cellCol = (offset + direction + ([0, 1, 2, 1])[event._pattern % 4]) % 12;
eventObject.material.diffuseTexture.uOffset = cellCol / 12.0;
eventObject.material.diffuseTexture.vOffset = 1 - (1 + cellRow) / 8.0;
}
eventObject.position = new BABYLON.Vector3(-event._realX, MBS.FPLE.cameraHeight, event._realY);
});
disposed.forEach(function (disposed) {
this._events.splice(this._events.indexOf(disposed), 1);
}.bind(this));
};
})();
//=============================================================================
// MBS.FPLE.Camera
//-----------------------------------------------------------------------------
// FPLE Camera, this is a subclass of BABYLON.UniversalCamera with some utils
// used by FPLE
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------
MBS.FPLE.Camera = function() {
this.initialize.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Inheritance
//-----------------------------------------------------------------------
if (!MBS.FPLE.anaglyph3d)
MBS.FPLE.Camera.prototype = Object.create(BABYLON.UniversalCamera.prototype);
else
MBS.FPLE.Camera.prototype = Object.create(BABYLON.AnaglyphFreeCamera.prototype);
MBS.FPLE.Camera.prototype.constructor = MBS.FPLE.Camera;
//-----------------------------------------------------------------------
// Camera initialization
//-----------------------------------------------------------------------
MBS.FPLE.Camera.prototype.initialize = function(scene) {
if (!MBS.FPLE.anaglyph3d)
BABYLON.UniversalCamera.apply(this, [
"fpleCamera",
BABYLON.Vector3.Zero(),
scene
]);
else
BABYLON.AnaglyphFreeCamera.apply(this, [
"fpleCamera",
BABYLON.Vector3.Zero(),
0.1,
scene
]);
this.upVector = new BABYLON.Vector3(0, 1, 0);
this.fov = MBS.FPLE.fov;
this.minZ = 0.1;
this.maxZ = 100;//MBS.FPLE.viewRadius;
this.rotation.y = $gamePlayer.cameraAngle() * Math.PI / 180.0;
this.rotation.z = Math.PI; // Stop looking at the floor!
this.rotation.x = Math.PI; // Upside down...?
};
//-----------------------------------------------------------------------
// Updates the camera rotation and position based on the player
//-----------------------------------------------------------------------
MBS.FPLE.Camera.prototype.update = function() {
this._updatePosition();
this._updateRotation();
};
//-----------------------------------------------------------------------
// Updates the camera position
//-----------------------------------------------------------------------
MBS.FPLE.Camera.prototype._updatePosition = function() {
this.position = new BABYLON.Vector3(-$gamePlayer._realX, 1, $gamePlayer._realY);
};
//-----------------------------------------------------------------------
// Updates the camera rotation
//-----------------------------------------------------------------------
MBS.FPLE.Camera.prototype._updateRotation = function() {
var radians = $gamePlayer.cameraAngle() * Math.PI / 180.0;
var rotation = this.rotation.y;
var dA = radians - rotation;
var dB = Math.PI * 2 + radians - rotation;
var dC = -Math.PI * 2 + radians - rotation;
var n = [dA, dB, dC].reduce(function (a, b) {
return Math.abs(a) < Math.abs(b) ? a : b;
});
if (n === 0) return;
var t = (n / Math.abs(n));
if (Math.round(n * 10) / 10 === 0)
this.rotation.y = radians;
else
this.rotation.y += t * Math.PI / 180.0 * 5;
this.rotation.y %= Math.PI * 2;
};
})();
//=============================================================================
// Game_Player
//-----------------------------------------------------------------------------
// The game object class for the player. Changed the way movement happens,
// since it's tridimensional now
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Returns the camera angle representing the player direction.
//-----------------------------------------------------------------------
Game_Player.prototype.cameraAngle = function() {
return ([180, 270, 90, 0])[this._direction / 2 - 1];
};
//-----------------------------------------------------------------------
// Moves the player around according to FPLE rules
//-----------------------------------------------------------------------
var oldMoveByInput = Game_Player.prototype.moveByInput;
Game_Player.prototype.moveByInput = function() {
if (!$fple)
oldMoveByInput.apply(this, arguments);
else if ($fple.camera.rotation.y === (this.cameraAngle() * Math.PI / 180.0) &&
!this.isMoving() && this.canMove())
if (Input.isPressed('right'))
this.turnRight90();
else if (Input.isPressed('left'))
this.turnLeft90();
else if (Input.isPressed('up'))
this.moveForward();
else if (Input.isPressed('down'))
this.moveBackward();
};
})();
//=============================================================================
// MBS.FPLE.Scene
//
// FPLE Scene, this is a subclass of BABYLON.Scene with some utils used by FPLE.
// This automatically creates instances of MBS.FPLE.Map and MBS.FPLE.Camera.
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------
MBS.FPLE.Scene = function() {
this.initialize.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Inheritance
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype = Object.create(BABYLON.Scene.prototype);
MBS.FPLE.Scene.prototype.constructor = MBS.FPLE.Scene;
//-----------------------------------------------------------------------
// Initializes the FPLE babylonJS scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype.initialize = function() {
BABYLON.Scene.apply(this, arguments);
this.clearColor = new BABYLON.Color4(0, 0, 0, 0);
this.optimize();
this._createCamera();
this._createMap();
this._createLight();
};
//-----------------------------------------------------------------------
// Optimizes the scene by disabling some features and reducing the resolution.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype.optimize = function() {
var level;
if (MBS.FPLE.optimization.match(/^low$/i)) {
level = new BABYLON.SceneOptimizerOptions(70, 2000);
level.optimizations.push(new BABYLON.TextureOptimization(0, 256));
level.optimizations.push(new BABYLON.ParticlesOptimization(1));
level.optimizations.push(new BABYLON.ShadowsOptimization(1));
level.optimizations.push(new BABYLON.LensFlaresOptimization(1));
level.optimizations.push(new BABYLON.RenderTargetsOptimization(1));
} else if (MBS.FPLE.optimization.match(/^medium$/i)) {
level = new BABYLON.SceneOptimizerOptions(70, 1500);
level.optimizations.push(new BABYLON.TextureOptimization(0, 256));
level.optimizations.push(new BABYLON.ParticlesOptimization(1));
level.optimizations.push(new BABYLON.LensFlaresOptimization(1));
level.optimizations.push(new BABYLON.ShadowsOptimization(1));
level.optimizations.push(new BABYLON.RenderTargetsOptimization(1));
level.optimizations.push(new BABYLON.HardwareScalingOptimization(2, 2));
} else if (MBS.FPLE.optimization.match(/^high$/i)) {
level = new BABYLON.SceneOptimizerOptions(70, 500);
level.optimizations.push(new BABYLON.TextureOptimization(0, 256));
level.optimizations.push(new BABYLON.ParticlesOptimization(1));
level.optimizations.push(new BABYLON.ShadowsOptimization(1));
level.optimizations.push(new BABYLON.LensFlaresOptimization(1));
level.optimizations.push(new BABYLON.RenderTargetsOptimization(1));
level.optimizations.push(new BABYLON.HardwareScalingOptimization(2, 4));
//level.optimizations.push(new BABYLON.MergeMeshesOptimization(3, true));
} else
return;
BABYLON.SceneOptimizer.OptimizeAsync(this, level);
};
//-----------------------------------------------------------------------
// Creates the cubic map for the scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._createMap = function() {
this._map = new MBS.FPLE.Map();
this._map.toScene(this);
};
//-----------------------------------------------------------------------
// Creates the camera for the scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._createCamera = function() {
this._camera = new MBS.FPLE.Camera(this);
};
//-----------------------------------------------------------------------
// Creates the light for the scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._createLight = function() {
this._light = new BABYLON.PointLight("fpleLight", BABYLON.Vector3.Zero(), this);
this._light.diffuse = new BABYLON.Color3.FromHexString(MBS.FPLE.lightColor);
this._light.range = MBS.FPLE.viewRadius;
};
//-----------------------------------------------------------------------
// Refreshes the light for the scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype.refreshLight = function() {
if (!!this._light)
this._light.dispose();
this._createLight();
};
//-----------------------------------------------------------------------
// Updates the scene.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype.update = function() {
this._updateMap();
this._updateCamera();
this._updateLight();
};
//-----------------------------------------------------------------------
// Updates the map.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._updateMap = function() {
this._map.update();
};
//-----------------------------------------------------------------------
// Updates the camera.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._updateCamera = function() {
this._camera.update();
};
//-----------------------------------------------------------------------
// Updates the light.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype._updateLight = function() {
this._light.position.copyFrom(this._camera.position);
if (this._light.range != MBS.FPLE.viewRadius)
this._light.range = MBS.FPLE.viewRadius;
};
//-----------------------------------------------------------------------
// Terminates the scene process.
//-----------------------------------------------------------------------
MBS.FPLE.Scene.prototype.terminate = function() {
Graphics.terminateFPLE();
};
//-----------------------------------------------------------------------
// Properties
//-----------------------------------------------------------------------
Object.defineProperties(MBS.FPLE.Scene.prototype, {
//-----------------------------------------------------------------
// FPLE map (read only)
//-----------------------------------------------------------------
map: {
get: function() {
return this._map;
}
},
//-----------------------------------------------------------------
// FPLE Scene camera (read only)
//-----------------------------------------------------------------
camera: {
get: function() {
return this._camera;
}
}
});
})();
//=============================================================================
// Scene_Map
//-----------------------------------------------------------------------------
// The scene class of the map screen.
// Added MBS.FPLE.Scene.
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Creates the display-related objects
//-----------------------------------------------------------------------
var aliasCreateDisplayObjects = Scene_Map.prototype.createDisplayObjects;
Scene_Map.prototype.createDisplayObjects = function() {
aliasCreateDisplayObjects.apply(this, arguments);
if (this.useFPLE())
this._createFPLE();
};
//-----------------------------------------------------------------------
// Creates the scene's spriteset. Changed to not display the map
//-----------------------------------------------------------------------
var aliasCreateSpriteset = Scene_Map.prototype.createSpriteset;
Scene_Map.prototype.createSpriteset = function() {
if (this.useFPLE()) {
this._spriteset = new Spriteset_Base();
this._spriteset._blackScreen.opacity = 0;
this.addChild(this._spriteset);
} else {
aliasCreateSpriteset.apply(this, arguments);
}
};
//-----------------------------------------------------------------------
// Creates the FPLE Scene
//-----------------------------------------------------------------------
Scene_Map.prototype._createFPLE = function() {
Graphics.startFPLE();
this._fple = new MBS.FPLE.Scene($babylon);
$fple = this._fple;
};
//-----------------------------------------------------------------------
// Checks if the scene is ready to go.
//-----------------------------------------------------------------------
var aliasIsReady = Scene_Map.prototype.isReady;
Scene_Map.prototype.isReady = function() {
if (this._fple)
return aliasIsReady.apply(this, arguments) && this._fple.isReady();
else
return aliasIsReady.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Updates the scene's main objects
//-----------------------------------------------------------------------
var aliasUpdateMain = Scene_Map.prototype.updateMain;
Scene_Map.prototype.updateMain = function() {
aliasUpdateMain.apply(this, arguments);
if (this.useFPLE())
this._updateFPLE();
};
//-----------------------------------------------------------------------
// Updates the FPLE Scene
//-----------------------------------------------------------------------
Scene_Map.prototype._updateFPLE = function() {
this._fple.update();
};
//-----------------------------------------------------------------------
// Starts an encounter.
// This avoids errors when battle starts.
//-----------------------------------------------------------------------
var aliasStartEncounterEffect = Scene_Map.prototype.startEncounterEffect;
Scene_Map.prototype.startEncounterEffect = function() {
if (this.useFPLE())
this._encounterEffectDuration = this.encounterEffectSpeed();
else
aliasStartEncounterEffect.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Stops the scene process
//-----------------------------------------------------------------------
var aliasStop = Scene_Map.prototype.stop;
Scene_Map.prototype.stop = function() {
if (this.useFPLE())
this._fple.terminate();
$fple = null;
aliasStop.apply(this, arguments);
};
//-----------------------------------------------------------------------
// Checks whether to use FPLE on the current map
//-----------------------------------------------------------------------
Scene_Map.prototype.useFPLE = function() {
return !!$dataMap.meta.fple;
};
})();
//=============================================================================
// Graphics
//-----------------------------------------------------------------------------
// The static class that carries out graphics processing.
// Added FPLE start and terminate functions.
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Starts FPLE rendering process
//-----------------------------------------------------------------------
Graphics.startFPLE = function() {
if (!$babylon)
this._createFPLERenderer();
};
//-----------------------------------------------------------------------
// Creates a WebGL renderer used to process FPLE scene
//-----------------------------------------------------------------------
Graphics._createFPLERenderer = function() {
// Adds the fple canvas to the document body
// Using GameCanvas would cause problem with PIXI
if (!this._fpleCanvas) {
this._fpleCanvas = document.createElement('canvas');
this._fpleCanvas.id = "FPLECanvas";
this._centerElement(this._fpleCanvas);
document.body.appendChild(this._fpleCanvas);
}
// Fit canvas on screen
this._updateFPLE();
$babylon = new BABYLON.Engine(this._fpleCanvas, MBS.FPLE.antialias, null, false);
};
//-----------------------------------------------------------------------
// Updates all graphical elements
//-----------------------------------------------------------------------
var aliasUpdateAll = Graphics._updateAllElements;
Graphics._updateAllElements = function() {
aliasUpdateAll.apply(this, arguments);
this._updateFPLE();
};
//-----------------------------------------------------------------------
// Updates FPLE canvas
//-----------------------------------------------------------------------
Graphics._updateFPLE = function() {
if (!this._fpleCanvas) return;
this._fpleCanvas.width = this._width;
this._fpleCanvas.height = this._height;
this._centerElement(this._fpleCanvas);
};
//-----------------------------------------------------------------------
// Terminates FPLE rendering process.
//-----------------------------------------------------------------------
Graphics.terminateFPLE = function() {
$babylon.clear();
};
//-----------------------------------------------------------------------
// Creates the PIXI renderer.
//-----------------------------------------------------------------------
Graphics._createRenderer = function() {
PIXI.dontSayHello = true;
var width = this._width;
var height = this._height;
var options = { view: this._canvas, transparent: true };
try {
switch (this._rendererType) {
case 'canvas':
this._renderer = new PIXI.CanvasRenderer(width, height, options);
break;
case 'webgl':
this._renderer = new PIXI.WebGLRenderer(width, height, options);
break;
default:
this._renderer = PIXI.autoDetectRenderer(width, height, options);
break;
}
} catch (e) {
this._renderer = null;
}
};
//-----------------------------------------------------------------------
// Renders the stage to the game screen.
//-----------------------------------------------------------------------
Graphics.render = function(stage) {
if (this._skipCount === 0) {
var startTime = Date.now();
if (!!$fple)
$fple.render();
if (stage)
this._renderer.render(stage);
var endTime = Date.now();
var elapsed = endTime - startTime;
this._skipCount = Math.min(Math.floor(elapsed / 15), this._maxSkip);
this._rendered = true;
} else {
this._skipCount--;
this._rendered = false;
}
this.frameCount++;
};
})();
//=============================================================================
// Game_Interpreter
//-----------------------------------------------------------------------------
// Plugin commands
//=============================================================================
(function() {
//-----------------------------------------------------------------------
// Plugin command function
//-----------------------------------------------------------------------
var aliasPluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
aliasPluginCommand.apply(this, arguments);
if (command == 'FPLE') {
var match;
// Resolution change
if (args[0] == 'setResolution') {
if (args[1]) {
if (match = args[1].match(/v\[(\d+)\]/i)) {
args[1] = args[1].replace(match[0],
$gameVariables.value(Number(match[1])));
}
MBS.FPLE.setPixelRate(Number(args[1]));
} else {
console.error('FPLE: PluginCommand: no resolution specified.');
}
// View distance change
} else if (args[0] == 'setViewDistance') {
if (args[1]) {
if (match = args[1].match(/v\[(\d+)\]/i)) {
args[1] = args[1].replace(match[0],
$gameVariables.value(Number(match[1])));
}
MBS.FPLE.viewRadius = Number(args[1]);
} else {
console.error('FPLE: PluginCommand: no view distance specified.');
}
// Light color change
} else if (args[0] == 'setLightColor') {
if (args[1]) {
if (match = args[1].match(/v\[(\d+)\]/i)) {
args[1] = args[1].replace(match[0],
$gameVariables.value(String(match[1])));
}
MBS.FPLE.lightColor = args[1][0] == '#' ? args[1] : '#' + args[1];
if (!!$fple)
$fple.refreshLight();
} else {
console.error('FPLE: PluginCommand: no view distance specified.');
}
}
}
};
})();