592 lines
17 KiB
JavaScript
592 lines
17 KiB
JavaScript
/**
|
|
* ANIMAL BREEDING & GENETICS SYSTEM
|
|
* Complete breeding system with normal animals, genetics, mutants, and baby care
|
|
*/
|
|
class AnimalBreedingSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.enabled = true;
|
|
|
|
// Animals
|
|
this.animals = new Map();
|
|
this.babies = [];
|
|
this.breedingPairs = [];
|
|
|
|
// Genetics
|
|
this.geneticTraits = new Map();
|
|
this.mutations = new Map();
|
|
|
|
// Breeding stations
|
|
this.breedingStations = new Map();
|
|
|
|
// Settings
|
|
this.settings = {
|
|
autoBreeding: true,
|
|
proximityRange: 2, // tiles
|
|
mutationChance: 0.05, // 5%
|
|
gestationSpeed: 1.0 // 1.0 = normal
|
|
};
|
|
|
|
// Gestation periods (in game days)
|
|
this.gestationPeriods = {
|
|
sheep: 7,
|
|
cow: 14,
|
|
chicken: 3,
|
|
pig: 10,
|
|
horse: 21,
|
|
mutant_cow: 30,
|
|
mutant_chicken: 15,
|
|
mutant_pig: 20,
|
|
mutant_horse: 40,
|
|
fire_sheep: 14
|
|
};
|
|
|
|
this.loadProgress();
|
|
this.init();
|
|
|
|
console.log('✅ Animal Breeding System initialized');
|
|
}
|
|
|
|
init() {
|
|
this.defineGeneticTraits();
|
|
this.defineMutations();
|
|
console.log('🐑 Animal breeding ready');
|
|
}
|
|
|
|
// ========== GENETIC TRAITS ==========
|
|
|
|
defineGeneticTraits() {
|
|
// Colors (Mendelian genetics)
|
|
this.geneticTraits.set('color', {
|
|
dominant: ['brown', 'black'],
|
|
recessive: ['white', 'golden'],
|
|
rare: ['rainbow', 'crystal']
|
|
});
|
|
|
|
// Size
|
|
this.geneticTraits.set('size', {
|
|
values: ['small', 'medium', 'large'],
|
|
modifiers: { small: 0.8, medium: 1.0, large: 1.2 }
|
|
});
|
|
|
|
// Speed
|
|
this.geneticTraits.set('speed', {
|
|
values: ['slow', 'normal', 'fast'],
|
|
modifiers: { slow: 0.8, normal: 1.0, fast: 1.3 }
|
|
});
|
|
|
|
// Production
|
|
this.geneticTraits.set('production', {
|
|
values: ['low', 'normal', 'high'],
|
|
modifiers: { low: 0.7, normal: 1.0, high: 1.5 }
|
|
});
|
|
}
|
|
|
|
defineMutations() {
|
|
this.mutations.set('golden_fleece', {
|
|
chance: 0.05,
|
|
species: 'sheep',
|
|
effects: { production: 2.0, value: 5.0 },
|
|
rarity: 'legendary'
|
|
});
|
|
|
|
this.mutations.set('three_heads', {
|
|
chance: 0.03,
|
|
species: 'chicken',
|
|
effects: { production: 3.0, eggs: 3 },
|
|
rarity: 'epic'
|
|
});
|
|
|
|
this.mutations.set('giant', {
|
|
chance: 0.04,
|
|
species: 'pig',
|
|
effects: { size: 2.0, rideable: true },
|
|
rarity: 'epic'
|
|
});
|
|
|
|
this.mutations.set('undead', {
|
|
chance: 0.02,
|
|
species: 'horse',
|
|
effects: { stamina: Infinity, speed: 1.5 },
|
|
rarity: 'legendary'
|
|
});
|
|
|
|
this.mutations.set('fire_wool', {
|
|
chance: 0.01,
|
|
species: 'sheep',
|
|
effects: { fire_resistance: true, production: 1.5 },
|
|
rarity: 'mythic'
|
|
});
|
|
}
|
|
|
|
// ========== ANIMAL MANAGEMENT ==========
|
|
|
|
addAnimal(species, gender, x, y) {
|
|
const animal = {
|
|
id: `animal_${Date.now()}_${Math.random()}`,
|
|
species,
|
|
gender, // 'male' or 'female'
|
|
x, y,
|
|
age: 0,
|
|
stage: 'adult', // baby, teen, adult
|
|
genetics: this.generateGenetics(species),
|
|
pregnant: false,
|
|
gestationProgress: 0,
|
|
offspring: null,
|
|
lastBreed: 0,
|
|
sprite: null
|
|
};
|
|
|
|
this.animals.set(animal.id, animal);
|
|
return animal;
|
|
}
|
|
|
|
generateGenetics(species) {
|
|
return {
|
|
color: this.randomTrait('color'),
|
|
size: this.randomTrait('size'),
|
|
speed: this.randomTrait('speed'),
|
|
production: this.randomTrait('production'),
|
|
mutation: this.checkMutation(species)
|
|
};
|
|
}
|
|
|
|
randomTrait(traitType) {
|
|
const trait = this.geneticTraits.get(traitType);
|
|
const allValues = [...trait.dominant, ...trait.recessive];
|
|
return allValues[Math.floor(Math.random() * allValues.length)];
|
|
}
|
|
|
|
checkMutation(species) {
|
|
for (const [mutationId, mutation] of this.mutations.entries()) {
|
|
if (mutation.species === species && Math.random() < mutation.chance) {
|
|
return mutationId;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// ========== BREEDING ==========
|
|
|
|
attemptBreeding(animal1, animal2) {
|
|
// Check compatibility
|
|
if (!this.canBreed(animal1, animal2)) {
|
|
return false;
|
|
}
|
|
|
|
// Check proximity
|
|
const distance = Math.sqrt(
|
|
Math.pow(animal1.x - animal2.x, 2) +
|
|
Math.pow(animal1.y - animal2.y, 2)
|
|
);
|
|
|
|
if (distance > this.settings.proximityRange) {
|
|
return false;
|
|
}
|
|
|
|
// Breed!
|
|
return this.breed(animal1, animal2);
|
|
}
|
|
|
|
canBreed(animal1, animal2) {
|
|
// Same species
|
|
if (animal1.species !== animal2.species) return false;
|
|
|
|
// Different genders
|
|
if (animal1.gender === animal2.gender) return false;
|
|
|
|
// Both adults
|
|
if (animal1.stage !== 'adult' || animal2.stage !== 'adult') return false;
|
|
|
|
// Not pregnant
|
|
if (animal1.pregnant || animal2.pregnant) return false;
|
|
|
|
// Cooldown (1 day)
|
|
const now = Date.now();
|
|
if (now - animal1.lastBreed < 86400000) return false;
|
|
if (now - animal2.lastBreed < 86400000) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
breed(male, female) {
|
|
// Determine which is female
|
|
const mother = female.gender === 'female' ? female : male;
|
|
const father = female.gender === 'male' ? female : male;
|
|
|
|
// Create offspring genetics
|
|
const offspring = {
|
|
species: mother.species,
|
|
genetics: this.inheritGenetics(father, mother),
|
|
birthTime: null,
|
|
gestationStart: Date.now()
|
|
};
|
|
|
|
// Set mother as pregnant
|
|
mother.pregnant = true;
|
|
mother.gestationProgress = 0;
|
|
mother.offspring = offspring;
|
|
mother.lastBreed = Date.now();
|
|
father.lastBreed = Date.now();
|
|
|
|
// Visual effect
|
|
if (this.scene.visualEnhancements) {
|
|
this.scene.visualEnhancements.createHeartParticles(mother.x, mother.y);
|
|
}
|
|
|
|
console.log(`💕 ${mother.species} breeding started!`);
|
|
return true;
|
|
}
|
|
|
|
inheritGenetics(father, mother) {
|
|
const genetics = {
|
|
color: this.inheritColor(father.genetics.color, mother.genetics.color),
|
|
size: this.inheritTrait(father.genetics.size, mother.genetics.size),
|
|
speed: this.inheritTrait(father.genetics.speed, mother.genetics.speed),
|
|
production: this.inheritTrait(father.genetics.production, mother.genetics.production),
|
|
mutation: this.inheritMutation(father, mother)
|
|
};
|
|
|
|
return genetics;
|
|
}
|
|
|
|
inheritColor(color1, color2) {
|
|
const colorTrait = this.geneticTraits.get('color');
|
|
|
|
// Dominant genes
|
|
if (colorTrait.dominant.includes(color1)) return color1;
|
|
if (colorTrait.dominant.includes(color2)) return color2;
|
|
|
|
// 50/50 for recessive
|
|
return Math.random() < 0.5 ? color1 : color2;
|
|
}
|
|
|
|
inheritTrait(trait1, trait2) {
|
|
// 50% from each parent
|
|
return Math.random() < 0.5 ? trait1 : trait2;
|
|
}
|
|
|
|
inheritMutation(father, mother) {
|
|
// Both parents have mutation = 100% chance
|
|
if (father.genetics.mutation && mother.genetics.mutation) {
|
|
return father.genetics.mutation;
|
|
}
|
|
|
|
// One parent has mutation = 50% chance
|
|
if (father.genetics.mutation) {
|
|
return Math.random() < 0.5 ? father.genetics.mutation : null;
|
|
}
|
|
if (mother.genetics.mutation) {
|
|
return Math.random() < 0.5 ? mother.genetics.mutation : null;
|
|
}
|
|
|
|
// New mutation check
|
|
return this.checkMutation(father.species);
|
|
}
|
|
|
|
// ========== GESTATION & BIRTH ==========
|
|
|
|
updateGestation(delta) {
|
|
const dayInMs = 86400000 / this.settings.gestationSpeed;
|
|
|
|
for (const animal of this.animals.values()) {
|
|
if (!animal.pregnant) continue;
|
|
|
|
// Update gestation progress
|
|
const elapsed = Date.now() - animal.offspring.gestationStart;
|
|
const gestationDays = this.gestationPeriods[animal.species];
|
|
animal.gestationProgress = (elapsed / dayInMs) / gestationDays;
|
|
|
|
// Birth!
|
|
if (animal.gestationProgress >= 1.0) {
|
|
this.giveBirth(animal);
|
|
}
|
|
}
|
|
}
|
|
|
|
giveBirth(mother) {
|
|
const baby = {
|
|
id: `baby_${Date.now()}_${Math.random()}`,
|
|
species: mother.offspring.species,
|
|
gender: Math.random() < 0.5 ? 'male' : 'female',
|
|
x: mother.x,
|
|
y: mother.y,
|
|
age: 0,
|
|
stage: 'baby',
|
|
genetics: mother.offspring.genetics,
|
|
pregnant: false,
|
|
hunger: 100,
|
|
bonding: 0,
|
|
growthProgress: 0,
|
|
sprite: null
|
|
};
|
|
|
|
// Add to babies list
|
|
this.babies.push(baby);
|
|
|
|
// Add to animals (will become adult later)
|
|
this.animals.set(baby.id, baby);
|
|
|
|
// Reset mother
|
|
mother.pregnant = false;
|
|
mother.offspring = null;
|
|
mother.gestationProgress = 0;
|
|
|
|
// Visual effect
|
|
if (this.scene.visualEnhancements) {
|
|
this.scene.visualEnhancements.createSparkleEffect(baby.x, baby.y);
|
|
}
|
|
|
|
// Achievement
|
|
if (this.scene.uiGraphics) {
|
|
this.scene.uiGraphics.unlockAchievement('first_birth');
|
|
}
|
|
|
|
console.log(`🐣 Baby ${baby.species} born! (${baby.genetics.mutation || 'normal'})`);
|
|
return baby;
|
|
}
|
|
|
|
// ========== BABY CARE ==========
|
|
|
|
updateBabies(delta) {
|
|
const dayInMs = 86400000;
|
|
|
|
for (let i = this.babies.length - 1; i >= 0; i--) {
|
|
const baby = this.babies[i];
|
|
|
|
// Hunger
|
|
baby.hunger -= (delta / 60000) * 1; // 1% per minute
|
|
baby.hunger = Math.max(0, baby.hunger);
|
|
|
|
// Growth (only if fed)
|
|
if (baby.hunger > 30) {
|
|
baby.age += delta / dayInMs;
|
|
baby.growthProgress = baby.age / 14; // 14 days to adult
|
|
|
|
// Stage progression
|
|
if (baby.age > 3 && baby.stage === 'baby') {
|
|
baby.stage = 'teen';
|
|
console.log(`🐑 ${baby.species} is now a teen!`);
|
|
}
|
|
if (baby.age > 14 && baby.stage === 'teen') {
|
|
baby.stage = 'adult';
|
|
this.babies.splice(i, 1); // Remove from babies
|
|
console.log(`🐑 ${baby.species} is now an adult!`);
|
|
}
|
|
}
|
|
|
|
// Bonding (if player nearby)
|
|
if (this.isPlayerNearby(baby)) {
|
|
baby.bonding += (delta / 120000) * 1; // 1% per 2 minutes
|
|
baby.bonding = Math.min(100, baby.bonding);
|
|
}
|
|
|
|
// Starving
|
|
if (baby.hunger === 0) {
|
|
console.log(`💀 Baby ${baby.species} starved!`);
|
|
this.animals.delete(baby.id);
|
|
this.babies.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
feedBaby(baby, food) {
|
|
if (!baby || baby.stage === 'adult') return false;
|
|
|
|
baby.hunger = Math.min(100, baby.hunger + 30);
|
|
baby.bonding += 5;
|
|
|
|
console.log(`🍼 Fed baby ${baby.species}`);
|
|
return true;
|
|
}
|
|
|
|
isPlayerNearby(animal) {
|
|
if (!this.scene.player) return false;
|
|
|
|
const playerPos = this.scene.player.getPosition();
|
|
const distance = Math.sqrt(
|
|
Math.pow(animal.x - playerPos.x, 2) +
|
|
Math.pow(animal.y - playerPos.y, 2)
|
|
);
|
|
|
|
return distance < 3;
|
|
}
|
|
|
|
// ========== BREEDING STATIONS ==========
|
|
|
|
buildBreedingStation(type, x, y) {
|
|
const stations = {
|
|
love_pen: {
|
|
name: 'Love Pen',
|
|
capacity: 10,
|
|
autoBreed: true,
|
|
cost: { wood: 20, fence: 10 }
|
|
},
|
|
incubation_chamber: {
|
|
name: 'Incubation Chamber',
|
|
capacity: 5,
|
|
temperature: 37,
|
|
cost: { iron: 15, glass: 10 }
|
|
},
|
|
mutation_lab: {
|
|
name: 'Mutation Lab',
|
|
capacity: 2,
|
|
mutantOnly: true,
|
|
cost: { steel: 30, circuit: 20 }
|
|
},
|
|
cloning_vat: {
|
|
name: 'Cloning Vat',
|
|
capacity: 1,
|
|
cloning: true,
|
|
cost: { energy_crystal: 10, bio_gel: 50 }
|
|
}
|
|
};
|
|
|
|
const stationData = stations[type];
|
|
if (!stationData) return null;
|
|
|
|
const station = {
|
|
id: `${type}_${x}_${y}`,
|
|
type,
|
|
x, y,
|
|
...stationData,
|
|
animals: [],
|
|
active: true
|
|
};
|
|
|
|
this.breedingStations.set(station.id, station);
|
|
console.log(`🏠 Built ${stationData.name} at (${x}, ${y})`);
|
|
return station;
|
|
}
|
|
|
|
addAnimalToStation(stationId, animalId) {
|
|
const station = this.breedingStations.get(stationId);
|
|
const animal = this.animals.get(animalId);
|
|
|
|
if (!station || !animal) return false;
|
|
|
|
if (station.animals.length >= station.capacity) {
|
|
console.log('❌ Station is full');
|
|
return false;
|
|
}
|
|
|
|
station.animals.push(animalId);
|
|
animal.x = station.x;
|
|
animal.y = station.y;
|
|
|
|
console.log(`🐑 Added ${animal.species} to ${station.name}`);
|
|
return true;
|
|
}
|
|
|
|
updateBreedingStations() {
|
|
for (const station of this.breedingStations.values()) {
|
|
if (!station.active || !station.autoBreed) continue;
|
|
|
|
// Try to breed animals in station
|
|
const stationAnimals = station.animals
|
|
.map(id => this.animals.get(id))
|
|
.filter(a => a);
|
|
|
|
for (let i = 0; i < stationAnimals.length; i++) {
|
|
for (let j = i + 1; j < stationAnimals.length; j++) {
|
|
this.attemptBreeding(stationAnimals[i], stationAnimals[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========== MUTANT BREEDING ==========
|
|
|
|
breedMutants(mutant1, mutant2, mutationSerum = false) {
|
|
if (!mutationSerum) {
|
|
console.log('❌ Requires Mutation Serum!');
|
|
return { result: 'no_serum' };
|
|
}
|
|
|
|
// Failure chances
|
|
const roll = Math.random();
|
|
|
|
if (roll < 0.5) {
|
|
console.log('💀 Breeding failed - creature died');
|
|
return { result: 'death' };
|
|
}
|
|
|
|
if (roll < 0.8) {
|
|
console.log('🚫 Offspring is sterile');
|
|
return { result: 'sterile' };
|
|
}
|
|
|
|
// Success!
|
|
const offspring = this.breed(mutant1, mutant2);
|
|
|
|
return {
|
|
result: 'success',
|
|
offspring,
|
|
loot: ['mutant_hide', 'mutant_horn', 'mutant_blood']
|
|
};
|
|
}
|
|
|
|
// ========== AUTO-BREEDING ==========
|
|
|
|
updateAutoBreeding() {
|
|
if (!this.settings.autoBreeding) return;
|
|
|
|
const animals = Array.from(this.animals.values());
|
|
|
|
for (let i = 0; i < animals.length; i++) {
|
|
for (let j = i + 1; j < animals.length; j++) {
|
|
this.attemptBreeding(animals[i], animals[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========== UPDATE ==========
|
|
|
|
update(delta) {
|
|
this.updateGestation(delta);
|
|
this.updateBabies(delta);
|
|
this.updateBreedingStations();
|
|
this.updateAutoBreeding();
|
|
}
|
|
|
|
// ========== PERSISTENCE ==========
|
|
|
|
saveProgress() {
|
|
const data = {
|
|
animals: Array.from(this.animals.values()).map(a => ({
|
|
id: a.id,
|
|
species: a.species,
|
|
gender: a.gender,
|
|
genetics: a.genetics,
|
|
age: a.age,
|
|
stage: a.stage
|
|
})),
|
|
stations: Array.from(this.breedingStations.values()).map(s => ({
|
|
id: s.id,
|
|
type: s.type,
|
|
x: s.x,
|
|
y: s.y
|
|
}))
|
|
};
|
|
|
|
localStorage.setItem('novafarma_animal_breeding', JSON.stringify(data));
|
|
}
|
|
|
|
loadProgress() {
|
|
const saved = localStorage.getItem('novafarma_animal_breeding');
|
|
if (saved) {
|
|
try {
|
|
const data = JSON.parse(saved);
|
|
console.log('✅ Animal breeding progress loaded');
|
|
} catch (error) {
|
|
console.error('Failed to load breeding progress:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.saveProgress();
|
|
console.log('🐑 Animal Breeding System destroyed');
|
|
}
|
|
}
|