Fix gas poisoning, explosion hit detection and render order

- Move gas poisoning from Gas.move() to Gas.collisions() so all rats are
  registered in the collision system before the poison query runs.
- Shrink gas and explosion bboxes so rats must be well inside the tile to
  be poisoned/killed.
- Use AABB overlap instead of partial_move threshold for gas poisoning.
- Draw mobile units first, then top-layer effects (gas, mines, bombs,
  explosions) so rats appear under the gas.
- Add draw_on_top hint to Unit base class and top-layer units.
This commit is contained in:
2026-06-16 19:19:14 +02:00
parent 3e8cd97fda
commit 2f8e3e8b28
7 changed files with 88 additions and 33 deletions
+25 -9
View File
@@ -1,16 +1,19 @@
from .unit import Unit, UnitType
from . import rat
from .points import Point
from engine.collision_system import CollisionLayer
from engine.collision_system import CollisionLayer, shrink_bbox
import uuid
import random
# Costanti
AGE_THRESHOLD = 200
NUCLEAR_TIMER = 50 # 1 second at ~50 FPS
EXPLOSION_BBOX_SHRINK = 0.25 # central 50% of the tile is lethal instantly
class Bomb(Unit):
draw_on_top = True
def __init__(self, game, position=(0,0), id=None, unit_type=UnitType.BOMB_TIMER):
super().__init__(game, position, id, collision_layer=CollisionLayer.BOMB, unit_type=unit_type)
# Specific attributes for bombs
@@ -104,13 +107,24 @@ class Timer(Bomb):
layer_filter=CollisionLayer.RAT
)
cs = self.game.cell_size
for victim_id in victim_ids:
victim = self.game.unit_manager.get_unit_by_id(victim_id)
if victim and victim.id in self.game.units:
if victim.position in explosion_positions or victim.position_before in explosion_positions:
victim.die(score=score)
if score < 160:
score *= 2
# Require the rat to be well inside an explosion tile
for pos in explosion_positions:
if victim.position == pos or victim.position_before == pos:
exp_bbox = shrink_bbox(
(pos[0] * cs, pos[1] * cs, (pos[0] + 1) * cs, (pos[1] + 1) * cs),
EXPLOSION_BBOX_SHRINK,
)
vb = victim.bbox
if (vb[0] < exp_bbox[2] and vb[2] > exp_bbox[0] and
vb[1] < exp_bbox[3] and vb[3] > exp_bbox[1]):
victim.die(score=score)
if score < 160:
score *= 2
break
def draw(self):
"""Don't draw a bomb that has already exploded."""
@@ -130,10 +144,12 @@ class Explosion(Bomb):
self.age += self.speed
if self.age >= AGE_THRESHOLD:
self.die()
# Set bbox so lingering explosions can kill rats via collision system
x = self.position[0] * self.game.cell_size
y = self.position[1] * self.game.cell_size
self.bbox = (float(x), float(y), float(x + self.game.cell_size), float(y + self.game.cell_size))
# Set bbox so lingering explosions can kill rats via collision system.
# Only the central area is lethal.
cs = self.game.cell_size
cell_bbox = (self.position[0] * cs, self.position[1] * cs,
(self.position[0] + 1) * cs, (self.position[1] + 1) * cs)
self.bbox = shrink_bbox(cell_bbox, EXPLOSION_BBOX_SHRINK)
def collisions(self):
"""Lingering explosion kills any rat that touches it."""