sistemati spells
This commit is contained in:
parent
18c59755d4
commit
5ef1b78c42
13 changed files with 865 additions and 126 deletions
|
@ -21,8 +21,6 @@ from typeclasses.rooms import IndoorRoom
|
||||||
from typeclasses.objects import Item, EquippableItem
|
from typeclasses.objects import Item, EquippableItem
|
||||||
from typeclasses.scripts import Script, CmdActionScript
|
from typeclasses.scripts import Script, CmdActionScript
|
||||||
|
|
||||||
from world import spells
|
|
||||||
|
|
||||||
CMD_SEARCH_TIME = 5
|
CMD_SEARCH_TIME = 5
|
||||||
|
|
||||||
|
|
||||||
|
@ -574,62 +572,6 @@ class CmdInventory(Command):
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdCast(Command):
|
|
||||||
"""
|
|
||||||
cast a spell.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
cast <spell> [at <target>]
|
|
||||||
|
|
||||||
Casts a spell.
|
|
||||||
"""
|
|
||||||
key = "cast"
|
|
||||||
aliases = ["cs"]
|
|
||||||
lock = "cmd:false()"
|
|
||||||
help_category = "General"
|
|
||||||
arg_regex = r"\s.+|$"
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
"""
|
|
||||||
Handle parsing of:
|
|
||||||
::
|
|
||||||
<spell> [at|on <target>]
|
|
||||||
"""
|
|
||||||
self.args = args = self.args.strip().lower()
|
|
||||||
|
|
||||||
if " at " in args:
|
|
||||||
spell_name, *rest = args.split(" at ", 1)
|
|
||||||
elif " on " in args:
|
|
||||||
spell_name, *rest = args.split(" on ", 1)
|
|
||||||
else:
|
|
||||||
spell_name, rest = args, []
|
|
||||||
|
|
||||||
target_name = rest[0] if rest else ""
|
|
||||||
|
|
||||||
self.spell_name = spell_name.strip()
|
|
||||||
self.target_name = target_name.strip()
|
|
||||||
|
|
||||||
def func(self):
|
|
||||||
caller = self.caller
|
|
||||||
|
|
||||||
if not self.args or not self.spell_name:
|
|
||||||
caller.msg(self.get_help(caller, self.cmdset))
|
|
||||||
return
|
|
||||||
|
|
||||||
spell_id = self.spell_name.replace(' ', '_')
|
|
||||||
if self.spell_name not in caller.db.spells:
|
|
||||||
caller.msg("You cannot cast {}.".format(self.spell_name))
|
|
||||||
return
|
|
||||||
|
|
||||||
if hasattr(spells, "spell_" + spell_id):
|
|
||||||
spell = getattr(spells, "spell_" + spell_id)
|
|
||||||
else:
|
|
||||||
logger.log_err("Cannot find spell {}.".format("spell_" + spell_id))
|
|
||||||
return
|
|
||||||
|
|
||||||
spell(caller, self.target_name)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdTestPy(Command):
|
class CmdTestPy(Command):
|
||||||
key = "testpy"
|
key = "testpy"
|
||||||
aliases = ["testpy"]
|
aliases = ["testpy"]
|
||||||
|
|
|
@ -24,7 +24,6 @@ from commands.command import CmdTestPy
|
||||||
from commands.command import CmdSearch
|
from commands.command import CmdSearch
|
||||||
from commands.command import CmdEquip
|
from commands.command import CmdEquip
|
||||||
from commands.command import CmdInventory
|
from commands.command import CmdInventory
|
||||||
from commands.command import CmdCast
|
|
||||||
from commands.command import CmdPut
|
from commands.command import CmdPut
|
||||||
from commands.command import CmdOpenCloseDoor
|
from commands.command import CmdOpenCloseDoor
|
||||||
|
|
||||||
|
@ -35,7 +34,10 @@ from commands.builder import CmdZone
|
||||||
from commands.builder import CmdAddToZone
|
from commands.builder import CmdAddToZone
|
||||||
from commands.builder import CmdPopen
|
from commands.builder import CmdPopen
|
||||||
|
|
||||||
from utils.crafting import CmdCraft
|
from commands.unloggedin import CmdUnconnectedLook
|
||||||
|
|
||||||
|
from utils.crafting import CraftingCmdSet
|
||||||
|
from utils.spells import CastingCmdSet
|
||||||
|
|
||||||
|
|
||||||
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
|
@ -64,7 +66,6 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
self.add(CmdSearch())
|
self.add(CmdSearch())
|
||||||
self.add(CmdEquip())
|
self.add(CmdEquip())
|
||||||
self.add(CmdInventory())
|
self.add(CmdInventory())
|
||||||
self.add(CmdCast())
|
|
||||||
self.add(CmdPut())
|
self.add(CmdPut())
|
||||||
self.add(CmdOpenCloseDoor())
|
self.add(CmdOpenCloseDoor())
|
||||||
|
|
||||||
|
@ -75,7 +76,8 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
self.add(CmdAddToZone())
|
self.add(CmdAddToZone())
|
||||||
self.add(CmdPopen())
|
self.add(CmdPopen())
|
||||||
|
|
||||||
self.add(CmdCraft())
|
self.add(CraftingCmdSet())
|
||||||
|
self.add(CastingCmdSet())
|
||||||
|
|
||||||
|
|
||||||
class AccountCmdSet(default_cmds.AccountCmdSet):
|
class AccountCmdSet(default_cmds.AccountCmdSet):
|
||||||
|
@ -114,6 +116,7 @@ class UnloggedinCmdSet(default_cmds.UnloggedinCmdSet):
|
||||||
#
|
#
|
||||||
# any commands you add below will overload the default ones.
|
# any commands you add below will overload the default ones.
|
||||||
#
|
#
|
||||||
|
self.add(CmdUnconnectedLook())
|
||||||
|
|
||||||
|
|
||||||
class SessionCmdSet(default_cmds.SessionCmdSet):
|
class SessionCmdSet(default_cmds.SessionCmdSet):
|
||||||
|
|
36
commands/unloggedin.py
Normal file
36
commands/unloggedin.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from evennia import Command
|
||||||
|
from evennia.utils.evmenu import EvMenu
|
||||||
|
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||||
|
|
||||||
|
from menus.login import plain_node_formatter
|
||||||
|
|
||||||
|
|
||||||
|
class CmdUnconnectedLook(Command):
|
||||||
|
"""
|
||||||
|
look when in unlogged-in state
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
look
|
||||||
|
|
||||||
|
This is an unconnected version of the look command for simplicity.
|
||||||
|
|
||||||
|
This is called by the server and kicks everything in gear.
|
||||||
|
All it does is display the connect screen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = CMD_LOGINSTART
|
||||||
|
aliases = ["look", "l"]
|
||||||
|
locks = "cmd:all()"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"""Show the connect screen."""
|
||||||
|
EvMenu(
|
||||||
|
self.caller,
|
||||||
|
"menus.login",
|
||||||
|
startnode="node_enter_username",
|
||||||
|
auto_look=False,
|
||||||
|
auto_quit=False,
|
||||||
|
cmd_on_exit=None,
|
||||||
|
node_formatter=plain_node_formatter,
|
||||||
|
)
|
||||||
|
|
58
menus/char_manager.py
Normal file
58
menus/char_manager.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
_ACCOUNT_HELP = (
|
||||||
|
"Enter the name you used to log into the game before, " "or a new account-name if you are new."
|
||||||
|
)
|
||||||
|
_PASSWORD_HELP = (
|
||||||
|
"Password should be a minimum of 8 characters (preferably longer) and "
|
||||||
|
"can contain a mix of letters, spaces, digits and @/./+/-/_/'/, only."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_help(caller, raw_string, **kwargs):
|
||||||
|
"""Echo help message, then re-run node that triggered it"""
|
||||||
|
help_entry = kwargs["help_entry"]
|
||||||
|
caller.msg(help_entry)
|
||||||
|
return None # re-run calling node
|
||||||
|
|
||||||
|
|
||||||
|
def node_enter_char_management(caller, raw_string, **kwargs):
|
||||||
|
account = caller.ndb._menutree.account
|
||||||
|
|
||||||
|
if not account:
|
||||||
|
raise Exception("No account found!")
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# text = "{}".format(w_first_login)
|
||||||
|
#
|
||||||
|
# options = (
|
||||||
|
# {"key": "", "goto": "node_enter_char_management"},
|
||||||
|
# {"key": ("help", "h"), "goto": (_show_help, {"help_entry": _ACCOUNT_HELP, **kwargs})},
|
||||||
|
# {"key": "_default", "goto": "node_enter_char_management"},
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# return text, options
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
character = account.db._last_puppet
|
||||||
|
character.db.health = 10
|
||||||
|
character.db.mana = 10
|
||||||
|
|
||||||
|
character.db.spells = []
|
||||||
|
character.db.spells.append('light')
|
||||||
|
character.db.recipes = []
|
||||||
|
character.db.recipes.append('vial of blood')
|
||||||
|
character.db.recipes.append('torch')
|
||||||
|
|
||||||
|
caller.msg("|gLogging in ...|n")
|
||||||
|
caller.sessionhandler.login(caller, account)
|
||||||
|
return "", {}
|
||||||
|
|
||||||
|
|
||||||
|
def plain_node_formatter(nodetext, optionstext, caller=None):
|
||||||
|
"""Do not display the options, only the text.
|
||||||
|
|
||||||
|
This function is used by EvMenu to format the text of nodes. The menu login
|
||||||
|
is just a series of prompts, so we disable all automatic display decoration
|
||||||
|
and let the nodes handle everything on their own.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return nodetext
|
203
menus/login.py
Normal file
203
menus/login.py
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from evennia.utils import utils
|
||||||
|
|
||||||
|
from evennia.utils.evmenu import EvMenu
|
||||||
|
|
||||||
|
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||||
|
_GUEST_ENABLED = settings.GUEST_ENABLED
|
||||||
|
_ACCOUNT = utils.class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||||
|
_GUEST = utils.class_from_module(settings.BASE_GUEST_TYPECLASS)
|
||||||
|
|
||||||
|
_ACCOUNT_HELP = (
|
||||||
|
"Enter the name you used to log into the game before, " "or a new account-name if you are new."
|
||||||
|
)
|
||||||
|
_PASSWORD_HELP = (
|
||||||
|
"Password should be a minimum of 8 characters (preferably longer) and "
|
||||||
|
"can contain a mix of letters, spaces, digits and @/./+/-/_/'/, only."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_help(caller, raw_string, **kwargs):
|
||||||
|
"""Echo help message, then re-run node that triggered it"""
|
||||||
|
help_entry = kwargs["help_entry"]
|
||||||
|
caller.msg(help_entry)
|
||||||
|
return None # re-run calling node
|
||||||
|
|
||||||
|
|
||||||
|
def node_enter_username(caller, raw_text, **kwargs):
|
||||||
|
"""
|
||||||
|
Start node of menu
|
||||||
|
Start login by displaying the connection screen and ask for a user name.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _check_input(caller, username, **kwargs):
|
||||||
|
"""
|
||||||
|
'Goto-callable', set up to be called from the _default option below.
|
||||||
|
|
||||||
|
Called when user enters a username string. Check if this username already exists and set the flag
|
||||||
|
'new_user' if not. Will also directly login if the username is 'guest'
|
||||||
|
and GUEST_ENABLED is True.
|
||||||
|
|
||||||
|
The return from this goto-callable determines which node we go to next
|
||||||
|
and what kwarg it will be called with.
|
||||||
|
|
||||||
|
"""
|
||||||
|
username = username.rstrip("\n")
|
||||||
|
|
||||||
|
if username == "guest" and _GUEST_ENABLED:
|
||||||
|
# do an immediate guest login
|
||||||
|
session = caller
|
||||||
|
address = session.address
|
||||||
|
account, errors = _GUEST.authenticate(ip=address)
|
||||||
|
if account:
|
||||||
|
return "node_quit_or_login", {"login": True, "account": account}
|
||||||
|
else:
|
||||||
|
session.msg("|R{}|n".format("\n".join(errors)))
|
||||||
|
return None # re-run the username node
|
||||||
|
|
||||||
|
try:
|
||||||
|
_ACCOUNT.objects.get(username__iexact=username)
|
||||||
|
except _ACCOUNT.DoesNotExist:
|
||||||
|
new_user = True
|
||||||
|
else:
|
||||||
|
new_user = False
|
||||||
|
|
||||||
|
# pass username/new_user into next node as kwargs
|
||||||
|
return "node_enter_password", {"new_user": new_user, "username": username}
|
||||||
|
|
||||||
|
"""Show the connect screen."""
|
||||||
|
|
||||||
|
callables = utils.callables_from_module(CONNECTION_SCREEN_MODULE)
|
||||||
|
if "connection_screen" in callables:
|
||||||
|
connection_screen = callables["connection_screen"]()
|
||||||
|
else:
|
||||||
|
connection_screen = utils.random_string_from_module(CONNECTION_SCREEN_MODULE)
|
||||||
|
if not connection_screen:
|
||||||
|
connection_screen = "No connection screen found. Please contact an admin."
|
||||||
|
|
||||||
|
if _GUEST_ENABLED:
|
||||||
|
text = "Enter a new or existing user name to login (write 'guest' for a guest login):"
|
||||||
|
else:
|
||||||
|
text = "Enter a new or existing user name to login:"
|
||||||
|
|
||||||
|
text = "{}\n\n{}".format(connection_screen, text)
|
||||||
|
|
||||||
|
options = (
|
||||||
|
{"key": "", "goto": "node_enter_username"},
|
||||||
|
{"key": ("quit", "q"), "goto": "node_quit_or_login"},
|
||||||
|
{"key": ("help", "h"), "goto": (_show_help, {"help_entry": _ACCOUNT_HELP, **kwargs})},
|
||||||
|
{"key": "_default", "goto": _check_input},
|
||||||
|
)
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_enter_password(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Handle password input.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _check_input(caller, password, **kwargs):
|
||||||
|
"""
|
||||||
|
'Goto-callable', set up to be called from the _default option below.
|
||||||
|
|
||||||
|
Called when user enters a password string. Check username + password
|
||||||
|
viability. If it passes, the account will have been created and login
|
||||||
|
will be initiated.
|
||||||
|
|
||||||
|
The return from this goto-callable determines which node we go to next
|
||||||
|
and what kwarg it will be called with.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# these flags were set by the goto-callable
|
||||||
|
username = kwargs["username"]
|
||||||
|
new_user = kwargs["new_user"]
|
||||||
|
password = password.rstrip("\n")
|
||||||
|
|
||||||
|
session = caller
|
||||||
|
address = session.address
|
||||||
|
if new_user:
|
||||||
|
# create a new account
|
||||||
|
account, errors = _ACCOUNT.create(
|
||||||
|
username=username, password=password, ip=address, session=session
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# check password against existing account
|
||||||
|
account, errors = _ACCOUNT.authenticate(
|
||||||
|
username=username, password=password, ip=address, session=session
|
||||||
|
)
|
||||||
|
|
||||||
|
if account:
|
||||||
|
if new_user:
|
||||||
|
session.msg("|gA new account |c{}|g was created. Welcome!|n".format(username))
|
||||||
|
# pass login info to login node
|
||||||
|
return "node_quit_or_login", {"login": True, "account": account}
|
||||||
|
else:
|
||||||
|
# restart due to errors
|
||||||
|
session.msg("|R{}".format("\n".join(errors)))
|
||||||
|
kwargs["retry_password"] = True
|
||||||
|
return "node_enter_password", kwargs
|
||||||
|
|
||||||
|
def _restart_login(caller, *args, **kwargs):
|
||||||
|
caller.msg("|yCancelled login.|n")
|
||||||
|
return "node_enter_username"
|
||||||
|
|
||||||
|
username = kwargs["username"]
|
||||||
|
if kwargs["new_user"]:
|
||||||
|
if kwargs.get("retry_password"):
|
||||||
|
# Attempting to fix password
|
||||||
|
text = "Enter a new password:"
|
||||||
|
else:
|
||||||
|
text = "Creating a new account |c{}|n. " "Enter a password (empty to abort):".format(username)
|
||||||
|
else:
|
||||||
|
text = "Enter the password for account |c{}|n (empty to abort):".format(username)
|
||||||
|
options = (
|
||||||
|
{"key": "", "goto": _restart_login},
|
||||||
|
{"key": ("quit", "q"), "goto": "node_quit_or_login"},
|
||||||
|
{"key": ("help", "h"), "goto": (_show_help, {"help_entry": _PASSWORD_HELP, **kwargs})},
|
||||||
|
{"key": "_default", "goto": (_check_input, kwargs)},
|
||||||
|
)
|
||||||
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_quit_or_login(caller, raw_text, **kwargs):
|
||||||
|
"""
|
||||||
|
Exit menu, either by disconnecting or logging in.
|
||||||
|
|
||||||
|
"""
|
||||||
|
session = caller
|
||||||
|
if kwargs.get("login"):
|
||||||
|
account = kwargs.get("account")
|
||||||
|
# TODO
|
||||||
|
# session.msg("|gLogging in ...|n")
|
||||||
|
# session.sessionhandler.login(session, account)
|
||||||
|
|
||||||
|
# # go OOC
|
||||||
|
# account.unpuppet_object(session)
|
||||||
|
|
||||||
|
EvMenu(
|
||||||
|
caller,
|
||||||
|
"menus.char_manager",
|
||||||
|
startnode="node_enter_char_management",
|
||||||
|
auto_look=False,
|
||||||
|
auto_quit=False,
|
||||||
|
cmd_on_exit=None,
|
||||||
|
node_formatter=plain_node_formatter,
|
||||||
|
account=account,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
session.sessionhandler.disconnect(session, "Goodbye! Logging off.")
|
||||||
|
return "", {}
|
||||||
|
|
||||||
|
|
||||||
|
def plain_node_formatter(nodetext, optionstext, caller=None):
|
||||||
|
"""Do not display the options, only the text.
|
||||||
|
|
||||||
|
This function is used by EvMenu to format the text of nodes. The menu login
|
||||||
|
is just a series of prompts, so we disable all automatic display decoration
|
||||||
|
and let the nodes handle everything on their own.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return nodetext
|
|
@ -41,6 +41,7 @@ GLOBAL_SCRIPTS = {
|
||||||
|
|
||||||
PROTOTYPE_MODULES += ["world.tutorial_prototypes"]
|
PROTOTYPE_MODULES += ["world.tutorial_prototypes"]
|
||||||
CRAFT_RECIPE_MODULES = ['world.recipes_base']
|
CRAFT_RECIPE_MODULES = ['world.recipes_base']
|
||||||
|
SPELL_MODULES = ['world.spells']
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Settings given in secret_settings.py override those in this file.
|
# Settings given in secret_settings.py override those in this file.
|
||||||
|
|
|
@ -56,6 +56,7 @@ class Character(DefaultCharacter):
|
||||||
}
|
}
|
||||||
|
|
||||||
self.db.spells = []
|
self.db.spells = []
|
||||||
|
self.db.recipes = []
|
||||||
self.db.current_action = None
|
self.db.current_action = None
|
||||||
|
|
||||||
def get_health(self):
|
def get_health(self):
|
||||||
|
|
|
@ -39,6 +39,13 @@ class Exit(DefaultExit):
|
||||||
defined, in which case that will simply be echoed.
|
defined, in which case that will simply be echoed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def at_init(self):
|
||||||
|
# check if this exit has directional alias
|
||||||
|
self.ndb.directional_alias = None
|
||||||
|
directional_aliases = [ele for ele in ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'] if (ele in self.aliases.all())]
|
||||||
|
if directional_aliases:
|
||||||
|
self.ndb.directional_alias = directional_aliases[0]
|
||||||
|
|
||||||
def at_traverse(self, traversing_object, target_location, **kwargs):
|
def at_traverse(self, traversing_object, target_location, **kwargs):
|
||||||
if has_effect(traversing_object, "is_busy"):
|
if has_effect(traversing_object, "is_busy"):
|
||||||
traversing_object.msg("You are already busy {}.".format(traversing_object.db.current_action.busy_msg()))
|
traversing_object.msg("You are already busy {}.".format(traversing_object.db.current_action.busy_msg()))
|
||||||
|
|
|
@ -33,8 +33,8 @@ class Room(DefaultRoom):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
self.locks.add("light:false()")
|
self.locks.add("light:false()")
|
||||||
|
|
||||||
self.db.x = 0
|
self.db.x = -1
|
||||||
self.db.y = 0
|
self.db.y = -1
|
||||||
|
|
||||||
self.db.map_icon = '|w⊡|n'
|
self.db.map_icon = '|w⊡|n'
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ class IndoorRoom(Room):
|
||||||
"""
|
"""
|
||||||
Called when room is first recached (such as after a reload)
|
Called when room is first recached (such as after a reload)
|
||||||
"""
|
"""
|
||||||
|
super().at_init()
|
||||||
self.check_light_state()
|
self.check_light_state()
|
||||||
|
|
||||||
def return_appearance(self, looker, **kwargs):
|
def return_appearance(self, looker, **kwargs):
|
||||||
|
@ -88,7 +89,10 @@ class IndoorRoom(Room):
|
||||||
for con in visible:
|
for con in visible:
|
||||||
key = con.get_display_name(looker)
|
key = con.get_display_name(looker)
|
||||||
if con.destination:
|
if con.destination:
|
||||||
exits.append(key)
|
if con.ndb.directional_alias:
|
||||||
|
exits.append("|m{}|n|w)|n{}".format(con.ndb.directional_alias, key))
|
||||||
|
else:
|
||||||
|
exits.append(key)
|
||||||
elif con.has_account:
|
elif con.has_account:
|
||||||
users.append("|c%s|n" % key)
|
users.append("|c%s|n" % key)
|
||||||
elif con.db.feature_desc:
|
elif con.db.feature_desc:
|
||||||
|
@ -326,3 +330,47 @@ class Zone(DefaultRoom):
|
||||||
for ex in room.exits: # iterate exits in the room
|
for ex in room.exits: # iterate exits in the room
|
||||||
if not self.ndb.sp_graph.is_edge(room, ex.destination):
|
if not self.ndb.sp_graph.is_edge(room, ex.destination):
|
||||||
self.ndb.sp_graph.add_edge(room, ex.destination, 1, ex.name)
|
self.ndb.sp_graph.add_edge(room, ex.destination, 1, ex.name)
|
||||||
|
|
||||||
|
# if destination is already inserted in zone we can proceed, otherwise
|
||||||
|
# aliases are created later.
|
||||||
|
if ex.destination.db.x != - 1 and ex.destination.db.y != - 1:
|
||||||
|
self._update_exit_alias(ex)
|
||||||
|
if ex.db.return_exit:
|
||||||
|
self._update_exit_alias(ex.db.return_exit)
|
||||||
|
|
||||||
|
def _update_exit_alias(self, exit_obj):
|
||||||
|
xl, yl = exit_obj.location.db.x, exit_obj.location.db.y
|
||||||
|
xd, yd = exit_obj.destination.db.x, exit_obj.destination.db.y
|
||||||
|
|
||||||
|
# if aliases aren't already created, create them.
|
||||||
|
if not [ele for ele in ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'] if(ele in exit_obj.aliases.all())]:
|
||||||
|
aliases = []
|
||||||
|
dx = xd - xl
|
||||||
|
dy = yd - yl
|
||||||
|
if dx == 0 and dy == -1:
|
||||||
|
aliases.extend(["n", "north"])
|
||||||
|
exit_obj.ndb.directional_alias = "n"
|
||||||
|
elif dx == 0 and dy == 1:
|
||||||
|
aliases.extend(["s", "south"])
|
||||||
|
exit_obj.ndb.directional_alias = "s"
|
||||||
|
elif dx == -1 and dy == 0:
|
||||||
|
aliases.extend(["w", "west"])
|
||||||
|
exit_obj.ndb.directional_alias = "w"
|
||||||
|
elif dx == 1 and dy == 0:
|
||||||
|
aliases.extend(["e", "east"])
|
||||||
|
exit_obj.ndb.directional_alias = "e"
|
||||||
|
elif dx == 1 and dy == -1:
|
||||||
|
aliases.extend(["ne", "northeast"])
|
||||||
|
exit_obj.ndb.directional_alias = "ne"
|
||||||
|
elif dx == -1 and dy == -1:
|
||||||
|
aliases.extend(["nw", "northwest"])
|
||||||
|
exit_obj.ndb.directional_alias = "nw"
|
||||||
|
elif dx == 1 and dy == 1:
|
||||||
|
aliases.extend(["se", "southeast"])
|
||||||
|
exit_obj.ndb.directional_alias = "se"
|
||||||
|
elif dx == -1 and dy == 1:
|
||||||
|
aliases.extend(["sw", "southwest"])
|
||||||
|
exit_obj.ndb.directional_alias = "sw"
|
||||||
|
|
||||||
|
exit_obj.aliases.add(aliases)
|
||||||
|
|
||||||
|
|
|
@ -121,9 +121,10 @@ a full example of the components for creating a sword from base components.
|
||||||
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from evennia import logger
|
from evennia import logger
|
||||||
from evennia.utils.utils import callables_from_module, inherits_from, make_iter, iter_to_string
|
from evennia.utils import evtable
|
||||||
|
from evennia.utils.utils import callables_from_module, inherits_from, make_iter, iter_to_string, list_to_string
|
||||||
from evennia.commands.cmdset import CmdSet
|
from evennia.commands.cmdset import CmdSet
|
||||||
from evennia.commands.command import Command
|
from commands.command import Command
|
||||||
from evennia.prototypes.spawner import spawn
|
from evennia.prototypes.spawner import spawn
|
||||||
from evennia.utils.create import create_object, create_script
|
from evennia.utils.create import create_object, create_script
|
||||||
|
|
||||||
|
@ -677,7 +678,6 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
|
|
||||||
tools = []
|
tools = []
|
||||||
for itag, tag in enumerate(cls.tool_tags):
|
for itag, tag in enumerate(cls.tool_tags):
|
||||||
|
|
||||||
tools.append(
|
tools.append(
|
||||||
create_object(
|
create_object(
|
||||||
key=tool_key or (cls.tool_names[itag] if cls.tool_names else tag.capitalize()),
|
key=tool_key or (cls.tool_names[itag] if cls.tool_names else tag.capitalize()),
|
||||||
|
@ -690,7 +690,7 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
consumables.append(
|
consumables.append(
|
||||||
create_object(
|
create_object(
|
||||||
key=cons_key
|
key=cons_key
|
||||||
or (cls.consumable_names[itag] if cls.consumable_names else tag.capitalize()),
|
or (cls.consumable_names[itag] if cls.consumable_names else tag.capitalize()),
|
||||||
tags=[(tag, cls.consumable_tag_category), *cons_tags],
|
tags=[(tag, cls.consumable_tag_category), *cons_tags],
|
||||||
**consumable_kwargs,
|
**consumable_kwargs,
|
||||||
)
|
)
|
||||||
|
@ -717,14 +717,14 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _check_completeness(
|
def _check_completeness(
|
||||||
tagmap,
|
tagmap,
|
||||||
taglist,
|
taglist,
|
||||||
namelist,
|
namelist,
|
||||||
exact_match,
|
exact_match,
|
||||||
exact_order,
|
exact_order,
|
||||||
error_missing_message,
|
error_missing_message,
|
||||||
error_order_message,
|
error_order_message,
|
||||||
error_excess_message,
|
error_excess_message,
|
||||||
):
|
):
|
||||||
"""Compare tagmap (inputs) to taglist (required)"""
|
"""Compare tagmap (inputs) to taglist (required)"""
|
||||||
valids = []
|
valids = []
|
||||||
|
@ -771,17 +771,17 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
obj: obj.tags.get(category=self.tool_tag_category, return_list=True)
|
obj: obj.tags.get(category=self.tool_tag_category, return_list=True)
|
||||||
for obj in self.inputs
|
for obj in self.inputs
|
||||||
if obj
|
if obj
|
||||||
and hasattr(obj, "tags")
|
and hasattr(obj, "tags")
|
||||||
and inherits_from(obj, "evennia.objects.models.ObjectDB")
|
and inherits_from(obj, "evennia.objects.models.ObjectDB")
|
||||||
}
|
}
|
||||||
tool_map = {obj: tags for obj, tags in tool_map.items() if tags}
|
tool_map = {obj: tags for obj, tags in tool_map.items() if tags}
|
||||||
consumable_map = {
|
consumable_map = {
|
||||||
obj: obj.tags.get(category=self.consumable_tag_category, return_list=True)
|
obj: obj.tags.get(category=self.consumable_tag_category, return_list=True)
|
||||||
for obj in self.inputs
|
for obj in self.inputs
|
||||||
if obj
|
if obj
|
||||||
and hasattr(obj, "tags")
|
and hasattr(obj, "tags")
|
||||||
and obj not in tool_map
|
and obj not in tool_map
|
||||||
and inherits_from(obj, "evennia.objects.models.ObjectDB")
|
and inherits_from(obj, "evennia.objects.models.ObjectDB")
|
||||||
}
|
}
|
||||||
consumable_map = {obj: tags for obj, tags in consumable_map.items() if tags}
|
consumable_map = {obj: tags for obj, tags in consumable_map.items() if tags}
|
||||||
|
|
||||||
|
@ -1008,6 +1008,38 @@ class CraftingCmdSet(CmdSet):
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
self.add(CmdCraft())
|
self.add(CmdCraft())
|
||||||
|
self.add(CmdRecipes())
|
||||||
|
|
||||||
|
|
||||||
|
class CmdRecipes(Command):
|
||||||
|
"""
|
||||||
|
List known recipes.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
recipes
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "recipes"
|
||||||
|
locks = "cmd:all()"
|
||||||
|
help_category = "General"
|
||||||
|
arg_regex = r"\s|$"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
# delayed loading/caching of recipes
|
||||||
|
_load_recipes()
|
||||||
|
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
table = evtable.EvTable("|wName|n", "|wMaterials", "|wTools|n",
|
||||||
|
border_left_char="|y|||n", border_right_char="|y|||n",
|
||||||
|
border_top_char="|y-|n", border_bottom_char="|y-|n",
|
||||||
|
corner_char="|y+|n")
|
||||||
|
|
||||||
|
for recipe in _RECIPE_CLASSES.values():
|
||||||
|
if (recipe.name in caller.db.recipes) or caller.is_superuser:
|
||||||
|
table.add_row("|W{}".format(recipe.name), "|M{}".format(list_to_string(recipe.tool_tags)), "|G{}".format(list_to_string(recipe.consumable_tags)))
|
||||||
|
|
||||||
|
caller.msg(table)
|
||||||
|
|
||||||
|
|
||||||
class CmdCraft(Command):
|
class CmdCraft(Command):
|
||||||
|
@ -1095,9 +1127,9 @@ class CmdCraft(Command):
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
not inherits_from(obj, "evennia.objects.models.ObjectDB")
|
not inherits_from(obj, "evennia.objects.models.ObjectDB")
|
||||||
or obj.sessions.all()
|
or obj.sessions.all()
|
||||||
or not obj.access(caller, "craft", default=True)
|
or not obj.access(caller, "craft", default=True)
|
||||||
):
|
):
|
||||||
# We don't allow to include puppeted objects nor those with the
|
# We don't allow to include puppeted objects nor those with the
|
||||||
# 'negative' permission 'nocraft'.
|
# 'negative' permission 'nocraft'.
|
||||||
|
@ -1139,7 +1171,9 @@ class CmdCraft(Command):
|
||||||
|
|
||||||
toggle_effect(caller, "is_busy")
|
toggle_effect(caller, "is_busy")
|
||||||
caller.msg("You start crafting {} {}.".format(indefinite_article(recipe_cls.name), recipe_cls.name))
|
caller.msg("You start crafting {} {}.".format(indefinite_article(recipe_cls.name), recipe_cls.name))
|
||||||
action_script = create_script("utils.crafting.CmdCraftComplete", obj=caller, interval=recipe_cls.crafting_time, attributes=[("recipe", recipe_cls.name), ("tools_and_ingredients", tools_and_ingredients)])
|
action_script = create_script("utils.crafting.CmdCraftComplete", obj=caller, interval=recipe_cls.crafting_time,
|
||||||
|
attributes=[("recipe", recipe_cls.name),
|
||||||
|
("tools_and_ingredients", tools_and_ingredients)])
|
||||||
caller.db.current_action = action_script
|
caller.db.current_action = action_script
|
||||||
|
|
||||||
|
|
||||||
|
@ -1171,4 +1205,3 @@ class CmdCraftComplete(CmdActionScript):
|
||||||
|
|
||||||
def busy_msg(self):
|
def busy_msg(self):
|
||||||
return "crafting {} {}".format(indefinite_article(self.db.recipe), self.db.recipe)
|
return "crafting {} {}".format(indefinite_article(self.db.recipe), self.db.recipe)
|
||||||
|
|
||||||
|
|
385
utils/spells.py
Normal file
385
utils/spells.py
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
from copy import copy
|
||||||
|
from evennia import logger
|
||||||
|
from evennia.utils import evtable
|
||||||
|
from evennia.utils.utils import callables_from_module, inherits_from, make_iter, iter_to_string, list_to_string
|
||||||
|
from evennia.commands.cmdset import CmdSet
|
||||||
|
from commands.command import Command
|
||||||
|
from typeclasses.scripts import CmdActionScript
|
||||||
|
from utils.utils import toggle_effect, has_effect
|
||||||
|
from evennia.utils.create import create_object, create_script
|
||||||
|
|
||||||
|
_SPELL_CLASSES = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _load_spells():
|
||||||
|
"""
|
||||||
|
Delayed loading of recipe classes. This parses
|
||||||
|
`settings.SPELL_MODULES`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
global _SPELL_CLASSES
|
||||||
|
if not _SPELL_CLASSES:
|
||||||
|
paths = []
|
||||||
|
if hasattr(settings, "SPELL_MODULES"):
|
||||||
|
paths += make_iter(settings.SPELL_MODULES)
|
||||||
|
for path in paths:
|
||||||
|
for cls in callables_from_module(path).values():
|
||||||
|
if inherits_from(cls, Spell):
|
||||||
|
_SPELL_CLASSES[cls.name] = cls
|
||||||
|
|
||||||
|
|
||||||
|
class Spell:
|
||||||
|
name = "spell base"
|
||||||
|
casting_time = 1
|
||||||
|
duration = 1
|
||||||
|
target_type = None
|
||||||
|
# general cast-failure msg to show after other error-messages.
|
||||||
|
failure_message = ""
|
||||||
|
# show after a successful cast
|
||||||
|
success_message = "You cast {}.".format(name)
|
||||||
|
|
||||||
|
def __init__(self, caster, **kwargs):
|
||||||
|
self.caster = caster
|
||||||
|
self.target_obj = None
|
||||||
|
self.cast_kwargs = kwargs
|
||||||
|
self.allow_cast = True
|
||||||
|
|
||||||
|
def msg(self, message, **kwargs):
|
||||||
|
"""
|
||||||
|
Send message to caster. This is a central point to override if wanting
|
||||||
|
to change casting return style in some way.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message(str): The message to send.
|
||||||
|
**kwargs: Any optional properties relevant to this send.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.caster.msg(message, oob=({"type": "casting"}))
|
||||||
|
|
||||||
|
def pre_cast(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Hook to override.
|
||||||
|
|
||||||
|
This is called just before casting operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**kwargs: Any optional extra kwargs passed during initialization of
|
||||||
|
the spell class.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CastingError: If validation fails. At this point the caster
|
||||||
|
is expected to have been informed of the problem already.
|
||||||
|
"""
|
||||||
|
caster = self.caster
|
||||||
|
self.target_obj = None
|
||||||
|
|
||||||
|
if self.target_type:
|
||||||
|
if not self.cast_kwargs.get('target_name', None):
|
||||||
|
self.msg("You need a target to cast {} on.".format(self.name))
|
||||||
|
raise CastingError("You need a target to cast {} on.".format(self.name))
|
||||||
|
|
||||||
|
target_name = self.cast_kwargs.get('target_name')
|
||||||
|
self.target_obj = caster.search(target_name, location=[caster, caster.location])
|
||||||
|
|
||||||
|
if not self.target_obj:
|
||||||
|
self.msg("{} is not a valid target for {}.".format(target_name, self.name))
|
||||||
|
raise CastingError("{} is not a valid target for {}.".format(target_name, self.name))
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# gestire i vari tipi di target_type
|
||||||
|
|
||||||
|
def do_cast(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Hook to override. This will not be called if validation in `pre_cast`
|
||||||
|
fails.
|
||||||
|
|
||||||
|
This performs the actual casting. At this point the inputs are
|
||||||
|
expected to have been verified already.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
any: The result of casting if any.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
This method should use `self.msg` to inform the user about the
|
||||||
|
specific reason of failure immediately.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def post_cast(self, cast_result, **kwargs):
|
||||||
|
"""
|
||||||
|
Hook to override.
|
||||||
|
|
||||||
|
This is called just after casting has finished.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if cast_result:
|
||||||
|
self.msg(self.success_message)
|
||||||
|
elif self.failure_message:
|
||||||
|
self.msg(self.failure_message)
|
||||||
|
|
||||||
|
return cast_result
|
||||||
|
|
||||||
|
def cast(self, raise_exception=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Main casting call method. Call this to cast a spell and make
|
||||||
|
sure all hooks run correctly.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
raise_exception (bool): If casting would go wrong, raise
|
||||||
|
exception instead.
|
||||||
|
**kwargs (any): Any other parameters that is relevant
|
||||||
|
for this particular cast operation. This will temporarily
|
||||||
|
override same-named kwargs given at the creation of this recipe
|
||||||
|
and be passed into all the casting hooks.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
any: The result of the cast, or `None`.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CastingError: If casting validation failed and
|
||||||
|
`raise_exception` is True.
|
||||||
|
"""
|
||||||
|
cast_result = None
|
||||||
|
if self.allow_cast:
|
||||||
|
|
||||||
|
# override/extend cast_kwargs from initialization.
|
||||||
|
cast_kwargs = copy(self.cast_kwargs)
|
||||||
|
cast_kwargs.update(kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# this assigns to self.validated_inputs
|
||||||
|
self.pre_cast(**cast_kwargs)
|
||||||
|
except CastingError:
|
||||||
|
if raise_exception:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
cast_result = self.do_cast(**cast_kwargs)
|
||||||
|
finally:
|
||||||
|
cast_result = self.post_cast(cast_result, **cast_kwargs)
|
||||||
|
except CastingError:
|
||||||
|
if raise_exception:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if cast_result is None and raise_exception:
|
||||||
|
raise CastingError(f"Casting of {self.name} failed.")
|
||||||
|
return cast_result
|
||||||
|
|
||||||
|
|
||||||
|
def cast(caster, spell_name, raise_exception=False, **kwargs):
|
||||||
|
# delayed loading/caching of spells
|
||||||
|
_load_spells()
|
||||||
|
|
||||||
|
SpellClass = search_spell(caster, spell_name)
|
||||||
|
|
||||||
|
if not SpellClass:
|
||||||
|
raise KeyError(
|
||||||
|
f"No spell in settings.SPELL_MODULES has a name matching {spell_name}"
|
||||||
|
)
|
||||||
|
spell = SpellClass(caster, **kwargs)
|
||||||
|
return spell.cast(raise_exception=raise_exception)
|
||||||
|
|
||||||
|
|
||||||
|
def can_cast(caster, spell_name, **kwargs):
|
||||||
|
"""
|
||||||
|
Access function.Check if crafter can craft a given recipe from a source recipe module.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
caster (Object): The one doing the crafting.
|
||||||
|
spell_name (str): The `Spell.name` to use. This uses fuzzy-matching
|
||||||
|
if the result is unique.
|
||||||
|
**kwargs: Optional kwargs to pass into the casting.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: Error messages, if any.
|
||||||
|
"""
|
||||||
|
# delayed loading/caching of spells
|
||||||
|
_load_spells()
|
||||||
|
|
||||||
|
SpellClass = search_spell(caster, spell_name)
|
||||||
|
|
||||||
|
if not SpellClass:
|
||||||
|
raise KeyError(
|
||||||
|
f"No spell in settings.SPELL_MODULES has a name matching {spell_name}"
|
||||||
|
)
|
||||||
|
spell = SpellClass(caster, **kwargs)
|
||||||
|
|
||||||
|
if spell.allow_cast:
|
||||||
|
|
||||||
|
# override/extend craft_kwargs from initialization.
|
||||||
|
cast_kwargs = copy(spell.cast_kwargs)
|
||||||
|
cast_kwargs.update(kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
spell.pre_cast(**cast_kwargs)
|
||||||
|
except CastingError:
|
||||||
|
logger.log_err(CastingError.args)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def search_spell(caster, spell_name):
|
||||||
|
# delayed loading/caching of spells
|
||||||
|
_load_spells()
|
||||||
|
|
||||||
|
spell_class = _SPELL_CLASSES.get(spell_name, None)
|
||||||
|
if not spell_class:
|
||||||
|
# try a startswith fuzzy match
|
||||||
|
matches = [key for key in _SPELL_CLASSES if key.startswith(spell_name)]
|
||||||
|
if not matches:
|
||||||
|
# try in-match
|
||||||
|
matches = [key for key in _SPELL_CLASSES if spell_name in key]
|
||||||
|
if len(matches) == 1:
|
||||||
|
spell_class = _SPELL_CLASSES.get(matches[0], None)
|
||||||
|
|
||||||
|
return spell_class
|
||||||
|
|
||||||
|
|
||||||
|
class CastingCmdSet(CmdSet):
|
||||||
|
"""
|
||||||
|
Store Casting command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "Casting cmdset"
|
||||||
|
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(CmdCast())
|
||||||
|
self.add(CmdSpells())
|
||||||
|
|
||||||
|
|
||||||
|
class CmdSpells(Command):
|
||||||
|
"""
|
||||||
|
List known spells.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
spells
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "spells"
|
||||||
|
locks = "cmd:all()"
|
||||||
|
help_category = "General"
|
||||||
|
arg_regex = r"\s|$"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
_load_spells()
|
||||||
|
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
table = evtable.EvTable("|wName|n", "|wTarget", "|wCasting time", "|wDuration",
|
||||||
|
border_left_char="|y|||n", border_right_char="|y|||n",
|
||||||
|
border_top_char="|y-|n", border_bottom_char="|y-|n",
|
||||||
|
corner_char="|y+|n")
|
||||||
|
|
||||||
|
for spell in _SPELL_CLASSES.values():
|
||||||
|
if (spell.name in caller.db.spells) or caller.is_superuser:
|
||||||
|
table.add_row("|W{}".format(spell.name), "|M{}".format(spell.target_type), "|G{}".format(spell.casting_time), "|M{}".format(spell.duration))
|
||||||
|
|
||||||
|
caller.msg(table)
|
||||||
|
|
||||||
|
|
||||||
|
class CmdCast(Command):
|
||||||
|
"""
|
||||||
|
cast a spell.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
cast <spell> [at <target>]
|
||||||
|
|
||||||
|
Casts a spell.
|
||||||
|
"""
|
||||||
|
key = "cast"
|
||||||
|
aliases = ["cs"]
|
||||||
|
lock = "cmd:false()"
|
||||||
|
help_category = "General"
|
||||||
|
arg_regex = r"\s.+|$"
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
"""
|
||||||
|
Handle parsing of:
|
||||||
|
::
|
||||||
|
<spell> [at|on <target>]
|
||||||
|
"""
|
||||||
|
self.args = args = self.args.strip().lower()
|
||||||
|
|
||||||
|
if " at " in args:
|
||||||
|
spell_name, *rest = args.split(" at ", 1)
|
||||||
|
elif " on " in args:
|
||||||
|
spell_name, *rest = args.split(" on ", 1)
|
||||||
|
else:
|
||||||
|
spell_name, rest = args, []
|
||||||
|
|
||||||
|
target_name = rest[0] if rest else ""
|
||||||
|
|
||||||
|
self.spell_name = spell_name.strip()
|
||||||
|
self.target_name = target_name.strip()
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
if not self.args or not self.spell_name:
|
||||||
|
caller.msg(self.get_help(caller, self.cmdset))
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.spell_name not in caller.db.spells:
|
||||||
|
caller.msg("You don't know how to cast {}.".format(self.spell_name))
|
||||||
|
return
|
||||||
|
|
||||||
|
if has_effect(caller, "is_busy"):
|
||||||
|
caller.msg("You are already busy {}.".format(caller.db.current_action.busy_msg()))
|
||||||
|
return
|
||||||
|
|
||||||
|
spell_cls = search_spell(caller, self.spell_name)
|
||||||
|
if not spell_cls:
|
||||||
|
caller.msg("You don't know how to cast {}.".format(self.spell_name))
|
||||||
|
return
|
||||||
|
|
||||||
|
if not can_cast(caller, spell_cls.name, target_name=self.target_name):
|
||||||
|
return
|
||||||
|
|
||||||
|
toggle_effect(caller, "is_busy")
|
||||||
|
caller.msg("You start casting {}.".format(spell_cls.name))
|
||||||
|
action_script = create_script("utils.spells.CastCompleteScript", obj=caller, interval=spell_cls.casting_time,
|
||||||
|
attributes=[("spell", spell_cls.name),
|
||||||
|
("target_name", self.target_name)])
|
||||||
|
caller.db.current_action = action_script
|
||||||
|
|
||||||
|
|
||||||
|
class CastCompleteScript(CmdActionScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
super().at_script_creation()
|
||||||
|
|
||||||
|
self.key = "cmd_cast_complete"
|
||||||
|
self.desc = ""
|
||||||
|
|
||||||
|
self.db.spell = ""
|
||||||
|
self.db.target_name = ""
|
||||||
|
|
||||||
|
def at_repeat(self):
|
||||||
|
caller = self.obj
|
||||||
|
|
||||||
|
if has_effect(caller, "is_busy"):
|
||||||
|
toggle_effect(caller, "is_busy")
|
||||||
|
|
||||||
|
# perform casting (the recipe handles all returns to caller).
|
||||||
|
result = cast(caller, self.db.spell, target_name=self.db.target_name)
|
||||||
|
if result and not isinstance(result, bool):
|
||||||
|
for obj in result:
|
||||||
|
if inherits_from(obj, "typeclasses.objects.Feature"):
|
||||||
|
obj.location = caller.location
|
||||||
|
else:
|
||||||
|
obj.location = caller
|
||||||
|
|
||||||
|
def busy_msg(self):
|
||||||
|
return "casting {}.".format(self.db.spell)
|
||||||
|
|
||||||
|
|
||||||
|
class CastingError(RuntimeError):
|
||||||
|
"""
|
||||||
|
Casting error.
|
||||||
|
|
||||||
|
"""
|
|
@ -1,26 +1,30 @@
|
||||||
from evennia.prototypes import spawner
|
|
||||||
|
|
||||||
def has_tag(obj, key, category):
|
def has_tag(obj, key, category):
|
||||||
return obj.tags.get(key=key, category=category) != None;
|
return obj.tags.get(key=key, category=category) is not None;
|
||||||
|
|
||||||
|
|
||||||
def fmt_light(message):
|
def fmt_light(message):
|
||||||
return "|r\u2600|y {} |r\u2600|n".format(message)
|
return "|r\u2600|y {} |r\u2600|n".format(message)
|
||||||
|
|
||||||
|
|
||||||
def fmt_dark(message):
|
def fmt_dark(message):
|
||||||
return "|w\u2600|=h {} |w\u2600|n".format(message)
|
return "|w\u2600|=h {} |w\u2600|n".format(message)
|
||||||
|
|
||||||
|
|
||||||
def toggle_effect(obj, effect):
|
def toggle_effect(obj, effect):
|
||||||
if has_tag(obj, effect, "effect"):
|
if has_tag(obj, effect, "effect"):
|
||||||
obj.tags.remove(effect, category="effect")
|
obj.tags.remove(effect, category="effect")
|
||||||
else:
|
else:
|
||||||
obj.tags.add(effect, category="effect")
|
obj.tags.add(effect, category="effect")
|
||||||
|
|
||||||
|
|
||||||
def has_effect(obj, effect):
|
def has_effect(obj, effect):
|
||||||
return has_tag(obj, effect, "effect")
|
return has_tag(obj, effect, "effect")
|
||||||
|
|
||||||
|
|
||||||
def has_effect_in(obj, effects):
|
def has_effect_in(obj, effects):
|
||||||
return any(True for effect in effects if has_tag(obj, effect, "effect"))
|
return any(True for effect in effects if has_tag(obj, effect, "effect"))
|
||||||
|
|
||||||
|
|
||||||
def indefinite_article(name):
|
def indefinite_article(name):
|
||||||
"""Return the right english indefinite article for given name."""
|
"""Return the right english indefinite article for given name."""
|
||||||
the_vowels = ["a","e","i","o","u"]
|
the_vowels = ["a","e","i","o","u"]
|
||||||
|
@ -30,4 +34,4 @@ def indefinite_article(name):
|
||||||
else:
|
else:
|
||||||
value = "a"
|
value = "a"
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -1,45 +1,63 @@
|
||||||
from evennia import utils, create_script, logger
|
from evennia import create_script
|
||||||
from evennia.utils import inherits_from
|
|
||||||
|
|
||||||
from typeclasses import effects
|
from typeclasses import effects
|
||||||
from typeclasses.mobs import Mob
|
|
||||||
from utils.utils import has_effect
|
from utils.utils import has_effect
|
||||||
|
|
||||||
|
from utils.spells import Spell
|
||||||
def spell_light(caller, target, **kwargs):
|
|
||||||
if not target:
|
|
||||||
target_obj = caller
|
|
||||||
else:
|
|
||||||
target_obj = caller.search(target, location=[caller, caller.location])
|
|
||||||
|
|
||||||
if not target_obj:
|
|
||||||
return
|
|
||||||
|
|
||||||
if has_effect(target_obj, "emit_magic_light"):
|
|
||||||
caller.msg("{} already has a magical light on itself.".format(target_obj.name))
|
|
||||||
return
|
|
||||||
|
|
||||||
light_script = create_script(effects.EffectMagicalLight, obj=target_obj)
|
|
||||||
caller.msg("You cast |wlight|n on {}.".format(target_obj.name))
|
|
||||||
|
|
||||||
|
|
||||||
def spell_charm(caller, target, **kwargs):
|
class LightSpell(Spell):
|
||||||
if not target:
|
"""Make the target emit magical light"""
|
||||||
caller.msg("You need someone to place your charm on.")
|
name = "light" # name to refer to this recipe as
|
||||||
return
|
casting_time = 1
|
||||||
|
duration = 60
|
||||||
|
target_type = "SINGLE"
|
||||||
|
success_message = "You cast |wlight|n on {}."
|
||||||
|
|
||||||
target_obj = caller.search(target, location=[caller.location])
|
def do_cast(self, **kwargs):
|
||||||
|
target_obj = self.target_obj
|
||||||
|
if has_effect(target_obj, "emit_magic_light"):
|
||||||
|
self.msg("{} already has a magical light on itself.".format(target_obj.name))
|
||||||
|
return False
|
||||||
|
|
||||||
if not target_obj:
|
create_script(effects.EffectMagicalLight, obj=target_obj, interval=self.duration)
|
||||||
return
|
self.success_message = self.success_message.format(target_obj.name)
|
||||||
|
|
||||||
if not inherits_from(target_obj, Mob):
|
return True
|
||||||
caller.msg("You cannot charm {}".format(target_obj.name))
|
|
||||||
return
|
|
||||||
|
|
||||||
if has_effect(target_obj, "charm"):
|
# def spell_light(caller, target, **kwargs):
|
||||||
caller.msg("{} is already charmed.".format(target_obj.name))
|
# if not target:
|
||||||
return
|
# target_obj = caller
|
||||||
|
# else:
|
||||||
|
# target_obj = caller.search(target, location=[caller, caller.location])
|
||||||
|
#
|
||||||
|
# if not target_obj:
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# if has_effect(target_obj, "emit_magic_light"):
|
||||||
|
# caller.msg("{} already has a magical light on itself.".format(target_obj.name))
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# light_script = create_script(effects.EffectMagicalLight, obj=target_obj)
|
||||||
|
# caller.msg("You cast |wlight|n on {}.".format(target_obj.name))
|
||||||
|
|
||||||
charm_script = create_script(effects.EffectCharm, obj=target_obj, attributes=[("source", caller.dbref)])
|
|
||||||
caller.msg("You cast |wcharm|n on {}.".format(target_obj.name))
|
# def spell_charm(caller, target, **kwargs):
|
||||||
|
# if not target:
|
||||||
|
# caller.msg("You need someone to place your charm on.")
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# target_obj = caller.search(target, location=[caller.location])
|
||||||
|
#
|
||||||
|
# if not target_obj:
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# if not inherits_from(target_obj, Mob):
|
||||||
|
# caller.msg("You cannot charm {}".format(target_obj.name))
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# if has_effect(target_obj, "charm"):
|
||||||
|
# caller.msg("{} is already charmed.".format(target_obj.name))
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
# charm_script = create_script(effects.EffectCharm, obj=target_obj, attributes=[("source", caller.dbref)])
|
||||||
|
# caller.msg("You cast |wcharm|n on {}.".format(target_obj.name))
|
Loading…
Reference in a new issue