You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.7 KiB
128 lines
4.7 KiB
import random |
|
import uuid |
|
from units import gas, rat, bomb, mine |
|
|
|
|
|
|
|
class UnitManager: |
|
def _spawnable_rat_positions(self): |
|
positions = [] |
|
for y in range(1, self.map.height - 1): |
|
for x in range(1, self.map.width - 1): |
|
if not self.map.is_empty(x, y): |
|
continue |
|
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: |
|
nx = x + dx |
|
ny = y + dy |
|
if self.map.in_bounds(nx, ny) and self.map.is_empty(nx, ny): |
|
positions.append((x, y)) |
|
break |
|
return positions |
|
|
|
def has_weapon_at(self, position): |
|
"""Check if there's a weapon (bomb, gas, mine) at the given position""" |
|
for unit in self.units.values(): |
|
if unit.position == position: |
|
# Check if it's a weapon type (not a rat or points) |
|
if isinstance(unit, (bomb.Timer, bomb.NuclearBomb, gas.Gas, mine.Mine)): |
|
return True |
|
return False |
|
|
|
def can_place_weapon_at(self, position): |
|
x, y = position |
|
if not self.map.in_bounds(x, y): |
|
return False |
|
if not self.map.is_empty(x, y): |
|
return False |
|
if self.has_weapon_at(position): |
|
return False |
|
return True |
|
|
|
def count_rats(self): |
|
count = 0 |
|
for unit in self.units.values(): |
|
if isinstance(unit, rat.Rat): |
|
count += 1 |
|
return count |
|
|
|
def spawn_gas(self, parent_id=None): |
|
if not self.can_place_weapon_at(self.pointer): |
|
return |
|
if self.ammo["gas"]["count"] <= 0: |
|
return |
|
self.ammo["gas"]["count"] -= 1 |
|
self.render_engine.play_sound("GAS.WAV") |
|
self.spawn_unit(gas.Gas, self.pointer, parent_id=parent_id) |
|
|
|
def spawn_rat(self, position=None): |
|
if position is None: |
|
position = self.choose_start() |
|
if position is None: |
|
print("[flow] spawn_rat aborted: no valid spawn position", flush=True) |
|
return |
|
|
|
print(f"[flow] spawn_rat using position={position}", flush=True) |
|
|
|
# Don't spawn rats on top of weapons |
|
if self.has_weapon_at(position): |
|
# Try nearby positions |
|
for dx, dy in [(0,1), (1,0), (0,-1), (-1,0), (1,1), (-1,-1), (1,-1), (-1,1)]: |
|
alt_pos = (position[0] + dx, position[1] + dy) |
|
if not self.map.in_bounds(alt_pos[0], alt_pos[1]): |
|
continue |
|
if self.map.is_empty(alt_pos[0], alt_pos[1]) and not self.has_weapon_at(alt_pos): |
|
position = alt_pos |
|
break |
|
else: |
|
# All nearby positions blocked, abort spawn |
|
print(f"[flow] spawn_rat aborted: weapon blocks spawn near {position}", flush=True) |
|
return |
|
|
|
rat_class = rat.Male if random.random() < 0.5 else rat.Female |
|
self.spawn_unit(rat_class, position) |
|
|
|
def spawn_bomb(self, position): |
|
if not self.can_place_weapon_at(position): |
|
return |
|
if self.ammo["bomb"]["count"] <= 0: |
|
return |
|
self.render_engine.play_sound("PUTDOWN.WAV") |
|
self.spawn_unit(bomb.Timer, position) |
|
self.ammo["bomb"]["count"] -= 1 |
|
|
|
def spawn_nuclear_bomb(self, position): |
|
"""Spawn a nuclear bomb at the specified position""" |
|
if self.ammo["nuclear"]["count"] <= 0: |
|
return |
|
if not self.can_place_weapon_at(position): |
|
return |
|
self.render_engine.play_sound("NUCLEAR.WAV") |
|
self.ammo["nuclear"]["count"] -= 1 |
|
self.spawn_unit(bomb.NuclearBomb, position) |
|
|
|
def spawn_mine(self, position): |
|
if self.ammo["mine"]["count"] <= 0: |
|
return |
|
if not self.can_place_weapon_at(position): |
|
return |
|
self.render_engine.play_sound("PUTDOWN.WAV") |
|
self.ammo["mine"]["count"] -= 1 |
|
self.spawn_unit(mine.Mine, position, on_bottom=True) |
|
|
|
def spawn_unit(self, unit, position, on_bottom=False, **kwargs): |
|
id = uuid.uuid4() |
|
if on_bottom: |
|
self.units = {id: unit(self, position, id, **kwargs), **self.units} |
|
else: |
|
self.units[id] = unit(self, position, id, **kwargs) |
|
|
|
def choose_start(self): |
|
if not hasattr(self, '_valid_positions') or self._valid_positions is None: |
|
self._valid_positions = self._spawnable_rat_positions() |
|
print(f"[flow] choose_start computed {len(self._valid_positions)} spawnable cells", flush=True) |
|
if not self._valid_positions: |
|
return None |
|
return random.choice(self._valid_positions) |
|
|
|
def get_unit_by_id(self, id): |
|
return self.units.get(id) or None
|
|
|