slave-princess-charlotte/data/Script/attack/attack_core.js
2025-10-12 13:17:46 -05:00

541 lines
15 KiB
JavaScript

// このオブジェクトはPreAttackから使用される
var CoreAttack = defineObject(BaseObject,
{
_attackParam: null,
_battleType: 0,
_isUnitLostEventShown: false,
_attackFlow: null,
_battleObject: null,
_isBattleCut: false,
enterCoreAttackCycle: function(attackParam) {
this._prepareMemberData(attackParam);
return this._completeMemberData(attackParam);
},
moveCoreAttackCycle: function() {
if (this._battleObject.isBattleSkipAllowed() && InputControl.isStartAction()) {
this._doSkipAction();
return MoveResult.END;
}
else {
return this._battleObject.moveBattleCycle();
}
},
drawCoreAttackCycle: function() {
this._battleObject.drawBattleCycle();
},
backCoreAttackCycle: function() {
if (root.getEventCommandType() !== EventCommandType.FORCEBATTLE) {
this._battleObject.backBattleCycle();
}
},
isRealBattle: function() {
return this._battleType === BattleType.REAL;
},
getAttackFlow: function() {
return this._attackFlow;
},
getBattleObject: function() {
return this._battleObject;
},
isUnitLostEventShown: function() {
return this._isUnitLostEventShown;
},
recordUnitLostEvent: function(isShown) {
this._isUnitLostEventShown = isShown;
},
isBattleCut: function() {
return this._isBattleCut;
},
_prepareMemberData: function(attackParam) {
this._attackParam = attackParam;
this._battleType = 0;
this._isUnitLostEventShown = false;
this._attackFlow = createObject(AttackFlow);
this._battleObject = null;
},
_completeMemberData: function(attackParam) {
var result = EnterResult.CONTINUE;
this._checkAttack(attackParam);
if (CurrentMap.isCompleteSkipMode()) {
// 戦闘をスキップする場合は、この時点で戦闘を終わらす
this._finalizeAttack();
result = EnterResult.NOTENTER;
}
else {
this._playAttackStartSound();
this._battleObject.openBattleCycle(this);
}
return result;
},
_checkAttack: function() {
if (this._attackParam.attackStartType === AttackStartType.NORMAL) {
this._startNormalAttack();
}
else if (this._attackParam.attackStartType === AttackStartType.FORCE) {
this._startForceAttack();
}
},
_startNormalAttack: function() {
var infoBuilder = createObject(NormalAttackInfoBuilder);
var orderBuilder = createObject(NormalAttackOrderBuilder);
var attackInfo = infoBuilder.createAttackInfo(this._attackParam);
var attackOrder = orderBuilder.createAttackOrder(attackInfo);
return this._startCommonAttack(attackInfo, attackOrder);
},
_startForceAttack: function() {
var infoBuilder = createObject(ForceAttackInfoBuilder);
var orderBuilder = createObject(ForceAttackOrderBuilder);
var attackInfo = infoBuilder.createAttackInfo(this._attackParam);
var attackOrder = orderBuilder.createAttackOrder(attackInfo);
return this._startCommonAttack(attackInfo, attackOrder);
},
_startCommonAttack: function(attackInfo, attackOrder) {
this._setBattleTypeAndObject(attackInfo, attackOrder);
this._attackFlow.setAttackInfoAndOrder(attackInfo, attackOrder, this);
},
_finalizeAttack: function() {
this._attackFlow.finalizeAttack();
},
_doSkipAction: function() {
this._isBattleCut = true;
this._attackFlow.finalizeAttack();
this._battleObject.endBattle();
this._isBattleCut = false;
},
_setBattleTypeAndObject: function(attackInfo, attackOrder) {
var battleType = attackInfo.battleType;
var unitSrc = this._attackParam.unit;
var unitDest = this._attackParam.targetUnit;
if (battleType === BattleType.FORCEREAL) {
this._battleType = BattleType.REAL;
}
else if (battleType === BattleType.FORCEEASY) {
this._battleType = BattleType.EASY;
}
else {
// 強制戦闘でない同盟軍の戦闘は、isAllyBattleFixedがtrueを返す場合に簡易戦闘となる
if ((unitSrc.getUnitType() === UnitType.ALLY || unitDest.getUnitType() === UnitType.ALLY) && DataConfig.isAllyBattleFixed()) {
this._battleType = BattleType.EASY;
}
else {
this._battleType = battleType;
}
}
if (!DataConfig.isMotionGraphicsEnabled()) {
// リアル戦闘オフの場合は無条件に簡易戦闘
this._battleType = BattleType.EASY;
}
else {
if (this._isAnimeEmpty(unitSrc, unitDest)) {
// ユニットにアニメが設定されていない場合は簡易戦闘
this._battleType = BattleType.EASY;
}
}
if (this._battleType === BattleType.REAL) {
this._battleObject = createObject(RealBattle);
}
else {
this._battleObject = createObject(EasyBattle);
}
},
_isAnimeEmpty: function(unitSrc, unitDest) {
var animeSrc = BattlerChecker.findBattleAnimeFromUnit(unitSrc);
var animeDest = BattlerChecker.findBattleAnimeFromUnit(unitDest);
return animeSrc === null || animeDest === null;
},
_playAttackStartSound: function() {
if (this.isRealBattle()) {
// リアル戦闘では攻撃の開始音を再生
MediaControl.soundDirect('attackstart');
}
}
}
);
var BaseAttackInfoBuilder = defineObject(BaseObject,
{
createAttackInfo: function(attackParam) {
var picBackground;
var unitSrc = attackParam.unit;
var unitDest = attackParam.targetUnit;
var attackInfo = StructureBuilder.buildAttackInfo();
var mapInfo = root.getCurrentSession().getCurrentMapInfo();
var terrain = PosChecker.getTerrainFromPosEx(unitDest.getMapX(), unitDest.getMapY());
var terrainLayer = PosChecker.getTerrainFromPos(unitDest.getMapX(), unitDest.getMapY());
var direction = PosChecker.getSideDirection(unitSrc.getMapX(), unitSrc.getMapY(), unitDest.getMapX(), unitDest.getMapY());
// 背景画像を取得する。
// レイヤーが背景を持たない場合は下地から取得する。
picBackground = terrainLayer.getBattleBackgroundImage(mapInfo.getMapColorIndex());
if (picBackground === null) {
picBackground = terrain.getBattleBackgroundImage(mapInfo.getMapColorIndex());
}
attackInfo.unitSrc = unitSrc;
attackInfo.unitDest = unitDest;
attackInfo.terrainLayer = terrainLayer;
attackInfo.terrain = terrain;
attackInfo.picBackground = picBackground;
attackInfo.isDirectAttack = direction !== DirectionType.NULL;
attackInfo.isCounterattack = AttackChecker.isCounterattack(unitSrc, unitDest);
this._setMagicWeaponAttackData(attackInfo);
return attackInfo;
},
_setMagicWeaponAttackData: function(attackInfo) {
attackInfo.checkMagicWeaponAttack = function(unit) {
var result;
if (unit === this.unitSrc) {
result = this.isMagicWeaponAttackSrc;
}
else {
result = this.isMagicWeaponAttackDest;
}
return result;
};
if (this._isMagicWeaponAttackAllowed(attackInfo)) {
attackInfo.isMagicWeaponAttackSrc = this._isMotionEnabled(attackInfo, attackInfo.unitSrc);
attackInfo.isMagicWeaponAttackDest = this._isMotionEnabled(attackInfo, attackInfo.unitDest);
}
},
_isMotionEnabled: function(attackInfo, unit) {
var midData = MotionIdControl.createEmptyMotionIdData();
midData.unit = unit;
midData.weapon = BattlerChecker.getRealBattleWeapon(unit);
midData.cls = BattlerChecker.getRealBattleClass(unit, midData.weapon);
midData.attackTemplateType = BattlerChecker.findAttackTemplateType(midData.cls, midData.weapon);
// 「戦士系」でない場合は、「魔法武器攻撃」という概念は存在しない
if (midData.attackTemplateType !== AttackTemplateType.FIGHTER) {
return false;
}
if (midData.weapon === null) {
return false;
}
// 装備武器の「武器エフェクト」において、「魔法武器」が設定されているか調べる
if (WeaponEffectControl.getAnime(unit, WeaponEffectAnime.MAGICWEAPON) === null) {
return false;
}
// 「魔法武器攻撃」のための「モーション情報」が設定されているか調べる
return this._getMagicWeaponAttackId(midData, false) !== MotionIdValue.NONE;
},
_getMagicWeaponAttackId: function(midData, isCritical) {
midData.isCritical = isCritical;
MotionIdControl.getMagicWeaponAttackId(midData);
return midData.id;
},
_isMagicWeaponAttackAllowed: function(attackInfo) {
if (root.getAnimePreference().isDirectMagicWeaponAttackAllowed()) {
// 直接でも間接でも「魔法武器攻撃」が考慮される
return true;
}
// 間接時のみ「魔法武器攻撃」が考慮される
return !attackInfo.isDirectAttack;
}
}
);
var NormalAttackInfoBuilder = defineObject(BaseAttackInfoBuilder,
{
createAttackInfo: function(attackParam) {
var attackInfo = BaseAttackInfoBuilder.createAttackInfo.call(this, attackParam);
attackInfo.battleType = EnvironmentControl.getBattleType();
attackInfo.isPosBaseAttack = true;
return attackInfo;
}
}
);
var ForceAttackInfoBuilder = defineObject(BaseAttackInfoBuilder,
{
createAttackInfo: function(attackParam) {
var forceBattleObject = attackParam.forceBattleObject;
var attackInfo = BaseAttackInfoBuilder.createAttackInfo.call(this, attackParam);
attackInfo.battleType = this._getBattleType(forceBattleObject);
attackInfo.forceBattleObject = forceBattleObject;
// 強制戦闘を示す値を設定
attackInfo.attackStartType = AttackStartType.FORCE;
// 経験値を考慮するかどうかを設定
attackInfo.isExperienceEnabled = forceBattleObject.isExperienceEnabled();
return attackInfo;
},
_getBattleType: function(forceBattleObject) {
var n, battleType;
n = forceBattleObject.getBattleType();
if (n === 0) {
battleType = BattleType.FORCEREAL;
}
else if (n === 1) {
battleType = BattleType.FORCEEASY;
}
else {
battleType = EnvironmentControl.getBattleType();
}
return battleType;
}
}
);
// 主要オブジェクトを外部から取得できるようにする
var AttackControl = {
_preAttack: null,
setPreAttackObject: function(preAttack) {
this._preAttack = preAttack;
},
isAttack: function() {
return this._preAttack !== null;
},
getPreAttackObject: function() {
return this._preAttack;
},
getCoreAttack: function() {
return this.getPreAttack().getCoreAttack();
},
getAttackParam: function() {
return this.getPreAttack().getAttackParam();
},
getAttackFlow: function() {
return this.getCoreAttack().getAttackFlow();
},
getBattleObject: function() {
return this.getCoreAttack().getBattleObject();
},
getAttackOrder: function() {
return this.getAttackFlow().getAttackOrder();
},
getAttackInfo: function() {
return this.getAttackFlow().getAttackInfo();
}
};
// リアル戦闘用補助オブジェクト
var BattlerChecker = {
findBattleAnimeFromUnit: function(unit) {
var weapon = this.getRealBattleWeapon(unit);
var cls = this.getRealBattleClass(unit, weapon);
return this.findBattleAnime(cls, weapon);
},
findBattleAnime: function(cls, weapon) {
var anime;
if (weapon !== null) {
return cls.getClassAnime(weapon.getWeaponCategoryType());
}
anime = cls.getClassAnime(WeaponCategoryType.PHYSICS);
if (anime !== null) {
return anime;
}
anime = cls.getClassAnime(WeaponCategoryType.SHOOT);
if (anime !== null) {
return anime;
}
anime = cls.getClassAnime(WeaponCategoryType.MAGIC);
if (anime !== null) {
return anime;
}
return null;
},
findAttackTemplateTypeFromUnit: function(unit) {
var weapon = BattlerChecker.getRealBattleWeapon(unit);
var cls = BattlerChecker.getRealBattleClass(unit, weapon);
return this.findAttackTemplateType(cls, weapon);
},
findAttackTemplateType: function(cls, weapon) {
var classMotionFlag;
if (weapon !== null) {
return weapon.getWeaponCategoryType();
}
classMotionFlag = cls.getClassMotionFlag();
if (classMotionFlag & ClassMotionFlag.FIGHTER) {
return AttackTemplateType.FIGHTER;
}
if (classMotionFlag & ClassMotionFlag.ARCHER) {
return AttackTemplateType.ARCHER;
}
if (classMotionFlag & ClassMotionFlag.MARGE) {
return AttackTemplateType.MARGE;
}
return 0;
},
getRealBattleWeapon: function(unit) {
// 本来の武器とは異なる武器でリアル戦闘を行いたい場合は、このメソッドで調整する
return ItemControl.getEquippedWeapon(unit);
},
getRealBattleClass: function(unit, weapon) {
// 本来のクラスとは異なるクラスでリアル戦闘を行いたい場合は、このメソッドで調整する
return unit.getClass();
}
};
var BattlerPosChecker = {
getRealInitialPos: function(motionParam, isSrc, order) {
var moveMotionId;
if (!root.getAnimePreference().isMoveMotionPosInherited()) {
// 移動モーションの位置を引き継がない場合は、既定の位置が使用される
return this._getDefaultPos(motionParam);
}
moveMotionId = this._getMoveId(motionParam, order);
if (moveMotionId === MotionIdValue.NONE) {
// 戦闘するユニット両方が移動を行わない場合は、既定の位置が使用される
return this._getDefaultPos(motionParam);
}
// 移動モーションのIDをベースに位置を求める
return this._getAbsolutePos(motionParam, moveMotionId);
},
_getDefaultPos: function(motionParam) {
var size = Miscellaneous.getFirstKeySpriteSize(motionParam.animeData, motionParam.motionId);
var boundaryWidth = root.getAnimePreference().getBoundaryWidth();
var boundaryHeight = root.getAnimePreference().getBoundaryHeight();
var x = GraphicsFormat.BATTLEBACK_WIDTH - boundaryWidth;
var y = GraphicsFormat.BATTLEBACK_HEIGHT - boundaryHeight - size.height;
if (!motionParam.isRight) {
x = boundaryWidth - size.width;
}
return createPos(x, y);
},
_getAbsolutePos: function(motionParam, moveMotionId) {
var animeData = motionParam.animeData;
var frameIndex = 0;
var spriteIndex = this._getSpriteIndexFromSpriteType(animeData, moveMotionId, frameIndex, SpriteType.KEY);
var x = animeData.getSpriteX(moveMotionId, frameIndex, spriteIndex);
var y = animeData.getSpriteY(moveMotionId, frameIndex, spriteIndex);
if (!motionParam.isRight) {
x = (GraphicsFormat.BATTLEBACK_WIDTH - GraphicsFormat.MOTION_WIDTH) - x;
}
return createPos(x, y);
},
_getMoveId: function(motionParam, order) {
var i;
var count = 0;
var moveMotionId = MotionIdValue.NONE;
for (;;) {
if (order.getMoveId() !== MotionIdValue.NONE && order.getActiveUnit() === motionParam.unit) {
moveMotionId = order.getMoveId();
break;
}
if (!order.nextOrder()) {
break;
}
count++;
}
for (i = 0; i < count; i++) {
order.prevOrder();
}
return moveMotionId;
},
_getSpriteIndexFromSpriteType: function(animeData, motionId, frameIndex, spriteType) {
var i;
var count = animeData.getSpriteCount(motionId, frameIndex);
for (i = 0; i < count; i++) {
if (animeData.getSpriteType(motionId, frameIndex, i) === spriteType) {
return i;
}
}
return 0;
}
};