|
|
@@ -138,6 +138,8 @@ export class SteelAssaultGame extends Component {
|
|
|
private score = 0;
|
|
|
private state: 'playing' | 'win' | 'next' | 'lose' = 'playing';
|
|
|
private bossTime = 0;
|
|
|
+ private bossEnrageAnnounced = false;
|
|
|
+ private bossRoarTimer = 0;
|
|
|
private elapsed = 0;
|
|
|
private spritesReady = false;
|
|
|
private heroSprite: Sprite | null = null;
|
|
|
@@ -271,7 +273,7 @@ export class SteelAssaultGame extends Component {
|
|
|
this.node.addChild(musicNode);
|
|
|
this.musicSource = musicNode.addComponent(AudioSource);
|
|
|
this.musicSource.loop = true;
|
|
|
- this.musicSource.volume = 0.22;
|
|
|
+ this.musicSource.volume = 0.34;
|
|
|
|
|
|
const sfxNode = new Node('RuntimeSfx');
|
|
|
sfxNode.layer = Layers.Enum.UI_2D;
|
|
|
@@ -653,7 +655,7 @@ if (!this.backgroundSprite) return;
|
|
|
if (!clip) return;
|
|
|
this.musicSource.clip = clip;
|
|
|
this.musicSource.loop = true;
|
|
|
- this.musicSource.volume = 0.22;
|
|
|
+ this.musicSource.volume = 0.34;
|
|
|
this.musicSource.play();
|
|
|
this.musicStarted = true;
|
|
|
}
|
|
|
@@ -664,6 +666,25 @@ if (!this.backgroundSprite) return;
|
|
|
this.sfxSource.playOneShot(clip, volume);
|
|
|
}
|
|
|
|
|
|
+ private updateBossEnrageAudio(enraged: boolean, dt: number) {
|
|
|
+ if (!enraged) {
|
|
|
+ this.bossEnrageAnnounced = false;
|
|
|
+ this.bossRoarTimer = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.bossEnrageAnnounced) {
|
|
|
+ this.playSfx('boss_alert', 1.0);
|
|
|
+ this.bossEnrageAnnounced = true;
|
|
|
+ this.bossRoarTimer = 1.8;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.bossRoarTimer -= dt;
|
|
|
+ if (this.bossRoarTimer <= 0) {
|
|
|
+ this.playSfx('boss_alert', 0.86);
|
|
|
+ this.bossRoarTimer = 2.4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private updatePlayer(dt: number) {
|
|
|
this.previousPlayerY = this.player.y;
|
|
|
const left = this.keys.has(KeyCode.KEY_A) || this.keys.has(KeyCode.ARROW_LEFT);
|
|
|
@@ -770,6 +791,7 @@ if (!this.backgroundSprite) return;
|
|
|
enemy.y = BOSS_Y + Math.sin(this.bossTime * 1.1) * U(10);
|
|
|
const hpRatio = enemy.hp / enemy.maxHp;
|
|
|
const phase = hpRatio <= BOSS_ENRAGE_RATIO ? 3 : hpRatio <= BOSS_PHASE_TWO_RATIO ? 2 : 1;
|
|
|
+ this.updateBossEnrageAudio(phase >= 3, dt);
|
|
|
if (enemy.cd <= 0 && this.canEnemyShoot(enemy, U(980))) {
|
|
|
const shotX = enemy.x - U(115);
|
|
|
const targetX = this.player.x;
|
|
|
@@ -1249,7 +1271,10 @@ if (!this.backgroundSprite) return;
|
|
|
const face = enemy.type === 'spore'
|
|
|
? (enemy.x > this.player.x ? 1 : -1)
|
|
|
: (enemy.x > this.player.x ? -1 : 1);
|
|
|
- const scale = enemy.type === 'boss' ? US(1.06) : enemy.type === 'turret' ? US(0.44) : enemy.type === 'wing' ? US(0.41) : US(0.51);
|
|
|
+ const bossEnraged = enemy.type === 'boss' && enemy.hp <= enemy.maxHp * BOSS_ENRAGE_RATIO;
|
|
|
+ const scale = enemy.type === 'boss'
|
|
|
+ ? (bossEnraged ? US(1.42) : US(1.24))
|
|
|
+ : enemy.type === 'turret' ? US(0.44) : enemy.type === 'wing' ? US(0.41) : US(0.51);
|
|
|
sprite.node.setScale(new Vec3(scale * face, scale, 1));
|
|
|
sprite.node.active = true;
|
|
|
}
|
|
|
@@ -1477,26 +1502,30 @@ if (!this.backgroundSprite) return;
|
|
|
}
|
|
|
|
|
|
private handleOutcomeAction(event: EventTouch | EventMouse) {
|
|
|
- const { x, y } = this.pointerToWorldPoint(event);
|
|
|
+ const points = this.pointerWorldPointCandidates(event);
|
|
|
if (this.state === 'win') {
|
|
|
- if (this.pointInRect(x, y, this.outcomeNextRect())) {
|
|
|
+ if (this.anyPointInRect(points, this.outcomeNextRect())) {
|
|
|
this.state = 'next';
|
|
|
this.clearMobileControls();
|
|
|
return true;
|
|
|
}
|
|
|
- if (this.pointInRect(x, y, this.outcomeRestartRect())) {
|
|
|
+ if (this.anyPointInRect(points, this.outcomeRestartRect())) {
|
|
|
this.reset();
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
- if ((this.state === 'lose' || this.state === 'next') && this.pointInRect(x, y, this.outcomeSingleRestartRect())) {
|
|
|
+ if ((this.state === 'lose' || this.state === 'next') && this.anyPointInRect(points, this.outcomeSingleRestartRect())) {
|
|
|
this.reset();
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ private anyPointInRect(points: Array<{ x: number; y: number }>, rect: UiRect) {
|
|
|
+ return points.some((point) => this.pointInRect(point.x, point.y, rect));
|
|
|
+ }
|
|
|
+
|
|
|
private pointInRect(x: number, y: number, rect: UiRect) {
|
|
|
return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
|
|
|
}
|
|
|
@@ -1793,8 +1822,12 @@ if (!this.backgroundSprite) return;
|
|
|
|
|
|
private onMouseDown(event: EventMouse) {
|
|
|
if (event.getButton() !== EventMouse.BUTTON_LEFT) return;
|
|
|
- if (this.state !== 'playing') return;
|
|
|
this.unlockAudio();
|
|
|
+ if (this.isOutcomeState()) {
|
|
|
+ if (this.handleOutcomeAction(event)) this.stopTouchEvent(event);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.state !== 'playing') return;
|
|
|
this.handlePointerStart(-1, event);
|
|
|
}
|
|
|
|
|
|
@@ -2040,6 +2073,8 @@ private switchButtonCenter() {
|
|
|
this.score = 0;
|
|
|
this.state = 'playing';
|
|
|
this.bossTime = 0;
|
|
|
+ this.bossEnrageAnnounced = false;
|
|
|
+ this.bossRoarTimer = 0;
|
|
|
this.elapsed = 0;
|
|
|
for (const sprite of this.enemySprites.values()) sprite.node.destroy();
|
|
|
for (const sprite of this.bulletSprites.values()) sprite.node.destroy();
|