from __future__ import annotations
import math
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
from ..world.generator import WorldGenerator
from ..world.resource_manager import ResourceManager
from ..world.tile import Tile
if TYPE_CHECKING:
from ..entities.base import Entity
[docs]
class World:
def __init__(self, width: int, height: int, seed: Optional[int] = None):
self.width = width
self.height = height
self.seed = seed
self.tiles: List[List[Tile]] = []
self.entities: Dict[int, Entity] = {}
self.current_tick = 0
self.resource_manager: Optional[ResourceManager] = None
self._initialize_world()
def _initialize_world(self) -> None:
generator = WorldGenerator(seed=self.seed)
self.tiles = generator.generate_world(self.width, self.height)
# Initialize resource manager after world generation
self.resource_manager = ResourceManager(self)
[docs]
def get_tile(self, x: int, y: int) -> Optional[Tile]:
if self.is_valid_position(x, y):
return self.tiles[y][x]
return None
[docs]
def is_valid_position(self, x: int, y: int) -> bool:
return 0 <= x < self.width and 0 <= y < self.height
[docs]
def is_passable(self, x: int, y: int) -> bool:
tile = self.get_tile(x, y)
return tile is not None and tile.can_pass()
[docs]
def get_neighbors(
self, x: int, y: int, diagonal: bool = True
) -> List[Tuple[int, int]]:
neighbors = []
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
if diagonal:
directions.extend([(-1, -1), (-1, 1), (1, -1), (1, 1)])
for dx, dy in directions:
nx, ny = x + dx, y + dy
if self.is_valid_position(nx, ny):
neighbors.append((nx, ny))
return neighbors
[docs]
def get_passable_neighbors(
self, x: int, y: int, diagonal: bool = True
) -> List[Tuple[int, int]]:
neighbors = self.get_neighbors(x, y, diagonal)
return [(nx, ny) for nx, ny in neighbors if self.is_passable(nx, ny)]
[docs]
def get_entities_at(self, x: int, y: int) -> Set[Entity]:
result = set()
for entity in self.entities.values():
if entity.position == (x, y):
result.add(entity)
return result
[docs]
def get_entities_in_range(
self, x: int, y: int, radius: float
) -> List[Tuple[Entity, float]]:
entities_with_distance = []
for entity in self.entities.values():
ex, ey = entity.position
distance = math.sqrt((ex - x) ** 2 + (ey - y) ** 2)
if distance <= radius:
entities_with_distance.append((entity, distance))
entities_with_distance.sort(key=lambda x: x[1])
return entities_with_distance
[docs]
def add_entity(self, entity: Entity) -> None:
self.entities[entity.id] = entity
x, y = entity.position
tile = self.get_tile(x, y)
if tile:
tile.entities.add(entity.id)
[docs]
def remove_entity(self, entity_id: int) -> None:
if entity_id in self.entities:
entity = self.entities[entity_id]
x, y = entity.position
tile = self.get_tile(x, y)
if tile:
tile.entities.discard(entity_id)
del self.entities[entity_id]
[docs]
def move_entity(self, entity: Entity, new_x: int, new_y: int) -> bool:
if not self.is_passable(new_x, new_y):
return False
old_x, old_y = entity.position
old_tile = self.get_tile(old_x, old_y)
new_tile = self.get_tile(new_x, new_y)
if old_tile:
old_tile.entities.discard(entity.id)
if new_tile:
new_tile.entities.add(entity.id)
entity.position = (new_x, new_y)
return True
[docs]
def get_spawn_points(self, zone_name: str) -> List[Tuple[int, int]]:
spawn_points = []
for y in range(self.height):
for x in range(self.width):
tile = self.tiles[y][x]
if tile.is_spawn_zone(zone_name):
spawn_points.append((x, y))
return spawn_points
[docs]
def tick(self) -> None:
self.current_tick += 1
[docs]
def get_terrain_distribution(self) -> Dict[str, float]:
from ..world.terrain import TerrainType
counts = {terrain_type.value: 0 for terrain_type in TerrainType}
total = self.width * self.height
for row in self.tiles:
for tile in row:
counts[tile.terrain_type.value] += 1
return {k: v / total for k, v in counts.items()}