c7ed24483d
- Introduced `test_final_level_flow.py` to validate final level transitions and game end scenarios. - Created `test_game_over_flow.py` to ensure game over conditions trigger correctly based on rat counts. - Implemented `test_keybindings.py` to verify keybinding configurations and their context-specific actions. - Developed `test_level_editor.py` to assess level editor functionalities and layout computations. - Added `test_level_io.py` for testing level data serialization and deserialization. - Established `test_loop_logic_parity.py` to ensure consistent game state across multiple simulation runs. - Created `test_non_regression.py` to simulate game behavior and capture states for future verification. - Implemented `test_verify.py` to compare current game states against a golden master for regression detection.
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
from abc import ABC, abstractmethod
|
|
import uuid
|
|
from enum import Enum, auto
|
|
|
|
class UnitType(Enum):
|
|
RAT_MALE = auto()
|
|
RAT_FEMALE = auto()
|
|
BOMB_TIMER = auto()
|
|
BOMB_NUCLEAR = auto()
|
|
GAS = auto()
|
|
MINE = auto()
|
|
POINT = auto()
|
|
EXPLOSION = auto()
|
|
|
|
class Unit(ABC):
|
|
"""
|
|
Abstract base class for all game units.
|
|
|
|
Attributes
|
|
----------
|
|
id : UUID
|
|
Unique identifier for the unit.
|
|
game : Game
|
|
Reference to the main game object.
|
|
position : tuple
|
|
The current position of the unit (x, y).
|
|
position_before : tuple
|
|
The position of the unit before the last update.
|
|
age : int
|
|
The age of the unit in game ticks.
|
|
speed : float
|
|
Movement speed of the unit.
|
|
partial_move : float
|
|
Partial movement progress for smooth animation.
|
|
bbox : tuple
|
|
Bounding box for collision detection (x1, y1, x2, y2).
|
|
stop : int
|
|
Number of ticks to remain stationary.
|
|
collision_layer : int
|
|
Collision layer for the optimized collision system.
|
|
type : UnitType
|
|
The specific type of the unit.
|
|
|
|
Methods
|
|
-------
|
|
move()
|
|
Update unit position and state (must be implemented by subclasses).
|
|
draw()
|
|
Render the unit on screen (must be implemented by subclasses).
|
|
collisions()
|
|
Handle collisions with other units (optional override).
|
|
die()
|
|
Remove unit from game and handle cleanup.
|
|
"""
|
|
def __init__(self, game, position=(0, 0), id=None, collision_layer=0, unit_type=None):
|
|
"""Initialize a unit with game reference and position."""
|
|
self.id = id if id else uuid.uuid4()
|
|
self.game = game
|
|
self.position = position
|
|
self.position_before = position
|
|
self.age = 0
|
|
self.speed = 1.0
|
|
self.partial_move = 0
|
|
self.bbox = (0.0, 0.0, 0.0, 0.0) # Ensure it's a tuple of floats
|
|
self.stop = 0
|
|
self.collision_layer = collision_layer
|
|
self.type = unit_type if unit_type else UnitType.POINT # Default type
|
|
|
|
@abstractmethod
|
|
def move(self):
|
|
"""Update unit position and state. Must be implemented by subclasses."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def draw(self):
|
|
"""Render the unit on screen. Must be implemented by subclasses."""
|
|
pass
|
|
|
|
def collisions(self):
|
|
"""Handle collisions with other units. Default implementation does nothing."""
|
|
pass
|
|
|
|
def is_hidden_in_tunnel(self, image_size, position=None):
|
|
"""Return True when the sprite center falls inside a tunnel cell."""
|
|
if position is None:
|
|
position = self.position_before
|
|
|
|
x_pos = position[0] * self.game.cell_size + (self.game.cell_size - image_size[0]) // 2
|
|
y_pos = position[1] * self.game.cell_size + (self.game.cell_size - image_size[1]) // 2
|
|
center_x = int(x_pos + image_size[0] / 2)
|
|
center_y = int(y_pos + image_size[1] / 2)
|
|
cell_x = center_x // self.game.cell_size
|
|
cell_y = center_y // self.game.cell_size
|
|
|
|
if not self.game.map.in_bounds(cell_x, cell_y):
|
|
return False
|
|
return self.game.map.is_tunnel(cell_x, cell_y)
|
|
|
|
def die(self, score=None):
|
|
"""Remove unit from game and handle basic cleanup."""
|
|
if self.id in self.game.units:
|
|
self.game.units.pop(self.id)
|