Source code for game.entities.base_entity

import pygame
import random
import math

[docs] class BaseEntity: """Base class for all entities, handling smooth movement and rendering.""" def __init__(self, x, y, asset, speed=50.0, move_interval=3.0): # Logical grid position
[docs] self.x = x
[docs] self.y = y
# Pixel position for smooth rendering, initialized later
[docs] self.pixel_x = 0
[docs] self.pixel_y = 0
# Movement state
[docs] self.target_pos = None
[docs] self.is_moving = False
[docs] self.speed = speed # Pixels per second
# Decision-making timer
[docs] self.move_timer = random.uniform(0.5, move_interval)
[docs] self.move_interval = move_interval
# Asset
[docs] self.asset = asset
[docs] self.rect = self.asset.get_rect()
[docs] def update(self, dt, isometric_map): """Update entity state, including movement.""" if self.is_moving: self._move_towards_target(dt, isometric_map) else: self._idle_behavior(dt, isometric_map)
def _idle_behavior(self, dt, isometric_map): """Handle behavior when not moving, like deciding where to go next.""" self.move_timer -= dt if self.move_timer <= 0: self._find_new_target(isometric_map) self.move_timer = random.uniform(1.0, self.move_interval) def _find_new_target(self, isometric_map): """Find a valid, walkable, adjacent tile to move to.""" possible_moves = [] # Check N, S, E, W neighbors for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: new_x, new_y = self.x + dx, self.y + dy if isometric_map.is_valid_position(new_x, new_y): possible_moves.append((new_x, new_y)) if possible_moves: self.target_pos = random.choice(possible_moves) self.is_moving = True def _move_towards_target(self, dt, isometric_map): """Interpolate pixel position towards the target tile.""" if not self.target_pos: self.is_moving = False return target_pixel_x, target_pixel_y = isometric_map.world_to_screen(self.target_pos[0], self.target_pos[1]) dx = target_pixel_x - self.pixel_x dy = target_pixel_y - self.pixel_y distance = math.sqrt(dx**2 + dy**2) if distance < self.speed * dt: self.x, self.y = self.target_pos self.pixel_x, self.pixel_y = target_pixel_x, target_pixel_y self.is_moving = False self.target_pos = None else: self.pixel_x += (dx / distance) * self.speed * dt self.pixel_y += (dy / distance) * self.speed * dt def _step_towards_target(self, dt, speed): """Move (self.x, self.y) toward (self.target_x, self.target_y). Returns (dx, dy, distance) measured before the move, or (0, 0, 0) when already at the target. """ dx = self.target_x - self.x dy = self.target_y - self.y distance = math.sqrt(dx * dx + dy * dy) if distance > 0.1: move_speed = speed * dt if distance > move_speed: self.x += (dx / distance) * move_speed self.y += (dy / distance) * move_speed else: self.x = self.target_x self.y = self.target_y return dx, dy, distance return 0, 0, 0
[docs] def get_screen_position(self, isometric_map): """Get screen position for rendering.""" return isometric_map.world_to_screen(self.x, self.y)
[docs] def render(self, screen, isometric_map): """Render the entity on the screen.""" if not self.is_moving: # Snap to grid position if idle self.pixel_x, self.pixel_y = isometric_map.world_to_screen(self.x, self.y) self.rect.center = (self.pixel_x, self.pixel_y) screen.blit(self.asset, self.rect)