unholy-maiden/js/plugins/SoR_EnemySymbolEncounter_MZ.js
2025-12-18 09:44:14 -06:00

2011 lines
No EOL
64 KiB
JavaScript

//=============================================================================
// SoR_EnemySymbolEncounter_MZ.js
// SoR License (C) 2020 蒼竜, REQUIRED User Registration on Dragon Cave
// http://dragonflare.blue/dcave/license.php
// ---------------------------------------------------------------------------
// Latest version v1.72 (2022/12/13)
//=============================================================================
/*:ja
@plugindesc <シンボルエンカウント総合> v1.72
@author 蒼竜
@target MZ
@url https://dragonflare.blue/dcave/
@help シンボルエンカウント方式のゲームデザインを実現するために
一般のコンシューマゲームのような挙動をモチーフに
次のような一通りの仕組みを単独プラグインで実装します。
- 接敵時の状態に応じた先制攻撃、不意打ちの判定
- 敵シンボルの経路探索および追跡
- 戦闘後の処理(プレイヤーの無敵時間,敵シンボルの復帰)
敵シンボルとするイベントを作成し、メモ欄に
<EnemySymbol>
と記述すると、このプラグインの効能を受けます。
シンボルごとの細かな挙動指定は、各イベント内における
イベントコマンドの「注釈」を用いて行います。
上のタグを記述するだけですぐに最低限の動作が可能ですが、
各製作者のゲームデザインにより沿った挙動をさせるには
様々なカスタマイズ(設定)が必要となります。
詳細な設定方法・応用例はpdfドキュメントを熟読して下さい。
@param -----一般設定-----
@param AutoDisable_DefaultEncounter
@desc true: 通常エンカウント方式の無効化設定をスクリプトが代行します (default: false)
@type boolean
@default false
@param FollowerAttack
@type select
@option 隊列歩行なし/先頭キャラクター以外エンカウント判定なし&シンボルのフォロワーすり抜けなし
@value 0
@option 先頭キャラクター以外にもエンカウント(接触)判定を付ける
@value 1
@option 先頭キャラクター以外エンカウント判定なし&シンボルのフォロワーすり抜けあり
@value 2
@option 先頭キャラクター以外エンカウント判定なし&シンボルのフォロワーすり抜けなし&フォロワー検知あり
@value 3
@option 先頭キャラクター以外にもエンカウント(接触)判定を付ける&フォロワー検知あり
@value 4
@option 先頭キャラクター以外エンカウント判定なし&シンボルのフォロワーすり抜けあり&フォロワー検知あり
@value 5
@default 0
@desc 「隊列歩行」使用時の、フォロワー考慮に関する敵シンボル挙動設定。隊列歩行を使わない場合はそのままで可
@param InvincibleTime_AfterBattle
@desc 戦闘後の無敵(発見されない、接触しない)フレーム時間 (default: 300)
@default 300
@type number
@decimal
@param EnemySearchRange_scale
@desc 敵シンボル移動経路探索量倍率 (default: 2.0)
@default 2.0
@type number
@decimals 2
@param SymbolKeepOut_RegionID
@desc 敵シンボル通行不可リージョンID, -1で無効 (default: -1)
@default -1
@type number
@min -1
@param -リンクエンカウント-
@param Enable_SymbolLink
@desc true: エンカウント時に付近にシンボルが複数存在した場合、まとめて戦闘を行います。要68.「トループ増援システム」
@type boolean
@default false
@param SymbolInvolvementRange
@desc 敵シンボルをリンクエンカウントに巻き込む範囲。プレイヤー中心の半径 (default: 3)
@type number
@default 3
@min 1
@param MaximumSymbolInvolvement
@desc リンクエンカウントに巻き込む周辺敵シンボルの最大数。0で制限なし
@type number
@default 0
@min 0
@param -ゲーム演出-
@param BalloonID_PlayerDetected
@desc プレイヤーを発見した時にシンボル頭上に表示するふきだしアイコン, -1で無効 (default: 1)
@default 1
@type number
@min -1
@param SE_PlayerDetected
@desc プレイヤーを発見した時に再生される効果音, 無指定で再生しない
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@dir audio/se/
@param BalloonID_PlayerFled
@desc プレイヤーを見失った時にシンボル頭上に表示するふきだしアイコン, -1で無効 (default: 6)
@default 6
@type number
@min -1
@param SE_PlayerFled
@desc プレイヤーを見失った時に再生される効果音, 無指定で再生しない
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@dir audio/se/
@param BalloonID_Evacuate
@desc プレイヤーを発見して,逃走する時にシンボル頭上に表示するふきだしアイコン, -1で無効 (default: 6)
@default 6
@type number
@min -1
@param SE_Evacuate
@desc プレイヤーを発見て,逃走する時に再生される効果音, 無指定で再生しない
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@dir audio/se/
@param BattleBGM_surprised
@desc 不意打ち状態で戦闘突入時の戦闘BGM, 無指定で変更なし
@dir audio/bgm/
@type struct<BGMDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@param -エンカウント発生後表現-
@param InvincibleStyle_Flash
@desc シンボルエンカウント発生後のプレイヤー無敵時間における表示方法(点滅) (default: true)
@type boolean
@default true
@param InvincibleStyle_Opaque
@desc シンボルエンカウント発生後のプレイヤー無敵時間における表示方法(透明) (default: false)
@type boolean
@default false
@param InvincibleStyle_Blend
@desc シンボルエンカウント発生後のプレイヤー無敵時間における表示方法(合成方法変更) (default: 0)
@type select
@option 通常
@value 0
@option 加算
@value 1
@option 乗算
@value 2
@option スクリーン
@value 3
@default 0
*/
/*:
@plugindesc <Symbol Encoutner System> v1.71
@author Soryu
@target MZ
@url https://dragonflare.blue/dcave/index_e.php
@help This introduces essencial and a highly functional suite
of symbol encounter system which are inspired by existing console RPGs.
Indispensable features for symbol encoutner system such as
- Preemptive/surprised encoutner based on the direction of players
and enemy symbol when they collide
- Route Search and Chase (Optimal or less-smart) for enemy symbols
- Invincible time for the player after battles with enemy symbols
are implemented on your game with one plugin.
Fundamentaly, you just make an event and write a tag as
<EnemySymbol>
in the note to work as an enemy symbol.
We can specify the behavior of symbols in detail by writing command in "Comment" command.
@param ---General---
@param AutoDisable_DefaultEncounter
@desc Set true so that the script disables regular encotner. (default: false)
@type boolean
@default false
@param FollowerAttack
@type select
@option No followers./No encounter except for the leader & Symbols does not pass through followers.
@value 0
@option Encoutner is occurred for all party characters touching to symbols.
@value 1
@option No encounter except for the leader & Symbols can pass through followers.
@value 2
@option No encounter except for the leader & Symbols does not pass through followers & Followers are also detected.
@value 3
@option Encoutner is occurred for all party characters & Followers are also detected.
@value 4
@option No encounter except for the leader & Symbols can pass through followers & Followers are also detected.
@value 5
@default 0
@desc Configuration for symbols in terms of collision with a party with followers.
@param InvincibleTime_AfterBattle
@desc Invincible (never detected and start battles) time (frame) after battle (default: 300)
@default 300
@type number
@param EnemySearchRange_scale
@desc Constant for route search length of enemy symbols (default: 2.0)
@default 2.0
@type number
@decimals 2
@param SymbolKeepOut_RegionID
@desc Region ID which Enemy symbols cannot pass, -1 to disable (default: -1)
@default -1
@type number
@min -1
@param -Link Encoutner-
@param Enable_SymbolLink
@desc If true, battles can involve enemies of multiple symbols around the player. Require 68. SoR_TroopWaveReinforcement_MZ.js
@type boolean
@default false
@param SymbolInvolvementRange
@desc The range, a radius, of enemy symbol involvement for link encounter assuming its center is the player position (default: 3)
@default 3
@min 1
@type number
@param MaximumSymbolInvolvement
@desc Maximum number of enemy symbols can be involved to a link encounter, set 0 for no limit.
@type number
@default 0
@min 0
@param -Screen Direction-
@param BalloonID_PlayerDetected
@desc Balloon icon appeared above the symbol when it detects player, -1 to disable (default: 1)
@default 1
@type number
@min -1
@param SE_PlayerDetected
@desc SE played when the symbol detects player, nothing specified to disable
@dir audio/se/
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@param BalloonID_PlayerFled
@desc Balloon icon appeared above the symbol when it loses player, -1 to disable (default: 6)
@default 6
@type number
@min -1
@param SE_PlayerFled
@desc SE played when the symbol loses player, nothing specified to disable
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@dir audio/se/
@param BalloonID_Evacuate
@desc Balloon icon appeared above the symbol which is about to escape, -1 to disable (default: 6)
@default 6
@type number
@min -1
@param SE_Evacuate
@desc SE played when the symbol being about to escape, nothing specified to disable
@type struct<SEDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@dir audio/se/
@type file
@param BattleBGM_surprised
@desc Change battle BGM when players are surprised, nothing specified to disable
@dir audio/bgm/
@type struct<BGMDATA>
@default {"name":"","volume":"100","pitch":"100","pan":"0"}
@param ---Effect After Encoutner---
@param InvincibleStyle_Flash
@desc Drawing style of player during invincible time after symbol encoutners (flash)(default: true)
@type boolean
@default true
@param InvincibleStyle_Opaque
@desc Drawing style of player during invincible time after symbol encoutners (opaque)(default: false)
@type boolean
@default false
@param InvincibleStyle_Blend
@desc Drawing style of player during invincible time after symbol encoutners (blend)(default: 0)
@type select
@option Normal
@value 0
@option Add
@value 1
@option Multiply
@value 2
@option Screen
@value 3
@default 0
*/
/*~struct~BGMDATA:
@type string
@param name
@dir audio/bgm/
@type file
@desc BGM
@param volume
@desc 音量 [0...100]
@type number
@default 100
@min 0
@max 100
@param pitch
@desc ピッチ [50...150]
@type number
@default 100
@min 50
@max 150
@param pan
@desc パン(位相) [-50...50]
@type number
@default 0
@min -50
@max 50
*/
/*~struct~BGMDATAE:
@type string
@param name
@dir audio/bgm/
@type file
@desc BGM
@param volume
@desc Volume of BGM [0...100]
@type number
@default 100
@min 0
@max 100
@param pitch
@desc Pitch of BGM [50...150]
@type number
@default 100
@min 50
@max 150
@param pan
@desc Pan of BGM [-50...50]
@type number
@default 0
@min -50
@max 50
*/
/*~struct~SEDATA:
@type string
@param name
@dir audio/se/
@type file
@desc 効果音
@param volume
@desc 音量 [0...100]
@type number
@default 100
@min 0
@max 100
@param pitch
@desc ピッチ [50...150]
@type number
@default 100
@min 50
@max 150
@param pan
@desc パン(位相) [-50...50]
@type number
@default 0
@min -50
@max 50
*/
/*~struct~SEDATAE:
@type string
@param name
@dir audio/se/
@type file
@desc SE File
@param volume
@desc Voulme [0...100]
@type number
@default 100
@min 0
@max 100
@param pitch
@desc Pitch [50...150]
@type number
@default 100
@min 50
@max 150
@param pan
@desc Pan [-50...50]
@type number
@default 0
@min -50
@max 50
*/
(function() {
const Param = PluginManager.parameters('SoR_EnemySymbolEncounter_MZ');
const AutoDisable_DefaultEncounter = Boolean(Param['AutoDisable_DefaultEncounter'] === 'true') || false;
const FollowerAttack = Number(Param['FollowerAttack']) || 0;
const InvinsibleDuration_AB = Number(Param['InvincibleTime_AfterBattle']) || 300;
const SearchRange_scale = Number(Param['EnemySearchRange_scale']) || 2.0;
const SymbolKeepOut_RegionID = Number(Param['SymbolKeepOut_RegionID']) || -1;
const BalloonID_PlayerDetected = Number(Param['BalloonID_PlayerDetected']) || 1;
const BalloonID_PlayerFled = Number(Param['BalloonID_PlayerFled']) || 6;
const SE_PlayerDetected = convertJsonSE(Param['SE_PlayerDetected']) || '';
const SE_PlayerFled = convertJsonSE(Param['SE_PlayerFled']) || '';
const BattleBGM_surprised = convertJsonSE(Param['BattleBGM_surprised']) || '';
const InvincibleStyle_Flash = Boolean(Param['InvincibleStyle_Flash'] === 'true') || false;
const InvincibleStyle_Opaque = Boolean(Param['InvincibleStyle_Opaque'] === 'true') || false;
const InvincibleStyle_Blend = Number(Param['InvincibleStyle_Blend']) || 0;
//v1.30
const BalloonID_Evacuate = Number(Param['BalloonID_Evacuate']) || 6;
const SE_Evacuate = convertJsonSE(Param['SE_Evacuate']) || '';
function convertJsonSE(param) {
const obj = JSON.parse(param);
obj.volume = Number(obj.volume);
obj.pan = Number(obj.pan);
obj.pitch = Number(obj.pitch);
return obj;
}
//v1.70
const isTWR = PluginManager._scripts.includes("SoR_TroopWaveReinforcement_MZ") ? true : false;
const Enable_SymbolLink = Boolean(Param['Enable_SymbolLink'] === 'true') || false;
const isActive_SymbolLink = isTWR && Enable_SymbolLink;
const SymbolInvolvementRange = Number(Param['SymbolInvolvementRange']) || 5;
const MaximumSymbolInvolvement = Number(Param['MaximumSymbolInvolvement']) || 5;
//////////////////////////////////////////////////////////////////////////
//
// Encount Manager
//
//////////////////////////////////////////////////////////////////////////
const SoR_ESE_BM_onEncounter = BattleManager.onEncounter;
BattleManager.onEncounter = function() {
SoR_ESE_BM_onEncounter.call(this);
this._preemptive = $gameTemp.encflag==2? true : false;
this._surprise = $gameTemp.encflag==1? true : false;
}
const SoR_ESE_GE_lock = Game_Event.prototype.lock;
Game_Event.prototype.lock = function() {
const ens = this.event().meta.EnemySymbol;
if(ens){
if (this._locked) return;
this._prelockDirection = this.direction();
$gameTemp.isEncounteredonMap = ens;
this.EncountDir();
if(isActive_SymbolLink==true) BattleManager.searchSEInvolvement(this._eventId); //v1.70
$gameTemp.JustEncounterID = this._eventId;
this._locked = true;
}
else SoR_ESE_GE_lock.call(this);
}
Game_Event.prototype.EncountDir = function() {
let player = $gamePlayer;
if($gameTemp.collisionFollower != null) player = $gameTemp.collisionFollower
const p = {x:player.x,y:player.y,dir:player._direction};
const e = {x:this.x,y:this.y,dir:this._direction};
$gameTemp.encflag = 0;
if (p.dir == e.dir){
AlignSymbolWithDirection(p, e);
if(e.x > p.x) $gameTemp.encflag = 2;
else $gameTemp.encflag = 1;
}
}
function AlignSymbolWithDirection(p, e){
e.x-=p.x;
e.y-=p.y;
p.x-=p.x;
p.y-=p.y;
while(p.dir!=6){
//Cyclic subgroup generated by 2 \in Group(Z_{10}-{0},*)
p.dir = (p.dir*2)%10;
e.dir = (e.dir*2)%10;
//rot90
const tmp = -e.y;
e.y = e.x;
e.x = tmp;
}
}
const SoR_ESE_SB_terminate = Scene_Battle.prototype.terminate;
Scene_Battle.prototype.terminate = function() {
SoR_ESE_SB_terminate.call(this);
if($gameTemp.isEncounteredonMap){
$gamePlayer.SetupInvinsibleEffectsAfterSE();
$gameTemp.isEncounteredonMap = false;
$gameTemp.JustAfterEncounter = true;
$gameTemp.Encounter_BattleEndSwitch = true;
if($gameTemp.encflag==1 && BattleBGM_surprised.name != ''){//surprised bgm
$gameSystem.setBattleBgm($gameTemp.defaultBattleBGM);
}
}
$gameTemp.encflag = 0;
$gameTemp.collisionFollower = null;
}
const SoR_ESE_SM_update = Spriteset_Map.prototype.update;
Spriteset_Map.prototype.update = function(){
SoR_ESE_SM_update.call(this);
this.updateCharacterForSymbolEnc();
}
Spriteset_Map.prototype.updateCharacterForSymbolEnc = function(){
if($gameSystem.SoRSE_InvincibleExcept()){
//invinsible time after battles
if($gameTemp.invincibleAfterEnc>=0) $gamePlayer.UpdateInvinsibleEffectsAfterSE();
else if($gameTemp.invincibleAfterEnc != -1) $gamePlayer.FinishInvinsibleEffectsAfterSE();
}
}
const SoR_ESE_GP_clearTransferInfo = Game_Player.prototype.clearTransferInfo;
Game_Player.prototype.clearTransferInfo = function() {
SoR_ESE_GP_clearTransferInfo.call(this);
this.FinishInvinsibleEffectsAfterSE();
}
const SoR_ESE_GE_start = Game_Event.prototype.start;
Game_Event.prototype.start = function() {
if(this.event().meta.EnemySymbol){
const ES = this._SoRES;
if(VehicleCheck(0) == false) return;// boat vs enemy on the ground ???
if($gameTemp.invincibleAfterEnc != -1) return;
else{ //For after event(battle)
if(ES.Isreturn_afterwait){
ES.IsDetectPlayer = false;
ES.IsChasePlayer = false;
ES.IsFleePlayer = true;
ES.waitAC_count = ES.wait_afterchase;
ES.IsStayAfterFlee = true;
}
else this.checkAllchaseFlags();
this.setMoveSpeed(ES.default_speed);
}
}
SoR_ESE_GE_start.call(this);
}
function VehicleCheck(v){
if(v==0){
if($gamePlayer.isInVehicle()) return false;
else return true;
}
return true;
}
const SoR_ESE_GI_command301 = Game_Interpreter.prototype.command301;
Game_Interpreter.prototype.command301 = function(params) {
if($gameTemp.isEncounteredonMap == true && !$gameParty.inBattle()){
let troopId;
if (params[0] === 0) troopId = params[1]; // Direct designation
else if (params[0] === 1) troopId = $gameVariables.value(params[1]); // Designation with a variable
else troopId = $gamePlayer.makeEncounterTroopId(); // Same as Random Encounters
if ($dataTroops[troopId]) {
BattleManager.setup(troopId, params[2], params[3]);
BattleManager.onEncounter();
if($gameTemp.encflag==1 && BattleBGM_surprised.name != ''){//surprised bgm
$gameTemp.defaultBattleBGM = $gameSystem._battleBgm;
$gameSystem.setBattleBgm(BattleBGM_surprised);
}
BattleManager.setEventCallback(function(n) {
this._branch[this._indent] = n;
}.bind(this));
$gamePlayer.makeEncounterCount();
SceneManager.push(Scene_Battle);
}
return true;
}
return SoR_ESE_GI_command301.call(this, ...arguments); //return for the interpreter
}
//v1.70
BattleManager.searchSEInvolvement = function(mainEId){
if($gameParty.inBattle()) return;
if($dataMap.meta.SoR_NoLinkEncounter == true) return;
const seEVs = $gameMap.events().filter(event => !!event._SoRES && event.findProperPageIndex()>=0 && calcDist(event, $gamePlayer)<=SymbolInvolvementRange);
let n_involved = 0;
$gameTemp.SoRSE_involvedEIDs = [];
for(const ev of seEVs){
if(MaximumSymbolInvolvement>0 && n_involved>=MaximumSymbolInvolvement) break;
if(ev._eventId == mainEId) continue;
if(ev._SoRES.can_linkSymbols==true && BresenhamTest(ev)){
const p_idx = ev.findProperPageIndex();
const nL = ev.event().pages[p_idx].list.length;
let isfound = false;
for(let i=0; i<nL;i++){
if(ev.event().pages[p_idx].list[i].code == 301){
const com = ev.event().pages[p_idx].list[i].parameters;
if(typeof $gameTemp.TroopWaves === "undefined") $gameTemp.TroopWaves = [];
$gameTemp.TroopWaves.push(com[1]);
n_involved++;
isfound = true;
}
else if(isfound==true && Math.floor(ev.event().pages[p_idx].list[i].code/100)!=3){
$gameTemp.SoRSE_involvedEIDs.push({evId: ev._eventId, idx: i+1});
break;
}
}
}
}
//if not mainEId
//if With EnemySymbol tag
//if Can pass bresenham test
//if canSymbolInvolved_SE
//-> involve (call troopWaveReinforcement)
// record EventID involved
}
function BresenhamTest(e){
const p = $gamePlayer;
if(e.x-p.x!=0) return Bresenham(e,p,true);
return Bresenham90(e,p,true);
}
const SoR_ESE_GI_currentCommand = Game_Interpreter.prototype.currentCommand;
Game_Interpreter.prototype.currentCommand = function() {
if($gameTemp.Encounter_BattleEndSwitch == true){
//trigger involved symbol events
const data = {
code: 911,
indent: 0,
parameters: []
};
return data;
}
return SoR_ESE_GI_currentCommand.call(this);
}
Game_Interpreter.prototype.command911 = function(params) {
$gameTemp.Encounter_BattleEndSwitch = false;
this._index--;
if(typeof $gameTemp.SoRSE_involvedEIDs === "undefined") return true;
const Events = $gameMap.events();
for(const cand of $gameTemp.SoRSE_involvedEIDs){
const fidx = Events.findIndex(x=>x._eventId==cand.evId);
if(fidx==-1) continue;
const ev = Events[fidx];
ev.start();
const elist = ev.list().slice(cand.idx+1);
if(ev._interpreter==null) ev._interpreter = new Game_Interpreter();
ev._interpreter.setup(elist, ev._eventId);
}
return true;
}
/*
Game_Map.prototype.setupStartingMapEvent = function() {
for (const event of this.events()) {
if (event.isStarting()) {
event.clearStartingFlag();
this._interpreter.setup(event.list(), event.eventId());
return true;
}
}
return false;
};*/
//////////////////////////////////////////////////////////////////////////
//
// Process Invinsible Effect for player Afeter Symbol Encounter
//
//////////////////////////////////////////////////////////////////////////
Game_Player.prototype.SetupInvinsibleEffectsAfterSE = function() {
$gameTemp.invincibleAfterEnc = InvinsibleDuration_AB;
if(InvincibleStyle_Opaque) $gamePlayer.setOpacity(127);
if(InvincibleStyle_Blend !=0) $gamePlayer.setBlendMode(InvincibleStyle_Blend);
$gameTemp._flash_count = 0;
}
Game_Player.prototype.UpdateInvinsibleEffectsAfterSE = function() {
$gameTemp.invincibleAfterEnc--;
if(InvincibleStyle_Flash){
let flash;
if($gameTemp.invincibleAfterEnc>=InvinsibleDuration_AB*0.5) flash=12;
else if($gameTemp.invincibleAfterEnc>=InvinsibleDuration_AB*0.25) flash=9;
else if($gameTemp.invincibleAfterEnc>=InvinsibleDuration_AB*0.1) flash=7;
else flash = 5;
$gameTemp._flash_count = ($gameTemp._flash_count + 1) % flash;
if($gameTemp._flash_count<=1) $gamePlayer.SetInvinsibleAfterSymbolEnc(true);
else $gamePlayer.SetInvinsibleAfterSymbolEnc(false);
if($gameTemp.invincibleAfterEnc==0) $gamePlayer.FinishInvinsibleEffectsAfterSE(false);
}
if(InvincibleStyle_Opaque && $gameTemp.invincibleAfterEnc==0) $gamePlayer.FinishInvinsibleEffectsAfterSE(false);
}
Game_Player.prototype.FinishInvinsibleEffectsAfterSE = function() {
$gameTemp.invincibleAfterEnc = -1;
$gameTemp._flash_count = 0;
$gamePlayer.SetInvinsibleAfterSymbolEnc(false);
}
Game_Player.prototype.SetInvinsibleAfterSymbolEnc = function(val) {
if(InvincibleStyle_Opaque) $gamePlayer.setOpacity(255);
if(InvincibleStyle_Blend !=0) $gamePlayer.setBlendMode(0);
if(InvincibleStyle_Flash){
const v = val===true? (InvincibleStyle_Opaque===true? 127:0) :255;
$gamePlayer.setOpacity(v);
}
}
//////////////////////////////////////////////////////////////////////////
//
// Player Observer on Map Scene (by enemy events)
//
//////////////////////////////////////////////////////////////////////////
const SoR_ESE_GE_setupPage = Game_Event.prototype.setupPage;
Game_Event.prototype.setupPage = function() {
SoR_ESE_GE_setupPage.call(this);
this.InitEnemySymbolBehavior();
}
Game_Event.prototype.InitEnemySymbolBehavior = function() {
if(!this.event().meta.EnemySymbol) return;
const entag = this.event().meta.EnemySymbol;
let seinv_tag = null;
if (typeof entag === "string" || entag instanceof String) seinv_tag = entag.trim();
else seinv_tag = entag;
this._SoRES = {
Is_symbolIgnore : false,
can_linkSymbols: seinv_tag=="NotInvolved"? false : true,
trails : [],
trails_n : 0,
see_distance : 5,
see_angle : 30*Math.PI/180+0.00001,
serach_state : null,
default_speed : this._moveSpeed,
chase_speed : 4,
chase_duration : 40,
Dir8moveChase : false,
waitAC_count : 180,
bushrate : 0.2,
IsDetectBalloon : false,
IsDetectPlayer : false,
IsChasePlayer : false,
IsFleePlayer : false,
Isreturn_afterwait : false,
IsReturnOriginalPos : false,
detectedAlertDuration : 10,
detectedAlert_count : 0,
wait_afterchase : 150,
updateRoute_interval : 5,
Ischase_onVehicle : false,
call_CommonEventID : [0,0,0,0,0],
call_ScriptsInPhases : [null,null,null,null,null],
SpontaneousEvacuate : false,
bullmove : false,
_bullcounter : 0,
_bullaxis : -1,
leapmove : false,
leapanim : 0,
leapduration : 180,
EvacCond : "true",
//Breakable Map Objects
_BMOattacked : false,
_BMOcanstan : true,
_BMOstanned : false,
_BMOstanwait : 0
}
this.default_x = this.x;
this.default_y = this.y;
const p_idx = this.findProperPageIndex();
if(p_idx>=0){//do not process for invalid page (no unconditional pages)
const nL = this.event().pages[p_idx].list.length;
for(let i=0; i<nL;i++){
if(this.event().pages[p_idx].list[i].code == 108 || this.event().pages[p_idx].list[i].code == 408){
const com = this.event().pages[p_idx].list[i].parameters;
this.SetSymbolTags(com[0]);
}
}
}
}
Game_Event.prototype.canSymbolInvolved_SE = function() {
if(!this._SoRES) return false;
return this._SoRES.can_linkSymbols;
}
Game_Event.prototype.SetSymbolTags = function(com) {
const ES = this._SoRES;
if(com.match(/(?:distance):[ ]*(.*)/i)) ES.see_distance = parseInt(RegExp.$1);
else if(com.match(/(?:angle):[ ]*(.*)/i)) ES.see_angle = parseInt(RegExp.$1)*Math.PI/180+0.00001;
else if(com.match(/(?:speed):[ ]*(.*)/i)) ES.chase_speed = Number(RegExp.$1)!=NaN ? Number(RegExp.$1) : ES.chase_speed;
else if(com.match(/(?:duration):[ ]*(.*)/i)) ES.chase_duration = parseInt(RegExp.$1);
else if(com.match(/(?:wait):[ ]*(.*)/i)) ES.wait_afterchase = parseInt(RegExp.$1);
else if(com.match(/(?:return)[ ]*/i)) ES.Isreturn_afterwait = true;
else if(com.match(/(?:ignore)/i)) ES.Is_symbolIgnore = true;
else if(com.match(/(?:bush_rate):[ ]*(.*)/i)) ES.bushrate = parseFloat(RegExp.$1);
else if(com.match(/(?:alert_time):[ ]*(.*)/i)) ES.detectedAlertDuration = parseInt(RegExp.$1);
else if(com.match(/(?:route_search):[ ]*(.*)/i)) ES.updateRoute_interval = parseInt(RegExp.$1);
else if(com.match(/(?:chase_vehicle)[ ]*/i)) ES.Ischase_onVehicle = true;
else if(com.match(/(?:script5):[ ]*(.*)/i)) ES.call_ScriptsInPhases[4] = String(RegExp.$1).trim();
else if(com.match(/(?:script4):[ ]*(.*)/i)) ES.call_ScriptsInPhases[3] = String(RegExp.$1).trim();
else if(com.match(/(?:script3):[ ]*(.*)/i)) ES.call_ScriptsInPhases[2] = String(RegExp.$1).trim();
else if(com.match(/(?:script2):[ ]*(.*)/i)) ES.call_ScriptsInPhases[1] = String(RegExp.$1).trim();
else if(com.match(/(?:script):[ ]*(.*)/i)) ES.call_ScriptsInPhases[0] = String(RegExp.$1).trim();
else if(com.match(/(?:common_event5):[ ]*(.*)/i)) ES.call_CommonEventID[4] = parseInt(RegExp.$1);
else if(com.match(/(?:common_event4):[ ]*(.*)/i)) ES.call_CommonEventID[3] = parseInt(RegExp.$1);
else if(com.match(/(?:common_event3):[ ]*(.*)/i)) ES.call_CommonEventID[2] = parseInt(RegExp.$1);
else if(com.match(/(?:common_event2):[ ]*(.*)/i)) ES.call_CommonEventID[1] = parseInt(RegExp.$1);
else if(com.match(/(?:common_event):[ ]*(.*)/i)) ES.call_CommonEventID[0] = parseInt(RegExp.$1);
else if(com.match(/(?:evacuate):[ ]*(.*)/i)){ES.SpontaneousEvacuate = true; ES.EvacCond = String(RegExp.$1).trim();}
else if(com.match(/(?:evacuate)[ ]*/i)){ES.SpontaneousEvacuate = true;}
else if(com.match(/(?:8dir)[ ]*/i)){ES.Dir8moveChase = true;}
else if(com.match(/(?:bull)[ ]*/i)){ES.bullmove = true;}
else if(com.match(/(?:leap):[ ]*(.*)/i)){ES.leapmove = true; ES.leapanim = parseInt(RegExp.$1);}
}
const SoR_ESE_GE_updateSelfMovement = Game_Event.prototype.updateSelfMovement;
Game_Event.prototype.updateSelfMovement = function() {
if(this.event().meta.EnemySymbol){
const ES = this._SoRES;
if(this.reactBMO_checkStanned()) return;
else if(!ES.Is_symbolIgnore && ES.serach_state != null){
if(this._erased || this.findProperPageIndex()==-1) return;
if($gameMap.isEventTriggered()) return;
const state_bin = this.checkAllchaseFlags();
const state = ES.serach_state;
if(ES.IsChasePlayer){
if(ES.SpontaneousEvacuate && SoR_Eval(ES.EvacCond)) this.EvacuateFromPlayer(state);
else if(ES.bullmove) this.BullPlayerChaser(state);
else if(ES.leapmove && state.dist>3) this.LeapPlayerChaser(state);
else this.PlayerChaser(state);
}
else if(ES.IsFleePlayer) this.FleePlayer();
else if(ES.IsReturnOriginalPos) this.ReturnOrigPoint(state.dist);
if(state_bin != 0b00000) return;
}
}
SoR_ESE_GE_updateSelfMovement.call(this);
}
const SoR_ESE_GE_update = Game_Event.prototype.update;
Game_Event.prototype.update = function() {
SoR_ESE_GE_update.call(this);
if(this.event().meta.EnemySymbol) this.updateSEprocess();
}
Game_Event.prototype.updateSEprocess = function() {
const ES = this._SoRES;
if($gameTemp.JustAfterEncounter && $gameTemp.JustEncounterID == this._eventId){//just after a battle
if(ES.call_CommonEventID[3] != 0) $gameTemp.reserveCommonEvent(ES.call_CommonEventID[3]);
if(ES.call_ScriptsInPhases[3] != null) this.SoR_ExecScript(ES.call_ScriptsInPhases[3]);
$gameTemp.JustAfterEncounter = false;
$gameTemp.JustEncounterID = -1;
}
if(this._erased || this.findProperPageIndex()==-1) return;
if(ES.Is_symbolIgnore || this.isVehicleChase() ){
this.clearAllchaseFlags();
return;
}
if( $gameTemp.invincibleAfterEnc > -1){
if(ES.IsChasePlayer){
ES.IsChasePlayer = false;
ES.IsFleePlayer = true;
}
ES.IsDetectPlayer = false;
}
else if($gameTemp.invincibleAfterEnc == -1 && !this.isMoving()){
const state = this.serachPlayer();
ES.serach_state = state;
if(state.flag) ES.IsDetectPlayer = true;
if(ES.IsDetectPlayer && !ES.IsChasePlayer && !ES.IsFleePlayer) this.DetectedPlayer();
}
}
Game_Event.prototype.isVehicleChase = function() {
const ES = this._SoRES;
let flag = $gamePlayer.isInVehicle();
if(ES.Ischase_onVehicle) flag = false;
return flag;
}
Game_Event.prototype.checkAllchaseFlags = function() {
const ES = this._SoRES;
let ret = 0b00000;
if(ES.IsDetectPlayer) ret += 0b000001;
if(ES.IsDetectBalloon) ret += 0b000010;
if(ES.IsChasePlayer) ret += 0b000100;
if(ES.IsFleePlayer) ret += 0b001000;
if(ES.IsStayAfterFlee) ret += 0b010000;
if(ES.IsReturnOriginalPos) ret += 0b100000;
return ret;
}
Game_Event.prototype.clearAllchaseFlags = function() {
const ES = this._SoRES;
ES.IsDetectPlayer = false;
ES.IsDetectBalloon = false;
ES.IsChasePlayer = false;
ES.IsFleePlayer = false;
ES.IsStayAfterFlee = false;
ES.IsReturnOriginalPos = false;
this.setMoveSpeed(ES.default_speed);
}
/////////////////////////////////////////////////////////////////
Game_Event.prototype.reactBMO_checkStanned = function() {
const ES = this._SoRES;
if(!PluginManager._scripts.includes("SoR_BreakableMapObjects_MZ")) return false;
if(!ES._BMOattacked) return false;
if(ES._BMOstanned){
if(!this._balloonPlaying) $gameTemp.requestBalloon(this, 13);
}
ES._BMOstanwait--;
if(ES._BMOstanwait>0) return true;
else if(ES._BMOattacked){
ES._BMOstanwait = 0;
ES._BMOattacked = false;
ES._BMOstanned = false;
ES.IsDetectPlayer = true; // start to chase
return false;
}
return false;
}
/////////////////////////////////////////////////////////////////
//v1.31
/////////////////////////////////////////////////////////////////////////
const SoR_ESE_GP_triggerButtonAction = Game_Player.prototype.triggerButtonAction;
Game_Player.prototype.triggerButtonAction = function() {
$gameTemp._ESE_triggerFromOK = true;//////
return SoR_ESE_GP_triggerButtonAction.call(this);
}
const SoR_ESE_GP_triggerTouchAction = Game_Player.prototype.triggerTouchAction;
Game_Player.prototype.triggerTouchAction = function() {
$gameTemp._ESE_triggerFromOK = false;
return SoR_ESE_GP_triggerTouchAction.call(this);
}
const SoR_ESE_GP_startMapEvent = Game_Player.prototype.startMapEvent;
Game_Player.prototype.startMapEvent = function(x, y, triggers, normal) {
//avoid encounter with symbol on the ground while in the sky
const cands = $gameMap.GetEventsXY_SoRESE(x, y);
for(const ev of cands){
if (ev.event().meta.EnemySymbol){
if($gamePlayer.isInAirship()) return;
if($gameTemp._ESE_triggerFromOK===true) return;
}
}
SoR_ESE_GP_startMapEvent.call(this,x, y, triggers, normal);
}
/////////////////////////////////////////////////////////////////////////
Game_Event.prototype.serachPlayer = function() {
const ES = this._SoRES;
let flag = false;
const p = {x:$gamePlayer.x,y:$gamePlayer.y,dir:$gamePlayer._direction};
const e = {x:this.x,y:this.y,dir:this._direction};
const bush_rate = $gameMap.isBush(p.x,p.y) == true ? ES.bushrate:1;
const dist = calcDist(e,p);
if(dist <= Math.ceil(ES.see_distance*bush_rate)){
const ang = calcAngle(e,p);
if(ang >= -ES.see_angle && ang <= ES.see_angle){
if(e.x-p.x!=0) flag = Bresenham(e,p);
else flag = Bresenham90(e,p);
}
}
if(FollowerAttack >=3){// walking with followers
const flws = this.AdditionalSerachForFollowers(e);
flag = flag || flws.flag;
if(flws.flag) dist = flws.dist;
}
return {dist: dist, flag: flag};
}
// for only followers
Game_Event.prototype.AdditionalSerachForFollowers = function(e) {
const ES = this._SoRES;
const flw = $gamePlayer._followers.visibleFollowers();
let flag = false;
let dist = 0;
const n_flw = flw.length;
for(let i=0; i<n_flw;i++){
let p = {x:flw[i].x,y:flw[i].y,dir:flw[i]._direction};
const bush_rate = $gameMap.isBush(p.x,p.y) == true ? ES.bushrate:1;
dist = calcDist(e,p);
if(dist <= Math.ceil(ES.see_distance*bush_rate)){
const ang = calcAngle(e,p);
if(ang >= -ES.see_angle && ang <= ES.see_angle){
if(e.x-p.x!=0) flag = flag || Bresenham(e,p);
else flag = flag || Bresenham90(e,p);
}
}
}
return {dist: dist, flag: flag};
}
function Bresenham(a1,a2,ignore){
let flag = true;
const dy = Math.abs(a2.y-a1.y);
const dx = Math.abs(a2.x-a1.x);
let err = dx-dy;
const inc_x = a2.x > a1.x ? 1 : -1;
const inc_y = a2.y > a1.y ? 1 : -1;
let cx = a1.x;
let cy = a1.y;
const dir_x = dx>0? 6:4;
const dir_y = dy>0? 2:8;
while(1){
if(!$gameMap.isPassable(cx, cy, dir_x) || !$gameMap.isPassable(cx, cy, dir_y) || $gameMap.regionId(cx, cy) == SymbolKeepOut_RegionID){
flag = false;
break;
}
if(cx==a2.x && cy==a2.y) break;
if(!(cx == a1.x && cy == a1.y) && !(cx == a2.x && cy == a2.y) && !isUnPassableEvent(cx,cy,ignore)){
flag = false;
break;
}
const e2 = err*2;
if(e2 > -dy){
err -= dy;
cx += inc_x;
}
if(e2 < dx){
err += dx;
cy += inc_y;
}
}
return flag;
}
function Bresenham90(a1,a2,ignore){
let flag = true;
const incre = a2.y > a1.y ? 1 : -1;
const cx = a1.x;
for(let cy = a1.y; ; cy+=incre){
if(!$gameMap.isPassable(cx, cy, a1.dir) || $gameMap.regionId(cx, cy) == SymbolKeepOut_RegionID){
flag = false;
break;
}
if(!(cx == a1.x && cy == a1.y) && !(cx == a2.x && cy == a2.y) && !isUnPassableEvent(cx,cy,ignore)){
flag = false;
break;
}
if(cy==a2.y) break;
}
return flag;
}
function isUnPassableEvent(x,y,ignore){
if(ignore==true) return true;//to ignore character events for bresenham
let flag = true;
$gameMap.GetEventsXY_SoRESE(x, y).forEach(function(event) {
if (event.isNormalPriority() === true) {
flag = false;
}
});
return flag;
}
Game_Event.prototype.DetectedPlayer = function() {
const ES = this._SoRES;
if(!ES.IsDetectBalloon){
if(!ES.IsReturnOriginalPos){
this.default_x = this.x;
this.default_y = this.y;
}
ES.detectedAlert_count = ES.detectedAlertDuration;
ES.IsDetectBalloon = true;
ES.IsStayAfterFlee = false;
ES.IsReturnOriginalPos = false;
//balloon
if(this.SpontaneousEvacuate && SoR_Eval(ES.EvacCond)){//evacuate
if(BalloonID_Evacuate!=-1) $gameTemp.requestBalloon(this, BalloonID_Evacuate);
if(SE_Evacuate.name!='')AudioManager.playSe(SE_Evacuate);
}
else{//regular
if(BalloonID_PlayerDetected!=-1) $gameTemp.requestBalloon(this, BalloonID_PlayerDetected);
if(SE_PlayerDetected.name!='')AudioManager.playSe(SE_PlayerDetected);
}
//common ev
if(ES.call_CommonEventID[0] != 0) $gameTemp.reserveCommonEvent(ES.call_CommonEventID[0]);
if(ES.call_ScriptsInPhases[0] != null) this.SoR_ExecScript(ES.call_ScriptsInPhases[0]);
}
ES.detectedAlert_count--;
if(ES.detectedAlert_count < 0){
ES.IsDetectBalloon = false;
this.InitPlayerChase();
}
}
Game_Event.prototype.InitPlayerChase = function() {
const ES = this._SoRES;
this.setMoveSpeed(ES.chase_speed);
ES.IsChasePlayer = true;
ES.chase_time = ES.chase_duration;
ES.trails = [];
if(ES.call_CommonEventID[1] != 0) $gameTemp.reserveCommonEvent(ES.call_CommonEventID[1]);
if(ES.call_ScriptsInPhases[1] != null) this.SoR_ExecScript(ES.call_ScriptsInPhases[1]);
if(!ES.SpontaneousEvacuate || !SoR_Eval(ES.EvacCond)){
if(ES.bullmove){
this.setMoveSpeed(1);
this.setDirectionFix(true);
ES._bullaxis=-1;
ES._bullcounter=0;
}
else if(ES.leapmove){
ES.leapduration = 180;
}
}
}
Game_Event.prototype.PlayerChaser = function(state) {
const ES = this._SoRES;
const d = state.dist
let isChaseFeasible = true;
if(ES.chase_time >= 0){
//make route
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.AstarDir($gamePlayer.x, $gamePlayer.y, ES.see_distance *($gameMap.isBush($gamePlayer.x,$gamePlayer.y) == true ? ES.bushrate:1));///d
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
const direction = this.MakeDir(ES.trails[0]);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2(direction,ES.trails[0]);
if(d>=2 && pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6) this.moveDiagonally(direction,dir2);
else this.moveDiagonally(dir2,direction);
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0) this.moveStraight(direction);
}
else if(direction > 0) this.moveStraight(direction);
}
else isChaseFeasible = false; // player missing
ES.chase_time--;
}
if(ES.chase_time < 0 && isChaseFeasible && d <= ES.see_distance/3) ES.chase_time=10;
if(ES.chase_time < 0 || !isChaseFeasible){
ES.IsDetectPlayer = false;
ES.IsChasePlayer = false;
ES.IsFleePlayer = true;
}
}
Game_Event.prototype.BullPlayerChaser = function(state) {
const ES = this._SoRES;
const d = state.dist
let isChaseFeasible = true;
if(ES._bullcounter<90){//charge
ES._bullaxis = Math.abs($gamePlayer.x-this.x) <= Math.abs($gamePlayer.y-this.y)? $gamePlayer.x>this.x? 1:2 : $gamePlayer.y>this.y? -1:-2;
const destx = ES._bullaxis>0? $gamePlayer.x : this.x;// xe=xp
const desty = ES._bullaxis<0? $gamePlayer.y : this.y;// ye=yp
//make route
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.AstarDir(destx, desty, 10);///d
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
const direction = this.MakeDir(ES.trails[0]);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2(direction,ES.trails[0]);
if(pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6) this.moveDiagonally(direction,dir2);
else this.moveDiagonally(dir2,direction);
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0) this.moveStraight(direction);
}
else if(direction > 0) this.moveStraight(direction);
}
else ES._bullcounter++; // go bull fast
ES._bullcounter++;
if(ES._bullcounter >= 90){
ES.trails = [];
}
}
else if(ES.chase_time >= 0){//bull
//make route
this.setDirectionFix(false);
this.setMoveSpeed(ES.chase_speed);
const destx = ES._bullaxis<0? $gamePlayer.x + ($gamePlayer.x>this.x?0:-0): this.x;
const desty = ES._bullaxis>0? $gamePlayer.y + ($gamePlayer.y>this.y?0:-0) : this.y;
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.AstarDir(destx, desty, 10);///d
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
const direction = this.MakeDir(ES.trails[0]);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2(direction,ES.trails[0]);
if(pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6) this.moveDiagonally(direction,dir2);
else this.moveDiagonally(dir2,direction);
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0) this.moveStraight(direction);
}
else if(direction > 0) this.moveStraight(direction);
}
else isChaseFeasible = false; // player missing
ES.chase_time--;
}
if(ES.chase_time < 0 ){
ES.IsDetectPlayer = false;
ES.IsChasePlayer = false;
ES.IsStayAfterFlee = false;
ES.IsFleePlayer = true;
if(ES.Isreturn_afterwait){
ES.IsReturnOriginalPos = true;
ES.trails = [];
ES.trails_n = 0;
}
}
}
Game_Event.prototype.LeapPlayerChaser = function(state) {
const ES = this._SoRES;
const d = state.dist
let isChaseFeasible = true;
//leapduration
ES.leapduration--;
if(ES.chase_time >= 0){
//make route
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.AstarDir($gamePlayer.x, $gamePlayer.y, ES.see_distance);///d
if(ES.trails.length>=3) ES.trails.pop();
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
if(ES.leapduration<=0){//wait leap
let testx = this._x;
let testy = this._y;
$gameTemp.requestAnimation([this], ES.leapanim);
while(ES.trails.length >= 1){
const direction = this.MakeDirTest(ES.trails[0],testx,testy);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2Test(direction,ES.trails[0],testx,testy);
if(pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6){
const test = this.moveDiagonalTest(direction,dir2,testx,testy);
testx = test.x; testy = test.y;
}
else{
const test = this.moveDiagonalTest(dir2,direction,testx,testy);
testx = test.x; testy = test.y;
}
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0){
const test = this.moveStraightTest(direction,testx,testy);
testx = test.x; testy = test.y;
}
}
else if(direction > 0){
const test = this.moveStraightTest(direction,testx,testy);
testx = test.x; testy = test.y;
}
}
this.setPosition(testx,testy);
ES.leapduration=180;
}
}
else isChaseFeasible = false; // player missing
if(ES.leapduration<=0) ES.chase_time--;
}
if(ES.chase_time < 0 && isChaseFeasible && d <= ES.see_distance/3) ES.chase_time=10;
if(ES.chase_time < 0 || !isChaseFeasible){
ES.IsDetectPlayer = false;
ES.IsChasePlayer = false;
ES.IsFleePlayer = true;
}
}
Game_Event.prototype.EvacuateFromPlayer = function(state) {
const ES = this._SoRES;
const d = state.dist
let isChaseFeasible = true;
if(ES.chase_time >= 0){
//make route
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.calcEvacuateDestination();
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
const direction = this.MakeDir(ES.trails[0]);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2(direction,ES.trails[0]);
if(pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6) this.moveDiagonally(direction,dir2);
else this.moveDiagonally(dir2,direction);
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0) this.moveStraight(direction);
}
else if(direction > 0) this.moveStraight(direction);
}
ES.chase_time--;
}
if(ES.chase_time < 0 ){
ES.IsDetectPlayer = false;
ES.IsChasePlayer = false;
ES.IsStayAfterFlee = false;
ES.IsFleePlayer = true;
if(ES.Isreturn_afterwait){
ES.IsReturnOriginalPos = true;
ES.trails = [];
ES.trails_n = 0;
}
}
}
Game_Event.prototype.calcEvacuateDestination = function() {
//temporary (BFS&BFS => DP in the future work)
const symbolbfs = [];
const playerbfs = [];
const candidates = [];
const MaxSteps = 8;
playerbfs.unshift({x: $gamePlayer.x, y: $gamePlayer.y});
symbolbfs.unshift({x: this.x, y: this.y, dist: 0});
const currentdist = this.AstarDirTest(symbolbfs[0].x, symbolbfs[0].y, playerbfs[0].x, playerbfs[0].y);
symbolbfs[0].dist = currentdist.length;
for(let steps=0; steps<MaxSteps; steps++){
const n_plevel = playerbfs.length;
for(let i=0;i<n_plevel;i++){
const nexts = playerbfs[n_plevel-1];
if(!nexts) continue;
let shiftcandidate = false;
for(let dir=2; dir<=8; dir+=2){
const x2 = this.GetMovedirXPos_SoRESE(nexts.x, dir);
const y2 = this.GetMovedirYPos_SoRESE(nexts.y, dir);
if (!this.IsEnablePassAdjcentCells(nexts.x, nexts.y, x2, y2, dir)) continue;
if(playerbfs.some((v)=>v.x==x2&&v.y==y2)) continue;
shiftcandidate = true;
playerbfs.unshift({x: x2, y: y2});
}
if(shiftcandidate == true) playerbfs.pop();
}
const n_elevel = symbolbfs.length;
for(let i=0;i<n_plevel;i++){
const nexts = symbolbfs[n_elevel-1];
if(!nexts) continue;
candidates.unshift({x: nexts.x, y: nexts.y, s: steps+1});
let shiftcandidate = false;
for(let dir=2; dir<=8; dir+=2){
const x2 = this.GetMovedirXPos_SoRESE(nexts.x, dir);
const y2 = this.GetMovedirYPos_SoRESE(nexts.y, dir);
if (!this.IsEnablePassAdjcentCells(nexts.x, nexts.y, x2, y2, dir)) continue;
if(symbolbfs.some((v)=>v.x==x2&&v.y==y2)) continue;
if(playerbfs.some((v)=>v.x==x2&&v.y==y2)) continue;
const n_plevel = playerbfs.length;
let mindist = 999999;
let minid = -1;
let i=0;
for(const p of playerbfs){
const dist = calcDist(x2, y2, p.x, p.y);
if(mindist > dist){
mindist = dist;
minid = i;
}
i++;
}
if(currentdist > mindist) continue;
symbolbfs.unshift({x: x2, y: y2, dist: mindist});
if(steps<MaxSteps-1) shiftcandidate = true;
}
if(shiftcandidate == true) candidates.shift();
symbolbfs.pop();
}
}
const ncand = candidates.length;
let candid = 0;
let candmax = candidates[0];
for(i=1; i<ncand; i++){
if(candmax < candidates[i] && Math.random()<0.7){
candmax = candidates[i];
candid = i;
}
}
const cand = candidates[candid];
if(cand) return this.AstarDir(cand.x, cand.y, cand.s);
return this.AstarDir(this.x, this.y, 1);
}
Game_Event.prototype.FleePlayer = function() {
const ES = this._SoRES;
if(!ES.IsStayAfterFlee){
ES.waitAC_count = ES.wait_afterchase;
ES.IsStayAfterFlee = true;
//balloon
if(BalloonID_PlayerFled != -1) $gameTemp.requestBalloon(this, BalloonID_PlayerFled);
if(SE_PlayerFled.se!='' && this.isNearTheScreen()) AudioManager.playSe(SE_PlayerFled);
if(ES.call_CommonEventID[2] != 0) $gameTemp.reserveCommonEvent(ES.call_CommonEventID[2]);
if(ES.call_ScriptsInPhases[2] != null) this.SoR_ExecScript(ES.call_ScriptsInPhases[2]);
this.setMoveSpeed(ES.default_speed);
}
ES.waitAC_count--;
if(ES.waitAC_count < 0){
ES.IsStayAfterFlee = false;
ES.IsFleePlayer = false;
if(ES.Isreturn_afterwait){
ES.IsReturnOriginalPos = true;
ES.trails = [];
ES.trails_n = 0;
}
}
}
Game_Event.prototype.ReturnOrigPoint = function(d) {
const ES = this._SoRES;
if((ES.trails.length == 0 || ES.trails_n >= ES.updateRoute_interval)){
ES.trails = this.AstarDir(this.default_x, this.default_y, 99);
ES.trails_n = 0;
}
if(ES.trails.length >= 1){// trail the player
const direction = this.MakeDir(ES.trails[0]);
ES.trails_n++;
ES.trails.shift();
if(ES.Dir8moveChase && ES.trails.length >= 1){
const pattern = [8, 12, 32, 48];
const dir2 = this.MakeDir2(direction,ES.trails[0]);
if(pattern.some(x=> x==direction*dir2)){////8dir
if(direction== 4 || direction == 6) this.moveDiagonally(direction,dir2);
else this.moveDiagonally(dir2,direction);
ES.trails_n++;
ES.trails.shift();
}
else if(direction > 0) this.moveStraight(direction);
}
else if(direction > 0) this.moveStraight(direction);
}
if(this.x == this.default_x && this.y == this.default_y){
ES.IsReturnOriginalPos = false;
if(ES.call_CommonEventID[4] != 0) $gameTemp.reserveCommonEvent(ES.call_CommonEventID[4]);
if(ES.call_ScriptsInPhases[4] != null) this.SoR_ExecScript(ES.call_ScriptsInPhases[4]);
}
}
function calcDist(e,p){
return Math.sqrt((e.x-p.x)*(e.x-p.x)+(e.y-p.y)*(e.y-p.y));
}
function calcAngle(e,p){
const va = {x: p.x-e.x, y: p.y-e.y};
let x = 2, n_op = 0;
//Cyclic subgroup generated by 2 \in Group(Z_{10}-{0},*)
while(x!=e.dir){
x = (x*2)%10;
n_op++;
}
const vec = {z: 0, c: 1};
//(C,*) isomorphism to sub of Z[i] ~ rotate90
for(let i=0; i<n_op;i++){
const tmp = -vec.c;
vec.c = vec.z;
vec.z = tmp;
}
const theta = Math.acos((vec.z*va.x+vec.c*va.y)/Math.sqrt(va.x*va.x+va.y*va.y));
if( vec.z*va.y - vec.c*va.x < 0) return -theta;
else return theta;// -pi < theta < pi
}
Game_Character.prototype.AstarDir = function(goalX, goalY, dist) {
const searchLimit = Math.floor(dist*SearchRange_scale);
const mapWidth = $gameMap.width();
let nodeList = [];
let openList = [];
let closedList = [];
let start = {};
let best = start;
if (this.x === goalX && this.y === goalY) return [];
start.parent = null;
start.x = this.x;
start.y = this.y;
start.g = 0;
start.f = dist;
nodeList.push(start);
openList.push(start.y * mapWidth + start.x);
while (nodeList.length > 0) {
let bestIndex = 0;
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i].f < nodeList[bestIndex].f) {
bestIndex = i;
}
}
const current = nodeList[bestIndex];
const x1 = current.x;
const y1 = current.y;
const pos1 = y1 * mapWidth + x1;
const g1 = current.g;
nodeList.splice(bestIndex, 1);
openList.splice(openList.indexOf(pos1), 1);
closedList.push(pos1);
if (current.x === goalX && current.y === goalY){
best = current;
break;
}
if (g1 >= searchLimit) continue;
for (let j = 0; j < 4; j++) {
const direction = 2 + j * 2;
const x2 = this.GetMovedirXPos_SoRESE(x1, direction);
const y2 = this.GetMovedirYPos_SoRESE(y1, direction);
const pos2 = y2 * mapWidth + x2;
if (closedList.contains(pos2)) continue;
if (!this.IsEnablePassAdjcentCells(x1, y1, x2, y2, direction)) continue;
const g2 = g1 + 1;
const index2 = openList.indexOf(pos2);
if (index2 < 0 || g2 < nodeList[index2].g) {
let neighbor;
if (index2 >= 0) neighbor = nodeList[index2];
else {
neighbor = {};
nodeList.push(neighbor);
openList.push(pos2);
}
neighbor.parent = current;
neighbor.x = x2;
neighbor.y = y2;
neighbor.g = g2;
neighbor.f = g2 + $gameMap.distance(x2, y2, goalX, goalY);
if (!best || neighbor.f - neighbor.g < best.f - best.g) best = neighbor;
}
}
}
let trails = [];
let goal = {x: goalX, y: goalY};
if(calcDist(best,goal)==0){
for(node = best; node.parent; node = node.parent){
trails.unshift(node);
if(node.parent === start) break;
}
}
return trails;
}
Game_Character.prototype.AstarDirTest = function(startX, startY, goalX, goalY) {
const searchLimit = 8;
const mapWidth = $gameMap.width();
let nodeList = [];
let openList = [];
let closedList = [];
let start = {};
let best = start;
if (startX === goalX && startY === goalY) return 0;
start.parent = null;
start.x = startX;
start.y = startY;
start.g = 0;
start.f = 4;
nodeList.push(start);
openList.push(start.y * mapWidth + start.x);
while (nodeList.length > 0) {
let bestIndex = 0;
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i].f < nodeList[bestIndex].f) {
bestIndex = i;
}
}
const current = nodeList[bestIndex];
const x1 = current.x;
const y1 = current.y;
const pos1 = y1 * mapWidth + x1;
const g1 = current.g;
nodeList.splice(bestIndex, 1);
openList.splice(openList.indexOf(pos1), 1);
closedList.push(pos1);
if (current.x === goalX && current.y === goalY){
best = current;
break;
}
if (g1 >= searchLimit) continue;
for (let j = 0; j < 4; j++) {
const direction = 2 + j * 2;
const x2 = this.GetMovedirXPos_SoRESE(x1, direction);
const y2 = this.GetMovedirYPos_SoRESE(y1, direction);
const pos2 = y2 * mapWidth + x2;
if (closedList.contains(pos2)) continue;
if (!this.IsEnablePassAdjcentCells(x1, y1, x2, y2, direction)) continue;
const g2 = g1 + 1;
const index2 = openList.indexOf(pos2);
if (index2 < 0 || g2 < nodeList[index2].g) {
let neighbor;
if (index2 >= 0) neighbor = nodeList[index2];
else {
neighbor = {};
nodeList.push(neighbor);
openList.push(pos2);
}
neighbor.parent = current;
neighbor.x = x2;
neighbor.y = y2;
neighbor.g = g2;
neighbor.f = g2 + $gameMap.distance(x2, y2, goalX, goalY);
if (!best || neighbor.f - neighbor.g < best.f - best.g) best = neighbor;
}
}
}
let trails = [];
let goal = {x: goalX, y: goalY};
if(calcDist(best,goal)==0){
for(node = best; node.parent; node = node.parent){
trails.unshift(node);
if(node.parent === start) break;
}
}
return trails.length;
}
//extention of canPass
Game_Event.prototype.IsEnablePassAdjcentCells = function(x, y, x2, y2, d) {
if (!$gameMap.isValid(x2, y2)) return false;
//keep out by plugin parameter
if($gameMap.regionId(x, y) == SymbolKeepOut_RegionID) return false;
if($gameMap.regionId(x2, y2) == SymbolKeepOut_RegionID) return false;
if (this.isThrough()) return true;
if (!this.isMapPassable(x, y, d)) return false;
if ( FollowerAttack % 3 == 0 && this.isCollidedWithFollowerCharacters(x,y)) return false;
if (this.isCollidedWithEventObjects(x2, y2)) return false; // except for the player
return true;
}
Game_Event.prototype.IsEnablePassDiagonallyAdjcentCells = function(x, y, horz, vert) {
const x2 = $gameMap.roundXWithDirection(x, horz);
const y2 = $gameMap.roundYWithDirection(y, vert);
if (this.IsEnablePassAdjcentCells(x, y, x2, y2, vert) && this.IsEnablePassAdjcentCells(x, y, x2, y2, horz)) {
return true;
}
if (this.IsEnablePassAdjcentCells(x, y, x2, y2, horz) && this.IsEnablePassAdjcentCells(x, y, x2, y2, vert)) {
return true;
}
return false;
}
Game_Event.prototype.isCollidedWithEventObjects = function(x, y) {
return Game_Character.prototype.isCollidedWithCharacters.call(this, x, y);
}
Game_Character.prototype.MakeDir = function(node) {
const deltaX1 = $gameMap.deltaX(node.x, this.x);
const deltaY1 = $gameMap.deltaY(node.y, this.y);
if (deltaY1 == 1) return 2;
else if (deltaX1 == -1) return 4;
else if (deltaX1 == 1) return 6;
else if (deltaY1 == -1) return 8;
else return 0;
}
Game_Character.prototype.MakeDir2 = function(next,node) {
let paddx = 0, paddy = 0;
paddx = next==6? 1 : next==4? -1 : 0;
paddy = next==2? 1 : next==8? -1 : 0;
const deltaX1 = $gameMap.deltaX(node.x, this.x+paddx);
const deltaY1 = $gameMap.deltaY(node.y, this.y+paddy);
if (deltaY1 == 1) return 2;
else if (deltaX1 == -1) return 4;
else if (deltaX1 == 1) return 6;
else if (deltaY1 == -1) return 8;
else return 0;
}
Game_Map.prototype.isEventTriggered = function() {
return this._interpreter.isRunning();
}
const SoR_ESE_GP_updateVehicleGetOn = Game_Player.prototype.updateVehicleGetOn;
Game_Player.prototype.updateVehicleGetOn = function() {
//force quit invincinble for enemy symbol
if($gameTemp.invincibleAfterEnc && $gameTemp.invincibleAfterEnc != -1) $gamePlayer.FinishInvinsibleEffectsAfterSE();
SoR_ESE_GP_updateVehicleGetOn.call(this);
}
////////////////////////////////////////////////////
//
// Option for follower attack by enemy symbols
//
////////////////////////////////////////////////////
if(FollowerAttack%3 == 1){
const SoR_ESE_GE_checkEventTriggerTouch = Game_Event.prototype.checkEventTriggerTouch;
Game_Event.prototype.checkEventTriggerTouch = function(x, y) {
if(this.event().meta.EnemySymbol){
if (!$gameMap.isEventRunning()) {
let flw = $gamePlayer._followers.visibleFollowers();
let isF_collided = false;
const n_flw = flw.length;
for(let i=0; i<n_flw;i++){
isF_collided = flw[i].pos(x,y);
if(isF_collided==true){
$gameTemp.collisionFollower = flw[i];
break;
}
}
if (this._trigger === 2 && isF_collided) {
if (!this.isJumping() && this.isNormalPriority()) {
this.start();
}
}
}
}
SoR_ESE_GE_checkEventTriggerTouch.call(this, x, y);
}
}
else if(FollowerAttack%3 == 2){
const SoR_ESE_GE_isCollidedWithPlayerCharacters = Game_Event.prototype.isCollidedWithPlayerCharacters;
Game_Event.prototype.isCollidedWithPlayerCharacters = function (x, y) {
if(this.event().meta.EnemySymbol){
return this.isNormalPriority() && !$gamePlayer.isThrough() && $gamePlayer.pos(x, y);
}
return SoR_ESE_GE_isCollidedWithPlayerCharacters.call(this,...arguments);
}
}
Game_Event.prototype.isCollidedWithFollowerCharacters = function(x, y) {
let flw = $gamePlayer._followers.visibleFollowers();
let isF_collided = false;
const n_flw = flw.length;
for(let i=0; i<n_flw;i++){
isF_collided = flw[i].pos(x,y);
if(isF_collided==true){
return true;
}
}
return isF_collided;
}
///////////////////////////////////////////////////////////
//test for leap move
Game_Character.prototype.MakeDirTest = function(node,x,y) {
const deltaX1 = $gameMap.deltaX(node.x, x);
const deltaY1 = $gameMap.deltaY(node.y, y);
if (deltaY1 == 1) return 2;
else if (deltaX1 == -1) return 4;
else if (deltaX1 == 1) return 6;
else if (deltaY1 == -1) return 8;
else return 0;
}
Game_Character.prototype.MakeDir2Test = function(next,node,x,y) {
let paddx = 0, paddy = 0;
paddx = next==6? 1 : next==4? -1 : 0;
paddy = next==2? 1 : next==8? -1 : 0;
const deltaX1 = $gameMap.deltaX(node.x, x+paddx);
const deltaY1 = $gameMap.deltaY(node.y, y+paddy);
if (deltaY1 == 1) return 2;
else if (deltaX1 == -1) return 4;
else if (deltaX1 == 1) return 6;
else if (deltaY1 == -1) return 8;
else return 0;
}
Game_CharacterBase.prototype.moveDiagonalTest = function(horz, vert , x, y) {
let test = {x, y};
if (this.IsEnablePassDiagonallyAdjcentCells(x, y, horz, vert)) {
test.x = this.GetMovedirXPos_SoRESE(test.x, horz);
test.y = this.GetMovedirYPos_SoRESE(test.y, vert);
}
return test;
}
Game_CharacterBase.prototype.moveStraightTest = function(d, x, y) {
let test = {x, y};
if (this.canPass(x, y, d)) {
test.x = this.GetMovedirXPos_SoRESE(test.x, d);
test.y = this.GetMovedirYPos_SoRESE(test.y, d);
}
return test;
}
///////////////////////////////////////////////////////
//
// Treatment for MenuSubCommand.js
//
///////////////////////////////////////////////////////
Game_System.prototype.SoRSE_InvincibleExcept = function(){
return true;
}
//TODO
//Find an isomorphic function {2,4,8,6} to {i,-1,-i,1}
////////////////////////////////////////////////////////
const SoR_SSE_DM_setupNewGame = DataManager.setupNewGame;
DataManager.setupNewGame = function() {
SoR_SSE_DM_setupNewGame.call(this);
if(AutoDisable_DefaultEncounter) $gameSystem.disableEncounter();
}
////////////////////////////////////////////////////////
Game_Party.prototype.AverageLv = function() {
let avg = 0;
const npt = $gameParty.size();
for(x of $gameParty.members()){
avg += x.level;
}
return Math.floor(avg/npt);
}
////////////////////////////////////////////////////////
// v1.61 Tri_HalfMove
////////////////////////////////////////////////////////
Game_Character.prototype.GetMovedirXPos_SoRESE = function(x,dir){
return $gameMap.roundXWithDirection(x, dir);
}
Game_Character.prototype.GetMovedirYPos_SoRESE = function(y,dir){
return $gameMap.roundYWithDirection(y, dir);
}
Game_Map.prototype.GetEventsXY_SoRESE = function(x,y){
return $gameMap.eventsXy(x, y);
}
////////////////////////////////////////////////////////
function SoR_Eval(ev) {
const sentence = "return (" + ev + ");";
if(typeof $gameTemp.SoRTmp_script === "undefined") $gameTemp.SoRTmp_script = new Map();
if(!$gameTemp.SoRTmp_script.has(sentence)){
$gameTemp.SoRTmp_script.set(sentence, new Function(sentence));
}
const res = $gameTemp.SoRTmp_script.get(sentence)();
return res;
}
Game_Event.prototype.SoR_ExecScript = function(ev){
let sentence = ev;
sentence = sentence.replace(/EVID/g, this._eventId);
sentence = sentence.replace(/MAPID/g, this._mapId);
sentence = sentence.replace(/PAGEID/g, this._pageIndex);
sentence = sentence.replace(/selfON\((.*)\)/g, (_, p1) => {
const tx = "$gameSelfSwitches.setValue(["+ this._mapId +","+ this._eventId +",\""+ String(p1).trim() +"\"],"+ true + ")";
return tx;
});
sentence = sentence.replace(/selfOFF\((.*)\)/g, (_, p1) => {
const tx = "$gameSelfSwitches.setValue(["+ this._mapId +","+ this._eventId +",\""+ String(p1).trim() +"\"],"+ false + ")";
return tx;
});
sentence = sentence.replace(/delEV\(\)/g, "$gameMap.eraseEvent("+ this._eventId + ")");
if(typeof $gameTemp.SoRTmp_script === "undefined") $gameTemp.SoRTmp_script = new Map();
if(!$gameTemp.SoRTmp_script.has(sentence)){
$gameTemp.SoRTmp_script.set(sentence, new Function(sentence));
}
const res = $gameTemp.SoRTmp_script.get(sentence)();
return res;
}
})();