From d794ed5404f089797aee5bf62141f9e63b0d0c2f Mon Sep 17 00:00:00 2001 From: Kiel Anthony Date: Fri, 23 Jan 2026 00:05:35 +0800 Subject: [PATCH] Revert "Add files via upload" --- animal.py | 505 ------------------------------ asset_manager.py | 217 ------------- crafting.py | 129 -------- crop.py | 147 --------- house_map.txt | 0 inventory.py | 584 ----------------------------------- main.py | 787 ----------------------------------------------- map.txt | 5 - npc.py | 471 ---------------------------- objects.txt | 0 player.py | 136 -------- plot_system.py | 265 ---------------- quest_system.py | 533 -------------------------------- save.json | 0 settings.py | 41 --- test_assets.py | 71 ----- tile.py | 99 ------ time_system.py | 68 ---- ui.py | 208 ------------- world.py | 128 -------- 20 files changed, 4394 deletions(-) delete mode 100644 animal.py delete mode 100644 asset_manager.py delete mode 100644 crafting.py delete mode 100644 crop.py delete mode 100644 house_map.txt delete mode 100644 inventory.py delete mode 100644 main.py delete mode 100644 map.txt delete mode 100644 npc.py delete mode 100644 objects.txt delete mode 100644 player.py delete mode 100644 plot_system.py delete mode 100644 quest_system.py delete mode 100644 save.json delete mode 100644 settings.py delete mode 100644 test_assets.py delete mode 100644 tile.py delete mode 100644 time_system.py delete mode 100644 ui.py delete mode 100644 world.py diff --git a/animal.py b/animal.py deleted file mode 100644 index fad6059..0000000 --- a/animal.py +++ /dev/null @@ -1,505 +0,0 @@ -import pygame -import random -from settings import * - -class Animal(pygame.sprite.Sprite): - ANIMAL_TYPES = { - "chicken": { - "color": (255, 255, 255), - "size": (20, 18), - "product": "egg", - "product_time": 180, # 3 minutes (180 seconds) - "product_value": 15, - "speed": 1.2, - "wander_radius": 100, - "feed_cooldown": 180 - }, - "cow": { - "color": (139, 90, 43), - "size": (28, 24), - "product": "milk", - "product_time": 240, # 4 minutes (240 seconds) - "product_value": 20, - "speed": 0.8, - "wander_radius": 80, - "feed_cooldown": 180 - }, - "sheep": { - "color": (245, 245, 245), - "size": (24, 20), - "product": "wool", - "product_time": 210, # 3.5 minutes (210 seconds) - "product_value": 25, - "speed": 1.0, - "wander_radius": 90, - "feed_cooldown": 180 - }, - "pig": { - "color": (255, 192, 203), - "size": (26, 22), - "product": "truffle", - "product_time": 300, # 5 minutes (300 seconds) - "product_value": 30, - "speed": 0.9, - "wander_radius": 85, - "feed_cooldown": 180 - }, - "goat": { - "color": (210, 180, 140), - "size": (24, 22), - "product": "cheese", - "product_time": 270, # 4.5 minutes (270 seconds) - "product_value": 35, - "speed": 1.1, - "wander_radius": 95, - "feed_cooldown": 180 - }, - "duck": { - "color": (255, 255, 100), - "size": (18, 16), - "product": "duck_egg", - "product_time": 195, # 3.25 minutes (195 seconds) - "product_value": 28, - "speed": 1.3, - "wander_radius": 110, - "feed_cooldown": 180 - }, - "rabbit": { - "color": (200, 200, 200), - "size": (16, 14), - "product": "rabbit_foot", - "product_time": 180, # 3 minutes (180 seconds) - "product_value": 35, - "speed": 1.5, - "wander_radius": 120, - "feed_cooldown": 180 - }, - "horse": { - "color": (101, 67, 33), - "size": (32, 28), - "product": "horseshoe", - "product_time": 300, # 5 minutes (300 seconds) - "product_value": 60, - "speed": 0.7, - "wander_radius": 70, - "feed_cooldown": 180 - }, - "llama": { - "color": (220, 200, 180), - "size": (26, 30), - "product": "llama_wool", - "product_time": 255, # 4.25 minutes (255 seconds) - "product_value": 45, - "speed": 0.85, - "wander_radius": 80, - "feed_cooldown": 180 - }, - "turkey": { - "color": (160, 82, 45), - "size": (22, 20), - "product": "turkey_feather", - "product_time": 225, # 3.75 minutes (225 seconds) - "product_value": 32, - "speed": 1.0, - "wander_radius": 100, - "feed_cooldown": 180 - } - } - - def __init__(self, pos, animal_type="chicken"): - super().__init__() - - self.animal_type = animal_type - self.data = self.ANIMAL_TYPES[animal_type] - - # Create animal sprite - width, height = self.data["size"] - self.image = pygame.Surface((width, height), pygame.SRCALPHA) - self.create_sprite() - - self.rect = self.image.get_rect(center=pos) - - # Store home position for wandering - self.home_pos = pygame.math.Vector2(pos) - self.position = pygame.math.Vector2(pos) - - # Movement behavior - self.speed = self.data["speed"] - self.base_speed = self.speed - self.direction = pygame.math.Vector2(random.uniform(-1, 1), random.uniform(-1, 1)) - if self.direction.length() > 0: - self.direction = self.direction.normalize() - - # Behavior timers - self.change_direction_timer = 0 - self.change_direction_delay = random.randint(60, 180) - self.pause_timer = 0 - self.is_paused = random.choice([True, False]) - self.pause_duration = random.randint(30, 120) if self.is_paused else 0 - - # Movement state - self.wander_radius = self.data["wander_radius"] - self.movement_state = random.choice(["wander", "pause", "roam"]) - self.state_timer = random.randint(120, 300) - - # Animal state system - self.state = "has_product" - self.product_timer = 0 - self.feed_cooldown_timer = 0 - self.feed_cooldown_duration = self.data["feed_cooldown"] - - # Legacy support - self.happiness = 100 - - def create_sprite(self): - """Create animal visual representation""" - color = self.data["color"] - width, height = self.data["size"] - - if self.animal_type == "chicken": - # Body - pygame.draw.ellipse(self.image, color, (2, 6, 16, 12)) - # Head - pygame.draw.circle(self.image, color, (14, 6), 5) - # Beak - pygame.draw.polygon(self.image, (255, 165, 0), - [(17, 6), (22, 5), (22, 7)]) - # Eye - pygame.draw.circle(self.image, BLACK, (15, 5), 1) - # Comb - pygame.draw.circle(self.image, RED, (14, 2), 2) - # Legs - pygame.draw.line(self.image, (255, 165, 0), (8, 18), (8, 16), 2) - pygame.draw.line(self.image, (255, 165, 0), (12, 18), (12, 16), 2) - - elif self.animal_type == "cow": - # Body - pygame.draw.ellipse(self.image, color, (2, 8, 24, 14)) - # Head - pygame.draw.ellipse(self.image, color, (20, 6, 8, 10)) - # Spots - pygame.draw.circle(self.image, BLACK, (8, 12), 3) - pygame.draw.circle(self.image, BLACK, (16, 14), 2) - # Eyes - pygame.draw.circle(self.image, BLACK, (24, 9), 1) - # Horns - pygame.draw.line(self.image, (200, 200, 200), (22, 6), (20, 4), 2) - pygame.draw.line(self.image, (200, 200, 200), (26, 6), (28, 4), 2) - # Legs - for x in [6, 10, 16, 20]: - pygame.draw.line(self.image, color, (x, 22), (x, 20), 2) - - elif self.animal_type == "sheep": - # Fluffy body - pygame.draw.circle(self.image, color, (12, 12), 10) - pygame.draw.circle(self.image, color, (8, 10), 6) - pygame.draw.circle(self.image, color, (16, 10), 6) - # Head (darker) - pygame.draw.circle(self.image, (50, 50, 50), (18, 8), 4) - # Eye - pygame.draw.circle(self.image, BLACK, (19, 7), 1) - # Legs - for x in [6, 10, 14, 18]: - pygame.draw.line(self.image, (50, 50, 50), (x, 20), (x, 18), 2) - - elif self.animal_type == "pig": - # Body - pygame.draw.ellipse(self.image, color, (3, 8, 20, 14)) - # Head - pygame.draw.circle(self.image, color, (20, 12), 6) - # Snout - pygame.draw.ellipse(self.image, (255, 182, 193), (21, 11, 4, 3)) - # Eye - pygame.draw.circle(self.image, BLACK, (20, 10), 1) - # Ear - pygame.draw.polygon(self.image, color, [(18, 8), (16, 6), (18, 6)]) - # Tail (curly) - pygame.draw.arc(self.image, color, (1, 10, 6, 6), 0, 3.14, 2) - # Legs - for x in [7, 11, 15, 19]: - pygame.draw.line(self.image, color, (x, 22), (x, 20), 2) - - elif self.animal_type == "goat": - # Body - pygame.draw.ellipse(self.image, color, (2, 8, 20, 12)) - # Head - pygame.draw.circle(self.image, color, (18, 10), 5) - # Horns - pygame.draw.line(self.image, (139, 69, 19), (16, 8), (14, 4), 2) - pygame.draw.line(self.image, (139, 69, 19), (20, 8), (22, 4), 2) - # Eye - pygame.draw.circle(self.image, BLACK, (18, 9), 1) - # Beard - pygame.draw.line(self.image, (100, 80, 60), (18, 13), (18, 16), 2) - # Legs - for x in [6, 10, 14, 18]: - pygame.draw.line(self.image, color, (x, 20), (x, 18), 2) - - elif self.animal_type == "duck": - # Body - pygame.draw.ellipse(self.image, color, (2, 6, 14, 10)) - # Head - pygame.draw.circle(self.image, color, (13, 5), 4) - # Bill - pygame.draw.polygon(self.image, (255, 165, 0), - [(15, 5), (18, 4), (18, 6)]) - # Eye - pygame.draw.circle(self.image, BLACK, (13, 4), 1) - # Wing - pygame.draw.ellipse(self.image, (200, 200, 50), (4, 8, 8, 5)) - # Legs (webbed) - pygame.draw.line(self.image, (255, 165, 0), (6, 16), (6, 14), 2) - pygame.draw.line(self.image, (255, 165, 0), (10, 16), (10, 14), 2) - - elif self.animal_type == "rabbit": - # Body - pygame.draw.ellipse(self.image, color, (2, 6, 12, 8)) - # Head - pygame.draw.circle(self.image, color, (11, 6), 4) - # Long ears - pygame.draw.ellipse(self.image, color, (9, 0, 3, 6)) - pygame.draw.ellipse(self.image, color, (13, 0, 3, 6)) - # Eye - pygame.draw.circle(self.image, BLACK, (11, 5), 1) - # Nose - pygame.draw.circle(self.image, (255, 192, 203), (11, 7), 1) - # Cotton tail - pygame.draw.circle(self.image, WHITE, (3, 10), 2) - - elif self.animal_type == "horse": - # Body - pygame.draw.ellipse(self.image, color, (4, 10, 24, 16)) - # Neck - pygame.draw.rect(self.image, color, (22, 6, 6, 10)) - # Head - pygame.draw.ellipse(self.image, color, (24, 4, 8, 8)) - # Mane - pygame.draw.polygon(self.image, (50, 30, 20), - [(24, 6), (26, 4), (28, 6)]) - # Eye - pygame.draw.circle(self.image, BLACK, (28, 7), 1) - # Legs - for x in [8, 12, 18, 22]: - pygame.draw.line(self.image, color, (x, 26), (x, 24), 3) - - elif self.animal_type == "llama": - # Body - pygame.draw.ellipse(self.image, color, (3, 14, 20, 12)) - # Long neck - pygame.draw.rect(self.image, color, (18, 6, 5, 12)) - # Head - pygame.draw.ellipse(self.image, color, (18, 4, 8, 8)) - # Ears (upright) - pygame.draw.polygon(self.image, color, [(20, 4), (19, 2), (21, 2)]) - pygame.draw.polygon(self.image, color, [(24, 4), (23, 2), (25, 2)]) - # Eye - pygame.draw.circle(self.image, BLACK, (21, 6), 1) - # Fluffy top - pygame.draw.circle(self.image, (240, 220, 200), (13, 12), 6) - # Legs - for x in [7, 11, 15, 19]: - pygame.draw.line(self.image, color, (x, 26), (x, 24), 2) - - elif self.animal_type == "turkey": - # Body - pygame.draw.ellipse(self.image, color, (3, 8, 16, 12)) - # Head/Neck - pygame.draw.line(self.image, color, (15, 12), (18, 8), 3) - pygame.draw.circle(self.image, (200, 100, 100), (18, 7), 3) - # Eye - pygame.draw.circle(self.image, BLACK, (18, 6), 1) - # Wattle (red thing) - pygame.draw.circle(self.image, RED, (18, 9), 2) - # Tail fan - for i in range(5): - angle_offset = (i - 2) * 0.3 - x = int(5 + 6 * (1 - abs(i - 2) * 0.2)) - pygame.draw.circle(self.image, (100, 60, 30), (x, 10), 3) - # Legs - pygame.draw.line(self.image, (255, 165, 0), (9, 20), (9, 18), 2) - pygame.draw.line(self.image, (255, 165, 0), (13, 20), (13, 18), 2) - - def choose_new_direction(self): - """Choose a new random direction""" - distance_from_home = self.position.distance_to(self.home_pos) - - if distance_from_home > self.wander_radius * 1.5: - direction_to_home = self.home_pos - self.position - if direction_to_home.length() > 0: - self.direction = direction_to_home.normalize() - self.direction.x += random.uniform(-0.3, 0.3) - self.direction.y += random.uniform(-0.3, 0.3) - if self.direction.length() > 0: - self.direction = self.direction.normalize() - else: - self.direction = pygame.math.Vector2( - random.uniform(-1, 1), - random.uniform(-1, 1) - ) - if self.direction.length() > 0: - self.direction = self.direction.normalize() - - def change_movement_state(self): - """Change between different movement behaviors""" - states = ["wander", "pause", "roam", "wander", "roam"] - self.movement_state = random.choice(states) - - if self.movement_state == "pause": - self.is_paused = True - self.pause_duration = random.randint(30, 120) - self.pause_timer = 0 - self.speed = 0 - elif self.movement_state == "wander": - self.is_paused = False - self.speed = self.base_speed * random.uniform(0.5, 1.0) - self.choose_new_direction() - else: - self.is_paused = False - self.speed = self.base_speed * random.uniform(0.7, 1.3) - self.choose_new_direction() - - self.state_timer = random.randint(120, 300) - - def update(self, dt): - """Update animal behavior""" - # State management - self.state_timer -= 1 - if self.state_timer <= 0: - self.change_movement_state() - - # Animal state machine - if self.state == "cooldown": - self.feed_cooldown_timer -= 1 - if self.feed_cooldown_timer <= 0: - self.state = "producing" - self.product_timer = 0 - - elif self.state == "producing": - self.product_timer += dt - if self.product_timer >= self.data["product_time"]: - self.state = "has_product" - self.product_timer = 0 - self.is_paused = True - self.pause_duration = 30 - self.pause_timer = 0 - - # Handle pausing - if self.is_paused: - self.pause_timer += 1 - if self.pause_timer >= self.pause_duration: - self.is_paused = False - self.speed = self.base_speed * random.uniform(0.7, 1.2) - self.choose_new_direction() - else: - # Random movement direction changes - self.change_direction_timer += 1 - - if self.change_direction_timer >= self.change_direction_delay: - self.choose_new_direction() - self.change_direction_timer = 0 - self.change_direction_delay = random.randint(60, 180) - - if random.random() < 0.2: - self.is_paused = True - self.pause_duration = random.randint(20, 60) - self.pause_timer = 0 - self.speed = 0 - - # Move using float position - self.position.x += self.direction.x * self.speed - self.position.y += self.direction.y * self.speed - - # Update rect position - self.rect.centerx = int(self.position.x) - self.rect.centery = int(self.position.y) - - # Keep on screen with bouncing - if self.rect.left < 0: - self.position.x = self.rect.width // 2 - self.direction.x = abs(self.direction.x) - elif self.rect.right > SCREEN_WIDTH: - self.position.x = SCREEN_WIDTH - self.rect.width // 2 - self.direction.x = -abs(self.direction.x) - - if self.rect.top < 0: - self.position.y = self.rect.height // 2 - self.direction.y = abs(self.direction.y) - elif self.rect.bottom > SCREEN_HEIGHT: - self.position.y = SCREEN_HEIGHT - self.rect.height // 2 - self.direction.y = -abs(self.direction.y) - - if (self.rect.left <= 0 or self.rect.right >= SCREEN_WIDTH or - self.rect.top <= 0 or self.rect.bottom >= SCREEN_HEIGHT): - if random.random() < 0.5: - self.choose_new_direction() - - def feed(self): - """Feed the animal""" - if self.state == "needs_feed": - self.state = "cooldown" - self.feed_cooldown_timer = self.feed_cooldown_duration - self.happiness = min(100, self.happiness + 20) - self.is_paused = True - self.pause_duration = 20 - self.pause_timer = 0 - return True - return False - - def collect_product(self): - """Collect animal product""" - if self.state == "has_product": - self.state = "needs_feed" - return self.data["product"], self.data["product_value"] - return None, 0 - - def can_collect(self): - """Check if product can be collected""" - return self.state == "has_product" - - def can_feed(self): - """Check if animal can be fed""" - return self.state == "needs_feed" - - def get_state_info(self): - """Get human-readable state information""" - if self.state == "has_product": - return "Ready to collect!" - elif self.state == "needs_feed": - return "Hungry - needs feeding" - elif self.state == "cooldown": - time_left = int(self.feed_cooldown_timer / 60) - return f"Digesting... ({time_left}s)" - elif self.state == "producing": - progress = int((self.product_timer / self.data["product_time"]) * 100) - return f"Producing... ({progress}%)" - return "" - - def draw_status(self, surface): - """Draw status indicators above animal""" - if self.state == "has_product": - pygame.draw.circle(surface, YELLOW, - (self.rect.centerx, self.rect.top - 10), 4) - pygame.draw.circle(surface, YELLOW, - (self.rect.centerx, self.rect.top - 16), 2) - - elif self.state == "needs_feed": - heart_x = self.rect.centerx - heart_y = self.rect.top - 12 - pygame.draw.circle(surface, RED, (heart_x - 3, heart_y), 3) - pygame.draw.circle(surface, RED, (heart_x + 3, heart_y), 3) - pygame.draw.polygon(surface, RED, [ - (heart_x - 6, heart_y), - (heart_x, heart_y + 6), - (heart_x + 6, heart_y) - ]) - - elif self.state == "cooldown": - clock_x = self.rect.centerx - clock_y = self.rect.top - 12 - pygame.draw.circle(surface, GRAY, (clock_x, clock_y), 4) - pygame.draw.circle(surface, WHITE, (clock_x, clock_y), 4, 1) - pygame.draw.line(surface, WHITE, (clock_x, clock_y), (clock_x, clock_y - 3), 1) - - elif self.state == "producing": - pygame.draw.circle(surface, (100, 200, 100), - (self.rect.centerx, self.rect.top - 10), 3) \ No newline at end of file diff --git a/asset_manager.py b/asset_manager.py deleted file mode 100644 index 21facc2..0000000 --- a/asset_manager.py +++ /dev/null @@ -1,217 +0,0 @@ -import pygame -import os -from settings import * - -class AssetManager: - """ - Advanced asset manager with sprite sheet support. - Handles loading, extracting, and scaling sprites from sprite sheets. - """ - - def __init__(self): - self.assets_path = "assets" - self.loaded_images = {} - self.sprite_sheets = {} - - # Check if assets folder exists - self.assets_available = os.path.exists(self.assets_path) - - if self.assets_available: - print(f"✓ Assets found at '{self.assets_path}'") - print(f" Loading sprite sheets...") - self._preload_sprite_sheets() - else: - print(f"⚠ Assets not found at '{self.assets_path}'") - print(f" Using procedural graphics as fallback.") - - def _preload_sprite_sheets(self): - """Preload commonly used sprite sheets""" - sprite_sheets = { - 'tileset': 'Tilemap/Tileset_Spring.png', - 'crops': 'Crops/Spring_Crops.png', - 'player_idle': 'Characters/Idle.png', - 'player_walk': 'Characters/Walk.png', - 'chicken': 'Animals/Chicken_Red.png', - 'cow': 'Animals/Female_Cow_Brown.png', - 'tree': 'Objects/Maple_Tree.png', - 'fence': 'Objects/Fence_s_copiar.png', - 'chest': 'Objects/chest.png', - } - - for name, path in sprite_sheets.items(): - full_path = os.path.join(self.assets_path, path) - if os.path.exists(full_path): - try: - self.sprite_sheets[name] = pygame.image.load(full_path).convert_alpha() - print(f" ✓ Loaded: {name}") - except Exception as e: - print(f" ✗ Error loading {name}: {e}") - - def extract_sprite(self, sheet_name, x, y, width, height, scale=2): - """Extract a sprite from a sprite sheet""" - if sheet_name not in self.sprite_sheets: - return None - - sheet = self.sprite_sheets[sheet_name] - sprite = pygame.Surface((width, height), pygame.SRCALPHA) - sprite.blit(sheet, (0, 0), (x, y, width, height)) - - if scale != 1: - sprite = pygame.transform.scale(sprite, (width * scale, height * scale)) - - return sprite - - def get_tile_sprite(self, tile_type, size=TILE_SIZE): - """Get tile sprite from tileset""" - # Tileset_Spring.png layout (16x16 tiles) - # Row 0: Various grass types - # Row 1: Dirt/soil types - # Row 2: Water and other - - tile_coords = { - "G": (0, 0, 16, 16), # Grass - top left - "S": (0, 16, 16, 16), # Soil/dirt - second row - "W": (32, 32, 16, 16), # Water - third row - "P": (16, 0, 16, 16), # Path - grass variant - } - - if self.assets_available and tile_type in tile_coords: - x, y, w, h = tile_coords[tile_type] - sprite = self.extract_sprite('tileset', x, y, w, h, scale=2) - if sprite: - return sprite - - # Fallback for objects that aren't in tileset - if tile_type == "T": # Tree - return self.get_tree_sprite(size) - elif tile_type == "F": # Fence - return self.get_fence_sprite(size) - elif tile_type == "R": # Rock - return self._create_rock_fallback(size) - - # Default fallback - return self._create_tile_fallback(tile_type, size) - - def get_tree_sprite(self, size=TILE_SIZE): - """Get tree sprite""" - if 'tree' in self.sprite_sheets: - # Maple tree is approximately 16x24, extract first frame - sprite = self.extract_sprite('tree', 0, 0, 16, 24, scale=2) - if sprite: - # Scale to fit tile size - return pygame.transform.scale(sprite, (size, size)) - return self._create_tree_fallback(size) - - def get_fence_sprite(self, size=TILE_SIZE): - """Get fence sprite""" - if 'fence' in self.sprite_sheets: - sprite = self.extract_sprite('fence', 0, 0, 16, 16, scale=2) - if sprite: - return sprite - return self._create_fence_fallback(size) - - def get_crop_sprites(self, crop_type): - """Get crop sprite stages from Spring_Crops.png""" - if 'crops' not in self.sprite_sheets: - return None - - # Spring_Crops.png layout (16x16 per sprite) - # Each crop has multiple growth stages - # Approximate positions (adjust based on actual layout): - crop_positions = { - "wheat": [(0, 0), (16, 0), (32, 0), (48, 0)], # Row 1, 4 stages - "carrot": [(0, 16), (16, 16), (32, 16), (48, 16)], # Row 2, 4 stages - "tomato": [(64, 0), (80, 0), (96, 0), (112, 0)], # Row 1, later columns - "corn": [(64, 16), (80, 16), (96, 16), (112, 16)], # Row 2, later columns - } - - if crop_type not in crop_positions: - return None - - sprites = [] - for x, y in crop_positions[crop_type]: - sprite = self.extract_sprite('crops', x, y, 16, 16, scale=2) - if sprite: - sprites.append(sprite) - - return sprites if len(sprites) == 4 else None - - def get_character_sprite(self, char_type, size=(28, 32)): - """Get character sprite""" - if char_type == "player": - # Extract first frame of idle animation (16x16) - if 'player_idle' in self.sprite_sheets: - sprite = self.extract_sprite('player_idle', 0, 0, 16, 16, scale=2) - if sprite: - return pygame.transform.scale(sprite, size) - - return None - - def get_animal_sprite(self, animal_type): - """Get animal sprite""" - animal_sheets = { - "chicken": 'chicken', - "cow": 'cow', - } - - if animal_type in animal_sheets: - sheet_name = animal_sheets[animal_type] - if sheet_name in self.sprite_sheets: - # Extract first frame (16x16 for chicken, might be different for cow) - w, h = (16, 16) if animal_type == "chicken" else (16, 16) - sprite = self.extract_sprite(sheet_name, 0, 0, w, h, scale=2) - if sprite: - return sprite - - return None - - # Fallback creation methods - def _create_tile_fallback(self, tile_type, size): - """Create procedural tile graphics (fallback)""" - img = pygame.Surface((size, size)) - - if tile_type == "G": # Grass - img.fill(GREEN) - for i in range(8): - x = pygame.Rect((size // 8) * (i % 4), (size // 4) * (i // 4), 2, 4) - pygame.draw.rect(img, DARK_GREEN, x) - elif tile_type == "S": # Soil - img.fill(BROWN) - for i in range(4): - pygame.draw.line(img, (100, 50, 10), (0, i * 8), (size, i * 8), 1) - elif tile_type == "W": # Water - img.fill(BLUE) - pygame.draw.circle(img, (100, 180, 255), (8, 8), 4) - elif tile_type == "P": # Path - img.fill(LIGHT_BROWN) - pygame.draw.circle(img, GRAY, (8, 8), 2) - else: - img.fill(BLACK) - - return img - - def _create_tree_fallback(self, size): - """Create fallback tree""" - img = pygame.Surface((size, size)) - img.fill(GREEN) - pygame.draw.rect(img, BROWN, (12, 16, 8, 16)) - pygame.draw.circle(img, DARK_GREEN, (16, 12), 10) - return img - - def _create_fence_fallback(self, size): - """Create fallback fence""" - img = pygame.Surface((size, size)) - img.fill(GREEN) - pygame.draw.rect(img, BROWN, (2, 12, 28, 4)) - pygame.draw.rect(img, BROWN, (8, 8, 4, 20)) - return img - - def _create_rock_fallback(self, size): - """Create fallback rock""" - img = pygame.Surface((size, size)) - img.fill(GREEN) - pygame.draw.polygon(img, GRAY, [(16, 8), (26, 20), (16, 28), (6, 20)]) - return img - -# Global asset manager instance -asset_manager = AssetManager() diff --git a/crafting.py b/crafting.py deleted file mode 100644 index 86a3d46..0000000 --- a/crafting.py +++ /dev/null @@ -1,129 +0,0 @@ -import pygame -from settings import * - -class Crafting: - def __init__(self): - self.recipes = { - "fence": { - "ingredients": {"wood": 5}, - "result": "fence", - "amount": 1 - }, - "scarecrow": { - "ingredients": {"wood": 10, "wheat": 5}, - "result": "scarecrow", - "amount": 1 - }, - "chest": { - "ingredients": {"wood": 20, "stone": 10}, - "result": "chest", - "amount": 1 - }, - } - self.font = pygame.font.Font(None, 20) - self.title_font = pygame.font.Font(None, 24) - self.show_menu = False - - def can_craft(self, recipe_name, inventory): - """Check if recipe can be crafted""" - if recipe_name not in self.recipes: - return False - - recipe = self.recipes[recipe_name] - for ingredient, amount in recipe["ingredients"].items(): - if not inventory.has_item(ingredient, amount): - return False - return True - - def craft(self, recipe_name, inventory): - """Craft an item""" - if not self.can_craft(recipe_name, inventory): - return False - - recipe = self.recipes[recipe_name] - - # Remove ingredients - for ingredient, amount in recipe["ingredients"].items(): - inventory.remove_item(ingredient, amount) - - # Add result - inventory.add_item(recipe["result"], recipe["amount"]) - return True - - def toggle_menu(self): - """Toggle crafting menu""" - self.show_menu = not self.show_menu - - def draw_menu(self, surface, inventory, screen_width, screen_height): - """Draw crafting menu""" - if not self.show_menu: - return - - # Draw background - menu_width = 400 - menu_height = 300 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - # Semi-transparent background - bg = pygame.Surface((menu_width, menu_height)) - bg.set_alpha(230) - bg.fill((40, 40, 40)) - surface.blit(bg, (menu_x, menu_y)) - - # Border - pygame.draw.rect(surface, WHITE, (menu_x, menu_y, menu_width, menu_height), 3) - - # Title - title = self.title_font.render("Crafting", True, WHITE) - surface.blit(title, (menu_x + 20, menu_y + 10)) - - # Draw recipes - y_offset = 50 - for i, (recipe_name, recipe) in enumerate(self.recipes.items()): - y = menu_y + y_offset + i * 70 - - # Recipe box - recipe_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 60) - can_craft = self.can_craft(recipe_name, inventory) - color = (60, 80, 60) if can_craft else (80, 60, 60) - pygame.draw.rect(surface, color, recipe_rect) - pygame.draw.rect(surface, WHITE if can_craft else GRAY, recipe_rect, 2) - - # Recipe name - name_text = self.font.render(recipe_name.capitalize(), True, WHITE) - surface.blit(name_text, (recipe_rect.x + 10, recipe_rect.y + 5)) - - # Ingredients - ingredients_str = ", ".join([f"{amt} {ing}" for ing, amt in recipe["ingredients"].items()]) - ing_text = self.font.render(f"Needs: {ingredients_str}", True, WHITE) - surface.blit(ing_text, (recipe_rect.x + 10, recipe_rect.y + 28)) - - # Craft button hint - if can_craft: - hint = self.font.render("[Click to Craft]", True, YELLOW) - surface.blit(hint, (recipe_rect.right - 120, recipe_rect.y + 28)) - - # Close hint - close_text = self.font.render("Press C to close", True, WHITE) - surface.blit(close_text, (menu_x + 20, menu_y + menu_height - 30)) - - def handle_click(self, pos, inventory, screen_width, screen_height): - """Handle mouse click on crafting menu""" - if not self.show_menu: - return False - - menu_width = 400 - menu_height = 300 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - y_offset = 50 - for i, recipe_name in enumerate(self.recipes.keys()): - y = menu_y + y_offset + i * 70 - recipe_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 60) - - if recipe_rect.collidepoint(pos): - return self.craft(recipe_name, inventory) - - return False \ No newline at end of file diff --git a/crop.py b/crop.py deleted file mode 100644 index 7495a73..0000000 --- a/crop.py +++ /dev/null @@ -1,147 +0,0 @@ -import pygame -from settings import * - -class Crop(pygame.sprite.Sprite): - # Crop data: growth_time (seconds), sell_price, seed_cost - CROP_DATA = { - "wheat": {"growth_time": 15, "sell_price": 15, "seed_cost": 50, "color": (218, 165, 32)}, - "carrot": {"growth_time": 20, "sell_price": 25, "seed_cost": 125, "color": (255, 140, 0)}, - "tomato": {"growth_time": 25, "sell_price": 40, "seed_cost": 150, "color": (255, 50, 50)}, - "corn": {"growth_time": 30, "sell_price": 50, "seed_cost": 200, "color": (255, 215, 0)}, - } - - def __init__(self, pos, crop_type="wheat", current_time=0): - super().__init__() - - self.crop_type = crop_type - self.stage = 0 # 0-3 growth stages - self.max_stage = 3 - self.growth_time = self.CROP_DATA[crop_type]["growth_time"] - self.time_planted = current_time - self.needs_water = False # Start without needing water for first stage - self.watered = True # Consider newly planted crops as watered for initial growth - self.ready_to_harvest = False - - # Create crop images for each stage - self.images = self.create_crop_images() - self.image = self.images[self.stage] - self.rect = self.image.get_rect(topleft=pos) - - def create_crop_images(self): - """Create visual representation of crop at different stages""" - images = [] - color = self.CROP_DATA[self.crop_type]["color"] - - for stage in range(4): - img = pygame.Surface((TILE_SIZE, TILE_SIZE), pygame.SRCALPHA) - - if stage == 0: # Seed - pygame.draw.circle(img, (139, 69, 19), (TILE_SIZE // 2, TILE_SIZE - 4), 3) - - elif stage == 1: # Sprout - # Small green sprout - pygame.draw.line(img, (50, 150, 50), - (TILE_SIZE // 2, TILE_SIZE - 2), - (TILE_SIZE // 2, TILE_SIZE - 10), 2) - pygame.draw.circle(img, (50, 200, 50), - (TILE_SIZE // 2, TILE_SIZE - 10), 3) - - elif stage == 2: # Growing - # Larger plant - stem_x = TILE_SIZE // 2 - pygame.draw.line(img, (40, 120, 40), - (stem_x, TILE_SIZE - 2), - (stem_x, TILE_SIZE - 18), 3) - # Leaves - pygame.draw.circle(img, (50, 180, 50), - (stem_x - 6, TILE_SIZE - 12), 4) - pygame.draw.circle(img, (50, 180, 50), - (stem_x + 6, TILE_SIZE - 12), 4) - # Small crop forming - pygame.draw.circle(img, color, - (stem_x, TILE_SIZE - 16), 3) - - else: # Mature - # Full grown plant - stem_x = TILE_SIZE // 2 - pygame.draw.line(img, (40, 120, 40), - (stem_x, TILE_SIZE - 2), - (stem_x, TILE_SIZE - 24), 4) - # Leaves - pygame.draw.circle(img, (50, 180, 50), - (stem_x - 8, TILE_SIZE - 14), 5) - pygame.draw.circle(img, (50, 180, 50), - (stem_x + 8, TILE_SIZE - 14), 5) - pygame.draw.circle(img, (50, 200, 50), - (stem_x, TILE_SIZE - 20), 6) - # Mature crop - pygame.draw.circle(img, color, - (stem_x, TILE_SIZE - 22), 6) - pygame.draw.circle(img, color, - (stem_x - 5, TILE_SIZE - 18), 4) - pygame.draw.circle(img, color, - (stem_x + 5, TILE_SIZE - 18), 4) - # Shine effect when ready - pygame.draw.circle(img, (255, 255, 200), - (stem_x + 3, TILE_SIZE - 24), 2) - - images.append(img) - return images - - def water(self): - """Water the crop""" - if not self.watered: - self.watered = True - self.needs_water = False - return True - return False - - def update(self, current_time): - """Update crop growth""" - # Calculate growth progress - time_since_planted = current_time - self.time_planted - - # If not watered, grow at 50% speed after stage 1 - growth_multiplier = 1.0 - if not self.watered and self.stage >= 1: - growth_multiplier = 0.5 - - adjusted_time = time_since_planted * growth_multiplier - growth_per_stage = self.growth_time / self.max_stage - new_stage = min(int(adjusted_time / growth_per_stage), self.max_stage) - - # Update stage - if new_stage > self.stage: - self.stage = new_stage - self.image = self.images[self.stage] - # Need water for optimal next stage growth - if self.stage < self.max_stage: - self.needs_water = True - self.watered = False - - # Check if ready to harvest - if self.stage >= self.max_stage: - self.ready_to_harvest = True - - def draw_status(self, surface): - """Draw status indicators above crop""" - if self.ready_to_harvest: - # Draw sparkle when ready to harvest - import pygame - from settings import YELLOW - pygame.draw.circle(surface, YELLOW, - (self.rect.centerx, self.rect.top - 5), 3) - elif self.needs_water and self.stage > 0: - # Draw water drop when needs water - import pygame - from settings import BLUE - pygame.draw.circle(surface, BLUE, - (self.rect.centerx, self.rect.top - 5), 3) - pygame.draw.circle(surface, (150, 200, 255), - (self.rect.centerx, self.rect.top - 7), 2) - - def harvest(self): - """Harvest the crop and return sell price""" - if self.ready_to_harvest: - return self.CROP_DATA[self.crop_type]["sell_price"] - return 0 \ No newline at end of file diff --git a/house_map.txt b/house_map.txt deleted file mode 100644 index e69de29..0000000 diff --git a/inventory.py b/inventory.py deleted file mode 100644 index 0219c5d..0000000 --- a/inventory.py +++ /dev/null @@ -1,584 +0,0 @@ -import pygame -from settings import * -from crop import Crop - -class Inventory: - def __init__(self): - # Main inventory storage (36 slots) - self.items = { - "wheat_seed": INITIAL_SEEDS, - "carrot_seed": 5, - "tomato_seed": 3, - "corn_seed": 2, - "wheat": 0, - "carrot": 0, - "tomato": 0, - "corn": 0, - "wood": 0, - "stone": 0, - } - - # Hotbar (5 slots) - stores item names - self.hotbar = ["wheat_seed", "carrot_seed", "tomato_seed", "corn_seed", None] - self.selected_hotbar_slot = 0 - - # UI state - self.show_full_inventory = False - self.selected_inventory_item = None - - # Fonts - self.font = pygame.font.Font(None, 20) - self.title_font = pygame.font.Font(None, 28) - self.small_font = pygame.font.Font(None, 16) - - # Item categories for display - self.item_categories = { - "Seeds": ["wheat_seed", "carrot_seed", "tomato_seed", "corn_seed"], - "Crops": ["wheat", "carrot", "tomato", "corn"], - "Resources": ["wood", "stone"], - "Products": ["egg", "milk", "wool"], - "Crafted": ["fence", "scarecrow", "chest"] - } - - def add_item(self, item, amount=1): - """Add item to inventory""" - if item in self.items: - self.items[item] += amount - else: - self.items[item] = amount - - def remove_item(self, item, amount=1): - """Remove item from inventory""" - if item in self.items and self.items[item] >= amount: - self.items[item] -= amount - return True - return False - - def has_item(self, item, amount=1): - """Check if inventory has item""" - return self.items.get(item, 0) >= amount - - def use(self, item): - """Use one of an item""" - return self.remove_item(item, 1) - - def toggle_inventory(self): - """Toggle full inventory display""" - self.show_full_inventory = not self.show_full_inventory - - def get_selected_item(self): - """Get currently selected item from hotbar""" - selected_item = self.hotbar[self.selected_hotbar_slot] - if selected_item and self.has_item(selected_item): - return selected_item - return None - - def get_selected_seed(self): - """Get currently selected seed type from hotbar""" - selected_item = self.get_selected_item() - if selected_item and selected_item.endswith("_seed"): - return selected_item.replace("_seed", "") - return None - - def set_hotbar_slot(self, slot, item_name): - """Set an item in a hotbar slot""" - if 0 <= slot < 5: - self.hotbar[slot] = item_name - - def handle_inventory_click(self, pos, screen_width, screen_height): - """Handle clicks on the full inventory screen""" - if not self.show_full_inventory: - return False - - panel_width = min(600, screen_width - 40) - panel_height = min(600, screen_height - 40) - panel_x = (screen_width - panel_width) // 2 - panel_y = (screen_height - panel_height) // 2 - - # Check if click is inside panel - if not (panel_x <= pos[0] <= panel_x + panel_width and - panel_y <= pos[1] <= panel_y + panel_height): - hotbar_result = self.handle_integrated_hotbar_click(pos, panel_x, panel_y, panel_width, panel_height) - if hotbar_result: - return True - return False - - # Check inventory grid clicks - slot_size = min(70, (panel_width - 80) // 6) - slots_per_row = 6 - start_x = panel_x + 30 - start_y = panel_y + 75 - slot_spacing = 10 - - slot_index = 0 - all_items = list(self.items.keys()) - - for row in range(6): - for col in range(6): - if slot_index >= len(all_items): - break - - x = start_x + col * (slot_size + slot_spacing) - y = start_y + row * (slot_size + slot_spacing) - - if x <= pos[0] <= x + slot_size and y <= pos[1] <= y + slot_size: - item_name = all_items[slot_index] - count = self.items.get(item_name, 0) - - if count > 0: - self.selected_inventory_item = item_name - return True - - slot_index += 1 - - hotbar_result = self.handle_integrated_hotbar_click(pos, panel_x, panel_y, panel_width, panel_height) - if hotbar_result: - return True - - return False - - def handle_integrated_hotbar_click(self, pos, panel_x, panel_y, panel_width, panel_height): - """Handle clicks on integrated hotbar slots""" - slot_size = 64 - spacing = 8 - total_width = (slot_size * 5) + (spacing * 4) - start_x = panel_x + (panel_width - total_width) // 2 - start_y = panel_y + panel_height - slot_size - 30 - - for i in range(5): - x = start_x + i * (slot_size + spacing) - y = start_y - - if x <= pos[0] <= x + slot_size and y <= pos[1] <= y + slot_size: - if self.selected_inventory_item: - self.hotbar[i] = self.selected_inventory_item - self.selected_inventory_item = None - return True - else: - self.selected_hotbar_slot = i - return True - - return False - - def handle_hotbar_click(self, pos, screen_width, screen_height): - """Handle clicks on hotbar slots when inventory is closed""" - slot_size = 64 - spacing = 8 - total_width = (slot_size * 5) + (spacing * 4) - start_x = (screen_width - total_width) // 2 - start_y = screen_height - slot_size - 20 - - for i in range(5): - x = start_x + i * (slot_size + spacing) - y = start_y - - if x <= pos[0] <= x + slot_size and y <= pos[1] <= y + slot_size: - self.selected_hotbar_slot = i - return True - - return False - - def draw_hotbar(self, surface, screen_width, screen_height): - """Draw the hotbar at bottom center of screen""" - slot_size = 64 - spacing = 8 - total_width = (slot_size * 5) + (spacing * 4) - start_x = (screen_width - total_width) // 2 - start_y = screen_height - slot_size - 20 - - for i in range(5): - x = start_x + i * (slot_size + spacing) - y = start_y - - is_selected = i == self.selected_hotbar_slot - color = (100, 100, 100) if is_selected else (60, 60, 60) - pygame.draw.rect(surface, color, (x, y, slot_size, slot_size)) - pygame.draw.rect(surface, WHITE if is_selected else GRAY, - (x, y, slot_size, slot_size), 3 if is_selected else 2) - - item_name = self.hotbar[i] - if item_name and item_name in self.items: - count = self.items.get(item_name, 0) - - self.draw_item_icon(surface, item_name, x + slot_size // 2, y + 20) - - count_text = self.font.render(str(count), True, WHITE) - text_bg = pygame.Surface((count_text.get_width() + 4, count_text.get_height() + 2)) - text_bg.fill((0, 0, 0)) - text_bg.set_alpha(180) - surface.blit(text_bg, (x + 3, y + slot_size - 24)) - surface.blit(count_text, (x + 5, y + slot_size - 22)) - - display_name = item_name.replace("_", " ").replace(" seed", "").title() - if len(display_name) > 8: - display_name = display_name[:7] + "." - name_text = self.small_font.render(display_name, True, WHITE) - name_x = x + slot_size // 2 - name_text.get_width() // 2 - surface.blit(name_text, (name_x, y + 40)) - - num_text = self.small_font.render(str(i + 1), True, YELLOW if is_selected else GRAY) - surface.blit(num_text, (x + 4, y + 4)) - - def draw_integrated_hotbar(self, surface, panel_x, panel_y, panel_width, panel_height): - """Draw the hotbar integrated into the inventory panel""" - slot_size = 64 - spacing = 8 - total_width = (slot_size * 5) + (spacing * 4) - start_x = panel_x + (panel_width - total_width) // 2 - start_y = panel_y + panel_height - slot_size - 30 - - hotbar_label = self.font.render("Hotbar", True, YELLOW) - label_x = panel_x + (panel_width - hotbar_label.get_width()) // 2 - surface.blit(hotbar_label, (label_x, start_y - 25)) - - for i in range(5): - x = start_x + i * (slot_size + spacing) - y = start_y - - is_selected = i == self.selected_hotbar_slot - color = (100, 100, 100) if is_selected else (60, 60, 60) - pygame.draw.rect(surface, color, (x, y, slot_size, slot_size)) - pygame.draw.rect(surface, WHITE if is_selected else GRAY, - (x, y, slot_size, slot_size), 3 if is_selected else 2) - - item_name = self.hotbar[i] - if item_name and item_name in self.items: - count = self.items.get(item_name, 0) - - self.draw_item_icon(surface, item_name, x + slot_size // 2, y + 20) - - count_text = self.font.render(str(count), True, WHITE) - text_bg = pygame.Surface((count_text.get_width() + 4, count_text.get_height() + 2)) - text_bg.fill((0, 0, 0)) - text_bg.set_alpha(180) - surface.blit(text_bg, (x + 3, y + slot_size - 24)) - surface.blit(count_text, (x + 5, y + slot_size - 22)) - - display_name = item_name.replace("_", " ").replace(" seed", "").title() - if len(display_name) > 8: - display_name = display_name[:7] + "." - name_text = self.small_font.render(display_name, True, WHITE) - name_x = x + slot_size // 2 - name_text.get_width() // 2 - surface.blit(name_text, (name_x, y + 40)) - - num_text = self.small_font.render(str(i + 1), True, YELLOW if is_selected else GRAY) - surface.blit(num_text, (x + 4, y + 4)) - - def draw_full_inventory(self, surface, screen_width, screen_height): - """Draw the full inventory screen with integrated hotbar - responsive""" - if not self.show_full_inventory: - return - - # Draw semi-transparent background overlay - overlay = pygame.Surface((screen_width, screen_height)) - overlay.set_alpha(180) - overlay.fill((0, 0, 0)) - surface.blit(overlay, (0, 0)) - - # Responsive panel dimensions - panel_width = min(600, screen_width - 40) - panel_height = min(600, screen_height - 40) - panel_x = (screen_width - panel_width) // 2 - panel_y = (screen_height - panel_height) // 2 - - # Draw panel background - bg = pygame.Surface((panel_width, panel_height)) - bg.fill((40, 40, 40)) - surface.blit(bg, (panel_x, panel_y)) - - pygame.draw.rect(surface, WHITE, (panel_x, panel_y, panel_width, panel_height), 3) - - title = self.title_font.render("Inventory", True, WHITE) - surface.blit(title, (panel_x + 20, panel_y + 15)) - - close_text = self.font.render("Press E to close", True, GRAY) - surface.blit(close_text, (panel_x + panel_width - 150, panel_y + 15)) - - if self.selected_inventory_item: - instruction = self.small_font.render( - f"Selected: {self.selected_inventory_item.replace('_', ' ').title()} - Click hotbar slot to assign", - True, YELLOW - ) - else: - instruction = self.small_font.render( - "Click item to select, then click hotbar slot to assign", - True, GRAY - ) - surface.blit(instruction, (panel_x + 20, panel_y + 45)) - - # Draw inventory grid with responsive slot size - slot_size = min(70, (panel_width - 80) // 6) - slots_per_row = 6 - start_x = panel_x + 30 - start_y = panel_y + 75 - slot_spacing = 10 - - slot_index = 0 - all_items = list(self.items.keys()) - - for row in range(6): - for col in range(6): - if slot_index >= len(all_items): - break - - x = start_x + col * (slot_size + slot_spacing) - y = start_y + row * (slot_size + slot_spacing) - - item_name = all_items[slot_index] - count = self.items.get(item_name, 0) - is_selected = item_name == self.selected_inventory_item - - slot_color = (80, 100, 80) if is_selected else (60, 60, 60) - pygame.draw.rect(surface, slot_color, (x, y, slot_size, slot_size)) - border_color = YELLOW if is_selected else GRAY - border_width = 3 if is_selected else 2 - pygame.draw.rect(surface, border_color, (x, y, slot_size, slot_size), border_width) - - if count > 0: - self.draw_item_icon(surface, item_name, x + slot_size // 2, y + 15) - - count_text = self.font.render(str(count), True, WHITE) - text_bg = pygame.Surface((count_text.get_width() + 4, count_text.get_height() + 2)) - text_bg.fill((0, 0, 0)) - text_bg.set_alpha(180) - surface.blit(text_bg, (x + 3, y + slot_size - 24)) - surface.blit(count_text, (x + 5, y + slot_size - 22)) - - display_name = item_name.replace("_", " ").title() - if len(display_name) > 9: - display_name = display_name[:8] + "." - name_text = self.small_font.render(display_name, True, WHITE) - name_x = x + slot_size // 2 - name_text.get_width() // 2 - surface.blit(name_text, (name_x, y + 45)) - else: - display_name = item_name.replace("_", " ").title() - if len(display_name) > 9: - display_name = display_name[:8] + "." - name_text = self.small_font.render(display_name, True, (100, 100, 100)) - name_x = x + slot_size // 2 - name_text.get_width() // 2 - name_y = y + slot_size // 2 - name_text.get_height() // 2 - surface.blit(name_text, (name_x, name_y)) - - count_text = self.small_font.render("0", True, (100, 100, 100)) - surface.blit(count_text, (x + 5, y + slot_size - 20)) - - slot_index += 1 - - separator_y = panel_y + panel_height - 120 - pygame.draw.line(surface, GRAY, (panel_x + 20, separator_y), (panel_x + panel_width - 20, separator_y), 2) - - self.draw_integrated_hotbar(surface, panel_x, panel_y, panel_width, panel_height) - - def draw_item_icon(self, surface, item_name, x, y): - """Draw detailed icon for an item""" - - # Seeds - if item_name == "wheat_seed": - # Golden wheat seed - pygame.draw.ellipse(surface, (218, 165, 32), (x - 6, y - 8, 12, 16)) - pygame.draw.ellipse(surface, (180, 130, 20), (x - 6, y - 8, 12, 16), 2) - pygame.draw.line(surface, (150, 110, 15), (x - 2, y - 4), (x - 2, y + 4), 1) - - elif item_name == "carrot_seed": - # Orange carrot seed - pygame.draw.ellipse(surface, (255, 140, 0), (x - 6, y - 8, 12, 16)) - pygame.draw.ellipse(surface, (200, 100, 0), (x - 6, y - 8, 12, 16), 2) - pygame.draw.circle(surface, (255, 180, 50), (x - 2, y - 2), 2) - - elif item_name == "tomato_seed": - # Red tomato seed - pygame.draw.ellipse(surface, (255, 50, 50), (x - 6, y - 8, 12, 16)) - pygame.draw.ellipse(surface, (200, 30, 30), (x - 6, y - 8, 12, 16), 2) - pygame.draw.circle(surface, (255, 100, 100), (x + 2, y - 1), 2) - - elif item_name == "corn_seed": - # Yellow corn seed - pygame.draw.ellipse(surface, (255, 215, 0), (x - 6, y - 8, 12, 16)) - pygame.draw.ellipse(surface, (200, 170, 0), (x - 6, y - 8, 12, 16), 2) - for i in range(3): - pygame.draw.line(surface, (220, 185, 0), (x - 3, y - 4 + i * 3), (x + 3, y - 4 + i * 3), 1) - - # Crops - elif item_name == "wheat": - # Wheat bundle - stem_x = x - for i in range(3): - offset = (i - 1) * 4 - pygame.draw.line(surface, (180, 130, 20), (stem_x + offset, y + 6), (stem_x + offset, y - 6), 2) - pygame.draw.circle(surface, (218, 165, 32), (stem_x + offset, y - 8), 3) - pygame.draw.rect(surface, (139, 69, 19), (x - 6, y + 6, 12, 3)) - - elif item_name == "carrot": - # Carrot with leaves - pygame.draw.polygon(surface, (255, 140, 0), [(x, y + 8), (x - 5, y - 2), (x + 5, y - 2)]) - pygame.draw.polygon(surface, (200, 100, 0), [(x, y + 8), (x - 5, y - 2), (x + 5, y - 2)], 2) - # Leaves - pygame.draw.line(surface, (50, 150, 50), (x - 3, y - 2), (x - 6, y - 8), 2) - pygame.draw.line(surface, (50, 150, 50), (x, y - 2), (x, y - 10), 2) - pygame.draw.line(surface, (50, 150, 50), (x + 3, y - 2), (x + 6, y - 8), 2) - - elif item_name == "tomato": - # Tomato with stem - pygame.draw.circle(surface, (255, 50, 50), (x, y), 10) - pygame.draw.circle(surface, (200, 30, 30), (x, y), 10, 2) - pygame.draw.circle(surface, (255, 100, 100), (x - 3, y - 3), 3) - # Stem - pygame.draw.rect(surface, (50, 150, 50), (x - 1, y - 12, 2, 4)) - pygame.draw.circle(surface, (50, 180, 50), (x - 3, y - 11), 2) - pygame.draw.circle(surface, (50, 180, 50), (x + 3, y - 11), 2) - - elif item_name == "corn": - # Corn cob - pygame.draw.ellipse(surface, (255, 215, 0), (x - 5, y - 10, 10, 20)) - pygame.draw.ellipse(surface, (200, 170, 0), (x - 5, y - 10, 10, 20), 2) - for i in range(6): - pygame.draw.line(surface, (230, 190, 0), (x - 3, y - 8 + i * 3), (x + 3, y - 8 + i * 3), 1) - # Husk - pygame.draw.line(surface, (150, 200, 100), (x - 6, y - 5), (x - 10, y - 10), 2) - pygame.draw.line(surface, (150, 200, 100), (x + 6, y - 5), (x + 10, y - 10), 2) - - # Resources - elif item_name == "wood": - # Wood plank with grain - pygame.draw.rect(surface, (139, 69, 19), (x - 10, y - 8, 20, 16)) - pygame.draw.rect(surface, (101, 50, 15), (x - 10, y - 8, 20, 16), 2) - # Wood grain lines - for i in range(3): - y_offset = y - 4 + i * 4 - pygame.draw.line(surface, (101, 50, 15), (x - 8, y_offset), (x + 8, y_offset), 1) - # Wood rings - pygame.draw.circle(surface, (180, 90, 30), (x - 4, y - 2), 3, 1) - pygame.draw.circle(surface, (180, 90, 30), (x + 3, y + 2), 2, 1) - - elif item_name == "stone": - # Stone with cracks - points = [(x, y - 10), (x + 9, y - 2), (x + 7, y + 8), (x - 7, y + 8), (x - 9, y - 2)] - pygame.draw.polygon(surface, (128, 128, 128), points) - pygame.draw.polygon(surface, (80, 80, 80), points, 2) - # Cracks/texture - pygame.draw.line(surface, (100, 100, 100), (x - 4, y - 4), (x + 2, y), 1) - pygame.draw.line(surface, (100, 100, 100), (x, y - 2), (x + 4, y + 4), 1) - pygame.draw.circle(surface, (150, 150, 150), (x - 3, y + 2), 2) - - # Animal Products - elif item_name == "egg": - # Egg with speckles - pygame.draw.ellipse(surface, (255, 255, 220), (x - 8, y - 10, 16, 20)) - pygame.draw.ellipse(surface, (230, 230, 200), (x - 8, y - 10, 16, 20), 2) - # Speckles - pygame.draw.circle(surface, (220, 200, 180), (x - 3, y - 3), 1) - pygame.draw.circle(surface, (220, 200, 180), (x + 2, y + 1), 1) - pygame.draw.circle(surface, (220, 200, 180), (x - 1, y + 4), 1) - # Shine - pygame.draw.circle(surface, (255, 255, 255), (x - 2, y - 5), 2) - - elif item_name == "milk": - # Milk bottle - pygame.draw.rect(surface, (255, 255, 255), (x - 6, y - 6, 12, 14)) - pygame.draw.rect(surface, (200, 200, 200), (x - 6, y - 6, 12, 14), 2) - # Bottle neck - pygame.draw.rect(surface, (230, 230, 230), (x - 3, y - 10, 6, 4)) - pygame.draw.rect(surface, (200, 200, 200), (x - 3, y - 10, 6, 4), 1) - # Cap - pygame.draw.rect(surface, (255, 200, 200), (x - 4, y - 12, 8, 2)) - # Milk level - pygame.draw.line(surface, (240, 240, 240), (x - 4, y - 2), (x + 4, y - 2), 1) - - elif item_name == "wool": - # Fluffy wool - for offset_x, offset_y in [(-3, -3), (3, -3), (-3, 3), (3, 3), (0, 0)]: - pygame.draw.circle(surface, (245, 245, 245), (x + offset_x, y + offset_y), 4) - for offset_x, offset_y in [(-3, -3), (3, -3), (-3, 3), (3, 3), (0, 0)]: - pygame.draw.circle(surface, (220, 220, 220), (x + offset_x, y + offset_y), 4, 1) - - elif item_name == "truffle": - # Dark truffle - pygame.draw.ellipse(surface, (60, 40, 30), (x - 8, y - 6, 16, 12)) - pygame.draw.ellipse(surface, (40, 20, 10), (x - 8, y - 6, 16, 12), 2) - # Texture spots - pygame.draw.circle(surface, (50, 30, 20), (x - 2, y - 1), 2) - pygame.draw.circle(surface, (50, 30, 20), (x + 3, y + 1), 1) - - elif item_name == "cheese": - # Yellow cheese wedge - points = [(x - 8, y + 6), (x + 8, y + 6), (x + 2, y - 8)] - pygame.draw.polygon(surface, (255, 220, 100), points) - pygame.draw.polygon(surface, (220, 180, 60), points, 2) - # Holes - pygame.draw.circle(surface, (230, 200, 80), (x - 2, y + 2), 2) - pygame.draw.circle(surface, (230, 200, 80), (x + 3, y - 1), 2) - - elif item_name == "duck_egg": - # Slightly blue-tinted egg - pygame.draw.ellipse(surface, (220, 240, 240), (x - 8, y - 10, 16, 20)) - pygame.draw.ellipse(surface, (200, 220, 220), (x - 8, y - 10, 16, 20), 2) - pygame.draw.circle(surface, (255, 255, 255), (x - 2, y - 5), 2) - - elif item_name == "rabbit_foot": - # Lucky rabbit foot - pygame.draw.ellipse(surface, (200, 200, 200), (x - 4, y - 8, 8, 16)) - pygame.draw.ellipse(surface, (180, 180, 180), (x - 4, y - 8, 8, 16), 2) - # Toes - for toe_x in [-2, 2]: - pygame.draw.circle(surface, (190, 190, 190), (x + toe_x, y + 8), 2) - pygame.draw.circle(surface, (190, 190, 190), (x, y + 10), 2) - - elif item_name == "horseshoe": - # U-shaped horseshoe - pygame.draw.arc(surface, (150, 150, 150), (x - 8, y - 6, 16, 16), 0, 3.14, 3) - pygame.draw.circle(surface, (130, 130, 130), (x - 6, y + 4), 2) - pygame.draw.circle(surface, (130, 130, 130), (x + 6, y + 4), 2) - - elif item_name == "llama_wool": - # Colorful llama wool - for offset_x, offset_y in [(-3, -3), (3, -3), (-3, 3), (3, 3), (0, 0)]: - pygame.draw.circle(surface, (220, 200, 180), (x + offset_x, y + offset_y), 4) - for offset_x, offset_y in [(-3, -3), (3, -3), (-3, 3), (3, 3), (0, 0)]: - pygame.draw.circle(surface, (200, 180, 160), (x + offset_x, y + offset_y), 4, 1) - - elif item_name == "turkey_feather": - # Striped feather - pygame.draw.ellipse(surface, (160, 82, 45), (x - 3, y - 10, 6, 20)) - pygame.draw.ellipse(surface, (120, 60, 30), (x - 3, y - 10, 6, 20), 1) - # Stripes - for i in range(4): - pygame.draw.line(surface, (100, 60, 30), (x - 2, y - 8 + i * 4), (x + 2, y - 8 + i * 4), 1) - # Feather tip - pygame.draw.circle(surface, (180, 100, 50), (x, y - 10), 2) - - # Crafted items - elif item_name == "fence": - # Fence piece - pygame.draw.rect(surface, (139, 69, 19), (x - 8, y - 2, 16, 3)) - pygame.draw.rect(surface, (139, 69, 19), (x - 8, y + 2, 16, 3)) - pygame.draw.rect(surface, (101, 50, 15), (x - 6, y - 8, 3, 16)) - pygame.draw.rect(surface, (101, 50, 15), (x + 3, y - 8, 3, 16)) - - elif item_name == "scarecrow": - # Scarecrow figure - pygame.draw.rect(surface, (139, 69, 19), (x - 1, y - 6, 2, 12)) - pygame.draw.rect(surface, (139, 69, 19), (x - 8, y - 2, 16, 2)) - pygame.draw.circle(surface, (218, 165, 32), (x, y - 8), 4) - pygame.draw.circle(surface, BLACK, (x - 2, y - 9), 1) - pygame.draw.circle(surface, BLACK, (x + 2, y - 9), 1) - - elif item_name == "chest": - # Wooden chest - pygame.draw.rect(surface, (101, 67, 33), (x - 10, y - 6, 20, 14)) - pygame.draw.rect(surface, (80, 50, 25), (x - 10, y - 6, 20, 14), 2) - pygame.draw.rect(surface, (139, 90, 43), (x - 2, y, 4, 3)) - pygame.draw.line(surface, (80, 50, 25), (x - 8, y - 4), (x + 8, y - 4), 1) - - # Default for unknown items - else: - pygame.draw.rect(surface, (150, 150, 150), (x - 10, y - 10, 20, 20)) - pygame.draw.rect(surface, WHITE, (x - 10, y - 10, 20, 20), 2) - pygame.draw.line(surface, (200, 200, 200), (x - 6, y - 6), (x + 6, y + 6), 2) - pygame.draw.line(surface, (200, 200, 200), (x - 6, y + 6), (x + 6, y - 6), 2) - - def draw(self, surface, screen_width, screen_height): - """Draw inventory UI""" - if self.show_full_inventory: - self.draw_full_inventory(surface, screen_width, screen_height) - else: - self.draw_hotbar(surface, screen_width, screen_height) \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 83926b9..0000000 --- a/main.py +++ /dev/null @@ -1,787 +0,0 @@ -import pygame -import sys -import json -from settings import * -from player import Player -from inventory import Inventory -# from crafting import Crafting -from world import World -from animal import Animal -from npc import NPC -from time_system import TimeSystem -from ui import UI -from plot_system import PlotSystem -from quest_system import QuestSystem - -class FarmGame: - def __init__(self): - pygame.init() - self.screen = pygame.display.set_mode((DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT), - pygame.RESIZABLE) - pygame.display.set_caption("Pixel Farm - Harvest Valley") - self.clock = pygame.time.Clock() - - # Track current window size - self.screen_width = DEFAULT_SCREEN_WIDTH - self.screen_height = DEFAULT_SCREEN_HEIGHT - - # Create world surface (fixed size for game world) - self.world_width = TILE_SIZE * MAP_WIDTH - self.world_height = TILE_SIZE * MAP_HEIGHT - self.world_surface = pygame.Surface((self.world_width, self.world_height)) - - # Game objects - self.player = Player((self.world_width // 2, self.world_height // 2)) - self.world = World() - self.world.create_default_map() - - # Animals - NOW WITH MORE VARIETY! - self.animals = pygame.sprite.Group() - # Original animals - self.animals.add(Animal((300, 300), "chicken")) - self.animals.add(Animal((350, 320), "chicken")) - self.animals.add(Animal((500, 400), "cow")) - - # New animals - self.animals.add(Animal((400, 350), "pig")) - self.animals.add(Animal((550, 320), "rabbit")) - self.animals.add(Animal((380, 280), "turkey")) - - # NPCs - self.npcs = pygame.sprite.Group() - self.shopkeeper = NPC((100, 150), "shopkeeper") - self.mayor = NPC((self.world_width - 100, 150), "mayor") - self.npcs.add(self.shopkeeper, self.mayor) - - # Show welcome message - self.show_notification("Welcome! Press F near Shopkeeper to trade!") - self.notification_timer = 300 - - # Systems - self.inventory = Inventory() - # self.crafting = Crafting() - self.time_system = TimeSystem() - self.ui = UI() - self.plot_system = PlotSystem() - self.quest_system = QuestSystem() - - # Track last completed quest for notification - self.last_completed_quest = None - self.quest_notification_timer = 0 - - # Game state - self.show_grid = False - self.notification = "" - self.notification_timer = 0 - self.paused = False - self.show_exit_confirmation = False - - # Interaction - self.nearby_npc = None - self.nearby_animal = None - self.interaction_distance = 60 - - # Interaction prompt animation - self.prompt_timer = 0 - self.prompt_fade_duration = 120 # 2 seconds at 60 FPS to fully fade - self.prompt_shown = False # Track if prompt was already shown - - self.font = pygame.font.Font(None, 24) - - # Settings button rect - self.settings_button_rect = None - - def handle_resize(self, width, height): - """Handle window resize event""" - self.screen_width = max(MIN_SCREEN_WIDTH, width) - self.screen_height = max(MIN_SCREEN_HEIGHT, height) - self.screen = pygame.display.set_mode((self.screen_width, self.screen_height), - pygame.RESIZABLE) - # Update global settings - update_screen_size(self.screen_width, self.screen_height) - - def screen_to_world_pos(self, screen_pos): - """Convert screen position to world position""" - # Calculate scale to fit world in window - scale_x = self.screen_width / self.world_width - scale_y = self.screen_height / self.world_height - scale = min(scale_x, scale_y) - - # Calculate scaled dimensions and offset - scaled_width = int(self.world_width * scale) - scaled_height = int(self.world_height * scale) - offset_x = (self.screen_width - scaled_width) // 2 - offset_y = (self.screen_height - scaled_height) // 2 - - # Adjust for world offset - adjusted_x = screen_pos[0] - offset_x - adjusted_y = screen_pos[1] - offset_y - - # Scale to world coordinates - world_x = adjusted_x / scale - world_y = adjusted_y / scale - - return (int(world_x), int(world_y)) - - def is_click_in_world(self, screen_pos): - """Check if a screen position is within the world area""" - scale_x = self.screen_width / self.world_width - scale_y = self.screen_height / self.world_height - scale = min(scale_x, scale_y) - - scaled_width = int(self.world_width * scale) - scaled_height = int(self.world_height * scale) - offset_x = (self.screen_width - scaled_width) // 2 - offset_y = (self.screen_height - scaled_height) // 2 - - x, y = screen_pos - return (offset_x <= x < offset_x + scaled_width and - offset_y <= y < offset_y + scaled_height) - - def handle_events(self): - """Handle all game events""" - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self.save_game() - pygame.quit() - sys.exit() - - elif event.type == pygame.VIDEORESIZE: - self.handle_resize(event.w, event.h) - - elif event.type == pygame.KEYDOWN: - # F key - Interact with nearby NPCs/Animals - if event.key == pygame.K_f: - if self.nearby_npc: - self.interact_with_npc(self.nearby_npc) - elif self.nearby_animal: - self.interact_with_animal(self.nearby_animal) - - # L key - Toggle plot lock at mouse position - elif event.key == pygame.K_l: - screen_pos = pygame.mouse.get_pos() - if self.is_click_in_world(screen_pos): - world_pos = self.screen_to_world_pos(screen_pos) - grid_x = world_pos[0] // TILE_SIZE - grid_y = world_pos[1] // TILE_SIZE - grid_pos = (grid_x, grid_y) - - if self.plot_system.is_claimed(grid_pos): - success, message = self.plot_system.toggle_lock(grid_pos) - if message: - self.show_notification(message) - - # Tool selection - elif event.key == pygame.K_t: - self.player.change_tool() - self.show_notification(f"Tool: {self.player.current_tool.replace('_', ' ').title()}") - - # Hotbar selection (1-5) - elif event.key == pygame.K_1: - self.inventory.selected_hotbar_slot = 0 - elif event.key == pygame.K_2: - self.inventory.selected_hotbar_slot = 1 - elif event.key == pygame.K_3: - self.inventory.selected_hotbar_slot = 2 - elif event.key == pygame.K_4: - self.inventory.selected_hotbar_slot = 3 - elif event.key == pygame.K_5: - self.inventory.selected_hotbar_slot = 4 - - # Toggle systems - elif event.key == pygame.K_e: - self.inventory.toggle_inventory() - elif event.key == pygame.K_c: - self.show_notification("Crafting menu is currently disabled.") - #self.crafting.toggle_menu() - elif event.key == pygame.K_q: - self.quest_system.toggle_quest_tab() - elif event.key == pygame.K_i: - self.show_grid = not self.show_grid - elif event.key == pygame.K_h: - self.show_notification("Shopkeeper is in top-left area with 'SHOP' label!") - - # Close shop with ESC or show exit confirmation - elif event.key == pygame.K_ESCAPE: - if self.shopkeeper.shop_mode: - self.shopkeeper.shop_mode = None - elif self.show_exit_confirmation: - # Close the exit dialog if ESC is pressed again - self.show_exit_confirmation = False - else: - # Show exit confirmation dialog - self.show_exit_confirmation = True - - # Handle exit confirmation - elif event.key == pygame.K_y and self.show_exit_confirmation: - self.save_game() - pygame.quit() - sys.exit() - elif event.key == pygame.K_n and self.show_exit_confirmation: - self.show_exit_confirmation = False - - elif event.type == pygame.MOUSEBUTTONDOWN: - mouse_pos = pygame.mouse.get_pos() - - # Check settings button click - if self.settings_button_rect and self.settings_button_rect.collidepoint(mouse_pos) and event.button == 1: - self.ui.toggle_controls() - continue - - # Handle inventory clicks first - if self.inventory.show_full_inventory and event.button == 1: - if self.inventory.handle_inventory_click(mouse_pos, self.screen_width, self.screen_height): - continue - - # Handle quest tab clicks - if self.quest_system.show_quest_tab and event.button == 1: - result = self.quest_system.handle_click(mouse_pos, self.screen_width, - self.screen_height, self.player, self.inventory) - if result: - self.show_notification(result) - continue - - # Don't process tool/interact clicks if inventory or quest tab is open - if self.inventory.show_full_inventory or self.quest_system.show_quest_tab: - continue - - # Handle crafting menu clicks - # if self.crafting.show_menu and event.button == 1: - if self.crafting.handle_click(mouse_pos, self.inventory, self.screen_width, self.screen_height): - self.show_notification("Item crafted!") - - # Track crafted item for quests - for recipe_name in self.crafting.recipes: - completed = self.quest_system.update_quest("craft", recipe_name, 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - break - - # Handle shop clicks - elif self.shopkeeper.shop_mode and event.button == 1: - action, result, message = self.shopkeeper.handle_shop_click( - mouse_pos, self.player, self.inventory, self.screen_width, self.screen_height - ) - if message: - self.show_notification(message) - - # Left click - use tool (only if clicking in world area) - # elif event.button == 1 and not self.crafting.show_menu: - # if self.is_click_in_world(mouse_pos): - # world_pos = self.screen_to_world_pos(mouse_pos) - # self.use_tool(world_pos) - - # Right click - claim or sell plots - elif event.button == 3: - if self.shopkeeper.shop_mode: - self.shopkeeper.shop_mode = None - elif self.is_click_in_world(mouse_pos): - world_pos = self.screen_to_world_pos(mouse_pos) - grid_x = world_pos[0] // TILE_SIZE - grid_y = world_pos[1] // TILE_SIZE - grid_pos = (grid_x, grid_y) - - # Check if plot is already claimed - if so, try to sell - if self.plot_system.is_claimed(grid_pos): - success, message = self.plot_system.sell_plot(grid_pos, self.player, self.world) - if message: - self.show_notification(message) - else: - # Try to claim plot - success, message = self.plot_system.claim_plot(grid_pos, self.player, self.world) - if success or message: - self.show_notification(message) - - if success: - # Update quest - completed = self.quest_system.update_quest("claim", "plot", 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - def check_nearby_entities(self): - """Check for nearby NPCs and animals for F key interaction""" - player_pos = pygame.math.Vector2(self.player.rect.center) - - # Check NPCs - self.nearby_npc = None - for npc in self.npcs: - npc_pos = pygame.math.Vector2(npc.rect.center) - distance = player_pos.distance_to(npc_pos) - if distance <= self.interaction_distance: - self.nearby_npc = npc - break - - # Check animals (only if no NPC nearby) - self.nearby_animal = None - if not self.nearby_npc: - for animal in self.animals: - animal_pos = pygame.math.Vector2(animal.rect.center) - distance = player_pos.distance_to(animal_pos) - if distance <= self.interaction_distance: - self.nearby_animal = animal - break - - def interact_with_npc(self, npc): - """Interact with an NPC""" - dialogue = npc.talk() - - def interact_with_animal(self, animal): - """Interact with an animal""" - # Check animal state and respond accordingly - if animal.can_collect(): - # Collect product - product, value = animal.collect_product() - if product: - self.inventory.add_item(product, 1) - self.show_notification(f"Collected {product.replace('_', ' ')}! You can now feed the animal again!") - - # Update quests - completed = self.quest_system.update_quest("collect", product, 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - elif animal.can_feed(): - # Feed the animal - if animal.feed(): - self.show_notification(f"Fed {animal.animal_type}! Wait for digestion.") - - # Update quests - completed = self.quest_system.update_quest("feed", "animal", 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - else: - # Animal is in cooldown or producing - state_info = animal.get_state_info() - self.show_notification(f"{animal.animal_type.title()}: {state_info}") - - def use_tool(self, pos): - """Use the currently equipped tool""" - tool = self.player.current_tool - - if tool == "hoe": - # Till soil - ONLY on claimed plots - grid_x = pos[0] // TILE_SIZE - grid_y = pos[1] // TILE_SIZE - grid_pos = (grid_x, grid_y) - - if not self.plot_system.is_claimed(grid_pos): - self.show_notification("Must claim plot first! (Right-click grass)") - return - - if self.player.use_energy(5): - if self.world.till(pos): - self.show_notification("Soil tilled!") - - # Update quests - completed = self.quest_system.update_quest("till", "soil", 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - elif tool == "watering_can": - # Water crops and soil - if self.player.use_energy(3): - if self.world.water(pos): - self.show_notification("Watered!") - - # Update quests - completed = self.quest_system.update_quest("water", "crop", 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - elif tool == "hand": - # Plant or harvest - crop_type = self.inventory.get_selected_seed() - if crop_type: - seed_name = f"{crop_type}_seed" - if self.inventory.use(seed_name): - if self.world.plant(pos, crop_type): - self.show_notification(f"Planted {crop_type}!") - - # Update quests - completed = self.quest_system.update_quest("plant", crop_type, 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - else: - # Refund seed if planting failed - self.inventory.add_item(seed_name, 1) - else: - # Try to harvest - crop_type, value = self.world.harvest(pos) - if crop_type: - self.inventory.add_item(crop_type, 1) - self.show_notification(f"Harvested {crop_type}! Sell to shopkeeper!") - - # Update quests - completed = self.quest_system.update_quest("harvest", crop_type, 1) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - elif tool == "axe": - # Chop trees for wood - tile = self.world.get_tile_at_pos(pos) - if tile and tile.kind == "T" and self.player.use_energy(10): - self.inventory.add_item("wood", 3) - self.show_notification("Chopped wood! +3 wood") - - # Update quests - completed = self.quest_system.update_quest("collect", "wood", 3) - if completed: - self.last_completed_quest = completed - self.quest_notification_timer = 180 - - elif tool == "scythe": - # Clear grass - tile = self.world.get_tile_at_pos(pos) - if tile and tile.kind == "G" and self.player.use_energy(2): - self.show_notification("Cleared grass!") - - def show_notification(self, message): - """Show a notification message""" - self.notification = message - self.notification_timer = 120 - - def update(self, dt): - """Update all game systems""" - if self.paused: - return - - keys = pygame.key.get_pressed() - - # Update systems - self.player.update(keys, dt) - self.world.update() - self.time_system.update(dt) - - # Check for nearby entities - self.check_nearby_entities() - - # Update animals - for animal in self.animals: - animal.update(dt) - - # Update NPCs - for npc in self.npcs: - npc.update(dt) - - # Update notification - if self.notification_timer > 0: - self.notification_timer -= 1 - if self.notification_timer == 0: - self.notification = "" - - # Update quest notification - if self.quest_notification_timer > 0: - self.quest_notification_timer -= 1 - if self.quest_notification_timer == 0: - self.last_completed_quest = None - - # Update interaction prompt animation timer - if self.nearby_npc or self.nearby_animal: - if self.prompt_timer < self.prompt_fade_duration: - self.prompt_timer += 1 - else: - # Reset when leaving the area - self.prompt_timer = 0 - - # Update quest for money (check periodically) - if hasattr(self, 'last_money_check'): - if self.player.money != self.last_money_check: - self.quest_system.update_quest("money", "total", self.player.money) - self.last_money_check = self.player.money - else: - self.last_money_check = self.player.money - - def draw(self): - """Draw everything""" - # Clear screen with black - self.screen.fill(BLACK) - - # Draw world to world surface - self.world_surface.fill(BLACK) - self.world.tiles.draw(self.world_surface) - - # Draw claimed plot indicators - self.plot_system.draw_claimed_indicators(self.world_surface) - - # Draw grid if enabled - if self.show_grid: - self.world.draw_grid(self.world_surface) - - # Draw crops - self.world.crops.draw(self.world_surface) - - # Draw crop status indicators - for crop in self.world.crops: - crop.draw_status(self.world_surface) - - # Draw animals - self.animals.draw(self.world_surface) - for animal in self.animals: - animal.draw_status(self.world_surface) - - # Draw NPCs - self.npcs.draw(self.world_surface) - for npc in self.npcs: - npc.draw_label(self.world_surface) - npc.draw_dialogue(self.world_surface) - - # Draw player - self.world_surface.blit(self.player.image, self.player.rect) - - # Draw interaction prompt (in world space) - if self.nearby_npc: - self.draw_interaction_prompt_world(self.world_surface, - self.nearby_npc.rect.centerx, - self.nearby_npc.rect.top - 30, - f"Press [F] to talk to {self.nearby_npc.npc_type.title()}") - elif self.nearby_animal: - if self.nearby_animal.can_collect(): - action = "Collect" - elif self.nearby_animal.can_feed(): - action = "Feed" - else: - action = "Check" - self.draw_interaction_prompt_world(self.world_surface, - self.nearby_animal.rect.centerx, - self.nearby_animal.rect.top - 30, - f"Press [F] to {action} {self.nearby_animal.animal_type.title()}") - - # Draw claimable/sellable plot hint (only if inventory not open) - in world space - if not self.inventory.show_full_inventory: - screen_mouse_pos = pygame.mouse.get_pos() - if self.is_click_in_world(screen_mouse_pos): - world_mouse_pos = self.screen_to_world_pos(screen_mouse_pos) - self.plot_system.draw_claimable_hint(self.world_surface, world_mouse_pos, self.world, self.player) - - # Apply darkness for night - if self.time_system.is_night(): - darkness = pygame.Surface((self.world_width, self.world_height)) - darkness.set_alpha(self.time_system.get_darkness_alpha()) - darkness.fill((0, 0, 40)) - self.world_surface.blit(darkness, (0, 0)) - - # Scale world to fill window while maintaining aspect ratio - scale_x = self.screen_width / self.world_width - scale_y = self.screen_height / self.world_height - scale = min(scale_x, scale_y) - - scaled_width = int(self.world_width * scale) - scaled_height = int(self.world_height * scale) - offset_x = (self.screen_width - scaled_width) // 2 - offset_y = (self.screen_height - scaled_height) // 2 - - scaled_surface = pygame.transform.scale(self.world_surface, (scaled_width, scaled_height)) - self.screen.blit(scaled_surface, (offset_x, offset_y)) - - # Draw UI elements (screen space) - self.ui.draw_player_stats(self.screen, self.player, self.time_system) - self.ui.draw_controls(self.screen, self.screen_width, self.screen_height) - - # Draw inventory (hotbar always visible, full inventory when toggled) - self.inventory.draw(self.screen, self.screen_width, self.screen_height) - - # Draw crafting menu - # self.crafting.draw_menu(self.screen, self.inventory, self.screen_width, self.screen_height) - - # Draw quest tab - self.quest_system.draw_quest_tab(self.screen, self.screen_width, self.screen_height) - - # Draw quest notification - if self.last_completed_quest and self.quest_notification_timer > 0: - self.quest_system.draw_quest_notification(self.last_completed_quest, - self.screen, self.screen_width, self.screen_height) - - # Draw shop menus - if self.shopkeeper.shop_mode: - self.shopkeeper.draw_shop_menu(self.screen, self.screen_width, self.screen_height) - self.shopkeeper.draw_buy_menu(self.screen, self.player.money, self.screen_width, self.screen_height) - self.shopkeeper.draw_sell_menu(self.screen, self.inventory, self.player.money, self.screen_width, self.screen_height) - - # Draw notification - if self.notification: - self.ui.draw_notification(self.screen, self.notification, self.screen_width, self.screen_height) - - # Draw settings button (always on top) - self.settings_button_rect = self.ui.draw_settings_button(self.screen, self.screen_width, self.screen_height) - - # Draw exit confirmation dialog (if active) - if self.show_exit_confirmation: - self.draw_exit_confirmation() - - # Update display - pygame.display.flip() - - def draw_interaction_prompt_world(self, surface, x, y, text): - """Draw interaction prompt in world space with fade-out animation""" - # Calculate alpha based on timer - fade from 255 to 0 over 2 seconds - fade_progress = self.prompt_timer / self.prompt_fade_duration - alpha = int(255 * (1 - fade_progress)) - - # Don't draw if fully faded - if alpha <= 0: - return - - prompt_font = pygame.font.Font(None, 18) - text_surface = prompt_font.render(text, True, WHITE) - - # Background - padding = 6 - bg_width = text_surface.get_width() + padding * 2 - bg_height = text_surface.get_height() + padding * 2 - bg_x = x - bg_width // 2 - bg_y = y - - bg = pygame.Surface((bg_width, bg_height)) - bg.set_alpha(int(220 * (alpha / 255))) # Scale background alpha - bg.fill((40, 40, 40)) - surface.blit(bg, (bg_x, bg_y)) - - # Border - create a surface with alpha - border_surface = pygame.Surface((bg_width, bg_height), pygame.SRCALPHA) - pygame.draw.rect(border_surface, (*YELLOW, alpha), (0, 0, bg_width, bg_height), 2) - surface.blit(border_surface, (bg_x, bg_y)) - - # Text with alpha - text_with_alpha = pygame.Surface((text_surface.get_width(), text_surface.get_height()), pygame.SRCALPHA) - text_with_alpha.fill((0, 0, 0, 0)) - temp_text = prompt_font.render(text, True, (*WHITE, alpha)) - text_with_alpha.blit(temp_text, (0, 0)) - text_with_alpha.set_alpha(alpha) - - text_x = bg_x + padding - text_y = bg_y + padding - surface.blit(text_with_alpha, (text_x, text_y)) - - def draw_exit_confirmation(self): - """Draw exit confirmation dialog""" - # Create semi-transparent overlay - overlay = pygame.Surface((self.screen_width, self.screen_height)) - overlay.set_alpha(180) - overlay.fill((0, 0, 0)) - self.screen.blit(overlay, (0, 0)) - - # Dialog box - dialog_width = 400 - dialog_height = 150 - dialog_x = (self.screen_width - dialog_width) // 2 - dialog_y = (self.screen_height - dialog_height) // 2 - - # Background - pygame.draw.rect(self.screen, (50, 50, 50), (dialog_x, dialog_y, dialog_width, dialog_height)) - pygame.draw.rect(self.screen, (200, 200, 200), (dialog_x, dialog_y, dialog_width, dialog_height), 3) - - # Title text - title_font = pygame.font.Font(None, 36) - title_text = title_font.render("Exit Game?", True, WHITE) - title_x = dialog_x + (dialog_width - title_text.get_width()) // 2 - title_y = dialog_y + 20 - self.screen.blit(title_text, (title_x, title_y)) - - # Message text - message_font = pygame.font.Font(None, 24) - message_text = message_font.render("Do you want to exit the game?", True, WHITE) - message_x = dialog_x + (dialog_width - message_text.get_width()) // 2 - message_y = dialog_y + 60 - self.screen.blit(message_text, (message_x, message_y)) - - # Instructions - instruction_font = pygame.font.Font(None, 22) - yes_text = instruction_font.render("Press [Y] for Yes", True, (100, 255, 100)) - no_text = instruction_font.render("Press [N] for No", True, (255, 100, 100)) - - yes_x = dialog_x + dialog_width // 4 - yes_text.get_width() // 2 - no_x = dialog_x + 3 * dialog_width // 4 - no_text.get_width() // 2 - instruction_y = dialog_y + 100 - - self.screen.blit(yes_text, (yes_x, instruction_y)) - self.screen.blit(no_text, (no_x, instruction_y)) - - def save_game(self): - """Save game state""" - save_data = { - "player": { - "pos": self.player.rect.center, - "money": self.player.money, - "energy": self.player.energy - }, - "inventory": self.inventory.items, - "hotbar": self.inventory.hotbar, - "time": { - "time": self.time_system.time, - "day": self.time_system.day, - "season": self.time_system.season - }, - "plots": self.plot_system.save_data(), - "quests": self.quest_system.save_data() - } - - try: - with open("save_game.json", "w") as f: - json.dump(save_data, f, indent=2) - print("Game saved!") - except Exception as e: - print(f"Error saving game: {e}") - - def load_game(self): - """Load game state""" - try: - with open("save_game.json", "r") as f: - save_data = json.load(f) - - # Restore player - self.player.rect.center = tuple(save_data["player"]["pos"]) - self.player.money = save_data["player"]["money"] - self.player.energy = save_data["player"]["energy"] - - # Restore inventory - self.inventory.items = save_data["inventory"] - - # Restore hotbar - if "hotbar" in save_data: - self.inventory.hotbar = save_data["hotbar"] - - # Restore time - self.time_system.time = save_data["time"]["time"] - self.time_system.day = save_data["time"]["day"] - self.time_system.season = save_data["time"]["season"] - - # Restore plots - if "plots" in save_data: - self.plot_system.load_data(save_data["plots"]) - - # Restore quests - if "quests" in save_data: - self.quest_system.load_data(save_data["quests"]) - - print("Game loaded!") - return True - except FileNotFoundError: - print("No save file found, starting new game") - return False - except Exception as e: - print(f"Error loading game: {e}") - return False - - def run(self): - """Main game loop""" - # Try to load save - self.load_game() - - while True: - dt = self.clock.tick(FPS) / 16.67 - - self.handle_events() - self.update(dt) - self.draw() - -if __name__ == "__main__": - game = FarmGame() - game.run() \ No newline at end of file diff --git a/map.txt b/map.txt deleted file mode 100644 index 0e5821b..0000000 --- a/map.txt +++ /dev/null @@ -1,5 +0,0 @@ -GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG -GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG -GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG -GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG -GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG diff --git a/npc.py b/npc.py deleted file mode 100644 index b71387d..0000000 --- a/npc.py +++ /dev/null @@ -1,471 +0,0 @@ -import pygame -from settings import * -from crop import Crop - -class NPC(pygame.sprite.Sprite): - NPC_DATA = { - "shopkeeper": { - "color": (150, 75, 0), - "dialogues": [ - "Welcome to my shop!", - "Need some seeds?", - "Fresh supplies daily!", - "How's the farm going?" - ], - "shop": True - }, - "mayor": { - "color": (50, 50, 150), - "dialogues": [ - "Welcome to our village!", - "The harvest festival is coming!", - "Keep up the good work!", - "We're proud of our farmers!" - ], - "shop": False - }, - "fisherman": { - "color": (100, 150, 200), - "dialogues": [ - "The fish are biting today!", - "Have you tried fishing?", - "I caught a big one yesterday!", - "The lake is beautiful this time of year." - ], - "shop": False - } - } - - def __init__(self, pos, npc_type="shopkeeper"): - super().__init__() - - self.npc_type = npc_type - self.data = self.NPC_DATA[npc_type] - - # Create NPC sprite - self.image = pygame.Surface((28, 32)) - self.create_sprite() - - self.rect = self.image.get_rect(center=pos) - - # Dialogue - self.current_dialogue = 0 - self.dialogue_visible = False - self.dialogue_timer = 0 - self.font = pygame.font.Font(None, 18) - self.title_font = pygame.font.Font(None, 24) - - # Shop state - self.shop_mode = None # None, 'menu', 'buy', or 'sell' - - def create_sprite(self): - """Create NPC visual representation""" - color = self.data["color"] - - # Body - self.image.fill((255, 200, 150)) # Skin - pygame.draw.rect(self.image, color, (6, 8, 16, 12)) # Shirt - pygame.draw.rect(self.image, (50, 50, 50), (6, 20, 16, 12)) # Pants - - # Head - pygame.draw.circle(self.image, (255, 220, 177), (14, 6), 6) - pygame.draw.circle(self.image, BLACK, (11, 5), 2) # Left eye - pygame.draw.circle(self.image, BLACK, (17, 5), 2) # Right eye - pygame.draw.line(self.image, BLACK, (12, 8), (16, 8), 1) # Smile - - # Hat (for shopkeeper) - if self.npc_type == "shopkeeper": - pygame.draw.rect(self.image, color, (8, 0, 12, 4)) - pygame.draw.rect(self.image, color, (6, 3, 16, 2)) - - # Fishing rod (for fisherman) - if self.npc_type == "fisherman": - pygame.draw.line(self.image, BROWN, (24, 12), (28, 2), 2) - - def talk(self): - """Start dialogue with NPC""" - if self.npc_type == "shopkeeper": - self.shop_mode = 'menu' - return "Hello! What can I do for you today?" - else: - self.dialogue_visible = True - self.dialogue_timer = 180 # Show for 3 seconds at 60 FPS - dialogue = self.data["dialogues"][self.current_dialogue] - self.current_dialogue = (self.current_dialogue + 1) % len(self.data["dialogues"]) - return dialogue - - def update(self, dt): - """Update NPC""" - if self.dialogue_visible: - self.dialogue_timer -= 1 - if self.dialogue_timer <= 0: - self.dialogue_visible = False - - def draw_dialogue(self, surface): - """Draw dialogue bubble""" - if self.dialogue_visible and self.npc_type != "shopkeeper": - dialogue = self.data["dialogues"][self.current_dialogue - 1] - - # Create dialogue box - padding = 10 - text_surface = self.font.render(dialogue, True, BLACK) - box_width = text_surface.get_width() + padding * 2 - box_height = text_surface.get_height() + padding * 2 - - # Position above NPC - box_x = self.rect.centerx - box_width // 2 - box_y = self.rect.top - box_height - 10 - - # Keep on screen (use world surface dimensions) - world_width = TILE_SIZE * MAP_WIDTH - world_height = TILE_SIZE * MAP_HEIGHT - box_x = max(5, min(box_x, world_width - box_width - 5)) - box_y = max(5, box_y) - - # Draw box - pygame.draw.rect(surface, WHITE, (box_x, box_y, box_width, box_height)) - pygame.draw.rect(surface, BLACK, (box_x, box_y, box_width, box_height), 2) - - # Draw pointer - points = [ - (self.rect.centerx, self.rect.top - 5), - (self.rect.centerx - 5, box_y + box_height), - (self.rect.centerx + 5, box_y + box_height) - ] - pygame.draw.polygon(surface, WHITE, points) - pygame.draw.lines(surface, BLACK, True, points, 2) - - # Draw text - surface.blit(text_surface, (box_x + padding, box_y + padding)) - - def draw_label(self, surface): - """Draw name label above NPC""" - if self.npc_type == "shopkeeper": - label_font = pygame.font.Font(None, 16) - label = label_font.render("SHOP", True, (255, 215, 0)) - label_bg = pygame.Surface((label.get_width() + 6, label.get_height() + 4)) - label_bg.fill((0, 0, 0)) - label_bg.set_alpha(180) - - label_x = self.rect.centerx - label.get_width() // 2 - 3 - label_y = self.rect.top - 20 - - surface.blit(label_bg, (label_x, label_y)) - surface.blit(label, (label_x + 3, label_y + 2)) - - def draw_shop_menu(self, surface, screen_width, screen_height): - """Draw the initial shop menu with Buy/Sell options""" - if self.shop_mode != 'menu': - return - - menu_width = 400 - menu_height = 280 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - # Background - bg = pygame.Surface((menu_width, menu_height)) - bg.set_alpha(240) - bg.fill((60, 40, 20)) - surface.blit(bg, (menu_x, menu_y)) - - # Border - pygame.draw.rect(surface, YELLOW, (menu_x, menu_y, menu_width, menu_height), 3) - - # Title - title = self.title_font.render("Shopkeeper's Store", True, YELLOW) - surface.blit(title, (menu_x + 20, menu_y + 15)) - - # Greeting - greeting = self.font.render("Hello there! What would you like to do?", True, WHITE) - surface.blit(greeting, (menu_x + 20, menu_y + 55)) - - # Buy button - buy_rect = pygame.Rect(menu_x + 40, menu_y + 100, menu_width - 80, 50) - pygame.draw.rect(surface, (80, 120, 80), buy_rect) - pygame.draw.rect(surface, WHITE, buy_rect, 3) - buy_text = self.title_font.render("BUY SEEDS", True, WHITE) - buy_x = buy_rect.centerx - buy_text.get_width() // 2 - buy_y = buy_rect.centery - buy_text.get_height() // 2 - surface.blit(buy_text, (buy_x, buy_y)) - - # Sell button - sell_rect = pygame.Rect(menu_x + 40, menu_y + 165, menu_width - 80, 50) - pygame.draw.rect(surface, (120, 80, 80), sell_rect) - pygame.draw.rect(surface, WHITE, sell_rect, 3) - sell_text = self.title_font.render("SELL CROPS", True, WHITE) - sell_x = sell_rect.centerx - sell_text.get_width() // 2 - sell_y = sell_rect.centery - sell_text.get_height() // 2 - surface.blit(sell_text, (sell_x, sell_y)) - - # Close instruction - close_text = self.font.render("Right-click or ESC to close", True, GRAY) - surface.blit(close_text, (menu_x + 20, menu_y + menu_height - 30)) - - def draw_buy_menu(self, surface, player_money, screen_width, screen_height): - """Draw the buy seeds menu""" - if self.shop_mode != 'buy': - return - - shop_items = self.get_shop_items() - - menu_width = 400 - menu_height = 380 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - # Background - bg = pygame.Surface((menu_width, menu_height)) - bg.set_alpha(240) - bg.fill((60, 40, 20)) - surface.blit(bg, (menu_x, menu_y)) - - # Border - pygame.draw.rect(surface, YELLOW, (menu_x, menu_y, menu_width, menu_height), 3) - - # Title - title = self.title_font.render("Buy Seeds", True, YELLOW) - surface.blit(title, (menu_x + 20, menu_y + 10)) - - # Money display - money = self.font.render(f"Your Money: ${player_money}", True, WHITE) - surface.blit(money, (menu_x + 20, menu_y + 40)) - - # Items - y_offset = 80 - for item, price in shop_items.items(): - y = menu_y + y_offset - - # Item box - item_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 40) - can_buy = player_money >= price - color = (80, 60, 40) if can_buy else (60, 40, 30) - pygame.draw.rect(surface, color, item_rect) - pygame.draw.rect(surface, WHITE if can_buy else GRAY, item_rect, 2) - - # Item name - name = item.replace("_", " ").title() - name_text = self.font.render(name, True, WHITE) - surface.blit(name_text, (item_rect.x + 10, item_rect.y + 10)) - - # Price - price_text = self.font.render(f"${price}", True, YELLOW if can_buy else GRAY) - surface.blit(price_text, (item_rect.right - 80, item_rect.y + 10)) - - y_offset += 50 - - # Back button - back_rect = pygame.Rect(menu_x + 40, menu_y + menu_height - 70, 100, 35) - pygame.draw.rect(surface, (100, 100, 100), back_rect) - pygame.draw.rect(surface, WHITE, back_rect, 2) - back_text = self.font.render("BACK", True, WHITE) - back_x = back_rect.centerx - back_text.get_width() // 2 - back_y = back_rect.centery - back_text.get_height() // 2 - surface.blit(back_text, (back_x, back_y)) - - # Instructions - instructions = self.font.render("Click item to buy", True, WHITE) - surface.blit(instructions, (menu_x + 20, menu_y + menu_height - 30)) - - def draw_sell_menu(self, surface, inventory, player_money, screen_width, screen_height): - """Draw the sell crops menu""" - if self.shop_mode != 'sell': - return - - # Get sellable items from inventory - sellable_items = self.get_sellable_items(inventory) - - menu_width = 450 - menu_height = 450 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - # Background - bg = pygame.Surface((menu_width, menu_height)) - bg.set_alpha(240) - bg.fill((60, 40, 20)) - surface.blit(bg, (menu_x, menu_y)) - - # Border - pygame.draw.rect(surface, YELLOW, (menu_x, menu_y, menu_width, menu_height), 3) - - # Title - title = self.title_font.render("Sell Crops & Products", True, YELLOW) - surface.blit(title, (menu_x + 20, menu_y + 10)) - - # Money display - money = self.font.render(f"Your Money: ${player_money}", True, WHITE) - surface.blit(money, (menu_x + 20, menu_y + 40)) - - # Items - if not sellable_items: - no_items = self.font.render("You don't have any crops or products to sell!", True, WHITE) - surface.blit(no_items, (menu_x + 40, menu_y + 100)) - else: - y_offset = 80 - for item, (count, price) in sellable_items.items(): - y = menu_y + y_offset - - # Item box - item_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 40) - pygame.draw.rect(surface, (80, 60, 40), item_rect) - pygame.draw.rect(surface, WHITE, item_rect, 2) - - # Item name and count - name = item.replace("_", " ").title() - name_text = self.font.render(f"{name} x{count}", True, WHITE) - surface.blit(name_text, (item_rect.x + 10, item_rect.y + 10)) - - # Price - price_text = self.font.render(f"${price} each", True, YELLOW) - surface.blit(price_text, (item_rect.right - 100, item_rect.y + 10)) - - y_offset += 50 - - # Sell All button - if sellable_items: - sell_all_rect = pygame.Rect(menu_x + menu_width - 150, menu_y + menu_height - 70, 110, 35) - pygame.draw.rect(surface, (80, 120, 80), sell_all_rect) - pygame.draw.rect(surface, WHITE, sell_all_rect, 2) - sell_all_text = self.font.render("SELL ALL", True, WHITE) - sell_all_x = sell_all_rect.centerx - sell_all_text.get_width() // 2 - sell_all_y = sell_all_rect.centery - sell_all_text.get_height() // 2 - surface.blit(sell_all_text, (sell_all_x, sell_all_y)) - - # Back button - back_rect = pygame.Rect(menu_x + 40, menu_y + menu_height - 70, 100, 35) - pygame.draw.rect(surface, (100, 100, 100), back_rect) - pygame.draw.rect(surface, WHITE, back_rect, 2) - back_text = self.font.render("BACK", True, WHITE) - back_x = back_rect.centerx - back_text.get_width() // 2 - back_y = back_rect.centery - back_text.get_height() // 2 - surface.blit(back_text, (back_x, back_y)) - - # Instructions - instructions = self.font.render("Click item to sell one, or SELL ALL", True, WHITE) - surface.blit(instructions, (menu_x + 20, menu_y + menu_height - 30)) - - def get_shop_items(self): - """Return shop inventory""" - if self.data["shop"]: - return { - "wheat_seed": 50, - "carrot_seed": 125, - "tomato_seed": 150, - "corn_seed": 200, - } - return None - - def get_sellable_items(self, inventory): - """Get items that can be sold with their prices""" - sellable = {} - - # Crops with sell prices from Crop.CROP_DATA - for crop_name, crop_data in Crop.CROP_DATA.items(): - if inventory.has_item(crop_name): - count = inventory.items.get(crop_name, 0) - if count > 0: - sellable[crop_name] = (count, crop_data["sell_price"]) - - # Animal products - animal_products = { - "egg": 15, - "milk": 25, - "wool": 30 - } - - for product, price in animal_products.items(): - if inventory.has_item(product): - count = inventory.items.get(product, 0) - if count > 0: - sellable[product] = (count, price) - - return sellable - - def handle_shop_click(self, pos, player, inventory, screen_width, screen_height): - """Handle clicks in shop interface - returns (action, result, message)""" - menu_width = 400 - menu_x = (screen_width - menu_width) // 2 - - # Handle menu selection - if self.shop_mode == 'menu': - menu_y = (screen_height - 280) // 2 - buy_rect = pygame.Rect(menu_x + 40, menu_y + 100, menu_width - 80, 50) - sell_rect = pygame.Rect(menu_x + 40, menu_y + 165, menu_width - 80, 50) - - if buy_rect.collidepoint(pos): - self.shop_mode = 'buy' - return ('switch', True, "") - elif sell_rect.collidepoint(pos): - self.shop_mode = 'sell' - return ('switch', True, "") - - # Handle buy menu - elif self.shop_mode == 'buy': - menu_y = (screen_height - 380) // 2 - shop_items = self.get_shop_items() - - # Check back button - back_rect = pygame.Rect(menu_x + 40, menu_y + 380 - 70, 100, 35) - if back_rect.collidepoint(pos): - self.shop_mode = 'menu' - return ('switch', True, "") - - # Check item purchases - y_offset = 80 - for item, price in shop_items.items(): - y = menu_y + y_offset - item_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 40) - - if item_rect.collidepoint(pos): - if player.money >= price: - player.money -= price - inventory.add_item(item, 1) - return ('buy', True, f"Bought {item.replace('_', ' ')}!") - else: - return ('buy', False, "Not enough money!") - - y_offset += 50 - - # Handle sell menu - elif self.shop_mode == 'sell': - menu_width = 450 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - 450) // 2 - sellable_items = self.get_sellable_items(inventory) - - # Check back button - back_rect = pygame.Rect(menu_x + 40, menu_y + 450 - 70, 100, 35) - if back_rect.collidepoint(pos): - self.shop_mode = 'menu' - return ('switch', True, "") - - # Check sell all button - if sellable_items: - sell_all_rect = pygame.Rect(menu_x + menu_width - 150, menu_y + 450 - 70, 110, 35) - if sell_all_rect.collidepoint(pos): - total_value = 0 - items_sold = [] - for item, (count, price) in sellable_items.items(): - value = count * price - total_value += value - inventory.remove_item(item, count) - items_sold.append(f"{count} {item}") - - player.money += total_value - return ('sell', True, f"Sold all items for ${total_value}!") - - # Check individual item sales - y_offset = 80 - for item, (count, price) in sellable_items.items(): - y = menu_y + y_offset - item_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 40) - - if item_rect.collidepoint(pos): - inventory.remove_item(item, 1) - player.money += price - return ('sell', True, f"Sold {item.replace('_', ' ')} for ${price}!") - - y_offset += 50 - - return ('none', False, "") \ No newline at end of file diff --git a/objects.txt b/objects.txt deleted file mode 100644 index e69de29..0000000 diff --git a/player.py b/player.py deleted file mode 100644 index 015f99f..0000000 --- a/player.py +++ /dev/null @@ -1,136 +0,0 @@ -import pygame -import os -from settings import * - -def import_sheet(path, frame_width, frame_height, scale=2): - """Slices and resizes frames from the sprite sheet.""" - try: - surface = pygame.image.load(path).convert_alpha() - except pygame.error: - print(f"Error: Could not find {path}") - return None - - directions = {0: 'down', 1: 'up', 2: 'right'} - animation_data = {'down': [], 'up': [], 'right': [], 'left': []} - - for row in range(3): - for col in range(surface.get_width() // frame_width): - x = col * frame_width - y = row * frame_height - - frame_surf = pygame.Surface((frame_width, frame_height), pygame.SRCALPHA) - frame_surf.blit(surface, (0, 0), pygame.Rect(x, y, frame_width, frame_height)) - - # Scale up for visibility (64x64) - scaled_size = (frame_width * scale, frame_height * scale) - frame_surf = pygame.transform.scale(frame_surf, scaled_size) - - dir_name = directions[row] - animation_data[dir_name].append(frame_surf) - - if dir_name == 'right': - left_frame = pygame.transform.flip(frame_surf, True, False) - animation_data['left'].append(left_frame) - - return animation_data - -class Player(pygame.sprite.Sprite): - def __init__(self, pos): - super().__init__() - - asset_folder = "assets\Character" - - # Load both animation sets - self.idle_frames = import_sheet(os.path.join(asset_folder, 'Idle.png'), 32, 32, 2) - self.walk_frames = import_sheet(os.path.join(asset_folder, 'Walk.png'), 32, 32, 2) - - # State and Direction - self.status = 'idle' - self.facing = 'down' - self.frame_index = 0 - self.animation_speed = 10 # Increased speed for better walking feel - - # Image and Rect - self.image = self.idle_frames[self.facing][self.frame_index] - self.rect = self.image.get_rect(center=pos) - self.hitbox = self.rect.inflate(-20, -20) - - # Attributes (Restored for UI compatibility) - self.speed = 3 - self.current_tool = "hand" - self.money = INITIAL_MONEY - self.energy = 100 - self.max_energy = 100 - - def animate(self, dt): - # Determine which sheet to use - if self.status == 'walk': - current_animation = self.walk_frames[self.facing] - else: - current_animation = self.idle_frames[self.facing] - - # Cycle frames - self.frame_index += self.animation_speed * dt - if self.frame_index >= len(current_animation): - self.frame_index = 0 - - # Update current image - self.image = current_animation[int(self.frame_index)] - - def update(self, keys, dt): - dx, dy = 0, 0 - - # Movement and Facing logic - if keys[pygame.K_w] or keys[pygame.K_UP]: - dy -= self.speed - self.facing = 'up' - elif keys[pygame.K_s] or keys[pygame.K_DOWN]: - dy += self.speed - self.facing = 'down' - - if keys[pygame.K_a] or keys[pygame.K_LEFT]: - dx -= self.speed - self.facing = 'left' - elif keys[pygame.K_d] or keys[pygame.K_RIGHT]: - dx += self.speed - self.facing = 'right' - - # Update status: MUST be walk if dx or dy is not 0 - if dx != 0 or dy != 0: - self.status = 'walk' - else: - self.status = 'idle' - - # Apply movement - if dx != 0 and dy != 0: - dx *= 0.7071 - dy *= 0.7071 - - self.rect.x += dx - self.rect.y += dy - self.hitbox.center = self.rect.center - - # Animate! - self.animate(dt) - - self.rect.clamp_ip(pygame.Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) - - # Energy regeneration - if self.energy < self.max_energy: - self.energy += 0.03 * dt - - - # Helper methods for your UI and mechanics - def use_energy(self, amount): - if self.energy >= amount: - self.energy -= amount - return True - return False - - def change_tool(self): - tools = ["hand", "hoe", "watering_can", "axe", "scythe"] - current_index = tools.index(self.current_tool) - self.current_tool = tools[(current_index + 1) % len(tools)] - - def get_tool_position(self): - return self.rect.center \ No newline at end of file diff --git a/plot_system.py b/plot_system.py deleted file mode 100644 index 7fe6171..0000000 --- a/plot_system.py +++ /dev/null @@ -1,265 +0,0 @@ -# plot_system.py -import pygame -from settings import * - -class PlotSystem: - """Manages farmable plot claiming, selling, and locking""" - - def __init__(self): - self.claimed_plots = set() # Set of (grid_x, grid_y) tuples - self.locked_plots = set() # Set of locked plots that can't be sold - self.claim_cost = 50 # Cost to claim a plot - self.sell_value = int(self.claim_cost * 0.8) # 80% refund - self.font = pygame.font.Font(None, 18) - self.small_font = pygame.font.Font(None, 14) - - def is_claimed(self, grid_pos): - """Check if a plot is claimed""" - return grid_pos in self.claimed_plots - - def is_locked(self, grid_pos): - """Check if a plot is locked""" - return grid_pos in self.locked_plots - - def can_claim(self, grid_pos, world, player): - """Check if a position can be claimed - ONLY with hoe equipped""" - # Must have hoe equipped to claim plots - if player.current_tool != "hoe": - return False - - tile = world.tile_map.get(grid_pos) - if not tile: - return False - - # Only grass tiles can be claimed - return tile.kind == "G" and grid_pos not in self.claimed_plots - - def can_sell(self, grid_pos, world): - """Check if a plot can be sold""" - if grid_pos not in self.claimed_plots: - return False, "Plot not claimed!" - - if grid_pos in self.locked_plots: - return False, "Plot is locked! Unlock first (L key)" - - # Check if there's a crop on this plot - tile_pos = (grid_pos[0] * TILE_SIZE, grid_pos[1] * TILE_SIZE) - for crop in world.crops: - if crop.rect.topleft == tile_pos: - return False, "Remove crops first!" - - # Check if tile is tilled - tile = world.tile_map.get(grid_pos) - if tile and tile.kind == "S": - return False, "Tile is tilled! Can't sell." - - return True, "" - - def claim_plot(self, grid_pos, player, world): - """Attempt to claim a plot - ONLY with hoe equipped""" - # Check if hoe is equipped first - if player.current_tool != "hoe": - return False, "Must have HOE equipped to claim plots! (Press T)" - - if not self.can_claim(grid_pos, world, player): - return False, "Cannot claim this tile!" - - if player.money < self.claim_cost: - return False, f"Need ${self.claim_cost} to claim plot!" - - # Deduct money and claim plot - player.money -= self.claim_cost - self.claimed_plots.add(grid_pos) - return True, f"Plot claimed! (-${self.claim_cost})" - - def sell_plot(self, grid_pos, player, world): - """Sell a claimed plot for 80% of claim cost""" - can_sell, message = self.can_sell(grid_pos, world) - - if not can_sell: - return False, message - - # Remove from claimed plots and give money back - self.claimed_plots.remove(grid_pos) - - # Also remove from locked plots if it was locked - if grid_pos in self.locked_plots: - self.locked_plots.remove(grid_pos) - - player.money += self.sell_value - return True, f"Plot sold! (+${self.sell_value})" - - def toggle_lock(self, grid_pos): - """Toggle lock status of a plot""" - if grid_pos not in self.claimed_plots: - return False, "Plot not claimed!" - - if grid_pos in self.locked_plots: - self.locked_plots.remove(grid_pos) - return True, "Plot unlocked!" - else: - self.locked_plots.add(grid_pos) - return True, "Plot locked!" - - def get_connected_plots(self): - """Group claimed plots into connected regions for outline drawing""" - if not self.claimed_plots: - return [] - - visited = set() - regions = [] - - for plot in self.claimed_plots: - if plot in visited: - continue - - # BFS to find connected region - region = set() - queue = [plot] - - while queue: - current = queue.pop(0) - if current in visited: - continue - - visited.add(current) - region.add(current) - - # Check 4 neighbors - x, y = current - neighbors = [(x+1, y), (x-1, y), (x, y+1), (x, y-1)] - - for neighbor in neighbors: - if neighbor in self.claimed_plots and neighbor not in visited: - queue.append(neighbor) - - regions.append(region) - - return regions - - def draw_claimed_indicators(self, surface): - """Draw visual indicators for claimed plots with connected outlines""" - regions = self.get_connected_plots() - - for region in regions: - # Draw outline for each connected region - outline_color = (255, 215, 0) # Gold - - for grid_x, grid_y in region: - x = grid_x * TILE_SIZE - y = grid_y * TILE_SIZE - - # Draw lock icon if plot is locked - if (grid_x, grid_y) in self.locked_plots: - lock_surface = pygame.Surface((12, 12), pygame.SRCALPHA) - # Lock body - pygame.draw.rect(lock_surface, (255, 215, 0), (2, 5, 8, 6)) - # Lock shackle - pygame.draw.arc(lock_surface, (255, 215, 0), (3, 1, 6, 6), 0, 3.14159, 2) - # Draw lock in corner of tile - surface.blit(lock_surface, (x + 2, y + 2)) - - # Check each edge to see if it should be drawn - # Top edge - if (grid_x, grid_y - 1) not in region: - pygame.draw.line(surface, outline_color, - (x, y), (x + TILE_SIZE, y), 2) - - # Bottom edge - if (grid_x, grid_y + 1) not in region: - pygame.draw.line(surface, outline_color, - (x, y + TILE_SIZE), (x + TILE_SIZE, y + TILE_SIZE), 2) - - # Left edge - if (grid_x - 1, grid_y) not in region: - pygame.draw.line(surface, outline_color, - (x, y), (x, y + TILE_SIZE), 2) - - # Right edge - if (grid_x + 1, grid_y) not in region: - pygame.draw.line(surface, outline_color, - (x + TILE_SIZE, y), (x + TILE_SIZE, y + TILE_SIZE), 2) - - def draw_claimable_hint(self, surface, mouse_pos, world, player): - """Draw hint when hovering over claimable plot - ONLY when hoe is equipped""" - grid_x = mouse_pos[0] // TILE_SIZE - grid_y = mouse_pos[1] // TILE_SIZE - grid_pos = (grid_x, grid_y) - - x = grid_x * TILE_SIZE - y = grid_y * TILE_SIZE - - # Check if plot is claimed and can be sold - if grid_pos in self.claimed_plots: - can_sell, sell_message = self.can_sell(grid_pos, world) - is_locked = grid_pos in self.locked_plots - - # Draw highlight - highlight = pygame.Surface((TILE_SIZE, TILE_SIZE)) - highlight.set_alpha(80) - if is_locked: - highlight.fill((255, 165, 0)) # Orange for locked - elif can_sell: - highlight.fill((255, 100, 100)) # Red for sellable - else: - highlight.fill((150, 150, 150)) # Gray for not sellable - surface.blit(highlight, (x, y)) - - # Draw prompt - if can_sell: - text = self.font.render(f"Right-click: Sell (+${self.sell_value}) | L: Lock", True, WHITE) - elif is_locked: - text = self.font.render(f"L: Unlock plot", True, WHITE) - else: - text = self.font.render(sell_message, True, RED) - - # Position above tile - text_x = x + TILE_SIZE // 2 - text.get_width() // 2 - text_y = y - 25 - - # Background - bg = pygame.Surface((text.get_width() + 8, text.get_height() + 4)) - bg.set_alpha(200) - bg.fill((40, 40, 40)) - surface.blit(bg, (text_x - 4, text_y - 2)) - - surface.blit(text, (text_x, text_y)) - - # Check if plot can be claimed (only when hoe equipped) - elif player.current_tool == "hoe" and self.can_claim(grid_pos, world, player): - # Draw highlight - highlight = pygame.Surface((TILE_SIZE, TILE_SIZE)) - highlight.set_alpha(80) - highlight.fill((255, 255, 100)) - surface.blit(highlight, (x, y)) - - # Draw claim prompt - can_afford = player.money >= self.claim_cost - color = WHITE if can_afford else RED - text = self.font.render(f"Right-click: Claim (${self.claim_cost})", True, color) - - # Position above tile - text_x = x + TILE_SIZE // 2 - text.get_width() // 2 - text_y = y - 20 - - # Background - bg = pygame.Surface((text.get_width() + 8, text.get_height() + 4)) - bg.set_alpha(200) - bg.fill((40, 40, 40)) - surface.blit(bg, (text_x - 4, text_y - 2)) - - surface.blit(text, (text_x, text_y)) - - def save_data(self): - """Return data for saving""" - return { - "claimed_plots": list(self.claimed_plots), - "locked_plots": list(self.locked_plots) - } - - def load_data(self, data): - """Load saved data""" - if "claimed_plots" in data: - self.claimed_plots = set(tuple(pos) for pos in data["claimed_plots"]) - if "locked_plots" in data: - self.locked_plots = set(tuple(pos) for pos in data["locked_plots"]) \ No newline at end of file diff --git a/quest_system.py b/quest_system.py deleted file mode 100644 index cb969a0..0000000 --- a/quest_system.py +++ /dev/null @@ -1,533 +0,0 @@ -import pygame -from settings import * - -class Quest: - """Individual quest with objectives and rewards""" - def __init__(self, quest_id, title, description, objectives, rewards, prerequisite=None): - self.id = quest_id - self.title = title - self.description = description - self.objectives = objectives # List of (type, target, amount, current) - self.rewards = rewards # Dict: {"money": amount, "items": {item: count}} - self.prerequisite = prerequisite # Quest ID that must be completed first - self.completed = False - self.claimed = False - - def update_progress(self, obj_type, target, amount=1): - """Update objective progress""" - for i, obj in enumerate(self.objectives): - if obj["type"] == obj_type and obj["target"] == target: - self.objectives[i]["current"] = min( - obj["current"] + amount, - obj["amount"] - ) - - def is_complete(self): - """Check if all objectives are complete""" - for obj in self.objectives: - if obj["current"] < obj["amount"]: - return False - return True - - def get_progress_text(self): - """Get progress text for UI""" - lines = [] - for obj in self.objectives: - current = obj["current"] - target = obj["amount"] - desc = obj["description"] - status = "✓" if current >= target else f"{current}/{target}" - lines.append(f"{status} {desc}") - return lines - -class QuestSystem: - """Manages all quests in the game""" - def __init__(self): - self.quests = {} - self.active_quests = [] - self.completed_quests = [] - self.show_quest_tab = False - self.selected_quest = None - - # Fonts - self.font = pygame.font.Font(None, 20) - self.title_font = pygame.font.Font(None, 28) - self.small_font = pygame.font.Font(None, 16) - - # Initialize quests - self.init_quests() - - # Auto-start first quest - self.start_quest("welcome") - - def init_quests(self): - """Initialize all available quests""" - - # Tutorial/Starter Quests - self.quests["welcome"] = Quest( - "welcome", - "Welcome to the Farm!", - "Learn the basics of farming in Harvest Valley.", - [ - {"type": "till", "target": "soil", "amount": 3, "current": 0, - "description": "Till 3 soil patches"}, - {"type": "plant", "target": "wheat", "amount": 2, "current": 0, - "description": "Plant 2 wheat seeds"}, - ], - {"money": 50, "items": {"wheat_seed": 5}}, - prerequisite=None - ) - - self.quests["first_harvest"] = Quest( - "first_harvest", - "First Harvest", - "Water and harvest your first crops.", - [ - {"type": "water", "target": "crop", "amount": 3, "current": 0, - "description": "Water crops 3 times"}, - {"type": "harvest", "target": "wheat", "amount": 2, "current": 0, - "description": "Harvest 2 wheat"}, - ], - {"money": 100, "items": {"carrot_seed": 3}}, - prerequisite="welcome" - ) - - self.quests["claim_land"] = Quest( - "claim_land", - "Expand Your Farm", - "Claim plots to expand your farming operation.", - [ - {"type": "claim", "target": "plot", "amount": 5, "current": 0, - "description": "Claim 5 plots"}, - ], - {"money": 200, "items": {"wood": 10}}, - prerequisite="first_harvest" - ) - - # Farming Quests - self.quests["diverse_farm"] = Quest( - "diverse_farm", - "Diversify Your Crops", - "Plant different types of crops.", - [ - {"type": "plant", "target": "carrot", "amount": 3, "current": 0, - "description": "Plant 3 carrots"}, - {"type": "plant", "target": "tomato", "amount": 2, "current": 0, - "description": "Plant 2 tomatoes"}, - ], - {"money": 150, "items": {"corn_seed": 2, "tomato_seed": 3}}, - prerequisite="first_harvest" - ) - - self.quests["master_farmer"] = Quest( - "master_farmer", - "Master Farmer", - "Harvest a variety of crops to become a master farmer.", - [ - {"type": "harvest", "target": "wheat", "amount": 10, "current": 0, - "description": "Harvest 10 wheat"}, - {"type": "harvest", "target": "carrot", "amount": 5, "current": 0, - "description": "Harvest 5 carrots"}, - {"type": "harvest", "target": "tomato", "amount": 5, "current": 0, - "description": "Harvest 5 tomatoes"}, - ], - {"money": 500, "items": {"corn_seed": 5}}, - prerequisite="diverse_farm" - ) - - # Resource Quests - self.quests["lumberjack"] = Quest( - "lumberjack", - "Lumberjack", - "Gather wood for crafting.", - [ - {"type": "collect", "target": "wood", "amount": 20, "current": 0, - "description": "Collect 20 wood"}, - ], - {"money": 100, "items": {}}, - prerequisite=None - ) - - self.quests["crafting_basics"] = Quest( - "crafting_basics", - "Learn to Craft", - "Craft items to improve your farm.", - [ - {"type": "craft", "target": "fence", "amount": 2, "current": 0, - "description": "Craft 2 fences"}, - ], - {"money": 150, "items": {"wood": 15}}, - prerequisite="lumberjack" - ) - - # Animal Quests - self.quests["animal_friend"] = Quest( - "animal_friend", - "Friend of Animals", - "Take care of your animals.", - [ - {"type": "feed", "target": "animal", "amount": 5, "current": 0, - "description": "Feed animals 5 times"}, - {"type": "collect", "target": "egg", "amount": 3, "current": 0, - "description": "Collect 3 eggs"}, - ], - {"money": 200, "items": {}}, - prerequisite="first_harvest" - ) - - # Money Quests - self.quests["entrepreneur"] = Quest( - "entrepreneur", - "Entrepreneur", - "Build up your savings.", - [ - {"type": "money", "target": "total", "amount": 500, "current": 0, - "description": "Earn $500 total"}, - ], - {"money": 250, "items": {"corn_seed": 3}}, - prerequisite="first_harvest" - ) - - self.quests["rich_farmer"] = Quest( - "rich_farmer", - "Rich Farmer", - "Become wealthy through farming.", - [ - {"type": "money", "target": "total", "amount": 2000, "current": 0, - "description": "Earn $2000 total"}, - ], - {"money": 1000, "items": {}}, - prerequisite="entrepreneur" - ) - - def start_quest(self, quest_id): - """Start a new quest if prerequisites are met""" - if quest_id in self.quests: - quest = self.quests[quest_id] - - # Check if already active or completed - if quest in self.active_quests or quest in self.completed_quests: - return False - - # Check prerequisite - if quest.prerequisite: - prereq = self.quests[quest.prerequisite] - if prereq not in self.completed_quests or not prereq.claimed: - return False - - self.active_quests.append(quest) - return True - return False - - def update_quest(self, obj_type, target, amount=1): - """Update progress for all active quests""" - for quest in self.active_quests: - quest.update_progress(obj_type, target, amount) - - # Auto-complete if objectives done - if quest.is_complete() and not quest.completed: - quest.completed = True - return quest # Return completed quest for notification - return None - - def claim_reward(self, quest): - """Claim quest rewards""" - if quest.completed and not quest.claimed: - quest.claimed = True - self.active_quests.remove(quest) - self.completed_quests.append(quest) - - # Check for new quests that can be started - self.check_new_quests() - - return quest.rewards - return None - - def check_new_quests(self): - """Check if any new quests can be started""" - for quest_id, quest in self.quests.items(): - if quest not in self.active_quests and quest not in self.completed_quests: - # Check if prerequisite is met - if quest.prerequisite: - prereq = self.quests[quest.prerequisite] - if prereq.claimed: - self.start_quest(quest_id) - else: - self.start_quest(quest_id) - - def toggle_quest_tab(self): - """Toggle quest UI""" - self.show_quest_tab = not self.show_quest_tab - - def handle_click(self, pos, screen_width, screen_height, player, inventory): - """Handle clicks on quest UI""" - if not self.show_quest_tab: - return None - - panel_width = min(700, screen_width - 40) - panel_height = min(600, screen_height - 40) - panel_x = (screen_width - panel_width) // 2 - panel_y = (screen_height - panel_height) // 2 - - # Check quest list clicks - list_width = 280 - list_x = panel_x + 20 - list_y = panel_y + 70 - - y_offset = 0 - for quest in self.active_quests: - quest_rect = pygame.Rect(list_x, list_y + y_offset, list_width, 60) - if quest_rect.collidepoint(pos): - self.selected_quest = quest - return None - y_offset += 70 - - # Check claim button - if self.selected_quest and self.selected_quest.completed: - button_x = panel_x + panel_width - 140 - button_y = panel_y + panel_height - 70 - claim_rect = pygame.Rect(button_x, button_y, 120, 40) - - if claim_rect.collidepoint(pos): - rewards = self.claim_reward(self.selected_quest) - if rewards: - # Give rewards - if "money" in rewards: - player.money += rewards["money"] - if "items" in rewards: - for item, count in rewards["items"].items(): - inventory.add_item(item, count) - - self.selected_quest = None - return f"Quest completed! Rewards claimed!" - - return None - - def draw_quest_notification(self, quest, surface, screen_width, screen_height): - """Draw quest completion notification""" - if quest and quest.completed and not quest.claimed: - # Background - notif_width = 350 - notif_height = 80 - notif_x = (screen_width - notif_width) // 2 - notif_y = 100 - - bg = pygame.Surface((notif_width, notif_height)) - bg.set_alpha(240) - bg.fill((40, 80, 40)) - surface.blit(bg, (notif_x, notif_y)) - - # Border with glow effect - pygame.draw.rect(surface, (100, 255, 100), - (notif_x, notif_y, notif_width, notif_height), 3) - - # Title - title = self.title_font.render("Quest Complete!", True, (255, 255, 100)) - surface.blit(title, (notif_x + 20, notif_y + 15)) - - # Quest name - name = self.font.render(quest.title, True, WHITE) - surface.blit(name, (notif_x + 20, notif_y + 45)) - - def draw_quest_tab(self, surface, screen_width, screen_height): - """Draw quest tab UI""" - if not self.show_quest_tab: - return - - # Semi-transparent overlay - overlay = pygame.Surface((screen_width, screen_height)) - overlay.set_alpha(180) - overlay.fill((0, 0, 0)) - surface.blit(overlay, (0, 0)) - - # Main panel - panel_width = min(700, screen_width - 40) - panel_height = min(600, screen_height - 40) - panel_x = (screen_width - panel_width) // 2 - panel_y = (screen_height - panel_height) // 2 - - bg = pygame.Surface((panel_width, panel_height)) - bg.fill((40, 40, 40)) - surface.blit(bg, (panel_x, panel_y)) - - pygame.draw.rect(surface, (255, 215, 0), - (panel_x, panel_y, panel_width, panel_height), 3) - - # Title - title = self.title_font.render("Quest Log", True, (255, 215, 0)) - surface.blit(title, (panel_x + 20, panel_y + 15)) - - # Close hint - close_text = self.font.render("Press Q to close", True, GRAY) - surface.blit(close_text, (panel_x + panel_width - 150, panel_y + 15)) - - # Stats - stats_text = self.small_font.render( - f"Active: {len(self.active_quests)} | Completed: {len(self.completed_quests)}", - True, WHITE - ) - surface.blit(stats_text, (panel_x + 20, panel_y + 45)) - - # Divider line - pygame.draw.line(surface, GRAY, - (panel_x + 20, panel_y + 65), - (panel_x + panel_width - 20, panel_y + 65), 2) - - # Quest list (left side) - list_width = 280 - list_x = panel_x + 20 - list_y = panel_y + 70 - - list_label = self.font.render("Active Quests", True, YELLOW) - surface.blit(list_label, (list_x, list_y)) - - # Draw quest items - y_offset = 30 - for quest in self.active_quests: - quest_y = list_y + y_offset - is_selected = quest == self.selected_quest - - # Quest box - quest_rect = pygame.Rect(list_x, quest_y, list_width, 60) - color = (80, 80, 80) if is_selected else (60, 60, 60) - if quest.completed: - color = (80, 120, 80) if is_selected else (60, 100, 60) - - pygame.draw.rect(surface, color, quest_rect) - pygame.draw.rect(surface, YELLOW if is_selected else GRAY, quest_rect, 2) - - # Quest title - title_text = self.font.render(quest.title, True, WHITE) - surface.blit(title_text, (quest_rect.x + 10, quest_rect.y + 8)) - - # Completion status - if quest.completed: - status = self.small_font.render("✓ Complete!", True, (100, 255, 100)) - else: - completed = sum(1 for obj in quest.objectives if obj["current"] >= obj["amount"]) - total = len(quest.objectives) - status = self.small_font.render( - f"Progress: {completed}/{total}", - True, GRAY - ) - surface.blit(status, (quest_rect.x + 10, quest_rect.y + 35)) - - y_offset += 70 - - # No active quests message - if not self.active_quests: - no_quests = self.font.render("No active quests", True, GRAY) - surface.blit(no_quests, (list_x + 60, list_y + 50)) - - # Vertical divider - divider_x = panel_x + 320 - pygame.draw.line(surface, GRAY, - (divider_x, panel_y + 70), - (divider_x, panel_y + panel_height - 20), 2) - - # Quest details (right side) - detail_x = divider_x + 20 - detail_y = panel_y + 70 - - if self.selected_quest: - quest = self.selected_quest - - # Title - detail_title = self.title_font.render(quest.title, True, YELLOW) - surface.blit(detail_title, (detail_x, detail_y)) - - # Description - desc_y = detail_y + 40 - desc_text = self.font.render(quest.description, True, WHITE) - surface.blit(desc_text, (detail_x, desc_y)) - - # Objectives - obj_y = desc_y + 40 - obj_label = self.font.render("Objectives:", True, YELLOW) - surface.blit(obj_label, (detail_x, obj_y)) - - obj_y += 30 - for line in quest.get_progress_text(): - obj_text = self.small_font.render(line, True, WHITE) - surface.blit(obj_text, (detail_x + 10, obj_y)) - obj_y += 25 - - # Rewards - reward_y = obj_y + 20 - reward_label = self.font.render("Rewards:", True, YELLOW) - surface.blit(reward_label, (detail_x, reward_y)) - - reward_y += 30 - if "money" in quest.rewards: - money_text = self.small_font.render( - f"💰 ${quest.rewards['money']}", - True, (255, 215, 0) - ) - surface.blit(money_text, (detail_x + 10, reward_y)) - reward_y += 25 - - if "items" in quest.rewards: - for item, count in quest.rewards["items"].items(): - item_name = item.replace("_", " ").title() - item_text = self.small_font.render( - f"📦 {count}x {item_name}", - True, WHITE - ) - surface.blit(item_text, (detail_x + 10, reward_y)) - reward_y += 25 - - # Claim button - if quest.completed and not quest.claimed: - button_x = panel_x + panel_width - 140 - button_y = panel_y + panel_height - 70 - claim_rect = pygame.Rect(button_x, button_y, 120, 40) - - pygame.draw.rect(surface, (80, 150, 80), claim_rect) - pygame.draw.rect(surface, (100, 255, 100), claim_rect, 3) - - claim_text = self.font.render("CLAIM", True, WHITE) - claim_x = claim_rect.centerx - claim_text.get_width() // 2 - claim_y = claim_rect.centery - claim_text.get_height() // 2 - surface.blit(claim_text, (claim_x, claim_y)) - else: - # No quest selected - no_select = self.font.render("Select a quest to view details", True, GRAY) - surface.blit(no_select, (detail_x, detail_y + 50)) - - def save_data(self): - """Return data for saving""" - return { - "active": [q.id for q in self.active_quests], - "completed": [q.id for q in self.completed_quests], - "progress": { - quest.id: [ - {"current": obj["current"]} for obj in quest.objectives - ] - for quest in self.active_quests - } - } - - def load_data(self, data): - """Load saved data""" - if "active" in data: - self.active_quests = [] - for quest_id in data["active"]: - if quest_id in self.quests: - self.active_quests.append(self.quests[quest_id]) - - if "completed" in data: - self.completed_quests = [] - for quest_id in data["completed"]: - if quest_id in self.quests: - quest = self.quests[quest_id] - quest.completed = True - quest.claimed = True - self.completed_quests.append(quest) - - if "progress" in data: - for quest_id, progress_list in data["progress"].items(): - if quest_id in self.quests: - quest = self.quests[quest_id] - for i, progress in enumerate(progress_list): - if i < len(quest.objectives): - quest.objectives[i]["current"] = progress["current"] \ No newline at end of file diff --git a/save.json b/save.json deleted file mode 100644 index e69de29..0000000 diff --git a/settings.py b/settings.py deleted file mode 100644 index e845ee3..0000000 --- a/settings.py +++ /dev/null @@ -1,41 +0,0 @@ -# Game Settings -TILE_SIZE = 32 -MAP_WIDTH = 30 -MAP_HEIGHT = 17 -DEFAULT_SCREEN_WIDTH = TILE_SIZE * MAP_WIDTH -DEFAULT_SCREEN_HEIGHT = TILE_SIZE * MAP_HEIGHT -MIN_SCREEN_WIDTH = 800 -MIN_SCREEN_HEIGHT = 600 -FPS = 60 - -# Dynamic screen size (will be updated by game) -SCREEN_WIDTH = DEFAULT_SCREEN_WIDTH -SCREEN_HEIGHT = DEFAULT_SCREEN_HEIGHT - -# Colors -WHITE = (255, 255, 255) -BLACK = (0, 0, 0) -GREEN = (34, 139, 34) -BROWN = (139, 69, 19) -BLUE = (64, 164, 223) -DARK_GREEN = (20, 80, 20) -LIGHT_BROWN = (210, 180, 140) -GRAY = (128, 128, 128) -YELLOW = (255, 255, 100) -RED = (200, 50, 50) - -# Game Balance -INITIAL_SEEDS = 10 -INITIAL_MONEY = 100 -CROP_SELL_MULTIPLIER = 2 - -# Time Settings -TIME_SPEED = 0.001 # How fast time passes -NIGHT_START = 18 -NIGHT_END = 6 - -def update_screen_size(width, height): - """Update global screen size variables""" - global SCREEN_WIDTH, SCREEN_HEIGHT - SCREEN_WIDTH = max(MIN_SCREEN_WIDTH, width) - SCREEN_HEIGHT = max(MIN_SCREEN_HEIGHT, height) \ No newline at end of file diff --git a/test_assets.py b/test_assets.py deleted file mode 100644 index 73d1d0b..0000000 --- a/test_assets.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -""" -Asset Loading Test Script -Tests if all sprites are loading correctly from the assets folder. -""" - -import pygame -import sys - -pygame.init() - -# Import asset manager -from asset_manager import asset_manager - -print("\n" + "=" * 60) -print("ASSET LOADING TEST") -print("=" * 60) - -# Test tile sprites -print("\n📦 Testing Tile Sprites:") -tiles = ["G", "S", "W", "P", "T", "F", "R"] -for tile in tiles: - sprite = asset_manager.get_tile_sprite(tile) - if sprite: - print(f" ✓ {tile} tile: {sprite.get_size()}") - else: - print(f" ✗ {tile} tile: Failed to load") - -# Test crop sprites -print("\n🌱 Testing Crop Sprites:") -crops = ["wheat", "carrot", "tomato", "corn"] -for crop in crops: - sprites = asset_manager.get_crop_sprites(crop) - if sprites: - print(f" ✓ {crop}: {len(sprites)} growth stages loaded") - else: - print(f" ⚠ {crop}: Using fallback graphics") - -# Test character sprite -print("\n👤 Testing Character Sprites:") -player = asset_manager.get_character_sprite("player") -if player: - print(f" ✓ Player: {player.get_size()}") -else: - print(f" ⚠ Player: Using fallback graphics") - -# Test animal sprites -print("\n🐓 Testing Animal Sprites:") -animals = ["chicken", "cow"] -for animal in animals: - sprite = asset_manager.get_animal_sprite(animal) - if sprite: - print(f" ✓ {animal.title()}: {sprite.get_size()}") - else: - print(f" ⚠ {animal.title()}: Using fallback graphics") - -# Summary -print("\n" + "=" * 60) -if asset_manager.assets_available: - print("✅ ASSETS LOADED SUCCESSFULLY!") - print(" The game will use real pixel art sprites!") -else: - print("⚠️ NO ASSETS FOUND") - print(" The game will use procedural graphics.") -print("=" * 60) - -print("\n💡 Tip: Run 'python main.py' to start the game!") -print() - -pygame.quit() -sys.exit(0) diff --git a/tile.py b/tile.py deleted file mode 100644 index 4debe14..0000000 --- a/tile.py +++ /dev/null @@ -1,99 +0,0 @@ -import pygame -from settings import * - -class Tile(pygame.sprite.Sprite): - def __init__(self, pos, kind): - super().__init__() - self.kind = kind - self.farmable = False - self.watered = False - - # Create tile graphics - self.image = pygame.Surface((TILE_SIZE, TILE_SIZE)) - - if kind == "G": # Grass - self.image.fill(GREEN) - self.farmable = True - # Add texture - for _ in range(8): - x = pygame.Rect( - pygame.math.Vector2( - pygame.math.Vector2(0, 0).x + (TILE_SIZE // 8) * (_ % 4), - pygame.math.Vector2(0, 0).y + (TILE_SIZE // 4) * (_ // 4) - ), - (2, 4) - ) - pygame.draw.rect(self.image, DARK_GREEN, x) - - elif kind == "S": # Soil/Tilled - self.image.fill(BROWN) - self.farmable = True - self.tilled = True - # Add lines for tilled look - for i in range(4): - pygame.draw.line(self.image, (100, 50, 10), - (0, i * 8), (TILE_SIZE, i * 8), 1) - - elif kind == "W": # Water - self.image.fill(BLUE) - # Add wave effect - pygame.draw.circle(self.image, (100, 180, 255), (8, 8), 4) - pygame.draw.circle(self.image, (100, 180, 255), (24, 20), 3) - - elif kind == "P": # Path - self.image.fill(LIGHT_BROWN) - # Add stones - pygame.draw.circle(self.image, GRAY, (8, 8), 2) - pygame.draw.circle(self.image, GRAY, (24, 20), 2) - pygame.draw.circle(self.image, GRAY, (16, 24), 2) - - elif kind == "T": # Tree - self.image.fill(GREEN) - # Draw tree trunk - pygame.draw.rect(self.image, BROWN, (12, 16, 8, 16)) - # Draw tree canopy - pygame.draw.circle(self.image, DARK_GREEN, (16, 12), 10) - pygame.draw.circle(self.image, (50, 120, 50), (12, 10), 6) - pygame.draw.circle(self.image, (50, 120, 50), (20, 10), 6) - - elif kind == "R": # Rock - self.image.fill(GREEN) - pygame.draw.polygon(self.image, GRAY, - [(16, 8), (26, 20), (16, 28), (6, 20)]) - pygame.draw.polygon(self.image, (100, 100, 100), - [(16, 8), (26, 20), (16, 16)]) - - elif kind == "F": # Fence - self.image.fill(GREEN) - pygame.draw.rect(self.image, BROWN, (2, 12, 28, 4)) - pygame.draw.rect(self.image, BROWN, (2, 20, 28, 4)) - pygame.draw.rect(self.image, BROWN, (8, 8, 4, 20)) - pygame.draw.rect(self.image, BROWN, (20, 8, 4, 20)) - - else: # Default - self.image.fill(BLACK) - - self.rect = self.image.get_rect(topleft=pos) - - def till(self): - """Convert grass to tilled soil""" - if self.kind == "G" and not self.watered: - self.kind = "S" - self.tilled = True - self.image.fill(BROWN) - for i in range(4): - pygame.draw.line(self.image, (100, 50, 10), - (0, i * 8), (TILE_SIZE, i * 8), 1) - return True - return False - - def water(self): - """Water the tile""" - if self.kind == "S" and not self.watered: - self.watered = True - self.image.fill((80, 50, 20)) # Darker, wet soil - for i in range(4): - pygame.draw.line(self.image, (60, 40, 15), - (0, i * 8), (TILE_SIZE, i * 8), 1) - return True - return False \ No newline at end of file diff --git a/time_system.py b/time_system.py deleted file mode 100644 index 14a62e9..0000000 --- a/time_system.py +++ /dev/null @@ -1,68 +0,0 @@ -import pygame -from settings import * - -class TimeSystem: - def __init__(self): - self.time = 6.0 # Start at 6 AM - self.day = 1 - self.season = "Spring" - self.seasons = ["Spring", "Summer", "Fall", "Winter"] - self.days_per_season = 10 - self.time_speed = TIME_SPEED - - def update(self, dt=1): - """Update time""" - self.time += self.time_speed * dt - - if self.time >= 24: - self.time = 0 - self.day += 1 - - # Change season - if self.day % self.days_per_season == 0: - season_index = (self.seasons.index(self.season) + 1) % len(self.seasons) - self.season = self.seasons[season_index] - - def is_night(self): - """Check if it's nighttime""" - return self.time >= NIGHT_START or self.time < NIGHT_END - - def get_time_string(self): - """Get formatted time string""" - hour = int(self.time) - minute = int((self.time - hour) * 60) - - # Convert to 12-hour format - if hour == 0: - hour_12 = 12 - period = "AM" - elif hour < 12: - hour_12 = hour - period = "AM" - elif hour == 12: - hour_12 = 12 - period = "PM" - else: - hour_12 = hour - 12 - period = "PM" - - return f"{hour_12}:{minute:02d} {period}" - - def get_darkness_alpha(self): - """Get darkness overlay alpha based on time""" - if NIGHT_END <= self.time < NIGHT_START: - # Daytime - no darkness - if self.time < 8: - # Dawn - gradually brighten - return int(150 * (1 - (self.time - NIGHT_END) / 2)) - elif self.time > 16: - # Dusk - gradually darken - return int(150 * ((self.time - 16) / 2)) - return 0 - else: - # Nighttime - return 180 - - def get_day_string(self): - """Get day and season string""" - return f"{self.season} {self.day % self.days_per_season + 1}" \ No newline at end of file diff --git a/ui.py b/ui.py deleted file mode 100644 index b4ad4b8..0000000 --- a/ui.py +++ /dev/null @@ -1,208 +0,0 @@ -import pygame -from settings import * - -class UI: - def __init__(self): - self.font = pygame.font.Font(None, 22) - self.title_font = pygame.font.Font(None, 28) - self.small_font = pygame.font.Font(None, 18) - self.tiny_font = pygame.font.Font(None, 14) - - # Settings - self.show_controls = False - - def toggle_controls(self): - """Toggle controls visibility""" - self.show_controls = not self.show_controls - - def draw_player_stats(self, surface, player, time_system): - """Draw player stats in top-left corner""" - # Background - bg = pygame.Surface((250, 120)) - bg.set_alpha(200) - bg.fill((40, 40, 40)) - surface.blit(bg, (10, 10)) - - # Border - pygame.draw.rect(surface, WHITE, (10, 10, 250, 120), 2) - - y_offset = 20 - - # Money - money_text = self.font.render(f"Money: ${player.money}", True, YELLOW) - surface.blit(money_text, (20, y_offset)) - - # Energy bar - y_offset += 30 - energy_text = self.font.render(f"Energy:", True, WHITE) - surface.blit(energy_text, (20, y_offset)) - - bar_width = 150 - bar_height = 16 - bar_x = 100 - bar_y = y_offset + 2 - - # Background bar - pygame.draw.rect(surface, (100, 100, 100), (bar_x, bar_y, bar_width, bar_height)) - # Energy bar - energy_width = int((player.energy / player.max_energy) * bar_width) - energy_color = GREEN if player.energy > 30 else RED - pygame.draw.rect(surface, energy_color, (bar_x, bar_y, energy_width, bar_height)) - # Border - pygame.draw.rect(surface, WHITE, (bar_x, bar_y, bar_width, bar_height), 2) - - # Current tool - y_offset += 30 - tool_text = self.font.render(f"Tool: {player.current_tool.replace('_', ' ').title()}", - True, WHITE) - surface.blit(tool_text, (20, y_offset)) - - # Time and date - y_offset += 30 - time_str = time_system.get_time_string() - day_str = time_system.get_day_string() - time_text = self.font.render(f"{day_str} - {time_str}", True, WHITE) - surface.blit(time_text, (20, y_offset)) - - def draw_controls(self, surface, screen_width, screen_height): - """Draw controls guide""" - if not self.show_controls: - return - - controls = [ - "WASD/Arrows - Move", - "Left Click - Use Tool", - "Right Click - Claim/Sell", - "L - Lock/Unlock Plot", - "1-5 - Select Hotbar", - "T - Change Tool", - "E - Inventory", - "C - Crafting Menu", - "I - Toggle Grid", - "H - Shop Help", - "ESC - Save & Quit" - ] - - # Background - bg_height = len(controls) * 22 + 30 - bg = pygame.Surface((220, bg_height)) - bg.set_alpha(200) - bg.fill((40, 40, 40)) - surface.blit(bg, (screen_width - 230, 10)) - - # Border - pygame.draw.rect(surface, WHITE, (screen_width - 230, 10, 220, bg_height), 2) - - # Title - title = self.title_font.render("Controls", True, WHITE) - surface.blit(title, (screen_width - 220, 20)) - - # Controls list - for i, control in enumerate(controls): - text = self.small_font.render(control, True, WHITE) - surface.blit(text, (screen_width - 220, 50 + i * 22)) - - def draw_settings_button(self, surface, screen_width, screen_height): - """Draw settings button in bottom-right corner""" - button_size = 32 - button_x = screen_width - button_size - 10 - button_y = screen_height - button_size - 10 - - # Button background - button_rect = pygame.Rect(button_x, button_y, button_size, button_size) - pygame.draw.rect(surface, (60, 60, 60), button_rect) - pygame.draw.rect(surface, WHITE, button_rect, 2) - - # Gear icon (simplified) - center_x = button_x + button_size // 2 - center_y = button_y + button_size // 2 - - # Draw gear teeth (8 spokes) - import math - for i in range(8): - angle = i * 45 * math.pi / 180 - x1 = center_x + int(10 * math.cos(angle)) - y1 = center_y + int(10 * math.sin(angle)) - x2 = center_x + int(12 * math.cos(angle)) - y2 = center_y + int(12 * math.sin(angle)) - pygame.draw.line(surface, WHITE, (x1, y1), (x2, y2), 2) - - # Center circle - pygame.draw.circle(surface, (60, 60, 60), (center_x, center_y), 6) - pygame.draw.circle(surface, WHITE, (center_x, center_y), 6, 2) - pygame.draw.circle(surface, (60, 60, 60), (center_x, center_y), 3) - - return button_rect - - def draw_notification(self, surface, message, screen_width, screen_height, duration=120): - """Draw temporary notification""" - if message: - # Background - text_surface = self.font.render(message, True, WHITE) - padding = 20 - width = text_surface.get_width() + padding * 2 - height = text_surface.get_height() + padding * 2 - - x = (screen_width - width) // 2 - y = screen_height - 200 - - bg = pygame.Surface((width, height)) - bg.set_alpha(230) - bg.fill((50, 50, 50)) - surface.blit(bg, (x, y)) - - pygame.draw.rect(surface, YELLOW, (x, y, width, height), 3) - - surface.blit(text_surface, (x + padding, y + padding)) - - def draw_shop(self, surface, shop_items, player_money, screen_width, screen_height): - """Draw shop interface""" - # Background - menu_width = 400 - menu_height = 350 - menu_x = (screen_width - menu_width) // 2 - menu_y = (screen_height - menu_height) // 2 - - bg = pygame.Surface((menu_width, menu_height)) - bg.set_alpha(240) - bg.fill((60, 40, 20)) - surface.blit(bg, (menu_x, menu_y)) - - # Border - pygame.draw.rect(surface, YELLOW, (menu_x, menu_y, menu_width, menu_height), 3) - - # Title - title = self.title_font.render("Shop", True, YELLOW) - surface.blit(title, (menu_x + 20, menu_y + 10)) - - # Money display - money = self.font.render(f"Your Money: ${player_money}", True, WHITE) - surface.blit(money, (menu_x + 20, menu_y + 40)) - - # Items - y_offset = 80 - for item, price in shop_items.items(): - y = menu_y + y_offset - - # Item box - item_rect = pygame.Rect(menu_x + 20, y, menu_width - 40, 40) - can_buy = player_money >= price - color = (80, 60, 40) if can_buy else (60, 40, 30) - pygame.draw.rect(surface, color, item_rect) - pygame.draw.rect(surface, WHITE if can_buy else GRAY, item_rect, 2) - - # Item name - name = item.replace("_", " ").title() - name_text = self.font.render(name, True, WHITE) - surface.blit(name_text, (item_rect.x + 10, item_rect.y + 10)) - - # Price - price_text = self.font.render(f"${price}", True, YELLOW if can_buy else GRAY) - surface.blit(price_text, (item_rect.right - 80, item_rect.y + 10)) - - y_offset += 50 - - # Instructions - instructions = self.small_font.render("Click item to buy | Right-click to close", - True, WHITE) - surface.blit(instructions, (menu_x + 20, menu_y + menu_height - 30)) \ No newline at end of file diff --git a/world.py b/world.py deleted file mode 100644 index aa170e5..0000000 --- a/world.py +++ /dev/null @@ -1,128 +0,0 @@ -import pygame -from tile import Tile -from crop import Crop -from settings import * -import time - -class World: - def __init__(self): - self.tiles = pygame.sprite.Group() - self.crops = pygame.sprite.Group() - self.tile_map = {} # Store tiles by grid position - self.current_time = time.time() - - def load(self, filepath): - """Load map from file""" - try: - with open(filepath) as f: - for y, row in enumerate(f): - for x, tile_type in enumerate(row.strip()): - pos = (x * TILE_SIZE, y * TILE_SIZE) - tile = Tile(pos, tile_type) - self.tiles.add(tile) - self.tile_map[(x, y)] = tile - except FileNotFoundError: - # Create default map if file doesn't exist - self.create_default_map() - - def create_default_map(self): - """Create a default map layout""" - for y in range(MAP_HEIGHT): - for x in range(MAP_WIDTH): - pos = (x * TILE_SIZE, y * TILE_SIZE) - - # Create varied terrain - if y < 3: # Top border - trees - tile_type = "T" if x % 3 == 0 else "G" - elif x < 2 or x > MAP_WIDTH - 3: # Side borders - tile_type = "F" - elif y > MAP_HEIGHT - 3: # Bottom - water - tile_type = "W" - elif 8 <= x <= 12 and 8 <= y <= 12: # Center farm area - tile_type = "S" - elif x % 4 == 0 and y % 4 == 0: # Scattered rocks - tile_type = "R" - elif (x + y) % 8 == 0: # Paths - tile_type = "P" - else: - tile_type = "G" - - tile = Tile(pos, tile_type) - self.tiles.add(tile) - self.tile_map[(x, y)] = tile - - def get_tile_at_pos(self, pixel_pos): - """Get tile at pixel position""" - grid_x = pixel_pos[0] // TILE_SIZE - grid_y = pixel_pos[1] // TILE_SIZE - return self.tile_map.get((grid_x, grid_y)) - - def get_crop_at_pos(self, pixel_pos): - """Get crop at pixel position""" - for crop in self.crops: - if crop.rect.collidepoint(pixel_pos): - return crop - return None - - def till(self, pixel_pos): - """Till soil at position""" - tile = self.get_tile_at_pos(pixel_pos) - if tile: - return tile.till() - return False - - def water(self, pixel_pos): - """Water tile at position""" - tile = self.get_tile_at_pos(pixel_pos) - if tile and tile.water(): - # Also water any crop on this tile - crop = self.get_crop_at_pos(pixel_pos) - if crop: - crop.water() - return True - return False - - def plant(self, pixel_pos, crop_type): - """Plant a seed at position""" - grid_x = pixel_pos[0] // TILE_SIZE - grid_y = pixel_pos[1] // TILE_SIZE - tile = self.tile_map.get((grid_x, grid_y)) - - # Check if tile is farmable and tilled - if tile and tile.kind == "S": - # Check if there's already a crop here - tile_pos = (grid_x * TILE_SIZE, grid_y * TILE_SIZE) - for crop in self.crops: - if crop.rect.topleft == tile_pos: - return False # Already has a crop - - # Plant new crop - crop = Crop(tile_pos, crop_type) - crop.time_planted = self.current_time - self.crops.add(crop) - return True - return False - - def harvest(self, pixel_pos): - """Harvest crop at position""" - crop = self.get_crop_at_pos(pixel_pos) - if crop and crop.ready_to_harvest: - value = crop.harvest() - crop.kill() - return crop.crop_type, value - return None, 0 - - def update(self): - """Update world state""" - self.current_time = time.time() - - # Update all crops - for crop in self.crops: - crop.update(self.current_time) - - def draw_grid(self, surface): - """Draw grid lines for debugging""" - for x in range(0, SCREEN_WIDTH, TILE_SIZE): - pygame.draw.line(surface, (100, 100, 100), (x, 0), (x, SCREEN_HEIGHT), 1) - for y in range(0, SCREEN_HEIGHT, TILE_SIZE): - pygame.draw.line(surface, (100, 100, 100), (0, y), (SCREEN_WIDTH, y), 1) \ No newline at end of file