class Enemy(DirtySprite, TargetABC): def __init__(self, scene, position: Vector, player: 'Player'): super().__init__(scene.groups["interact"]) self.image = image.load(path.join(path.dirname(__file__), "enemy.png")) self.rect = self.image.get_rect() self.rect.center = tuple(position) self._position = position self.direction = Vector(0, 1).rotate(random.randint(0, 359)) self.dirty = 1 self.scene = scene self.speed = 30 self.reach = 20 self.sense_limit = 200 self.player = player @property def position(self): return self._position def interact(self, player: 'Player'): logging.getLogger(type(self).__name__).debug("Enemy killed.") self.kill() def update(self, time_delta): direction_to_player = self.player.position - self.position distance_to_player = direction_to_player.length if distance_to_player < self.reach: pass elif distance_to_player < self.sense_limit: self._position += direction_to_player.scale(self.speed * time_delta) else: self.direction = self.direction.rotate(random.randint(-2, 2)) self._position += self.direction.scale(self.speed * time_delta * .5) self.rect.center = tuple(self.position) self.dirty = 1
class BaseSprite(EventMixin): """ The base Sprite class. All sprites should inherit from this (directly or indirectly). Attributes: * image (str): The image file * resource_path (pathlib.Path): The path that image is relative to * position: Location of the sprite * facing: The direction of the "top" of the sprite (rendering only) * size: The width/height of the sprite (sprites are square) """ image = None resource_path = None position: Vector = Vector(0, 0) facing: Vector = Vector(0, -1) _size: Union[int, float] = 1 _offset_value = None def __init__(self, **kwargs): super().__init__() # Make these instance properties with fresh instances # Don't use Vector.convert() because we need copying self.position = Vector(*self.position) self.facing = Vector(*self.facing) # Initialize things for k, v in kwargs.items(): # Abbreviations if k == 'pos': k = 'position' # Castings if k in ('position', 'facing'): v = Vector(*v) # Vector.convert() when that ships. setattr(self, k, v) # Trigger some calculations self.size = self.size @property def center(self) -> Vector: return self.position @center.setter def center(self, value: Sequence[float]): x = value[0] y = value[1] self.position.x = x self.position.y = y @property def left(self) -> Side: return Side(self, LEFT) @left.setter def left(self, value: float): self.position.x = value + self._offset_value @property def right(self) -> Side: return Side(self, RIGHT) @right.setter def right(self, value): self.position.x = value - self._offset_value @property def top(self): return Side(self, TOP) @top.setter def top(self, value): self.position.y = value + self._offset_value @property def bottom(self): return Side(self, BOTTOM) @bottom.setter def bottom(self, value): self.position.y = value - self._offset_value @property def size(self) -> Union[int, float]: return self._size @size.setter def size(self, value: Union[int, float]): self._size = value self._offset_value = self._size / 2 def rotate(self, degrees: Number): self.facing.rotate(degrees) def __image__(self): if self.image is None: self.image = f"{type(self).__name__.lower()}.png" return self.image def __resource_path__(self): if self.resource_path is None: self.resource_path = Path(realpath(getfile(type(self)))).absolute().parent return self.resource_path