Add four-way cannon and stimulant bonuses with XP multiplier and healing ticks
This commit is contained in:
parent
145341d992
commit
26d2dad489
Binary file not shown.
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
|
|
@ -30,8 +30,10 @@ export class Player {
|
||||||
this._baseFireInterval = 250; // ms between shots
|
this._baseFireInterval = 250; // ms between shots
|
||||||
this._isMoving = false;
|
this._isMoving = false;
|
||||||
this._isShooting = false;
|
this._isShooting = false;
|
||||||
this.weaponMode = 'default'; // 'default' | 'shotgun' | 'rocket'
|
this.weaponMode = 'default'; // 'default' | 'shotgun' | 'rocket' | 'fourway' | 'stimulant'
|
||||||
this.weaponTimeLeft = 0; // ms remaining for timed weapon
|
this.weaponTimeLeft = 0; // ms remaining for timed weapon
|
||||||
|
this.weaponDuration = 0; // initial duration of current weapon (for HUD ratio)
|
||||||
|
this._stimulantTickTimer = 0;
|
||||||
|
|
||||||
this._createAnims();
|
this._createAnims();
|
||||||
this._buildSprite(x, y);
|
this._buildSprite(x, y);
|
||||||
|
|
@ -82,6 +84,13 @@ export class Player {
|
||||||
this._updateRockets();
|
this._updateRockets();
|
||||||
if (this.weaponTimeLeft > 0) {
|
if (this.weaponTimeLeft > 0) {
|
||||||
this.weaponTimeLeft -= delta;
|
this.weaponTimeLeft -= delta;
|
||||||
|
if (this.weaponMode === 'stimulant') {
|
||||||
|
this._stimulantTickTimer -= delta;
|
||||||
|
if (this._stimulantTickTimer <= 0) {
|
||||||
|
this._stimulantTickTimer = 3000;
|
||||||
|
this.hp = Math.min(this.hp + this.stats.maxHp * 0.1, this.stats.maxHp);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.weaponTimeLeft <= 0) {
|
if (this.weaponTimeLeft <= 0) {
|
||||||
this.weaponTimeLeft = 0;
|
this.weaponTimeLeft = 0;
|
||||||
this.weaponMode = 'default';
|
this.weaponMode = 'default';
|
||||||
|
|
@ -118,6 +127,7 @@ export class Player {
|
||||||
this._fireCooldown = this._baseFireInterval / this.stats.fireRate;
|
this._fireCooldown = this._baseFireInterval / this.stats.fireRate;
|
||||||
if (this.weaponMode === 'shotgun') this._spawnShotgunBurst();
|
if (this.weaponMode === 'shotgun') this._spawnShotgunBurst();
|
||||||
else if (this.weaponMode === 'rocket') this._spawnRocket();
|
else if (this.weaponMode === 'rocket') this._spawnRocket();
|
||||||
|
else if (this.weaponMode === 'fourway') this._spawnFourWayBurst();
|
||||||
else this._spawnBullet();
|
else this._spawnBullet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -172,6 +182,20 @@ export class Player {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_spawnFourWayBurst() {
|
||||||
|
this.scene.sound.play('sfx-shoot', { volume: 0.4 });
|
||||||
|
[0, Math.PI / 2, Math.PI, Math.PI * 1.5].forEach(offset => {
|
||||||
|
const angle = this.facing + offset;
|
||||||
|
const bx = this.x + Math.cos(angle) * GUN_TIP_DIST;
|
||||||
|
const by = this.y + Math.sin(angle) * GUN_TIP_DIST;
|
||||||
|
const bullet = this.scene.add.circle(bx, by, BULLET_SIZE, 0xffff00);
|
||||||
|
this.scene.physics.add.existing(bullet);
|
||||||
|
bullet.body.setVelocity(Math.cos(angle) * BULLET_SPEED, Math.sin(angle) * BULLET_SPEED);
|
||||||
|
bullet.damage = this.stats.damage;
|
||||||
|
this.bullets.add(bullet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_spawnRocket() {
|
_spawnRocket() {
|
||||||
this.scene.sound.play('sfx-shoot', { volume: 0.5 });
|
this.scene.sound.play('sfx-shoot', { volume: 0.5 });
|
||||||
const bx = this.x + Math.cos(this.facing) * GUN_TIP_DIST;
|
const bx = this.x + Math.cos(this.facing) * GUN_TIP_DIST;
|
||||||
|
|
@ -193,15 +217,30 @@ export class Player {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get xpMultiplier() {
|
||||||
|
return this.weaponMode === 'stimulant' ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
applyBonus(type) {
|
applyBonus(type) {
|
||||||
if (type === 'medpack') {
|
if (type === 'medpack') {
|
||||||
this.hp = Math.min(this.hp + this.stats.maxHp * 0.5, this.stats.maxHp);
|
this.hp = Math.min(this.hp + this.stats.maxHp * 0.5, this.stats.maxHp);
|
||||||
} else if (type === 'shotgun') {
|
} else if (type === 'shotgun') {
|
||||||
this.weaponMode = 'shotgun';
|
this.weaponMode = 'shotgun';
|
||||||
this.weaponTimeLeft = 20000;
|
this.weaponTimeLeft = 20000;
|
||||||
|
this.weaponDuration = 20000;
|
||||||
} else if (type === 'rocket') {
|
} else if (type === 'rocket') {
|
||||||
this.weaponMode = 'rocket';
|
this.weaponMode = 'rocket';
|
||||||
this.weaponTimeLeft = 20000;
|
this.weaponTimeLeft = 20000;
|
||||||
|
this.weaponDuration = 20000;
|
||||||
|
} else if (type === 'fourway') {
|
||||||
|
this.weaponMode = 'fourway';
|
||||||
|
this.weaponTimeLeft = 20000;
|
||||||
|
this.weaponDuration = 20000;
|
||||||
|
} else if (type === 'stimulant') {
|
||||||
|
this.weaponMode = 'stimulant';
|
||||||
|
this.weaponTimeLeft = 15000;
|
||||||
|
this.weaponDuration = 15000;
|
||||||
|
this._stimulantTickTimer = 3000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export class GameScene extends Phaser.Scene {
|
||||||
this._enemyProjectiles = [];
|
this._enemyProjectiles = [];
|
||||||
|
|
||||||
// Event wiring
|
// Event wiring
|
||||||
this.events.on('enemy-killed', ({ xp }) => this.xpSystem.addXP(xp));
|
this.events.on('enemy-killed', ({ xp }) => this.xpSystem.addXP(xp * this.player.xpMultiplier));
|
||||||
this.events.on('enemy-projectile-spawned', proj => this._enemyProjectiles.push(proj));
|
this.events.on('enemy-projectile-spawned', proj => this._enemyProjectiles.push(proj));
|
||||||
this.events.on('level-up', level => this._showLevelUp(level));
|
this.events.on('level-up', level => this._showLevelUp(level));
|
||||||
this.events.on('game-over', () => this._onGameOver());
|
this.events.on('game-over', () => this._onGameOver());
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
const BONUS_LIFETIME_MS = 15000;
|
const BONUS_LIFETIME_MS = 15000;
|
||||||
const BLINK_START_MS = 10000; // blink when < 5s left (at 10s elapsed of 15s)
|
const BLINK_START_MS = 10000; // blink when < 5s left (at 10s elapsed of 15s)
|
||||||
const COOLDOWN_PICKUP = 20000;
|
const COOLDOWN_PICKUP = 10000;
|
||||||
const COOLDOWN_EXPIRE = 10000;
|
const COOLDOWN_EXPIRE = 10000;
|
||||||
const FIRST_SPAWN_MS = 20000;
|
const FIRST_SPAWN_MS = 20000;
|
||||||
const PICKUP_RADIUS = 32;
|
const PICKUP_RADIUS = 32;
|
||||||
const BONUS_TYPES = ['medpack', 'shotgun', 'rocket'];
|
const BONUS_TYPES = ['medpack', 'shotgun', 'rocket', 'fourway', 'stimulant'];
|
||||||
|
|
||||||
const FRAME_INDEX = { medpack: 0, shotgun: 1, rocket: 2 };
|
const FRAME_INDEX = { medpack: 0, shotgun: 1, rocket: 2, fourway: 3, stimulant: 4 };
|
||||||
const GLOW_COLOR = { medpack: 0x44ff44, shotgun: 0xff9900, rocket: 0xff3300 };
|
const GLOW_COLOR = { medpack: 0x44ff44, shotgun: 0xff9900, rocket: 0xff3300, fourway: 0x00ffff, stimulant: 0xaa44ff };
|
||||||
|
|
||||||
export class BonusManager {
|
export class BonusManager {
|
||||||
constructor(scene, player) {
|
constructor(scene, player) {
|
||||||
|
|
@ -19,8 +19,10 @@ export class BonusManager {
|
||||||
|
|
||||||
update(delta) {
|
update(delta) {
|
||||||
if (!this._active) {
|
if (!this._active) {
|
||||||
this._countdown -= delta;
|
if (this._player.weaponMode === 'default') {
|
||||||
if (this._countdown <= 0) this._spawn();
|
this._countdown -= delta;
|
||||||
|
if (this._countdown <= 0) this._spawn();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +59,7 @@ export class BonusManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
_spawn() {
|
_spawn() {
|
||||||
const type = BONUS_TYPES[Phaser.Math.Between(0, 2)];
|
const type = BONUS_TYPES[Phaser.Math.Between(0, BONUS_TYPES.length - 1)];
|
||||||
const cell = this._findSpawnCell();
|
const cell = this._findSpawnCell();
|
||||||
if (!cell) {
|
if (!cell) {
|
||||||
// No valid cell found — retry in 2 seconds
|
// No valid cell found — retry in 2 seconds
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,12 @@ export class HUD {
|
||||||
this._weaponBarFill.setVisible(hasWeapon);
|
this._weaponBarFill.setVisible(hasWeapon);
|
||||||
this._weaponLabel.setVisible(hasWeapon);
|
this._weaponLabel.setVisible(hasWeapon);
|
||||||
if (hasWeapon) {
|
if (hasWeapon) {
|
||||||
const ratio = Math.max(0, this.player.weaponTimeLeft / 20000);
|
const ratio = Math.max(0, this.player.weaponTimeLeft / this.player.weaponDuration);
|
||||||
this._weaponBarFill.width = 200 * ratio;
|
this._weaponBarFill.width = 200 * ratio;
|
||||||
const color = this.player.weaponMode === 'rocket' ? 0xff3300 : 0xff9900;
|
const colorMap = { shotgun: 0xff9900, rocket: 0xff3300, fourway: 0x00ffff, stimulant: 0xaa44ff };
|
||||||
this._weaponBarFill.setFillStyle(color);
|
const labelMap = { shotgun: 'SHOTGUN', rocket: 'ROCKET LAUNCHER', fourway: 'FOUR-WAY CANNON', stimulant: 'STIMULANT' };
|
||||||
this._weaponLabel.setText(this.player.weaponMode === 'rocket' ? 'ROCKET LAUNCHER' : 'SHOTGUN');
|
this._weaponBarFill.setFillStyle(colorMap[this.player.weaponMode] ?? 0xffffff);
|
||||||
|
this._weaponLabel.setText(labelMap[this.player.weaponMode] ?? '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue