# SPDX-License-Identifier: GPL-3.0
# Copyright (c) 2014-2023 William Edwards <shadowapex@gmail.com>, Benjamin Bean <superman2k5@gmail.com>
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Generator, Sequence, Tuple, cast, final
from tuxemon.event import get_npc
from tuxemon.event.eventaction import EventAction
from tuxemon.map import Direction, dirs2
from tuxemon.math import Vector2
[docs]def simple_path(
origin: Tuple[int, int],
direction: Direction,
tiles: int,
) -> Generator[Tuple[int, int], None, None]:
origin_vec = Vector2(origin)
for _ in range(tiles):
origin_vec += dirs2[direction]
yield (int(origin_vec[0]), int(origin_vec[1]))
[docs]def parse_path_parameters(
origin: Tuple[int, int],
move_list: Sequence[str],
) -> Generator[Tuple[int, int], None, None]:
for move in move_list:
try:
direction, tiles = move.strip().split()
except ValueError:
direction, tiles = move.strip(), "1"
# Pending https://github.com/python/mypy/issues/9718
assert direction in dirs2
direction = cast(Direction, direction)
for point in simple_path(origin, direction, int(tiles)):
yield point
origin = point
[docs]@final
@dataclass
class NpcMoveAction(EventAction):
"""
Relative tile movement for NPC.
This action blocks until the destination is reached.
Script usage:
.. code-block::
npc_move <npc_slug>,<move>...
Script parameters:
npc_slug: Either "player" or npc slug name (e.g. "npc_maple").
move: A tuple with format ``<direction> [amount_of_tiles]`` where
``direction`` can be one of "up", "down", "left" and "right" and
``amount_of_tiles`` is the number of tiles moved in that direction,
being 1 by default. Several movements can be passed, that will be
executed one after the other. For example:
``up 10, down 5, left 5``.
"""
name = "npc_move"
raw_parameters: Sequence[str] = field(init=False)
def __init__(self, *args: Any) -> None:
super().__init__()
self.raw_parameters = args
# parameter checking not supported due to undefined number of parameters
[docs] def start(self) -> None:
npc_slug = self.raw_parameters[0]
self.npc = get_npc(self.session, npc_slug)
if self.npc is None:
return
path = list(
parse_path_parameters(
self.npc.tile_pos,
self.raw_parameters[1:],
)
)
path.reverse()
self.npc.path = path
self.npc.next_waypoint()
[docs] def update(self) -> None:
if self.npc is None:
self.stop()
return
if not self.npc.moving and not self.npc.path:
self.stop()