pure-full-lily/js/plugins/Keke_PredictionSystem.js
2025-09-16 10:12:44 -05:00

3437 lines
No EOL
130 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//=============================================================================
// 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
* <prediction: x: y: dire: name: icon: redoAct redoTarget on off>
* ★example)
* <prediction: x:100>
* Shift the position of the action view by 100 pixels to the right.
* -100 is left
* <prediction: y:100>
* Shift the position of the action view downward by 100 pixels. -100 is up
* <prediction: dire:up>
* Set the display direction of the action view to up
* Direction can be "Center", "Top", "Bottom", "Left", and "Right"
* <prediction: text: Mysterious attack>
* 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
* <prediction: icon:11>
* Set the icon displayed in the action view to the icon with ID 11
* If not specified, the skill icon will be displayed
* <prediction: redoAct>
* Redetermine the actions of the enemy characters.
* The target is also re-determined at the same time
* <prediction: redoTarget>
* Redetermine the enemy character's target. action as it is
* <prediction: on>
* Action prediction he turned on (enabled)
* <prediction: off>
* He turns off (disables) action prediction
* If there are both on and off, off takes precedence
* <prediction: showLine>
* display target line
* <prediction: hideLine>
* Do not display target line
* <prediction: x:100 y:100 dire:up icon:11 name:mysterious attack on>
* 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 行動ビューの文字色。赤、緑、青、濃度。色0255、濃度01。基本 240, 240, 0
* @default 240, 240, 0
*
* @param 行動-文字色(詠唱中)
* @parent 行動ビュー
* @desc act-fonColorCast 詠唱中の行動ビューの文字色。赤、緑、青、濃度。色0255、濃度01。基本 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 行動ビューの背景の色。(赤, 緑, 青, 濃度)。色0255、濃度01。基本 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<viewPos>
* @default {"表示方向":"上","ずらしX":"","ずらしY":""}
*
* @param 行動-表示位置/敵
* @parent 行動ビュー
* @desc act-showPos/enemy 敵の行動ビューの表示位置
* @type struct<viewPos>
* @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<noShowScope>
* @default {"上":"0","下":"0","左":"0","右":"0"}
*
* @param 行動-表示レイヤー
* @parent 行動ビュー
* @desc act-showLayer 行動ビューの表示レイヤー。基本 ウインドウより下
* @type select
* @option ウインドウより下
* @option ウインドウより上
* @default ウインドウより下
*
* @param 行動-不発エフェクト
* @parent 行動ビュー
* @desc act-misfireEffect 行動がMP不足等で不発に終わった時のエフェクト
* @type struct<effect>
* @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<lineStart>
* @default {"始点-方向":"右","始点-ずらしX":"","始点-ずらしY":""}
*
* @param ライン-終点
* @parent ターゲットライン
* @desc line-endPoint ライン終点の位置設定。終点は味方と敵キャラ両方ある(回復行動など、敵が敵をターゲットすることがあるため)
* @type struct<lineEnd>
* @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 ターゲットラインの色。赤, 緑, 青。各0255。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<allWordCfg>
* @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<se>[]
* @default []
*
* @param 画面フラッシュ
* @desc screenFlash 画面フラッシュを実行する
* @type boolean
* @default false
*
* @param …フラッシュ色
* @desc flashColor 画面フラッシュの色。赤, 緑, 青, 強さ。各0255。基本 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);
}
};
})();