mirror of
https://github.com/rr-/pq-cli.git
synced 2026-11-12 22:40:36 -07:00
misc: drop typing as T, use builtin generics
Python 3.10 is the minimum, so annotations use `X | None`, `list[X]` and friends, enforced by ruff's pyupgrade rules. The remaining typing names are imported directly, with abstract types coming from collections.abc. first() is now generic over the item and default.
This commit is contained in:
40 files changed
+248
-236
No files matched your search
+3
-3
@@ -1,7 +1,7 @@
|
||||
import argparse
|
||||
import sys
|
||||
import typing as T
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from xdg_base_dirs import xdg_config_home
|
||||
|
||||
@@ -84,7 +84,7 @@ def parse_args() -> argparse.Namespace:
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def list_players(roster: Roster, file: T.Optional[T.Any] = None) -> None:
|
||||
def list_players(roster: Roster, file: Any | None = None) -> None:
|
||||
for i, player in enumerate(roster.players, start=1):
|
||||
print(f"{i}. {player.name}", file=file)
|
||||
|
||||
@@ -130,7 +130,7 @@ def main() -> None:
|
||||
list_players(roster)
|
||||
exit(0)
|
||||
|
||||
player: T.Optional[Player] = None
|
||||
player: Player | None = None
|
||||
if args.load_save:
|
||||
try:
|
||||
player = list(roster.players)[args.load_save - 1]
|
||||
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
import enum
|
||||
import typing as T
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@@ -60,16 +59,16 @@ class EquipmentPreset:
|
||||
class Monster:
|
||||
name: str
|
||||
level: int
|
||||
item: T.Optional[str]
|
||||
item: str | None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Race:
|
||||
name: str
|
||||
attr: T.List[StatType]
|
||||
attr: list[StatType]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Class:
|
||||
name: str
|
||||
attr: T.List[StatType]
|
||||
attr: list[StatType]
|
||||
+43
-42
@@ -1,9 +1,10 @@
|
||||
import json
|
||||
import sys
|
||||
import typing as T
|
||||
from collections.abc import Callable, Sequence
|
||||
from dataclasses import dataclass, fields
|
||||
from importlib import resources
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
|
||||
from pqcli.config import (
|
||||
Class,
|
||||
@@ -23,24 +24,24 @@ class DataError(Exception):
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GameData:
|
||||
spells: T.Tuple[str, ...]
|
||||
offense_attrib: T.Tuple[Modifier, ...]
|
||||
defense_attrib: T.Tuple[Modifier, ...]
|
||||
offense_bad: T.Tuple[Modifier, ...]
|
||||
defense_bad: T.Tuple[Modifier, ...]
|
||||
shields: T.Tuple[EquipmentPreset, ...]
|
||||
armors: T.Tuple[EquipmentPreset, ...]
|
||||
weapons: T.Tuple[EquipmentPreset, ...]
|
||||
specials: T.Tuple[str, ...]
|
||||
item_attrib: T.Tuple[str, ...]
|
||||
item_ofs: T.Tuple[str, ...]
|
||||
boring_items: T.Tuple[str, ...]
|
||||
monsters: T.Tuple[Monster, ...]
|
||||
races: T.Tuple[Race, ...]
|
||||
classes: T.Tuple[Class, ...]
|
||||
titles: T.Tuple[str, ...]
|
||||
impressive_titles: T.Tuple[str, ...]
|
||||
combat_verbs: T.Tuple[str, ...]
|
||||
spells: tuple[str, ...]
|
||||
offense_attrib: tuple[Modifier, ...]
|
||||
defense_attrib: tuple[Modifier, ...]
|
||||
offense_bad: tuple[Modifier, ...]
|
||||
defense_bad: tuple[Modifier, ...]
|
||||
shields: tuple[EquipmentPreset, ...]
|
||||
armors: tuple[EquipmentPreset, ...]
|
||||
weapons: tuple[EquipmentPreset, ...]
|
||||
specials: tuple[str, ...]
|
||||
item_attrib: tuple[str, ...]
|
||||
item_ofs: tuple[str, ...]
|
||||
boring_items: tuple[str, ...]
|
||||
monsters: tuple[Monster, ...]
|
||||
races: tuple[Race, ...]
|
||||
classes: tuple[Class, ...]
|
||||
titles: tuple[str, ...]
|
||||
impressive_titles: tuple[str, ...]
|
||||
combat_verbs: tuple[str, ...]
|
||||
|
||||
|
||||
DATA_FILES = tuple(field.name for field in fields(GameData))
|
||||
@@ -55,8 +56,8 @@ class _Context:
|
||||
|
||||
|
||||
def _check_keys(
|
||||
ctx: _Context, where: str, obj: T.Any, keys: T.Sequence[str]
|
||||
) -> T.Dict[str, T.Any]:
|
||||
ctx: _Context, where: str, obj: Any, keys: Sequence[str]
|
||||
) -> dict[str, Any]:
|
||||
if not isinstance(obj, dict):
|
||||
raise ctx.error(f"{where} must be an object")
|
||||
missing = [key for key in keys if key not in obj]
|
||||
@@ -68,19 +69,19 @@ def _check_keys(
|
||||
return obj
|
||||
|
||||
|
||||
def _str(ctx: _Context, where: str, value: T.Any) -> str:
|
||||
def _str(ctx: _Context, where: str, value: Any) -> str:
|
||||
if not isinstance(value, str):
|
||||
raise ctx.error(f"{where} must be a string")
|
||||
return value
|
||||
|
||||
|
||||
def _int(ctx: _Context, where: str, value: T.Any) -> int:
|
||||
def _int(ctx: _Context, where: str, value: Any) -> int:
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise ctx.error(f"{where} must be an integer")
|
||||
return value
|
||||
|
||||
|
||||
def _stat(ctx: _Context, where: str, value: T.Any) -> StatType:
|
||||
def _stat(ctx: _Context, where: str, value: Any) -> StatType:
|
||||
_str(ctx, where, value)
|
||||
for stat in StatType:
|
||||
if value.lower() in (stat.value.lower(), stat.name):
|
||||
@@ -89,18 +90,18 @@ def _stat(ctx: _Context, where: str, value: T.Any) -> StatType:
|
||||
raise ctx.error(f"{where}: unknown stat {value!r} (use one of {allowed})")
|
||||
|
||||
|
||||
def _parse_str(ctx: _Context, where: str, item: T.Any) -> str:
|
||||
def _parse_str(ctx: _Context, where: str, item: Any) -> str:
|
||||
return _str(ctx, where, item)
|
||||
|
||||
|
||||
def _parse_verb(ctx: _Context, where: str, item: T.Any) -> str:
|
||||
def _parse_verb(ctx: _Context, where: str, item: Any) -> str:
|
||||
verb = _str(ctx, where, item)
|
||||
if TARGET_PLACEHOLDER not in verb:
|
||||
raise ctx.error(f"{where} must contain {TARGET_PLACEHOLDER}")
|
||||
return verb
|
||||
|
||||
|
||||
def _parse_modifier(ctx: _Context, where: str, item: T.Any) -> Modifier:
|
||||
def _parse_modifier(ctx: _Context, where: str, item: Any) -> Modifier:
|
||||
obj = _check_keys(ctx, where, item, ["name", "quality"])
|
||||
return Modifier(
|
||||
_str(ctx, f"{where}.name", obj["name"]),
|
||||
@@ -108,7 +109,7 @@ def _parse_modifier(ctx: _Context, where: str, item: T.Any) -> Modifier:
|
||||
)
|
||||
|
||||
|
||||
def _parse_preset(ctx: _Context, where: str, item: T.Any) -> EquipmentPreset:
|
||||
def _parse_preset(ctx: _Context, where: str, item: Any) -> EquipmentPreset:
|
||||
obj = _check_keys(ctx, where, item, ["name", "quality"])
|
||||
return EquipmentPreset(
|
||||
_str(ctx, f"{where}.name", obj["name"]),
|
||||
@@ -116,7 +117,7 @@ def _parse_preset(ctx: _Context, where: str, item: T.Any) -> EquipmentPreset:
|
||||
)
|
||||
|
||||
|
||||
def _parse_monster(ctx: _Context, where: str, item: T.Any) -> Monster:
|
||||
def _parse_monster(ctx: _Context, where: str, item: Any) -> Monster:
|
||||
obj = _check_keys(ctx, where, item, ["name", "level", "item"])
|
||||
loot = obj["item"]
|
||||
return Monster(
|
||||
@@ -126,13 +127,13 @@ def _parse_monster(ctx: _Context, where: str, item: T.Any) -> Monster:
|
||||
)
|
||||
|
||||
|
||||
def _parse_attrs(ctx: _Context, where: str, value: T.Any) -> T.List[StatType]:
|
||||
def _parse_attrs(ctx: _Context, where: str, value: Any) -> list[StatType]:
|
||||
if not isinstance(value, list):
|
||||
raise ctx.error(f"{where} must be a list of stats")
|
||||
return [_stat(ctx, f"{where}[{i}]", stat) for i, stat in enumerate(value)]
|
||||
|
||||
|
||||
def _parse_race(ctx: _Context, where: str, item: T.Any) -> Race:
|
||||
def _parse_race(ctx: _Context, where: str, item: Any) -> Race:
|
||||
obj = _check_keys(ctx, where, item, ["name", "attr"])
|
||||
return Race(
|
||||
_str(ctx, f"{where}.name", obj["name"]),
|
||||
@@ -140,7 +141,7 @@ def _parse_race(ctx: _Context, where: str, item: T.Any) -> Race:
|
||||
)
|
||||
|
||||
|
||||
def _parse_class(ctx: _Context, where: str, item: T.Any) -> Class:
|
||||
def _parse_class(ctx: _Context, where: str, item: Any) -> Class:
|
||||
obj = _check_keys(ctx, where, item, ["name", "attr"])
|
||||
return Class(
|
||||
_str(ctx, f"{where}.name", obj["name"]),
|
||||
@@ -148,7 +149,7 @@ def _parse_class(ctx: _Context, where: str, item: T.Any) -> Class:
|
||||
)
|
||||
|
||||
|
||||
_PARSERS: T.Dict[str, T.Callable[[_Context, str, T.Any], T.Any]] = {
|
||||
_PARSERS: dict[str, Callable[[_Context, str, Any], Any]] = {
|
||||
"spells": _parse_str,
|
||||
"offense_attrib": _parse_modifier,
|
||||
"defense_attrib": _parse_modifier,
|
||||
@@ -171,7 +172,7 @@ _PARSERS: T.Dict[str, T.Callable[[_Context, str, T.Any], T.Any]] = {
|
||||
assert set(_PARSERS) == set(DATA_FILES)
|
||||
|
||||
|
||||
def _parse(name: str, path: str, text: str) -> T.Tuple[T.Any, ...]:
|
||||
def _parse(name: str, path: str, text: str) -> tuple[Any, ...]:
|
||||
ctx = _Context(path)
|
||||
try:
|
||||
value = json.loads(text)
|
||||
@@ -185,26 +186,26 @@ def _parse(name: str, path: str, text: str) -> T.Tuple[T.Any, ...]:
|
||||
return tuple(parser(ctx, f"[{i}]", item) for i, item in enumerate(value))
|
||||
|
||||
|
||||
def _builtin_dir() -> T.Any:
|
||||
def _builtin_dir() -> Any:
|
||||
return resources.files("pqcli") / "data"
|
||||
|
||||
|
||||
def _read_text(path: T.Any, display: str) -> str:
|
||||
def _read_text(path: Any, display: str) -> str:
|
||||
try:
|
||||
return T.cast(str, path.read_text(encoding="utf-8"))
|
||||
return cast(str, path.read_text(encoding="utf-8"))
|
||||
except UnicodeDecodeError as ex:
|
||||
raise DataError(f"{display}: not valid UTF-8: {ex}") from ex
|
||||
except OSError as ex:
|
||||
raise DataError(f"{display}: cannot read: {ex}") from ex
|
||||
|
||||
|
||||
def load_game_data(override_dir: T.Optional[Path] = None) -> GameData:
|
||||
def load_game_data(override_dir: Path | None = None) -> GameData:
|
||||
"""Load the built-in word lists, replacing any that override_dir has.
|
||||
|
||||
A missing override_dir is fine. Each <name>.json in it replaces the
|
||||
whole built-in list of that name.
|
||||
"""
|
||||
overrides: T.Set[str] = set()
|
||||
overrides: set[str] = set()
|
||||
if override_dir is not None and override_dir.exists():
|
||||
if not override_dir.is_dir():
|
||||
raise DataError(f"{override_dir}: not a directory")
|
||||
@@ -218,11 +219,11 @@ def load_game_data(override_dir: T.Optional[Path] = None) -> GameData:
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
values: T.Dict[str, T.Tuple[T.Any, ...]] = {}
|
||||
values: dict[str, tuple[Any, ...]] = {}
|
||||
for name in DATA_FILES:
|
||||
if name in overrides:
|
||||
assert override_dir is not None
|
||||
source: T.Any = override_dir / f"{name}.json"
|
||||
source: Any = override_dir / f"{name}.json"
|
||||
display = str(source)
|
||||
else:
|
||||
source = _builtin_dir() / f"{name}.json"
|
||||
@@ -231,7 +232,7 @@ def load_game_data(override_dir: T.Optional[Path] = None) -> GameData:
|
||||
return GameData(**values)
|
||||
|
||||
|
||||
def dump_builtin_data(target_dir: Path) -> T.List[Path]:
|
||||
def dump_builtin_data(target_dir: Path) -> list[Path]:
|
||||
"""Copy the built-in data files to target_dir, never overwriting."""
|
||||
targets = [target_dir / f"{name}.json" for name in DATA_FILES]
|
||||
existing = [path for path in targets if path.exists()]
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
import datetime
|
||||
import typing as T
|
||||
|
||||
from pqcli import random
|
||||
|
||||
@@ -114,7 +113,7 @@ def definite(subject: str, qty: int) -> str:
|
||||
return "the " + subject
|
||||
|
||||
|
||||
def prefix(a: T.List[str], m: int, subject: str, sep: str = " ") -> str:
|
||||
def prefix(a: list[str], m: int, subject: str, sep: str = " ") -> str:
|
||||
m = abs(m)
|
||||
if m < 1 or m > len(a):
|
||||
return subject
|
||||
|
||||
+53
-54
@@ -3,9 +3,10 @@ import functools
|
||||
import itertools
|
||||
import logging
|
||||
import math
|
||||
import typing as T
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Iterator, Sequence
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, cast
|
||||
|
||||
from pqcli import random
|
||||
from pqcli.config import (
|
||||
@@ -35,23 +36,21 @@ from pqcli.random import Alea, RngState
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_SIGNALS: T.Dict[
|
||||
T.Tuple["SignalMixin", str], T.List[T.Callable[..., T.Any]]
|
||||
] = defaultdict(list)
|
||||
_SIGNALS: dict[tuple["SignalMixin", str], list[Callable[..., Any]]] = (
|
||||
defaultdict(list)
|
||||
)
|
||||
|
||||
|
||||
class SignalMixin:
|
||||
def emit(self, signal_name: str, *user_data: T.Any) -> None:
|
||||
def emit(self, signal_name: str, *user_data: Any) -> None:
|
||||
for callback in _SIGNALS[self, signal_name]:
|
||||
callback(*user_data)
|
||||
|
||||
def connect(
|
||||
self, signal_name: str, callback: T.Callable[..., T.Any]
|
||||
) -> None:
|
||||
def connect(self, signal_name: str, callback: Callable[..., Any]) -> None:
|
||||
_SIGNALS[self, signal_name].append(callback)
|
||||
|
||||
def disconnect(
|
||||
self, signal_name: str, callback: T.Callable[..., T.Any]
|
||||
self, signal_name: str, callback: Callable[..., Any]
|
||||
) -> None:
|
||||
try:
|
||||
idx = _SIGNALS[self, signal_name].index(callback)
|
||||
@@ -108,10 +107,10 @@ class Bar(SignalMixin):
|
||||
class Stats(SignalMixin):
|
||||
signals = ["change"]
|
||||
|
||||
def __init__(self, values: T.Dict[StatType, int]) -> None:
|
||||
def __init__(self, values: dict[StatType, int]) -> None:
|
||||
self._values = values
|
||||
|
||||
def __iter__(self) -> T.Iterator[T.Tuple[StatType, int]]:
|
||||
def __iter__(self) -> Iterator[tuple[StatType, int]]:
|
||||
return iter(self._values.items())
|
||||
|
||||
def __getitem__(self, stat: StatType) -> int:
|
||||
@@ -137,22 +136,22 @@ class QuestBook(SignalMixin):
|
||||
signals = ["start_act", "start_quest"]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._quests: T.List[str] = []
|
||||
self._quests: list[str] = []
|
||||
self._act = 0
|
||||
self.plot_bar = Bar(max_=1)
|
||||
self.quest_bar = Bar(max_=1)
|
||||
self.monster: T.Optional[Monster] = None
|
||||
self.monster: Monster | None = None
|
||||
|
||||
@property
|
||||
def act(self) -> int:
|
||||
return self._act
|
||||
|
||||
@property
|
||||
def quests(self) -> T.List[str]:
|
||||
def quests(self) -> list[str]:
|
||||
return self._quests
|
||||
|
||||
@property
|
||||
def current_quest(self) -> T.Optional[str]:
|
||||
def current_quest(self) -> str | None:
|
||||
if self._quests:
|
||||
return self._quests[-1]
|
||||
return None
|
||||
@@ -178,7 +177,7 @@ class Inventory(SignalMixin):
|
||||
|
||||
def __init__(self, capacity: int = 0) -> None:
|
||||
self._gold = 0
|
||||
self._items: T.List[InventoryItem] = []
|
||||
self._items: list[InventoryItem] = []
|
||||
self.encum_bar = Bar(max_=capacity)
|
||||
|
||||
@property
|
||||
@@ -188,7 +187,7 @@ class Inventory(SignalMixin):
|
||||
def __len__(self) -> int:
|
||||
return len(self._items)
|
||||
|
||||
def __iter__(self) -> T.Iterator[InventoryItem]:
|
||||
def __iter__(self) -> Iterator[InventoryItem]:
|
||||
return iter(self._items)
|
||||
|
||||
def __getitem__(self, idx: int) -> InventoryItem:
|
||||
@@ -234,16 +233,16 @@ class Equipment(SignalMixin):
|
||||
signals = ["change"]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._items: T.Dict[EquipmentType, str] = {
|
||||
self._items: dict[EquipmentType, str] = {
|
||||
EquipmentType.weapon: "Sharp Rock",
|
||||
EquipmentType.hauberk: "-3 Burlap",
|
||||
}
|
||||
self.best = self[EquipmentType.weapon]
|
||||
|
||||
def __iter__(self) -> T.Iterator[T.Tuple[EquipmentType, str]]:
|
||||
def __iter__(self) -> Iterator[tuple[EquipmentType, str]]:
|
||||
return iter(self._items.items())
|
||||
|
||||
def __getitem__(self, equipment_type: EquipmentType) -> T.Optional[str]:
|
||||
def __getitem__(self, equipment_type: EquipmentType) -> str | None:
|
||||
return self._items.get(equipment_type, None)
|
||||
|
||||
def put(self, equipment_type: EquipmentType, item_name: str) -> None:
|
||||
@@ -267,9 +266,9 @@ class SpellBook(SignalMixin):
|
||||
signals = ["add", "change"]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._spells: T.List[Spell] = []
|
||||
self._spells: list[Spell] = []
|
||||
|
||||
def __iter__(self) -> T.Iterator[Spell]:
|
||||
def __iter__(self) -> Iterator[Spell]:
|
||||
return iter(self._spells)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
@@ -292,7 +291,7 @@ class SpellBook(SignalMixin):
|
||||
self.emit("add", spell)
|
||||
|
||||
@property
|
||||
def best(self) -> T.Optional[Spell]:
|
||||
def best(self) -> Spell | None:
|
||||
if not self._spells:
|
||||
return None
|
||||
return max(self._spells, key=lambda spell: spell.level)
|
||||
@@ -306,7 +305,7 @@ class BaseTask:
|
||||
|
||||
@dataclass
|
||||
class KillTask(BaseTask):
|
||||
monster: T.Optional[Monster] = None
|
||||
monster: Monster | None = None
|
||||
|
||||
|
||||
class BuyTask(BaseTask):
|
||||
@@ -347,7 +346,7 @@ class Player(SignalMixin):
|
||||
race: Race,
|
||||
class_: Class,
|
||||
stats: Stats,
|
||||
rng: T.Optional[Alea] = None,
|
||||
rng: Alea | None = None,
|
||||
) -> None:
|
||||
self.name: str = name
|
||||
self.birthday: datetime.datetime = birthday
|
||||
@@ -366,10 +365,10 @@ class Player(SignalMixin):
|
||||
self.spell_book = SpellBook()
|
||||
|
||||
self.task_bar = Bar(max_=1)
|
||||
self.task: T.Optional[BaseTask] = None
|
||||
self.queue: T.List[BaseTask] = []
|
||||
self.task: BaseTask | None = None
|
||||
self.queue: list[BaseTask] = []
|
||||
|
||||
def __setstate__(self, obj: T.Any) -> None:
|
||||
def __setstate__(self, obj: Any) -> None:
|
||||
self.__dict__.update(obj)
|
||||
self.elapsed = obj.get("elapsed", 0.0)
|
||||
if "rng" not in obj:
|
||||
@@ -402,7 +401,7 @@ class Player(SignalMixin):
|
||||
self.emit("level_up")
|
||||
|
||||
def win_stat(self) -> bool:
|
||||
chosen_stat: T.Optional[StatType] = None
|
||||
chosen_stat: StatType | None = None
|
||||
|
||||
if random.odds(1, 2):
|
||||
chosen_stat = random.choice(list(StatType))
|
||||
@@ -437,9 +436,9 @@ class Player(SignalMixin):
|
||||
def win_equipment(self, data: GameData) -> None:
|
||||
choice = random.choice(list(EquipmentType))
|
||||
|
||||
stuff: T.Sequence[EquipmentPreset]
|
||||
better: T.Sequence[Modifier]
|
||||
worse: T.Sequence[Modifier]
|
||||
stuff: Sequence[EquipmentPreset]
|
||||
better: Sequence[Modifier]
|
||||
worse: Sequence[Modifier]
|
||||
|
||||
if choice == EquipmentType.weapon:
|
||||
stuff = data.weapons
|
||||
@@ -566,7 +565,7 @@ class Simulation:
|
||||
self.player.task_bar.max_ / 1000
|
||||
)
|
||||
elif self.player.quest_book.act == 0 and not isinstance(
|
||||
self.player.task, (LoadingTask, PlotTask)
|
||||
self.player.task, LoadingTask | PlotTask
|
||||
):
|
||||
# the Prologue story tasks fill the plot bar
|
||||
self.player.quest_book.plot_bar.increment(
|
||||
@@ -599,7 +598,7 @@ class Simulation:
|
||||
self.player.inventory.add_gold(-self.player.equip_price())
|
||||
self.player.win_equipment(self.data)
|
||||
|
||||
elif isinstance(self.player.task, (HeadingToMarketTask, SellTask)):
|
||||
elif isinstance(self.player.task, HeadingToMarketTask | SellTask):
|
||||
if isinstance(self.player.task, SellTask):
|
||||
item = self.player.inventory[0]
|
||||
amount = item.quantity * self.player.level
|
||||
@@ -629,7 +628,7 @@ class Simulation:
|
||||
self.player.set_task(
|
||||
HeadingToMarketTask("Heading to market to sell loot", 4000)
|
||||
)
|
||||
elif not isinstance(old, (KillTask, HeadingToKillingFieldsTask)):
|
||||
elif not isinstance(old, KillTask | HeadingToKillingFieldsTask):
|
||||
if self.player.inventory.gold > self.player.equip_price():
|
||||
self.player.set_task(
|
||||
BuyTask(
|
||||
@@ -838,35 +837,35 @@ def special_item(data: GameData) -> str:
|
||||
return (
|
||||
interesting_item(data)
|
||||
+ " of "
|
||||
+ T.cast(str, random.choice(data.item_ofs))
|
||||
+ cast(str, random.choice(data.item_ofs))
|
||||
)
|
||||
|
||||
|
||||
def interesting_item(data: GameData) -> str:
|
||||
return (
|
||||
T.cast(str, random.choice(data.item_attrib))
|
||||
cast(str, random.choice(data.item_attrib))
|
||||
+ " "
|
||||
+ T.cast(str, random.choice(data.specials))
|
||||
+ cast(str, random.choice(data.specials))
|
||||
)
|
||||
|
||||
|
||||
def boring_item(data: GameData) -> str:
|
||||
return T.cast(str, random.choice(data.boring_items))
|
||||
return cast(str, random.choice(data.boring_items))
|
||||
|
||||
|
||||
def impressive_guy(data: GameData) -> str:
|
||||
if random.below(2):
|
||||
title = T.cast(str, random.choice(data.impressive_titles))
|
||||
race = T.cast(Race, random.choice(data.races))
|
||||
title = cast(str, random.choice(data.impressive_titles))
|
||||
race = cast(Race, random.choice(data.races))
|
||||
return f"the {title} of the {plural(race.name)}"
|
||||
title = T.cast(str, random.choice(data.impressive_titles))
|
||||
title = cast(str, random.choice(data.impressive_titles))
|
||||
return f"{title} {generate_name()} of {generate_name()}"
|
||||
|
||||
|
||||
def unnamed_monster(data: GameData, level: int, iterations: int) -> Monster:
|
||||
result = T.cast(Monster, random.choice(data.monsters))
|
||||
result = cast(Monster, random.choice(data.monsters))
|
||||
for _ in range(iterations):
|
||||
alternative = T.cast(Monster, random.choice(data.monsters))
|
||||
alternative = cast(Monster, random.choice(data.monsters))
|
||||
if abs(level - alternative.level) < abs(level - result.level):
|
||||
result = alternative
|
||||
return result
|
||||
@@ -878,18 +877,18 @@ def named_monster(data: GameData, level: int) -> str:
|
||||
|
||||
|
||||
def pick_equipment(
|
||||
source: T.Sequence[EquipmentPreset], goal: int
|
||||
source: Sequence[EquipmentPreset], goal: int
|
||||
) -> EquipmentPreset:
|
||||
result = T.cast(EquipmentPreset, random.choice(source))
|
||||
result = cast(EquipmentPreset, random.choice(source))
|
||||
for _ in range(5):
|
||||
alternative = T.cast(EquipmentPreset, random.choice(source))
|
||||
alternative = cast(EquipmentPreset, random.choice(source))
|
||||
if abs(goal - alternative.quality) < abs(goal - result.quality):
|
||||
result = alternative
|
||||
return result
|
||||
|
||||
|
||||
def monster_task(
|
||||
data: GameData, player_level: int, quest_monster: T.Optional[Monster]
|
||||
data: GameData, player_level: int, quest_monster: Monster | None
|
||||
) -> KillTask:
|
||||
level = player_level
|
||||
for _ in range(level):
|
||||
@@ -899,7 +898,7 @@ def monster_task(
|
||||
level = 1
|
||||
|
||||
is_definite = False
|
||||
monster: T.Optional[Monster] = None
|
||||
monster: Monster | None = None
|
||||
if random.odds(1, 25):
|
||||
# use an NPC every once in a while
|
||||
race = random.choice(data.races)
|
||||
@@ -968,7 +967,7 @@ def monster_task(
|
||||
verb = (
|
||||
data.combat_verbs[0]
|
||||
if len(data.combat_verbs) == 1
|
||||
else T.cast(str, random.choice(data.combat_verbs))
|
||||
else cast(str, random.choice(data.combat_verbs))
|
||||
)
|
||||
description = verb.replace(TARGET_PLACEHOLDER, result)
|
||||
return KillTask(description, duration, monster=monster)
|
||||
@@ -982,9 +981,9 @@ class _Roll:
|
||||
|
||||
|
||||
class StatsBuilder:
|
||||
def __init__(self, rng: T.Optional[Alea] = None) -> None:
|
||||
def __init__(self, rng: Alea | None = None) -> None:
|
||||
self._rng = rng or Alea()
|
||||
self.history: T.List[_Roll] = []
|
||||
self.history: list[_Roll] = []
|
||||
|
||||
@property
|
||||
def seed(self) -> RngState:
|
||||
@@ -998,7 +997,7 @@ class StatsBuilder:
|
||||
def roll(self) -> Stats:
|
||||
seed = self._rng.state()
|
||||
with random.using(self._rng):
|
||||
values: T.Dict[StatType, int] = {
|
||||
values: dict[StatType, int] = {
|
||||
stat: 3 + random.below(6) + random.below(6) + random.below(6)
|
||||
for stat in PRIME_STATS
|
||||
}
|
||||
@@ -1024,7 +1023,7 @@ def create_player(
|
||||
race: Race,
|
||||
class_: Class,
|
||||
stats: Stats,
|
||||
seed: T.Optional[RngState] = None,
|
||||
seed: RngState | None = None,
|
||||
) -> Player:
|
||||
player = Player(
|
||||
birthday=datetime.datetime.now(),
|
||||
|
||||
+7
-6
@@ -1,8 +1,9 @@
|
||||
import contextlib
|
||||
import time
|
||||
import typing as T
|
||||
from collections.abc import Callable, Iterator, Sequence
|
||||
from typing import Any
|
||||
|
||||
RngState = T.Tuple[float, float, float, int]
|
||||
RngState = tuple[float, float, float, int]
|
||||
|
||||
_2_POW_32 = 0x100000000
|
||||
_2_POW_MINUS_32 = 2.3283064365386963e-10
|
||||
@@ -13,7 +14,7 @@ def _to_uint32(value: float) -> float:
|
||||
return float(int(value) % _2_POW_32)
|
||||
|
||||
|
||||
def _mash() -> T.Callable[[str], float]:
|
||||
def _mash() -> Callable[[str], float]:
|
||||
n = float(0xEFC8249D)
|
||||
|
||||
def mash(data: str) -> float:
|
||||
@@ -90,7 +91,7 @@ def seed(source: str) -> None:
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def using(rng: Alea) -> T.Iterator[None]:
|
||||
def using(rng: Alea) -> Iterator[None]:
|
||||
"""Draw from rng instead of the shared generator inside the block."""
|
||||
global _current
|
||||
previous = _current
|
||||
@@ -101,11 +102,11 @@ def using(rng: Alea) -> T.Iterator[None]:
|
||||
_current = previous
|
||||
|
||||
|
||||
def choice(source: T.Sequence[T.Any]) -> T.Any:
|
||||
def choice(source: Sequence[Any]) -> Any:
|
||||
return source[below(len(source))]
|
||||
|
||||
|
||||
def choice_low(source: T.Sequence[T.Any]) -> T.Any:
|
||||
def choice_low(source: Sequence[Any]) -> Any:
|
||||
return source[below_low(len(source))]
|
||||
|
||||
|
||||
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
import pickle
|
||||
import typing as T
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -8,13 +7,13 @@ from pqcli.mechanic import Player
|
||||
|
||||
|
||||
class Roster:
|
||||
def __init__(self, path: Path, players: T.List[Player]) -> None:
|
||||
def __init__(self, path: Path, players: list[Player]) -> None:
|
||||
self.path = path
|
||||
self.players = players
|
||||
self._last_save = datetime.now()
|
||||
|
||||
@staticmethod
|
||||
def load(path: T.Union[str, Path]) -> "Roster":
|
||||
def load(path: str | Path) -> "Roster":
|
||||
real_path = Path(path)
|
||||
if real_path.exists():
|
||||
return Roster(
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
import abc
|
||||
import argparse
|
||||
import typing as T
|
||||
|
||||
from pqcli.game_data import GameData
|
||||
from pqcli.mechanic import Player
|
||||
@@ -11,7 +10,7 @@ class BaseUserInterface(abc.ABC):
|
||||
def __init__(
|
||||
self,
|
||||
roster: Roster,
|
||||
player: T.Optional[Player],
|
||||
player: Player | None,
|
||||
args: argparse.Namespace,
|
||||
data: GameData,
|
||||
) -> None:
|
||||
|
||||
@@ -4,8 +4,9 @@ import logging
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import typing as T
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from pqcli import lingo
|
||||
from pqcli.config import PRIME_STATS
|
||||
@@ -36,13 +37,13 @@ class BasicUserInterface(BaseUserInterface):
|
||||
def __init__(
|
||||
self,
|
||||
roster: Roster,
|
||||
player: T.Optional[Player],
|
||||
player: Player | None,
|
||||
args: argparse.Namespace,
|
||||
data: GameData,
|
||||
) -> None:
|
||||
super().__init__(roster, player, args, data)
|
||||
|
||||
def signal_handler(sig: T.Any, frame: T.Any) -> None:
|
||||
def signal_handler(sig: Any, frame: Any) -> None:
|
||||
logging.info("Quitting")
|
||||
if self.args.use_saves:
|
||||
self.roster.save()
|
||||
@@ -103,7 +104,7 @@ class BasicUserInterface(BaseUserInterface):
|
||||
self.quit()
|
||||
break
|
||||
|
||||
def create_player(self, *, auto_play: bool = True) -> T.Optional[Player]:
|
||||
def create_player(self, *, auto_play: bool = True) -> Player | None:
|
||||
name = input("Name your new character: ")
|
||||
if not name:
|
||||
print("Cancelled.")
|
||||
@@ -196,7 +197,7 @@ class BasicUserInterface(BaseUserInterface):
|
||||
if self.args.use_saves:
|
||||
self.roster.save()
|
||||
|
||||
def choose_player(self) -> T.Optional[Player]:
|
||||
def choose_player(self) -> Player | None:
|
||||
if not self.roster.players:
|
||||
print("No characters to choose from!")
|
||||
return None
|
||||
@@ -216,9 +217,9 @@ class BasicUserInterface(BaseUserInterface):
|
||||
|
||||
def menu(
|
||||
self,
|
||||
options: T.Sequence[T.Tuple[T.Any, str]],
|
||||
title: T.Optional[str] = None,
|
||||
) -> T.Any:
|
||||
options: Sequence[tuple[Any, str]],
|
||||
title: str | None = None,
|
||||
) -> Any:
|
||||
print()
|
||||
if title:
|
||||
print(f"{title}:")
|
||||
|
||||
@@ -3,7 +3,7 @@ import curses
|
||||
import os
|
||||
import signal
|
||||
import time
|
||||
import typing as T
|
||||
from typing import Any, cast
|
||||
|
||||
from pqcli import lingo
|
||||
from pqcli.config import Class, Race
|
||||
@@ -68,7 +68,7 @@ class CursesUserInterface(BaseUserInterface):
|
||||
curses.init_pair(COLOR_PROGRESSBAR, 0, 9)
|
||||
curses.init_pair(COLOR_HIGHLIGHT, 3, -1)
|
||||
|
||||
def signal_handler(sig: T.Any, frame: T.Any) -> None:
|
||||
def signal_handler(sig: Any, frame: Any) -> None:
|
||||
if self.args.use_saves:
|
||||
self.roster.save()
|
||||
exit(0)
|
||||
@@ -91,7 +91,7 @@ class CursesUserInterface(BaseUserInterface):
|
||||
self._switch_to_roster_view()
|
||||
|
||||
while True:
|
||||
key = T.cast(int, self._screen.getch())
|
||||
key = cast(int, self._screen.getch())
|
||||
|
||||
if key == curses.ERR:
|
||||
time.sleep(0.1)
|
||||
@@ -115,7 +115,7 @@ class CursesUserInterface(BaseUserInterface):
|
||||
self._switch_view(self._roster_view)
|
||||
|
||||
def _switch_to_create_char_name_view(
|
||||
self, player_name: T.Optional[str] = None
|
||||
self, player_name: str | None = None
|
||||
) -> None:
|
||||
view = ChooseCharacterNameView(self._screen, player_name)
|
||||
view.on_cancel += self._switch_to_roster_view
|
||||
@@ -123,7 +123,7 @@ class CursesUserInterface(BaseUserInterface):
|
||||
self._switch_view(view)
|
||||
|
||||
def _switch_to_create_char_race_view(
|
||||
self, player_name: str, race: T.Optional[Race] = None
|
||||
self, player_name: str, race: Race | None = None
|
||||
) -> None:
|
||||
view = ChooseCharacterRaceView(self._screen, self.data.races, race)
|
||||
view.on_cancel += lambda: self._switch_to_create_char_name_view(
|
||||
@@ -135,7 +135,7 @@ class CursesUserInterface(BaseUserInterface):
|
||||
self._switch_view(view)
|
||||
|
||||
def _switch_to_create_char_class_view(
|
||||
self, player_name: str, race: Race, class_: T.Optional[Class] = None
|
||||
self, player_name: str, race: Race, class_: Class | None = None
|
||||
) -> None:
|
||||
view = ChooseCharacterClassView(
|
||||
self._screen, self.data.classes, class_
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import typing as T
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
|
||||
class EventHandler:
|
||||
def __init__(self) -> None:
|
||||
self.callbacks: T.List[T.Callable[..., T.Any]] = []
|
||||
self.callbacks: list[Callable[..., Any]] = []
|
||||
|
||||
def __call__(self, *args: T.Any, **kwargs: T.Any) -> None:
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> None:
|
||||
for callback in self.callbacks:
|
||||
callback(*args, **kwargs)
|
||||
|
||||
def __iadd__(self, callback: T.Callable[..., T.Any]) -> "EventHandler":
|
||||
def __iadd__(self, callback: Callable[..., Any]) -> "EventHandler":
|
||||
self.callbacks.append(callback)
|
||||
return self
|
||||
+17
-4
@@ -1,7 +1,8 @@
|
||||
import curses
|
||||
import curses.ascii
|
||||
import typing as T
|
||||
from collections.abc import Callable, Iterable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, TypeVar, overload
|
||||
|
||||
KEYS_CYCLE = {curses.ascii.TAB}
|
||||
KEYS_DOWN = set(map(ord, "jJ")) | {curses.KEY_DOWN}
|
||||
@@ -13,10 +14,22 @@ KEYS_CANCEL = set(map(ord, "qQ")) | {curses.ascii.ESC}
|
||||
|
||||
@dataclass
|
||||
class Choice:
|
||||
keys: T.List[int]
|
||||
keys: list[int]
|
||||
desc: str
|
||||
callback: T.Callable[..., T.Any]
|
||||
callback: Callable[..., Any]
|
||||
|
||||
|
||||
def first(source: T.Iterable[T.Any], default: T.Any = None) -> T.Any:
|
||||
ItemT = TypeVar("ItemT")
|
||||
DefaultT = TypeVar("DefaultT")
|
||||
|
||||
|
||||
@overload
|
||||
def first(source: Iterable[ItemT]) -> ItemT | None: ...
|
||||
|
||||
|
||||
@overload
|
||||
def first(source: Iterable[ItemT], default: DefaultT) -> ItemT | DefaultT: ...
|
||||
|
||||
|
||||
def first(source: Iterable[Any], default: Any = None) -> Any:
|
||||
return next(iter(source), default)
|
||||
@@ -1,8 +1,8 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
|
||||
class BaseView:
|
||||
def __init__(self, screen: T.Any) -> None:
|
||||
def __init__(self, screen: Any) -> None:
|
||||
self.screen = screen
|
||||
|
||||
def start(self) -> None:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import functools
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.lingo import act_name, to_roman
|
||||
from pqcli.roster import Roster
|
||||
@@ -8,11 +8,11 @@ from pqcli.ui.curses.views.menu_view import MenuView
|
||||
|
||||
|
||||
class ChooseCharacterView(MenuView):
|
||||
def __init__(self, screen: T.Any, roster: Roster, title: str) -> None:
|
||||
def __init__(self, screen: Any, roster: Roster, title: str) -> None:
|
||||
super().__init__(screen, title)
|
||||
|
||||
for y, player in enumerate(roster.players, 1):
|
||||
key: T.Optional[str] = str(y % 10) if y <= 10 else None
|
||||
key: str | None = str(y % 10) if y <= 10 else None
|
||||
|
||||
best_equip = player.equipment.best
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
from pqcli.ui.curses.util import Choice
|
||||
@@ -8,14 +8,14 @@ from .base_view import BaseView
|
||||
|
||||
|
||||
class ConfirmView(BaseView):
|
||||
def __init__(self, screen: T.Any, title: str) -> None:
|
||||
def __init__(self, screen: Any, title: str) -> None:
|
||||
super().__init__(screen)
|
||||
|
||||
self.title = f"{title}:"
|
||||
self.on_cancel = EventHandler()
|
||||
self.on_confirm = EventHandler()
|
||||
|
||||
self.choices: T.List[Choice] = [
|
||||
self.choices: list[Choice] = [
|
||||
Choice(
|
||||
keys=list(map(ord, "yY")),
|
||||
desc="[Y] Yes",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import functools
|
||||
import typing as T
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from pqcli.config import Class
|
||||
from pqcli.ui.curses.util import KEYS_CANCEL, Choice
|
||||
@@ -9,9 +10,9 @@ from pqcli.ui.curses.views.menu_view import MenuView
|
||||
class ChooseCharacterClassView(MenuView):
|
||||
def __init__(
|
||||
self,
|
||||
screen: T.Any,
|
||||
classes: T.Sequence[Class],
|
||||
class_: T.Optional[Class] = None,
|
||||
screen: Any,
|
||||
classes: Sequence[Class],
|
||||
class_: Class | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
screen,
|
||||
@@ -20,7 +21,7 @@ class ChooseCharacterClassView(MenuView):
|
||||
)
|
||||
|
||||
for y, class_ in enumerate(classes, 1):
|
||||
key: T.Optional[str] = str(y % 10) if y <= 10 else None
|
||||
key: str | None = str(y % 10) if y <= 10 else None
|
||||
self._choices.append(
|
||||
Choice(
|
||||
keys=[ord(key)] if key is not None else [],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import curses
|
||||
import curses.ascii
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import generate_name
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
@@ -10,17 +10,15 @@ from pqcli.ui.curses.widgets.focusable import focus_standout
|
||||
|
||||
|
||||
class ChooseCharacterNameView(BaseView):
|
||||
def __init__(
|
||||
self, screen: T.Any, character_name: T.Optional[str] = None
|
||||
) -> None:
|
||||
def __init__(self, screen: Any, character_name: str | None = None) -> None:
|
||||
super().__init__(screen)
|
||||
|
||||
self.on_cancel = EventHandler()
|
||||
self.on_confirm = EventHandler()
|
||||
|
||||
self._active_widget = 0
|
||||
self._win: T.Optional[T.Any] = None
|
||||
self._text_win: T.Optional[T.Any] = None
|
||||
self._win: Any | None = None
|
||||
self._text_win: Any | None = None
|
||||
self._text = character_name or generate_name()
|
||||
|
||||
def start(self) -> None:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import functools
|
||||
import typing as T
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from pqcli.config import Race
|
||||
from pqcli.ui.curses.util import KEYS_CANCEL, Choice
|
||||
@@ -9,9 +10,9 @@ from pqcli.ui.curses.views.menu_view import MenuView
|
||||
class ChooseCharacterRaceView(MenuView):
|
||||
def __init__(
|
||||
self,
|
||||
screen: T.Any,
|
||||
races: T.Sequence[Race],
|
||||
race: T.Optional[Race] = None,
|
||||
screen: Any,
|
||||
races: Sequence[Race],
|
||||
race: Race | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
screen,
|
||||
@@ -20,7 +21,7 @@ class ChooseCharacterRaceView(MenuView):
|
||||
)
|
||||
|
||||
for y, race in enumerate(races, 1):
|
||||
key: T.Optional[str] = str(y % 10) if y <= 10 else None
|
||||
key: str | None = str(y % 10) if y <= 10 else None
|
||||
self._choices.append(
|
||||
Choice(
|
||||
keys=[ord(key)] if key is not None else [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses.ascii
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.config import PRIME_STATS
|
||||
from pqcli.mechanic import StatsBuilder
|
||||
@@ -10,15 +10,15 @@ from pqcli.ui.curses.widgets.focusable import focus_standout
|
||||
|
||||
|
||||
class ChooseCharacterStatsView(BaseView):
|
||||
def __init__(self, screen: T.Any) -> None:
|
||||
def __init__(self, screen: Any) -> None:
|
||||
super().__init__(screen)
|
||||
|
||||
self.on_cancel = EventHandler()
|
||||
self.on_confirm = EventHandler()
|
||||
|
||||
self._active_widget = 0
|
||||
self._win: T.Optional[T.Any] = None
|
||||
self._stats_win: T.Optional[T.Any] = None
|
||||
self._win: Any | None = None
|
||||
self._stats_win: Any | None = None
|
||||
|
||||
self._stats_builder = StatsBuilder()
|
||||
self._stats = self._stats_builder.roll()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import argparse
|
||||
import curses
|
||||
import datetime
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.game_data import GameData
|
||||
from pqcli.mechanic import Player, SignalMixin, Simulation
|
||||
@@ -30,7 +30,7 @@ from .task_progress_window import TaskProgressWindow
|
||||
class GameView(BaseView):
|
||||
def __init__(
|
||||
self,
|
||||
screen: T.Any,
|
||||
screen: Any,
|
||||
roster: Roster,
|
||||
player: Player,
|
||||
args: argparse.Namespace,
|
||||
@@ -225,7 +225,7 @@ class GameView(BaseView):
|
||||
x=0,
|
||||
)
|
||||
|
||||
self._focusable_children: T.List[Focusable] = [
|
||||
self._focusable_children: list[Focusable] = [
|
||||
self._char_sheet_win,
|
||||
self._spell_book_win,
|
||||
self._equipment_win,
|
||||
@@ -233,7 +233,7 @@ class GameView(BaseView):
|
||||
self._plot_win,
|
||||
self._quest_book_win,
|
||||
]
|
||||
self._children: T.List[Widget] = self._focusable_children + [
|
||||
self._children: list[Widget] = self._focusable_children + [
|
||||
self._task_win
|
||||
]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import Player, StatType
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
@@ -8,7 +8,7 @@ from .progress_bar_window import DataTableProgressBarWindow
|
||||
|
||||
class CharacterSheetWindow(Focusable, DataTableProgressBarWindow):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(
|
||||
parent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import EquipmentType, Player
|
||||
from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
|
||||
@@ -6,7 +6,7 @@ from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
|
||||
|
||||
class EquipmentWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._on_focus_change += self._render
|
||||
@@ -39,7 +39,7 @@ class EquipmentWindow(Focusable, WindowWrapper):
|
||||
self._render()
|
||||
|
||||
def _sync_equipment_change(
|
||||
self, equipment_type: EquipmentType, item_name: T.Optional[str]
|
||||
self, equipment_type: EquipmentType, item_name: str | None
|
||||
) -> None:
|
||||
self._data_table.set(equipment_type.value, item_name or "")
|
||||
self._data_table.select(equipment_type.value)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import InventoryItem, Player
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
@@ -8,7 +8,7 @@ from .progress_bar_window import DataTableProgressBarWindow
|
||||
|
||||
class InventoryWindow(Focusable, DataTableProgressBarWindow):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(
|
||||
parent,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.lingo import act_name
|
||||
from pqcli.mechanic import Player
|
||||
@@ -11,7 +11,7 @@ class PlotWindow(Focusable, ListBoxProgressBarWindow):
|
||||
cutoff = 100
|
||||
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(
|
||||
parent, h, w, y, x, " Plot Development ", show_time=True
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.widgets import (
|
||||
DataTable,
|
||||
@@ -13,7 +13,7 @@ from pqcli.ui.curses.widgets import (
|
||||
class ProgressBarWindow(WindowWrapper):
|
||||
def __init__(
|
||||
self,
|
||||
parent: T.Any,
|
||||
parent: Any,
|
||||
h: int,
|
||||
w: int,
|
||||
y: int,
|
||||
@@ -82,7 +82,7 @@ class ProgressBarWindow(WindowWrapper):
|
||||
class DataTableProgressBarWindow(ProgressBarWindow):
|
||||
def __init__(
|
||||
self,
|
||||
parent: T.Any,
|
||||
parent: Any,
|
||||
h: int,
|
||||
w: int,
|
||||
y: int,
|
||||
@@ -123,7 +123,7 @@ class DataTableProgressBarWindow(ProgressBarWindow):
|
||||
class ListBoxProgressBarWindow(ProgressBarWindow):
|
||||
def __init__(
|
||||
self,
|
||||
parent: T.Any,
|
||||
parent: Any,
|
||||
h: int,
|
||||
w: int,
|
||||
y: int,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
@@ -10,7 +10,7 @@ class QuestBookWindow(Focusable, ListBoxProgressBarWindow):
|
||||
cutoff = 100
|
||||
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x, " Quests ", show_time=True)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.lingo import to_roman
|
||||
from pqcli.mechanic import Player, Spell
|
||||
@@ -7,7 +7,7 @@ from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
|
||||
|
||||
class SpellBookWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import (
|
||||
@@ -11,7 +11,7 @@ from pqcli.ui.curses.widgets import (
|
||||
|
||||
class TaskProgressWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
self, player: Player, parent: Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
from pqcli.ui.curses.util import Choice
|
||||
@@ -8,7 +8,7 @@ from pqcli.ui.curses.widgets import Menu
|
||||
|
||||
class MenuView(BaseView):
|
||||
def __init__(
|
||||
self, screen: T.Any, title: str, active_choice: int = 0
|
||||
self, screen: Any, title: str, active_choice: int = 0
|
||||
) -> None:
|
||||
super().__init__(screen)
|
||||
|
||||
@@ -16,7 +16,7 @@ class MenuView(BaseView):
|
||||
self.on_confirm = EventHandler()
|
||||
|
||||
self._title = f"{title}:"
|
||||
self._choices: T.List[Choice] = []
|
||||
self._choices: list[Choice] = []
|
||||
self._active_choice = active_choice
|
||||
|
||||
def start(self) -> None:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_LOGO, COLOR_LOGO_ALT, has_colors
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
@@ -29,7 +29,7 @@ LOGO = """
|
||||
|
||||
class MainMenu(Menu):
|
||||
def __init__(
|
||||
self, choices: T.List[Choice], scr_height: int, scr_width: int
|
||||
self, choices: list[Choice], scr_height: int, scr_width: int
|
||||
) -> None:
|
||||
super().__init__(
|
||||
header=LOGO,
|
||||
@@ -50,7 +50,7 @@ class MainMenu(Menu):
|
||||
|
||||
|
||||
class RosterView(BaseView):
|
||||
def __init__(self, screen: T.Any) -> None:
|
||||
def __init__(self, screen: Any) -> None:
|
||||
super().__init__(screen)
|
||||
|
||||
self.on_create = EventHandler()
|
||||
@@ -58,7 +58,7 @@ class RosterView(BaseView):
|
||||
self.on_delete = EventHandler()
|
||||
self.on_quit = EventHandler()
|
||||
|
||||
self.choices: T.List[Choice] = [
|
||||
self.choices: list[Choice] = [
|
||||
Choice(
|
||||
keys=list(map(ord, "cC")),
|
||||
desc="[C] Create new character",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Widget:
|
||||
@@ -12,10 +12,10 @@ class Widget:
|
||||
|
||||
class WindowWrapper(Widget):
|
||||
def __init__(
|
||||
self, parent: T.Optional[T.Any], h: int, w: int, y: int, x: int
|
||||
self, parent: Any | None, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
self._parent = parent
|
||||
self._win: T.Optional[T.Any]
|
||||
self._win: Any | None
|
||||
|
||||
if not parent:
|
||||
self._win = None
|
||||
@@ -30,12 +30,12 @@ class WindowWrapper(Widget):
|
||||
del self._win
|
||||
self._win = None
|
||||
|
||||
def getbegyx(self) -> T.Tuple[int, int]:
|
||||
def getbegyx(self) -> tuple[int, int]:
|
||||
if not self._win:
|
||||
return (0, 0)
|
||||
return self._win.getbegyx()
|
||||
|
||||
def getmaxyx(self) -> T.Tuple[int, int]:
|
||||
def getmaxyx(self) -> tuple[int, int]:
|
||||
if not self._win:
|
||||
return (0, 0)
|
||||
return self._win.getmaxyx()
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
|
||||
|
||||
@@ -9,7 +9,7 @@ from .scrollable import Scrollable
|
||||
class DataTable(Scrollable):
|
||||
def __init__(
|
||||
self,
|
||||
parent: T.Any,
|
||||
parent: Any,
|
||||
h: int,
|
||||
w: int,
|
||||
y: int,
|
||||
@@ -19,8 +19,8 @@ class DataTable(Scrollable):
|
||||
super().__init__(parent, h, w, y, x)
|
||||
|
||||
self._align_right = align_right
|
||||
self._items: T.List[T.Tuple[str, str]]
|
||||
self._selected: T.Optional[int] = None
|
||||
self._items: list[tuple[str, str]]
|
||||
self._selected: int | None = None
|
||||
|
||||
def add(self, text: str, value: str) -> None:
|
||||
self._items.append((text, value))
|
||||
@@ -40,10 +40,10 @@ class DataTable(Scrollable):
|
||||
del self._items[idx]
|
||||
self.scroll_to_item(idx)
|
||||
|
||||
def select(self, text: T.Optional[str]) -> None:
|
||||
def select(self, text: str | None) -> None:
|
||||
self._selected = None if text is None else self.get_idx(text)
|
||||
|
||||
def get_idx(self, text: str) -> T.Optional[int]:
|
||||
def get_idx(self, text: str) -> int | None:
|
||||
for idx, row in enumerate(self._items):
|
||||
if row[0].strip() == text.strip():
|
||||
return idx
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import contextlib
|
||||
import curses
|
||||
import typing as T
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_FOCUSED, has_colors
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def focus_standout(focused: bool, win: T.Any) -> T.Generator:
|
||||
def focus_standout(focused: bool, win: Any) -> Generator:
|
||||
if focused:
|
||||
if has_colors():
|
||||
win.attron(curses.color_pair(COLOR_FOCUSED))
|
||||
@@ -22,7 +23,7 @@ def focus_standout(focused: bool, win: T.Any) -> T.Generator:
|
||||
|
||||
|
||||
class Focusable:
|
||||
def __init__(self, *args: T.Any, **kwargs: T.Any) -> None:
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
self._focused = False
|
||||
self._on_focus_change = EventHandler()
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -37,6 +38,6 @@ class Focusable:
|
||||
self._on_focus_change()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def focus_standout(self, win: T.Any) -> T.Generator:
|
||||
def focus_standout(self, win: Any) -> Generator:
|
||||
with focus_standout(self._focused, win):
|
||||
yield
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
|
||||
|
||||
@@ -7,11 +7,11 @@ from .scrollable import Scrollable
|
||||
|
||||
|
||||
class ListBox(Scrollable):
|
||||
_items: T.List[str]
|
||||
_items: list[str]
|
||||
|
||||
def __init__(self, parent: T.Any, h: int, w: int, y: int, x: int) -> None:
|
||||
def __init__(self, parent: Any, h: int, w: int, y: int, x: int) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._selected: T.Optional[int] = None
|
||||
self._selected: int | None = None
|
||||
|
||||
def add(self, text: str) -> None:
|
||||
self._items.append(text)
|
||||
@@ -22,7 +22,7 @@ class ListBox(Scrollable):
|
||||
idx %= len(self._items)
|
||||
self._items[idx] = value
|
||||
|
||||
def get(self, idx: int) -> T.Optional[str]:
|
||||
def get(self, idx: int) -> str | None:
|
||||
if not self._items:
|
||||
return None
|
||||
if idx < 0:
|
||||
@@ -35,7 +35,7 @@ class ListBox(Scrollable):
|
||||
def delete(self, idx: int, count: int = 1) -> None:
|
||||
del self._items[idx : idx + count]
|
||||
|
||||
def select(self, idx: T.Optional[int]) -> None:
|
||||
def select(self, idx: int | None) -> None:
|
||||
if idx < 0 and len(self._items):
|
||||
idx %= len(self._items)
|
||||
self._selected = idx
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import curses
|
||||
import curses.ascii
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.util import KEYS_CYCLE, KEYS_DOWN, KEYS_UP, Choice
|
||||
from pqcli.ui.curses.widgets.focusable import focus_standout
|
||||
@@ -12,7 +12,7 @@ class Menu(Widget):
|
||||
def __init__(
|
||||
self,
|
||||
header: str,
|
||||
choices: T.List[Choice],
|
||||
choices: list[Choice],
|
||||
active_choice: int,
|
||||
scr_height: int,
|
||||
scr_width: int,
|
||||
@@ -28,7 +28,7 @@ class Menu(Widget):
|
||||
|
||||
w = max(map(len, all_lines)) + 1
|
||||
h = len(all_lines) + 1
|
||||
self._pad: T.Optional[T.Any] = curses.newpad(h, w)
|
||||
self._pad: Any | None = curses.newpad(h, w)
|
||||
|
||||
for y, line in enumerate(self._header_lines):
|
||||
self._pad.move(y, 0)
|
||||
@@ -40,7 +40,7 @@ class Menu(Widget):
|
||||
del self._pad
|
||||
self._pad = None
|
||||
|
||||
def getmaxyx(self) -> T.Tuple[int, int]:
|
||||
def getmaxyx(self) -> tuple[int, int]:
|
||||
if not self._pad:
|
||||
return (0, 0)
|
||||
return self._pad.getmaxyx()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import curses
|
||||
import datetime
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.lingo import format_timespan
|
||||
from pqcli.ui.curses.colors import COLOR_PROGRESSBAR, has_colors
|
||||
@@ -9,7 +9,7 @@ from pqcli.ui.curses.widgets.base import WindowWrapper
|
||||
|
||||
class ProgressBar(WindowWrapper):
|
||||
def __init__(
|
||||
self, parent: T.Any, h: int, w: int, y: int, x: int, show_time: bool
|
||||
self, parent: Any, h: int, w: int, y: int, x: int, show_time: bool
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
|
||||
@@ -17,10 +17,10 @@ class ProgressBar(WindowWrapper):
|
||||
self._max_pos = 1.0
|
||||
|
||||
self._show_time = show_time
|
||||
self._last_tick: T.Optional[T.Tuple[datetime.datetime, float]] = None
|
||||
self._last_tick: tuple[datetime.datetime, float] | None = None
|
||||
|
||||
@property
|
||||
def time_left(self) -> T.Optional[datetime.timedelta]:
|
||||
def time_left(self) -> datetime.timedelta | None:
|
||||
if self._last_tick is None:
|
||||
return None
|
||||
time_then, pos_then = self._last_tick
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import curses
|
||||
import typing as T
|
||||
from typing import Any
|
||||
|
||||
from pqcli.ui.curses.colors import (
|
||||
COLOR_SCROLLBAR_THUMB,
|
||||
@@ -11,11 +11,11 @@ from .base import WindowWrapper
|
||||
|
||||
|
||||
class Scrollable(WindowWrapper):
|
||||
def __init__(self, parent: T.Any, h: int, w: int, y: int, x: int) -> None:
|
||||
def __init__(self, parent: Any, h: int, w: int, y: int, x: int) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._scroll_pos = 0
|
||||
self._pad: T.Optional[T.Any] = curses.newpad(1, 1)
|
||||
self._items: T.List[T.Any] = []
|
||||
self._pad: Any | None = curses.newpad(1, 1)
|
||||
self._items: list[Any] = []
|
||||
|
||||
def clear(self) -> None:
|
||||
self._items = []
|
||||
|
||||
Reference in new issue
Block a user