Add animated combat growth rewards
This commit is contained in:
@@ -90,6 +90,21 @@ class SoundDirector {
|
||||
window.setTimeout(() => this.playTone(450, 0.08, 0.028, 'sine'), 70);
|
||||
}
|
||||
|
||||
playGrowthTick() {
|
||||
if (this.playEffect('exp-gain', { volume: 0.18, rate: 1.12, stopAfterMs: 360 })) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playTone(620, 0.05, 0.018, 'triangle');
|
||||
}
|
||||
|
||||
playLevelUp() {
|
||||
this.playEffect('exp-gain', { volume: 0.42, rate: 1.08, stopAfterMs: 820 });
|
||||
this.playTone(520, 0.08, 0.035, 'triangle');
|
||||
window.setTimeout(() => this.playTone(720, 0.09, 0.04, 'triangle'), 82);
|
||||
window.setTimeout(() => this.playTone(980, 0.12, 0.034, 'sine'), 168);
|
||||
}
|
||||
|
||||
playEffect(key: string, options: PlayEffectOptions = {}) {
|
||||
if (!this.started || this.muted) {
|
||||
return false;
|
||||
|
||||
@@ -150,6 +150,38 @@ type CharacterGrowthResult = {
|
||||
leveled: boolean;
|
||||
};
|
||||
|
||||
type BondGrowthResult = {
|
||||
label: string;
|
||||
amount: number;
|
||||
previousLevel: number;
|
||||
previousExp: number;
|
||||
level: number;
|
||||
exp: number;
|
||||
next: number;
|
||||
leveled: boolean;
|
||||
};
|
||||
|
||||
type GrowthGaugeEntry = {
|
||||
label: string;
|
||||
owner: string;
|
||||
amountLabel: string;
|
||||
previousLevel: number;
|
||||
previousExp: number;
|
||||
previousNext: number;
|
||||
level: number;
|
||||
exp: number;
|
||||
next: number;
|
||||
leveled: boolean;
|
||||
color: number;
|
||||
};
|
||||
|
||||
type GrowthGaugeView = {
|
||||
fill: Phaser.GameObjects.Rectangle;
|
||||
valueText: Phaser.GameObjects.Text;
|
||||
levelText: Phaser.GameObjects.Text;
|
||||
eventText: Phaser.GameObjects.Text;
|
||||
};
|
||||
|
||||
type CombatPreview = {
|
||||
action: DamageCommand;
|
||||
usable?: BattleUsable;
|
||||
@@ -172,6 +204,7 @@ type CombatResult = CombatPreview & {
|
||||
attackerGrowth: EquipmentGrowthResult;
|
||||
defenderGrowth: EquipmentGrowthResult;
|
||||
bondExp: number;
|
||||
bondGrowth: BondGrowthResult[];
|
||||
counter?: CombatResult;
|
||||
};
|
||||
|
||||
@@ -2036,7 +2069,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
const attackerGrowth = this.awardEquipmentExp(attacker, action === 'attack' ? 'weapon' : 'accessory', this.attackerEquipmentExpGain(attacker, action));
|
||||
const armorGrowth = this.awardEquipmentExp(defender, 'armor', hit ? 6 : 3);
|
||||
const characterGrowth = this.awardCharacterExp(attacker, this.characterExpGain(preview, damage, hit, critical));
|
||||
const bondExp = attacker.faction === 'ally' && !isCounter ? this.recordBondAttackIntent(attacker, defender) : 0;
|
||||
const bondGrowth = attacker.faction === 'ally' && !isCounter ? this.recordBondAttackIntent(attacker, defender) : [];
|
||||
const bondExp = bondGrowth.reduce((total, growth) => total + growth.amount, 0);
|
||||
|
||||
defender.hp = Math.max(0, defender.hp - damage);
|
||||
this.faceUnitToward(attacker, defender);
|
||||
@@ -2064,7 +2098,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
characterGrowth,
|
||||
attackerGrowth,
|
||||
defenderGrowth: armorGrowth,
|
||||
bondExp
|
||||
bondExp,
|
||||
bondGrowth
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2793,8 +2828,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
private async playCombatCutIn(result: CombatResult) {
|
||||
this.hideCombatCutIn();
|
||||
|
||||
const panelWidth = 900;
|
||||
const panelHeight = 430;
|
||||
const panelWidth = 940;
|
||||
const panelHeight = 560;
|
||||
const left = Math.floor((this.scale.width - panelWidth) / 2);
|
||||
const top = Math.floor((this.scale.height - panelHeight) / 2);
|
||||
const depth = 70;
|
||||
@@ -2889,6 +2924,9 @@ export class BattleScene extends Phaser.Scene {
|
||||
result.characterGrowth.leveled,
|
||||
680
|
||||
);
|
||||
if (result.characterGrowth.leveled) {
|
||||
attackerStatus.nameText.setText(`${result.attacker.name} Lv ${result.characterGrowth.level}`);
|
||||
}
|
||||
|
||||
const resultLabel = result.hit ? `${result.critical ? '치명타! ' : ''}${result.damage} 피해` : '회피!';
|
||||
const resultText = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 252, resultLabel, {
|
||||
@@ -2902,18 +2940,17 @@ export class BattleScene extends Phaser.Scene {
|
||||
resultText.setOrigin(0.5);
|
||||
resultText.setDepth(depth + 16);
|
||||
|
||||
const growthText = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 330, this.combatGrowthLines(result).join('\n'), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
align: 'center',
|
||||
lineSpacing: 3
|
||||
}));
|
||||
growthText.setOrigin(0.5, 0);
|
||||
growthText.setDepth(depth + 16);
|
||||
await this.playGrowthRewardPanel({
|
||||
x: left + 86,
|
||||
y: top + 410,
|
||||
width: panelWidth - 172,
|
||||
title: '성장 결과',
|
||||
entries: this.combatGrowthEntries(result),
|
||||
celebrant: this.growthCelebrant(result),
|
||||
depth: depth + 18
|
||||
});
|
||||
|
||||
await this.delay(760);
|
||||
await this.delay(360);
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
@@ -2921,7 +2958,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
|
||||
const panelWidth = 760;
|
||||
const panelHeight = 330;
|
||||
const panelHeight = 500;
|
||||
const left = Math.floor((this.scale.width - panelWidth) / 2);
|
||||
const top = Math.floor((this.scale.height - panelHeight) / 2);
|
||||
const depth = 70;
|
||||
@@ -2989,20 +3026,17 @@ export class BattleScene extends Phaser.Scene {
|
||||
resultText.setOrigin(0.5);
|
||||
resultText.setDepth(depth + 5);
|
||||
|
||||
const growth = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 292, [
|
||||
`장수 경험치 +${result.characterGrowth.amount} (${result.characterGrowth.exp}/${result.characterGrowth.next})`,
|
||||
this.formatEquipmentGrowth(result.equipmentGrowth)
|
||||
].join(' '), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
align: 'center'
|
||||
}));
|
||||
growth.setOrigin(0.5);
|
||||
growth.setDepth(depth + 5);
|
||||
await this.playGrowthRewardPanel({
|
||||
x: left + 64,
|
||||
y: top + 322,
|
||||
width: panelWidth - 128,
|
||||
title: '성장 결과',
|
||||
entries: this.supportGrowthEntries(result),
|
||||
celebrant: result.user,
|
||||
depth: depth + 8
|
||||
});
|
||||
|
||||
await this.delay(760);
|
||||
await this.delay(360);
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
@@ -3023,13 +3057,13 @@ export class BattleScene extends Phaser.Scene {
|
||||
unitIcon.setDisplaySize(64, 64);
|
||||
}
|
||||
|
||||
const name = this.trackCombatObject(this.add.text(x + 78, y + 14, `${unit.name} Lv ${unit.level}`, {
|
||||
const nameText = this.trackCombatObject(this.add.text(x + 78, y + 14, `${unit.name} Lv ${growth?.previousLevel ?? unit.level}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
name.setDepth(85);
|
||||
nameText.setDepth(85);
|
||||
|
||||
const hpLabel = this.trackCombatObject(this.add.text(x + 78, y + 45, '병력', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
@@ -3077,7 +3111,283 @@ export class BattleScene extends Phaser.Scene {
|
||||
expText.setOrigin(1, 0);
|
||||
expText.setDepth(85);
|
||||
|
||||
return { hpFill, hpText, expFill, expText };
|
||||
return { hpFill, hpText, expFill, expText, nameText };
|
||||
}
|
||||
|
||||
private combatGrowthEntries(result: CombatResult): GrowthGaugeEntry[] {
|
||||
const entries: GrowthGaugeEntry[] = [];
|
||||
|
||||
if (result.attacker.faction === 'ally') {
|
||||
entries.push(this.characterGrowthEntry(result.attacker, result.characterGrowth));
|
||||
entries.push(this.equipmentGrowthEntry(result.attacker, result.attackerGrowth));
|
||||
}
|
||||
if (result.defender.faction === 'ally') {
|
||||
entries.push(this.equipmentGrowthEntry(result.defender, result.defenderGrowth));
|
||||
}
|
||||
result.bondGrowth.forEach((growth) => entries.push(this.bondGrowthEntry(growth)));
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private supportGrowthEntries(result: SupportResult): GrowthGaugeEntry[] {
|
||||
return [this.characterGrowthEntry(result.user, result.characterGrowth), this.equipmentGrowthEntry(result.user, result.equipmentGrowth)];
|
||||
}
|
||||
|
||||
private characterGrowthEntry(unit: UnitData, growth: CharacterGrowthResult): GrowthGaugeEntry {
|
||||
return {
|
||||
label: '장수 경험치',
|
||||
owner: unit.name,
|
||||
amountLabel: `+${growth.amount}`,
|
||||
previousLevel: growth.previousLevel,
|
||||
previousExp: growth.previousExp,
|
||||
previousNext: 100,
|
||||
level: growth.level,
|
||||
exp: growth.exp,
|
||||
next: growth.next,
|
||||
leveled: growth.leveled,
|
||||
color: 0xd8b15f
|
||||
};
|
||||
}
|
||||
|
||||
private equipmentGrowthEntry(unit: UnitData, growth: EquipmentGrowthResult): GrowthGaugeEntry {
|
||||
return {
|
||||
label: `${equipmentSlotLabels[growth.slot]} ${growth.itemName}`,
|
||||
owner: unit.name,
|
||||
amountLabel: `+${growth.amount}`,
|
||||
previousLevel: growth.previousLevel,
|
||||
previousExp: growth.previousExp,
|
||||
previousNext: equipmentExpToNext(growth.previousLevel),
|
||||
level: growth.level,
|
||||
exp: growth.exp,
|
||||
next: growth.next,
|
||||
leveled: growth.leveled,
|
||||
color: growth.slot === 'weapon' ? 0xffc45f : growth.slot === 'armor' ? 0x77b7ff : 0xb7df7a
|
||||
};
|
||||
}
|
||||
|
||||
private bondGrowthEntry(growth: BondGrowthResult): GrowthGaugeEntry {
|
||||
return {
|
||||
label: '공명 경험치',
|
||||
owner: growth.label,
|
||||
amountLabel: `+${growth.amount}`,
|
||||
previousLevel: growth.previousLevel,
|
||||
previousExp: growth.previousExp,
|
||||
previousNext: 100,
|
||||
level: growth.level,
|
||||
exp: growth.exp,
|
||||
next: growth.next,
|
||||
leveled: growth.leveled,
|
||||
color: 0x83d6ff
|
||||
};
|
||||
}
|
||||
|
||||
private growthCelebrant(result: CombatResult) {
|
||||
if (result.attacker.faction === 'ally') {
|
||||
return result.attacker;
|
||||
}
|
||||
if (result.defender.faction === 'ally') {
|
||||
return result.defender;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async playGrowthRewardPanel(config: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
title: string;
|
||||
entries: GrowthGaugeEntry[];
|
||||
celebrant?: UnitData;
|
||||
depth: number;
|
||||
}) {
|
||||
const entries = config.entries.filter((entry) => entry.amountLabel !== '+0' || entry.leveled);
|
||||
if (entries.length === 0) {
|
||||
await this.delay(280);
|
||||
return;
|
||||
}
|
||||
|
||||
const rowHeight = 25;
|
||||
const height = 48 + entries.length * rowHeight;
|
||||
const panel = this.trackCombatObject(this.add.rectangle(config.x, config.y, config.width, height, 0x0b121a, 0.96));
|
||||
panel.setOrigin(0);
|
||||
panel.setDepth(config.depth);
|
||||
panel.setStrokeStyle(2, palette.gold, 0.82);
|
||||
|
||||
const title = this.trackCombatObject(this.add.text(config.x + 16, config.y + 12, config.title, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
title.setDepth(config.depth + 1);
|
||||
|
||||
const levelEvent = entries.some((entry) => entry.leveled);
|
||||
const rowWidth = config.width - (levelEvent && config.celebrant ? 126 : 36);
|
||||
const views = entries.map((entry, index) => this.renderGrowthGaugeRow(entry, config.x + 18, config.y + 42 + index * rowHeight, rowWidth, config.depth + 1));
|
||||
|
||||
let celebration: Phaser.GameObjects.Sprite | undefined;
|
||||
if (levelEvent && config.celebrant) {
|
||||
celebration = this.createGrowthCelebrationSprite(config.celebrant, config.x + config.width - 46, config.y + 24, config.depth + 5);
|
||||
}
|
||||
|
||||
for (let index = 0; index < entries.length; index += 1) {
|
||||
await this.animateGrowthEntry(views[index], entries[index], 540 + index * 70);
|
||||
if (entries[index].leveled) {
|
||||
this.flashGrowthLevelUp(views[index], config.x, config.y, config.width, height, config.depth + 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (celebration) {
|
||||
await this.playGrowthCelebration(celebration);
|
||||
}
|
||||
}
|
||||
|
||||
private renderGrowthGaugeRow(entry: GrowthGaugeEntry, x: number, y: number, width: number, depth: number): GrowthGaugeView {
|
||||
const label = this.trackCombatObject(this.add.text(x, y - 3, `${entry.owner} ${entry.label}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
label.setDepth(depth);
|
||||
|
||||
const amount = this.trackCombatObject(this.add.text(x + 214, y - 3, entry.amountLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#ffdf7b',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
amount.setDepth(depth);
|
||||
|
||||
const barX = x + 270;
|
||||
const barWidth = Math.max(130, width - 440);
|
||||
const track = this.trackCombatObject(this.add.rectangle(barX, y + 8, barWidth, 9, 0x05090e, 0.92));
|
||||
track.setOrigin(0, 0.5);
|
||||
track.setDepth(depth);
|
||||
const fill = this.trackCombatObject(this.add.rectangle(barX, y + 8, barWidth, 7, entry.color, 0.98));
|
||||
fill.setOrigin(0, 0.5);
|
||||
fill.setDepth(depth + 1);
|
||||
fill.setScale(entry.previousExp / entry.previousNext, 1);
|
||||
|
||||
const valueText = this.trackCombatObject(this.add.text(barX + barWidth + 10, y - 3, `${entry.previousExp}/${entry.previousNext}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#f0e4c8',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
valueText.setDepth(depth);
|
||||
|
||||
const levelText = this.trackCombatObject(this.add.text(x + width - 6, y - 3, `Lv ${entry.previousLevel}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
levelText.setOrigin(1, 0);
|
||||
levelText.setDepth(depth);
|
||||
|
||||
const eventText = this.trackCombatObject(this.add.text(barX + Math.floor(barWidth / 2), y - 12, '', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: '#fff2b8',
|
||||
fontStyle: '700',
|
||||
stroke: '#05070a',
|
||||
strokeThickness: 3
|
||||
}));
|
||||
eventText.setOrigin(0.5);
|
||||
eventText.setDepth(depth + 3);
|
||||
|
||||
return { fill, valueText, levelText, eventText };
|
||||
}
|
||||
|
||||
private async animateGrowthEntry(view: GrowthGaugeView, entry: GrowthGaugeEntry, duration: number) {
|
||||
if (entry.leveled) {
|
||||
await this.animateGrowthGauge(view.fill, view.valueText, entry.previousExp / entry.previousNext, 1, entry.previousExp, entry.previousNext, entry.previousNext, false, duration);
|
||||
view.levelText.setText(`Lv ${entry.level}`);
|
||||
view.fill.setScale(0, 1);
|
||||
view.valueText.setText(`0/${entry.next}`);
|
||||
this.showGrowthEventText(view.eventText, `Lv ${entry.level} 달성!`);
|
||||
soundDirector.playLevelUp();
|
||||
await this.delay(260);
|
||||
await this.animateGrowthGauge(view.fill, view.valueText, 0, entry.exp / entry.next, 0, entry.exp, entry.next, false, Math.max(260, Math.floor(duration * 0.48)));
|
||||
return;
|
||||
}
|
||||
|
||||
await this.animateGrowthGauge(view.fill, view.valueText, entry.previousExp / entry.previousNext, entry.exp / entry.next, entry.previousExp, entry.exp, entry.next, false, duration);
|
||||
view.levelText.setText(`Lv ${entry.level}`);
|
||||
}
|
||||
|
||||
private showGrowthEventText(text: Phaser.GameObjects.Text, label: string) {
|
||||
text.setText(label);
|
||||
text.setAlpha(1);
|
||||
text.setScale(0.9);
|
||||
this.tweens.add({
|
||||
targets: text,
|
||||
y: text.y - 12,
|
||||
scale: 1.16,
|
||||
alpha: 0,
|
||||
duration: 780,
|
||||
ease: 'Sine.easeOut'
|
||||
});
|
||||
}
|
||||
|
||||
private flashGrowthLevelUp(view: GrowthGaugeView, x: number, y: number, width: number, height: number, depth: number) {
|
||||
const flash = this.trackCombatObject(this.add.rectangle(x + width / 2, y + height / 2, width - 10, height - 10, 0xffdf7b, 0.14));
|
||||
flash.setDepth(depth);
|
||||
this.tweens.add({
|
||||
targets: flash,
|
||||
alpha: 0,
|
||||
scaleX: 1.03,
|
||||
scaleY: 1.12,
|
||||
duration: 520,
|
||||
ease: 'Sine.easeOut',
|
||||
onComplete: () => flash.destroy()
|
||||
});
|
||||
this.tweens.add({
|
||||
targets: [view.levelText, view.valueText],
|
||||
scale: 1.16,
|
||||
duration: 130,
|
||||
yoyo: true,
|
||||
ease: 'Sine.easeOut'
|
||||
});
|
||||
}
|
||||
|
||||
private createGrowthCelebrationSprite(unit: UnitData, x: number, y: number, depth: number) {
|
||||
const sprite = this.trackCombatObject(
|
||||
this.add.sprite(x, y, this.unitActionTexture(unit), this.unitActionFrameIndex('south', this.celebrationPose(unit)))
|
||||
);
|
||||
sprite.setDisplaySize(82, 82);
|
||||
sprite.setDepth(depth);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
private celebrationPose(unit: UnitData): UnitActionPose {
|
||||
if (unit.id === 'liu-bei') {
|
||||
return 'strategy';
|
||||
}
|
||||
if (unit.id === 'zhang-fei') {
|
||||
return 'attack';
|
||||
}
|
||||
return 'item';
|
||||
}
|
||||
|
||||
private playGrowthCelebration(sprite: Phaser.GameObjects.Sprite) {
|
||||
return new Promise<void>((resolve) => {
|
||||
this.tweens.add({
|
||||
targets: sprite,
|
||||
y: sprite.y - 18,
|
||||
scale: 1.16,
|
||||
angle: 3,
|
||||
duration: 160,
|
||||
yoyo: true,
|
||||
repeat: 2,
|
||||
ease: 'Sine.easeInOut',
|
||||
onComplete: () => {
|
||||
sprite.setAngle(0);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private combatPortraitKey(unit: UnitData) {
|
||||
@@ -3244,8 +3554,56 @@ export class BattleScene extends Phaser.Scene {
|
||||
) {
|
||||
target.setScale(Phaser.Math.Clamp(fromRatio, 0, 1), 1);
|
||||
text.setText(`${fromValue} / ${next}`);
|
||||
if (toValue !== fromValue || leveled) {
|
||||
soundDirector.playEffect('exp-gain', { volume: leveled ? 0.44 : 0.3, stopAfterMs: duration + 220 });
|
||||
|
||||
if (leveled) {
|
||||
soundDirector.playGrowthTick();
|
||||
return new Promise<void>((resolve) => {
|
||||
const firstTracker = { ratio: Phaser.Math.Clamp(fromRatio, 0, 1), value: fromValue };
|
||||
this.tweens.add({
|
||||
targets: firstTracker,
|
||||
ratio: 1,
|
||||
value: next,
|
||||
duration: Math.floor(duration * 0.62),
|
||||
ease: 'Sine.easeInOut',
|
||||
onUpdate: () => {
|
||||
target.setScale(firstTracker.ratio, 1);
|
||||
text.setText(`${Math.round(firstTracker.value)} / ${next}`);
|
||||
},
|
||||
onComplete: () => {
|
||||
soundDirector.playLevelUp();
|
||||
target.setScale(0, 1);
|
||||
text.setText(`0 / ${next}`);
|
||||
const secondTracker = { ratio: 0, value: 0 };
|
||||
this.tweens.add({
|
||||
targets: secondTracker,
|
||||
ratio: Phaser.Math.Clamp(toRatio, 0, 1),
|
||||
value: toValue,
|
||||
duration: Math.floor(duration * 0.46),
|
||||
ease: 'Sine.easeInOut',
|
||||
onUpdate: () => {
|
||||
target.setScale(secondTracker.ratio, 1);
|
||||
text.setText(`${Math.round(secondTracker.value)} / ${next}`);
|
||||
},
|
||||
onComplete: () => {
|
||||
target.setScale(Phaser.Math.Clamp(toRatio, 0, 1), 1);
|
||||
text.setText(`${toValue} / ${next}`);
|
||||
this.tweens.add({
|
||||
targets: text,
|
||||
scale: 1.18,
|
||||
duration: 120,
|
||||
yoyo: true,
|
||||
ease: 'Sine.easeOut'
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (toValue !== fromValue) {
|
||||
soundDirector.playGrowthTick();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
@@ -3464,26 +3822,40 @@ export class BattleScene extends Phaser.Scene {
|
||||
const partners = this.attackIntents
|
||||
.filter((intent) => intent.targetId === target.id && intent.attackerId !== unit.id)
|
||||
.map((intent) => intent.attackerId);
|
||||
let gained = 0;
|
||||
const growthResults: BondGrowthResult[] = [];
|
||||
|
||||
partners.forEach((partnerId) => {
|
||||
const bond = this.findBond(unit.id, partnerId);
|
||||
if (!bond) {
|
||||
return;
|
||||
}
|
||||
const previousLevel = bond.level;
|
||||
const previousExp = bond.exp;
|
||||
let leveled = false;
|
||||
bond.battleExp += 4;
|
||||
bond.exp += 4;
|
||||
while (bond.exp >= 100) {
|
||||
bond.exp -= 100;
|
||||
bond.level = Math.min(100, bond.level + 1);
|
||||
leveled = true;
|
||||
}
|
||||
gained += 4;
|
||||
const [first, second] = bond.unitIds.map((unitId) => this.unitName(unitId));
|
||||
growthResults.push({
|
||||
label: `${first}·${second}`,
|
||||
amount: 4,
|
||||
previousLevel,
|
||||
previousExp,
|
||||
level: bond.level,
|
||||
exp: bond.exp,
|
||||
next: 100,
|
||||
leveled
|
||||
});
|
||||
});
|
||||
|
||||
this.attackIntents = this.attackIntents.filter((intent) => intent.attackerId !== unit.id);
|
||||
this.attackIntents.push({ attackerId: unit.id, targetId: target.id });
|
||||
|
||||
return gained;
|
||||
return growthResults;
|
||||
}
|
||||
|
||||
private recordAttackIntent(unit: UnitData) {
|
||||
|
||||
Reference in New Issue
Block a user