Normalize battle unit animation sprites
This commit is contained in:
@@ -1017,8 +1017,11 @@ const unitActionColumns: Record<UnitActionPose, number> = {
|
||||
attack: 0,
|
||||
strategy: 1,
|
||||
item: 2,
|
||||
hurt: 3
|
||||
hurt: 3,
|
||||
celebrate: 4
|
||||
};
|
||||
const unitActionFrameCount = 4;
|
||||
const unitActionPoseCount = 5;
|
||||
|
||||
type BattleLayout = {
|
||||
mapX: number;
|
||||
@@ -1066,10 +1069,9 @@ type UnitView = {
|
||||
};
|
||||
|
||||
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
||||
type UnitActionPose = 'attack' | 'strategy' | 'item' | 'hurt';
|
||||
type UnitActionPose = 'attack' | 'strategy' | 'item' | 'hurt' | 'celebrate';
|
||||
|
||||
const levelUpCelebrationPoseSequence: UnitActionPose[] = ['strategy', 'item', 'attack', 'strategy', 'item', 'attack', 'strategy'];
|
||||
const levelUpCelebrationStepOffsets = [0, -4, 0, -6, -2, 0, -3];
|
||||
const levelUpCelebrationStepOffsets = [0, -7, -2, -9, 0, -6, -3, -8];
|
||||
|
||||
type BattlePhase = 'idle' | 'moving' | 'command' | 'targeting' | 'animating' | 'resolved';
|
||||
type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait';
|
||||
@@ -5361,6 +5363,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const sprite = this.add.sprite(-width / 2 + 56, 10, this.unitActionTexture(animation.unit), this.unitActionFrameIndex('south', this.celebrationPose(animation.unit)));
|
||||
sprite.setDisplaySize(76, 76);
|
||||
container.add(sprite);
|
||||
void this.playUnitActionFrames(sprite, animation.unit, 'south', this.celebrationPose(animation.unit), 105, 2);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: burst, angle: 240, scale: 1.34, alpha: 0.16, duration: 760, ease: 'Sine.easeOut' });
|
||||
@@ -5404,26 +5407,26 @@ export class BattleScene extends Phaser.Scene {
|
||||
const baseY = sprite.y;
|
||||
const baseX = sprite.x;
|
||||
const baseAngle = sprite.angle;
|
||||
const preferredPose = this.celebrationPose(unit);
|
||||
const poses = [preferredPose, ...levelUpCelebrationPoseSequence];
|
||||
const pose = this.celebrationPose(unit);
|
||||
const frames = Array.from({ length: unitActionFrameCount * 2 }, (_, index) => index % unitActionFrameCount);
|
||||
return new Promise<void>((resolve) => {
|
||||
poses.forEach((pose, index) => {
|
||||
this.time.delayedCall(index * 120, () => {
|
||||
frames.forEach((frame, index) => {
|
||||
this.time.delayedCall(index * 105, () => {
|
||||
if (!sprite.active) {
|
||||
return;
|
||||
}
|
||||
sprite.setFrame(this.unitActionFrameIndex('south', pose));
|
||||
this.setUnitActionFrame(sprite, unit, 'south', pose, frame);
|
||||
sprite.setX(baseX + (index % 2 === 0 ? 0 : 1));
|
||||
sprite.setY(baseY + levelUpCelebrationStepOffsets[index % levelUpCelebrationStepOffsets.length]);
|
||||
sprite.setAngle(baseAngle);
|
||||
});
|
||||
});
|
||||
this.time.delayedCall(poses.length * 120 + 90, () => {
|
||||
this.time.delayedCall(frames.length * 105 + 80, () => {
|
||||
if (sprite.active) {
|
||||
sprite.setX(baseX);
|
||||
sprite.setY(baseY);
|
||||
sprite.setAngle(baseAngle);
|
||||
sprite.setFrame(this.unitActionFrameIndex('south', preferredPose));
|
||||
this.setUnitActionFrame(sprite, unit, 'south', pose, 0);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
@@ -7231,6 +7234,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
);
|
||||
userSprite.setDepth(depth + 3);
|
||||
userSprite.setDisplaySize(112, 112);
|
||||
void this.playUnitActionFrames(userSprite, result.user, 'east', supportPose, 100, 2);
|
||||
|
||||
const targetSprite = this.trackCombatObject(this.add.sprite(left + panelWidth - 160, top + 146, this.unitTextureKey(result.target), this.unitFrameIndex('west')));
|
||||
targetSprite.setDepth(depth + 3);
|
||||
@@ -7631,6 +7635,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const burst = this.add.star(-panelWidth / 2 + 56, -12, 8, 12, 34, 0xffdf7b, 0.72);
|
||||
container.add([burst, sprite]);
|
||||
this.tweens.add({ targets: burst, angle: 220, scale: 1.32, alpha: 0.18, duration: 760, ease: 'Sine.easeOut' });
|
||||
void this.playUnitActionFrames(sprite, celebrant, 'south', this.celebrationPose(celebrant), 105, 2);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1.04, duration: 180, ease: 'Back.easeOut' });
|
||||
@@ -7764,13 +7769,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private celebrationPose(unit: UnitData): UnitActionPose {
|
||||
if (unit.id === 'liu-bei') {
|
||||
return 'strategy';
|
||||
}
|
||||
if (unit.id === 'zhang-fei') {
|
||||
return 'attack';
|
||||
}
|
||||
return 'item';
|
||||
return 'celebrate';
|
||||
}
|
||||
|
||||
private playGrowthCelebration(sprite: Phaser.GameObjects.Sprite, unit: UnitData) {
|
||||
@@ -7846,6 +7845,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const contactX = defenderX - direction * 126;
|
||||
|
||||
soundDirector.playMeleeRush(mounted);
|
||||
void this.playUnitActionFrames(attackerSprite, result.attacker, 'east', 'attack', mounted ? 72 : 82);
|
||||
this.createDustPuff(attackerX - direction * 18, groundY + 48, depth - 1, mounted ? 1.08 : 0.82);
|
||||
this.tweens.add({
|
||||
targets: attackerSprite,
|
||||
@@ -7870,11 +7870,11 @@ export class BattleScene extends Phaser.Scene {
|
||||
await this.delay(mounted ? 170 : 205);
|
||||
|
||||
soundDirector.playMeleeSwing(result.hit, result.critical);
|
||||
attackerSprite.setTexture(this.unitActionTexture(result.attacker), this.unitActionFrameIndex('east', 'attack'));
|
||||
this.setUnitActionFrame(attackerSprite, result.attacker, 'east', 'attack', 2);
|
||||
this.createSlashArc(defenderX - direction * 68, groundY - 48, direction, depth + 2, result.critical);
|
||||
|
||||
if (result.hit) {
|
||||
defenderSprite.setTexture(this.unitActionTexture(result.defender), this.unitActionFrameIndex(defenderDirection, 'hurt'));
|
||||
void this.playUnitActionFrames(defenderSprite, result.defender, defenderDirection, 'hurt', 70);
|
||||
this.cameras.main.shake(result.critical ? 130 : 90, result.critical ? 0.006 : 0.0035);
|
||||
this.shakeCombatSprite(defenderSprite, defenderX, groundY, direction, result.critical);
|
||||
await this.delay(result.critical ? 130 : 92);
|
||||
@@ -7912,7 +7912,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
defenderDirection: UnitDirection,
|
||||
direction: number
|
||||
) {
|
||||
attackerSprite.setTexture(this.unitActionTexture(result.attacker), this.unitActionFrameIndex('east', 'attack'));
|
||||
void this.playUnitActionFrames(attackerSprite, result.attacker, 'east', 'attack', 88);
|
||||
this.createBowDrawEffect(attackerX + direction * 44, groundY - 52, direction, depth + 2);
|
||||
this.tweens.add({
|
||||
targets: attackerSprite,
|
||||
@@ -7939,7 +7939,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
await this.delay(result.hit ? 332 : 382);
|
||||
|
||||
if (result.hit) {
|
||||
defenderSprite.setTexture(this.unitActionTexture(result.defender), this.unitActionFrameIndex(defenderDirection, 'hurt'));
|
||||
void this.playUnitActionFrames(defenderSprite, result.defender, defenderDirection, 'hurt', 70);
|
||||
this.createArrowImpact(defenderX - direction * 58, groundY - 60, direction, depth + 5, result.critical);
|
||||
this.shakeCombatSprite(defenderSprite, defenderX, groundY, direction, result.critical);
|
||||
arrow.destroy();
|
||||
@@ -7978,7 +7978,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
soundDirector.playStrategyPulse();
|
||||
}
|
||||
|
||||
attackerSprite.setTexture(this.unitActionTexture(result.attacker), this.unitActionFrameIndex('east', this.actionPoseForCommand(result.action)));
|
||||
void this.playUnitActionFrames(attackerSprite, result.attacker, 'east', this.actionPoseForCommand(result.action), 92);
|
||||
this.createCasterPulse(attackerX + direction * 48, groundY - 50, depth + 2, result.action === 'item' ? 0xd8b15f : 0x6cc5ff);
|
||||
this.tweens.add({
|
||||
targets: attackerSprite,
|
||||
@@ -8005,7 +8005,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
});
|
||||
await this.delay(450);
|
||||
if (result.hit) {
|
||||
defenderSprite.setTexture(this.unitActionTexture(result.defender), this.unitActionFrameIndex(defenderDirection, 'hurt'));
|
||||
void this.playUnitActionFrames(defenderSprite, result.defender, defenderDirection, 'hurt', 70);
|
||||
this.createSpecialImpact(defenderX - direction * 68, result.action === 'item' ? groundY - 20 : groundY - 56, depth + 4, result.action);
|
||||
this.shakeCombatSprite(defenderSprite, defenderX, groundY, direction, result.critical);
|
||||
}
|
||||
@@ -9303,8 +9303,34 @@ export class BattleScene extends Phaser.Scene {
|
||||
return this.textures.exists(actionKey) ? actionKey : base;
|
||||
}
|
||||
|
||||
private unitActionFrameIndex(direction: UnitDirection, pose: UnitActionPose) {
|
||||
return unitFrameRows[direction] * 4 + unitActionColumns[pose];
|
||||
private unitActionFrameIndex(direction: UnitDirection, pose: UnitActionPose, frame = 0) {
|
||||
const safeFrame = Phaser.Math.Clamp(frame, 0, unitActionFrameCount - 1);
|
||||
return unitFrameRows[direction] * unitActionPoseCount * unitActionFrameCount + unitActionColumns[pose] * unitActionFrameCount + safeFrame;
|
||||
}
|
||||
|
||||
private setUnitActionFrame(sprite: Phaser.GameObjects.Sprite, unit: UnitData, direction: UnitDirection, pose: UnitActionPose, frame = 0) {
|
||||
sprite.setTexture(this.unitActionTexture(unit), this.unitActionFrameIndex(direction, pose, frame));
|
||||
}
|
||||
|
||||
private playUnitActionFrames(
|
||||
sprite: Phaser.GameObjects.Sprite,
|
||||
unit: UnitData,
|
||||
direction: UnitDirection,
|
||||
pose: UnitActionPose,
|
||||
frameDuration = 86,
|
||||
loops = 1
|
||||
) {
|
||||
const totalFrames = unitActionFrameCount * Math.max(1, loops);
|
||||
return new Promise<void>((resolve) => {
|
||||
for (let index = 0; index < totalFrames; index += 1) {
|
||||
this.time.delayedCall(index * frameDuration, () => {
|
||||
if (sprite.active) {
|
||||
this.setUnitActionFrame(sprite, unit, direction, pose, index % unitActionFrameCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.time.delayedCall(totalFrames * frameDuration, () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
private actionPoseForCommand(action: DamageCommand): UnitActionPose {
|
||||
|
||||
Reference in New Issue
Block a user