/** * BreedingSystem.js * ================= * Manages animal breeding, family trees, and baby growth * * Features: * - Breeding compatibility checks (species, gender, age) * - Baby creation with inherited traits * - Growth stages: baby → young → adult * - Family tree tracking * - Breeding cooldowns * - Animal happiness/relationship system * * Uses: farm_animals_family_grid_1766099078030.tsx * children_5_growth_stages_1766097043062.tsx * * @author NovaFarma Team * @date 2025-12-22 */ export default class BreedingSystem { constructor(scene) { this.scene = scene; // Animal registry this.animals = new Map(); // id -> animal data this.families = new Map(); // family id -> family tree // Breeding pairs currently in progress this.breedingPairs = new Map(); // Timing constants (in milliseconds) this.BREEDING_COOLDOWN = 7 * 24 * 60 * 60 * 1000; // 7 days this.BABY_TO_YOUNG_TIME = 2 * 24 * 60 * 60 * 1000; // 2 days this.YOUNG_TO_ADULT_TIME = 5 * 24 * 60 * 60 * 1000; // 5 days // Debug mode: faster times for testing if (this.scene.registry.get('debugMode')) { this.BREEDING_COOLDOWN = 30000; // 30 seconds this.BABY_TO_YOUNG_TIME = 10000; // 10 seconds this.YOUNG_TO_ADULT_TIME = 20000; // 20 seconds } // Species data this.speciesData = this.defineSpecies(); // Growth sprites this.growthSprites = this.defineGrowthSprites(); console.log('🐄 BreedingSystem initialized'); } defineSpecies() { return { cow: { name: 'Cow', basePrice: 500, products: ['milk', 'cheese'], gestationTime: 3.5 * 24 * 60 * 60 * 1000, // 3.5 days breedingCooldown: 7 * 24 * 60 * 60 * 1000, lifespan: 100 * 24 * 60 * 60 * 1000, // 100 days sprites: { male_baby: 'cow_male_baby', male_young: 'cow_male_young', male_adult: 'cow_male_adult', female_baby: 'cow_female_baby', female_young: 'cow_female_young', female_adult: 'cow_female_adult' } }, chicken: { name: 'Chicken', basePrice: 100, products: ['egg', 'feather'], gestationTime: 21 * 60 * 60 * 1000, // 21 hours breedingCooldown: 3 * 24 * 60 * 60 * 1000, // 3 days lifespan: 50 * 24 * 60 * 60 * 1000, sprites: { male_baby: 'chicken_male_baby', male_young: 'chicken_male_young', male_adult: 'chicken_rooster_adult', female_baby: 'chicken_female_baby', female_young: 'chicken_female_young', female_adult: 'chicken_hen_adult' } }, pig: { name: 'Pig', basePrice: 300, products: ['truffle'], gestationTime: 3 * 24 * 60 * 60 * 1000, breedingCooldown: 5 * 24 * 60 * 60 * 1000, lifespan: 80 * 24 * 60 * 60 * 1000, sprites: { male_baby: 'pig_male_baby', male_young: 'pig_male_young', male_adult: 'pig_male_adult', female_baby: 'pig_female_baby', female_young: 'pig_female_young', female_adult: 'pig_female_adult' } }, sheep: { name: 'Sheep', basePrice: 400, products: ['wool', 'milk'], gestationTime: 4 * 24 * 60 * 60 * 1000, breedingCooldown: 6 * 24 * 60 * 60 * 1000, lifespan: 90 * 24 * 60 * 60 * 1000, sprites: { male_baby: 'sheep_male_baby', male_young: 'sheep_male_young', male_adult: 'sheep_ram_adult', female_baby: 'sheep_female_baby', female_young: 'sheep_female_young', female_adult: 'sheep_ewe_adult' } } }; } defineGrowthSprites() { return { baby: 0, // Frame index young: 1, adult: 2 }; } // ===== ANIMAL REGISTRATION ===== registerAnimal(animalData) { const id = animalData.id || this.generateId(); const animal = { id, species: animalData.species, gender: animalData.gender || this.randomGender(), age: animalData.age || 'adult', name: animalData.name || this.generateName(animalData.species, animalData.gender), birthDate: animalData.birthDate || Date.now(), parents: animalData.parents || null, // [parentId1, parentId2] children: animalData.children || [], sprite: animalData.sprite || null, happiness: animalData.happiness || 100, lastBred: animalData.lastBred || null, x: animalData.x || 0, y: animalData.y || 0 }; this.animals.set(id, animal); // Create sprite if needed if (!animal.sprite && animalData.x && animalData.y) { animal.sprite = this.createAnimalSprite(animal); } // Setup growth timer if not adult if (animal.age !== 'adult') { this.setupGrowthTimer(animal); } console.log(`🐾 Registered ${animal.species} (${animal.gender}) - ${animal.name}`); return animal; } createAnimalSprite(animal) { const speciesData = this.speciesData[animal.species]; const spriteKey = `${animal.species}_${animal.gender}_${animal.age}`; const sprite = this.scene.add.sprite( animal.x, animal.y, 'farm_animals_family_grid', // tileset name this.getAnimalFrame(animal.species, animal.gender, animal.age) ); sprite.setData('animalId', animal.id); sprite.setInteractive(); // Click to view animal details sprite.on('pointerdown', () => { this.scene.events.emit('animalClicked', animal); }); return sprite; } getAnimalFrame(species, gender, age) { // Map to sprite sheet frames // This depends on your actual sprite sheet layout const frameMap = { cow: { male: { baby: 0, young: 1, adult: 2 }, female: { baby: 3, young: 4, adult: 5 } }, chicken: { male: { baby: 6, young: 7, adult: 8 }, female: { baby: 9, young: 10, adult: 11 } }, pig: { male: { baby: 12, young: 13, adult: 14 }, female: { baby: 15, young: 16, adult: 17 } }, sheep: { male: { baby: 18, young: 19, adult: 20 }, female: { baby: 21, young: 22, adult: 23 } } }; return frameMap[species]?.[gender]?.[age] || 0; } // ===== BREEDING MECHANICS ===== canBreed(animalId1, animalId2) { const animal1 = this.animals.get(animalId1); const animal2 = this.animals.get(animalId2); if (!animal1 || !animal2) { return { canBreed: false, reason: 'Animal not found' }; } // Must be same species if (animal1.species !== animal2.species) { return { canBreed: false, reason: 'Different species cannot breed' }; } // Must be opposite genders if (animal1.gender === animal2.gender) { return { canBreed: false, reason: 'Same gender cannot breed' }; } // Both must be adults if (animal1.age !== 'adult' || animal2.age !== 'adult') { return { canBreed: false, reason: 'Both must be adults' }; } // Check happiness (must be >50 to breed) if (animal1.happiness < 50 || animal2.happiness < 50) { return { canBreed: false, reason: 'Animals must be happy (>50 happiness)' }; } // Check breeding cooldown const now = Date.now(); if (animal1.lastBred && (now - animal1.lastBred) < this.BREEDING_COOLDOWN) { const waitTime = Math.ceil((this.BREEDING_COOLDOWN - (now - animal1.lastBred)) / 1000 / 60); return { canBreed: false, reason: `${animal1.name} needs ${waitTime} more minutes` }; } if (animal2.lastBred && (now - animal2.lastBred) < this.BREEDING_COOLDOWN) { const waitTime = Math.ceil((this.BREEDING_COOLDOWN - (now - animal2.lastBred)) / 1000 / 60); return { canBreed: false, reason: `${animal2.name} needs ${waitTime} more minutes` }; } // Check barn capacity if (this.scene.progressionSystem) { const barnBenefits = this.scene.progressionSystem.getCurrentBenefits('barn'); const currentAnimals = this.animals.size; if (currentAnimals >= barnBenefits.capacity) { return { canBreed: false, reason: 'Barn is full - upgrade needed!' }; } } return { canBreed: true }; } breed(animalId1, animalId2) { const check = this.canBreed(animalId1, animalId2); if (!check.canBreed) { this.scene.uiSystem?.showError(check.reason); console.warn('Cannot breed:', check.reason); return null; } const parent1 = this.animals.get(animalId1); const parent2 = this.animals.get(animalId2); // Determine which is female (for gestation) const female = parent1.gender === 'female' ? parent1 : parent2; const male = parent1.gender === 'male' ? parent1 : parent2; // Get gestation time const speciesData = this.speciesData[parent1.species]; const gestationTime = speciesData.gestationTime; // Start gestation period this.scene.time.delayedCall(gestationTime, () => { this.giveBirth(female, male); }); // Update breeding timestamps parent1.lastBred = Date.now(); parent2.lastBred = Date.now(); // Show notification this.scene.uiSystem?.showNotification({ title: '💕 Breeding Started!', message: `${male.name} and ${female.name} are breeding...`, icon: parent1.species, duration: 3000 }); // Add to breeding pairs this.breedingPairs.set(`${animalId1}_${animalId2}`, { parent1: animalId1, parent2: animalId2, startTime: Date.now(), endTime: Date.now() + gestationTime }); console.log(`💕 ${male.name} (♂) + ${female.name} (♀) breeding...`); return true; } giveBirth(mother, father) { // Create baby const baby = this.createBaby(mother, father); // Update parent records mother.children.push(baby.id); father.children.push(baby.id); // Show notification this.scene.uiSystem?.showNotification({ title: '🎉 Baby Born!', message: `${mother.name} gave birth to ${baby.name}!`, icon: baby.species, duration: 5000, color: '#FFD700' }); // Create family tree if doesn't exist const familyId = mother.parents?.[0] || mother.id; if (!this.families.has(familyId)) { this.families.set(familyId, { id: familyId, founder: mother, members: [] }); } this.families.get(familyId).members.push(baby.id); // Emit event this.scene.events.emit('babyBorn', { mother, father, baby }); // Play birth animation/particles if (this.scene.particleSystem) { this.scene.particleSystem.createBirthEffect(mother.sprite.x, mother.sprite.y); } console.log(`🍼 Baby born: ${baby.name} (${baby.species})`); return baby; } createBaby(parent1, parent2) { const baby = { id: this.generateId(), species: parent1.species, age: 'baby', gender: this.randomGender(), parents: [parent1.id, parent2.id], children: [], birthDate: Date.now(), happiness: 100, lastBred: null, // Spawn near mother x: parent1.x + Phaser.Math.Between(-20, 20), y: parent1.y + Phaser.Math.Between(-20, 20), sprite: null }; // Generate name baby.name = this.generateName(baby.species, baby.gender); // Create sprite baby.sprite = this.createAnimalSprite(baby); // Register animal this.animals.set(baby.id, baby); // Setup growth timer this.setupGrowthTimer(baby); return baby; } setupGrowthTimer(animal) { if (animal.age === 'baby') { // Baby → Young this.scene.time.delayedCall(this.BABY_TO_YOUNG_TIME, () => { this.growAnimal(animal, 'young'); }); } else if (animal.age === 'young') { // Young → Adult this.scene.time.delayedCall(this.YOUNG_TO_ADULT_TIME, () => { this.growAnimal(animal, 'adult'); }); } } growAnimal(animal, newAge) { const oldAge = animal.age; animal.age = newAge; // Update sprite if (animal.sprite) { const newFrame = this.getAnimalFrame(animal.species, animal.gender, newAge); animal.sprite.setFrame(newFrame); // Play growth animation this.scene.tweens.add({ targets: animal.sprite, scale: { from: 0.8, to: 1.0 }, duration: 500, ease: 'Back.easeOut' }); } // Show notification this.scene.uiSystem?.showNotification({ title: `${animal.name} Grew Up!`, message: `${oldAge} → ${newAge}`, icon: animal.species, duration: 3000 }); // If became adult, can now breed if (newAge === 'adult') { this.scene.events.emit('animalBecameAdult', animal); } // Continue growth timer if not yet adult if (newAge !== 'adult') { this.setupGrowthTimer(animal); } console.log(`📅 ${animal.name} grew from ${oldAge} to ${newAge}`); } // ===== HELPER FUNCTIONS ===== generateId() { return `animal_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } randomGender() { return Math.random() > 0.5 ? 'male' : 'female'; } generateName(species, gender) { const names = { cow: { male: ['Toro', 'Bruno', 'Max', 'Duke'], female: ['Daisy', 'Bessie', 'Bella', 'Rosie'] }, chicken: { male: ['Rocky', 'Cluck', 'Red', 'Rex'], female: ['Penny', 'Clucky', 'Henrietta', 'Ginger'] }, pig: { male: ['Porky', 'Hamlet', 'Bacon', 'Wilbur'], female: ['Petunia', 'Piglet', 'Penelope', 'Truffle'] }, sheep: { male: ['Woolly', 'Ram', 'Cloud', 'Fluff'], female: ['Fluffy', 'Snowball', 'Cotton', 'Lamb'] } }; const nameList = names[species]?.[gender] || ['Animal']; return nameList[Math.floor(Math.random() * nameList.length)]; } // ===== GETTERS ===== getAnimal(animalId) { return this.animals.get(animalId); } getAllAnimals() { return Array.from(this.animals.values()); } getAnimalsBySpecies(species) { return this.getAllAnimals().filter(a => a.species === species); } getAnimalsByGender(gender) { return this.getAllAnimals().filter(a => a.gender === gender); } getAdults() { return this.getAllAnimals().filter(a => a.age === 'adult'); } getBabies() { return this.getAllAnimals().filter(a => a.age === 'baby'); } getBreedablePairs() { const adults = this.getAdults(); const pairs = []; for (let i = 0; i < adults.length; i++) { for (let j = i + 1; j < adults.length; j++) { const check = this.canBreed(adults[i].id, adults[j].id); if (check.canBreed) { pairs.push([adults[i], adults[j]]); } } } return pairs; } getFamilyTree(animalId) { const animal = this.animals.get(animalId); if (!animal) return null; return { animal, parents: animal.parents ? animal.parents.map(id => this.animals.get(id)) : [], children: animal.children.map(id => this.animals.get(id)), grandparents: this.getGrandparents(animal), siblings: this.getSiblings(animal) }; } getGrandparents(animal) { if (!animal.parents) return []; const grandparents = []; for (const parentId of animal.parents) { const parent = this.animals.get(parentId); if (parent && parent.parents) { grandparents.push(...parent.parents.map(id => this.animals.get(id))); } } return grandparents.filter(Boolean); } getSiblings(animal) { if (!animal.parents) return []; const siblings = []; for (const parentId of animal.parents) { const parent = this.animals.get(parentId); if (parent && parent.children) { siblings.push(...parent.children .filter(id => id !== animal.id) .map(id => this.animals.get(id)) ); } } return [...new Set(siblings)]; // Remove duplicates } // ===== UPDATE ===== update(time, delta) { // Update breeding pairs progress for (const [pairId, pair] of this.breedingPairs.entries()) { if (Date.now() >= pair.endTime) { this.breedingPairs.delete(pairId); } } } // ===== CLEANUP ===== destroy() { this.animals.clear(); this.families.clear(); this.breedingPairs.clear(); } }