From 4bab8d1a7991725f4519c3e287506bfdfaa0ef61 Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Sat, 27 Jun 2026 19:52:27 +0200 Subject: [PATCH] Scale gas spread, weapon refill, litter size, and post-mating stop with difficulty Four gameplay parameters are now driven by the selected difficulty, following the existing 'hard = harder for the player' philosophy: param easy normal hard gas_spread_speed 40 50 90 (higher = slower gas) weapon_refill_multiplier 1.3 1.0 0.6 (less ammo on hard) max_babies 2 3 5 (more pups on hard) mate_stop_male / female 120/240 100/200 60/120 (shorter stop on hard) Implementation: - engine/config.py: new keys on each DIFFICULTY_OPTIONS entry. - rats.py: _apply_difficulty() loads them onto the game (with __init__ defaults for safety); normal keeps the previous hardcoded values. - units/gas.py: Gas.speed reads game.gas_spread_speed (fallback 50). - engine/unit_manager.py: refill_ammo() scales per-frame refill chances by game.weapon_refill_multiplier (fallback 1.0). - units/rat.py: Male.fuck() uses game.mate_stop_male/mate_stop_female and random.randint(1, game.max_babies) (fallbacks to old values). All reads use getattr with the previous constant as fallback, so older configs / saved state keep working. --- engine/config.py | 19 +++++++++++++++++++ engine/unit_manager.py | 13 +++++++++---- rats.py | 12 ++++++++++++ units/gas.py | 2 +- units/rat.py | 6 +++--- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/engine/config.py b/engine/config.py index 3fdf220..a623f55 100644 --- a/engine/config.py +++ b/engine/config.py @@ -27,6 +27,15 @@ DIFFICULTY_OPTIONS = ( "label": "Easy", "starting_rats_multiplier": 1, "speed_multiplier": 1.0, + # Gas spread tick interval (higher = slower spread = less effective weapon). + "gas_spread_speed": 40, + # Multiplier applied to per-frame weapon refill probabilities. + "weapon_refill_multiplier": 1.3, + # Upper bound for babies per litter (random.randint(1, max_babies)). + "max_babies": 2, + # Stop ticks after mating for the male / the pregnant female. + "mate_stop_male": 120, + "mate_stop_female": 240, "fill": (235, 246, 234), "accent": (88, 148, 82), }, @@ -35,6 +44,11 @@ DIFFICULTY_OPTIONS = ( "label": "Normal", "starting_rats_multiplier": 2, "speed_multiplier": 1.5, + "gas_spread_speed": 50, + "weapon_refill_multiplier": 1.0, + "max_babies": 3, + "mate_stop_male": 100, + "mate_stop_female": 200, "fill": (252, 242, 223), "accent": (204, 146, 44), }, @@ -43,6 +57,11 @@ DIFFICULTY_OPTIONS = ( "label": "Hard", "starting_rats_multiplier": 3, "speed_multiplier": 2.0, + "gas_spread_speed": 90, + "weapon_refill_multiplier": 0.6, + "max_babies": 5, + "mate_stop_male": 60, + "mate_stop_female": 120, "fill": (251, 229, 229), "accent": (188, 68, 68), }, diff --git a/engine/unit_manager.py b/engine/unit_manager.py index 1bcdaf5..46b3591 100644 --- a/engine/unit_manager.py +++ b/engine/unit_manager.py @@ -51,17 +51,22 @@ class UnitManager: return count def refill_ammo(self): - """Randomly refill ammo during gameplay.""" + """Randomly refill ammo during gameplay. + + Per-frame refill probabilities are scaled by the current difficulty's + weapon_refill_multiplier (harder difficulties refill less often). + """ import random + multiplier = getattr(self.game, "weapon_refill_multiplier", 1.0) for ammo_type, data in self.game.ammo.items(): if ammo_type == "bomb": - if random.random() < 0.02: + if random.random() < 0.02 * multiplier: data["count"] = min(data["count"] + 1, data["max"]) elif ammo_type == "mine": - if random.random() < 0.05: + if random.random() < 0.05 * multiplier: data["count"] = min(data["count"] + 1, data["max"]) elif ammo_type == "gas": - if random.random() < 0.01: + if random.random() < 0.01 * multiplier: data["count"] = min(data["count"] + 1, data["max"]) def spawn_gas(self, parent_id=None): diff --git a/rats.py b/rats.py index 21532bc..c66f75b 100644 --- a/rats.py +++ b/rats.py @@ -45,6 +45,12 @@ class MiceMaze: self.difficulty = config.DEFAULT_DIFFICULTY self.initial_rat_count = config.BASE_INITIAL_RATS self.rat_speed_multiplier = 1.0 + # Difficulty-driven gameplay parameters (defaults; overridden by _apply_difficulty). + self.gas_spread_speed = 50 + self.weapon_refill_multiplier = 1.0 + self.max_babies = 3 + self.mate_stop_male = 100 + self.mate_stop_female = 200 self._apply_difficulty(self.profile_integration.get_setting('difficulty', config.DEFAULT_DIFFICULTY), persist=False) self.cell_size = 40 @@ -176,6 +182,12 @@ class MiceMaze: int(round(config.BASE_INITIAL_RATS * difficulty_config["starting_rats_multiplier"])), ) self.rat_speed_multiplier = difficulty_config["speed_multiplier"] + # Difficulty-driven gameplay parameters. + self.gas_spread_speed = difficulty_config.get("gas_spread_speed", 50) + self.weapon_refill_multiplier = difficulty_config.get("weapon_refill_multiplier", 1.0) + self.max_babies = difficulty_config.get("max_babies", 3) + self.mate_stop_male = difficulty_config.get("mate_stop_male", 100) + self.mate_stop_female = difficulty_config.get("mate_stop_female", 200) if persist: self.profile_integration.set_setting("difficulty", normalized_difficulty) diff --git a/units/gas.py b/units/gas.py index 5530cb5..c3d0a26 100644 --- a/units/gas.py +++ b/units/gas.py @@ -13,7 +13,7 @@ class Gas(Unit): super().__init__(game, position, id, collision_layer=CollisionLayer.GAS, unit_type=UnitType.GAS) self.parent_id = parent_id # Specific attributes for gas - self.speed = 50 + self.speed = getattr(game, "gas_spread_speed", 50) self.fight = False self.age = 0 if parent_id: diff --git a/units/rat.py b/units/rat.py index 2f33c9d..8fdea70 100644 --- a/units/rat.py +++ b/units/rat.py @@ -228,10 +228,10 @@ class Male(Rat): def fuck(self, unit): if not unit.pregnant: self.game.render_engine.play_sound("SEX.WAV") - self.stop = 100 - unit.stop = 200 + self.stop = getattr(self.game, "mate_stop_male", 100) + unit.stop = getattr(self.game, "mate_stop_female", 200) unit.pregnant = PREGNANCY_DURATION - unit.babies = random.randint(1, 3) + unit.babies = random.randint(1, getattr(self.game, "max_babies", 3)) class Female(Rat): def __init__(self, game, position=(0,0), id=None):