from __future__ import annotations import numpy as np from dataclasses import dataclass, field from typing import Optional, Dict, Any, Set, Tuple from enum import Enum class CheatType(Enum): AIMBOT = "aimbot" WALLHACK = "wallhack" TRIGGERBOT = "triggerbot" class TogglePattern(Enum): ALWAYS = "always" CLUTCH_ONLY = "clutch_only" LOSING_ONLY = "losing_only" RANDOM = "random" @dataclass class HumanizationConfig: """Cheater's attempt to appear legit - but leaves artifacts.""" reaction_delay_ms: Tuple[float, float] = (0.0, 20.0) aim_smoothing: Tuple[float, float] = (0.0, 0.2) random_miss_rate: Tuple[float, float] = (0.0, 0.05) fov_degrees: Tuple[float, float] = (90.0, 180.0) noise_amplitude: Tuple[float, float] = (0.0, 0.0) prefire_suppression: float = 0.0 check_delay_ms: Tuple[float, float] = (0.0, 0.0) def sample(self, rng: np.random.Generator) -> Dict[str, float]: """Sample concrete values from ranges.""" return { "reaction_delay_ms": rng.uniform(*self.reaction_delay_ms), "aim_smoothing": rng.uniform(*self.aim_smoothing), "random_miss_rate": rng.uniform(*self.random_miss_rate), "fov_degrees": rng.uniform(*self.fov_degrees), "noise_amplitude": rng.uniform(*self.noise_amplitude), "prefire_suppression": self.prefire_suppression, "check_delay_ms": rng.uniform(*self.check_delay_ms) if self.check_delay_ms[1] > 0 else 0.0, } CHEAT_PROFILES: Dict[str, Dict[str, Any]] = { "blatant_rage": { "intensity": (0.8, 1.0), "toggle_pattern": TogglePattern.ALWAYS, "cheat_types": {CheatType.AIMBOT, CheatType.WALLHACK, CheatType.TRIGGERBOT}, "humanization": HumanizationConfig( reaction_delay_ms=(0.0, 20.0), aim_smoothing=(0.0, 0.2), random_miss_rate=(0.0, 0.05), fov_degrees=(90.0, 180.0), ), "base_skill_multiplier": (0.3, 0.5), }, "obvious": { "intensity": (0.5, 0.8), "toggle_pattern": TogglePattern.ALWAYS, "cheat_types": {CheatType.AIMBOT, CheatType.TRIGGERBOT}, "humanization": HumanizationConfig( reaction_delay_ms=(30.0, 80.0), aim_smoothing=(0.2, 0.5), random_miss_rate=(0.05, 0.10), fov_degrees=(30.0, 60.0), ), "base_skill_multiplier": (0.4, 0.6), }, "closet_moderate": { "intensity": (0.3, 0.5), "toggle_pattern": TogglePattern.CLUTCH_ONLY, "cheat_types": {CheatType.AIMBOT}, "humanization": HumanizationConfig( reaction_delay_ms=(80.0, 150.0), aim_smoothing=(0.5, 0.8), random_miss_rate=(0.10, 0.18), fov_degrees=(10.0, 25.0), ), "base_skill_multiplier": (0.5, 0.7), }, "closet_subtle": { "intensity": (0.15, 0.35), "toggle_pattern": TogglePattern.LOSING_ONLY, "cheat_types": {CheatType.AIMBOT, CheatType.TRIGGERBOT}, "humanization": HumanizationConfig( reaction_delay_ms=(150.0, 250.0), aim_smoothing=(0.8, 0.95), random_miss_rate=(0.15, 0.25), fov_degrees=(3.0, 10.0), noise_amplitude=(0.5, 1.5), ), "base_skill_multiplier": (0.6, 0.8), }, "wallhack_only": { "intensity": (0.4, 0.7), "toggle_pattern": TogglePattern.ALWAYS, "cheat_types": {CheatType.WALLHACK}, "humanization": HumanizationConfig( prefire_suppression=0.7, check_delay_ms=(500.0, 1500.0), ), "base_skill_multiplier": (0.7, 0.9), }, } @dataclass class CheatConfig: """Configuration for a specific cheater.""" profile_name: str cheat_types: Set[CheatType] intensity: float toggle_pattern: TogglePattern humanization: Dict[str, float] base_skill_multiplier: float @classmethod def from_profile( cls, profile_name: str, seed: Optional[int] = None, ) -> CheatConfig: """Create config from predefined profile.""" if profile_name not in CHEAT_PROFILES: raise ValueError(f"Unknown profile: {profile_name}. Valid: {list(CHEAT_PROFILES.keys())}") rng = np.random.default_rng(seed) profile = CHEAT_PROFILES[profile_name] intensity = rng.uniform(*profile["intensity"]) base_skill_mult = rng.uniform(*profile["base_skill_multiplier"]) humanization = profile["humanization"].sample(rng) return cls( profile_name=profile_name, cheat_types=profile["cheat_types"].copy(), intensity=intensity, toggle_pattern=profile["toggle_pattern"], humanization=humanization, base_skill_multiplier=base_skill_mult, ) @dataclass class CheatBehavior: """Runtime cheat behavior with toggle logic.""" config: CheatConfig is_active: bool = True rounds_since_toggle: int = 0 @classmethod def from_profile(cls, profile_name: str, seed: Optional[int] = None) -> CheatBehavior: config = CheatConfig.from_profile(profile_name, seed) return cls(config=config) @property def toggle_pattern(self) -> TogglePattern: return self.config.toggle_pattern def should_activate( self, is_clutch: bool = False, is_losing: bool = False, round_number: int = 0, rng: Optional[np.random.Generator] = None, ) -> bool: """Determine if cheat should be active this round.""" pattern = self.config.toggle_pattern if pattern == TogglePattern.ALWAYS: return True elif pattern == TogglePattern.CLUTCH_ONLY: return is_clutch elif pattern == TogglePattern.LOSING_ONLY: return is_losing elif pattern == TogglePattern.RANDOM: if rng is None: rng = np.random.default_rng() return rng.random() < 0.5 return True def get_aim_modification(self, target_angle: float, current_angle: float) -> float: """Calculate aim correction from cheat.""" if CheatType.AIMBOT not in self.config.cheat_types: return 0.0 if not self.is_active: return 0.0 angle_diff = target_angle - current_angle fov_limit = self.config.humanization["fov_degrees"] if abs(angle_diff) > fov_limit: return 0.0 smoothing = self.config.humanization["aim_smoothing"] intensity = self.config.intensity # Smoothed correction correction = angle_diff * (1.0 - smoothing) * intensity # Add humanization noise noise_amp = self.config.humanization.get("noise_amplitude", 0.0) if noise_amp > 0: correction += np.random.normal(0, noise_amp) return correction def get_reaction_delay(self) -> float: """Get reaction delay in ms.""" return self.config.humanization["reaction_delay_ms"] def should_miss_intentionally(self, rng: Optional[np.random.Generator] = None) -> bool: """Check if should intentionally miss (humanization).""" if rng is None: rng = np.random.default_rng() miss_rate = self.config.humanization["random_miss_rate"] return rng.random() < miss_rate