Initial commit with a mostly working engine.
Basic commands are being parsed. I think the only weird part is the 'use' command because it needs to possibly target two things. A tiny test example is provided in __main__, this will probably be broken out later. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
294
agame/action.py
Normal file
294
agame/action.py
Normal file
@@ -0,0 +1,294 @@
|
||||
import dataclasses
|
||||
import time
|
||||
from typing import Any, Optional, Sequence, Union, TYPE_CHECKING
|
||||
import enum
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agame.game import Game
|
||||
|
||||
|
||||
__all__ = (
|
||||
"Action",
|
||||
"SleepAction",
|
||||
"PrintAction",
|
||||
"TeleportAction",
|
||||
"GetAction",
|
||||
"CheckInvItemsAction",
|
||||
"CheckRoomItemsAction",
|
||||
"Compare",
|
||||
"Var",
|
||||
"CheckVarAction",
|
||||
"SetVarAction",
|
||||
)
|
||||
|
||||
|
||||
class Action:
|
||||
"""
|
||||
An action that the game engine can take.
|
||||
|
||||
This is the base class. It can be instantiated as a "dummy" action if
|
||||
needed.
|
||||
"""
|
||||
|
||||
def act(self, game: "Game"):
|
||||
"""
|
||||
Complete this action.
|
||||
"""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SleepAction(Action):
|
||||
"""
|
||||
An action that delays the amount of time, in seconds. Decimal values are
|
||||
allowed.
|
||||
"""
|
||||
|
||||
secs: float
|
||||
|
||||
def act(self, game: "Game"):
|
||||
time.sleep(self.secs)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class PrintAction(Action):
|
||||
"""
|
||||
Prints a message to the screen.
|
||||
"""
|
||||
|
||||
lines: Sequence[str]
|
||||
|
||||
def __init__(self, *lines: str):
|
||||
self.lines = lines
|
||||
|
||||
def act(self, game: "Game"):
|
||||
if not self.lines:
|
||||
return
|
||||
line = self.lines[0]
|
||||
game.say(line)
|
||||
for line in self.lines[1:]:
|
||||
game.say()
|
||||
game.say(line)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class TeleportAction(Action):
|
||||
"""
|
||||
Moves the player to another room.
|
||||
"""
|
||||
|
||||
room_id: str
|
||||
|
||||
def act(self, game: "Game"):
|
||||
# TODO
|
||||
raise NotImplementedError("TODO - implement teleport action")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class GetAction(Action):
|
||||
"""
|
||||
Removes an item from the current room and puts it in the player's inventory.
|
||||
"""
|
||||
|
||||
item_id: str
|
||||
pickup_text: Optional[str] = None
|
||||
|
||||
# def __init__(self, item_id: str, pickup_text: )
|
||||
|
||||
def act(self, game: "Game"):
|
||||
# Find the first instance of the item in the room and remove it
|
||||
item = game.room.remove(self.item_id)
|
||||
assert (
|
||||
item is not None
|
||||
), f"attempted to remove an item (id: {self.item_id}) that does not exist in the current room; this is a game logic error/bug"
|
||||
|
||||
if item.id in game.inventory:
|
||||
# Item in inventory already? Update count
|
||||
game.inventory[item.id].count += item.count
|
||||
else:
|
||||
# Otherwise just add it
|
||||
game.inventory[item.id] = item
|
||||
|
||||
# Print text
|
||||
if self.pickup_text is None:
|
||||
game.say(f"You pick up (({item.name})).")
|
||||
else:
|
||||
game.say(self.pickup_text)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CheckInvItemsAction(Action):
|
||||
"""
|
||||
Checks if all supplied items are present in the inventory, and executes the
|
||||
appropriate action sequence.
|
||||
"""
|
||||
|
||||
item_ids: Union[str, Sequence[str]]
|
||||
yes: Sequence[Action]
|
||||
no: Sequence[Action]
|
||||
|
||||
def act(self, game: "Game"):
|
||||
# If the item_ids are just a string, use that as a list.
|
||||
items = [self.item_ids] if isinstance(self.item_ids, str) else self.item_ids
|
||||
for item_id in items:
|
||||
if item_id not in game.inventory:
|
||||
game.do_actions(self.no)
|
||||
return
|
||||
game.do_actions(self.yes)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CheckRoomItemsAction(Action):
|
||||
"""
|
||||
Checks if all supplied items are present in the current room, and executes the
|
||||
appropriate action sequence.
|
||||
"""
|
||||
|
||||
item_ids: str
|
||||
yes: Sequence[Action]
|
||||
no: Sequence[Action]
|
||||
|
||||
def act(self, game: "Game"):
|
||||
items = [self.item_ids] if isinstance(self.item_ids, str) else self.item_ids
|
||||
for item_id in items:
|
||||
if item_id not in game.room.items:
|
||||
game.do_actions(self.no)
|
||||
return
|
||||
game.do_actions(self.yes)
|
||||
|
||||
|
||||
class Compare(enum.Enum):
|
||||
"""
|
||||
A comparison for a value.
|
||||
"""
|
||||
|
||||
# Does what it says on the tin.
|
||||
#
|
||||
# This is type-sensitive and literally just does `==` in Python. Seriously.
|
||||
EQUALS = enum.auto()
|
||||
|
||||
# Also does what it says on the tin.
|
||||
#
|
||||
# This is type-sensitive and literally just does `!=` in Python. Seriously.
|
||||
NOT_EQUALS = enum.auto()
|
||||
|
||||
# Checks if a value is less than to another value.
|
||||
#
|
||||
# This uses the `cmp` to check the values.
|
||||
LESS_THAN = enum.auto()
|
||||
|
||||
# Checks if a value is less than or equal to another value.
|
||||
#
|
||||
# This uses the `cmp` to check the values.
|
||||
LESS_THAN_EQUALS = enum.auto()
|
||||
|
||||
# Checks if a value is greater than to another value.
|
||||
#
|
||||
# This uses the `cmp` to check the values.
|
||||
GREATER_THAN = enum.auto()
|
||||
|
||||
# Checks if a value is greater than or equal to another value.
|
||||
#
|
||||
# This uses the `cmp` to check the values.
|
||||
GREATER_THAN_EQUALS = enum.auto()
|
||||
|
||||
# Checks if the string-ified version of the value matches the other value,
|
||||
# as a regex.
|
||||
MATCHES = enum.auto()
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Var:
|
||||
id: str
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class CheckVarAction(Action):
|
||||
"""
|
||||
Check a variable's value using some kind of comparison.
|
||||
|
||||
If you want to check one variable against another, use the `action.Var`
|
||||
type.
|
||||
"""
|
||||
|
||||
# The variable to check the value of.
|
||||
var_id: str
|
||||
|
||||
# The comparison operation to use. See `Compare` for comparisons.
|
||||
compare: Compare
|
||||
|
||||
# The constant value to check against.
|
||||
against: Any
|
||||
|
||||
# The action to execute when this is true.
|
||||
yes: Sequence[Action]
|
||||
|
||||
# The action to execute when this is false.
|
||||
no: Sequence[Action]
|
||||
|
||||
def act(self, game: "Game"):
|
||||
val = game.vars.get(self.var_id, None)
|
||||
compare_val = None
|
||||
if isinstance(self.against, Var):
|
||||
compare_val = game.vars.get(self.against.id, None)
|
||||
else:
|
||||
compare_val = self.against
|
||||
|
||||
result = False
|
||||
if self.compare == Compare.EQUALS:
|
||||
result = val == compare_val
|
||||
elif self.compare == Compare.NOT_EQUALS:
|
||||
result = val != compare_val
|
||||
elif self.compare == Compare.LESS_THAN:
|
||||
if isinstance(val, str) or isinstance(compare_val, str):
|
||||
result = str(val) < str(compare_val)
|
||||
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||
):
|
||||
result = val < compare_val
|
||||
elif self.compare == Compare.LESS_THAN_EQUALS:
|
||||
if isinstance(val, str) or isinstance(compare_val, str):
|
||||
result = str(val) <= str(compare_val)
|
||||
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||
):
|
||||
result = val <= compare_val
|
||||
elif self.compare == Compare.GREATER_THAN:
|
||||
if isinstance(val, str) or isinstance(compare_val, str):
|
||||
result = str(val) > str(compare_val)
|
||||
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||
):
|
||||
result = val > compare_val
|
||||
elif self.compare == Compare.GREATER_THAN_EQUALS:
|
||||
if isinstance(val, str) or isinstance(compare_val, str):
|
||||
result = str(val) >= str(compare_val)
|
||||
elif (isinstance(val, int) or isinstance(val, float)) and (
|
||||
isinstance(compare_val, int) or isinstance(compare_val, float)
|
||||
):
|
||||
result = val >= compare_val
|
||||
elif self.compare == Compare.MATCHES:
|
||||
pass
|
||||
else:
|
||||
assert False, f"{self.compare} isn't a Compare value, ya doink"
|
||||
|
||||
# Check result, do action
|
||||
if result:
|
||||
game.do_actions(self.yes)
|
||||
else:
|
||||
game.do_actions(self.no)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SetVarAction(Action):
|
||||
"""
|
||||
Set a variable to a specific value.
|
||||
"""
|
||||
|
||||
var_id: str
|
||||
value: Any
|
||||
|
||||
def act(self, game: "Game"):
|
||||
value = (
|
||||
game.vars.get(self.value.id) if isinstance(self.value, Var) else self.value
|
||||
)
|
||||
game.vars[self.var_id] = value
|
||||
Reference in New Issue
Block a user