import json import random from datetime import datetime, timedelta from typing import Dict, List, Any, Optional from dataclasses import dataclass, asdict import numpy as np @dataclass class Monster: """Monster data class""" name: str species: str stage: str # rookie, champion, ultimate, mega stats: Dict[str, int] care_state: Dict[str, float] personality: Dict[str, Any] birth_time: datetime evolution_time: Optional[datetime] = None training_count: int = 0 battle_count: int = 0 happiness_events: List[str] = None image_path: Optional[str] = None model_3d_path: Optional[str] = None def __post_init__(self): if self.happiness_events is None: self.happiness_events = [] def to_dict(self) -> Dict: """Convert monster to dictionary for storage""" data = asdict(self) data['birth_time'] = self.birth_time.isoformat() if self.evolution_time: data['evolution_time'] = self.evolution_time.isoformat() return data @classmethod def from_dict(cls, data: Dict) -> 'Monster': """Create monster from dictionary""" data['birth_time'] = datetime.fromisoformat(data['birth_time']) if data.get('evolution_time'): data['evolution_time'] = datetime.fromisoformat(data['evolution_time']) return cls(**data) def get_stats_display(self) -> Dict[str, Any]: """Get formatted stats for display""" return { "name": self.name, "species": self.species, "stage": self.stage, "level": self._calculate_level(), "stats": { "HP": f"{self.stats['hp']}/999", "ATK": f"{self.stats['attack']}/500", "DEF": f"{self.stats['defense']}/500", "SPD": f"{self.stats['speed']}/500", "SPC": f"{self.stats['special']}/500" }, "care": { "Hunger": f"{self.care_state['hunger']:.0f}%", "Happiness": f"{self.care_state['happiness']:.0f}%", "Fatigue": f"{self.care_state['fatigue']:.0f}%", "Health": f"{self.care_state['health']:.0f}%" }, "age": self._calculate_age() } def _calculate_level(self) -> int: """Calculate monster level based on stats and experience""" total_stats = sum(self.stats.values()) base_level = total_stats // 50 exp_bonus = (self.training_count + self.battle_count) // 10 return min(99, base_level + exp_bonus + 1) def _calculate_age(self) -> str: """Calculate monster age""" age = datetime.now() - self.birth_time if age.days > 0: return f"{age.days} days" elif age.seconds > 3600: return f"{age.seconds // 3600} hours" else: return f"{age.seconds // 60} minutes" class GameMechanics: """Core game mechanics inspired by Digimon World 1""" def __init__(self): # Stat ranges and limits self.stat_limits = { 'hp': (10, 999), 'attack': (5, 500), 'defense': (5, 500), 'speed': (5, 500), 'special': (5, 500) } # Care thresholds self.care_thresholds = { 'hunger': {'critical': 20, 'low': 40, 'good': 70}, 'happiness': {'critical': 20, 'low': 40, 'good': 70}, 'fatigue': {'good': 30, 'tired': 60, 'exhausted': 80}, 'health': {'critical': 30, 'low': 50, 'good': 80} } # Training effectiveness modifiers self.training_modifiers = { 'strength': {'attack': 1.5, 'defense': 0.8, 'speed': 0.7}, 'defense': {'attack': 0.7, 'defense': 1.5, 'hp': 1.2}, 'speed': {'speed': 1.5, 'attack': 0.9, 'special': 0.8}, 'intelligence': {'special': 1.5, 'defense': 0.9, 'hp': 0.8}, 'balanced': {'attack': 1.0, 'defense': 1.0, 'speed': 1.0, 'special': 1.0} } # Evolution requirements (simplified) self.evolution_requirements = { 'champion': { 'min_stats': 150, # Total stats 'min_care': 60, # Average care percentage 'min_age': 1, # Days 'training_count': 10 }, 'ultimate': { 'min_stats': 300, 'min_care': 70, 'min_age': 3, 'training_count': 30 }, 'mega': { 'min_stats': 500, 'min_care': 80, 'min_age': 7, 'training_count': 50 } } def create_monster(self, generation_result: Dict[str, Any], user_preferences: Dict = None) -> Monster: """Create a new monster from AI generation results""" traits = generation_result.get('traits', {}) preferences = user_preferences or {} # Generate base stats based on traits and preferences base_stats = self._generate_base_stats(traits, preferences.get('training_focus', 'balanced')) # Initialize care state care_state = { 'hunger': 80.0, 'happiness': 90.0, 'fatigue': 10.0, 'health': 100.0 } # Determine personality from traits personality = self._determine_personality(traits) # Create monster name name = traits.get('name', self._generate_name(traits)) # Create monster instance monster = Monster( name=name, species=traits.get('species', 'DigiPal'), stage='rookie', stats=base_stats, care_state=care_state, personality=personality, birth_time=datetime.now(), image_path=generation_result.get('image'), model_3d_path=generation_result.get('model_3d') ) return monster def _generate_base_stats(self, traits: Dict, focus: str) -> Dict[str, int]: """Generate base stats based on traits and focus""" # Base values base = { 'hp': random.randint(50, 100), 'attack': random.randint(15, 35), 'defense': random.randint(15, 35), 'speed': random.randint(15, 35), 'special': random.randint(15, 35) } # Apply focus modifiers if focus in self.training_modifiers: for stat, modifier in self.training_modifiers[focus].items(): if stat in base: base[stat] = int(base[stat] * modifier) # Apply trait-based modifiers if traits.get('element') == 'fire': base['attack'] += 10 base['special'] += 5 elif traits.get('element') == 'water': base['defense'] += 10 base['hp'] += 20 elif traits.get('element') == 'earth': base['defense'] += 15 base['hp'] += 10 elif traits.get('element') == 'wind': base['speed'] += 15 base['special'] += 5 # Ensure stats are within limits for stat in base: base[stat] = max(self.stat_limits[stat][0], min(self.stat_limits[stat][1], base[stat])) return base def _determine_personality(self, traits: Dict) -> Dict[str, Any]: """Determine monster personality from traits""" personality_traits = [ 'brave', 'timid', 'aggressive', 'gentle', 'playful', 'serious', 'loyal', 'independent' ] # Select primary trait primary = traits.get('personality', random.choice(personality_traits)) # Generate personality profile return { 'primary': primary, 'likes': self._generate_likes(primary), 'dislikes': self._generate_dislikes(primary), 'training_preference': self._get_training_preference(primary), 'battle_style': self._get_battle_style(primary) } def _generate_name(self, traits: Dict) -> str: """Generate a name if not provided""" prefixes = ['Digi', 'Cyber', 'Tech', 'Neo', 'Alpha', 'Beta'] suffixes = ['mon', 'pal', 'byte', 'bit', 'tron', 'x'] prefix = random.choice(prefixes) suffix = random.choice(suffixes) return f"{prefix}{suffix}" def _generate_likes(self, personality: str) -> List[str]: """Generate things the monster likes based on personality""" likes_map = { 'brave': ['battles', 'challenges', 'meat'], 'timid': ['quiet places', 'vegetables', 'praise'], 'aggressive': ['training', 'meat', 'battles'], 'gentle': ['praise', 'vegetables', 'playing'], 'playful': ['games', 'treats', 'attention'], 'serious': ['training', 'discipline', 'fish'], 'loyal': ['praise', 'companionship', 'meat'], 'independent': ['exploration', 'variety', 'fish'] } return likes_map.get(personality, ['food', 'play', 'rest']) def _generate_dislikes(self, personality: str) -> List[str]: """Generate things the monster dislikes based on personality""" dislikes_map = { 'brave': ['running away', 'vegetables', 'rest'], 'timid': ['battles', 'loud noises', 'scolding'], 'aggressive': ['vegetables', 'rest', 'gentle training'], 'gentle': ['battles', 'scolding', 'meat'], 'playful': ['discipline', 'vegetables', 'being ignored'], 'serious': ['games', 'treats', 'slacking'], 'loyal': ['being alone', 'scolding', 'betrayal'], 'independent': ['clingy behavior', 'routine', 'vegetables'] } return dislikes_map.get(personality, ['scolding', 'hunger', 'fatigue']) def _get_training_preference(self, personality: str) -> str: """Get preferred training type based on personality""" preferences = { 'brave': 'strength', 'timid': 'defense', 'aggressive': 'strength', 'gentle': 'intelligence', 'playful': 'speed', 'serious': 'balanced', 'loyal': 'defense', 'independent': 'speed' } return preferences.get(personality, 'balanced') def _get_battle_style(self, personality: str) -> str: """Get battle style based on personality""" styles = { 'brave': 'offensive', 'timid': 'defensive', 'aggressive': 'berserker', 'gentle': 'support', 'playful': 'trickster', 'serious': 'tactical', 'loyal': 'guardian', 'independent': 'adaptive' } return styles.get(personality, 'balanced') def train_monster(self, monster: Monster, training_type: str, intensity: int) -> Dict[str, Any]: """Train the monster to improve stats""" # Check if monster can train if monster.care_state['fatigue'] > self.care_thresholds['fatigue']['exhausted']: return { 'success': False, 'message': f"{monster.name} is too tired to train! 😴💤", 'stat_changes': {} } if monster.care_state['hunger'] < self.care_thresholds['hunger']['low']: return { 'success': False, 'message': f"{monster.name} is too hungry to train! 🍖❓", 'stat_changes': {} } # Calculate stat gains base_gain = intensity * 2 stat_gains = {} # Apply training type modifiers training_type = training_type.lower() if training_type in self.training_modifiers: for stat, modifier in self.training_modifiers[training_type].items(): if stat in monster.stats: gain = int(base_gain * modifier * random.uniform(0.8, 1.2)) # Personality bonus if training_type == monster.personality['training_preference']: gain = int(gain * 1.2) # Apply gain with stat limits old_value = monster.stats[stat] new_value = min(self.stat_limits[stat][1], old_value + gain) actual_gain = new_value - old_value if actual_gain > 0: monster.stats[stat] = new_value stat_gains[stat] = actual_gain # Update care state fatigue_gain = intensity * 5 + random.randint(0, 10) happiness_gain = 5 if training_type == monster.personality['training_preference'] else 2 monster.care_state['fatigue'] = min(100, monster.care_state['fatigue'] + fatigue_gain) monster.care_state['happiness'] = min(100, monster.care_state['happiness'] + happiness_gain) monster.care_state['hunger'] = max(0, monster.care_state['hunger'] - intensity * 2) # Update training count monster.training_count += 1 # Check for evolution evolution_check = self.check_evolution(monster) # Generate response message if stat_gains: gains_text = ", ".join([f"{stat.upper()} +{gain}" for stat, gain in stat_gains.items()]) message = f"💪 Training complete! {gains_text}" else: message = f"📈 {monster.name} has reached stat limits in this area!" return { 'success': True, 'message': message, 'stat_changes': stat_gains, 'fatigue_gained': fatigue_gain, 'evolution_check': evolution_check } def check_evolution(self, monster: Monster) -> Optional[Dict[str, Any]]: """Check if monster meets evolution requirements""" current_stage = monster.stage next_stage = None if current_stage == 'rookie': next_stage = 'champion' elif current_stage == 'champion': next_stage = 'ultimate' elif current_stage == 'ultimate': next_stage = 'mega' else: return None requirements = self.evolution_requirements.get(next_stage) if not requirements: return None # Check requirements total_stats = sum(monster.stats.values()) avg_care = sum(monster.care_state.values()) / len(monster.care_state) age_days = (datetime.now() - monster.birth_time).days meets_requirements = ( total_stats >= requirements['min_stats'] and avg_care >= requirements['min_care'] and age_days >= requirements['min_age'] and monster.training_count >= requirements['training_count'] ) if meets_requirements: return { 'can_evolve': True, 'next_stage': next_stage, 'message': f"✨ {monster.name} is ready to evolve to {next_stage}!" } else: return { 'can_evolve': False, 'next_stage': next_stage, 'progress': { 'stats': f"{total_stats}/{requirements['min_stats']}", 'care': f"{avg_care:.0f}%/{requirements['min_care']}%", 'age': f"{age_days}/{requirements['min_age']} days", 'training': f"{monster.training_count}/{requirements['training_count']}" } } def feed_monster(self, monster: Monster, food_type: str) -> Dict[str, Any]: """Feed the monster""" food_effects = { 'meat': {'hunger': 40, 'happiness': 10, 'health': 5}, 'fish': {'hunger': 35, 'happiness': 15, 'health': 10}, 'vegetable': {'hunger': 30, 'happiness': 5, 'health': 15}, 'treat': {'hunger': 20, 'happiness': 30, 'health': 0}, 'medicine': {'hunger': 0, 'happiness': -10, 'health': 50} } effects = food_effects.get(food_type.lower(), food_effects['meat']) # Apply personality preferences likes_food = food_type.lower() in [like.lower() for like in monster.personality.get('likes', [])] dislikes_food = food_type.lower() in [dislike.lower() for dislike in monster.personality.get('dislikes', [])] if likes_food: effects['happiness'] *= 2 elif dislikes_food: effects['happiness'] = -abs(effects['happiness']) # Update care state old_hunger = monster.care_state['hunger'] monster.care_state['hunger'] = min(100, monster.care_state['hunger'] + effects['hunger']) monster.care_state['happiness'] = max(0, min(100, monster.care_state['happiness'] + effects['happiness'])) monster.care_state['health'] = min(100, monster.care_state['health'] + effects['health']) # Generate response if likes_food: message = f"😋 {monster.name} loves {food_type}! 💖" elif dislikes_food: message = f"😒 {monster.name} doesn't like {food_type}... 💔" elif old_hunger < 30: message = f"🍽️ {monster.name} was very hungry! Much better now! 😊" else: message = f"🍴 {monster.name} enjoyed the {food_type}! 👍" return { 'success': True, 'message': message, 'effects': effects, 'current_state': monster.care_state } def update_care_state(self, monster: Monster, time_passed: timedelta) -> Dict[str, Any]: """Update monster care state based on time passed""" # Calculate hours passed hours = time_passed.total_seconds() / 3600 # Decrease hunger and happiness over time monster.care_state['hunger'] = max(0, monster.care_state['hunger'] - hours * 5) monster.care_state['happiness'] = max(0, monster.care_state['happiness'] - hours * 2) # Decrease fatigue over time (rest) monster.care_state['fatigue'] = max(0, monster.care_state['fatigue'] - hours * 10) # Health changes based on other stats if monster.care_state['hunger'] < 20: monster.care_state['health'] = max(0, monster.care_state['health'] - hours * 3) elif monster.care_state['happiness'] < 20: monster.care_state['health'] = max(0, monster.care_state['health'] - hours * 1) # Check for critical states alerts = [] if monster.care_state['hunger'] < self.care_thresholds['hunger']['critical']: alerts.append("🍖 Your monster is starving!") if monster.care_state['happiness'] < self.care_thresholds['happiness']['critical']: alerts.append("😢 Your monster is very unhappy!") if monster.care_state['health'] < self.care_thresholds['health']['critical']: alerts.append("🏥 Your monster needs medical attention!") return { 'updated_state': monster.care_state, 'alerts': alerts, 'time_since_update': str(time_passed) }