//======================================== // TF_ScreenUtil.js // Version :0.7.0.0 // For : RPGツクールMZ (RPG Maker MZ) // ----------------------------------------------- // Copyright : Tobishima-Factory 2020-2024 // Website : http://tonbi.jp // // This software is released under the MIT License. // http://opensource.org/licenses/mit-license.php //======================================== /*:ja * @target MZ * @plugindesc 各種画面サイズ設定と画面固定 * @author とんび﹫鳶嶋工房 * @url https://github.com/tonbijp/RPGMakerMZ/blob/master/TF_ScreenUtil.js * @base PluginCommonBase * @orderAfter PluginCommonBase * * * @help * マップ画面を拡大するとか画面周りのプラグイン。 * * ※ PluginCommonBase 定義によりパラメータや引数に \V[n] を使えます。 * * ●メタタグ * [マップ]のメモ欄にメタタグを書き込むと、そのマップはスクロールせず固定となります。 * 書式: * [画面左座標] マイナスや小数点以下も含む数値。 * [区切り文字] 数値以外の文字、複数も可(推奨: ", ") 他のプラグインで慣れてるやつでもいいです。 * [画面上座標] マイナスや小数点以下も含む数値。 * * 例: * * 利用規約 : MITライセンス * @================================================ * @command locateXY @text カメラを指定座標に設定 * @desc 瞬時に位置を変え、その位置に固定される。 * 指定するのは画面左上の座標。 * * @arg pointStr @text 移動先位置(タイル数) * @desc 移動先座標(小数点以下可) * @type string @default 0,0 * * @================================================ * @command locateEv @text カメラをイベント位置に設定 * @desc 瞬時に位置を変え、その位置に固定される。 * 指定するのは画面左上の座標。 * * @arg destinationId @text 目標イベントID * @desc * イベントID(数値)かイベントの名前 * @type combo @default this * @option this @option player @option follower0 @option follower1 @option follower2 * @option boat @option ship @option airship * * @arg pointStr @text 移動先位置(タイル数) * @desc イベントからの相対座標(小数点以下可) * @type string @default 0,0 * @================================================ * @command fixMap @text カメラを固定 * @desc カメラをその場に固定する。 * * @arg isMapFixed @text カメラ固定状態 * @desc * @type boolean @default false * @on 固定 @off 追従(規定値) * * @================================================ */ ( () => { ("use strict"); const PLUGIN_NAME = "TF_ScreenUtil"; // メタタグ const TAG_FLEXED_MAP = "TF_fixedMap"; // 定数 const DEFAULT_SCREEN_WIDTH = 816; const DEFAULT_SCREEN_HEIGHT = 624; const TF_zoomScale = 2; // 自由にできそうに見えて二倍固定 /** * パラメータを受け取る */ const pluginParams = PluginManagerEx.createParameter(document.currentScript); /*---- プラグインコマンド ----*/ const COM_LOCATE_XY = "locateXY"; const COM_LOCATE_EV = "locateEv"; const COM_FIX_MAP = "fixMap"; /** * プラグインコマンドの登録 */ // [ カメラを指定座標に配置 ] PluginManagerEx.registerCommand( document.currentScript, COM_LOCATE_XY, function (args) { const rect = stringToPoint(args.pointStr); locateCameraXY(rect.x, rect.y); } ); // [ カメラをイベント位置に配置 ] PluginManagerEx.registerCommand( document.currentScript, COM_LOCATE_EV, function (args) { const destinationEvent = stringToEvent(this, args.destinationId); const rect = stringToPoint(args.pointStr); locateCameraEv(destinationEvent, rect.x, rect.y); } ); // [ カメラを固定 ] PluginManagerEx.registerCommand( document.currentScript, COM_FIX_MAP, function (args) { $gameMap.isMapFixed = args.isMapFixed; } ); /** * カメラ位置の設定。 * * @param {Number} x x座標(タイル数) * @param {Number} y y座標(タイル数) */ function locateCameraXY(x, y) { $gameMap.isMapFixed = true; $gameMap.setDisplayPos(x, y); } /** * カメラ位置をキャラ起点で設定。 * * @param {Game_Character} destinationEvent 目標イベント * @param {Number} dx 目標イベントからの相対x座標(タイル数) * @param {Number} dy 目標イベントからの相対y座標(タイル数) */ function locateCameraEv(destinationEvent, dx, dy) { const x = $gameMap.roundX(destinationEvent.x + dx); const y = $gameMap.roundY(destinationEvent.y + dy); locateCameraXY(x, y); } // ソフトフォーカスかけない設定 // 個別に設定できるようにしたほうがいいと思う。 // 少なくともプラグインパラメータに用意しておいたほうがいいね。 const _Bitmap_initialize = Bitmap.prototype.initialize; Bitmap.prototype.initialize = function (width, height) { _Bitmap_initialize.apply(this, arguments); this._smooth = false; }; /*---- Game_Player ----*/ // マップ固定 const _Game_Player_updateScroll = Game_Player.prototype.updateScroll; Game_Player.prototype.updateScroll = function (lastScrolledX, lastScrolledY) { if ($gameMap.isMapFixed) return; _Game_Player_updateScroll.apply(this, arguments); }; Game_Player.prototype.centerX = function () { return (Graphics.width / $gameMap.displayTileWidth() - 1) / 2.0; }; Game_Player.prototype.centerY = function () { return (Graphics.height / $gameMap.displayTileHeight() - 1) / 2.0; }; /*---- Game_Map ----*/ Game_Map.prototype.parallaxOx = function () { if (this._parallaxZero) { return this._parallaxX * this.displayTileWidth(); } else if (this._parallaxLoopX) { return (this._parallaxX * this.displayTileWidth()) / 2; } else { return 0; } }; Game_Map.prototype.parallaxOy = function () { if (this._parallaxZero) { return this._parallaxY * this.displayTileHeight(); } else if (this._parallaxLoopY) { return (this._parallaxY * this.displayTileHeight()) / 2; } else { return 0; } }; Game_Map.prototype.screenTileX = function () { return Math.round((Graphics.width / this.displayTileWidth()) * 16) / 16; }; Game_Map.prototype.screenTileY = function () { return Math.round((Graphics.height / this.displayTileHeight()) * 16) / 16; }; // Game_Map.tileUnit は HalfMove.js 対応 Game_Map.prototype.canvasToMapX = function (x) { const tileWidth = this.displayTileWidth(); const originX = this._displayX * tileWidth; const mapX = Math.floor((originX + x) / tileWidth / Game_Map.tileUnit); return this.roundX(mapX * Game_Map.tileUnit); }; Game_Map.prototype.canvasToMapY = function (y) { const tileHeight = this.displayTileHeight(); const originY = this._displayY * tileHeight; const mapY = Math.floor((originY + y) / tileHeight / Game_Map.tileUnit); return this.roundY(mapY * Game_Map.tileUnit); }; Game_Map.prototype.updateParallax = function () { if (this._parallaxLoopX) { this._parallaxX += this._parallaxSx / this.displayTileWidth() / 2; } if (this._parallaxLoopY) { this._parallaxY += this._parallaxSy / this.displayTileHeight() / 2; } }; // 画面表示されるタイルの大きさ(ピクセル数) // tileWidth() は素材のタイルサイズとして残す。 Game_Map.prototype.displayTileWidth = function () { return this.tileWidth() * $gameScreen.zoomScale(); }; // 画面表示されるタイルの大きさ(ピクセル数) // tileHeight() は素材のタイルサイズとして残す。 Game_Map.prototype.displayTileHeight = function () { return this.tileWidth() * $gameScreen.zoomScale(); }; Game_CharacterBase.prototype.isNearTheScreen = function () { const gw = Graphics.width; const gh = Graphics.height; const tw = $gameMap.displayTileWidth(); const th = $gameMap.displayTileHeight(); const px = this.scrolledX() * tw + tw / 2 - gw / 2; const py = this.scrolledY() * th + th / 2 - gh / 2; return px >= -gw && px <= gw && py >= -gh && py <= gh; }; /*---- Scene_Map ----*/ // startではタイミングが遅いのでこのハンドラを使う const _Scene_Map_onMapLoaded = Scene_Map.prototype.onMapLoaded; Scene_Map.prototype.onMapLoaded = function () { //マップ拡大 $gameScreen._zoomScale = TF_zoomScale; _Scene_Map_onMapLoaded.call(this); if (!$dataMap.meta) return; // テスト時は $dataMap.meta が空なので以下処理しない // マップメモ固定座標の指定メタタグの処理 // 例: const fixedMapArgs = PluginManagerEx.findMetaValue( $dataMap, TAG_FLEXED_MAP ); $gameMap.isMapFixed = fixedMapArgs !== undefined; if (!$gameMap.isMapFixed) return; const pos = stringToPoint(fixedMapArgs); $gameMap.setDisplayPos(pos.x, pos.y); }; // 戦闘開始のエンカウントアニメーションを拡大に合わせて修正 Scene_Map.prototype.updateEncounterEffect = function () { if (this._encounterEffectDuration <= 0) return; this._encounterEffectDuration--; const speed = this.encounterEffectSpeed(); const n = speed - this._encounterEffectDuration; const p = n / speed; const q = ((p - 1) * 20 * p + 5) * p + 1; const zoomX = $gamePlayer.screenX(); const zoomY = $gamePlayer.screenY() - $gameMap.tileHeight() / 2; if (n === 2) { $gameScreen.setZoom(zoomX, zoomY, TF_zoomScale); this.snapForBattleBackground(); this.startFlashForEncounter(speed / 2); } $gameScreen.setZoom(zoomX, zoomY, q * TF_zoomScale); if (n === Math.floor(speed / 6)) { this.startFlashForEncounter(speed / 2); } if (n === Math.floor(speed / 2)) { BattleManager.playBattleBgm(); this.startFadeOut(this.fadeSpeed()); } }; /*--- Sprite_Battleback ---*/ // 背景をそのまま画面いっぱいに拡大する方式に変更 Sprite_Battleback.prototype.adjustPosition = function () { this.width = Graphics.width; this.height = Graphics.height; this.x = (Graphics.width - this.width) / 2; if ($gameSystem.isSideView()) { this.y = Graphics.height - this.height; } else { this.y = 0; } const ratioX = this.width / this.bitmap.width; const ratioY = this.height / this.bitmap.height; const scale = Math.max(ratioX, ratioY, 1.0); this.scale.x = scale; this.scale.y = scale; }; /*--- Sprite_Actor ---*/ // アクター位置をスクリーンサイズに合わせて調整 const PARTY_X = 600; const PARTY_Y = 250; const VERTICAL_DIFF = 48; const HORIZONTAL_DIFF = 32; const _Sprite_Actor_setActorHome = Sprite_Actor.prototype.setActorHome; Sprite_Actor.prototype.setActorHome = function (index) { _Sprite_Actor_setActorHome.apply(this, arguments); const x = PARTY_X + (Graphics.boxWidth - DEFAULT_SCREEN_WIDTH) + index * HORIZONTAL_DIFF; const y = PARTY_Y + (Graphics.boxHeight - DEFAULT_SCREEN_HEIGHT) + index * VERTICAL_DIFF; this.setHome(x, y); }; /*--- Spriteset_Base ---*/ const _Spriteset_Base_updatePosition = Spriteset_Base.prototype.updatePosition; Spriteset_Base.prototype.updatePosition = function () { _Spriteset_Base_updatePosition.call(this); // 拡大分のズレを考慮 this.x -= Math.round($gameScreen.zoomX() * (1 - TF_zoomScale)); this.y -= Math.round($gameScreen.zoomY() * (1 - TF_zoomScale)); }; /*--- Game_Picture ---*/ const _Game_Picture_show = Game_Picture.prototype.show; Game_Picture.prototype.show = function ( name, origin, x, y, scaleX, scaleY, opacity, blendMode ) { x = Math.floor(x / TF_zoomScale); y = Math.floor(y / TF_zoomScale); scaleX = Math.floor(scaleX / TF_zoomScale); scaleY = Math.floor(scaleY / TF_zoomScale); _Game_Picture_show.apply(this, arguments); }; const _Game_Picture_move = Game_Picture.prototype.move; Game_Picture.prototype.move = function( origin, x, y, scaleX, scaleY, opacity, blendMode, duration, easingType ) { x = Math.floor(x / TF_zoomScale); y = Math.floor(y / TF_zoomScale); scaleX = Math.floor(scaleX / TF_zoomScale); scaleY = Math.floor(scaleY / TF_zoomScale); _Game_Picture_move.apply(this, arguments); }; /*---- パラメータパース関数 ----*/ const PARAM_TRUE = "true"; const PARAM_ON = "on"; const TYPE_BOOLEAN = "boolean"; const TYPE_NUMBER = "number"; const TYPE_STRING = "string"; /** * 与えられた文字列に変数が指定されていたら、変数の内容に変換して返す。 * @param {String} value 変換元の文字列( \V[n]形式を含む ) * @return {String} 変換後の文字列 */ function treatValue(value) { if (typeof value === TYPE_NUMBER) return value; if (value === undefined || value === "") return "0"; const result = value.match(/\x1bV\[(.+)\]/i); if (result === null) return value; const id = parseInt(result[1], 10); if (isNaN(id)) { return $gameVariables.valueByName(result[1]); } else { return $gameVariables.value(id); } } /*--- Game_Variables ---*/ /** * 変数を文字列で指定し、値を返す。 * 標準のvalueは空文字列だと 0 を返す。 * @param {String} name 変数(ID, 名前による指定が可能) */ Game_Variables.prototype.valueByName = function (name) { const variableId = stringToVariableId(name); if (typeof this._data[variableId] === TYPE_STRING) { return this._data[variableId]; } else { return this.value(variableId); } }; /** * 指定された変数のIDを返す。 * @param {String} name 変数(ID, 名前, \V[n]による指定が可能) */ function stringToVariableId(name) { name = treatValue(name); let i = $dataSystem.variables.indexOf(name); if (0 <= i) return i; i = parseInt(name, 10); if (isNaN(i)) throw Error(`${PLUGIN_NAME}: I can't find the variable '${name}'`); return i; } /*--- ユーティリティ関数 ---*/ /** * 文字列をPointオブジェクトに変換して返す。 * @param {String} pointStr "x, y" 形式の文字列 * @returns {Point} */ function stringToPoint(pointStr) { const args = pointStr.match(/([-.0-9]+)[^-.0-9]+([-.0-9]+)/); if (args === null) throw `${PLUGIN_NAME}: wrong parameter '${pointStr}'`; return new Point(parseFloat(args[1]), parseFloat(args[2])); } /*--- イベントID・オブジェクト取得関数 ---*/ /*---- イベントIDの配置オフセット ----*/ const FOLLOWER_OFFSET = -2; const VEHICLE_OFFSET = -100; /*---- イベントID変換用文字列 ----*/ const EVENT_THIS = "this"; const EVENT_PLAYER = "player"; const EVENT_FOLLOWER0 = "follower0"; const EVENT_FOLLOWER1 = "follower1"; const EVENT_FOLLOWER2 = "follower2"; const EVENT_FOLLOWER_ALL = "all"; const VEHICLE_BOAT = "boat"; const VEHICLE_SHIP = "ship"; const VEHICLE_AIRSHIP = "airship"; const VEHICLE_WALK = "walk"; /** * character を拡張して隊列メンバーも指定できるようにしたもの。 * @param {Game_Interpreter} interpreter インタプリタ * @param {Number} id 拡張イベントID * @returns {Game_CharacterBase} */ function getEventById(interpreter, id) { if (id <= VEHICLE_OFFSET) { return $gameMap._vehicles[VEHICLE_OFFSET - id]; // 乗り物(0〜2) } else if (id <= FOLLOWER_OFFSET) { return $gamePlayer.followers().follower(FOLLOWER_OFFSET - id); // 隊列メンバー(0〜2) } else { return interpreter.character(id); // プレイヤーキャラおよびイベント } } /** * 文字列をイベントIDへ変換 * @param {String} value イベントIDの番号か識別子 * @returns {Number} 拡張イベントID */ function stringToEventId(value) { value = treatValue(value); const result = parseInt(value, 10); if (!isNaN(result)) return result; const lowValue = value.toLowerCase(); switch (lowValue) { case EVENT_THIS: return 0; case EVENT_PLAYER: return -1; case EVENT_FOLLOWER0: return FOLLOWER_OFFSET; case EVENT_FOLLOWER1: return FOLLOWER_OFFSET - 1; case EVENT_FOLLOWER2: return FOLLOWER_OFFSET - 2; case VEHICLE_BOAT: return VEHICLE_OFFSET; case VEHICLE_SHIP: return VEHICLE_OFFSET - 1; case VEHICLE_AIRSHIP: return VEHICLE_OFFSET - 2; } const e = $dataMap.events.find((e) => e && e.name === value); if (e === undefined) throw Error(`${PLUGIN_NAME}: I can't find the event '${value}'`); return e.id; } /** * 指定された文字列に対応するイベントを返す * @param {Game_Interpreter} interpreter インタプリタ * @param {String} eventId イベントIDの番号か識別子 * @returns {Game_CharacterBase} */ function stringToEvent(interpreter, eventId) { return getEventById(interpreter, stringToEventId(eventId)); } } )();