NotQuiteParadise2

Source code for nqp.command.unit_behaviour

from __future__ import annotations

import random
from typing import TYPE_CHECKING

import pygame
import snecs

from nqp.core.constants import TILE_SIZE
from nqp.core.utility import distance_to
from nqp.world_elements.entity_components import AI, Allegiance, IsDead, Position

if TYPE_CHECKING:
    from typing import List, Optional

    from snecs.typedefs import EntityID

    from nqp.command.unit import Unit
    from nqp.core.game import Game

__all__ = ["UnitBehaviour"]


REGROUP_RANGE = 32
RETREAT_AMT = 16


[docs]class UnitBehaviour:
[docs] def __init__(self, game: Game, unit: Unit): self._game = game self.unit = unit self.target_unit: Optional[Unit] = None self.reference_entity: Optional[EntityID] = None self.valid_targets: List = [] self.spread_max: int = self.unit.entity_spread_max self.force_regroup: bool = False self.regrouping: bool = False self.force_retreat: bool = False self.retreating: bool = False self.retreat_target: Optional[pygame.Vector2] = None self.position_log: List[pygame.Vector2] = [] self.check_visibility: bool = True if self.unit.is_ranged else False self.smart_range_retarget: bool = False
@property def leader(self) -> EntityID: return self.unit.entities[0] if len(self.unit.entities) else None
[docs] def find_target(self): """ Find the nearest enemy from a different team and update the target. """ nearest = [None, 9999999] for entity in self._game.world.model.get_all_entities(): other_team = snecs.entity_component(entity, Allegiance).team if other_team != self.unit.team: other_position = snecs.entity_component(entity, Position) dis = distance_to(self.unit.pos, other_position.pos) if dis < nearest[1]: nearest = [entity, dis] if nearest[0]: self.target_unit = snecs.entity_component(nearest[0], Allegiance).unit self.reference_entity = random.choice(self.target_unit.entities) else: self.target_unit = None self.reference_entity = None
[docs] def update_valid_targets(self): self.valid_targets = [] if self.target_unit: for entity in self.target_unit.entities: position = snecs.entity_component(entity, Position) other_position = snecs.entity_component(self.reference_entity, Position) if distance_to(position.pos, other_position.pos) < self.spread_max: self.valid_targets.append(entity)
[docs] def update(self, delta_time: float): if len(self.unit.entities): if (not self.target_unit) or (not self.target_unit.is_alive): self.find_target() if self.leader: position = snecs.entity_component(self.leader, Position) loc = self._game.world.model.px_to_loc(position.pos) if loc not in self.position_log: self.position_log.append(loc) self.position_log = self.position_log[-50:] if self.regrouping or self.retreating: all_x = [] all_y = [] for entity in self.unit.entities: position = snecs.entity_component(entity, Position) all_x.append(position.x) all_y.append(position.y) if self.retreating: all_x.append(self.retreat_target[0]) all_y.append(self.retreat_target[1]) if (max(all_x) - min(all_x)) < REGROUP_RANGE: if (max(all_y) - min(all_y)) < REGROUP_RANGE: self.regrouping = False self.retreating = False for entity in self.unit.entities: behaviour = snecs.entity_component(entity, AI).behaviour behaviour.state = behaviour.movement_mode # update reference entity when it dies if self.reference_entity is None: ref_entity_is_alive = False else: ref_entity_is_alive = not snecs.has_component(self.reference_entity, IsDead) if self.target_unit and self.target_unit.is_alive and (not ref_entity_is_alive): # if we have possible targets pick another random one to be the reference if len(self.target_unit.entities): self.reference_entity = random.choice(self.target_unit.entities) else: self.target_unit = None # regroup/retreat since target unit died if self.force_regroup or self.force_retreat: if self.force_regroup: self.regrouping = True if self.force_retreat: self.retreating = True self.retreat_target = ( self.position_log[-RETREAT_AMT][0] * TILE_SIZE, self.position_log[-RETREAT_AMT][1] * TILE_SIZE, ) for entity in self.unit.entities: behaviour = snecs.entity_component(entity, AI).behaviour behaviour.state = "path" behaviour.target_entity = None self.update_valid_targets()