//============================================================================= // Keke_PredictionSystem - 行動予測システム // バージョン: 1.3.1 //============================================================================= // Copyright (c) 2023 ケケー // Released under the MIT license // http://opensource.org/licenses/mit-license.php //============================================================================= /*: * @target MZ * @plugindesc 敵の行動&ターゲット先行表示システム * @author ケケー * @url https://kekeelabo.com * * @help * 【ver.1.3.1】 * 敵キャラの次の行動とターゲットを表示するシステム * ターン制&タイムプログレス両対応 * * ● 使い方 ● * * ■【最初にやること】ターゲットラインの調整 *  バトルのレイアウトに応じてターゲットラインの位置と方向を調整する * ◎ターゲットラインとは行動者からターゲットに向けて伸び、 *  誰がターゲットなのか示すラインのこと * ◎調整はプラグインパラメータで行う * ◎一例として、左が敵、右が味方のサイドビュー画面向けに調整してみる *  そのままやってみてほしい * [1]プラグインパラメータ → 『ライン-進行方向』 を「右」にする *  ラインは敵から味方に向けて伸びるもの *  そして敵が左側、味方が右側なので「右」に向かって伸ばす * [2]プラグインパラメータ → 『ライン-終点』 *  『終点-方向(味方)』を「左」にする *  ラインが味方グラの左側に向かって伸びるようになる * [3]以上。バトルを「サイドビュー」にしてテストしてみる * * * ■【設定1】行動予測を手動で発動するようにする * [1]プラグインパラメータ → 『常に行動予測』 を「false」にする *  行動予測が自動で発動しないようになる * [2]データベースのメモ欄orスイッチを使って行動予測をオンにする。詳細は後述 * * * ■【設定2】行動ビュー/ターゲットラインを無効化 *  デフォルトでは行動とターゲットの両方が表示されるが、 *  どちらか片方だけにしたい場合に * ◎行動表示を無効化 *  プラグインパラメータ → 『行動表示』 を「false」 * ◎ターゲット表示を無効化 *  プラグインパラメータ → 『ターゲット表示』 を「false」 * * * ■【設定3】行動ビューの見た目をカスタマイズ *  プラグインパラメータ → 『行動ビュー』 * ◎色々と設定項目が並んでいるが、好きにいじってよい * * * ■【設定4】ターゲットラインの見た目をカスタマイズ *  プラグインパラメータ → 『ターゲットライン』 * ◎色々と設定項目が並んでいるが、好きにいじってよい * * * ■【機能1】データベースのメモ欄で行動予測の設定変更 *  行動予測の設定を動的に変更できる *  ・敵毎に行動ビューの位置をずらす *  ・行動ビューに表示される名前を指定 *  ・行動予測を動的にオンオフ *  といった機能がある * ◎アクター、職業、スキル、アイテム、武器、防具、敵キャラ、ステートのメモ欄に *  <行動予測: x: y: absY: dire: name: icon: redoAct redoTarget on off> * ★例) * <行動予測: x:100> *  行動ビューの位置を右に 100 ピクセル ずらす。-100 なら左 * <行動予測: y:100> *  行動ビューの位置を下に 100 ピクセル ずらす。-100 なら上 * <行動予測: absY:100> *  行動ビューのY座標を 100 に固定する(Y座標絶対モード時のみ有効) * <行動予測: dire:上> *  行動ビューの表示方向を 上 にする *  方向は「中央」「上」「下」「左」「右」を入れられる * <行動予測: text:謎の攻撃> *  行動ビューに表示されるテキストを『謎の攻撃』にする。制御文字も使用可能 *  \n で改行。指定がなければスキル名が表示される * <行動予測: icon:11> *  行動ビューに表示されるアイコンを ID 11 のアイコンにする *  指定がなければスキルのアイコンが表示される * <行動予測: redoAct> *  敵キャラの行動を再決定する。ターゲットも同時に再決定される * <行動予測: redoTarget> *  敵キャラのターゲットを再決定する。行動はそのまま * <行動予測: on> *  行動予測を オン(有効) にする * <行動予測: off> *  行動予測を オフ(無効) にする *  オンとオフが両方ある場合はオフが優先される * <行動予測: showLine> *  ターゲットラインを表示する * <行動予測: hideLine> *  ターゲットラインを表示しない * <行動予測: x:100 y:100 dire:上 icon:11 name:謎の攻撃 on> *  とまとめて設定することも可能 * * * ■【機能2】スイッチで行動予測を有効/無効 * [1]プラグインパラメータ → *  『行動予測"有効"スイッチ』/『行動予測"無効"スイッチ』 に、 *  それぞれ好きなスイッチを割り当てる * [2]有効スイッチをオンにすると行動予測有効、 *  無効スイッチをオンにすると無効になる *  両方ともオンの場合は無効が優先され無効 * * * ● 利用規約 ● * MITライセンスのもと、自由に使ってくれて大丈夫です * * * * A system that displays the next action and target of the enemy character * Supports both turn-based and time-progress * * ● How to use ● * * ■【First thing to do】 Adjust the target line * Adjust the position and direction of the target line * according to the battle layout * ◎ The target line extends from the actor toward the target, * A line that indicates who is the target * ◎ Adjustments are made with plug-in parameters * ◎ As an example, try adjusting for the side view screen * with enemies on the left and allies on the right. * I want you to try it as it is * [1] Plug-in parameter → Set "ライン-進行方向" to "right" * Lines extend from enemies to allies * And since the enemy is on the left and the ally is on the right, * stretch toward "right" * [2] Plugin parameter → "ライン-進行方向" * Set "終点-方向(味方)" to "left" *  The line will extend toward the left side of the allied graphic * [3] and above. Test the battle in "side view" * * * ■ [Setting 1] Manually activate action prediction * [1] Plugin parameter → Set "常に行動予測r" to "false" *  Action prediction will not be activated automatically * [2] Turn on action prediction using the memo field in the database * or switch. Details later * * * ■ [Setting 2] Disable action view/target line * By default, both actions and targets are displayed, * If you want to use only one of them * ◎ Disable action display * Plugin parameter → "行動表示" to "false" * ◎ Disable target display * Plugin parameter → "ターゲット表示" to "false" * * * ■ [Setting 3] Customize the action view appearance * Plugin parameter → "行動ビュー" * ◎ Various setting items are lined up, but you can tweak them as you like. * * * ■ [Setting 4] Customize the appearance of the target line * Plug-in parameter → "ターゲットライン" * ◎ Various setting items are lined up, but you can tweak them as you like. * * * ■ [Function 1] Change action prediction settings * in the memo field of the database * You can dynamically change action prediction settings * ・Shift the position of the action view for each enemy * ・Specify the name displayed in the action view * ・ Dynamically turn on/off action prediction * There are functions such as * ◎ Actor, class, skill, item, weapon, armor, enemy character, * state memo column * * ★example) * * Shift the position of the action view by 100 pixels to the right. * -100 is left * * Shift the position of the action view downward by 100 pixels. -100 is up * * Set the display direction of the action view to up * Direction can be "Center", "Top", "Bottom", "Left", and "Right" * * Change the text displayed in the action view to "Mysterious Attack". * Control characters are allowed * Newline with \n. If not specified, the skill name will be displayed * * Set the icon displayed in the action view to the icon with ID 11 * If not specified, the skill icon will be displayed * * Redetermine the actions of the enemy characters. * The target is also re-determined at the same time * * Redetermine the enemy character's target. action as it is * * Action prediction he turned on (enabled) * * He turns off (disables) action prediction * If there are both on and off, off takes precedence * * display target line * * Do not display target line * * It is also possible to set together with * * * ■ [Function 2] Activate/deactivate action prediction with a switch * [1] Plugin parameters → * In the "行動予測"有効"スイッチ" / "行動予測"無効"スイッチ", * Allocate your favorite switch to each * [2] Action prediction is enabled when the enable switch is turned on, * Disabled when the disabled switch is turned on * If both are on, disable takes precedence over disable * * * ● Terms of Use ● * Feel free to use it under the MIT license. * * * * @param 常に行動予測 * @desc predictionAlways 常に有効予測を有効にする。有効にしない場合はデータベースのメモ欄で適宜有効にする * @type boolean * @default true * * @param 行動予測"有効"スイッチ * @desc prediction"valid"switch 行動予測を有効にするスイッチ * @type switch * @default * * @param 行動予測"無効"スイッチ * @desc prediction"unValid"switch 行動予測を無効にするスイッチ * @type switch * @default * * @param 行動ビュー * * @param 行動-表示 * @parent 行動ビュー * @desc act-show 味方キャラの行動ビューを表示する(場合によっては味方キャラも) * @type boolean * @default true * * @param 行動-入力中のみ表示 * @parent 行動ビュー * @desc act-showOnlyInput 行動入力中のみ行動ビューを表示する * @type boolean * @default true * * @param 行動-詠唱中の味方も表示 * @parent 行動ビュー * @desc act-showCastActor 詠唱中の味方キャラも行動ビューを表示する * @type boolean * @default false * * @param 行動-自動中の味方も表示 * @parent 行動ビュー * @desc act-showAutoActor 自動戦闘中の味方キャラも行動ビューを表示する * @type boolean * @default false * * @param 行動-フォント * @parent 行動ビュー * @desc act-font 行動ビューの使用フォント。Keke_CommonDataでフォント登録しそのフォント名を書く。空欄ならメインフォント * @default * * @param 行動-文字サイズ * @parent 行動ビュー * @desc act-fontSize 行動ビューの文字サイズ。基本 24 * @default 24 * * @param 行動-文字色 * @parent 行動ビュー * @desc act-fontColor 行動ビューの文字色。赤、緑、青、濃度。色0~255、濃度0~1。基本 240, 240, 0 * @default 240, 240, 0 * * @param 行動-文字色(詠唱中) * @parent 行動ビュー * @desc act-fonColorCast 詠唱中の行動ビューの文字色。赤、緑、青、濃度。色0~255、濃度0~1。基本 255, 192, 255 * @default 255, 192, 255 * * @param 行動-縁取り幅 * @parent 行動ビュー * @desc act-outWidth 行動ビューの文字縁取り幅。5 なら 5ピクセル。基本 7 * @default 7 * * @param 行動-横幅 * @parent 行動ビュー * @desc act-width 行動ビューの横幅。100 なら 100ピクセル。基本 200 * @default 200 * * @param 行動-背景色 * @parent 行動ビュー * @desc act-backColor 行動ビューの背景の色。(赤, 緑, 青, 濃度)。色0~255、濃度0~1。基本 0, 0, 0, 0.25 * @default 0, 0, 0, 0.25 * * @param 行動-アイコン表示 * @parent 行動ビュー * @desc act-showicon 行動のアイコンを表示する。基本 true * @type boolean * @default true * * @param …アイコン間隔 * @parent 行動ビュー * @desc act-space 行動テキストとアイコンの間隔。5 なら 5ピクセル。基本 0 * @default 0 * * @param …アイコンサイズ * @parent 行動ビュー * @desc act-iconSize アイコンの大きさ。150 なら 150%、50 なら 50% の大きさになる。基本 100 * @default 100 * * @param 行動-行間 * @parent 行動ビュー * @desc act-lineSpace 行動を複数行にわたって表示する時の行間。5 なら 5ピクセル。基本 2 * @default 2 * * @param 行動-表示位置/味方 * @parent 行動ビュー * @desc act-showPos/actor 味方の行動ビューの表示位置 * @type struct * @default {"表示方向":"上","ずらしX":"","ずらしY":""} * * @param 行動-表示位置/敵 * @parent 行動ビュー * @desc act-showPos/enemy 敵の行動ビューの表示位置 * @type struct * @default {"表示方向":"上","ずらしX":"","ずらしY":""} * * @param 行動-Y座標絶対モード * @parent 行動ビュー * @desc act-absoluteYMode 行動ビューのY座標を絶対座標にする。バトラーのY座標に影響されなくなる * @type boolean * @default false * * @param 行動-Y座標(絶対) * @parent 行動ビュー * @desc act-absoluteY Y座標絶対モード時のY座標。空欄なら 0 * @type number * @default 0 * * @param 行動-非表示範囲 * @parent 行動ビュー * @desc act-noShowScope 行動ビューを表示しない範囲。下50 なら、画面下端から 50ピクセル の範囲には表示しない * @type struct * @default {"上":"0","下":"0","左":"0","右":"0"} * * @param 行動-表示レイヤー * @parent 行動ビュー * @desc act-showLayer 行動ビューの表示レイヤー。基本 ウインドウより下 * @type select * @option ウインドウより下 * @option ウインドウより上 * @default ウインドウより下 * * @param 行動-不発エフェクト * @parent 行動ビュー * @desc act-misfireEffect 行動がMP不足等で不発に終わった時のエフェクト * @type struct * @default {"アニメーション":"64","効果音":"[\"{\\\"ファイル\\\":\\\"Monster4\\\",\\\"音量\\\":\\\"100\\\",\\\"ピッチ\\\":\\\"70\\\",\\\"位相\\\":\\\"0\\\"}\"]","画面フラッシュ":"true","…フラッシュ色":"255, 255, 255, 170","…フラッシュ遅延":"","…フラッシュ時間":"30","ヒットストップ":"0, 0","フリーアニメ":""} * * @param ターゲットライン * * @param ライン-表示 * @parent ターゲットライン * @desc line-show 敵キャラのターゲットラインを表示する * @type boolean * @default true * * @param ライン-入力中のみ表示 * @parent ターゲットライン * @desc line-showOnlyInput 行動入力中のみターゲットラインを表示する * @type boolean * @default true * * @param ライン-進行方向 * @parent ターゲットライン * @desc line-vector ターゲットラインの向かう方向 * @type select * @option 上 * @option 下 * @option 左 * @option 右 * @default 下 * * @param ライン-始点 * @parent ターゲットライン * @desc line-startPoint ライン始点の位置設定。始点は敵キャラにのみ発生(ターゲットを表示するのは敵のみであるため) * @type struct * @default {"始点-方向":"右","始点-ずらしX":"","始点-ずらしY":""} * * @param ライン-終点 * @parent ターゲットライン * @desc line-endPoint ライン終点の位置設定。終点は味方と敵キャラ両方ある(回復行動など、敵が敵をターゲットすることがあるため) * @type struct * @default {"味方へのライン":"","終点-方向(味方)":"上","終点-ずらしX(味方)":"","終点-ずらしY(味方)":"","敵同士のライン":"","終点-方向(敵)":"中央","終点-ずらしX(敵)":"","終点-ずらしY(敵)":""} * * @param ライン-太さ * @parent ターゲットライン * @desc line-width ターゲットラインの太さ。5 なら 5ピクセル。基本 4 * @default 4 * * @param ライン-縁取り幅 * @parent ターゲットライン * @desc line-outWidth ターゲットラインの縁取りの幅。1 なら 1ピクセル。基本 1.5 * @default 1.5 * * @param ライン-色リスト * @parent ターゲットライン * @desc line-colorList ターゲットラインの色。赤, 緑, 青。各0~255。1行目は一番目の敵、2行目は二番目の敵といった感じで適用 * @type string[] * @default ["255, 255, 0","255, 0, 255","0, 255, 255","255, 128, 0","128, 255, 0","0, 128, 255","255, 255, 255","255, 0, 0"] * * @param ライン-始点大きさ * @parent ターゲットライン * @desc line-startPointSize ターゲットラインの始点の円部分の大きさ。5 なら 5ピクセル。基本 3 * @default 3 * * @param ライン-矢印大きさ * @parent ターゲットライン * @desc line-arrowSize ターゲットラインの矢印部分の大きさ。5 なら 5ピクセル。基本 4 * @default 4 * * @param ライン-重なりずらし * @parent ターゲットライン * @desc line-pileOffset ターゲットラインが重なった時にずらす幅。5 なら 5ピクセル。基本 12 * @default 12 * * @param ライン-最低長さ * @parent ターゲットライン * @desc line-minLength ターゲットラインが短すぎる場合の最低長さ。この長さは下回らない。50 なら 50ピクセル。基本 50 * @default 50 * * @param ライン-全体時はワード * @parent ターゲットライン * @desc line-wordOnAll 全体対象の時はラインではなく指定の文字列を表示する * @type boolean * @default true * * @param …全体ワード設定 * @parent ターゲットライン * @desc line-allWordCfg 全体対象の時に表示する文字列の設定 * @type struct * @default {"ワード":"ALL","フォント":"","文字サイズ":"22","文字色":"96, 255, 255","縁取り幅":"7","ずらしX":"","ずらしY":""} */ //================================================== /*~struct~viewPos: //================================================== * @param 表示方向 * @desc showDire 行動ビューの表示方向。基本 上 * @type select * @option 中央 * @option 上 * @option 下 * @option 左 * @option 右 * @default 上 * * @param ずらしX * @desc offsetX 行動ビューのX位置ずらし。5 なら 5ピクセル 右へ。基本 0 * @default * * @param ずらしY * @desc offsetY 行動ビューのY位置ずらし。5 なら 5ピクセル 下へ。基本 0 * @default */ //================================================== /*~struct~noShowScope: //================================================== * @param 上 * @desc up 上側の非表示範囲。50 なら 画面上端から 50ピクセル の範囲には表示しない。基本 0 * * @param 下 * @desc down 下側の非表示範囲。50 なら 画面下端から 50ピクセル の範囲には表示しない。基本 0 * * @param 左 * @desc left 左側の非表示範囲。50 なら 画面左端から 50ピクセル の範囲には表示しない。基本 0 * * @param 右 * @desc right 右側の非表示範囲。50 なら 画面右端から 50ピクセル の範囲には表示しない。基本 0 */ //================================================== /*~struct~allWordCfg: //================================================== * @param ワード * @desc word 全体ワードとして表示する文字列 * @default ALL * * @param フォント * @parent 行動ビュー * @desc font 全体ワードの使用フォント。Keke_CommonDataでフォント登録したフォント名を書く。空欄ならメインフォント * @default * * @param 文字サイズ * @desc fontSize 全体ワードの文字サイズ。基本 22 * @default 22 * * @param 文字色 * @desc fontColor 全体ワードの文字色。赤、緑、青、濃度。各0〜255。基本 96, 255, 255 * @default 96, 255, 255 * * @param 縁取り幅 * @desc outWidth 全体ワードの文字縁取り幅。5 なら 5ピクセル。基本 7 * @default 7 * * @param ずらしX * @desc offsetX 全体ワードのX位置ずらし。5 なら 5ピクセル 右へ。基本 0 * @default * * @param ずらしY * @desc offsetY 全体ワードのY位置ずらし。5 なら 5ピクセル 下へ。基本 0 * @default */ //================================================== /*~struct~lineStart: //================================================== * @param 始点-方向 * @desc startPoint-dire ライン始点の位置方向。基本 右 * @type select * @option 中央 * @option 上 * @option 下 * @option 左 * @option 右 * @default 右 * * @param 始点-ずらしX * @desc startPoint-offsetX ライン始点のXずらし。5 なら 5ピクセル 右へ。基本 0 * @default * * @param 始点-ずらしY * @desc startPoint-offsetY ライン始点のYずらし。5 なら 5ピクセル 下へ。基本 0 * @default */ //================================================== /*~struct~lineEnd: //================================================== * @param 味方へのライン * * @param 終点-方向(味方) * @parent 味方へのライン * @desc endPoint-dire(actor) 味方のライン終点の位置方向。基本 上 * @type select * @option 中央 * @option 上 * @option 下 * @option 左 * @option 右 * @default 上 * * @param 終点-ずらしX(味方) * @parent 味方へのライン * @desc endPoint-offsetX(actor) 味方のライン終点のXずらし。5 なら 5ピクセル 右へ。基本 0 * @default * * @param 終点-ずらしY(味方) * @parent 味方へのライン * @desc endPoint-offsetY(actor) 味方のライン終点のYずらし。5 なら 5ピクセル 下へ。基本 0 * @default * * @param 敵同士のライン * * @param 終点-方向(敵) * @parent 敵同士のライン * @desc endPoint-dire(enemy) 敵のライン終点の位置方向。基本 中央 * @type select * @option 中央 * @option 上 * @option 下 * @option 左 * @option 右 * @default 中央 * * @param 終点-ずらしX(敵) * @parent 敵同士のライン * @desc endPoint-offsetX(enemy) 敵のライン終点のXずらし。5 なら 5ピクセル 右へ。基本 0 * @default * * @param 終点-ずらしY(敵) * @parent 敵同士のライン * @desc endPoint-offsetY(enemy) 敵のライン終点のYずらし。5 なら 5ピクセル 下へ。基本 0 * @default */ //================================================== /*~struct~effect: //================================================== * * @param アニメーション * @desc animation 表示するアニメーション * @type animation * * @param 効果音 * @desc SE 鳴らす効果音 * @type struct[] * @default [] * * @param 画面フラッシュ * @desc screenFlash 画面フラッシュを実行する * @type boolean * @default false * * @param …フラッシュ色 * @desc flashColor 画面フラッシュの色。赤, 緑, 青, 強さ。各0~255。基本 255, 255, 255, 170 * @default 255, 255, 255, 170 * * @param …フラッシュ遅延 * @desc flashDelay フラッシュのディレイ。5 なら 5フレーム 待ってからフラッシュ。基本 0 * @default * * @param …フラッシュ時間 * @desc flashTime フラッシュの所要時間。5 なら 5フレーム。基本 30 * @default 30 * * @param ヒットストップ * @desc hitStope 画面を止める演出。5, 7 なら 5フレーム 待ってから 7フレーム 止める。Keke_HitStopが必要。基本 0, 0 * @default 0, 0 * * @param フリーアニメ * @desc freeAnime フリーアニメを再生する。メモ欄同様に記述。Keke_FreeAnimeが必要 * @type multiline_string */ //================================================== /*~struct~se: //================================================== * @param ファイル * @desc file 効果音ファイル * @type file * @dir audio/se * * @param 音量 * @desc volime 効果音の音量 * @default 100 * * @param ピッチ * @desc pitch 効果音のピッチ * @default 100 * * @param 位相 * @desc pan 効果音の位相 * @default 0 */ (() => { //- プラグイン名 const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1]; //================================================== //-- 公開メソッド //================================================== //- 行動予測の開始(公開) Game_Temp.prototype.startPredictionKe = function(subject, force) { // 行動予測の開始 startPrediction(subject, force); }; //================================================== //-- 他プラグインとの連携メソッド //================================================== //- フルアニメステータスASIの取得 function getFullAnimeStatusAsi(battler) { // サイドビュー時はバトグラに表示 if ($gameSystem.isSideView()) { return null; } if (!$gameTemp._fullAnimeStatusKe) { return null; } const asi = $gameTemp.getFullAnimeStatusAsiKe(battler); // 顔アイコンがなければバトグラに表示 if (!asi || !asi.faceBaseSprite || !asi.faceBaseSprite.visible) { return null; } return asi; }; //- スピードスターバトルか function isSpeedStar() { const gs = $gameSystem; return gs._speedStarInitedKe && !gs._noSpeedStarKe; }; //- フリーアニメプラグインがあるか function isFreeAnime() { return PluginManager._scripts.some(n => n == "Keke_FreeAnime"); }; //- アイコンバックを描画するか - メニュー快適化 function isIconBackDraw() { if (!$gameTemp.isIconBackDrawKe) { return false; }; return $gameTemp.isIconBackDrawKe.apply(this, arguments); }; //- アイコンバックの描画 - メニュー快適化 function drawIconBack(bitmap, iconIndex, x, y) { if (!$gameTemp.drawIconBackKe) { return; }; $gameTemp.drawIconBackKe.apply(this, arguments); }; //================================================== //-- 追加スプライト /ベーシック //================================================== //- 破棄付きスプライト function SpriteKePrds() { this.initialize(...arguments); } SpriteKePrds.prototype = Object.create(Sprite.prototype); SpriteKePrds.prototype.constructor = SpriteKePrds; SpriteKePrds.prototype.destroy = function() { if (this.bitmap && !this.bitmap._url) { this.bitmap.destroy(); } if (this._texture) { Sprite.prototype.destroy.apply(this); } }; //================================================== //-- 文字列オート変換 /ベーシック //================================================== //- 文字列のハッシュ化 function strToHash(str) { if (!str || !str.length) { return {}; } let hash = {}; const strs = JSON.parse(str); let val = null; let val2 = null; for (const key in strs) { val = strs[key]; if (!key || !val) { continue; } val2 = strToAuto(val, key); hash[key] = val2; } return hash; }; //- 文字列のリスト化 function strToList(str) { if (!str || !str.length) { return []; } let array = JSON.parse(str); return array.map((val, i) => { return strToAuto(val); }); }; //- 文字列の自動処理 function strToAuto(val, key = "") { let val2 = null; let match = null; let end = false; if (!end) { if (val[0] == "{") { val2 = strToHash(val); end = true; } } if (!end) { if (val[0] == "[") { val2 = strToList(val); end = true; } } if (!end) { val = val + ","; } if (!end) { match = val.match(/^\s*(-?\d+,\s*-?\d+,\s*-?\d+,?\s*-?\d*\.?\d*)\s*,$/); if (match && !val.match(/[^\d\.\-,\s]/)) { if (key.match(/(カラー|色|塗り)/) && !key.includes("トーン") && !key.includes("ブレンド") && !key.includes("配色") && !key.includes("着色") && !key.includes("フラッシュ") && !key.includes("チェンジ") && !key.includes("選択")) { val2 = "rgba(" + match[1] + ")"; } else { val2 = JSON.parse("[" + match[1] + "]"); } end = true; } } if (!end) { match = val.match(/(-?\d+\.?\d*),\s*/g); if (match && match.length >= 2 && !val.match(/[^\d\.\-,\s]/)) { val2 = JSON.parse("[" + match.reduce((r, s) => r + s).replace(/,$/, "") + "]"); end = true; } } if (!end) { match = val.match(/^(true|false)\s*,/); if (match) { val2 = match[1] == "true" ? true : false; end = true; } } if (!end) { match = val.match(/^(-?\d+\.?\d*)\s*,/); if (match && !val.match(/[a-z]/)) { val2 = Number(match[1]); end = true; end = true; } } if (!end) { if (val[0] == "\"") { val = val.slice(1); } val2 = val.slice(0, -1); } return val2; }; //================================================== //-- パラメータ受け取り //================================================== //- 真偽化 function toBoolean(str) { if (!str) { return false; } const str2 = str.toString().toLowerCase(); if (str2 == "true" || str2 == "on") { return true; } if (str2 == "false" || str2 == "off") { return false; } return Number(str); }; let parameters = PluginManager.parameters(pluginName); const keke_predictionAlways = toBoolean(parameters["常に行動予測"]); const keke_predictionOnSwitch = Number(parameters["行動予測\"有効\"スイッチ"]); const keke_predictionOffSwitch = Number(parameters["行動予測\"無効\"スイッチ"]); //- 行動ビュー const keke_actShow = toBoolean(parameters["行動-表示"]); const keke_actOnlyInput = toBoolean(parameters["行動-入力中のみ表示"]); const keke_actShowCastActor = toBoolean(parameters["行動-詠唱中の味方も表示"]); const keke_actShowAutoActor = toBoolean(parameters["行動-自動中の味方も表示"]); const keke_actFontFace = parameters["行動-フォント"]; const keke_actFontSize = Number(parameters["行動-文字サイズ"]); const keke_actFontColor = "rgba(" + parameters["行動-文字色"] + ")"; const keke_actFontColorCast = "rgba(" + parameters["行動-文字色(詠唱中)"] + ")"; const keke_actOutW = Number(parameters["行動-縁取り幅"]); const keke_actW = Number(parameters["行動-横幅"]); const keke_actBackColor = parameters["行動-背景色"] ? `rgba(${parameters["行動-背景色"]})` : ""; const keke_actShowIcon = toBoolean(parameters["行動-アイコン表示"]); const keke_actIconSpace = Number(parameters["…アイコン間隔"]); const keke_actIconSize = Number(parameters["…アイコンサイズ"]) || 100; const keke_actLineSpace = Number(parameters["行動-行間"]); const keke_actPosActor = strToHash(parameters["行動-表示位置/味方"]); const keke_actPosEnemy = strToHash(parameters["行動-表示位置/敵"]); const keke_actAbsYMode = toBoolean(parameters["行動-Y座標絶対モード"]); const keke_actAbsY = Number(parameters["行動-Y座標(絶対)"]); const keke_actNoShowScope = strToHash(parameters["行動-非表示範囲"]); const keke_actLayer = parameters["行動-表示レイヤー"]; const keke_actMisfireEffect = strToHash(parameters["行動-不発エフェクト"]); //- ターゲットライン const keke_lineShow = toBoolean(parameters["ライン-表示"]); const keke_lineOnlyInput = toBoolean(parameters["ライン-入力中のみ表示"]); const keke_lineDire = parameters["ライン-進行方向"]; const keke_lineStart = strToHash(parameters["ライン-始点"]); const keke_lineEnd = strToHash(parameters["ライン-終点"]); const keke_lineW = Number(parameters["ライン-太さ"]); const keke_lineOutW = Number(parameters["ライン-縁取り幅"]); const keke_lineColorList = makeColorList(parameters["ライン-色リスト"]); const keke_linePileOffset = Number(parameters["ライン-重なりずらし"]); const keke_lineStartSize = Number(parameters["ライン-始点大きさ"]); const keke_lineArrowSize = Number(parameters["ライン-矢印大きさ"]); const keke_lineMinLength = Number(parameters["ライン-最低長さ"]); const keke_lineWordIfAll = toBoolean(parameters["ライン-全体時はワード"]); const keke_allWordCfg = strToHash(parameters["…全体ワード設定"]); parameters = null; //- カラーリストの作成 function makeColorList(str) { const list = strToList(str); return list.map(s => `rgba(${s})`); }; //================================================== //-- 共通開始 //================================================== //- バトルマネージャー/スタートアクション(処理追加) const _BattleManager_startAction = BattleManager.startAction; BattleManager.startAction = function() { _BattleManager_startAction.apply(this); // 再決定の処理 processRedo(this._subject, this._targets, this._action); // 行動予測中ステートのクリア clearInPredictionState(this._subject); }; //=============================================== //-- 共通終了 //================================================== //- バトルマネージャー/バトル終了(処理追加) const _BattleManager_endBattle = BattleManager.endBattle; BattleManager.endBattle = function(result) { _BattleManager_endBattle.apply(this, arguments); // ターゲット先行作成の消去-全員 delTargetFastAll(); // 受容ラインの初期化 initReceptLine(); // 行動予測中ステートのクリア-全員 clearInPredictionStateAll(); }; //- スプライトセット・バトル/破棄(処理追加) const _Spriteset_Battle_destroy = Spriteset_Battle.prototype.destroy Spriteset_Battle.prototype.destroy = function(options) { // スプライトの全破棄 destroySpriteAll(this); _Spriteset_Battle_destroy.apply(this, arguments); }; //- スプライトの全破棄 function destroySpriteAll(spriteset) { const layers = spriteset._predictionLayersKe; if (!layers) { return; } // 全てのレイヤーに対して処理 Object.keys(layers).forEach(key => { // スプライトの破棄 destroySprite(layers[key]); }); spriteset._predictionLayersKe = null; }; //- スプライトの破棄 function destroySprite(sprite) { if (!sprite) { return; } sprite.children.forEach(s => destroySprite(s)); if (sprite.bitmap && !sprite.bitmap._url) { sprite.bitmap.destroy(); } if (sprite._texture) { sprite.destroy(); } }; //================================================== //-- レイヤー //================================================== //- シーンバトル/ウインドウレイヤーの形成(処理追加) const _Scene_Battle_createWindowLayer = Scene_Battle.prototype.createWindowLayer; Scene_Battle.prototype.createWindowLayer = function() { _Scene_Battle_createWindowLayer.apply(this); // 行動予測レイヤーの形成 createPredictionLayer(this); }; //- 行動予測レイヤーの形成 function createPredictionLayer(scene) { const spriteset = scene._spriteset; const layers = {}; // Aレイヤー const layerA = new SpriteKePrds(); spriteset.addChild(layerA); layers["A"] = layerA; // Bレイヤー const layerB = new SpriteKePrds(); spriteset.addChild(layerB); layers["B"] = layerB; // Cレイヤー const layerC = new SpriteKePrds(); scene.addChild(layerC); layers["C"] = layerC; // Dレイヤー const layerD = new SpriteKePrds(); scene.addChild(layerD); layers["D"] = layerD; // 変数にセット spriteset._predictionLayersKe = layers; }; //- レイヤーチルド function childLayer(sprite, id) { SceneManager._scene._spriteset._predictionLayersKe[id].addChild(sprite); }; //================================================== //-- メモ欄からの取得 //================================================== //- 行動予測設定の取得 function getPredictionCfg(battler, tage, calc = "*", action = null, radioIndex) { const metas = totalAllMetaArray(battler, ["行動予測", "prediction"], action, true, radioIndex); if (!metas || !metas.length) { return null; } for (const str of metas.reverse()) { if (!str) { continue; } // ずらしX if (tage == "ずらしX") { const match = str.match(/(?:^|[,\s])(x|offsetX|posX|ずらしX|位置X):\s*(-?\d+\.?\d*)/i); if (match && match[2]) { return Number(match[2]); } continue; } // ずらしY if (tage == "ずらしY") { const match = str.match(/(?:^|[,\s])(y|offsetY|posY|ずらしY|位置Y):\s*(-?\d+\.?\d*)/i); if (match && match[2]) { return Number(match[2]); } continue; } // 絶対Y if (tage == "絶対Y") { const match = str.match(/(?:^|[,\s])(absY|absoluteY|絶対Y):\s*(-?\d+\.?\d*)/i); if (match && match[2]) { return Number(match[2]); } continue; } // 表示方向 if (tage == "表示方向") { const match = str.match(/(?:^|[,\s])(dire|direction|showDire|表示方向|方向):\s*(中央|左|右|上|下|center|left|right|up|down|c|l|r|u|d)/i); if (match && match[2]) { return convertDirection(match[2]); } continue; } // テキスト if (tage == "テキスト") { const match = str.match(/(?:^|[,\s])(text|name|テキスト|文章|名前):\s*([^\s,]+)/i); if (match && match[2]) { return match[2]; } continue; } // アイコン if (tage == "アイコン") { const match = str.match(/(?:^|[,\s])(icon|showIcon|アイコン|表示アイコン):\s*(\d*),*\s*(-?\d*\.?\d*)/i); if (match && (match[2] || match[3])) { return { index:Number(match[2]), size:Number(match[3]) }; } continue; } // 行動再決定 if (tage == "行動再決定") { const match = str.match(/(?:^|[,\s])(actRedo|redoAct|行動再決定|再行動)/i); if (match) { return true } continue; } // ターゲット再決定 if (tage == "ターゲット再決定") { const match = str.match(/(?:^|[,\s])(targetRedo|redoAct|ターゲット再決定|再ターゲット)/i); if (match) { return true } continue; } // ラインあり if (tage == "ラインあり") { const match = str.match(/(?:^|[,\s])(showLine|showTarget|ラインあり|ターゲットあり|ライン表示|ターゲット表示)/i); if (match) { return true } continue; } // ラインなし if (tage == "ラインなし") { const match = str.match(/(?:^|[,\s])(hideLine|hideTarget|ラインなし|ターゲットなし|ライン非表示|ターゲット非表示)/i); if (match) { return true } continue; } // 無効 if (tage == "無効") { const match = str.match(/(?:^|[,\s])(no|off|none|無効|オフ)/i); if (match) { return true } continue; } // 有効 if (tage == "有効") { const match = str.match(/(?:^|[,\s])(ok|on|valid|有効|オン)/i); if (match) { return true } continue; } } return null; }; //- 方向の変換 function convertDirection(d) { if (d.match(/center|c/i)) { return "中央"; } else if (d.match(/left|l/i)) { return "左"; } else if (d.match(/right|r/i)) { return "右"; } else if (d.match(/up|u/i)) { return "上"; } else if (d.match(/down|d/i)) { return "下"; } return d; }; //================================================== //-- 行動予測システム //================================================== // 再決定予約 let redoReserve = null; //- ゲームバトラー/TPBの初期化(処理追加) const _Game_Battler_initTpbChargeTime = Game_Battler.prototype.initTpbChargeTime; Game_Battler.prototype.initTpbChargeTime = function(advantageous) { _Game_Battler_initTpbChargeTime.apply(this, arguments); // 行動予測の開始(TPB) if (BattleManager.isTpb()) { setTimeout(startPrediction, 0, this); } }; //- バトルマネージャー/アクション終了時の処理(処理追加) const _BattleManager_endBattlerActions = BattleManager.endBattlerActions; BattleManager.endBattlerActions = function(subject) { _BattleManager_endBattlerActions.apply(this, arguments); // 行動予測の開始 startPrediction(subject); }; //- ゲームバトラー/ターン終了時の処理(処理追加) const _Game_Battler_onTurnEnd = Game_Battler.prototype.onTurnEnd; Game_Battler.prototype.onTurnEnd = function() { _Game_Battler_onTurnEnd.apply(this); // 行動予測の開始 startPrediction(this); }; //- ゲームバトラーベース/出現時の処理(処理追加) const _Game_BattlerBase_appear = Game_BattlerBase.prototype.appear; Game_BattlerBase.prototype.appear = function() { _Game_BattlerBase_appear.apply(this); // 行動予測の開始 startPrediction(this); }; //- バトルマネージャー/入力の開始(処理追加) const _BattleManager_startInput = BattleManager.startInput; BattleManager.startInput = function() { _BattleManager_startInput.apply(this); // 行動予測の開始-全員(ターン) if (!BattleManager.isTpb()) { startPredictionAll(); } }; // 行動予測の開始 function startPrediction(subject, force) { // 手動操作のアクターは行動予測しない if (subject._actorId && !subject.isAutoBattle()) { return; } // 先制攻撃時の敵は行動予測しない if (BattleManager._preemptive && subject._enemyId) { return; } // 行動の先行作成 force = makeActionFast(subject, force); // ターゲットの先行作成 makeTargetFast(subject, force); }; // 行動の先行作成 function makeActionFast(subject, force) { if (!subject || subject._actorId) { return; } let maked = false; // 行動作成(TPB時のみ) if (BattleManager.isTpb()) { if (!subject._actions.length || force) { // アクションのクリア subject.clearActions(); const preActionState = subject._actionState; // アクション作成 subject.makeActions(); subject.setActionState(preActionState); // 先行アクションフラグをオン subject._actionMakedFastKe = true; maked = true; } } // 行動ビューのリフレッシュフラグをオン subject._refreshsActViewKe = true; return maked; }; // 行動予測の開始-全員 function startPredictionAll() { // 全てのキャラに対して処理 const members = [...$gameTroop.aliveMembers(), ...$gameParty.aliveMembers()]; members.forEach(subject => { // 行動予測の開始 startPrediction(subject); }); }; //- ゲームバトラー/TPBターンの開始(処理追加) const _Game_Battler_startTpbTurn = Game_Battler.prototype.startTpbTurn; Game_Battler.prototype.startTpbTurn = function() { // TPB行動入力時に先行アクションを適用 // アクション先行作成をしていたら if (this._actionMakedFastKe) { // フラグオフ this._actionMakedFastKe = false; // 使用できないアクションは不発にさせる const preActionNum = this._actions.filter(action => action.item()).length; this._actions = this._actions.filter(action => action.isValid()); // 不発エフェクト if (preActionNum >= 1 && this._actions.length < preActionNum) { misfireEffect(this); } // 行動作成済みフラグをオン this._isActionMakedKe = true; } _Game_Battler_startTpbTurn.apply(this); // 行動作成済みフラグをオフ this._isActionMakedKe = false; // 被キャンセルフラグをオフ this._isCanceledKe = true; }; //- 不発エフェクト function misfireEffect(battler) { // エフェクトの実行 doEffect(keke_actMisfireEffect, battler); // 不発ポップアップ misfirePopup(battler); }; //- 不発ポップアップ function misfirePopup(battler) { if (!PluginManager._scripts.some(n => n == "Keke_StatePopup")) { return; } // 不発ポップの表示 battler.showMisfirePopKeStpp(); }; //- ゲームバトラー/アクション作成(処理追加) const _Game_Battler_makeActions = Game_Battler.prototype.makeActions; Game_Battler.prototype.makeActions = function() { // 行動作成時、行動作成済みなら作成スキップしてそれを採用 if (this._isActionMakedKe) { return; } _Game_Battler_makeActions.apply(this); }; const _Game_Battler_numActions = Game_Battler.prototype.numActions; Game_Battler.prototype.numActions = function() { // 行動作成時、行動作成済みなら作成スキップしてそれを採用 if (this._isActionMakedKe) { return 0; } return _Game_Battler_numActions.apply(this); }; //- ゲームバトラー/行動不能時の処理(処理追加) const _Game_Battler_onRestrict = Game_Battler.prototype.onRestrict; Game_Battler.prototype.onRestrict = function() { _Game_Battler_onRestrict.apply(this); // 行動予約の消去 delPrediction(this); }; //- 行動予約の消去 function delPrediction(subject) { // アクションを消去 subject._actions = []; // アクション先行作成フラグをオフ subject._actionMakedFastKe = false; // 行動作成済みフラグをオフ subject._isActionMakedKe = false; // ターゲット先行作成の消去 delTargetFast(subject); }; //- ゲームバトラー/TPBターンの初期化(処理追加) const _Game_Battler_initTpbTurn = Game_Battler.prototype.initTpbTurn; Game_Battler.prototype.initTpbTurn = function() { // 行動予測時はTPBターン初期値を 1 にする _Game_Battler_initTpbTurn.apply(this); this._tpbTurnCount = 1; }; //- 先行アクション済みのキャラをチャージ完了まで行動させない /*const _BattleManager_getNextSubject = BattleManager.getNextSubject; BattleManager.getNextSubject = function() { const battler = this._actionBattlers[0]; if (battler && battler._actionMakedFastKe) { this._actionBattlers.shift(); return null; } return _BattleManager_getNextSubject.apply(this); };*/ //================================================== //-- 行動の再決定 //================================================== //- 再決定の処理 function processRedo(subject, targets, action) { // ターゲットの重複を削除、敵キャラのみに限定 targets = Array.from(new Set(targets)).filter(target => target && target._enemyId); if (!targets.length) { return; } // 行動再決定 if (getPredictionCfg(subject, "行動再決定", "", action)) { // 行動再決定を予約 redoReserve = { targets:getRedoTarget(targets), type:"act" }; return; } // ターゲット再決定 if (getPredictionCfg(subject, "ターゲット再決定", "", action)) { // ターゲット再決定を予約 redoReserve = { targets:getRedoTarget(targets), type:"target" }; return; } }; //- 再決定ターゲットの取得 function getRedoTarget(targets) { // ターゲットの敵キャラが入っていたらそのまま返す if (targets.length && targets.some(battler => battler._enemyId)) { return targets; } // そうでなければ敵キャラ全てを返す return $gameTroop.aliveMembers(); }; //- バトルマネージャー/ターンの更新(処理追加) const _BattleManager_updateTurn = BattleManager.updateTurn; BattleManager.updateTurn = function(timeActive) { // 再決定の実行 executeRedo(); _BattleManager_updateTurn.apply(this, arguments); }; //- 再決定の実行 function executeRedo() { if (!redoReserve) { return; } const targets = redoReserve.targets; if (!targets || !targets.length) { return; } // 行動再決定の実行 if (redoReserve.type == "act") { targets.forEach(target => { // 行動の先行作成 makeActionFast(target, true); // ターゲットの先行作成 makeTargetFast(target, true); }); // ターゲット再決定の実行 } else { targets.forEach(target => { // ターゲットの先行作成 makeTargetFast(target, true); }); } // 予約を消去 redoReserve = null; }; //================================================== //-- 行動ビューウインドウ //================================================== //- 行動ビューウインドウ function Window_ActViewKe() { this.initialize(...arguments); } Window_ActViewKe.prototype = Object.create(Window_Base.prototype); Window_ActViewKe.prototype.constructor = Window_ActViewKe; //- 初期化 Window_ActViewKe.prototype.initialize = function(rect) { Window_Base.prototype.initialize.call(this, rect); this.frameVisible = false; this._backSprite.visible = false; }; //- 独自背景スプライトの形成 Window_ActViewKe.prototype._createBackSprite = function() { Window_Base.prototype._createBackSprite.call(this); // スプライト形成 & チルド this._backSpriteKe = new SpriteKePrds(); this._container.addChild(this._backSpriteKe); }; //- 背景の描画 Window_ActViewKe.prototype.drawBackKe = function(width, height, color) { // 背景を描画 const bitmap = new Bitmap(this._width, this._height); const padding = this._padding; fillSquare(bitmap, padding, padding, width, height, color, height / 3); // ビットマップを挿入 const backSprite = this._backSpriteKe; if (backSprite.bitmap) { backSprite.bitmap.destroy(); } backSprite.bitmap = bitmap; }; //- フォント基本設定(処理追加) Window_ActViewKe.prototype.resetFontSettings = function() { const contents = this.contents; contents.fontFace = keke_actFontFace || $gameSystem.mainFontFace(); contents.fontSize = keke_actFontSize; this.changeTextColor(isCasting(this._battlerKe) ? keke_actFontColorCast : keke_actFontColor); this.changeOutlineColor(ColorManager.outlineColor()); contents.outlineWidth = keke_actOutW; }; //- テキストの高さの計算(処理追加) Window_ActViewKe.prototype.calcTextHeight = function(textState) { const lastFontSize = this.contents.fontSize; const lines = textState.text.slice(textState.index).split("\n"); const textHeight = this.maxFontSizeInLine(lines[0]) + keke_actLineSpace; this.contents.fontSize = lastFontSize; return textHeight; }; //- テキストステート作成処理(処理追加) Window_ActViewKe.prototype.createTextState = function(text, x, y, width) { const textState = Window_Base.prototype.createTextState.call(this, text, x, y, width); // テキストステートを保存 this._textState = textState; // 各行のフレームを記録する変数を作成 textState.lineFrames = []; // 1行目のフレームを保存 textState.lineFrames.push(this.getLineFrame(textState)); return textState; }; //- 行替えの処理(処理追加) Window_ActViewKe.prototype.processNewLine = function(textState) { Window_Base.prototype.processNewLine.call(this, textState); // 新しい行のフレームを保存 textState.lineFrames.push(this.getLineFrame(textState)); }; //- 行フレームの取得 Window_ActViewKe.prototype.getLineFrame = function(textState) { const x = textState.x; const y = textState.y; const width = 0; const height = textState.height; const heightEx = textState.height - keke_actLineSpace; const text = textState.text.slice(textState.index); // 行頭か判定(改行文字による行でないか) let isTop = false; if (text.match(/^topke/)) { isTop = true; textState.index += 5; } return { x:x, y:y, width:width, height:height, heightEx:heightEx, isTop:isTop }; }; //- テキスト書き出しの処理(処理追加) Window_ActViewKe.prototype.flushTextState = function(textState) { const preOutputHeight = textState.outputHeight; // テキスト横幅を取得 const text = textState.buffer; const textW = this.textWidth(text); Window_Base.prototype.flushTextState.call(this, textState); // 行フレームの横幅を更新 const lineFrames = textState.lineFrames; lineFrames[lineFrames.length - 1].width += textW; // 書き出しがなかったらアウトプットヘイトの更新を無効 if (!text) { textState.outputHeight = preOutputHeight; } }; //================================================== //-- 行動ビュー //================================================== //- ゲームバトラー/詠唱開始時の処理(処理追加) const _Game_Battler_startTpbCasting = Game_Battler.prototype.startTpbCasting; Game_Battler.prototype.startTpbCasting = function() { _Game_Battler_startTpbCasting.apply(this); // 行動ビューのリフレッシュフラグをオン this._refreshsActViewKe = true; }; //- スブライトバトラー/バトラーのセット(処理追加) const _Sprite_Battler_setBattler = Sprite_Battler.prototype.setBattler; Sprite_Battler.prototype.setBattler = function(battler) { _Sprite_Battler_setBattler.apply(this, arguments); // 行動ビューのリフレッシュフラグをオン this._refreshsActViewKe = true; }; //- スプライトバトラー/更新(処理追加) const _Sprite_Battler_update = Sprite_Battler.prototype.update; Sprite_Battler.prototype.update = function() { _Sprite_Battler_update.apply(this); // 行動ビューの更新 updateActView(this, this._battler); }; //- 行動ビューの更新 function updateActView(battlerSprite, battler) { if (!battler) { return; } if (BattleManager._phase == "start" && !$gameTroop.isEventRunning()) { return; } // 行動ビューリフレッシュの更新 updateActViewRefresh(battler); // 行動ビュースプライトを更新 const actWindow = battlerSprite._actWindowKe; if (actWindow) { // 行動ビューの可視更新 updateViewVisibleAct(actWindow, battler); } }; //- 行動ビューリフレッシュの更新 function updateActViewRefresh(subject) { if (!subject._refreshsActViewKe) { return; } const asi = getFullAnimeStatusAsi(subject); const faceSprite = asi ? asi.faceBaseSprite : null; if (faceSprite) { if (asi.startNear) { return; } // 行動ビューのリフレッシュ refreshActView(subject); } else { const subjectSprite = searchSpriteBattler(subject); if (!subjectSprite || !subjectSprite._frame.width) { return;} // 行動ビューのリフレッシュ refreshActView(subject, subjectSprite); } }; //- 行動ビューのリフレッシュ function refreshActView(battler, battlerSprite) { battlerSprite = battlerSprite || searchSpriteBattler(battler); if (!battlerSprite) { return; } const actions = battler._actions.filter(action => action && action.item()); const skillNames = actions.map(action => getSkillName(action)).filter(a => a); // 行動テキストの作成 const text = makeActText(skillNames); // 行動ビューの形成 createActView(battlerSprite, battler, actions.length, text); const actWindow = battlerSprite._actWindowKe; // テキストを描画 const outW = keke_actOutW; actWindow.drawTextEx(text, outW / 2, outW / 2, Graphics.width); // 描画したテキストステートからフレームを取得 const textState = actWindow._textState; const frames = textState.lineFrames; // 行動アイコンの作成 const iconW = makeActIcon(actWindow, battler, actions, frames); // 行動ビューの可視更新 updateViewVisibleAct(actWindow, battler); // メモ欄からの位置設定を取得 actWindow._memoXKe = getPredictionCfg(battler, "ずらしX", "", actions[0]) || 0; actWindow._memoYKe = getPredictionCfg(battler, "ずらしY", "", actions[0]) || 0; actWindow._memoAbsYKe = getPredictionCfg(battler, "絶対Y", "", actions[0]); actWindow._showDireKe = getPredictionCfg(battler, "表示方向", "", actions[0]); // 最大横幅を保存 let widthMax = null; frames.forEach(frame => { // アイコン幅をフレームに保存 frame.iconW = iconW; if (widthMax == null || frame.width > widthMax) { widthMax = frame.width; } }); // 背景の描画 const backW = widthMax ? widthMax + keke_actOutW : 0; const backH = textState.outputHeight ? textState.outputHeight + keke_actOutW : 0; actWindow.drawBackKe(backW, backH, keke_actBackColor); if (widthMax != null) { actWindow._widthKe = widthMax; } // 行動ビューの位置更新 updateViewPosAct(actWindow, battlerSprite, battler); // フレームを保存 battlerSprite._actFramesKe = frames; // 行動予測中ステートの処理 processInPredictionState(battler, actions); // リフレッシュフラグをオフ battler._refreshsActViewKe = null; }; //- スキル名の取得 function getSkillName(action) { const item = action.item(); // スキル名を取得 const skillName = item.name; // メモ欄から表示名を取得 const showName = getPredictionCfg(null, "テキスト", "", action); return showName || skillName; }; //- 行動テキストの作成 function makeActText(skillNames) { let text = ""; skillNames.forEach(name => { name = name.replace(/\\n/g, "\n"); text += "topke" + name +"\n"; }); return text; }; //- 行動ビューの形成 function createActView(battlerSprite, battler, actionNum, text) { const preSprite = battlerSprite._actWindowKe; if (preSprite && preSprite._actionNum == actionNum) { return; } // 前のがあったら破棄 if (preSprite) { destroyActView(preSprite); } // 仮ウインドウを作成 const tempWindow = new Window_ActViewKe({ x:0, y:0, width:Graphics.width, height:Graphics.height }); // テキストのサイズを取得 const outW = keke_actOutW; const sizes = tempWindow.textSizeEx(text, 0, 0, Graphics.width); sizes.width += outW; sizes.height += outW; // ウインドウ形成 const padding = tempWindow._padding; const windowWidth = keke_actW + padding * 2; const windowHeight = sizes.height + padding * 2; const windo = new Window_ActViewKe({ x:0, y:0, width:windowWidth, height:windowHeight }); tempWindow.destroy(); // チルド const layerId = keke_actLayer == "ウインドウより上" ? "D" : "B"; childLayer(windo, layerId); // 原点 const pos = battler._enemyId ? keke_actPosEnemy : keke_actPosActor; // 変数にセット battlerSprite._actWindowKe = windo; // スプライトにサイズを保存 windo._widthKe = sizes.width; windo._heightKe = sizes.height + keke_actLineSpace; // ウインドウを中心に配置するために位置補正 windo._centerXKe = -windo._width / 2 - windo._padding + (windowWidth - sizes.width) / 2; windo._centerYKe = -windo._height + windo._padding // バトラーを保存 windo._battlerKe = battler; // アクション数を保存 windo._actionNumKe = actionNum; // 表示方向を保存 windo._direKe = pos["表示方向"]; }; //- 行動ビューの破棄 function destroyActView(actWindow) { if (!actWindow || actWindow._destroyed) { return; } // Detach and destroy children safely to prevent lifecycle conflicts. while (actWindow.children.length > 0) { const child = actWindow.removeChildAt(0); if (child && !child._destroyed) { child.destroy({ children: true, texture: true, baseTexture: true }); } } // Now that the window is empty, destroy it. actWindow.destroy({ children: true, texture: true, baseTexture: true }); }; //- 行動アイコンの作成 function makeActIcon(actWindow, battler, actions, frames) { if (!keke_actShowIcon) { return 0; } let iconWMax = 0; const labelText = "Next:"; const labelFontFace = keke_actFontFace || $gameSystem.mainFontFace(); const labelFontSize = keke_actFontSize; const labelPadding = 4; const tempBitmap = new Bitmap(1, 1); tempBitmap.fontFace = labelFontFace; tempBitmap.fontSize = labelFontSize; const nextLabelWidth = tempBitmap.measureTextWidth(labelText); tempBitmap.destroy(); const totalLabelWidth = nextLabelWidth + labelPadding; actions.forEach((action, i) => { const iconData = getIconData(action); const frame = getLineFrame(frames, i); const iconSprite = createIconSprite(iconData.index); // Create and configure the "Next:" label sprite const labelBitmapHeight = labelFontSize + 4; const labelSprite = new Sprite(new Bitmap(nextLabelWidth, labelBitmapHeight)); labelSprite.bitmap.fontFace = labelFontFace; labelSprite.bitmap.fontSize = labelFontSize; labelSprite.bitmap.textColor = keke_actFontColor; labelSprite.bitmap.outlineColor = ColorManager.outlineColor(); labelSprite.bitmap.outlineWidth = keke_actOutW; const textYInBitmap = (labelBitmapHeight - labelFontSize) / 2; labelSprite.bitmap.drawText(labelText, 0, textYInBitmap, nextLabelWidth, labelFontSize); actWindow.addChild(labelSprite); // Create and configure the icon sprite const iconW = frame.height * iconData.size; iconSprite.scale.x = iconW / ImageManager.iconWidth; iconSprite.scale.y = iconW / ImageManager.iconHeight; iconWMax = Math.max(iconW, iconWMax); actWindow.addChild(iconSprite); // --- Positioning --- const padding = actWindow._padding; const lineCenterY = padding + frame.y + frame.height / 2; const iconOffset = iconW / 2 + keke_actIconSpace; // Position icon (anchor is assumed to be 0.5, 0.5) const iconCenterX = padding - iconOffset; iconSprite.x = iconCenterX; iconSprite.y = lineCenterY; // Position label (anchor is 0, 0) const iconLeftEdge = iconCenterX - (iconW / 2); labelSprite.x = iconLeftEdge - labelPadding - nextLabelWidth; labelSprite.y = lineCenterY - (labelSprite.height / 2); // Adjust text position for the skill name frame.x -= (iconOffset + totalLabelWidth); }); iconWMax += keke_actIconSpace; actWindow._iconXKe = (iconWMax + totalLabelWidth) / 2; return iconWMax; }; //- 行フレームの取得 function getLineFrame(frames, tage_i) { let i = -1; let true_i = 0; for (const frame of frames) { i++; if (!frame.isTop) { continue; } if (true_i == tage_i) { break; } true_i++; }; return frames[i]; }; //- アイコンデータの取得 function getIconData(action) { const item = action.item(); // スキルのアイコンを取得 const iconIndex = item.iconIndex; // メモ欄から表示アイコンを取得 const d = getPredictionCfg(null, "アイコン", "", action) || {}; return { index:d.index || iconIndex, size:(d.size || keke_actIconSize) / 100 }; }; //- 行動ビューの位置更新 function updateViewPosAct(sprite, battlerSprite, battler) { const pos = battler._enemyId ? keke_actPosEnemy : keke_actPosActor; const dire = sprite._showDireKe || pos["表示方向"]; const asi = getFullAnimeStatusAsi(battler); // フルアニメ if (asi && asi.visible) { const frame = asi.frame; const faceFrame = asi.faceFrame; sprite.x = frame.x; sprite.y = frame.y; if (faceFrame.width) { const direX = dire == "左" ? -faceFrame.width / 2 : dire == "右" ? faceFrame.width / 2 : 0; const direY = dire == "上" ? -faceFrame.height / 2 : dire == "下" ? faceFrame.height / 2 : 0; sprite.x += faceFrame.x + direX; sprite.y += faceFrame.y + direY; } else { const direX = dire == "左" ? -frame.width / 2 : dire == "右" ? frame.width / 2 : 0; const direY = dire == "上" ? -frame.height / 2 : dire == "下" ? frame.height / 2 : 0; sprite.y += direY; sprite.x += direX; } // 通常 } else { const frame = battlerSprite._frame; const battlerW = frame.width * battlerSprite.scale.x; const battlerH = frame.height * battlerSprite.scale.y; const direX = dire == "左" ? -battlerW / 2 : dire == "右" ? battlerW / 2 : 0; const direY = dire == "下" ? 0 : dire == "中央" ? -battlerH / 2 : -battlerH * 1.1; sprite.x = battlerSprite._homeX + direX; sprite.y = battlerSprite._homeY + direY; } // Y座標絶対モード if (keke_actAbsYMode) { sprite.y = sprite._memoAbsYKe != null ? sprite._memoAbsYKe : keke_actAbsY; } // 共通の位置 sprite.x += (pos["ずらしX"] || 0) + (sprite._iconXKe || 0) + sprite._memoXKe + sprite._centerXKe; sprite.y += (pos["ずらしY"] || 0) + sprite._memoYKe + sprite._centerYKe; // 鋼体ビューがあるなら上にずらす /*if (existSteelView(battlerSprite, dire)) { sprite.y -= battlerSprite._steelSpriteKe._heightKe || 0; }*/ // 行動ビューとの合計高さを取得 const isSteelView = existSteelView(battlerSprite, dire); const steelH = isSteelView ? battlerSprite._steelSpriteKe._heightKe : 0; // 画面外に出さない noOutScreen(sprite, sprite._widthKe, sprite._heightKe, sprite._padding, steelH); }; //- 鋼体ビューがあるか function existSteelView(battlerSprite, dire) { const steelSprite = battlerSprite._steelSpriteKe; if (!steelSprite) { return false; } if (steelSprite._direKe != dire) { return false;} if (!steelSprite.visible) { return false;} return true; }; //- 画面外に出さない function noOutScreen(sprite, width, height, padding, upPlus) { // 非表示範囲を取得 noShowScope = keke_actNoShowScope || {}; // 上下左右の端の位置を取得 const leftEdge = sprite.x + padding; const rightEdge = sprite.x + width - padding; const upEdge = sprite.y + padding - upPlus; const downEdge = sprite.y + height - padding; // 上下左右の超過分を取得 const leftOver = leftEdge - (noShowScope["左"] || 0); const rightOver = rightEdge - (Graphics.width - (noShowScope["右"] || 0)); const upOver = upEdge - (noShowScope["上"] || 0); const downOver = downEdge - (Graphics.height - (noShowScope["下"] || 0)); // 超過分だけ位置をずらす if (leftOver < 0) { sprite.x -= leftOver; } else if (rightOver > 0) { sprite.x -= rightOver; } if (upOver < 0) { sprite.y -= upOver; } else if (downOver > 0) { sprite.y -= downOver; } }; //- 行動ビューの可視更新 function updateViewVisibleAct(sprite, battler) { if (isVisibleAct(battler)) { sprite.visible = true; } else { sprite.visible = false; } }; //- 見えるか-行動 function isVisibleAct(battler) { if (BattleManager._phase == "battleEnd") { return false;} if (!isActShow() || !isValidPrediction(battler)) { return false; } if (isActing(battler) || battler.isRestricted()) { return false; } if (keke_actOnlyInput && !BattleManager.isInputting()) { return false; } if (BattleManager._preemptive && battler._enemyId) { return false; } if (battler._actorId) { let valid = false; if (keke_actShowCastActor && isCasting(battler)) { valid = true; } if (keke_actShowAutoActor && isAutoBattle(battler)) { valid = true; } if (!valid) { return false; } } if ($gameTemp._uiHideKeSmrc) { return false; } return true; }; // 行動ビューを表示するか function isActShow() { return keke_actShow; }; //- 行動予測有効か function isValidPrediction(subject) { // 無効スイッチがオンなら無効 if ($gameSwitches.value(keke_predictionOffSwitch)) { return false; }; //メモ欄に無効フラグがあったら無効 const memoValid = getValidByMemo(subject); if (memoValid == false) { return false; } // 常に行動予測/有効スイッチ/有効フラグのいずれかがオンなら有効 return keke_predictionAlways || memoValid == true || $gameSwitches.value(keke_predictionOnSwitch); }; //- メモ欄からのフラグの取得 function getValidByMemo(subject) { const members = [subject, ...$gameParty.aliveMembers()]; // メモ欄に無効フラグがあったら無効 if (members.some(battler => getPredictionCfg(battler, "無効"))) { return false; }; // メモ欄に有効フラグがあったら有効 if (members.some(battler => getPredictionCfg(battler, "有効"))) { return true; }; return null; }; //- 行動中か function isActing(battler) { return battler.isActing() || isActingSpeedStar(battler); }; //- 行動中か(スピードスター) function isActingSpeedStar(battler) { const subject = BattleManager._action ? BattleManager._action.subject() : null; return isSpeedStar() && BattleManager._battleWaitKe && subject && subject == battler; }; //- 詠唱中か function isCasting(battler, isStart) { if (!battler) { return false; } const state = battler._tpbState; const isActive = battler.isAlive() && !battler.isRestricted() && BattleManager._phase != "battleEnd"; return state.match(/casting/) && isActive && (isStart ? true : battler._tpbCastTime); }; //================================================== //-- ターゲット予測 //================================================== //- ターゲットの先行作成 function makeTargetFast(subject, redo) { // 対象は敵キャラのみ if (subject._actorId) { return; } // すでに作成済みならリターン if (!redo && subject._targetsFastKe && subject._targetsFastKe.lists.length) { return; } // アクションを取得 const actions = subject._actions; if (!actions.length) { return; } // ターゲットを作成 let targets = []; let targetsForLine = []; actions.forEach((action, i) => { // 再決定時は強制ターゲットを消去 if (redo) { action._targetIndex = -1; } // アクションのターゲットを取得 const t = getActionTargets(action); if (t.list) { targets[i] = t.list; } if (t.listForLine) { targetsForLine[i] = t.listForLine; } }); // ターゲットを保存 if (targets.length) { subject._targetsFastKe = { lists:targets, listsForLine:targetsForLine }; // ターゲットラインのリフレッシュ refreshTargetLine(subject); } }; //- アクションのターゲットを取得 function getActionTargets(action) { if (!action.item()) { return {}; } // ターゲットの作成 const tages = action.makeTargets(); if (!tages.length) { return {}; } // 実際のターゲット用 let list = tages.map(target => battlerToId(target)); // ターゲットライン用 let listForLine = []; // 対全体アクションか const isAll = isForAllAction(action); // 全体ワードの時は全体ワードフラグを挿入 if (isAll && keke_lineWordIfAll) { listForLine.push({ isAll:true, isAllWord:true }); // それ以外はターゲットが重複しないように挿入 } else { const line = []; tages.forEach(target => { if (!line.includes(target)) { line.push(target); } }); listForLine = line.map(target => battlerToId(target)); listForLine[0].isAll = isAll; } return { list:list, listForLine:listForLine }; }; //- 対全体アクションか function isForAllAction(action) { if (!action || !action.item()) { return false; } return action.isForAll() || action.isForEveryone(); }; //- ゲームアクション/行動直前の処理(処理追加) const _Game_Action_prepare = Game_Action.prototype.prepare; Game_Action.prototype.prepare = function() { _Game_Action_prepare.apply(this); // ターゲット先行作成の適用 applyTargetFast(this.subject(), this); }; //- ターゲット先行作成の適用 function applyTargetFast(subject, action) { if (!subject._targetsFastKe || !subject._targetsFastKe.lists.length) { return; } // ターゲットラインの消去 delTargetLine(subject); // 先行作成ターゲットリストを取得 const targets = subject._targetsFastKe.lists.shift(); if (!targets || !targets.length) { return; } // アクションに先行作成ターゲットをセット const newTargets = targets.map(id => idToBattler(id)).filter(target => target && target.isAlive()); if (newTargets.length) { action._targetsFastKePrds = newTargets; } }; //- ゲームアクション/ターゲットの作成(処理追加) const _Game_Action_makeTargets = Game_Action.prototype.makeTargets; Game_Action.prototype.makeTargets = function() { // 先行作成ターゲットがあればそれを採用 if (this._targetsFastKePrds) { return this._targetsFastKePrds; } return _Game_Action_makeTargets.apply(this); }; //- ターゲット先行作成の消去 function delTargetFast(subject) { if (subject._actorId) { return; } if (!subject._targetsFastKe || !subject._targetsFastKe.lists.length) { return; } subject._targetsFastKe = null; // ターゲットラインの消去 delTargetLine(subject); }; //- ターゲット先行作成の消去-全員 function delTargetFastAll() { $gameTroop.members().forEach(subject => { // ターゲット先行作成の消去 delTargetFast(subject); }); }; //================================================== //-- ターゲットの再決定 //================================================== //- ゲームバトラー/戦闘不能の処理(処理追加) if (Game_Battler.prototype.die == Game_BattlerBase.prototype.die) { Game_Battler.prototype.die = function() { Game_BattlerBase.prototype.die.call(this); }; } const _Game_Battler_die = Game_Battler.prototype.die; Game_Battler.prototype.die = function() { // ターゲットが戦闘不能になったらターゲットの修正 _Game_Battler_die.apply(this); // 個別ターゲットの修正 setTimeout(restoreEachTarget, 0, this); // 全体ターゲットの修正 setTimeout(restoreAllTarget, 0, this); }; //- ゲームバトラー/復活の処理(処理追加) if (Game_Battler.prototype.revive == Game_BattlerBase.prototype.revive) { Game_Battler.prototype.revive = function() { Game_BattlerBase.prototype.revive.call(this); }; } const _Game_Battler_revive = Game_Battler.prototype.revive; Game_Battler.prototype.revive = function() { // ターゲット側が復活したら全体ターゲットの修正 _Game_Battler_revive.apply(this); // 全体ターゲットの修正 setTimeout(restoreAllTarget, 0, this); }; //- ゲームパーティ/アクターの追加(処理追加) const _Game_Party_addActor = Game_Party.prototype.addActor; Game_Party.prototype.addActor = function(actorId) { // メンバーが加わった時に全体ターゲットの修正 _Game_Party_addActor.apply(this, arguments); if (inBattle()) { // 加わったアクターを取得 const actor = $gameParty.members().find(a => a._actorId == actorId); if (actor) { // 全体ターゲットの修正 setTimeout(restoreAllTarget, 300, actor); } } }; //- ゲームパーティ/アクターの除外(処理追加) const _Game_Party_removeActor = Game_Party.prototype.removeActor; Game_Party.prototype.removeActor = function(actorId) { // メンバーが外れた時にターゲットの修正 // 外れたアクターを取得 let actor = null; if (inBattle()) { actor = $gameParty.members().find(a => a._actorId == actorId); } _Game_Party_removeActor.apply(this, arguments); if (actor) { // 個別ターゲットの修正 setTimeout(restoreEachTarget, 300, actor); // 全体ターゲットの修正 setTimeout(restoreAllTarget, 300, actor); } }; //- 戦闘中か function inBattle() { // 戦闘中以外はリターン if (!$gameParty.inBattle) { return false; } // 戦闘開始時はリターン if (!BattleManager._phase || BattleManager._phase == "start") { return false; } return true; }; //- 個別ターゲットの修正 function restoreEachTarget(delTarget) { if (!delTarget) { return; } // 全ての敵キャラに対して処理 $gameTroop.aliveMembers().forEach(subject => { const targets = subject._targetsFastKe; if (!targets || !targets.listsForLine.length) { return; } // 全ての行動に対して判定 targets.listsForLine.forEach((tages, i) => { // 対全体はリターン if (tages[0].isAll) { return; } // 消去ターゲットがターゲットに含まれてなければリターン if (!tages.some(target => target.id == delTarget._actorId)) { return; } // アクションを取得 const action = subject._actions[i]; if (!action) { return; } // アクションのターゲットを取得 const t = getActionTargets(action); // ターゲットを修正 if (t.list) { targets.lists[i] = t.list; }; if (t.listForLine) { targets.listsForLine[i] = t.listForLine; }; // ターゲットラインのリフレッシュ refreshTargetLine(subject); }); }); }; //- 全体ターゲットの修正 function restoreAllTarget(target) { if (!target) { return; } // 全ての敵キャラに対して処理 $gameTroop.aliveMembers().forEach(subject => { const targets = subject._targetsFastKe; if (!targets || !targets.listsForLine.length) { return; } // 全ての行動に対して判定 targets.listsForLine.forEach((tages, i) => { // 対全体以外はリターン if (!tages[0].isAll) { return; } // アクションを取得 const action = subject._actions[i]; if (!action) { return; } // アクションのターゲットを取得 const t = getActionTargets(action); // ターゲットを修正 if (t.list) { targets.lists[i] = t.list; }; if (t.listForLine) { targets.listsForLine[i] = t.listForLine; }; // ターゲットラインのリフレッシュ refreshTargetLine(subject); }); }); }; //================================================== //-- ターゲットラインの開始 //================================================== //- ターゲットラインのリフレッシュ function refreshTargetLine(subject) { subject._makesTargetLineKe = true; }; //- ターゲットラインのリフレッシュ-全員 function refreshTargetLineAll() { $gameTroop.members().forEach(subject => { subject._makesTargetLineKe = true; }); }; //- シーンバトル/ステータスウインドウ位置の更新(処理追加) const _Scene_Battle_updateStatusWindowPosition = Scene_Battle.prototype.updateStatusWindowPosition; Scene_Battle.prototype.updateStatusWindowPosition = function() { // ステータスウインドウが移動したらターゲットラインのリフレッシュ const statusWindow = this._statusWindow; const targetX = this.statusWindowX(); const preSame = statusWindow.x == targetX; _Scene_Battle_updateStatusWindowPosition.apply(this); if (statusWindow.visible) { const same = statusWindow.x == targetX;; if (!preSame && same) { // ターゲットラインのリフレッシュ-全員 refreshTargetLineAll(); } } }; //- スブライトアクター/移動終了時の処理(処理追加) const _Sprite_Actor_onMoveEnd = Sprite_Actor.prototype.onMoveEnd; Sprite_Actor.prototype.onMoveEnd = function() { // アクター移動終了時にターゲットラインのリフレッシュ _Sprite_Actor_onMoveEnd.apply(this); // ターゲットラインのリフレッシュ-全員 refreshTargetLineAll(); }; //================================================== //-- ターゲットラインの更新 //================================================== //- スプライトエネミー/更新(処理追加) const _Sprite_Enemy_update = Sprite_Enemy.prototype.update; Sprite_Enemy.prototype.update = function() { _Sprite_Enemy_update.apply(this); // ターゲットラインの更新 updateTargetLine(this); }; //- ターゲットラインの更新 function updateTargetLine(sprite) { if (BattleManager._phase == "start" && !$gameTroop.isEventRunning()) { return; } const subject = sprite._battler; // ターゲットライン作成の更新 updateTargetLineMake(subject); // ターゲットライン明滅の更新 updataTargetLineFlicker(subject, sprite); // ターゲットラインの可視更新 updateTargetLineVisible(subject, sprite); }; //- ターゲットライン作成の更新 function updateTargetLineMake(subject) { if (!subject._makesTargetLineKe) { return; } const asi = getFullAnimeStatusAsi(subject); const faceSprite = asi ? asi.faceBaseSprite : null; if (faceSprite) { if (asi.startNear) { return; } // ターゲットラインの作成 makeTargetLine(subject); } else { const subjectSprite = searchSpriteBattler(subject); if (!subjectSprite || !subjectSprite._frame.width) { return;} // ターゲットラインの作成 makeTargetLine(subject); } }; //- ターゲットラインの作成 function makeTargetLine(subject) { if (!subject) { return; } // ターゲットラインの消去 delTargetLine(subject); // アクションがない場合はリターン if (!subject._actions.filter(action => action.item()).length) { subject._makesTargetLineKe = null; return; } const targets = subject._targetsFastKe; if (!targets || !targets.listsForLine.length) { return; } // 全てのターゲットに対して処理 targets.listsForLine.forEach((tages, i) => { tages.forEach(targetId => { if (!targetId) { return; } // 全体ワードの作成 if (targetId.isAllWord) { makeAllWord(subject, i); // ワイヤーの形成 } else { const target = idToBattler(targetId); if (target) { createWire(subject, target, i); } } }) }); // 作成開始フラグをオフ subject._makesTargetLineKe = null; // ターゲットラインのリチルド rechildTargetLine(); }; //- 全体ワードの作成 function makeAllWord(subject, i) { subjectSprite = searchSpriteBattler(subject); const allCfg = keke_allWordCfg; //if (!subjectSprite) { return } // 全体ワードの形成 const allSprite = createAllWord(subjectSprite, subject, i); // ワードの描画 const cfg = {}; cfg.word = allCfg["ワード"]; cfg.fontFace = allCfg["フォント"]; cfg.fontSize = allCfg["文字サイズ"] || keke_actFontSize; cfg.fontColor = allCfg["文字色"] || keke_lineColorList[subject.index() % 8] || "rgba(255, 255, 255)"; cfg.outW = allCfg["縁取り幅"] || keke_actOutW; const frame = drawWard(allSprite, subject, cfg); // ライン始点の取得 const point = getLineStart(subject, subjectSprite, i, frame); // 位置をセット allSprite.x = point.x + (allCfg["ずらしX"] || 0); allSprite.y = point.y + (allCfg["ずらしY"] || 0); }; //- 全体ワードの形成 function createAllWord(subjectSprite, subject, i) { const allCfg = keke_allWordCfg; // サイズを取得 const outW = allCfg["縁取り幅"] || keke_actOutW; const width = keke_actW + outW; const height = keke_actFontSize + outW; // スプライト & ビットマップ形成 const bitmap = new Bitmap(width, height); const sprite = new SpriteKePrds(bitmap); // チルド const layerId = keke_actLayer == "ウインドウより上" ? "D" : "B"; childLayer(sprite, layerId); // 原点 sprite.anchor.x = 0.5; sprite.anchor.y = 0.5; // 変数にセット if (!subjectSprite._targetLinesKe) { subjectSprite._targetLinesKe = { list:[] }; } subjectSprite._targetLinesKe.list.push(sprite); // スプライトにサイズを保存 sprite._widthKe = width; sprite._heightKe = height; // 表示方向を保存 const pos = subject._enemyId ? keke_actPosEnemy : keke_actPosActor; sprite._direKe = pos["表示方向"]; // 全体ワードフラグをセット sprite._isAllWordKe = true; return sprite; }; //- ワードの描画 function drawWard(sprite, subject, cfg) { const fontSize = cfg.fontSize; const word = cfg.word; const outW = cfg.outW; // フォント設定 const bitmap = sprite.bitmap; bitmap.fontFace = cfg.fontFace || $gameSystem.mainFontFace();; bitmap.fontSize = fontSize; bitmap.textColor = cfg.fontColor; bitmap.outlineWidth = cfg.outW; // サイズを取得 const width = bitmap.measureTextWidth(word); const height = fontSize + outW; // 描画 bitmap.drawText(word, 0, 0, bitmap.width, height, "center"); // フレームを保存 const frame = { x:0, y:0, width:width, height:height, outW:outW } return frame; }; //- ワイヤーの形成 function createWire(subject, target, i) { // バトラースプライト取得 const subjectSprite = searchSpriteBattler(subject); if (!subjectSprite) { return; } const targetSprite = searchSpriteBattler(target); if (!isFrontViewActor(target) && !targetSprite ) { return; } // ライン始点の取得 const subjectPoint = getLineStart(subject, subjectSprite, i); // ワイヤー始点Yの補正 subjectPoint.y += keke_lineW / 2 + keke_lineOutW; // ライン終点の取得 const targetPoint = getLineEnd(target, targetSprite); // 受容ラインの選択 const receptIndex = saveReceptLine(target, subject); // ワイヤーの描画 const wireBitmap = new Bitmap(Graphics.width, Graphics.height); const wireSprite = new SpriteKePrds(wireBitmap); drawWire(subject, target, wireBitmap, subjectPoint.x, subjectPoint.y, targetPoint.x, targetPoint.y, receptIndex); wireSprite._isWireKe = true; // 矢印の描画 const arrowBitmap = new Bitmap(Graphics.width, Graphics.height); const arrowSprite = new SpriteKePrds(arrowBitmap); drawWire(subject, target, arrowBitmap, subjectPoint.x, subjectPoint.y, targetPoint.x, targetPoint.y, receptIndex, true); arrowSprite._isArrowKe = true; // スプライト内にターゲットIDを保存 wireSprite._targetIdKe = battlerToId(target); arrowSprite._targetIdKe = wireSprite._targetIdKe; // チルド const layerId = keke_actLayer == "ウインドウより上" ? "C" : "A"; childLayer(wireSprite, layerId); childLayer(arrowSprite, layerId); // 変数にセット if (!subjectSprite._targetLinesKe) { subjectSprite._targetLinesKe = { list:[] }; } subjectSprite._targetLinesKe.list.push(wireSprite); subjectSprite._targetLinesKe.list.push(arrowSprite); // ワイヤーの長さを保存 const length = pointDistance(subjectPoint.x, subjectPoint.y, targetPoint.x, targetPoint.y); const preLength = subjectSprite._targetLinesKe.length || 0; subjectSprite._targetLinesKe.length = Math.max(length, preLength); }; //- フロントビューアクターか function isFrontViewActor(battler) { return !$gameSystem.isSideView() && battler._actorId; }; //- ライン始点の取得 function getLineStart(battler, battlerSprite, i, allData) { // 行動ビューからのポイント取得 const point = getPointFromActView(battler, battlerSprite, i, allData); if (point) { return point; } // 取得できなければバトグラから始点を取得 const asi = getFullAnimeStatusAsi(battler); const faceSprite = asi ? asi.faceBaseSprite : null; const startCfg = keke_lineStart; const dire = startCfg["始点-方向"]; const offsetX = startCfg["始点-ずらしX"] || 0; const offsetY = startCfg["始点-ずらしY"] || 0; // ラインポイントの取得 return getLinePoint(battler, battlerSprite, asi, faceSprite, dire, offsetX, offsetY); }; //- 行動ビューからのポイント取得 function getPointFromActView(battler, battlerSprite, i, allData) { // 行動ビュースプライトを取得 const actWindow = battlerSprite._actWindowKe; if (!actWindow) { return; } // 行動ビューのフレームを取得 const actFrames = battlerSprite._actFramesKe; if (!actFrames || !actFrames.length) { return; } const frame = getLineFrame(actFrames, i); if (!frame) { return; } // 始点の設定を取得 const startCfg = keke_lineStart; const dire = startCfg["始点-方向"]; const offsetX = startCfg["始点-ずらしX"] || 0; const offsetY = startCfg["始点-ずらしY"] || 0; // ポイント位置を取得 const point = {}; const padding = actWindow._padding; point.x = actWindow.x + padding + offsetX; point.y = actWindow.y + padding + frame.y + frame.height / 2 + offsetY; const actOutW = keke_actOutW; const iconW = frame.iconW || 0; const space = 4; // 全体ワードの位置 if (allData) { const adjustX = actOutW + allData.width / 2; point.x += dire == "左" ? -iconW - adjustX : frame.width + adjustX + space; // 画面外に出さない-全体ワード noOutScreenAllWord(point, frame.width, allData.width, iconW); // 始点ポイントの位置 } else { const startSize = keke_lineStartSize; const adjustX = actOutW + startSize / 2; point.x += dire == "左" ? -iconW - adjustX : dire == "右" ? frame.width + adjustX + space : frame.width / 2; point.y += dire == "上" ? -frame.height / 2 - startSize - space : dire == "下" ? frame.height / 2 + startSize : 0; } return point; }; //- 画面外に出さない-全体ワード function noOutScreenAllWord(point, frameW, allW, iconW) { // 非表示範囲を取得 noShowScope = keke_actNoShowScope || {}; // 左右の端の位置を取得 const leftEdge = point.x - allW / 2; const rightEdge = point.x + allW / 2; // 左右の超過分を取得 const leftOver = leftEdge - (noShowScope["左"] || 0); const rightOver = rightEdge - (Graphics.width - (noShowScope["右"] || 0)); // 超過分だけ位置をずらす if (leftOver < 0) { sprite.x -= leftOver; } else if (rightOver > 0) { sprite.x -= rightOver; } }; //- ライン終点の取得 function getLineEnd(battler, battlerSprite) { const asi = getFullAnimeStatusAsi(battler); const faceSprite = asi ? asi.faceBaseSprite : null; const endCfg = keke_lineEnd; const word = battler._actorId ? "(味方)" : "(敵)"; const dire = endCfg["終点-方向" + word]; const offsetX = endCfg["終点-ずらしX" + word] || 0; const offsetY = endCfg["終点-ずらしY" + word] || 0; // ラインポイントの取得 return getLinePoint(battler, battlerSprite, asi, faceSprite, dire, offsetX, offsetY); }; //- ラインポイントの取得 function getLinePoint(battler, battlerSprite, asi, faceSprite, dire, offsetX, offsetY) { const point = {} let frame = null; let x = 0; let y = 0; let inX = 0; let inY = 0; // 顔アイコンの位置情報 if (faceSprite) { frame = asi.faceFrame; x = asi.faceHomeX; y = asi.faceHomeY; // バトグラの位置情報 } else { // フロントビュー時のアクターはステータス位置を取得 if (isFrontViewActor(battler)) { const statusWindow = SceneManager._scene._statusWindow; $gameTemp.omitSupportMember = true; const i = $gameParty.battleMembers().indexOf(battler); frame = copyHash(statusWindow.faceRect(i)); frame.height = 0; x = statusWindow.x + frame.x + frame.width / 2; y = statusWindow.y + frame.y; // その他はバトグラ位置 } else { frame = copyHash(battlerSprite._frame); frame.width *= battlerSprite.scale.x; frame.height *= battlerSprite.scale.y; x = battlerSprite.x; y = battlerSprite.y - frame.height / 4; // 原点による補正 inX += (0.5 - battlerSprite.anchor.x) * frame.width; inY += (0.5 - battlerSprite.anchor.y) * frame.height; } } // ポイントの位置 inX += dire == "左" ? -frame.width / 2 : dire == "右" ? frame.width / 2 : 0; inY += dire == "上" ? -frame.height / 2 : dire == "下" ? frame.height / 2 : 0; // 座標 point.x = x + inX + offsetX; point.y = y + inY + offsetY; // 画面外に出さない-ポイント noOutScreenPoint(point); return point; }; //- 画面外に出さない-ポイント function noOutScreenPoint(point) { // 非表示範囲を取得 noShowScope = keke_actNoShowScope || {}; // 上下左右の限界位置を取得 const leftMax = noShowScope["左"] || 0; const rightMax = Graphics.width - (noShowScope["右"] || 0); const upMax = noShowScope["上"] || 0; const downMax = Graphics.height - (noShowScope["下"] || 0); // 限界位置を超えた分だけずらす if (point.x < leftMax) { point.x += leftMax - point.x; } else if (point.x > rightMax) { point.x += rightMax - point.x; } if (point.y < upMax) { point.y += upMax - point.y; } else if (point.y > downMax) { point.y += downMax - point.y; } }; //- ワイヤーの描画 function drawWire(subject, target, bitmap, ax, ay, bx, by, receptIndex, isArrow) { if (!target) { return; } const context = bitmap.context; // 矢印の方向を取得 let dire = keke_lineDire; // 敵キャラ同志をつなぐ場合は反転 if (target._enemyId) { dire = dire == "左" ? "右" : dire == "右" ? "左" : dire == "上" ? "下" : "上"; } const lineW = keke_lineW; const ow = keke_lineOutW; // ラインが複数ある時のずらしを取得 const pileOffset = keke_linePileOffset; let offsetX = 0; let offsetY = 0; // 横ラインの時 if (dire.match(/左|右/)) { offsetY = pileOffset * receptIndex; // 縦ラインの時 } else { offsetX = -pileOffset * receptIndex; } // アウトラインを描画 context.strokeStyle = "rgba(0, 0, 0)"; context.lineWidth = ow; const radius = keke_lineStartSize || lineW; context.beginPath(); context.arc(ax, ay, radius, 0, Math.PI * 2, false); context.stroke(); context.lineWidth = lineW + ow * 2; if (isArrow) { strokeArrow(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow, true); } else { strokeWire(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow, true); } // 本線を描画 context.strokeStyle = keke_lineColorList[subject.index() % 8] || "rgba(255, 255, 255)"; context.fillStyle = context.strokeStyle; context.beginPath(); context.arc(ax, ay, radius, 0, Math.PI * 2, false); context.fill(); context.lineWidth = lineW; if (isArrow) { strokeArrow(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow); } else { strokeWire(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow); } // 終了 context.restore(); bitmap._baseTexture.update(); }; //- ワイヤーの線描画 function strokeWire(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow, isOutLine) { const arrowW = keke_lineArrowSize + ow; const minLength = keke_lineMinLength; const xLength = Math.abs(bx - ax); const yLength = Math.abs(by - ay); const w = dire.match(/左|右/i) ? bx - ax : by - ay; context.beginPath(); // 上 if (dire == "上") { by += arrowW; bx += offsetX; if (ax == bx) { ax -= keke_linePileOffset; } const middleY= by > ay || yLength < minLength ? by + minLength : ay + w * 0.5; context.moveTo(ax, ay); context.lineTo(bx, middleY); // 下 } else if (dire == "下") { by += -arrowW; bx += offsetX; if (ax == bx) { ax -= keke_linePileOffset; } const middleY= by < ay || yLength < minLength ? by - minLength : ay + w * 0.5; context.moveTo(ax, ay); context.lineTo(bx, middleY); // 左 } else if (dire == "左") { bx += arrowW; by += offsetY; if (ay == by) { ay += keke_linePileOffset; } const middleX= bx > ax || xLength < minLength ? bx + minLength : ax + w * 0.5; context.moveTo(ax, ay); context.lineTo(middleX, by); // 右 } else if (dire == "右") { bx += -arrowW; by += offsetY; if (ay == by) { ay += keke_linePileOffset; } const middleX= bx < ax || xLength < minLength ? bx - minLength : ax + w * 0.5; context.moveTo(ax, ay); context.lineTo(middleX, by); } context.lineTo(bx, by); context.stroke(); }; //- 矢印の線描画 function strokeArrow(context, ax, ay, bx, by, dire, offsetX, offsetY, lineW, ow, isOutLine) { const arrowW = keke_lineArrowSize; const middleW = Math.max((arrowW * 2 - lineW - ow * 2) / 2, 1); const sharp = arrowW / 20; context.beginPath(); // 上 if (dire == "上") { bx += offsetX; if (ax == bx) { ax -= keke_linePileOffset; } context.beginPath(); context.moveTo(bx, by); context.lineTo(bx - arrowW, by + arrowW); context.lineTo(bx, by); context.lineTo(bx + arrowW, by + arrowW); if (isOutLine) { context.lineTo(bx + arrowW - middleW, by + arrowW - sharp); context.moveTo(bx - arrowW + middleW, by + arrowW - sharp); } context.lineTo(bx - arrowW, by + arrowW); // 下 } else if (dire == "下") { bx += offsetX; if (ax == bx) { ax -= keke_linePileOffset; } context.beginPath(); context.moveTo(bx, by); context.lineTo(bx - arrowW, by - arrowW); context.lineTo(bx, by); context.lineTo(bx + arrowW, by - arrowW); if (isOutLine) { context.lineTo(bx + arrowW - middleW, by - arrowW + sharp); context.moveTo(bx - arrowW + middleW, by - arrowW + sharp); } context.lineTo(bx - arrowW, by - arrowW); // 左 } else if (dire == "左") { by += offsetY; if (ay == by) { ay += keke_linePileOffset; } context.beginPath(); context.moveTo(bx, by); context.lineTo(bx + arrowW, by - arrowW); context.lineTo(bx, by); context.lineTo(bx + arrowW, by + arrowW); if (isOutLine) { context.lineTo(bx + arrowW - sharp, by + arrowW - middleW); context.moveTo(bx + arrowW - sharp, by - arrowW + middleW); } context.lineTo(bx + arrowW, by - arrowW); // 右 } else if (dire == "右") { by += offsetY; if (ay == by) { ay += keke_linePileOffset; } context.beginPath(); context.moveTo(bx, by); context.lineTo(bx - arrowW, by - arrowW); context.lineTo(bx, by); context.lineTo(bx - arrowW, by + arrowW); if (isOutLine) { context.lineTo(bx - arrowW + sharp, by + arrowW - middleW); context.moveTo(bx - arrowW + sharp, by - arrowW + middleW); } context.lineTo(bx - arrowW, by - arrowW) } context.lineTo(bx, by); context.stroke(); context.fill(); }; //- 受容ラインの選択 function saveReceptLine(target, subject) { if (!target._receptLinesKe) { target._receptLinesKe = []; } const recepts = target._receptLinesKe; let receptIndex = 0; // 攻撃者ID const subjectId = battlerToId(subject); // 空きがあったらそこを選択 let saved = false; recepts.forEach((slot, i) => { if (!slot && !saved) { recepts[i] = subjectId; receptIndex = i; saved = true; } }); // 空きがなかったら末尾を選択 if (!saved) { recepts.push(subjectId); receptIndex = recepts.length - 1; } return receptIndex; }; //- 受容ラインの消去 function delReceptLine(target, subject) { if (!target || !target._receptLinesKe) { return; } const recepts = target._receptLinesKe; // 行動者のスロットがあったら消去 recepts.forEach((slot, i) => { if (idToBattler(slot) == subject) { recepts[i] = null; } }); }; //- 受容ラインの初期化 function initReceptLine() { $gameParty.battleMembers().forEach(target => { target._receptLinesKe = null; }); }; //- ターゲットラインのリチルド function rechildTargetLine() { const layerId = keke_actLayer == "ウインドウより上" ? "C" : "A"; // ラインを全て取得 const allLines = []; $gameTroop.aliveMembers().forEach(enemy => { const sprite = searchSpriteBattler(enemy); if (!sprite) { return false; } const lines = sprite._targetLinesKe; if (!lines || !lines.list.length) { return false; } allLines.push(lines); }); if (!allLines.length) { return; } // ラインを長さが長い順にソート allLines.sort((a, b) => b.length - a.length); // ワイヤーを再チルド allLines.forEach(lines => { lines.list.forEach(sprite => { // ワイヤーでなければ除外 if (!sprite._isWireKe) { return; } childLayer(sprite, layerId); }); }); // 矢印を再チルド allLines.forEach(lines => { lines.list.forEach(sprite => { // 矢印でなければ除外 if (!sprite._isArrowKe) { return; } childLayer(sprite, layerId); }); }); }; //- ターゲットラインの消去 function delTargetLine(subject) { // ターゲットライン作成フラグを消去 subject._makesTargetLineKe = null; if (!subject) { return; } // ラインを取得 const sprite = searchSpriteBattler(subject); if (!sprite) { return; } const lines = sprite._targetLinesKe; if (!lines || !lines.list.length) { return; } // 全てのラインに対して処理 lines.list.forEach(sprite => { // スプライト破棄 sprite.parent.removeChild(sprite); sprite.destroy(); // 受容ラインの消去 delReceptLine(idToBattler(sprite._targetIdKe), subject) }); sprite._targetLinesKe = null; }; //================================================== //-- ターゲットライン-アニメ //================================================== //- スプライトセット・バトル/バトル背景の更新(処理追加) const _Spriteset_Battle_updateBattleback = Spriteset_Battle.prototype.updateBattleback; Spriteset_Battle.prototype.updateBattleback = function() { // ターゲットライン周期の更新 updateTargetLineCycle(); _Spriteset_Battle_updateBattleback.apply(this); }; //- ターゲットライン周期の更新 function updateTargetLineCycle() { const gt = $gameTemp; if (gt._tlFlickerDurationKe == null) { gt._tlFlickerDurationKe = 45; } gt._tlFlickerDurationKe--; if (gt._tlFlickerDurationKe <= 0) { gt._tlFlickerDurationKe = 45; } }; //- ターゲットライン明滅の更新 function updataTargetLineFlicker(subject, sprite) { const lines = sprite._targetLinesKe; if (!lines || !lines.list.length) { return; } // 全てのラインに対して処理 lines.list.forEach(sprite => { // 明滅の更新 updataFlicker(sprite); }); }; //- 明滅の更新 function updataFlicker(sprite) { // 明滅の開始 startFlicker(sprite); // 明滅の処理 processFlicker(sprite); }; //- 明滅の開始 function startFlicker(sprite) { if (sprite._flickerKe) { return; } const d = {}; d.start = 64; d.target = 255; d.timeMax = 45; d.easing = "eo"; d.duration = $gameTemp._tlFlickerDurationKe || d.timeMax; d.current = d.start; sprite._flickerKe = d; }; //- 明滅の処理 function processFlicker(sprite) { if (!sprite._flickerKe) { return; } const d = sprite._flickerKe; // カウントを減らす d.duration--; // ターンの処理 //d.current = processTurn(d.current, d.start, d.target, d.duration + 1, d.timeMax); d.current = processEasing(d.current, d.target, d.duration + 1, d.timeMax, d.easing); sprite.opacity = d.current; // ループ if (d.duration <= 0) { d.duration = d.timeMax; d.current = d.start; } }; //- ターゲットラインの可視更新 function updateTargetLineVisible(subject, subjectSprite) { if (!subjectSprite._targetLinesKe || !subjectSprite._targetLinesKe.list.length) { return; } let valid = true; while (true) { if (!isTargetLineShow(subject) || !isValidPrediction(subject)) { valid = false; break; } if ($gameTemp._uiHideKeSmrc) { valid = false; break; } if (BattleManager._phase == "battleEnd") { valid = false; break; } if (BattleManager.actor()) { valid = true; break; } if (subject.isRestricted()) { valid = false; break; } if (keke_lineOnlyInput && !BattleManager.actor()) { valid = false; break; } //if (isActing(subject) && !BattleManager.actor()) { valid = false; break; } break; } subjectSprite._targetLinesKe.list.forEach(line => { line.visible = valid; }) }; // ターゲットラインを表示するか function isTargetLineShow(subject) { if (getPredictionCfg(subject, "ラインあり")) { return true; }; if (getPredictionCfg(subject, "ラインなし")) { return false; }; return keke_lineShow; }; //================================================== //-- 行動予測中ステート //================================================== //- 行動予測中ステートの処理 function processInPredictionState(battler, actions) { // 行動予測中ステートのクリア clearInPredictionState(battler) if (!actions.length) { return; } // 全てのアクションを処理 actions.forEach(action => { // 行動予測中ステートIDの取得 const stateId = getInPredictionStateId(action); if (!stateId) { return; } // 行動予測中ステートのセット setInPredictionState(battler, stateId); }); }; //- 行動予測中ステートIDの取得 function getInPredictionStateId(action) { const item = action.item(); // メモ欄からステートIDを取得 const metas = metaAll(item.note, ["行動予測中ステート", "inPredictionState"]) if (!metas || !metas.length) { return null; } return Number(metas[metas.length - 1]); }; //- 行動予測中ステートのセット function setInPredictionState(battler, stateId) { // ステートを付与 battler.addState(stateId); // 付与したステートIDを保存 if (!battler._addedInPredictionStatesKe) { battler._addedInPredictionStatesKe = []; } battler._addedInPredictionStatesKe.push(stateId); }; //- 行動予測中ステートのクリア function clearInPredictionState(battler) { if (!battler._addedInPredictionStatesKe) { return; } battler._addedInPredictionStatesKe.forEach(stateId => battler.removeState(stateId)); battler._addedInPredictionStatesKe = []; }; //- 行動予測中ステートのクリア-全員 function clearInPredictionStateAll() { $gameParty.members().forEach(actor => { clearInPredictionState(actor); }); }; //================================================== //-- 配列基本 /ベーシック //================================================== //- ハッシュのディープコピー function copyHash(hash) { const copy = {}; Object.keys(hash).forEach(k => { if (!hash[k]) { copy[k] = hash[k]; return; } if (hash[k].constructor.name == "Object") { copy[k] = copyHash(hash[k]); } else if (hash[k].constructor.name == "Array") { copy[k] = copyArray(hash[k]); } else { copy[k] = hash[k]; } }); return copy; }; //- 配列のディープコピー function copyArray(array) { const copy = []; array.forEach((v, i) => { if (v.constructor.name == "Object") { copy[i] = copyHash(v); } else if (v.constructor.name == "Array") { copy[i] = copyArray(v); } else { copy[i] = v; } }); return copy; }; //================================================== //-- スプライト基本 /ベーシック //================================================== //- スプライトの検索-バトラー function searchSpriteBattler(battler) { const spriteset = SceneManager._scene._spriteset; let result = null; const sprites = battler._enemyId ? spriteset._enemySprites : spriteset._actorSprites; for (const sprite of sprites) { if(!sprite._battler) { continue; } if ((battler._actorId && sprite._battler._actorId == battler._actorId) || (battler._enemyId && sprite._battler.index() == battler.index())) { result = sprite; break; } } return result; }; //- ポイント間の距離 function pointDistance(ax, ay, bx, by) { return Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2)); }; //================================================== //-- バトラー基本 /ベーシック //================================================== //- バトラーをIDに function battlerToId(battler) { if (battler._actorId) { return { type:"actor", id:battler._actorId}; } else { return { type:"enemy", id:battler.index()}; } }; //- IDをバトラーに function idToBattler(id) { if (!id) { return; } if (id.type == "actor") { return $gameParty.allMembers().find(actor => actor._actorId == id.id); } else { return $gameTroop.members()[id.id]; } }; //- 自動戦闘中か function isAutoBattle(battler) { if (!battler) { return false; } return battler.isAutoBattle(); }; //================================================== //-- メタ取得 /ベーシック //================================================== //- 全てのメタ配列を合算 function totalAllMetaArray(battler, words, action, noDelSpace, ratioIndex) { let data = null let array = []; // バトラー値 if (battler) { data = battler._actorId ? battler.actor() : battler.enemy(); if (data) { metaAll(data.note, words, ratioIndex).forEach(e => array.push(e)); } if (battler._actorId) { // 職業値 data = battler.currentClass(); if (data) { metaAll(data.note, words, ratioIndex).forEach(e => array.push(e)); } // 装備値 battler._equips.forEach(equip => { data = equip.object(); if (data) { metaAll(data.note, words, ratioIndex).forEach(e => array.push(e)); } }); } // ステート値 battler._states.forEach(stateId => { data = $dataStates[stateId]; if (data) { metaAll(data.note, words, ratioIndex).forEach(e => array.push(e)); } }); } // アクション値 if (action) { data = action.item(); if (data) { metaAll(data.note, words, ratioIndex).forEach(e => array.push(e)); } } // スペースを削除 if (!noDelSpace) { array = array.map(e => e.replace(/\s/g, "")); } // 空の要素は削除 array = array.filter(e => e); return array; }; //- 全取得メタ function metaAll(note, words, ratioIndex) { var result = []; words.forEach(word => { // 複数用と単体用の正規表現を作成 var regText = '\<' + word + ':([^\>]*)\>'; var regExp_g = new RegExp(regText, 'gi'); var regExp = new RegExp(regText, 'i'); // マッチ判定 var matches = note.match(regExp_g); if (matches) { // 全てのマッチを処理 matches.forEach(function(line) { const match = line.match(regExp); // 成功率として扱う引数の処理 const vals = match[1].replace(/\s/g, "").split(","); const ratioIndexEx = ratioIndex - 1; if (ratioIndex && vals[ratioIndexEx] && Math.randomInt(100) >= Number(vals[ratioIndexEx])) { return; } result.push(match[1]); }); } }); return result; }; //================================================== //-- イージング /ベーシック //================================================== //- イージングの処理 function processEasing(current, target, duration, timeMax, easing, easingRate = 1) { const lt = calcEasing((timeMax - duration) / timeMax, easing, easingRate); const t = calcEasing((timeMax - duration + 1) / timeMax, easing, easingRate); const start = (current - target * lt) / (1 - lt); return start + (target - start) * t; }; //- イージングの計算 function calcEasing(t, easing, easingRate = 1) { const exponent = 2 * easingRate; switch (easing.toUpperCase()) { case "EI": return easeIn(t, exponent); case "EO": return easeOut(t, exponent); case "E": return easeInOut(t, exponent); default: return t; } }; //- 各イージング処理 function easeIn(t, exponent) { return Math.pow(t, exponent) || 0.001; }; function easeOut(t, exponent) {; return 1 - (Math.pow(1 - t, exponent) || 0.001); }; function easeInOut(t, exponent) { if (t < 0.5) { return easeIn(t * 2, exponent) / 2; } else { return easeOut(t * 2 - 1, exponent) / 2 + 0.5; } }; //================================================== //-- アイコンスプライト /ベーシック //================================================== //- アイコンスプライトの形成 function createIconSprite(iconIndex, anchorX, anchorY) { const baseSprite = new SpriteKePrds(); baseSprite.anchor.x = anchorX || 0.5; baseSprite.anchor.y = anchorY || 0.5; const pw = ImageManager.iconWidth; const ph = ImageManager.iconHeight; const sx = (iconIndex % 16) * pw; const sy = Math.floor(iconIndex / 16) * ph; if (isIconBackDraw()) { const backSprite = new SpriteKePrds(new Bitmap(pw, ph)); backSprite.anchor.x = anchorX || 0.5; backSprite.anchor.y = anchorY || 0.5; drawIconBack(backSprite.bitmap, iconIndex); baseSprite.addChild(backSprite) } const iconSprite = new SpriteKePrds(); const bitmap = ImageManager.loadSystem("IconSet"); iconSprite.bitmap = bitmap; iconSprite.anchor.x = anchorX || 0.5; iconSprite.anchor.y = anchorY || 0.5; iconSprite.setFrame(sx, sy, pw, ph); baseSprite.addChild(iconSprite) return baseSprite; }; //================================================== //-- エフェクト /ベーシック //================================================== //- エフェクトの実行 function doEffect(effect, subject, target) { target = target || subject; // アニメの表示 showAnime(subject, target, effect["アニメーション"]); // 効果音の再生 playSe(effect["効果音"]); // 画面フラッシュの表示 showScreenFlash(effect); // 画面ストップの開始 startScreenStop(effect["ヒットストップ"]); // フリーアニメ・スキルの実行 doFreeAnimeSkill(subject, target, effect["フリーアニメ"]); }; //- アニメの表示 function showAnime(subject, target, animeId) { if (!animeId) { return; } Window_BattleLog.prototype.showAnimation(subject, [target], animeId); }; //- 効果音の再生 function playSe(ses) { if (!ses || !ses.length) { return; } ses.forEach(se => { if (!se) { return; } AudioManager.playSe({ name:se["ファイル"], volume:se["音量"], pitch:se["ピッチ"], pan:se["位相"] }); }); }; //- 画面フラッシュの表示 function showScreenFlash(d) { if (!d["画面フラッシュ"] || !d["…フラッシュ色" || !d["…フラッシュ時間"]]) { return; } const delay = d["…フラッシュ遅延"] || 0; if (delay) { setTimeout(screenFlash, delay * 1000 / 60, d); } else { screenFlash(d); } }; //- 画面フラッシュ function screenFlash(d) { $gameScreen.startFlash(d["…フラッシュ色"], d["…フラッシュ時間"]); }; //- 画面ストップの開始 function startScreenStop(str) { if (!PluginManager._scripts.some(n => n == "Keke_HitStop")) { return; } // 画面ストップ $gameTemp.screenStopKe(str); }; //- フリーアニメ・スキルの実行 function doFreeAnimeSkill(subject, target, note) { if (!isFreeAnime() || !note) { return; } // アニメファイルとコモンを取得 const metaList = metaAll(note, ["フリーアニメ", "freeAnime"]); if (!metaList || !metaList.length) { return; } metaList.forEach(meta => { //-フリーアニメ・スキルの実行-個別 BattleManager.doFreeAnimeSkillKe(meta, subject, [target]); }); }; //================================================== //-- 図形描画 /ベーシック //================================================== // スクエアの塗り潰し function fillSquare(bitmap, x, y, width, height, color = "rgba(0,0,0,1)", round = 0, corner = "") { const context = bitmap.context; context.save(); context.fillStyle = color; designSquare(bitmap, x, y, width, height, round, corner); context.fill(); context.restore(); bitmap._baseTexture.update(); }; //- スクエアの線画 function strokeSquare(bitmap, x, y, width, height, color = "rgba(0,0,0,1)", lineW = 1, round = 0, corner = "", tsuno = {}, dire = "") { const context = bitmap.context; context.save(); context.strokeStyle = color; context.lineWidth = lineW; designSquare(bitmap, x, y, width, height, round, corner, tsuno, dire, lineW); context.stroke(); context.restore(); bitmap._baseTexture.update(); }; //- スクエアのデザイン function designSquare(bitmap, x, y, width, height, round = 0, corner = "", tsuno = {}, dire = "", lineW = 0) { const context = bitmap.context; context.beginPath(); const c1 = corner.includes("1") ? 0 : round; // 左上 const c2 = corner.includes("2") ? 0 : round; // 左下 const c3 = corner.includes("3") ? 0 : round; // 右上 const c4 = corner.includes("4") ? 0 : round; // 右下 context.moveTo(x + c1, y); if (tsuno.w && dire == "下") { context.lineTo(x + width / 2 + tsuno.o - tsuno.w / 2, y); context.moveTo(x + width / 2 + tsuno.o + tsuno.w / 2, y); } context.lineTo(x + width - c3, y); context.quadraticCurveTo(x + width, y, x + width, y + c3); context.lineTo(x + width, y + height - c4); context.quadraticCurveTo(x + width, y + height, x + width - c4, y + height); if (tsuno.w && dire == "上") { context.lineTo(x + width / 2 + tsuno.o + tsuno.w / 2, y + height); context.moveTo(x + width / 2 + tsuno.o - tsuno.w / 2, y + height); } context.lineTo(x + c2, y + height); context.quadraticCurveTo(x, y + height, x, y + height - c2); context.lineTo(x, y + c1); if (c1) { context.quadraticCurveTo(x, y, x + c1, y); } else { context.lineTo(x, y - lineW / 2); } }; })();