Ok I've now tested this (also with RETRO in MV) and it seems to work. Please let me know if this fixes mentioned issues. Please note that this is not yet officially released version.
JavaScript://-----------------------------------------------------------------------------
// OcRam plugins - OcRam_Passages.js
//=============================================================================
"use strict"; if (!Imported || !Imported.OcRam_Core) alert('OcRam_Core.js ' +
'is required!'); if (parseFloat(OcRam.version) < 1.14) alert("OcRam core v1.14 or greater is required!");
OcRam.addPlugin("Passages", "1.15");
/*:
* @target MZ
* @plugindesc v1.15 Use region IDs to determine a character 'floor level'.
* Even autotiles can be drawn ABOVE characters.
* @author OcRam
* @url https://ocram-codes.net
* @orderAfter OcRam_Layers
* @orderAfter OcRam_Lights
* @
*
* ----------------------------------------------------------------------------
* PLUGIN COMMANDS
* ============================================================================
*
* @command setFloorLevel
* @text Set Floor Level
* @desc Set desired floor level to desired game object.
*
* @arg objectId
* @type number
* @decimals 0
* @min -255
* @max 999999
* @default -1
* @text Object Id
* @desc All positive numbers = event id. -1 = player + followers, -100 = boat, -101 = ship, -102 = airship.
*
* @arg floorLevel
* @type select
* @option Low
* @value Low
* @option High
* @value High
* @option Auto
* @value Auto
* @text Floor level
* @default Auto
* @desc To which floor level object is assigned?
*
* @command setTransferLevel
* @text Set Transfer Level
* @desc Set desired floor level where player should be transfered.
*
* @arg floorLevel
* @type select
* @option Low
* @value 1
* @option High
* @value 2
* @option Auto
* @value 0
* @text Floor level
* @default 0
* @desc Set desired floor level where player should be transfered.
*
* ----------------------------------------------------------------------------
* PLUGIN PARAMETERS
* ============================================================================
*
* @param Underpass Region ID
* @type number
* @desc Region ID for reducing character 'floor level' lower
* @default 16
*
* @param Overpass Region ID
* @type number
* @desc Region ID for raising character 'floor level' higher. Also draws B-E tiles
* @default 17
*
* @param Cover Region ID
* @type number
* @desc Region ID for "cover" such as bridges and other B-E tiles
* @default 18
*
* @param Cover Autotile Region ID
* @type number
* @desc Region ID for "Autotile cover" such as cliffs, roofs and other A-tiles
* @default 19
*
* @param Block Region ID
* @type number
* @desc Region ID to block movement from ALL floor levels
* @default 20
*
* @param Overhead Region ID
* @type number
* @desc Region ID to block movement AND show tiles ABOVE character (if low ground)
* @default 21
*
* @param Block High-Low Region ID
* @type number
* @desc Use to block movement from high <> low (must be next to underpass)
* @default 22
*
* @param Use automatic floor levels
* @type boolean
* @desc 'true' Enable automatic floor level detection else disable function
* @default true
*
* @param Use tall sprites
* @type boolean
* @desc 'true' = player and followers have same floor level.
* 'false' = individual floor levels.
* @default false
*
* @param Debug mode
* @parent Other parameters
* @type boolean
* @desc Write some events to console log (F8 or F12).
* @default false
*
* @help
* ----------------------------------------------------------------------------
* Introduction (Made for RPG Maker MZ + RETRO support for MV)
* ============================================================================
* This plugin uses region ID to determine a character 'floor level'. Even
* autotiles can be drawn ABOVE characters (events, player, followers etc).
*
* Character passage is also allowed based on floor level (collision tests).
*
* Events are signed to desired 'floor level' via event comments <lower> or
* <higher>. Event comments are processed per event PAGE! Or use "floor_level"
* plugin command. By default: event interaction is allowed only within same
* floor level. <trigger_always> will change this.
*
* NOTE: Use DEBUG mode ONLY if you need to debug something. It's logging
* quite big JS objects and often ...so it might slow things down.
*
* When using "setTransferLevel" plugin command use it BEFORE transfer
* command. After transfer "transfer level" is always set to "Auto"
*
* ----------------------------------------------------------------------------
* Usage (Region IDs)
* ============================================================================
* For example horizontal bridge paint following regions (with default ids)
*
* 16 = Underpassage point, 17 = Overpassage point, 18 = Bridge (cover tile)
*
* [16] [16] [16]
* [17] [18] [18] [18] [17]
* [16] [16] [16]
*
* Possible event comments: <higher>, <lower>, <trigger_always>
*
* ----------------------------------------------------------------------------
* Plugin commands
* ============================================================================
* objectId:
* -102 = Airship
* -101 = Ship
* -100 = Boat
* -2 to -99 = Followers
* -1 = Player (and followers)
* > 0 = Event id
*
* floorLevel:
* Low = Below cover tiles
* High = Above cover tiles
* Auto = Automatic assigment based on tile type where character is.
*
* MV example: OcRam_Passages/setFloorLevel -1 Low
* setFloorLevel To set player, event or vehicle "floor level"
* >> objectId See: "objectId" in the begining of this section.
* >> floorLevel To which floor level object is assigned?
*
* MV example: OcRam_Passages/setTransferLevel Low
* setFloorLevel To set player transfer "floor level"
* >> floorLevel To which floor level player will be transfered?
*
* ----------------------------------------------------------------------------
* JavaScript commands
* ============================================================================
* OcRam.Passages.setFloorLevel($gamePlayer, 'low/high/auto'); // Set floor lvl
* OcRam.Passages.initSprites(); // Force initialization of ALL sprites
* character.setFloorLevel('low/high/auto'); // Set floor lvl
*
* ----------------------------------------------------------------------------
* Terms of Use
* ============================================================================
* Edits are allowed as long as "Terms of Use" is not changed in any way.
* Exception: Obfuscating and/or minifying JS, where ALL comments are removed
* (incluging these "Terms of Use"), is allowed (won't change ToU itself).
*
* NON-COMMERCIAL USE: Free to use with credits to 'OcRam'
*
* If you gain money with your project by ANY MEANS (including: donations,
* crypto-mining, micro-transactions, advertisements, merchandises etc..)
* it's considered as COMMERCIAL use of this plugin!
*
* COMMERCIAL USE: (Standard license: 20 EUR, No-credits license: 60 EUR)
* Payment via PayPal (https://paypal.me/MarkoPaakkunainen), please mention
* PLUGIN NAME(S) you are buying / ALL plugins and your PROJECT NAME(S).
*
* Licenses are purchased per project and standard licenses requires credits.
* ALL of my plugins for 1 project = 40 EUR (standard licenses).
*
* License for lifetime is 3x base price of any license / bundle. Permission
* to use this type of license only in projects where you own most of it.
* Else project license OR project owner lifetime license is required.
*
* OcRam -plugins available at https://ocram-codes.net/plugins.aspx?engine=mz
*
* DO NOT COPY, RESELL OR CLAIM ANY PIECE OF THIS PLUGIN AS YOUR OWN!
* Copyright (c) 2021, Marko Paakkunainen // mmp_81 (at) hotmail.com
*
* ----------------------------------------------------------------------------
* Version History
* ============================================================================
* 2020/09/03 v1.00 - Initial release for v1.00
* 2020/09/19 v1.01 - Autosave won't mess up previous maps event floor levels
* 2020/09/27 v1.02 - Compatibility patch for OcRam_Layers
* + Tileset change bug fixes
* 2020/10/10 v1.03 - Compatibility patch for OcRam_Movement
* 2020/10/25 v1.04 - 2. compatibility patch for OcRam_Movement + Followers
* 2020/10/28 v1.05 - Fixed rare bug when $gamePlayer and 2 events with
* different floor levels in same tile caused stack overflow
* 2021/01/06 v1.06 - Compatibility patch for VisuMZ_4_EncounterEffects
* (Credits to Kneeshaw (also for the Visu plugin license))
* New JS method OcRam.Passages.initSprites() in case you
* need to refresh floor levels for every object.
* (Credits to poorrabit)
* Optimized a little bit event update method!
* 2021/01/26 v1.07 - Compatibility patch for MOG_CharShatterEffect
* (Credits to MasterTenchi)
* 2021/03/07 v1.08 - Fixed a bug where there was a slight chance for "_flags"
* to be null (_flags can be "empty" but NOT null)
* <higher> and <lower> notetags are now checked for EACH
* page individually and will override any floor level +
* auto-assign will ne performed IF tag not found!
* Balloons/Animations are now shown ABOVE cover tiles!
* "Higher level" sprites are now only created when needed
* and will be purged when changed to "lower level".
* character.autoAssign() -method added (Credits: clitvin)
* Optimized isMapPassable/checkPassage (Credits: clitvin)
* All known character "flickering" issues has been fixed.
* Support for LOOPPED MAPS! + Optimized mapCulling method!
* 2021/04/02 v1.09 - Compatibility patch for OcRam_Events lift & throw system!
* If character is on underpass region id > autoAssign = low
* Game_Character.setFloorLevel('high'/'low'/'auto');
* 2021/06/04 v1.10 - RETRO'ed for RMMV! (Credits to Drakkonis)
* startMapEvent doesn't any more start "Below character"
* events if they are on NON-passable tile.
* 1px glitch above "higher level" sprites is now fixed.
* 2021/10/21 v1.11 - Plugin meta data (order/base) is now editor compatible!
* 2021/12/01 v1.12 - Fixed bug when airship was higher level even if placed
* on "underpass" region id (16 by default).
* 2022/01/23 v1.13 - OcRam.Passage.doCulling() // Performs culling NOW
* initSprites() can be used now only twice per second.
* + Seamless map transfer with OcRam_Map_Transfer!
* Airship floor level autoassign now works as intended.
* Loopped maps are now working properly.
* Menu while in airship now works properly!
* 2022/04/22 v1.14 - Fixed bug if follower had no character sprite
* (Credits to OMGerm for report and testing)
* New plugin command "Set transfer floor" to make your
* life easier (no need for complex JS / eventing)
* 2022/??/?? v1.15 - Added support for RMMZ v1.5.0 + RETRO compatibility
* for this change. (Credits to OMGerm for report)
*
* ----------------------------------------------------------------------------
* RMMV CORE function overrides (destructive) are listed here
* ============================================================================
* Game_Map.prototype.isMapPassable
* Game_Map.prototype.isPassable
* Game_Map.prototype.checkPassage
* Game_Player.prototype.startMapEvent
* Game_CharacterBase.prototype.isCollidedWithEvents
* Game_Event.prototype.isCollidedWithEvents
* Game_Vehicle.prototype.refreshBushDepth
* Tilemap.prototype._readMapData
* Game_CharacterBase.prototype.isCollidedWithEvents_OC (IF OcRam_Movement)
* Sprite_Balloon.updatePosition
* Sprite_AnimationMV.updatePosition
* Game_Player.prototype.checkThrowPass (If OcRam_Events is imported)
*/
Sprite.prototype.isOcRam = () => { return false; }; // Fix for the Visu_4_EncounterEffects
OcRam.hackSC_LooppedMaps = sc_oc => { }; // This will remove lot of iffing every frame IF not needed ;d
// New class from Sprite_Character to make some adjustments... To reduce code base A LOT
class Sprite_Character_OC extends Sprite_Character {
constructor(character) {
super(character); //this._character._adjX = 0; this._character._adjY = 0;
}
update () {
if (this._showHigherSprite) {
this.visible = true; super.update();
} else {
if (this.visible) {
this.visible = false; super.update();
}
}
}
isOcRam () { return true; }
updatePosition() { // Removed + _adjX && + _adjY from calculations (couldn't find out what they were added for...)
if (!this.visible) { this.x = -100; this.y = -100; return; }
this.x = (this._character._realX * OcRam.twh[0] + OcRam.twh50[0]);
this.y = ((((this._character._realY * OcRam.twh[1] + OcRam.twh[1] - this._character.jumpHeight() -
(!this._character._isObjectCharacter ? 6 : 0)) | 0) + (this._character._altitude ? -this._character._altitude : 0)));
OcRam.hackSC_LooppedMaps(this); this.z = this._character.screenZ();
}
}
// Used in OcRam_Time_System (preloader)...
ImageManager.loadTileset_OC = function (filename) {
return this.loadBitmap('img/tilesets/', filename, 0, true);
};
(function () {
// ------------------------------------------------------------------------------
// Plugin variables and parameters
// ==============================================================================
const _this = this; // Refers to this plugin - To be used in subscopes...
const _underpassId = Number(this.parameters['Underpass Region ID'] || 16);
const _overpassId = Number(this.parameters['Overpass Region ID'] || 17);
const _coverId = Number(this.parameters['Cover Region ID'] || 18);
const _autoCoverId = Number(this.parameters['Cover Autotile Region ID'] || 19);
const _blockId = Number(this.parameters['Block Region ID'] || 20);
const _overheadId = Number(this.parameters['Overhead Region ID'] || 21);
const _blockHighLowId = Number(this.parameters['Block High-Low Region ID'] || 22);
const _useAutoassign = OcRam.getBoolean(this.parameters['Use automatic floor levels']);
const _useTallSprites = OcRam.getBoolean(this.parameters['Use tall sprites']);
const _tableEdgeVirtualId = 10000;
let _isAirshipLanded = true; // AirShip Sprite landed?
let _flags = []; // Game_Map flags needs to be loaded only once...
let _currentShaderTilemap = null; // Used to get shadertilemap bitmaps
let _cacheReady = false; // Check that cache is ready
let _tileSprites = []; // Static tiles here (to avoid Y sorting)
let _initDone = false; // Prevents init spamming
let _shiftedX = false; let _shiftedY = false;
let _transferLevel = 0;
let _useTimeSystem = OcRam.Time_System && OcRam.getFloat(OcRam.Time_System.version) > 1.02;
if (!_useTimeSystem) {
this.debug("Create fake OcRam.Time_System object (for tileset changes).");
OcRam.Time_System = {};
OcRam.Time_System._currentTilesetId = 0;
OcRam.Time_System._prevTilesetId = 0;
}
// Debuging purposes
this.getPassageTilesA = () => {
return _tileSprites[0];
}; this.getPassageTilesBE = () => {
return _tileSprites[1];
}; this.getPassagesLayer = () => {
if (!SceneManager || !SceneManager._scene || !SceneManager._scene._spriteset) return;
return SceneManager._scene._spriteset._layerContainer_OC;
}; this.totalNumberOfCoverSprites = () => {
let sum = 0; if (SceneManager && SceneManager._scene && SceneManager._scene._spriteset && SceneManager._scene._spriteset._layerContainer_OC) sum += SceneManager._scene._spriteset._layerContainer_OC.children.length - 2;
if (_tileSprites[0] && _tileSprites[0].children) sum += _tileSprites[0] && _tileSprites[0].children.length; if (_tileSprites[1] && _tileSprites[1].children) sum += _tileSprites[1] && _tileSprites[1].children.length; return sum;
}
// ------------------------------------------------------------------------------
// Private Utility functions - Inherited to all sub scopes here
// ==============================================================================
const autoAssignFloorLevel = ev => {
if (!$gameMap.tileset().mode) {
this.debug("autoAssignFloorLevel [OVERWORLD!] (_higherLevel=" + false + ")", ev);
return false;
}
if (ev._higherLevel != undefined) return ev._higherLevel;
if (!_useAutoassign) { return ev._higherLevel || false; }
const tiles = $gameMap.allTiles(ev._x, ev._y); let tile_id = 0;
for (let i = 0; i < tiles.length; i++) {
if (Tilemap.isAutotile(tiles[i])) tile_id = tiles[i];
}
let ret = false;
switch (ev.regionId()) {
case _underpassId: ret = false; break;
case _coverId: case _autoCoverId: case _overpassId: case _overheadId: ret = true; break;
default: ret = (Tilemap.isRoofTile(tile_id) || Tilemap.isWallTopTile(tile_id)); break;
} this.debug("autoAssignFloorLevel (_higherLevel=" + ret + ")", ev); return ret;
};
const purgeHigherLevelSprite = obj => {
let sprite = getCharSprite(obj); if (sprite)
SceneManager._scene._spriteset._layerContainer_OC.removeChild(sprite); sprite = null;
};
const setObjFloorLevel = (obj, level) => {
if (obj !== undefined && obj != null) {
let sprite = getCharSprite(this); if (sprite)
SceneManager._scene._spriteset._layerContainer_OC.removeChild(sprite); sprite = null;
if (level == "low") obj._higherLevel = false;
if (level == "high") obj._higherLevel = true;
if (level == "auto") obj._higherLevel = autoAssignFloorLevel(obj);
updateEvent(obj);
}
};
const preLoadSeasonTilesets = () => {
let tmp = [];
$dataTilesets.forEach(ts => {
if (ts != null) {
tmp = [];
ts.tilesetNames.forEach(s => {
tmp.push(ImageManager.loadTileset_OC(s));
});
}
}); isCacheReady();
};
const isCacheReady = () => {
window.setTimeout(() => {
_cacheReady = ImageManager.isReady();
if (!_cacheReady) {
this.debug("preLoadSeasonTilesets()", "...Loading...");
isCacheReady();
} else {
this.debug("preLoadSeasonTilesets()", "...Done!");
}
}, 1000);
};
const callSeasonChange = () => {
if (SceneManager._scene.isTitle() || DataManager.isEventTest()) return;
// Do not change tileset if not required to...
this.debug("Tileset check:", OcRam.Time_System._prevTilesetId + " -VS- " + OcRam.Time_System._currentTilesetId);
if (OcRam.Time_System._prevTilesetId == OcRam.Time_System._currentTilesetId) return;
_currentShaderTilemap = SceneManager._scene._spriteset._tilemap; // Current ShaderTilemap
let tileset_names = $dataTilesets[OcRam.Time_System._currentTilesetId].tilesetNames;
this.debug("LOADED NEW TILESET!", tileset_names); // Force new tileset bitmaps...
OcRam.Time_System._currentTilesetId = OcRam.Time_System._prevTilesetId;
for (let i = 0; i < tileset_names.length - 1; i++) {
_currentShaderTilemap._bitmaps[i] = ImageManager.loadTileset_OC(tileset_names[i]);
}
};
const reStartScene = () => {
if (!OcRam.scene()) return;
if (OcRam.scene().isReady()) {
requestAnimationFrame(() => {
OcRam.scene().start();
});
} else {
requestAnimationFrame(() => {
reStartScene();
});
}
};
const initSprites = () => {
if (_initDone) return;
_initDone = true;
setTimeout(() => { _initDone = false; }, 500);
OcRam.twh = [$gameMap.tileWidth(), $gameMap.tileHeight()];
this.debug("Do culling?", OcRam._doCulling);
// Initialize bitmap arrays on autotile covers...
_flags = $gameMap.tilesetFlags();
let ev_cmts = [];
$gameSystem.loadPassageData(); callSeasonChange();
let is_auto_or_overhead = false; let region_id = 0;
const lw = $gameMap.width(); const lh = $gameMap.height();;
for (let x = 0; x < lw; x++) {
for (let y = 0; y < lh; y++) {
region_id = $gameMap.regionId(x, y); is_auto_or_overhead = (region_id == _autoCoverId || region_id == _overheadId);
if (is_auto_or_overhead) {
drawLowerLayers(x, y); // Autotiles covers WHOLE tile
} if (region_id == _coverId || is_auto_or_overhead || region_id == _overpassId) {
drawBELayers(x, y); // Characters and B-E tiles may have transparent backgrounds
}
}
}
$gameMap.events().forEach(ev => {
if (ev._higherLevel === undefined) {
ev._higherLevel = autoAssignFloorLevel(ev);
ev_cmts = ev.getStringComments();
ev_cmts.forEach(s => {
if (s == "<higher>") {
ev._higherLevel = true;
} else if (s == "<lower>") {
ev._higherLevel = false;
}
});
} if (ev._higherLevel === undefined) ev._higherLevel = autoAssignFloorLevel(ev);
if (ev._higherLevel) { updateEvent(ev); }
});
const aship_obj = $gameMap.airship();
if (aship_obj._mapId == $gameMap._mapId) {
aship_obj._higherLevel = undefined;
if ($gamePlayer._vehicleType == "airship") {
aship_obj._higherLevel = true;
setObjFloorLevel(aship_obj, "high");
updateEvent(aship_obj);
} else {
aship_obj._higherLevel = autoAssignFloorLevel(aship_obj);
setObjFloorLevel(aship_obj, aship_obj._higherLevel ? 'high' : 'low');
updateEvent(aship_obj);
}
} drawActors();
mapCulling(); $gameMap.moveSpritesX_OC(); $gameMap.moveSpritesY_OC();
};
const drawLowerLayers = (px, py) => {
let low_bm = new Bitmap(OcRam.twh[0], OcRam.twh[1]);
let ctm = SceneManager._scene._spriteset._tilemap;
ctm.paintTilesOnBitmap_OC(low_bm, null, px, py, true);
addTileBitmap(low_bm, px * OcRam.twh[0], py * OcRam.twh[1], 0);
};
const drawActors = () => {
// Pre-load actor higherlevel sprites to avoid minor lag when triggered 'higher' level...
let ev = null; let old_hl = false;
for (let i = $gamePlayer._followers.visibleFollowers().length - 1; i > -1; i--) {
ev = $gamePlayer._followers.visibleFollowers()[i];
old_hl = ev._higherLevel; ev._higherLevel = true; addCharBitmap(ev); ev._higherLevel = old_hl;
ev._higherLevel = !_transferLevel ? autoAssignFloorLevel(ev) : _transferLevel == 1 ? false : true; updateEvent(ev);
} ev = $gamePlayer; old_hl = ev._higherLevel; ev._higherLevel = true; addCharBitmap(ev); ev._higherLevel = old_hl;
ev._higherLevel = !_transferLevel ? autoAssignFloorLevel(ev) : _transferLevel == 1 ? false : true; updateEvent(ev);
};
// Set visibilities to avoid sprite rendering/sorting for sprites that are off screen!
const mapCulling = () => {
if (!OcRam._doCulling) return;
if (_tileSprites[0]) {
_tileSprites[0].children.forEach(s => {
if (s.visible) { // Is it off screen now?
if (!OcRam.isInScreen(s._x, s._y)) s.visible = false;
} else { // Is it in screen now?
if (OcRam.isInScreen(s._x, s._y)) s.visible = true;
}
});
}
if (_tileSprites[1]) {
_tileSprites[1].children.forEach(s => {
if (s.visible) { // Is it off screen now?
if (!OcRam.isInScreen(s._x, s._y)) s.visible = false;
} else { // Is it in screen now?
if (OcRam.isInScreen(s._x, s._y)) s.visible = true;
}
});
}
};
// Draw tile to desired layer
const addTileBitmap = (p_bitmap, x, y, z) => {
let sprite = new Sprite(); sprite.bitmap = p_bitmap;
sprite.x = x; sprite.y = y;
sprite._x = (x / OcRam.twh[0]) | 0;
sprite._y = (y / OcRam.twh[1]) | 0;
sprite.z = z; sprite.visible = !OcRam._doCulling;
if (z == 0) {
_tileSprites[0].addChild(sprite); // Lower tiles
} else {
_tileSprites[1].addChild(sprite); // Upper tiles
}
};
// Add char to _layerContainer_OC
const addCharBitmap = ev => {
if (ev._eventId !== undefined) {
if (ev._characterName == "" && ev._tileId == 0) { this.debug("Event with no graphics...", ev); return; }
if (!SceneManager._scene._spriteset || !SceneManager._scene._spriteset._layerContainer_OC) return;
let sprite = new Sprite_Character_OC(ev); sprite.z = ev._priorityType * 2 + 1; sprite.visible = !OcRam._doCulling;
SceneManager._scene._spriteset._layerContainer_OC.addChild(sprite); updateEvent(ev);
}
};
// Update event sprite
const updateEvent = ev => {
if (ev._eventId == undefined) {
if (ev.isPlayer()) ev._eventId = -1;
if (ev.isFollower()) ev._eventId = -(ev._memberIndex + 1);
if (ev.isVehicle()) {
if (ev._type == "boat") { ev._eventId = -100; ev._higherLevel = false; }
if (ev._type == "ship") { ev._eventId = -101; ev._higherLevel = false; }
if (ev._type == "airship") { ev._eventId = -102; }
}
} let sprite = getCharSprite(ev);
if (ev._higherLevel) {
if (sprite) {
sprite._showHigherSprite = ev._higherLevel && !ev._transparent;
} else { // Event not created yet?
addCharBitmap(ev);
}
} else {
if (sprite) requestAnimationFrame(() => { // 3 frames should be enough to avoid flickering...
requestAnimationFrame(() => { // 1 more after this one...
requestAnimationFrame(() => { purgeHigherLevelSprite(ev); });
});
});
}
};
const getCharSprite = ev => {
if (!ev || !SceneManager._scene._spriteset || !SceneManager._scene._spriteset._layerContainer_OC) return null;
return SceneManager._scene._spriteset._layerContainer_OC.children.find(sprite => {
if (sprite.isOcRam() && sprite._character) {
if (sprite._character._eventId == ev._eventId) return true;
}
});
};
const drawBELayers = (px, py) => { // Draw B-E tiles - Returns: Bitmap
let tmp_x = px * OcRam.twh[0]; let tmp_y = py * OcRam.twh[1];
let low_bm = new Bitmap(OcRam.twh[0], OcRam.twh[1]);
let high_bm = new Bitmap(OcRam.twh[0], OcRam.twh[1]);
const ctm = SceneManager._scene._spriteset._tilemap;
ctm.paintTilesOnBitmap_OC(low_bm, high_bm, px, py, false);
addTileBitmap(low_bm, tmp_x, tmp_y, 0, px, py); // Add below chars
addTileBitmap(high_bm, tmp_x, tmp_y, 4, px, py); // Add above chars
};
const shiftScreenPassagesX = () => {
if (_shiftedX) {
_this.debug("SHOULD REVERT SPRITES TO ORGINAL X!");
let tiles = _tileSprites[0].children.filter(t => { // A tiles
return t.x > $gameMap._pixelWidth;
}); tiles.forEach(t => {
t.x -= ($gameMap._pixelWidth);
}); tiles = _tileSprites[1].children.filter(t => { // B-E tiles
return t.x > $gameMap._pixelWidth;
}); tiles.forEach(t => {
t.x -= ($gameMap._pixelWidth);
}); _shiftedX = false;
} else {
_this.debug("SHOULD SHIFT SPRITES TO NEW DISP X!");
let tiles = _tileSprites[0].children.filter(t => {
return t.x < Graphics.width * 0.75;
}); tiles.forEach(t => {
t.x += ($gameMap._pixelWidth);
}); tiles = _tileSprites[1].children.filter(t => {
return t.x < Graphics.width * 0.75;
}); tiles.forEach(t => {
t.x += ($gameMap._pixelWidth);
}); _shiftedX = true;
}
};
const shiftScreenPassagesY = () => {
if (_shiftedY) {
_this.debug("SHOULD REVERT SPRITES TO ORGINAL Y!");
let tiles = _tileSprites[0].children.filter(t => { // A tiles
return t.y > $gameMap._pixelHeight;
}); tiles.forEach(t => {
t.y -= ($gameMap._pixelHeight);
}); tiles = _tileSprites[1].children.filter(t => { // B-E tiles
return t.y > $gameMap._pixelHeight;
}); tiles.forEach(t => {
t.y -= ($gameMap._pixelHeight);
}); _shiftedY = false;
} else {
_this.debug("SHOULD SHIFT SPRITES TO NEW DISP Y!");
let tiles = _tileSprites[0].children.filter(t => {
return t.y < Graphics.height * 0.75;
}); tiles.forEach(t => {
t.y += ($gameMap._pixelHeight);
}); tiles = _tileSprites[1].children.filter(t => {
return t.y < Graphics.height * 0.75;
}); tiles.forEach(t => {
t.y += ($gameMap._pixelHeight);
}); _shiftedY = true;
}
};
// ------------------------------------------------------------------------------
// Public plugin functions - Usage: OcRam.PluginName.myFunction(arguments)
// ==============================================================================
this.setFloorLevel = (game_obj, str_level) => {
setObjFloorLevel(game_obj, str_level);
}; this.initSprites = () => { initSprites(); };
this.reStartScene = () => { reStartScene(); };
this.doCulling = () => { mapCulling();};
this.setTransferLevel = level => { _transferLevel = level; };
// ------------------------------------------------------------------------------
// MOG_CharShatter fix
// ==============================================================================
if (Imported.MOG_CharShatterEffect) {
const _MOG_CharShatterEffect_Fix = Sprite_Character.prototype.loadShatterData;
Sprite_Character.prototype.loadShatterData = function (i) {
if (this._character._shatter[1][i].opacity === undefined) {
this.saveShatterData(); $gameTemp._forceSkipShatter = true;
} else {
_MOG_CharShatterEffect_Fix.call(this, i);
}
};
}
// ------------------------------------------------------------------------------
// New methods
// ==============================================================================
// Force autoassign...
Game_CharacterBase.prototype.autoAssign = function() {
this._higherLevel = undefined;
this._higherLevel = autoAssignFloorLevel(this);
updateEvent(this);
};
// This is triggered every frame!
Tilemap.prototype._sortChildren_OC = function () {
SceneManager._scene._spriteset._layerContainer_OC.children.sort(this._compareChildOrder_OC.bind(this));
};
// This is triggered every frame!
Tilemap.prototype._compareChildOrder_OC = function (a, b) {
if (a.z !== b.z) return a.z - b.z;
if (a.y !== b.y) return a.y - b.y;
return a.spriteId - b.spriteId;
};
// Save passage data (high / low)
Game_System.prototype.savePassageData = function () {
this._passageHigh = []; this._passageLow = []; const fl = OcRam.followers().length;
if ($gamePlayer._higherLevel) { this._passageHigh.push(-1); } else { this._passageLow.push(-1); }
if (fl > 0 && OcRam.getGameObject(-2)._higherLevel) { this._passageHigh.push(-2); } else { this._passageLow.push(-2); }
if (fl > 1 && OcRam.getGameObject(-3)._higherLevel) { this._passageHigh.push(-3); } else { this._passageLow.push(-3); }
if (fl > 2 && OcRam.getGameObject(-4)._higherLevel) { this._passageHigh.push(-4); } else { this._passageLow.push(-4); }
if (OcRam.getGameObject(-100)._higherLevel) { this._passageHigh.push(-100); } else { this._passageLow.push(-100); }
if (OcRam.getGameObject(-101)._higherLevel) { this._passageHigh.push(-101); } else { this._passageLow.push(-101); }
if (OcRam.getGameObject(-102)._higherLevel) { this._passageHigh.push(-102); } else { this._passageLow.push(-102); }
$gameMap.events().forEach(ev => {
if (ev._higherLevel) {
this._passageHigh.push(ev.eventId());
} else { this._passageLow.push(ev.eventId()); }
}); _this.debug("savePassageData >>> Saved _passageHigh:", this._passageHigh);
_this.debug("savePassageData >>> Saved _passageLow:", this._passageLow);
};
// Load previously saved passage data (high / low)
Game_System.prototype.loadPassageData = function () {
let gobj = null;
if (this._passageHigh != undefined) {
if (this._passageHigh.length > 0) {
_this.debug("Loaded _passageHigh:", this._passageHigh);
this._passageHigh.forEach(i => {
gobj = OcRam.getGameObject(i); if (gobj) gobj._higherLevel = true;
}); this._passageHigh = [];
}
}
if (this._passageLow != undefined) {
if (this._passageLow.length > 0) {
_this.debug("Loaded _passageLow:", this._passageLow);
this._passageLow.forEach(i => {
gobj = OcRam.getGameObject(i); if (gobj) gobj._higherLevel = false;
}); this._passageLow = [];
}
}
};
Game_Map.prototype.moveSpritesX_OC = function () {
SceneManager._scene._spriteset._layerContainer_OC.x = -(this._displayX * OcRam.twh[0]) | 0;
};
Game_Map.prototype.moveSpritesY_OC = function () {
SceneManager._scene._spriteset._layerContainer_OC.y = -(this._displayY * OcRam.twh[1]) | 0;
};
// Create layers for cover graphics
Spriteset_Map.prototype.createCoverLayers_OC = function () {
// Clear layer container, just in case (you never know what other plugins do in this.terminate)
if (this._layerContainer_OC !== undefined) {
alert("OMG! Had to clear _layerContainer_OC >> This shouldn't happen!\nPlease report this message to passages thread!");
this._tilemap.removeChild(this._layerContainer_OC);
}
_tileSprites = []; // Init tile sprite containers
this._layerContainer_OC = new Sprite(); // Layer wrapper
this._layerContainer_OC.z = 9999 + 1;
let s = new Sprite(); s.z = 0; _tileSprites.push(s);
this._layerContainer_OC.addChild(_tileSprites[0]);
s = new Sprite(); s.z = 4; _tileSprites.push(s);
this._layerContainer_OC.addChild(_tileSprites[1]);
this._tilemap.addChild(this._layerContainer_OC);
};
// These methods will force system to draw tiles on bitmap (cover tiles)
if (OcRam.Layers) {
if (OcRam.Layers.parallaxOptimization()) {
this.debug("OcRam.Layers parallax optimization detected", "DISABLE tile drawing totally!");
Tilemap.prototype.drawTileToBitmap_OC = function () { return; };
} else {
this.debug("OcRam.Layers detected", "DISABLE passage tile drawing for <parallax> tilesets/maps!");
Tilemap.prototype.drawTileToBitmap_OC = function (bitmap, tileId, dx, dy) {
if (OcRam.Layers._usingParallax) return;
if (Tilemap.isVisibleTile(tileId)) {
if (Tilemap.isAutotile(tileId)) {
this.drawAutotile_OC(bitmap, tileId, dx, dy);
} else {
this.drawNormalTile_OC(bitmap, tileId, dx, dy);
}
}
};
}
} else {
Tilemap.prototype.drawTileToBitmap_OC = function (bitmap, tileId, dx, dy) {
if (Tilemap.isVisibleTile(tileId)) {
if (Tilemap.isAutotile(tileId)) {
this.drawAutotile_OC(bitmap, tileId, dx, dy);
} else {
this.drawNormalTile_OC(bitmap, tileId, dx, dy);
}
}
};
}
Tilemap.prototype.drawAutotile_OC = function (bitmap, tileId, dx, dy) {
const kind = Tilemap.getAutotileKind(tileId);
const shape = Tilemap.getAutotileShape(tileId);
const tx = kind % 8;
const ty = Math.floor(kind / 8);
let setNumber = 0;
let bx = 0;
let by = 0;
let autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
let isTable = false;
if (Tilemap.isTileA1(tileId)) {
const waterSurfaceIndex = [0, 1, 2, 1][this.animationFrame % 4];
setNumber = 0;
if (kind === 0) {
bx = waterSurfaceIndex * 2;
by = 0;
} else if (kind === 1) {
bx = waterSurfaceIndex * 2;
by = 3;
} else if (kind === 2) {
bx = 6;
by = 0;
} else if (kind === 3) {
bx = 6;
by = 3;
} else {
bx = Math.floor(tx / 4) * 8;
by = ty * 6 + (Math.floor(tx / 2) % 2) * 3;
if (kind % 2 === 0) {
bx += waterSurfaceIndex * 2;
} else {
bx += 6;
autotileTable = Tilemap.WATERFALL_AUTOTILE_TABLE;
by += this.animationFrame % 3;
}
}
} else if (Tilemap.isTileA2(tileId)) {
setNumber = 1;
bx = tx * 2;
by = (ty - 2) * 3;
isTable = this._isTableTile(tileId);
} else if (Tilemap.isTileA3(tileId)) {
setNumber = 2;
bx = tx * 2;
by = (ty - 6) * 2;
autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
} else if (Tilemap.isTileA4(tileId)) {
setNumber = 3;
bx = tx * 2;
by = Math.floor((ty - 10) * 2.5 + (ty % 2 === 1 ? 0.5 : 0));
if (ty % 2 === 1) {
autotileTable = Tilemap.WALL_AUTOTILE_TABLE;
}
}
const table = autotileTable[shape];
const w1 = OcRam.twh50[0];
const h1 = OcRam.twh50[1];
const source = this._bitmaps[setNumber];
for (let i = 0; i < 4; i++) {
const qsx = table[i][0];
const qsy = table[i][1];
const sx1 = (bx * 2 + qsx) * w1;
const sy1 = (by * 2 + qsy) * h1;
const dx1 = dx + (i % 2) * w1;
const dy1 = dy + Math.floor(i / 2) * h1;
if (isTable && (qsy === 1 || qsy === 5)) {
const qsx2 = qsy === 1 ? (4 - qsx) % 4 : qsx;
const qsy2 = 3;
const sx2 = (bx * 2 + qsx2) * w1;
const sy2 = (by * 2 + qsy2) * h1;
bitmap.blt(source, sx2, sy2, w1, h1, dx1, dy1, w1, h1);
dy1 += h1 * 0.5;
bitmap.blt(source, sx1, sy1, w1, h1 * 0.5, dx1, dy1, w1, h1 * 0.5);
} else {
bitmap.blt(source, sx1, sy1, w1, h1, dx1, dy1, w1, h1);
}
}
};
Tilemap.prototype.drawNormalTile_OC = function (bitmap, tileId, dx, dy) {
let setNumber = 0;
if (Tilemap.isTileA5(tileId)) {
setNumber = 4;
} else {
setNumber = 5 + Math.floor(tileId / 256);
}
const w = OcRam.twh[0]; const h = OcRam.twh[1];
const sx = ((Math.floor(tileId / 128) % 2) * 8 + (tileId % 8)) * w;
const sy = (Math.floor((tileId % 256) / 8) % 16) * h;
const source = this._bitmaps[setNumber];
if (source) bitmap.blt(source, sx, sy, w, h, dx, dy, w, h);
};
Tilemap.prototype.drawTableEdge_OC = function (bitmap, tileId, dx, dy) {
if (Tilemap.isTileA2(tileId)) {
const autotileTable = Tilemap.FLOOR_AUTOTILE_TABLE;
const kind = Tilemap.getAutotileKind(tileId);
const shape = Tilemap.getAutotileShape(tileId);
const tx = kind % 8;
const ty = Math.floor(kind / 8);
const bx = tx * 2;
const by = (ty - 2) * 3;
const table = autotileTable[shape];
const w1 = OcRam.twh50[0];
const h1 = OcRam.twh50[1];
const source = this._bitmaps[1];
for (let i = 0; i < 2; i++) {
const qsx = table[2 + i][0];
const qsy = table[2 + i][1];
const sx1 = (bx * 2 + qsx) * w1;
const sy1 = (by * 2 + qsy) * h1 + h1 / 2;
const dx1 = dx + (i % 2) * w1;
const dy1 = dy + Math.floor(i / 2) * h1;
bitmap.blt(source, sx1, sy1, w1, h1 * 0.5, dx1, dy1, w1, h1 * 0.5);
}
}
};
Tilemap.prototype.drawShadow_OC = function (bitmap, shadowBits, dx, dy) {
if (shadowBits & 0x0f) {
const w1 = OcRam.twh50[0];
const h1 = OcRam.twh50[1];
for (let i = 0; i < 4; i++) {
if (shadowBits & (1 << i)) {
const dx1 = dx + (i % 2) * w1;
const dy1 = dy + Math.floor(i / 2) * h1;
bitmap.fillRect(dx1, dy1, w1, h1, 'rgba(0,0,0,0.5)');
}
}
}
};
Tilemap.prototype.paintTilesOnBitmap_OC = function (bm_lo, bm_hi, x, y, at) {
const x1 = 0; const y1 = 0;
const tileId0 = this._readMapData_OC(x, y, 0); // Autotile (ground)
const tileId1 = this._readMapData_OC(x, y, 1); // Autotile (bush)
const tileId2 = this._readMapData_OC(x, y, 2, true); // B-E tile (x/o)
const tileId3 = this._readMapData_OC(x, y, 3, true); // B-E tile (*)
const upperTileId1 = this._readMapData_OC(x, y - 1, 1);
let tilesHigh = []; let tilesLow = [];
const shadowBits = this._readMapData_OC(x, y, 4);
if (at) { // Autotiles will be drawn "flat" (1 bitmap)
if (this._isHigherTile(tileId0)) { tilesHigh.push(tileId0); }
else { tilesLow.push(tileId0); }
if (this._isHigherTile(tileId1)) { tilesHigh.push(tileId1); }
else { tilesLow.push(tileId1); }
bm_lo.clearRect(x1, y1, OcRam.twh[0], OcRam.twh[1]);
let i = 0;
for (i = 0; i < tilesLow.length; i++) {
const lowerTileId = tilesLow[i];
if (lowerTileId < 0) {
} else if (lowerTileId >= _tableEdgeVirtualId) {
this.drawTableEdge_OC(bm_lo, upperTileId1, x1, y1);
} else {
this.drawTileToBitmap_OC(bm_lo, lowerTileId, x1, y1);
} if (at) this.drawShadow_OC(bm_lo, shadowBits, x1, y1);
}
for (i = 0; i < tilesHigh.length; i++) {
this.drawTileToBitmap_OC(bm_lo, tilesHigh[i], x1, y1);
}
} else { // B-E layers are drawn to low and high (2 bitmaps)
if (this._isTableTile(upperTileId1) && !this._isTableTile(tileId1)) {
if (!Tilemap.isShadowingTile(tileId0)) { tilesLow.push(_tableEdgeVirtualId + upperTileId1); }
}
if (this._isHigherTile(tileId2)) { tilesHigh.push(tileId2); }
else { tilesLow.push(tileId2); }
if (this._isHigherTile(tileId3)) { tilesHigh.push(tileId3); }
else { tilesLow.push(tileId3); }
bm_lo.clearRect(x1, y1, OcRam.twh[0], OcRam.twh[1]);
bm_hi.clearRect(x1, y1, OcRam.twh[0], OcRam.twh[1]);
for (let i = 0; i < tilesLow.length; i++) {
const lowerTileId = tilesLow[i];
if (lowerTileId < 0) {
} else if (lowerTileId >= _tableEdgeVirtualId) {
this.drawTableEdge_OC(bm_lo, upperTileId1, x1, y1);
} else {
this.drawTileToBitmap_OC(bm_lo, lowerTileId, x1, y1);
} if (at) this.drawShadow_OC(bm_lo, shadowBits, x1, y1);
}
for (let i = 0; i < tilesHigh.length; i++) {
this.drawTileToBitmap_OC(bm_hi, tilesHigh[i], x1, y1);
}
}
};
// Create shadow sprite to top layer
Spriteset_Map.prototype.createShadow_OC = function () {
this._shadowSprite = new Sprite();
this._shadowSprite.bitmap = ImageManager.loadSystem('Shadow1');
this._shadowSprite.anchor.x = 0.5;
this._shadowSprite.anchor.y = 1;
this._shadowSprite.z = 6;
this._layerContainer_OC.addChild(this._shadowSprite);
};
// Local_Coop compatibility
Game_Followers.prototype.isSomeoneCollided_OC = function (x, y, hl) {
return this.visibleFollowers().some(function (follower) {
return follower.pos(x, y) && (follower._higherLevel == hl);
}, this);
};
// ------------------------------------------------------------------------------
// Aliases
// ==============================================================================
this.extend(Spriteset_Map, "createDestination", function () {
_this["Spriteset_Map_createDestination"].apply(this, arguments);
this._destinationSprite.z = 9999 + 2;
});
this.extend(Game_Player, "center", function () {
_this["Game_Player_center"].apply(this, arguments); mapCulling();
});
// Preload tilesets
this.extend(Scene_Boot, "isReady", function () {
let ret = _this["Scene_Boot_isReady"].apply(this, arguments);
if (ret) preLoadSeasonTilesets(); return ret;
});
// Forces system to reload whole tileset
this.forceTilesetReload = function () {
_flags = []; _currentShaderTilemap = null;
_cacheReady = false; _tileSprites = [];
};
// Tileset change
this.extend(Game_Map, "changeTileset", function (tilesetId, just_loaded) {
OcRam.Time_System._prevTilesetId = this._tilesetId;
OcRam.Time_System._currentTilesetId = tilesetId;
_this["Game_Map_changeTileset"].apply(this, arguments);
if (!just_loaded) {
requestAnimationFrame(() => { reStartScene(); });
}
});
// Update "z" based on Y coordinate if same priority
this.extend(Tilemap, "updateTransform", function () {
this._sortChildren_OC(); _this["Tilemap_updateTransform"].apply(this, arguments);
}); if (!OcRam.isMZ()) { // In MV there's ShaderTilemap for webGL
this.extend(ShaderTilemap, "updateTransform", function () {
this._sortChildren_OC(); _this["ShaderTilemap_updateTransform"].apply(this, arguments);
});
}
// Do not face up when behind ladders
this.extend(Game_CharacterBase, "isOnLadder", function () {
if (!this._higherLevel && this.regionId() == _coverId) return false;
return _this["Game_CharacterBase_isOnLadder"].apply(this, arguments);
});
// Auto-assign floor level to new party members
this.extend(Game_Party, "addActor", function (actorId) {
_this["Game_Party_addActor"].apply(this, arguments);
if (SceneManager._scene.isMap()) {
const new_index = $gamePlayer._followers.visibleFollowers().length - 1;
if (new_index < 4 && new_index > -1) {
$gamePlayer._followers._data[new_index]._higherLevel = autoAssignFloorLevel($gamePlayer._followers._data[new_index]);
}
}
});
// Move sprites when scrolling map
this.extend(Spriteset_Map, "update", function () {
_this["Spriteset_Map_update"].apply(this, arguments);
if (SceneManager._scene._spriteset && SceneManager._scene._spriteset._layerContainer_OC) {
$gameMap.moveSpritesX_OC(); $gameMap.moveSpritesY_OC();
}
});
// This is the day, events can underpass AND overpass despite of player floor level...
this.extend(Game_CharacterBase, "refreshBushDepth", function () {
_this["Game_CharacterBase_refreshBushDepth"].apply(this, arguments);
const region_id = this.regionId();
if (region_id == _overpassId) {
this._higherLevel = true;
if (SceneManager._scene._spriteset !== undefined) updateEvent(this);
} else if (region_id == _underpassId) {
this._higherLevel = this._priorityType == 2;
if (SceneManager._scene._spriteset !== undefined) updateEvent(this);
}
});
if (_useTallSprites) { // Followers use same floor level as player...
this.extend(Game_Player, "refreshBushDepth", function () {
mapCulling(); const old_lvl = this._higherLevel;
_this["Game_Player_refreshBushDepth"].apply(this, arguments);
if (old_lvl != this._higherLevel) {
this._followers.visibleFollowers().forEach(function (f) {
f._higherLevel = $gamePlayer._higherLevel;
if (SceneManager._scene._spriteset !== undefined) {
const hs = getCharSprite(f);
if (hs) hs._showHigherSprite = f._higherLevel;
}
});
}
});
this.extend(Game_Follower, "refreshBushDepth", function () {
_this["Game_CharacterBase_refreshBushDepth"].apply(this, arguments);
});
} else {
// Culling done when player is moved (once in distance of tile)
this.extend(Game_Player, "refreshBushDepth", function () {
mapCulling(); _this["Game_Player_refreshBushDepth"].apply(this, arguments);
});
}
// Update character graphics on event page change
this.extend(Game_Event, "setupPage", function () {
_this["Game_Event_setupPage"].apply(this, arguments);
const ev_cmts = this.getStringComments();
ev_cmts.forEach(s => {
if (s == "<higher>") {
this._higherLevel = true;
} else if (s == "<lower>") {
this._higherLevel = false;
}
});
if (SceneManager._scene._spriteset !== undefined) {
if (this._higherLevel === undefined) this._higherLevel = autoAssignFloorLevel(this);
updateEvent(this);
} else {
requestAnimationFrame(() => {
if (this._higherLevel === undefined) this._higherLevel = autoAssignFloorLevel(this);
updateEvent(this);
});
}
});
// Check if sprite needs to be drawed
this.extend(Sprite_Character, "updateVisibility", function () {
_this["Sprite_Character_updateVisibility"].apply(this, arguments);
if (this._showHigherSprite === undefined && !this._character._higherLevel) return;
this.visible = (this._character._higherLevel) ? this._showHigherSprite : false;
});
// Update sprite graphics on updateAirshipAltitude
this.extend(Game_Vehicle, "updateAirshipAltitude", function () {
const is_hl = this._higherLevel;
const cs = getCharSprite(this);
if (!this._driving && this.isHighest() && cs) cs.visible = is_hl;
if (!this.isLowest() && !this.isHighest()) {
updateEvent(this); purgeHigherLevelSprite($gamePlayer);
OcRam.followers().forEach(f => { purgeHigherLevelSprite(f); });
}
_this["Game_Vehicle_updateAirshipAltitude"].apply(this, arguments);
if (!this._driving && this.isLowest()) {
if (!_isAirshipLanded) {
_isAirshipLanded = true; $gamePlayer._higherLevel = is_hl; updateEvent($gamePlayer);
if (is_hl) getCharSprite($gamePlayer)._showHigherSprite = is_hl;
$gamePlayer._followers.visibleFollowers().forEach(function (f) {
f._higherLevel = $gamePlayer._higherLevel; updateEvent(f);
if (is_hl) getCharSprite(f)._showHigherSprite = is_hl;
});
}
}
});
// Floor level wise landing...
this.extend(Game_Vehicle, "isLandOk", function (x, y, d) {
const tmp_ret = _this["Game_Vehicle_isLandOk"].apply(this, arguments);
if (tmp_ret) {
if ($gameMap.regionId(x, y) == _coverId) return false;
if (this._type == "airship") {
if (!$gameMap.checkPassage(x, y, (1 << (d / 2 - 1)) & 0x0f, true)) return false;
}
return true;
}
});
// "Smart" drop >> check which autotile is drawn to landing point
this.extend(Game_Vehicle, "getOff", function () {
_isAirshipLanded = false; let is_hl = false;
if ($gamePlayer._vehicleType == "airship") {
$gamePlayer._higherLevel = undefined;
is_hl = autoAssignFloorLevel($gamePlayer) ||
($gameMap.regionId($gamePlayer._x, $gamePlayer._y) == _overpassId);
this._higherLevel = is_hl;
} _this.debug("Landed to " + (is_hl ? "higher" : "lower") + " level", $gamePlayer);
_this["Game_Vehicle_getOff"].apply(this, arguments);
});
// If it's boat sea level is always 'low' level... Must be overwritten via JS if this is not the case
this.extend(Game_Player, "updateVehicleGetOff", function() {
if (!this.areFollowersGathering() && this.vehicle().isLowest()) {
if ($gamePlayer._vehicleType != "airship") {
setObjFloorLevel($gamePlayer, 'low');
OcRam.followers().forEach(f => {
setObjFloorLevel(f, 'low');
});
}
} _this["Game_Player_updateVehicleGetOff"].apply(this, arguments);
});
// For OcRam_Movement compatibility and floor level wise boarding...
this.extend(Game_Player, "getOnVehicle", function () {
const d = this.direction(); const x1 = this.x; const y1 = this.y;
const x2 = $gameMap.roundXWithDirection(x1, d);
const y2 = $gameMap.roundYWithDirection(y1, d);
let vehicle_type = ''; let ret = false;
if ($gameMap.airship().pos(x1, y1)) {
vehicle_type = 'airship';
} else if ($gameMap.ship().pos(x2, y2)) {
vehicle_type = 'ship';
} else if ($gameMap.boat().pos(x2, y2)) {
vehicle_type = 'boat';
} else {
return false;
}
if (vehicle_type == 'airship' && (this._higherLevel != $gameMap.airship()._higherLevel)) {
ret = false;
} else {
ret = _this["Game_Player_getOnVehicle"].apply(this, arguments);
}
if (ret && vehicle_type == 'airship') {
setObjFloorLevel(OcRam.getGameObject(-102), 'high');
} return ret;
});
// Hide actor higherlevel sprites...
this.extend(Game_Vehicle, "getOn", function () {
_this["Game_Vehicle_getOn"].apply(this, arguments); let sprite = null;
this._higherLevel = this.isAirship();
for (let i = 0; i < $gamePlayer._followers.visibleFollowers().length; i++) {
$gamePlayer._followers.visibleFollowers()[i]._transparent = true;
updateEvent($gamePlayer._followers.visibleFollowers()[i]);
} sprite = getCharSprite($gamePlayer);
$gamePlayer._higherLevel = this._higherLevel;
OcRam.followers().forEach(f => {
f._higherLevel = this._higherLevel;
}); if (sprite != undefined) sprite.visible = false; updateEvent($gamePlayer);
});
// Floor level wise collision check
this.extend(Game_Event, "isCollidedWithPlayerCharacters", function (x, y) {
if ($gamePlayer._followers.isSomeoneCollided_OC(x, y, this._higherLevel)) return true;
if ($gamePlayer._higherLevel != this._higherLevel) return false;
return _this["Game_Event_isCollidedWithPlayerCharacters"].apply(this, arguments);
});
// Special alias (to be used diffrently than normal aliases)
const OC_Tilemap_readMapData = Tilemap.prototype._readMapData;
// Passages will draw B-E COVER layers ALWAYS
Tilemap.prototype._readMapData_OC = function (x, y, z) {
return OC_Tilemap_readMapData.call(this, x, y, z);
};
// Core engine calls this to draw tiles
// >> Do not draw B-E COVER layers if they are already drawn on higher layers
Tilemap.prototype._readMapData = function (x, y, z) {
if (z == 2 || z == 3) {
if ($gameMap.regionId(x, y) == _coverId) return 0;
} return OC_Tilemap_readMapData.call(this, x, y, z);
};
// ------------------------------------------------------------------------------
// Overrides
// ==============================================================================
if (Imported.OcRam_Movement) {
Game_CharacterBase.prototype.isCollidedWithEvents_OC = function (x, y, d, mx, my) {
if (this.isPlayer()) {
if (this.isInAirship()) return false;
let idir = this.getInputDirection();
if (OcRam.Movement.isDiagonalDir(idir)) return false;
} let events = $gameMap.eventsXyNt(x, y);
let ret = events.some(event => event.isNormalPriority() && (event._higherLevel == this._higherLevel));
ret = ret && ((mx < 0.5 && my < 0.1) || (my < 0.5 && mx < 0.1));
if (!ret) {
switch (d) {
case 2: case 8:
if (mx > 0.5 && my < 0.1) {
events = $gameMap.eventsXyNt(x + 1, y);
ret = events.some(event => event.isNormalPriority() && (event._higherLevel == this._higherLevel));
} break;
case 4: case 6:
if (my > 0.5 && mx < 0.1) {
events = $gameMap.eventsXyNt(x, y + 1);
ret = events.some(event => event.isNormalPriority() && (event._higherLevel == this._higherLevel));
} break;
}
} return ret;
};
}
if (Imported.OcRam_Movement && OcRam.Movement.pixelMoveEnabled()) { // Consider floor level also! - PIXEL MOVEMENT VERSION!
// Consider floor level also! (with OcRam pixel movement...)
Game_Map.prototype.isPassable = function (x, y, d, hl) {
return this.checkPassage(Math.round(x), Math.round(y), (1 << (d / 2 - 1)) & 0x0f, hl);
};
Game_CharacterBase.prototype.isMapPassable = function (x, y, d) {
if (x > $gameMap.width() - 1) x %= $gameMap.width();
if (y > $gameMap.height() - 1) y %= $gameMap.height();
const x2 = $gameMap.roundXWithDirection(x, d);
const y2 = $gameMap.roundYWithDirection(y, d);
const next_region_id = $gameMap.regionId(x2, y2);
if (next_region_id == _blockId) return false;
const this_region_id = $gameMap.regionId(x, y);
const t_hl = this._higherLevel;
const is_this_cover = (this_region_id == _coverId || this_region_id == _autoCoverId);
if (t_hl) { // This char is in higher ground
if (is_this_cover) {
if (next_region_id == _underpassId) return false;
} if (this_region_id == _blockHighLowId && next_region_id == _underpassId) return false;
} else { // This char is in lower ground
if (is_this_cover && (next_region_id == _overpassId || next_region_id == 0)) return false;
if (next_region_id == _overheadId) return false;
if ((this_region_id == _underpassId || is_this_cover) && (next_region_id == _underpassId ||
next_region_id == _coverId || next_region_id == _autoCoverId)) return true;
} return $gameMap.isPassable(x, y, d, this._higherLevel) && $gameMap.isPassable(x2, y2, this.reverseDir(d), this._higherLevel);
};
} else { // Consider floor level also! - TILE BASED MOVEMENT
// Consider floor level also!
Game_Map.prototype.isPassable = function (x, y, d, hl) {
return this.checkPassage(x, y, (1 << (d / 2 - 1)) & 0x0f, hl);
};
Game_CharacterBase.prototype.isMapPassable = function (x, y, d) {
const x2 = $gameMap.roundXWithDirection(x, d);
const y2 = $gameMap.roundYWithDirection(y, d);
const next_region_id = $gameMap.regionId(x2, y2);
if (next_region_id == _blockId) return false;
const this_region_id = $gameMap.regionId(x, y);
const t_hl = this._higherLevel;
const is_this_cover = (this_region_id == _coverId || this_region_id == _autoCoverId);
if (t_hl) { // This char is in higher ground
if (is_this_cover) {
if (next_region_id == _underpassId) return false;
} if (this_region_id == _blockHighLowId && next_region_id == _underpassId) return false;
} else { // This char is in lower ground
if (is_this_cover && (next_region_id == _overpassId || next_region_id == 0)) return false;
if (next_region_id == _overheadId) return false;
if ((this_region_id == _underpassId || is_this_cover) && (next_region_id == _underpassId ||
next_region_id == _coverId || next_region_id == _autoCoverId)) return true;
} return $gameMap.isPassable(x, y, d, this._higherLevel) && $gameMap.isPassable(x2, y2, this.reverseDir(d), this._higherLevel);
};
}
// Consider floor level also!
Game_Map.prototype.checkPassage = function (x, y, bit, hl) {
const this_region_id = $gameMap.regionId(x, y);
if (this_region_id == _blockId) return false;
if (!hl) { // Event which called this method, is on lower floor level
if (this_region_id == _overheadId) return false;
if (this_region_id == _blockHighLowId) return false;
}
const tiles = this.allTiles(x, y);
for (let i = 0; i < tiles.length; i++) {
if (tiles) {
const flag = _flags[tiles[i]];
if (((flag & 0x10) !== 0) || (hl ? false : (this_region_id == _coverId || this_region_id == _autoCoverId) ? true : false)) // [*] No effect on passage
continue;
if ((flag & bit) === 0) // [o] Passable
return true;
if ((flag & bit) === bit) // [x] Impassable
return false;
}
} return true;
};
// Consider floor level also!
Game_CharacterBase.prototype.isCollidedWithEvents = function (x, y) {
let events = $gameMap.eventsXyNt(x, y); const is_hl = this._higherLevel;
return events.some(function (event) {
return (event.isNormalPriority() && (event._higherLevel == is_hl));
});
};
// Consider floor level also!
Game_Event.prototype.isCollidedWithEvents = function (x, y) {
let events = $gameMap.eventsXyNt(x, y); const is_hl = this._higherLevel;
return events.some(function (event) {
return (event.isNormalPriority() && (event._higherLevel == is_hl));
});
};
// Change event interaction by floor level
Game_Player.prototype.startMapEvent = function (x, y, triggers, normal) { // Start events ONLY if they are on same 'floor'
if (!$gameMap.isEventRunning()) {
const this_hl = (this !== undefined) ? this._higherLevel : false;
for (const event of $gameMap.eventsXy(x, y)) {
if (
event.isTriggerIn(triggers) &&
event.isNormalPriority() === normal
) {
let ev_cmts = []; let trigger_always = false;
ev_cmts = event.getStringComments(); trigger_always = false;
ev_cmts.forEach(s => {
if (s == "<trigger_always>") trigger_always = true;
}); if (event._higherLevel == this_hl || trigger_always) {
event.start(); updateEvent(event);
}
}
}
}
};
// Do not "refreshBushDepth" if vehicle (prevents undesired under-/over passages)
Game_Vehicle.prototype.refreshBushDepth = function () { /* do nothing */ };
// Show balloons ABOVE cover tiles...
Sprite_Balloon.prototype.updatePosition = function () {
if ($gameMap) {
this.x = this._target.x + Math.floor($gameMap._displayX * OcRam.twh[0]);
this.y = this._target.y - this._target.height + Math.floor($gameMap._displayY * OcRam.twh[1]);
} else {
this.x = this._target.x;
this.y = this._target.y - this._target.height;
}
};
// Show animations ABOVE cover tiles...
Sprite_AnimationMV.prototype.updatePosition = function () {
if (this._animation.position === 3) {
this.x = this.parent.width / 2;
this.y = this.parent.height / 2;
} else if (this._targets.length > 0) {
const target = this._targets[0];
const parent = target.parent;
const grandparent = parent ? parent.parent : null;
if ($gameMap) {
this.x = target.x + Math.floor($gameMap._displayX * OcRam.twh[0]);
this.y = target.y + Math.floor($gameMap._displayY * OcRam.twh[1]);
} else {
this.x = target.x;
this.y = target.y;
}
if (this.parent === grandparent) {
this.x += parent.x;
this.y += parent.y;
}
if (this._animation.position === 0) {
this.y -= target.height;
} else if (this._animation.position === 1) {
this.y -= target.height / 2;
}
}
};
Game_Character.prototype.setFloorLevel = function (str_level) {
setObjFloorLevel(this, str_level);
};
if (Imported.OcRam_Events) { // Overwrite Game_Player.prototype.checkThrowPass to allow throw from higher to lower!
Game_Character.prototype.checkThrowPass = function (x, y) {
const x1 = this._direction == 6 ? x + 1 : this._direction == 4 ? x - 1 : x;
const y1 = this._direction == 2 ? y + 1 : this._direction == 8 ? y - 1 : y;
const nxt_rid = $gameMap.regionId(x1, y1); const ths_rid = $gameMap.regionId(x, y);
const hne = !($gameMap.eventsXyNt(x1, y1).find(e => { return e._priorityType == 1 && this._higherLevel == e._higherLevel; }));
return (this.isMapPassable(x, y, this._direction, this._higherLevel) && hne) ||
(nxt_rid == _underpassId && hne) ||
nxt_rid == OcRam.Events.allowedThrowRegionId() ||
nxt_rid == OcRam.Events.allowedLandingRegionId() ||
ths_rid == OcRam.Events.allowedThrowRegionId() ||
ths_rid == OcRam.Events.allowedLandingRegionId();
};
}
// ------------------------------------------------------------------------------
// Core "must overrides"
// ==============================================================================
this.clearPluginData = function () {
this.forceTilesetReload(); // When exited to title clear things up...
};
this.loadPluginData = gs => {
gs.loadPassageData();
};
this.savePluginData = gs => {
if (!OcRam._autoSaving) gs.savePassageData();
};
this.onMapStart = sm => {
initSprites();
let loop_hack_redefined = false;
if (!$gameMap.isLoopHorizontal() && !$gameMap.isLoopVertical()) {
OcRam.hackSC_LooppedMaps = sc_oc => { }; loop_hack_redefined = true;
} else if ($gameMap.isLoopHorizontal() && $gameMap.isLoopVertical()) {
OcRam.hackSC_LooppedMaps = sc_oc => { // Loopping maps... both x and y
if ((-(sc_oc.parent.x) > OcRam.Passages._loopEndPointX)) {
if (sc_oc.x < OcRam.Passages._loopEndPointX) sc_oc.x += $gameMap._pixelWidth;
} else {
if (sc_oc.x < Graphics.width * 0.5) sc_oc.x += $gameMap._pixelWidth;
} if ((-(sc_oc.parent.y) > OcRam.Passages._loopEndPointY)) {
if (sc_oc.y < OcRam.Passages._loopEndPointY) sc_oc.y += $gameMap._pixelHeight;
} else {
if (sc_oc.y < Graphics.height * 0.5) sc_oc.y += $gameMap._pixelHeight;
}
}; loop_hack_redefined = true;
}
if ($gameMap.isLoopHorizontal()) { // Horizontal loopped map edges....
this._loopEndPointX = $gameMap._pixelWidth - (Graphics.width * 0.5);
Game_Map.prototype.moveSpritesX_OC = function () {
const cx = (this._displayX * OcRam.twh[0]) | 0;
SceneManager._scene._spriteset._layerContainer_OC.x = -cx;
if (_shiftedX && cx < this._pixelWidth - Graphics.width) {
shiftScreenPassagesX();
} else if (!_shiftedX && cx > this._pixelWidth - Graphics.width) {
shiftScreenPassagesX();
}
}; if (OcRam._menuCalled) shiftScreenPassagesX();
if (!loop_hack_redefined) {
OcRam.hackSC_LooppedMaps = sc_oc => { // Loopping maps... x
if ((-(sc_oc.parent.x) > OcRam.Passages._loopEndPointX)) {
if (sc_oc.x < OcRam.Passages._loopEndPointX) sc_oc.x += $gameMap._pixelWidth;
} else {
if (sc_oc.x < Graphics.width * 0.5) sc_oc.x += $gameMap._pixelWidth;
}
};
}
} else {
Game_Map.prototype.moveSpritesX_OC = function () {
SceneManager._scene._spriteset._layerContainer_OC.x = -(this._displayX * OcRam.twh[0]) | 0;
}; this._loopEndPointX = 512 * OcRam.twh[0]; // Maps will never hit this...
}
if ($gameMap.isLoopVertical()) { // Vertical loopped map edges....
this._loopEndPointY = $gameMap._pixelHeight - (Graphics.height * 0.5);
Game_Map.prototype.moveSpritesY_OC = function () {
const cy = (this._displayY * OcRam.twh[1]) | 0;
SceneManager._scene._spriteset._layerContainer_OC.y = -cy;
if (_shiftedY && cy < this._pixelHeight - Graphics.height) {
shiftScreenPassagesY();
} else if (!_shiftedY && cy > this._pixelHeight - Graphics.height) {
shiftScreenPassagesY();
}
}; if (OcRam._menuCalled) shiftScreenPassagesY();
if (!loop_hack_redefined) {
OcRam.hackSC_LooppedMaps = sc_oc => { // Loopping maps... y
if ((-(sc_oc.parent.y) > OcRam.Passages._loopEndPointY)) {
if (sc_oc.y < OcRam.Passages._loopEndPointY) sc_oc.y += $gameMap._pixelHeight;
} else {
if (sc_oc.y < Graphics.height * 0.5) sc_oc.y += $gameMap._pixelHeight;
}
};
}
} else {
Game_Map.prototype.moveSpritesY_OC = function () {
SceneManager._scene._spriteset._layerContainer_OC.y = -(this._displayY * OcRam.twh[1]) | 0;
}; this._loopEndPointY = 512 * OcRam.twh[1]; // Maps will never hit this...
}
};
this.onMapTerminate = sm => { // Make sure layers are saved before scene is terminated (except if new map)
if (SceneManager.isNextScene(Scene_Map)) {
if ($gamePlayer.newMapId() != $gameMap.mapId()) {
Game_Map.prototype.moveSpritesY_OC = function () { }; Game_Map.prototype.moveSpritesX_OC = function () { };
this.forceTilesetReload(); this.debug("New map is coming...", "Force to re-load whole tileset!");
} else {
if (OcRam.Time_System._currentTilesetId != OcRam.Time_System._prevTilesetId) {
this.forceTilesetReload(); this.debug("Season has been changed!", "Force to re-load whole tileset!");
}
}
}
};
this.createLowerMapLayer = sm => {
sm._baseSprite.removeChild(sm._shadowSprite);
sm.createCoverLayers_OC();
sm._baseSprite.addChild(sm._shadowSprite);
sm._effectsContainer = sm._layerContainer_OC; // Show balloons + animations ABOVE cover tiles...
};
this.createLowerBattleLayer = sb => { };
this.onMapLoaded = sm => { };
// ------------------------------------------------------------------------------
// Plugin commands
// ==============================================================================
PluginManager.registerCommand("OcRam_" + this.name, "setFloorLevel", function (args) {
_this.debug("Plugin command: setFloorLevel", args);
if (SceneManager._scene._spriteset !== undefined) {
const obj_id = Number(args.objectId);
const this_obj = OcRam.getGameObject(obj_id);
const str_level = String(args.floorLevel).toLowerCase();
this_obj.setFloorLevel(str_level.toLowerCase());
if (obj_id == -1) { // update followers also...
OcRam.followers().forEach(f => {
f.setFloorLevel(str_level.toLowerCase());
});
}
}
});
PluginManager.registerCommand("OcRam_" + this.name, "setTransferLevel", function (args) {
_this.debug("Plugin command: setTransferLevel", args);
_transferLevel = Number(args.floorLevel);
});
}.bind(OcRam.Passages)());