✅ NEW SYSTEMS: 1. AnimalBehavior.js - Wander, Flee, Follow, Glowing Eyes 2. NPCIdleBehavior.js - Idle animations (hat fix, sweat wipe) 3. ANIMAL_SOUND_MANIFEST.md - 40+ sound mappings 🎮 AI FEATURES: - Smooth movement (not grid-based) ✓ - Flee from player (100px range) ✓ - Follow with delay (cargo animals: llama, horse, donkey) ✓ - Glowing eyes in darkness (NOIR EFFECT!) ✓ - Neon pink/green eyes visible at distance - Perfect for 'eyes in forest' atmosphere 👤 NPC FEATURES: - Random idle animations every 3-8 seconds - Fix hat, wipe sweat, look around, scratch head - Gronk-specific: coin counting, item polishing 🔊 SOUND SYSTEM: - Kenney pack mapping for 23 animal sounds - Footstep variations (mud, grass, cobble) - Noir sound design: muffled distant sounds + reverb DEMO: 56/60 sprites (93%)! Next: 4 remaining sprites + integration testing
154 lines
4.4 KiB
JavaScript
154 lines
4.4 KiB
JavaScript
/**
|
|
* 👤 NPC IDLE BEHAVIOR SYSTEM
|
|
* "Krvava Žetev" - Gothic Farm Game
|
|
*
|
|
* Makes NPCs feel alive with random idle animations:
|
|
* - Fix hat
|
|
* - Wipe sweat
|
|
* - Look around
|
|
* - Scratch head
|
|
* - Adjust clothes
|
|
*/
|
|
|
|
export class NPCIdleBehavior {
|
|
constructor(scene, sprite, npcType) {
|
|
this.scene = scene;
|
|
this.sprite = sprite;
|
|
this.npcType = npcType;
|
|
|
|
// Idle animation timing
|
|
this.idleTimer = 0;
|
|
this.nextIdleTime = Phaser.Math.Between(3000, 8000);
|
|
|
|
// Available idle animations
|
|
this.idleAnimations = this.getIdleAnimations();
|
|
this.currentIdle = null;
|
|
}
|
|
|
|
getIdleAnimations() {
|
|
// Different NPCs have different idle behaviors
|
|
const genericIdles = [
|
|
'fix_hat',
|
|
'wipe_sweat',
|
|
'look_around',
|
|
'scratch_head',
|
|
'adjust_clothes',
|
|
'yawn',
|
|
'stretch'
|
|
];
|
|
|
|
const npcSpecific = {
|
|
'gronk': [...genericIdles, 'adjust_apron', 'count_coins', 'polish_item'],
|
|
'farmer': [...genericIdles, 'lean_on_fence', 'check_watch', 'spit'],
|
|
'merchant': [...genericIdles, 'fix_glasses', 'write_note', 'arrange_goods'],
|
|
'default': genericIdles
|
|
};
|
|
|
|
return npcSpecific[this.npcType] || npcSpecific.default;
|
|
}
|
|
|
|
update(time, delta) {
|
|
if (!this.sprite.active) return;
|
|
|
|
this.idleTimer += delta;
|
|
|
|
// Trigger random idle animation
|
|
if (this.idleTimer >= this.nextIdleTime && !this.currentIdle) {
|
|
this.playRandomIdle();
|
|
this.idleTimer = 0;
|
|
this.nextIdleTime = Phaser.Math.Between(3000, 8000);
|
|
}
|
|
}
|
|
|
|
playRandomIdle() {
|
|
const idle = Phaser.Utils.Array.GetRandom(this.idleAnimations);
|
|
this.currentIdle = idle;
|
|
|
|
// Play animation if it exists
|
|
const animKey = `${this.npcType}_${idle}`;
|
|
|
|
if (this.sprite.anims && this.sprite.anims.exists(animKey)) {
|
|
this.sprite.play(animKey);
|
|
|
|
// Return to default idle after animation
|
|
this.sprite.on('animationcomplete', () => {
|
|
this.sprite.play(`${this.npcType}_idle`);
|
|
this.currentIdle = null;
|
|
}, this);
|
|
} else {
|
|
// Fallback: simple visual effects for idle
|
|
this.playFallbackIdle(idle);
|
|
}
|
|
}
|
|
|
|
playFallbackIdle(idleType) {
|
|
// Create simple tween-based idle behaviors as fallback
|
|
switch (idleType) {
|
|
case 'fix_hat':
|
|
this.scene.tweens.add({
|
|
targets: this.sprite,
|
|
angle: -5,
|
|
duration: 200,
|
|
yoyo: true,
|
|
onComplete: () => {
|
|
this.currentIdle = null;
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'wipe_sweat':
|
|
this.scene.tweens.add({
|
|
targets: this.sprite,
|
|
scaleY: 0.98,
|
|
duration: 300,
|
|
yoyo: true,
|
|
onComplete: () => {
|
|
this.currentIdle = null;
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'look_around':
|
|
this.scene.tweens.add({
|
|
targets: this.sprite,
|
|
scaleX: -Math.abs(this.sprite.scaleX),
|
|
duration: 400,
|
|
yoyo: true,
|
|
onComplete: () => {
|
|
this.currentIdle = null;
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'scratch_head':
|
|
this.scene.tweens.add({
|
|
targets: this.sprite,
|
|
y: this.sprite.y - 5,
|
|
duration: 150,
|
|
yoyo: true,
|
|
repeat: 2,
|
|
onComplete: () => {
|
|
this.currentIdle = null;
|
|
}
|
|
});
|
|
break;
|
|
|
|
default:
|
|
// Generic bob
|
|
this.scene.tweens.add({
|
|
targets: this.sprite,
|
|
y: this.sprite.y - 3,
|
|
duration: 200,
|
|
yoyo: true,
|
|
onComplete: () => {
|
|
this.currentIdle = null;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.sprite = null;
|
|
}
|
|
}
|