import pygame import random import math from settings import * from tiles import get_tile # Perlin noise implementation def perlin_fade(t): """Fade function for Perlin noise""" return t * t * t * (t * (t * 6 - 15) + 10) def perlin_lerp(t, a, b): """Linear interpolation""" return a + t * (b - a) def perlin_noise_1d(x, seed=42): """Simple 1D Perlin-like noise""" xi = int(x) & 0xFF xf = x - int(x) u = perlin_fade(xf) # Generate consistent random values random.seed(seed + xi) a = random.random() random.seed(seed + xi + 1) b = random.random() return perlin_lerp(u, a, b) def generate_noise_map(width, height, scale=50, octaves=4, seed=42): """Generate a noise map using Perlin-like noise""" noise_map = [[0.0 for _ in range(width)] for _ in range(height)] for y in range(height): for x in range(width): value = 0.0 amplitude = 1.0 frequency = 1.0 max_value = 0.0 for i in range(octaves): sample_x = (x / scale) * frequency noise = perlin_noise_1d(sample_x + (y * 73 * frequency), seed + i * 256) value += noise * amplitude max_value += amplitude amplitude *= 0.5 frequency *= 2.0 noise_map[y][x] = value / max_value if max_value > 0 else 0.0 return noise_map class World: def __init__(self): self.width = WORLD_WIDTH self.height = WORLD_HEIGHT # 2D grid: world[y][x] self.grid = [ [AIR for _ in range(self.width)] for _ in range(self.height) ] self.generate_world() # ========================================================== # WORLD GENERATION - FLAT WITH TREES # ========================================================== def generate_world(self): """Generate flat world with trees""" # Create flat surface at SURFACE_LEVEL for x in range(self.width): for y in range(self.height): if y < SURFACE_LEVEL: self.grid[y][x] = AIR elif y == SURFACE_LEVEL: self.grid[y][x] = GRASS elif y < SURFACE_LEVEL + 5: self.grid[y][x] = DIRT else: self.grid[y][x] = STONE # Add trees (more frequently) self.generate_trees() # Add ores self.generate_ores() # ========================================================== # DRAW # ========================================================== def draw(self, screen, camera): # Determine visible tile range start_x = max(0, camera.offset.x // TILE_SIZE) end_x = min(self.width, (camera.offset.x + SCREEN_WIDTH) // TILE_SIZE + 2) start_y = max(0, camera.offset.y // TILE_SIZE) end_y = min(self.height, (camera.offset.y + SCREEN_HEIGHT) // TILE_SIZE + 2) for y in range(int(start_y), int(end_y)): for x in range(int(start_x), int(end_x)): tile_id = self.grid[y][x] if tile_id != AIR: color = get_tile(tile_id).color world_rect = pygame.Rect( x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE ) screen_rect = camera.apply(world_rect) pygame.draw.rect(screen, color, screen_rect) # ========================================================== # COLLISION SUPPORT # ========================================================== def get_nearby_tiles(self, rect): tiles = [] # Determine tile range around player start_x = max(0, rect.left // TILE_SIZE - 1) end_x = min(self.width, rect.right // TILE_SIZE + 2) start_y = max(0, rect.top // TILE_SIZE - 1) end_y = min(self.height, rect.bottom // TILE_SIZE + 2) for y in range(start_y, end_y): for x in range(start_x, end_x): tile_id = self.grid[y][x] if tile_id != AIR: tile_rect = pygame.Rect( x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE ) tiles.append({ "rect": tile_rect, "solid": get_tile(tile_id).collidable, "id": tile_id, "x": x, "y": y }) return tiles # ========================================================== # BLOCK BREAKING # ========================================================== def break_block(self, mouse_pos, camera, inventory, player_rect): """Break block at mouse position if in range""" world_x, world_y = camera.screen_to_world(mouse_pos) tile_x = int(world_x // TILE_SIZE) tile_y = int(world_y // TILE_SIZE) # Check if in range distance = math.sqrt( (tile_x * TILE_SIZE - player_rect.centerx) ** 2 + (tile_y * TILE_SIZE - player_rect.centery) ** 2 ) if distance > BREAK_RANGE * TILE_SIZE: print(f"DEBUG: Block too far away! Distance: {distance}, Max: {BREAK_RANGE * TILE_SIZE}") return False if self.in_bounds(tile_x, tile_y): tile_id = self.grid[tile_y][tile_x] tile = get_tile(tile_id) if tile_id != AIR: if tile.drop: inventory.add_item(tile.drop, 1) print(f"DEBUG: Broke {tile.name}, added {tile.drop} to inventory") self.grid[tile_y][tile_x] = AIR return True return False # ========================================================== # BLOCK PLACING # ========================================================== def place_block(self, mouse_pos, camera, player_rect, block_type=DIRT): """Place block at mouse position if in range""" world_x, world_y = camera.screen_to_world(mouse_pos) tile_x = int(world_x // TILE_SIZE) tile_y = int(world_y // TILE_SIZE) # Check if in range distance = math.sqrt( (tile_x * TILE_SIZE - player_rect.centerx) ** 2 + (tile_y * TILE_SIZE - player_rect.centery) ** 2 ) if distance > PLACE_RANGE * TILE_SIZE: print(f"DEBUG: Block placement too far away!") return False if self.in_bounds(tile_x, tile_y): if self.grid[tile_y][tile_x] == AIR: self.grid[tile_y][tile_x] = block_type print(f"DEBUG: Placed block at {tile_x}, {tile_y}") return True return False # ========================================================== # UTIL # ========================================================== def in_bounds(self, x, y): return 0 <= x < self.width and 0 <= y < self.height # ========================================================== # Get surface height for player spawn # ========================================================== def get_surface_height(self, x): for y in range(self.height): if self.grid[y][x] != AIR: return y return self.height - 1 # ========================================================== # TREE GENERATION - MORE TREES # ========================================================== def generate_trees(self): # Generate trees more frequently (every 4 tiles) for x in range(0, self.width, 4): if random.random() < 0.6: # 60% chance for trees surface_y = SURFACE_LEVEL if 0 <= x < self.width: self.spawn_tree(x, surface_y) def spawn_tree(self, x, surface_y): # trunk height height = random.randint(5, 8) for i in range(height): if surface_y - 1 - i >= 0: self.grid[surface_y - 1 - i][x] = WOOD # leaves - larger canopy leaf_start = surface_y - height - 1 for y in range(leaf_start, leaf_start - 5, -1): for lx in range(x - 3, x + 4): if 0 <= lx < self.width and 0 <= y < self.height: if random.random() > 0.2: # 80% leaf density self.grid[y][lx] = LEAVES # ========================================================== # ORE GENERATION # ========================================================== def generate_ores(self): for _ in range(250): x = random.randint(0, self.width - 1) y = random.randint(SURFACE_LEVEL + 5, self.height - 1) if self.grid[y][x] == STONE: r = random.random() if r < 0.5: self.grid[y][x] = COAL_ORE elif r < 0.75: self.grid[y][x] = COPPER_ORE elif r < 0.9: self.grid[y][x] = IRON_ORE else: self.grid[y][x] = GOLD_ORE if r < 0.5: self.grid[y][x] = COAL_ORE elif r < 0.75: self.grid[y][x] = COPPER_ORE elif r < 0.9: self.grid[y][x] = IRON_ORE else: self.grid[y][x] = GOLD_ORE