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

759 lines
24 KiB
JavaScript

// アニメ画像を単に画像として扱う場合は、AnimeSimpleを使用する。
// モーションとして扱う場合は、AnimeMotionを使用する。
var AnimeSimple = defineObject(BaseObject,
{
_animeData: null,
_interpolationMode: 0,
_motionId: 0,
_weaponResourceHandle: null,
setAnimeData: function(animeData) {
this._animeData = animeData;
this._interpolationMode = root.getAnimePreference().getInterpolationMode();
},
setMotionId: function(motionId) {
this._motionId = motionId;
},
setWeaponResourceHandle: function(weaponResourceHandle) {
this._weaponResourceHandle = weaponResourceHandle;
},
drawMotion: function(frameIndex, i, animeRenderParam, animeCoordinates) {
var isRight, pic, srcWidth, srcHeight;
var x = this._animeData.getSpriteX(this._motionId, frameIndex, i);
var y = this._animeData.getSpriteY(this._motionId, frameIndex, i);
var width = this._animeData.getSpriteWidth(this._motionId, frameIndex, i);
var height = this._animeData.getSpriteHeight(this._motionId, frameIndex, i);
var alpha = this._animeData.getSpriteAlpha(this._motionId, frameIndex, i);
var degree = this._animeData.getSpriteDegree(this._motionId, frameIndex, i);
var isReverse = this._animeData.isSpriteReverse(this._motionId, frameIndex, i);
var handle = this._animeData.getSpriteGraphicsHandle(this._motionId, frameIndex, i);
var xSrc = handle.getSrcX();
var ySrc = handle.getSrcY();
var isAbsolute = this._animeData.isAbsoluteMotion(this._motionId);
if (animeRenderParam !== null) {
// _alphaが設定されている場合は、スプライトに設定されているアルファ値を使用しない
if (animeRenderParam.alpha !== -1) {
alpha = animeRenderParam.alpha;
}
if (!animeRenderParam.isRight) {
// 右側に配置されていない、つまり反転している
isReverse = !isReverse;
}
isRight = animeRenderParam.isRight;
}
else {
isRight = true;
}
pic = this._getMotionPicture(frameIndex, i, animeRenderParam);
if (pic !== null) {
pic.setAlpha(alpha);
pic.setDegree(degree);
if (this._animeData.isMirrorAllowed()) {
pic.setReverse(isReverse);
}
pic.setInterpolationMode(this._interpolationMode);
if (this._animeData.getSpriteGraphicsType(this._motionId, frameIndex, i) === GraphicsType.PICTURE) {
srcWidth = pic.getWidth();
srcHeight = pic.getHeight();
}
else {
srcWidth = GraphicsFormat.MOTION_WIDTH;
srcHeight = GraphicsFormat.MOTION_HEIGHT;
}
this._drawSprite(x, y, width, height, pic, isAbsolute, isRight, xSrc, ySrc, srcWidth, srcHeight, animeCoordinates);
}
},
drawWeapon: function(frameIndex, i, animeRenderParam, animeCoordinates) {
var isRight, pic, xSrc, ySrc, srcWidth, srcHeight;
var x = this._animeData.getSpriteX(this._motionId, frameIndex, i);
var y = this._animeData.getSpriteY(this._motionId, frameIndex, i);
var width = this._animeData.getSpriteWidth(this._motionId, frameIndex, i);
var height = this._animeData.getSpriteHeight(this._motionId, frameIndex, i);
var alpha = this._animeData.getSpriteAlpha(this._motionId, frameIndex, i);
var degree = this._animeData.getSpriteDegree(this._motionId, frameIndex, i);
var isReverse = this._animeData.isSpriteReverse(this._motionId, frameIndex, i);
var plus = this._animeData.getWeaponSrcXPlus(this._motionId, frameIndex, i);
var isAbsolute = this._animeData.isAbsoluteMotion(this._motionId);
var isShoot = this._isShootAnime();
if (!this._isWeaponVisible()) {
return;
}
xSrc = this._weaponResourceHandle.getSrcX() + plus;
ySrc = this._weaponResourceHandle.getSrcY();
if (animeRenderParam !== null) {
if (animeRenderParam.alpha !== -1) {
alpha = animeRenderParam.alpha;
}
if (!animeRenderParam.isRight) {
isReverse = !isReverse;
}
isRight = animeRenderParam.isRight;
}
else {
isRight = true;
}
pic = this._getWeaponPicture(isShoot);
if (pic !== null) {
pic.setAlpha(alpha);
pic.setDegree(degree);
pic.setReverse(isReverse);
if (isShoot) {
srcWidth = GraphicsFormat.BOW_WIDTH / 3;
srcHeight = GraphicsFormat.BOW_HEIGHT;
}
else {
srcWidth = GraphicsFormat.WEAPON_WIDTH;
srcHeight = GraphicsFormat.WEAPON_HEIGHT;
}
this._drawSprite(x, y, width, height, pic, isAbsolute, isRight, xSrc, ySrc, srcWidth, srcHeight, animeCoordinates);
}
},
_isWeaponVisible: function() {
if (this._animeData.isWeaponDisabled(this._motionId)) {
return false;
}
if (this._weaponResourceHandle === null) {
return false;
}
if (this._animeData.getAttackTemplateType() === AttackTemplateType.MARGE) {
return false;
}
return true;
},
_isShootAnime: function() {
return this._animeData.getAttackTemplateType() === AttackTemplateType.ARCHER;
},
_getMotionPicture: function(frameIndex, i, animeRenderParam) {
var list, isRuntime;
var base = root.getBaseData();
var handle = this._animeData.getSpriteGraphicsHandle(this._motionId, frameIndex, i);
var id = handle.getResourceId();
var handleType = handle.getHandleType();
var colorIndex = handle.getColorIndex();
var graphicsType = this._animeData.getSpriteGraphicsType(this._motionId, frameIndex, i);
if (handleType === ResourceHandleType.ORIGINAL) {
isRuntime = false;
}
else if (handleType === ResourceHandleType.RUNTIME) {
isRuntime = true;
}
else {
return null;
}
if (animeRenderParam !== null && graphicsType === GraphicsType.MOTION) {
colorIndex = animeRenderParam.motionColorIndex;
}
list = base.getGraphicsResourceList(graphicsType, isRuntime);
return list.getCollectionDataFromId(id, colorIndex);
},
_getWeaponPicture: function(isShoot, silhouetteType) {
var list, isRuntime;
var base = root.getBaseData();
var handleType = this._weaponResourceHandle.getHandleType();
var resourceId = this._weaponResourceHandle.getResourceId();
var colorIndex = this._weaponResourceHandle.getColorIndex();
if (handleType === ResourceHandleType.ORIGINAL) {
isRuntime = false;
}
else if (handleType === ResourceHandleType.RUNTIME) {
isRuntime = true;
}
else {
return null;
}
if (isShoot) {
list = base.getGraphicsResourceList(GraphicsType.BOW, isRuntime);
}
else {
list = base.getGraphicsResourceList(GraphicsType.WEAPON, isRuntime);
}
return list.getCollectionDataFromId(resourceId, colorIndex);
},
_drawSprite: function(x, y, width, height, pic, isAbsolute, isRight, xSrc, ySrc, srcWidth, srcHeight, animeCoordinates) {
var xDest, yDest, dx, dy;
// xDest算出処理
if (isAbsolute) {
dx = 0;
}
else {
// 中心からのずれを求める
dx = animeCoordinates.xCenter - x;
}
if (!isRight) {
// 向きが異なるため、ずれる方向を変える
dx *= -1;
// キースプライトを基準にずらす
dx += width - animeCoordinates.keySpriteWidth;
}
// 描画予定だった位置(xBase)にずれを加算
xDest = animeCoordinates.xBase - dx;
// yDest算出処理
if (isAbsolute) {
dy = 0;
}
else {
dy = animeCoordinates.yCenter - animeCoordinates.yBase;
}
// 描画予定だった位置(yBase)にずれを加算
yDest = y - dy;
pic.drawStretchParts(xDest, yDest, width, height, xSrc * srcWidth, ySrc * srcHeight, srcWidth, srcHeight);
}
}
);
// 戦闘画面以外で、アニメ画像を表示する場合に使用。
// アニメーションダイアログで設定された位置関係を考慮しない。
var NonBattleAnimeSimple = defineObject(AnimeSimple,
{
_drawSprite: function(x, y, width, height, pic, isAbsolute, isRight, xSrc, ySrc, srcWidth, srcHeight, animeCoordinates) {
var xDest = animeCoordinates.xBase - Math.floor(width / 2);
var yDest = animeCoordinates.yBase - height;
pic.drawStretchParts(xDest, yDest, width, height, xSrc * srcWidth, ySrc * srcHeight, srcWidth, srcHeight);
}
}
);
var AnimeMotion = defineObject(BaseObject,
{
_unit: null,
_animeData: null,
_versusType: 0,
_xBase: null,
_yBase: null,
_animeRenderParam: null,
_color: 0,
_rangeType: EffectRangeType.NONE,
_isVolume: false,
_speedType: 0,
_counter: null,
_volumeCounter: null,
_animeSimple: null,
_motionId: 0,
_isLast: false,
_isLoopMode: false,
_frameIndex: 0,
_isThrowWeaponHidden: false,
_isWeaponShown: true,
_isLockSound: false,
_xKey: 0,
_yKey: 0,
setMotionParam: function(motionParam) {
this._unit = motionParam.unit;
this._animeData = motionParam.animeData;
this._versusType = motionParam.versusType;
this._xBase = [];
this._yBase = [];
this._animeRenderParam = StructureBuilder.buildAnimeRenderParam();
this._animeRenderParam.alpha = -1;
this._animeRenderParam.isRight = motionParam.isRight;
this._animeRenderParam.motionColorIndex = motionParam.motionColorIndex;
this._rangeType = EffectRangeType.NONE;
this._isVolume = false;
this._counter = createObject(CycleCounter);
this._volumeCounter = createObject(VolumeCounter);
this._animeSimple = createObject(AnimeSimple);
// モーションではなく、エフェクトの場合はnullになっている
if (this._animeData === null) {
this._animeData = BattlerChecker.findBattleAnime(this._unit.getClass(), null);
}
this._animeSimple.setAnimeData(this._animeData);
this._xKey = motionParam.x;
this._yKey = motionParam.y;
// 既定のモーションIDが設定されているかどうか
if (motionParam.motionId !== -1) {
this.setMotionId(motionParam.motionId);
}
},
setMotionId: function(motionId) {
this._motionId = motionId;
this._isLast = false;
this._isLoopMode = false;
this._frameIndex = 0;
this._isThrowWeaponHidden = false;
this._isWeaponShown = true;
this._animeSimple.setMotionId(motionId);
this._setFrame(this._frameIndex);
},
getMotionId: function() {
return this._motionId;
},
getOwnerUnit: function() {
return this._unit;
},
getAnimeData: function() {
return this._animeData;
},
moveMotion: function() {
if (this._isVolume) {
this._volumeCounter.moveVolumeCounter();
if (this._volumeCounter.getRoundCount() === 1) {
this._isVolume = false;
}
}
if (this._counter.moveCycleCounter() !== MoveResult.CONTINUE) {
return MoveResult.CONTINUE;
}
if (this._isLast) {
return MoveResult.END;
}
return MoveResult.END;
},
drawMotion: function(xScroll, yScroll) {
var i, spriteType;
var count = this._animeData.getSpriteCount(this._motionId, this._frameIndex);
var motionCategoryType = this._animeData.getMotionCategoryType(this._motionId);
var keyIndex = this._getSpriteIndexFromSpriteType(SpriteType.KEY, this._frameIndex);
var animeCoordinates = StructureBuilder.buildAnimeCoordinates();
// 武器や追加スプライトの座標を算出する際には、キースプライトの情報が必要になる
animeCoordinates.keySpriteWidth = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, keyIndex);
animeCoordinates.xCenter = Math.floor(GraphicsFormat.BATTLEBACK_WIDTH / 2) - Math.floor(animeCoordinates.keySpriteWidth / 2);
animeCoordinates.keySpriteHeight = this._animeData.getSpriteHeight(this._motionId, this._frameIndex, keyIndex);
animeCoordinates.yCenter = GraphicsFormat.BATTLEBACK_HEIGHT - root.getAnimePreference().getBoundaryHeight() - animeCoordinates.keySpriteHeight;
for (i = 0; i < count; i++) {
spriteType = this._animeData.getSpriteType(this._motionId, this._frameIndex, i);
if (this._isSpriteHidden(i, spriteType, motionCategoryType)) {
continue;
}
if (!this._animeData.isSpriteEnabled(this._motionId, this._frameIndex, i)) {
continue;
}
animeCoordinates.xBase = this._xBase[i] - xScroll;
animeCoordinates.yBase = this._yBase[i] - yScroll;
if (spriteType === SpriteType.KEY || spriteType === SpriteType.OPTION) {
this._animeSimple.drawMotion(this._frameIndex, i, this._animeRenderParam, animeCoordinates);
}
else {
if (this._isWeaponShown) {
this._animeSimple.drawWeapon(this._frameIndex, i, this._animeRenderParam, animeCoordinates);
}
}
}
},
nextFrame: function() {
var isContinue;
var count = this.getFrameCount();
if (this._frameIndex + 1 < count) {
this._frameIndex++;
this._setFrame(this._frameIndex);
isContinue = true;
}
else {
this._endFrame();
isContinue = false;
}
return isContinue;
},
isLastFrame: function() {
if (!this._isLast) {
return false;
}
return true;
},
isLoopMode: function() {
return this._isLoopMode;
},
setLoopMode: function(isLoopMode) {
this._isLoopMode = isLoopMode;
},
getKeyX: function() {
var index = this._getSpriteIndexFromSpriteType(SpriteType.KEY);
return this._xBase[index];
},
getKeyY: function() {
var index = this._getSpriteIndexFromSpriteType(SpriteType.KEY);
return this._yBase[index];
},
getFocusX: function() {
var index = this._getSpriteIndexFromFocus();
var width = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, index);
var dx = this._animeRenderParam.isRight ? 0 : Math.floor(width / 2);
return this._xBase[index] + dx;
},
getFocusY: function() {
var index = this._getSpriteIndexFromFocus();
var height = this._animeData.getSpriteHeight(this._motionId, this._frameIndex, index);
var dy = Math.floor(height / 2);
return this._yBase[index] + dy;
},
// 戦闘モーションの上からエフェクトを表示する場合に呼ばれる。
// 拡大縮小の関係から、エフェクトがモーションと同じサイズとは限らないため、
// 位置を求めるにはエフェクトのAnimeDataが必要になる。
// エフェクトの拡大縮小において、全てのキースプライトは一定サイズになっていることを前提にしている。
// すなわち、最初のキースプライトの拡大率が120の場合は、残りのキースプライトの拡大率も120でなければならない。
getEffectPos: function(effectAnimeData, isRight) {
var dx, dy, offset;
var index = this._getSpriteIndexFromSpriteType(SpriteType.KEY);
var width = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, index);
var height = this._animeData.getSpriteHeight(this._motionId, this._frameIndex, index);
var size = Miscellaneous.getFirstKeySpriteSize(effectAnimeData, 0);
var effectWidth = size.width;
var effectHeight = size.height;
dx = Math.floor((width - effectWidth) / 2);
dy = Math.floor(height - effectHeight);
offset = this._getEffectOffsetX();
if (!isRight) {
offset *= -1;
}
return createPos(this._xBase[index] + dx - offset, this._yBase[index] + dy);
},
getCenterPos: function(effectWidth, effectHeight) {
var dx, dy;
var index = this._getSpriteIndexFromSpriteType(SpriteType.KEY);
var x = this._xBase[index];
var y = this._yBase[index];
var width = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, index);
var height = this._animeData.getSpriteHeight(this._motionId, this._frameIndex, index);
dx = Math.floor((width - effectWidth) / 2);
dy = Math.floor((height - effectHeight) / 2);
return createPos(x + dx, y + dy);
},
getFrameIndex: function() {
return this._frameIndex;
},
setFrameIndex: function(frameIndex, isFrameChange) {
this._frameIndex = frameIndex;
if (isFrameChange) {
this._setFrame(this._frameIndex);
}
},
getFrameCount: function() {
return this._animeData.getFrameCount(this._motionId);
},
isThrowStartFrame: function() {
return this._animeData.isThrowFrame(this._motionId, this._frameIndex);
},
isAttackHitFrame: function() {
return this._animeData.isHitFrame(this._motionId, this._frameIndex);
},
isLoopStartFrame: function() {
var value = this._animeData.getLoopValue(this._motionId, this._frameIndex);
return value === LoopValue.START;
},
isLoopEndFrame: function() {
var value = this._animeData.getLoopValue(this._motionId, this._frameIndex);
return value === LoopValue.END;
},
isMagicLoopStartFrame: function() {
var value = this._animeData.getLoopValue(this._motionId, this._frameIndex);
return value === LoopValue.MAGICSTART;
},
isMagicLoopEndFrame: function() {
var value = this._animeData.getLoopValue(this._motionId, this._frameIndex);
return value === LoopValue.MAGICEND;
},
hideThrowWeapon: function() {
if (!this._animeData.isHitLossDisabled(this._motionId)) {
this._isThrowWeaponHidden = true;
}
},
setWeapon: function(weaponData) {
this._animeSimple.setWeaponResourceHandle(weaponData.getRealWeaponResourceHandle());
},
showWeapon: function(isShown) {
this._isWeaponShown = isShown;
},
setColorAlpha: function(alpha) {
this._animeRenderParam.alpha = alpha;
},
getScreenColor: function() {
return this._color;
},
getScreenAlpha: function() {
return this._volumeCounter.getVolume();
},
getScreenEffectRangeType: function() {
return this._rangeType;
},
lockSound: function() {
this._isLockSound = true;
},
_setFrame: function(frameIndex) {
this._checkCounter();
this._checkSound();
this._checkBright();
// 新しいフレームに変わるため、現在位置を更新する
this._updatePos();
this._isLast = false;
},
_endFrame: function() {
var type = this._animeData.getMotionCategoryType(this._motionId);
// 移動終了時には、キースプライトの基準位置を更新する
if (type === MotionCategoryType.APPROACH) {
this._xKey = this.getKeyX();
this._yKey = this.getKeyY();
}
this._isLast = true;
this._isVolume = false;
},
_checkCounter: function() {
var value = this._animeData.getFrameCounterValue(this._motionId, this._frameIndex);
// -1によって奇数となるため、60FPSの場合のみ、アニメの速度がわずかに速くなる。
// こうした例外は本来好ましくないが、このときの速度が最も安定していると判断した。
this._counter.setCounterInfo(value - 1);
},
_checkSound: function() {
var soundHandle;
if (!this._isLockSound && this._animeData.isSoundFrame(this._motionId, this._frameIndex)) {
soundHandle = this._animeData.getSoundHandle(this._motionId, this._frameIndex);
MediaControl.soundPlay(soundHandle);
}
},
_checkBright: function() {
var isStart, alpha, volume;
if (!this._animeData.isBrightFrame(this._motionId, this._frameIndex)) {
return;
}
isStart = this._animeData.isScreenColorOverlay(this._motionId, this._frameIndex);
if (isStart) {
this._color = this._animeData.getScreenColor(this._motionId, this._frameIndex);
this._rangeType = this._animeData.getScreenColorEffectRangeType(this._motionId, this._frameIndex);
alpha = this._animeData.getScreenColorAlpha(this._motionId, this._frameIndex);
this._speedType = this._animeData.getScreenColorChangeSpeedType(this._motionId, this._frameIndex);
if (this._speedType === SpeedType.DIRECT) {
this._volumeCounter.setVolume(alpha);
return;
}
this._volumeCounter.setChangeSpeed(Miscellaneous.convertSpeedType(this._speedType));
volume = this._volumeCounter.getVolume();
if (alpha > volume) {
this._volumeCounter.setVolumeRange(alpha, 0, volume, true);
}
else {
this._volumeCounter.setVolumeRange(0, alpha, volume, false);
}
}
else {
if (this._speedType === SpeedType.DIRECT) {
this._volumeCounter.setVolume(0);
return;
}
this._volumeCounter.setVolumeRange(0, 0, this._volumeCounter.getVolume(), false);
}
this._isVolume = true;
},
_updatePos: function() {
var i, count, x, width, keyIndex;
var isAbsolute = this._animeData.isAbsoluteMotion(this._motionId);
if (isAbsolute) {
keyIndex = this._getSpriteIndexFromSpriteType(SpriteType.KEY, this._frameIndex);
count = this._animeData.getSpriteCount(this._motionId, this._frameIndex);
for (i = 0; i < count; i++) {
x = this._animeData.getSpriteX(this._motionId, this._frameIndex, i);
if (!this._animeRenderParam.isRight) {
width = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, keyIndex);
this._xBase[i] = (GraphicsFormat.BATTLEBACK_WIDTH - width) - x;
}
else {
this._xBase[i] = x;
}
this._xBase[i] += this._getHorzOffset(this._animeRenderParam.isRight);
this._yBase[i] = this._animeData.getSpriteY(this._motionId, this._frameIndex, i);
}
}
else {
count = this._animeData.getSpriteCount(this._motionId, this._frameIndex);
for (i = 0; i < count; i++) {
this._xBase[i] = this._xKey + this._getHorzOffset(this._animeRenderParam.isRight);
this._yBase[i] = this._yKey;
}
}
},
_getHorzOffset: function(isRight) {
var offset;
if (this._animeData.getAnimeType() === AnimeType.EFFECT) {
return 0;
}
offset = root.getAnimePreference().getMotionOffset(this._versusType);
return isRight ? offset * -1 : offset;
},
_getSpriteIndexFromSpriteType: function(spriteType) {
var i;
var count = this._animeData.getSpriteCount(this._motionId, this._frameIndex);
for (i = 0; i < count; i++) {
if (this._animeData.getSpriteType(this._motionId, this._frameIndex, i) === spriteType) {
return i;
}
}
return 0;
},
_getSpriteIndexFromFocus: function() {
var i;
var count = this._animeData.getSpriteCount(this._motionId, this._frameIndex);
for (i = 0; i < count; i++) {
if (this._animeData.isFocusSprite(this._motionId, this._frameIndex, i)) {
return i;
}
}
return 0;
},
_isSpriteHidden: function(i, spriteType, motionCategoryType) {
// 非表示にすべき状態になっていないため、非表示にならない
if (!this._isThrowWeaponHidden) {
return false;
}
if (motionCategoryType === MotionCategoryType.THROW && this._animeData.isFocusSprite(this._motionId, this._frameIndex, i)) {
// 間接攻撃においては、視点となっているスプライトを非表示にする。
// 武器を非表示にしてもよいが、武器を放たずに追加スプライトを衝撃波のように放つこともあり、
// この場合は武器ではなく、追加スプライトの非表示が理想となる。
// 放たれたスプライトが非表示になるべきとし、isFocusSpriteで判定を行っている。
return true;
}
else if (motionCategoryType === MotionCategoryType.SHOOT && spriteType === SpriteType.ARROW) {
// 弓兵系の場合は、常に矢が非表示の対象とする。
// 矢を放たないというケースは極めて稀であると判断。
return true;
}
return false;
},
// アニメーションダイアログ上での中央からのずれを取得する
_getEffectOffsetX: function() {
var index = this._getSpriteIndexFromSpriteType(SpriteType.KEY);
var width = this._animeData.getSpriteWidth(this._motionId, this._frameIndex, index);
var xCenter = Math.floor(GraphicsFormat.BATTLEBACK_WIDTH / 2) - Math.floor(width / 2);
var x = this._animeData.getSpriteX(this._motionId, this._frameIndex, index);
var d = 0;
if (!this._animeData.isAbsoluteMotion(this._motionId)) {
d = xCenter - x;
}
return d;
}
}
);