Improve whale hunting mechanics with multi-hit system

- Changed to single whale spawning (one at a time)
- Added crosshair shake effect for increased difficulty (2px random shake)
- Implemented whale health system (3 hits to kill)
- Added dynamic health bar above whales (green -> yellow -> red)
- Visual feedback on hits: red flash, whale shake animation
- Health bar updates in real-time as whale takes damage
- New whales spawn 1.5 seconds after previous whale dies or leaves
- Separated hitWhale() and killWhale() functions for damage vs death
- Shows health status messages on each hit
- Much more challenging and engaging hunting gameplay

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Thomas Richter
2025-12-15 03:42:39 +01:00
parent 09a1b7a537
commit 9e6f1d24ea

View File

@@ -9,10 +9,11 @@ export default class HuntingScene extends Phaser.Scene {
this.inventory = data.inventory || { whaleOil: 0, fuel: 100, penguins: 0 };
this.whalesHunted = 0;
this.harpoons = [];
this.whales = [];
this.currentWhale = null;
this.crosshairX = 400;
this.crosshairY = 300;
this.crosshairSpeed = 5;
this.crosshairShakeAmount = 2; // Pixels of shake
this.useKeyboard = false; // Toggle between mouse and keyboard
}
@@ -26,15 +27,7 @@ export default class HuntingScene extends Phaser.Scene {
this.createCrosshair();
this.createInstructions();
// Spawn whales periodically
this.time.addEvent({
delay: 3000,
callback: this.spawnWhale,
callbackScope: this,
loop: true
});
// Spawn initial whale
// Spawn single whale
this.spawnWhale();
// Input setup
@@ -52,14 +45,16 @@ export default class HuntingScene extends Phaser.Scene {
this.updateCrosshairMouse();
}
// Update crosshair sprite position
this.crosshair.setPosition(this.crosshairX, this.crosshairY);
// Update crosshair sprite position with shake
const shakeX = (Math.random() - 0.5) * this.crosshairShakeAmount * 2;
const shakeY = (Math.random() - 0.5) * this.crosshairShakeAmount * 2;
this.crosshair.setPosition(this.crosshairX + shakeX, this.crosshairY + shakeY);
// Update harpoons
this.updateHarpoons();
// Update whales
this.updateWhales();
// Update whale
this.updateWhale();
// Check collisions
this.checkCollisions();
@@ -232,12 +227,17 @@ export default class HuntingScene extends Phaser.Scene {
}
spawnWhale() {
// Don't spawn if there's already a whale
if (this.currentWhale && this.currentWhale.getData('alive')) {
return;
}
// Random position and direction
const fromLeft = Math.random() > 0.5;
const x = fromLeft ? -50 : 850;
const y = 150 + Math.random() * 300;
const direction = fromLeft ? 1 : -1;
const speed = 1 + Math.random() * 1.5;
const speed = 0.8 + Math.random() * 0.7; // Slightly slower for single whale
// Create whale body
const whale = this.add.container(x, y);
@@ -258,39 +258,53 @@ export default class HuntingScene extends Phaser.Scene {
0x1a1a1a
);
whale.add([body, tail, fin]);
// Health bar background
const healthBg = this.add.rectangle(0, -35, 60, 6, 0x000000);
// Health bar (will be updated as whale takes damage)
const healthBar = this.add.rectangle(0, -35, 60, 6, 0x00ff00);
whale.add([body, tail, fin, healthBg, healthBar]);
whale.setData('direction', direction);
whale.setData('speed', speed);
whale.setData('alive', true);
whale.setData('health', 3); // Requires 3 hits
whale.setData('maxHealth', 3);
whale.setData('bodyWidth', 80);
whale.setData('bodyHeight', 40);
whale.setData('healthBar', healthBar);
// Flip if moving right
if (direction > 0) {
whale.setScale(-1, 1);
}
this.whales.push(whale);
this.currentWhale = whale;
}
updateWhales() {
for (let i = this.whales.length - 1; i >= 0; i--) {
const whale = this.whales[i];
updateWhale() {
if (!this.currentWhale) {
return;
}
if (!whale.getData('alive')) {
continue;
}
const whale = this.currentWhale;
// Move whale
const direction = whale.getData('direction');
const speed = whale.getData('speed');
whale.x += direction * speed;
if (!whale.getData('alive')) {
return;
}
// Remove if off screen
if (whale.x < -100 || whale.x > 900) {
whale.destroy();
this.whales.splice(i, 1);
}
// Move whale
const direction = whale.getData('direction');
const speed = whale.getData('speed');
whale.x += direction * speed;
// If whale goes off screen, spawn a new one
if (whale.x < -100 || whale.x > 900) {
whale.destroy();
this.currentWhale = null;
// Spawn new whale after a short delay
this.time.delayedCall(1500, () => {
this.spawnWhale();
});
}
}
@@ -333,6 +347,12 @@ export default class HuntingScene extends Phaser.Scene {
}
checkCollisions() {
if (!this.currentWhale || !this.currentWhale.getData('alive')) {
return;
}
const whale = this.currentWhale;
for (let h = this.harpoons.length - 1; h >= 0; h--) {
const harpoon = this.harpoons[h];
@@ -340,31 +360,74 @@ export default class HuntingScene extends Phaser.Scene {
continue;
}
for (let w = this.whales.length - 1; w >= 0; w--) {
const whale = this.whales[w];
// Simple collision detection
const distance = Phaser.Math.Distance.Between(
harpoon.x, harpoon.y,
whale.x, whale.y
);
if (!whale.getData('alive')) {
continue;
}
// Simple collision detection
const distance = Phaser.Math.Distance.Between(
harpoon.x, harpoon.y,
whale.x, whale.y
);
if (distance < 40) {
// Hit!
this.hitWhale(whale, harpoon);
break;
}
if (distance < 40) {
// Hit!
this.hitWhale(whale, harpoon);
break;
}
}
}
hitWhale(whale, harpoon) {
whale.setData('alive', false);
harpoon.setData('active', false);
harpoon.destroy();
// Reduce whale health
let health = whale.getData('health');
health -= 1;
whale.setData('health', health);
// Update health bar
const healthBar = whale.getData('healthBar');
const maxHealth = whale.getData('maxHealth');
const healthPercent = health / maxHealth;
healthBar.setDisplaySize(60 * healthPercent, 6);
// Change health bar color based on health
if (healthPercent > 0.6) {
healthBar.setFillStyle(0x00ff00); // Green
} else if (healthPercent > 0.3) {
healthBar.setFillStyle(0xffff00); // Yellow
} else {
healthBar.setFillStyle(0xff0000); // Red
}
// Hit flash effect
const hitFlash = this.add.circle(whale.x, whale.y, 50, 0xff0000, 0.6);
this.tweens.add({
targets: hitFlash,
alpha: 0,
scale: 1.5,
duration: 300,
onComplete: () => hitFlash.destroy()
});
// Whale damaged animation (shake)
this.tweens.add({
targets: whale,
x: whale.x + 5,
yoyo: true,
repeat: 2,
duration: 50
});
if (health <= 0) {
// Whale is dead!
this.killWhale(whale);
} else {
// Whale is still alive
this.showMessage(`Hit! Whale health: ${health}/${maxHealth}`);
}
}
killWhale(whale) {
whale.setData('alive', false);
// Check if enough fuel to process the whale
if (this.inventory.fuel < 2) {
@@ -376,11 +439,15 @@ export default class HuntingScene extends Phaser.Scene {
duration: 1000,
onComplete: () => {
whale.destroy();
this.currentWhale = null;
// Spawn new whale
this.time.delayedCall(1500, () => {
this.spawnWhale();
});
}
});
harpoon.destroy();
this.showMessage('Whale hit but no fuel to process it! Need 2 fuel to cook whale oil.');
this.showMessage('Whale killed but no fuel to process it! Need 2 fuel to cook whale oil.');
return;
}
@@ -393,12 +460,14 @@ export default class HuntingScene extends Phaser.Scene {
duration: 1000,
onComplete: () => {
whale.destroy();
this.currentWhale = null;
// Spawn new whale after delay
this.time.delayedCall(1500, () => {
this.spawnWhale();
});
}
});
// Remove harpoon
harpoon.destroy();
// Consume fuel for processing
this.inventory.fuel -= 2;
@@ -408,7 +477,7 @@ export default class HuntingScene extends Phaser.Scene {
this.updateStats();
// Show success message
this.showMessage(`Whale hunted! +1 Whale Oil, -2 Fuel (Oil: ${this.inventory.whaleOil}, Fuel: ${this.inventory.fuel})`);
this.showMessage(`Whale killed! +1 Whale Oil, -2 Fuel (Oil: ${this.inventory.whaleOil}, Fuel: ${this.inventory.fuel})`);
// Success flash
const flash = this.add.rectangle(400, 300, 800, 600, 0xffffff, 0.3);