358 lines
8.9 KiB
Markdown
358 lines
8.9 KiB
Markdown
# ✨ VFX & JUICE IMPLEMENTATION GUIDE
|
|
**Project:** Mrtva Dolina (DolinaSmrti)
|
|
**Last Updated:** 2026-01-05 19:28 CET
|
|
**Status:** 7/13 Complete (54%)
|
|
|
|
---
|
|
|
|
## ✅ **COMPLETED VFX (7/13)**
|
|
|
|
1. ✅ **Background Removal Automation** - Script ready (`batch_cleanup_all_assets.py`)
|
|
2. ✅ **Particle Library** - 6/6 sprites (smoke, dust, sparkle, blood, leaf, fire)
|
|
3. ✅ **Tool Swing Arc** - Visual feedback for tool usage
|
|
4. ✅ **Crop Growth Sparkle** - Growth stage transitions
|
|
5. ✅ **Dirt Particles** - Digging/hoeing feedback
|
|
6. ✅ **Water Splash** - Watering can effect
|
|
7. ✅ **Harvest Pop** - Crop collection feedback
|
|
|
|
---
|
|
|
|
## 🔴 **NEEDED VFX (6/13)**
|
|
|
|
### **1. Screen Shake System** 🔴
|
|
**Purpose:** Impact feedback for combat, explosions, building collapse
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class ScreenShakeSystem {
|
|
shake(duration, intensity) {
|
|
// Camera shake with decay
|
|
this.scene.cameras.main.shake(duration, intensity);
|
|
}
|
|
|
|
impactShake() {
|
|
this.shake(200, 0.01); // Heavy hit
|
|
}
|
|
|
|
explosionShake() {
|
|
this.shake(400, 0.015); // Explosion
|
|
}
|
|
|
|
subtleShake() {
|
|
this.shake(100, 0.005); // Tool use
|
|
}
|
|
}
|
|
```
|
|
|
|
**Triggers:**
|
|
- Zombie hit: 200ms, 0.008 intensity
|
|
- Raider attack: 250ms, 0.01 intensity
|
|
- Building collapse: 500ms, 0.02 intensity
|
|
- Tree falling: 300ms, 0.012 intensity
|
|
- Explosion/bomb: 400ms, 0.015 intensity
|
|
|
|
---
|
|
|
|
### **2. Flash Effects** 🔴
|
|
**Purpose:** State transitions, damage feedback, special events
|
|
|
|
**Types:**
|
|
- **Damage Flash:** Red (255, 0, 0), 100ms
|
|
- **Heal Flash:** Green (0, 255, 0), 150ms
|
|
- **Level Up Flash:** Gold (255, 215, 0), 300ms
|
|
- **Raid Warning:** Red pulse (255, 0, 0), 1000ms
|
|
- **Victory Flash:** white (255, 255, 255), 500ms
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class FlashSystem {
|
|
damageFlash() {
|
|
this.scene.cameras.main.flash(100, 255, 0, 0);
|
|
}
|
|
|
|
healFlash() {
|
|
this.scene.cameras.main.flash(150, 0, 255, 0);
|
|
}
|
|
|
|
levelUpFlash() {
|
|
this.scene.cameras.main.flash(300, 255, 215, 0);
|
|
}
|
|
|
|
raidWarningFlash() {
|
|
this.scene.cameras.main.flash(1000, 255, 0, 0, true); // Force visible
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **3. Floating Damage Numbers** 🔴
|
|
**Purpose:** Combat feedback showing exact damage/healing
|
|
|
|
**Specifications:**
|
|
- **Font:** Bold, 24px
|
|
- **Color Coding:**
|
|
- Damage: Red `#FF0000`
|
|
- Healing: Green `#00FF00`
|
|
- Critical: Yellow `#FFD700`
|
|
- XP Gain: Blue `#00BFFF`
|
|
- **Animation:** Float up 40px, fade out over 1second
|
|
- **Outline:** Black stroke, 3px
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class FloatingTextSystem {
|
|
showDamage(x, y, amount, isCrit = false) {
|
|
const color = isCrit ? '#FFD700' : '#FF0000';
|
|
const text = this.scene.add.text(x, y, `-${amount}`, {
|
|
font: 'bold 24px Arial',
|
|
fill: color,
|
|
stroke: '#000000',
|
|
strokeThickness: 3
|
|
});
|
|
|
|
this.scene.tweens.add({
|
|
targets: text,
|
|
y: y - 40,
|
|
alpha: 0,
|
|
duration: 1000,
|
|
onComplete: () => text.destroy()
|
|
});
|
|
}
|
|
|
|
showHeal(x, y, amount) {
|
|
// Similar with green color
|
|
}
|
|
|
|
showXP(x, y, amount) {
|
|
// Similar with blue color
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **4. Hit Stun/Knockback** 🔴
|
|
**Purpose:** Enemy reaction to damage
|
|
|
|
**Specifications:**
|
|
- **Hit Stun:** Freeze enemy for 100-200ms
|
|
- **Knockback:** Push enemy away from damage source
|
|
- Distance: 20-50px based on damage
|
|
- Duration: 300ms
|
|
- Ease: Quad.easeOut
|
|
- **Tint Flash:** Red tint (0xFF0000) for 100ms on hit
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class HitStunSystem {
|
|
applyHitStun(enemy, damage, sourceX, sourceY) {
|
|
// Freeze
|
|
enemy.setVelocity(0, 0);
|
|
|
|
// Red tint
|
|
enemy.setTint(0xFF0000);
|
|
this.scene.time.delayedCall(100, () => enemy.clearTint());
|
|
|
|
// Knockback
|
|
const angle = Phaser.Math.Angle.Between(sourceX, sourceY, enemy.x, enemy.y);
|
|
const knockbackForce = Math.min(50, damage * 2);
|
|
|
|
this.scene.tweens.add({
|
|
targets: enemy,
|
|
x: enemy.x + Math.cos(angle) * knockbackForce,
|
|
y: enemy.y + Math.sin(angle) * knockbackForce,
|
|
duration: 300,
|
|
ease: 'Quad.easeOut'
|
|
});
|
|
|
|
// Resume movement after 200ms
|
|
this.scene.time.delayedCall(200, () => enemy.resumeMovement());
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **5. Building Construction Progress** 🔴
|
|
**Purpose:** Visual feedback for building restoration
|
|
|
|
**Elements:**
|
|
- **Progress Bar:** Above building
|
|
- Width: 100px
|
|
- Height: 8px
|
|
- Background: Black `#000000`
|
|
- Fill: Green `#00FF00` → Yellow `#FFFF00` → Red (reverse, 100% = green)
|
|
- Border: 2px white
|
|
- **Particle Effects:**
|
|
- Dust clouds every 2 seconds during construction
|
|
- Hammer sparks (if NPC working)
|
|
- Completion burst (confetti/sparkles)
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class ConstructionVFX {
|
|
createProgressBar(building) {
|
|
const bar = this.scene.add.graphics();
|
|
building.progressBar = bar;
|
|
this.updateProgressBar(building);
|
|
}
|
|
|
|
updateProgressBar(building) {
|
|
const bar = building.progressBar;
|
|
bar.clear();
|
|
|
|
const progress = building.constructionProgress / 100;
|
|
const color = Phaser.Display.Color.Interpolate.ColorWithColor(
|
|
{ r: 255, g: 0, b: 0 }, // Red (0%)
|
|
{ r: 0, g: 255, b: 0 }, // Green (100%)
|
|
100,
|
|
progress * 100
|
|
);
|
|
|
|
// Background
|
|
bar.fillStyle(0x000000);
|
|
bar.fillRect(building.x - 50, building.y - 60, 100, 8);
|
|
|
|
// Progress
|
|
bar.fillStyle(Phaser.Display.Color.GetColor(color.r, color.g, color.b));
|
|
bar.fillRect(building.x - 50, building.y - 60, 100 * progress, 8);
|
|
|
|
// Border
|
|
bar.lineStyle(2, 0xFFFFFF);
|
|
bar.strokeRect(building.x - 50, building.y - 60, 100, 8);
|
|
}
|
|
|
|
completionBurst(x, y) {
|
|
// Confetti explosion
|
|
const particles = this.scene.add.particles('particle_sparkle');
|
|
const emitter = particles.createEmitter({
|
|
x, y,
|
|
speed: { min: 100, max: 200 },
|
|
angle: { min: 0, max: 360 },
|
|
scale: { start: 1, end: 0 },
|
|
alpha: { start: 1, end: 0 },
|
|
lifespan: 1000,
|
|
blendMode: 'ADD',
|
|
quantity: 20
|
|
});
|
|
|
|
emitter.explode();
|
|
this.scene.time.delayedCall(1500, () => particles.destroy());
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### **6. Death Animations** 🔴
|
|
**Purpose:** Enemy/NPC death feedback
|
|
|
|
**Zombie Death:**
|
|
- Fade to black
|
|
- Scale down 50%
|
|
- Dust particle burst
|
|
- Blood splatter (optional, toggle)
|
|
- Duration: 800ms
|
|
|
|
**Raider Death:**
|
|
- Spin 180° while falling
|
|
- Fade out
|
|
- Weapon drop
|
|
- Duration: 1000ms
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
class DeathAnimationSystem {
|
|
zombieDeath(zombie) {
|
|
// Particle burst
|
|
this.scene.vfxSystem.playParticleBurst(zombie.x, zombie.y, 'dust', 15);
|
|
|
|
// Fade + shrink
|
|
this.scene.tweens.add({
|
|
targets: zombie,
|
|
alpha: 0,
|
|
scale: 0.5,
|
|
tint: 0x000000,
|
|
duration: 800,
|
|
ease: 'Quad.easeIn',
|
|
onComplete: () => {
|
|
zombie.destroy();
|
|
this.dropLoot(zombie.x, zombie.y);
|
|
}
|
|
});
|
|
}
|
|
|
|
raiderDeath(raider) {
|
|
// Drop weapon
|
|
this.dropWeapon(raider.x, raider.y, raider.weaponType);
|
|
|
|
// Spin + fade
|
|
this.scene.tweens.add({
|
|
targets: raider,
|
|
angle: 180,
|
|
alpha: 0,
|
|
y: raider.y + 20,
|
|
duration: 1000,
|
|
ease: 'Quad.easeOut',
|
|
onComplete: () => {
|
|
raider.destroy();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 **VFX IMPLEMENTATION PRIORITIES**
|
|
|
|
1. **Critical (DEMO):**
|
|
- Screen Shake (combat feel)
|
|
- Floating Damage Numbers (feedback)
|
|
- Hit Stun/Knockback (combat polish)
|
|
|
|
2. **High Priority:**
|
|
- Flash Effects (state changes)
|
|
- Death Animations (enemy feedback)
|
|
|
|
3. **Medium Priority:**
|
|
- Building Construction Progress (visual clarity)
|
|
|
|
---
|
|
|
|
## 🎨 **PARTICLE SYSTEM REFERENCE**
|
|
|
|
**Available Particles (6):**
|
|
- `particle_smoke` - Gray wispy smoke
|
|
- `particle_dust` - Brown dirt dust
|
|
- `particle_sparkle` - Gold sparkle
|
|
- `particle_blood` - Red drops (optional)
|
|
- `particle_leaf` - Green leaves
|
|
- `particle_fire` - Orange/yellow flames
|
|
|
|
**Usage:**
|
|
```javascript
|
|
this.scene.vfxSystem.playParticleBurst(x, y, 'particleType', quantity);
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ **COMPLETION CHECKLIST**
|
|
|
|
- [x] Particle library sprites
|
|
- [x] Tool swing arc
|
|
- [x] Crop growth sparkle
|
|
- [x] Dirt particles
|
|
- [x] Water splash
|
|
- [x] Harvest pop
|
|
- [x] Background removal automation
|
|
- [ ] Screen shake system
|
|
- [ ] Flash effects
|
|
- [ ] Floating damage numbers
|
|
- [ ] Hit stun/knockback
|
|
- [ ] Building construction progress
|
|
- [ ] Death animations
|
|
|
|
**Next Steps:** Implement remaining 6 VFX systems in `src/systems/VFXSystem.js`
|