mirror of
https://github.com/rr-/pq-cli.git
synced 2026-09-29 06:44:00 -06:00
Compare commits
9
Commits
1.0
...
cli_player
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef9a7f9e20 | ||
|
|
1ad563ad93 | ||
|
|
29207c1d4c | ||
|
|
67c3888f82 | ||
|
|
d4675c747a | ||
|
|
c5f0c60ec8 | ||
|
|
fc4dcff9e3 | ||
|
|
f8a030a648 | ||
|
|
82c8d21395 |
@@ -41,3 +41,14 @@ $ git clone https://github.com/rr-/pq-cli.git
|
||||
$ cd pq-cli
|
||||
$ pip install --user .
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `_curses.error: init_pair() returned ERR`
|
||||
|
||||
If running on Linux and you get the error `_curses.error: init_pair() returned ERR`,
|
||||
try making sure that your `$TERM` variable is set to a value which supports
|
||||
256 colors, such as via the following:
|
||||
|
||||
TERM=xterm-256color pqcli
|
||||
|
||||
|
||||
+33
-1
@@ -1,8 +1,11 @@
|
||||
import argparse
|
||||
import sys
|
||||
import typing as T
|
||||
from pathlib import Path
|
||||
|
||||
import xdg
|
||||
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.roster import Roster
|
||||
from pqcli.ui.basic import BasicUserInterface
|
||||
from pqcli.ui.curses import CursesUserInterface
|
||||
@@ -44,15 +47,44 @@ def parse_args() -> argparse.Namespace:
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument("--cheats", action="store_true", help="???")
|
||||
parser.add_argument(
|
||||
"--list-saves",
|
||||
action="store_true",
|
||||
help="list saved characters and exit",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-save",
|
||||
type=int,
|
||||
metavar="NUM",
|
||||
help="play chosen character",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def list_players(roster: Roster, file: T.Optional[T.Any] = None) -> None:
|
||||
for i, player in enumerate(roster.players, start=1):
|
||||
print(f"{i}. {player.name}", file=file)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
roster = Roster.load(SAVE_PATH)
|
||||
|
||||
if args.list_saves:
|
||||
list_players(roster)
|
||||
exit(0)
|
||||
|
||||
player: T.Optional[Player] = None
|
||||
if args.load_save:
|
||||
try:
|
||||
ui = args.ui(roster, args)
|
||||
player = list(roster.players)[args.load_save - 1]
|
||||
except IndexError:
|
||||
print(f"Invalid player. Available players:", file=sys.stderr)
|
||||
list_players(roster, file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
ui = args.ui(roster, player, args)
|
||||
ui.run()
|
||||
finally:
|
||||
if args.use_saves:
|
||||
|
||||
@@ -29,6 +29,8 @@ class Roster:
|
||||
tmp_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp_path.write_bytes(pickle.dumps(self.players))
|
||||
if self.path.exists():
|
||||
if old_path.exists():
|
||||
old_path.unlink()
|
||||
self.path.rename(old_path)
|
||||
tmp_path.rename(self.path)
|
||||
|
||||
|
||||
+9
-1
@@ -1,13 +1,21 @@
|
||||
import abc
|
||||
import argparse
|
||||
import typing as T
|
||||
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.roster import Roster
|
||||
|
||||
|
||||
class BaseUserInterface(abc.ABC):
|
||||
def __init__(self, roster: Roster, args: argparse.Namespace) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
roster: Roster,
|
||||
player: T.Optional[Player],
|
||||
args: argparse.Namespace,
|
||||
) -> None:
|
||||
self.roster = roster
|
||||
self.args = args
|
||||
self.player = player
|
||||
|
||||
@abc.abstractmethod
|
||||
def run(self) -> None:
|
||||
|
||||
@@ -32,8 +32,13 @@ class MainMenu(enum.IntEnum):
|
||||
|
||||
|
||||
class BasicUserInterface(BaseUserInterface):
|
||||
def __init__(self, roster: Roster, args: argparse.Namespace) -> None:
|
||||
super().__init__(roster, args)
|
||||
def __init__(
|
||||
self,
|
||||
roster: Roster,
|
||||
player: T.Optional[Player],
|
||||
args: argparse.Namespace,
|
||||
) -> None:
|
||||
super().__init__(roster, player, args)
|
||||
|
||||
def signal_handler(sig: T.Any, frame: T.Any) -> None:
|
||||
logging.info("Quitting")
|
||||
@@ -51,6 +56,9 @@ class BasicUserInterface(BaseUserInterface):
|
||||
)
|
||||
|
||||
self.logo()
|
||||
if self.player:
|
||||
self.play(self.player)
|
||||
else:
|
||||
self.main_menu()
|
||||
|
||||
def logo(self) -> None:
|
||||
|
||||
@@ -39,8 +39,10 @@ class StopMainLoop(Exception):
|
||||
|
||||
|
||||
class CursesUserInterface(BaseUserInterface):
|
||||
def __init__(self, roster: Roster, args: argparse.Namespace) -> None:
|
||||
super().__init__(roster, args)
|
||||
def __init__(
|
||||
self, roster: Roster, player: Player, args: argparse.Namespace
|
||||
) -> None:
|
||||
super().__init__(roster, player, args)
|
||||
os.environ.setdefault("ESCDELAY", "25")
|
||||
self._screen = curses.initscr()
|
||||
self._screen.nodelay(True)
|
||||
@@ -77,6 +79,9 @@ class CursesUserInterface(BaseUserInterface):
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
if self.player:
|
||||
self._switch_to_game_view(self.player)
|
||||
else:
|
||||
self._switch_to_roster_view()
|
||||
|
||||
while True:
|
||||
@@ -130,9 +135,11 @@ class CursesUserInterface(BaseUserInterface):
|
||||
view.on_cancel += lambda: self._switch_to_create_char_race_view(
|
||||
player_name, race
|
||||
)
|
||||
view.on_confirm += lambda class_: self._switch_to_create_char_stats_view(
|
||||
view.on_confirm += (
|
||||
lambda class_: self._switch_to_create_char_stats_view(
|
||||
player_name, race, class_
|
||||
)
|
||||
)
|
||||
self._switch_view(view)
|
||||
|
||||
def _switch_to_create_char_stats_view(
|
||||
|
||||
@@ -3,10 +3,8 @@ import typing as T
|
||||
|
||||
from pqcli.lingo import act_name, to_roman
|
||||
from pqcli.roster import Roster
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
from pqcli.ui.curses.util import KEYS_CANCEL, Choice
|
||||
from pqcli.ui.curses.views.menu_view import MenuView
|
||||
from pqcli.ui.curses.widgets import Menu
|
||||
|
||||
|
||||
class ChooseCharacterView(MenuView):
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import curses
|
||||
import typing as T
|
||||
|
||||
from pqcli.lingo import to_roman
|
||||
from pqcli.mechanic import InventoryItem, Player
|
||||
from pqcli.ui.curses.widgets import (
|
||||
DataTable,
|
||||
Focusable,
|
||||
ProgressBar,
|
||||
WindowWrapper,
|
||||
)
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
|
||||
from .progress_bar_window import DataTableProgressBarWindow
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import typing as T
|
||||
|
||||
from pqcli.lingo import act_name
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import Focusable, ListBox, WindowWrapper
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
|
||||
from .progress_bar_window import ListBoxProgressBarWindow
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import typing as T
|
||||
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import Focusable, ListBox, WindowWrapper
|
||||
from pqcli.ui.curses.widgets import Focusable
|
||||
|
||||
from .progress_bar_window import ListBoxProgressBarWindow
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import functools
|
||||
import typing as T
|
||||
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import curses
|
||||
import enum
|
||||
import typing as T
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import curses
|
||||
import enum
|
||||
import typing as T
|
||||
|
||||
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
|
||||
@@ -37,7 +36,7 @@ class ListBox(Scrollable):
|
||||
del self._items[idx : idx + count]
|
||||
|
||||
def select(self, idx: T.Optional[int]) -> None:
|
||||
if idx < 0:
|
||||
if idx < 0 and len(self._items):
|
||||
idx %= len(self._items)
|
||||
self._selected = idx
|
||||
|
||||
|
||||
@@ -25,7 +25,10 @@ class ProgressBar(WindowWrapper):
|
||||
return None
|
||||
time_then, pos_then = self._last_tick
|
||||
time_now, pos_now = datetime.datetime.now(), self._cur_pos
|
||||
speed = (pos_now - pos_then) / (time_now - time_then).total_seconds()
|
||||
div = (time_now - time_then).total_seconds()
|
||||
if div == 0:
|
||||
return None
|
||||
speed = (pos_now - pos_then) / div
|
||||
if not speed:
|
||||
return None
|
||||
return datetime.timedelta(seconds=(self._max_pos - pos_now) / speed)
|
||||
|
||||
@@ -5,12 +5,17 @@ setup(
|
||||
author_email="rr-@sakuya.pl",
|
||||
name="pqcli",
|
||||
long_description="Progress Quest: the CLI edition",
|
||||
version="1.0",
|
||||
version="1.0.2",
|
||||
url="https://github.com/rr-/pq-cli.git",
|
||||
packages=find_packages(),
|
||||
entry_points={"console_scripts": ["pqcli = pqcli.__main__:main"]},
|
||||
package_dir={"pqcli": "pqcli"},
|
||||
install_requires=["xdg", "urwid", "urwid_readline"],
|
||||
install_requires=[
|
||||
"xdg",
|
||||
"urwid",
|
||||
"urwid_readline",
|
||||
'windows-curses;platform_system=="Windows"',
|
||||
],
|
||||
classifiers=[
|
||||
"Environment :: Console",
|
||||
"Development Status :: 4 - Beta",
|
||||
|
||||
Reference in New Issue
Block a user