/
Vibek
/
First_Game
Обзор
Документация
Войти
/
Vibek
/
First_Game
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
controllers/combat_system.py
116 строк
5 KB
Vibek
все перменные перенесены в конфиг, отрисовка динамическая
21 июн 2026, 20:32
21 июн 2026, 20:32
f32a834
Код
Авторство
О чём код?
from pygame.math import Vector2 from models.combat_states import CombatState, CombatResult from utils.collision import sat_collision, check_polygon_overlap, circle_overlaps_obb from models.entity import Entity from core.config import Config class CombatSystem: """ Контроллер боевой системы. Отвечает за: Кулдауны, расчет геометрии удара, проверку состояний. """ def __init__( self, attack_range: float | None = None, attack_angle: float | None = None, cooldown: float | None = None, ): combat = Config.Combat self.attack_range = attack_range if attack_range is not None else combat.SWORD_RANGE self.attack_angle = attack_angle if attack_angle is not None else combat.SWORD_ANGLE self.cooldown_duration = cooldown if cooldown is not None else combat.SWORD_COOLDOWN self.attack_timer = 0.0 self.current_state = CombatState.IDLE self.last_arc_points = [] def update(self, dt: float, is_inputting_attack: bool, facing_direction: Vector2, player_pos: Vector2, angle: float) -> CombatState: """ Обновляет логику боя каждый кадр. :param is_inputting_attack: True если нажат ЛКМ """ if self.attack_timer > 0: self.attack_timer -= dt if self.current_state == CombatState.ATTACKING: self.current_state = CombatState.IDLE elif is_inputting_attack and self.attack_timer <= 0: self.current_state = CombatState.ATTACKING self.attack_timer = self.cooldown_duration self._calculate_arc(player_pos, facing_direction) else: self.current_state = CombatState.IDLE self.last_arc_points = [] return self.current_state def _calculate_arc(self, center: Vector2, direction: Vector2) -> None: """Рассчитывает 3 точки полигона для отрисовки конуса.""" half_angle = self.attack_angle / 2 left_dir = direction.rotate(-half_angle).normalize() right_dir = direction.rotate(half_angle).normalize() p1 = center + left_dir * self.attack_range p2 = center + right_dir * self.attack_range self.last_arc_points = [ Vector2(center.x, center.y), Vector2(p1.x, p1.y), Vector2(p2.x, p2.y) ] def check_hit(self, attack_poly: list[Vector2], target: Entity) -> bool: """Делегирует проверку в чистый модуль коллизий.""" return check_polygon_overlap(attack_poly, target.get_obb_corners()) def resolve_contact(self, attack_poly: list[Vector2], defender: Entity) -> CombatResult: if not attack_poly or len(attack_poly) < 3: return CombatResult.MISSED if not check_polygon_overlap(attack_poly, defender.get_obb_corners()): return CombatResult.MISSED if getattr(defender, 'is_invulnerable', False): return CombatResult.INVULNERABLE if defender.is_in_parry_window(): return CombatResult.PARRIED if defender.is_blocking: if defender.drain_stamina(20.0): return CombatResult.BLOCK_BROKEN return CombatResult.BLOCKED defender.take_damage(1) return CombatResult.DAMAGED def resolve_projectile_hit(self, proj_pos: Vector2, defender: Entity, hit_radius: float = 20.0) -> CombatResult: """ Разрешает контакт снаряда с защитником. Повторяет строгий приоритет: Парри > Блок > Урон. Использует дистанционную проверку вместо SAT (оптимизация для точек). """ if getattr(defender, 'is_invulnerable', False): return CombatResult.INVULNERABLE if not circle_overlaps_obb( proj_pos, hit_radius, defender.hitbox_center, defender.hitbox.size, defender.facing_angle, ): return CombatResult.MISSED if defender.is_in_parry_window(): return CombatResult.PARRIED if defender.is_blocking: if defender.drain_stamina(20.0): return CombatResult.BLOCK_BROKEN return CombatResult.BLOCKED defender.take_damage(1) return CombatResult.DAMAGED