Add more parts of the turnbased combat tutorial

This commit is contained in:
Griatch 2022-07-11 20:45:30 +02:00
parent 83395211cc
commit e7f8926b23
7 changed files with 399 additions and 70 deletions

View file

@ -19,7 +19,7 @@ class EquipmentHandler:
""" """
_Knave_ puts a lot of emphasis on the inventory. You have CON_DEFENSE inventory _Knave_ puts a lot of emphasis on the inventory. You have CON_DEFENSE inventory
slots. Some things, like torches can fit multiple in one slot, other (like slots. Some things, like torches can fit multiple in one slot, other (like
big weapons) use more than one slot. The items carried and wielded has a big impact big weapons and armor) use more than one slot. The items carried and wielded has a big impact
on character customization - even magic requires carrying a runestone per spell. on character customization - even magic requires carrying a runestone per spell.
The inventory also doubles as a measure of negative effects. Getting soaked in mud The inventory also doubles as a measure of negative effects. Getting soaked in mud
@ -147,6 +147,43 @@ class EquipmentHandler:
weapon = slots[WieldLocation.WEAPON_HAND] weapon = slots[WieldLocation.WEAPON_HAND]
return weapon return weapon
def display_loadout(self):
"""
Get a visual representation of your current loadout.
Returns:
str: The current loadout.
"""
slots = self.slots
one_hand = None
weapon_str = "You are fighting with your bare fists"
shield_str = " and have no shield."
armor_str = "You wear no armor"
helmet_str = " and no helmet."
two_hands = slots[WieldLocation.TWO_HANDS]
if two_hands:
weapon_str = f"You wield {two_hands} with both hands"
shield_str = f" (you can't hold a shield at the same time)."
else:
one_hands = slots[WieldLocation.WEAPON_HAND]
if one_hands:
weapon_str = f"You are wielding {one_hands} in one hand."
shield = slots[WieldLocation.SHIELD_HAND]
if shield:
shield_str = f"You have {shield} in your off hand."
armor = slots[WieldLocation.BODY]
if armor:
armor_str = f"You are wearing {armor}"
helmet = slots[WieldLocation.BODY]
if helmet:
helmet_str = f" and {helmet} on your head."
return f"{weapon_str}{shield_str}\n{armor_str}{helmet_str}"
def use(self, obj): def use(self, obj):
""" """
Make use of item - this makes use of the object's wield slot to decide where Make use of item - this makes use of the object's wield slot to decide where
@ -242,6 +279,51 @@ class EquipmentHandler:
self._save() self._save()
return ret return ret
def get_wieldable_objects_from_backpack(self):
"""
Get all wieldable weapons (or spell runes) from backpack. This is useful in order to
have a list to select from when swapping your wielded loadout.
Returns:
list: A list of objects with a suitable `inventory_use_slot`. We don't check
quality, so this may include broken items (we may want to visually show them
in the list after all).
"""
return [obj for obj in slots[WieldLocation.BACKPACK]
if obj.inventory_use_slot in (
WieldLocation.WEAPON_HAND,
WieldLocation.TWO_HANDS,
WieldLocation.SHIELD_HAND)]
def get_wearable_objects_from_backpack(self):
"""
Get all wearable items (armor or helmets) from backpack. This is useful in order to
have a list to select from when swapping your worn loadout.
Returns:
list: A list of objects with a suitable `inventory_use_slot`. We don't check
quality, so this may include broken items (we may want to visually show them
in the list after all).
"""
return [obj for obj in slots[WieldLocation.BACKPACK]
if obj.inventory_use_slot in (
WieldLocation.BODY,
WieldLocation.HEAD
)]
def get_usable_objects_from_backpack(self):
"""
Get all 'usable' items (like potions) from backpack. This is useful for getting a
list to select from.
Returns:
list: A list of objects that are usable.
"""
return [obj for obj in slots[WieldLocation.BACKPACK] if obj.uses > 0]
class LivingMixin: class LivingMixin:
""" """

View file

@ -108,7 +108,9 @@ from evennia.utils import evtable, dbserialize, delay, evmenu
from .enums import Ability from .enums import Ability
from . import rules from . import rules
# for simplicity, we have a default duration for advantages/disadvantages
COMBAT_HANDLER_KEY = "evadventure_turnbased_combathandler"
COMBAT_HANDLER_INTERVAL = 60
class CombatFailure(RuntimeError): class CombatFailure(RuntimeError):
@ -134,8 +136,9 @@ class CombatAction:
aliases = [] aliases = []
help_text = "Combat action to perform." help_text = "Combat action to perform."
# if no target is needed (always affect oneself) # the next combat menu node to go to - this ties the combat action into the UI
no_target = False # use None to do nothing (jump directly to registering the action)
next_menu_node = "node_select_target"
# action to echo to everyone. # action to echo to everyone.
post_action_text = "{combatant} performed an action." post_action_text = "{combatant} performed an action."
@ -199,7 +202,7 @@ class CombatAction:
if available, should describe what the action does. if available, should describe what the action does.
""" """
return True if self.uses is None else self.uses < self.max_uses return True if self.uses is None else self.uses < (self.max_uses or 0)
def pre_use(self, *args, **kwargs): def pre_use(self, *args, **kwargs):
pass pass
@ -364,7 +367,7 @@ class CombatActionFlee(CombatAction):
aliases = ("d", "disengage", "flee") aliases = ("d", "disengage", "flee")
# this only affects us # this only affects us
no_target = True next_menu_node = "node_register_action"
help_text = ( help_text = (
"Disengage from combat. Use successfully two times in a row to leave combat at the " "Disengage from combat. Use successfully two times in a row to leave combat at the "
@ -419,6 +422,45 @@ class CombatActionBlock(CombatAction):
pass # they are getting away! pass # they are getting away!
class CombatActionSwapWieldedWeaponOrSpell(CombatAction):
"""
Swap Wielded weapon or spell.
"""
key = "Swap weapon/rune/shield"
desc = "Swap currently wielded weapon, shield or spell-rune."
aliases = ("s", "swap", "draw", "swap weapon", "draw weapon",
"swap rune", "draw rune", "swap spell", "draw spell")
help_text = ("Draw a new weapon or spell-rune from your inventory, "
"replacing your current loadout")
next_menu_node = "node_select_wield_from_inventory"
post_action_text = "{combatant} switches weapons."
def use(self, combatant, item, *args, **kwargs):
# this will make use of the item
combatant.inventory.use(item)
class CombatActionUseItem(CombatAction):
"""
Use an item from inventory.
"""
key = "Use an item from backpack"
desc = "Use an item from your inventory."
aliases = ("u", "use", "use item")
help_text = "Choose an item from your inventory to use."
next_menu_node = "node_select_use_item_from_inventory"
post_action_text = "{combatant} used an item."
def use(self, combatant, item, *args, **kwargs):
item.use(combatant, *args, **kwargs)
class CombatActionDoNothing(CombatAction): class CombatActionDoNothing(CombatAction):
""" """
Do nothing this turn. Do nothing this turn.
@ -431,7 +473,7 @@ class CombatActionDoNothing(CombatAction):
help_text = "Hold you position, doing nothing." help_text = "Hold you position, doing nothing."
# affects noone else # affects noone else
no_target = True next_menu_node = "node_register_action"
post_action_text = "{combatant} does nothing this turn." post_action_text = "{combatant} does nothing this turn."
@ -439,7 +481,9 @@ class CombatActionDoNothing(CombatAction):
class EvAdventureCombatHandler(DefaultScript): class EvAdventureCombatHandler(DefaultScript):
""" """
This script is created when combat is initialized and stores a queue This script is created when combat is initialized and stores a queue
of all active participants. It's also possible to join (or leave) the fray later. of all active participants.
It's also possible to join (or leave) the fray later.
""" """
@ -450,6 +494,7 @@ class EvAdventureCombatHandler(DefaultScript):
default_action_classes = [ default_action_classes = [
CombatActionAttack, CombatActionAttack,
CombatActionStunt, CombatActionStunt,
CombatActionSwapWieldedWeaponOrSpell,
CombatActionUseItem, CombatActionUseItem,
CombatActionFlee, CombatActionFlee,
CombatActionBlock, CombatActionBlock,
@ -481,7 +526,8 @@ class EvAdventureCombatHandler(DefaultScript):
def at_script_creation(self): def at_script_creation(self):
# how often this script ticks - the max length of each turn (in seconds) # how often this script ticks - the max length of each turn (in seconds)
self.interval = 60 self.key = COMBAT_HANDLER_KEY
self.interval = COMBAT_HANDLER_INTERVAL
def at_repeat(self, **kwargs): def at_repeat(self, **kwargs):
""" """
@ -621,6 +667,7 @@ class EvAdventureCombatHandler(DefaultScript):
""" """
if combatant not in self.combatants: if combatant not in self.combatants:
self.combatants.append(combatant) self.combatants.append(combatant)
combatant.db.turnbased_combathandler = self
# allow custom character actions (not used by default) # allow custom character actions (not used by default)
custom_action_classes = combatant.db.custom_combat_actions or [] custom_action_classes = combatant.db.custom_combat_actions or []
@ -637,13 +684,14 @@ class EvAdventureCombatHandler(DefaultScript):
{ {
"node_wait_start": node_wait_start, "node_wait_start": node_wait_start,
"node_select_target": node_select_target, "node_select_target": node_select_target,
"node_selct_action": node_select_action, "node_select_action": node_select_action,
"node_wait_turn": node_wait_turn, "node_wait_turn": node_wait_turn,
}, },
startnode="node_wait_turn", startnode="node_wait_turn",
auto_quit=False, auto_quit=False,
persistent=True, persistent=True,
session=session, session=session,
combathandler=self # makes this available as combatant.ndb._evmenu.combathandler
) )
def remove_combatant(self, combatant): def remove_combatant(self, combatant):
@ -658,6 +706,7 @@ class EvAdventureCombatHandler(DefaultScript):
self.combatants.remove(combatant) self.combatants.remove(combatant)
self.combatant_actions.pop(combatant, None) self.combatant_actions.pop(combatant, None)
combatant.ndb._evmenu.close_menu() combatant.ndb._evmenu.close_menu()
del combatant.db.turnbased_combathandler
def start_combat(self): def start_combat(self):
""" """
@ -866,7 +915,7 @@ def _register_action(caller, raw_string, **kwargs):
action_key = kwargs.get["action_key"] action_key = kwargs.get["action_key"]
action_args = kwargs["action_args"] action_args = kwargs["action_args"]
action_kwargs = kwargs["action_kwargs"] action_kwargs = kwargs["action_kwargs"]
action_target = kwargs["action_target"] action_target = kwargs.get("action_target")
combat_handler = caller._evmenu.combathandler combat_handler = caller._evmenu.combathandler
combat_handler.register_action( combat_handler.register_action(
caller, action_key, action_target, *action_args, **action_kwargs) caller, action_key, action_target, *action_args, **action_kwargs)
@ -884,41 +933,125 @@ def node_select_target(caller, raw_string, **kwargs):
action_key = kwargs.get("action_key") action_key = kwargs.get("action_key")
action_args = kwargs.get("action_args") action_args = kwargs.get("action_args")
action_kwargs = kwargs.get("action_kwargs") action_kwargs = kwargs.get("action_kwargs")
combat = caller.scripts.get("combathandler") combat = caller.ndb._evmenu.combathandler
text = "Select target for |w{action_key}|n." text = "Select target for |w{action_key}|n."
# make the apply-self option always the first one, give it key 0 # make the apply-self option always the first one, give it key 0
kwargs["action_target"] = caller
options = [ options = [
{ {
"key": "0", "key": "0",
"desc": "(yourself)", "desc": "(yourself)",
"goto": ( "goto": (_register_action, kwargs)
_register_action,
{
"action_key": action_key,
"action_args": action_args,
"action_kwargs": action_kwargs,
"action_target": caller,
},
),
} }
] ]
# filter out ourselves and then make options for everyone else # filter out ourselves and then make options for everyone else
combatants = [combatant for combatant in combat.combatants if combatant is not caller] combatants = [combatant for combatant in combat.combatants if combatant is not caller]
for combatant in combatants: for combatant in combatants:
# automatic menu numbering starts from 1 # automatic menu numbering starts from 1
kwargs["action_target"] = combatant
options.append( options.append(
{ {
"desc": combatant.key, "desc": combatant.key,
"goto": ( "goto": (_register_action, kwargs)
_register_action, }
)
# add ability to cancel
options.append(
{ {
"action_key": action_key, "key": "_default",
"action_args": action_args, "desc": "(No input to Abort and go back)",
"action_kwargs": action_kwargs, "goto": "node_select_action"
"action_target": combatant, }
}, )
),
return text, options
def _item_broken(caller, raw_string, **kwargs):
caller.msg("|rThis item is broken and unusable!|n")
return None # back to previous node
def node_select_wield_from_inventory(caller, raw_string, **kwargs):
"""
Menu node allowing for wielding item(s) from inventory.
"""
combat = caller.ndb._evmenu.combathandler
loadout = caller.inventory.display_loadout()
text = (f"{loadout}\nSelect weapon, spell or shield to draw. It will swap out "
"anything already in the same hand (you can't change armor or helmet in combat).")
# get a list of all suitable weapons/spells/shields
options = []
for obj in caller.inventory.get_wieldable_objects_from_backpack():
if obj.quality <= 0:
# object is broken
options.append(
{
"desc": f"|Rstr(obj)|n",
"goto": _item_broken,
}
)
else:
# normally working item
kwargs['action_args'] = (obj,)
options.append(
{
"desc": str(obj),
"goto": (_register_action, kwargs)
}
)
# add ability to cancel
options.append(
{
"key": "_default",
"desc": "(No input to Abort and go back)",
"goto": "node_select_action"
}
)
return text, options
def node_select_use_item_from_inventory(caller, raw_string, **kwargs):
"""
Menu item allowing for using usable items (like potions) from inventory.
"""
combat = caller.ndb._evmenu.combathandler
text = "Select an item to use."
# get a list of all suitable weapons/spells/shields
options = []
for obj in caller.inventory.get_usable_objects_from_backpack():
if obj.quality <= 0:
# object is broken
options.append(
{
"desc": f"|Rstr(obj)|n",
"goto": _item_broken,
}
)
else:
# normally working item
kwargs['action_args'] = (obj,)
options.append(
{
"desc": str(obj),
"goto": (_register_action, kwargs)
}
)
# add ability to cancel
options.append(
{
"key": "_default",
"desc": "(No input to Abort and go back)",
"goto": "node_select_action"
} }
) )
@ -941,8 +1074,8 @@ def node_select_action(caller, raw_string, **kwargs):
Menu node for selecting a combat action. Menu node for selecting a combat action.
""" """
combat = caller.scripts.get("combathandler") combat = caller.ndb._evmenu.combathandler
text = combat.get_previous_turn_status(caller) text = combat.get_combat_summary(caller)
options = [] options = []
for icount, action in enumerate(combat.get_available_actions(caller)): for icount, action in enumerate(combat.get_available_actions(caller)):
@ -968,9 +1101,9 @@ def node_select_action(caller, raw_string, **kwargs):
) )
} }
) )
elif action.no_target: elif action.next_menu_node is None:
# action is available, and requires no target. Redirect to register # action is available, but needs no intermediary step. Redirect to register
# without going via the select-target node. # the action immediately
options.append( options.append(
{ {
"key": key, "key": key,
@ -987,13 +1120,13 @@ def node_select_action(caller, raw_string, **kwargs):
} }
) )
else: else:
# action is available and requires a target, so we will select a target next. # action is available and next_menu_node is set to point to the next node we want
options.append( options.append(
{ {
"key": key, "key": key,
"desc": desc, "desc": desc,
"goto": ( "goto": (
"node_select_target", action.next_menu_node,
{ {
"action_key": action.key, "action_key": action.key,
"action_args": (), "action_args": (),
@ -1002,6 +1135,14 @@ def node_select_action(caller, raw_string, **kwargs):
), ),
} }
) )
# add ability to cancel
options.append(
{
"key": "_default",
"desc": "(No input to Abort and go back)",
"goto": "node_select_action"
}
)
return text, options return text, options
@ -1044,9 +1185,12 @@ def node_wait_start(caller, raw_string, **kwargs):
# -------------- end of combat menu definitions # -------------- end of combat menu definitions
def join_combat(caller, *targets, combathandler=None, session=None): def join_combat(caller, *targets, session=None):
""" """
Join or create a new combat involving caller and at least one target, Join or create a new combat involving caller and at least one target. The combat
is started on the current room location - this means there can only be one combat
in each room (this is not hardcoded in the combat per-se, but it makes sense for
this implementation).
Args: Args:
caller (Object): The one starting the combat. caller (Object): The one starting the combat.
@ -1055,9 +1199,6 @@ def join_combat(caller, *targets, combathandler=None, session=None):
one opponent!). one opponent!).
Keyword Args: Keyword Args:
combathandler (EvAdventureCombatHandler): If not given, a new combat will be created and
at least one `*targets` argument must be provided. If given, caller will
join an existing combat.
session (Session, optional): A player session to use. This is useful for multisession modes. session (Session, optional): A player session to use. This is useful for multisession modes.
Returns: Returns:
@ -1065,15 +1206,19 @@ def join_combat(caller, *targets, combathandler=None, session=None):
""" """
created = False created = False
if not combathandler: location = caller.location
if not location:
raise CombatFailure("Must have a location to start combat.")
if not targets: if not targets:
raise CombatFailure("Must have an opponent to start combat.") raise CombatFailure("Must have an opponent to start combat.")
combathandler, _ = EvAdventureCombatHandler.create(
f"Combat_{datetime.utcnow()}", combathandler = location.scripts.get(COMBAT_HANDLER_KEY).first()
autostart=False, # means we must use .start() to start the script if not combathandler:
) combathandler = location.scripts.add(EvAdventureCombatHandler, autostart=False)
created = True created = True
# it's safe to add a combatant to the same combat more than once
combathandler.add_combatant(caller, session=session) combathandler.add_combatant(caller, session=session)
for target in targets: for target in targets:
combathandler.add_combatant(target, session=session) combathandler.add_combatant(target, session=session)

View file

@ -0,0 +1,58 @@
"""
EvAdventure commands and cmdsets.
"""
from evennia import Command, default_cmds
from . combat_turnbased import join_combat
class EvAdventureCommand(Command):
"""
Base EvAdventure command. This is on the form
command <args>
where whitespace around the argument(s) are stripped.
"""
def parse(self):
self.args = self.args.strip()
class CmdAttackTurnBased(EvAdventureCommand):
"""
Attack a target or join an existing combat.
Usage:
attack <target>
attack <target>, <target>, ...
If the target is involved in combat already, you'll join combat with
the first target you specify. Attacking multiple will draw them all into
combat.
This will start/join turn-based, combat, where you have a limited
time to decide on your next action from a menu of options.
"""
def parse(self):
super().parse()
self.targets = [name.strip() for name in self.args.split(",")]
def func(self):
# find if
target_objs = []
for target in self.targets:
target_obj = self.caller.search(target)
if target_obj:
# show a warning but don't abort
continue
target_objs.append(target_obj)
if target_objs:
join_combat(self.caller, *target_objs, session=self.session)

View file

@ -20,13 +20,16 @@ class EvAdventureObject(DefaultObject):
""" """
# inventory management # inventory management
inventory_use_slot = AttributeProperty(default=WieldLocation.BACKPACK) inventory_use_slot = AttributeProperty(WieldLocation.BACKPACK)
# how many inventory slots it uses (can be a fraction) # how many inventory slots it uses (can be a fraction)
size = AttributeProperty(default=1) size = AttributeProperty(1)
armor = AttributeProperty(default=0) armor = AttributeProperty(0)
# items that are usable (like potions) have a value larger than 0. Wieldable items
# like weapons, armor etc are not 'usable' in this respect.
uses = AttributeProperty(0)
# when 0, item is destroyed and is unusable # when 0, item is destroyed and is unusable
quality = AttributeProperty(default=1) quality = AttributeProperty(1)
value = AttributeProperty(default=0) value = AttributeProperty(0)
class EvAdventureObjectFiller(EvAdventureObject): class EvAdventureObjectFiller(EvAdventureObject):
@ -41,8 +44,30 @@ class EvAdventureObjectFiller(EvAdventureObject):
meaning it's unusable. meaning it's unusable.
""" """
quality = AttributeProperty(0)
quality = AttributeProperty(default=0)
class EvAdventureConsumable(EvAdventureObject):
"""
Item that can be 'used up', like a potion or food. Weapons, armor etc does not
have a limited usage in this way.
"""
inventory_use_slot = AttributeProperty(WieldLocation.BACKPACK)
size = AttributeProperty(0.25)
uses = AttributeProperty(1)
def use(self, user, *args, **kwargs):
"""
Consume a 'use' of this item. Once it reaches 0 uses, it should normally
not be usable anymore and probably be deleted.
Args:
user (Object): The one using the item.
*args, **kwargs: Extra arguments depending on the usage and item.
"""
pass
class EvAdventureWeapon(EvAdventureObject): class EvAdventureWeapon(EvAdventureObject):
@ -53,13 +78,9 @@ class EvAdventureWeapon(EvAdventureObject):
inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND) inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND)
attack_type = AttributeProperty(default=Ability.STR) attack_type = AttributeProperty(Ability.STR)
defense_type = AttributeProperty(default=Ability.ARMOR) defense_type = AttributeProperty(Ability.ARMOR)
damage_roll = AttributeProperty(default="1d6") damage_roll = AttributeProperty("1d6")
# at which ranges this weapon can be used. If not listed, unable to use
distance_optimal = AttributeProperty(default=0) # normal usage (fists)
distance_suboptimal = AttributeProperty(default=None) # disadvantage (fists)
class EvAdventureRunestone(EvAdventureWeapon): class EvAdventureRunestone(EvAdventureWeapon):
@ -70,3 +91,8 @@ class EvAdventureRunestone(EvAdventureWeapon):
they are quite powerful (and scales with caster level). they are quite powerful (and scales with caster level).
""" """
inventory_use_slot = AttributeProperty(WieldLocation.TWO_HANDS)
attack_type = AttributeProperty(Ability.INT)
defense_type = AttributeProperty(Ability.CON)
damage_roll = AttributeProperty("1d8")

View file

@ -0,0 +1,12 @@
"""
EvAdventure rooms.
"""
from evennia import DefaultRoom
class EvAdventureRoom(DefaultRoom):
pass

View file

@ -6,6 +6,7 @@ Helpers for testing evadventure modules.
from evennia.utils import create from evennia.utils import create
from ..characters import EvAdventureCharacter from ..characters import EvAdventureCharacter
from ..objects import EvAdventureObject from ..objects import EvAdventureObject
from ..rooms import EvAdventureRoom
from .. import enums from .. import enums
@ -17,7 +18,9 @@ class EvAdventureMixin:
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.character = create.create_object(EvAdventureCharacter, key="testchar") self.location = create.create_object(EvAdventureRoom, key="testroom")
self.character = create.create_object(EvAdventureCharacter, key="testchar",
location=self.location)
self.helmet = create.create_object( self.helmet = create.create_object(
EvAdventureObject, EvAdventureObject,
key="helmet", key="helmet",

View file

@ -69,6 +69,9 @@ class ScriptHandler(object):
in script definition and listings) in script definition and listings)
autostart (bool, optional): Start the script upon adding it. autostart (bool, optional): Start the script upon adding it.
Returns:
Script: The newly created Script.
""" """
if self.obj.__dbclass__.__name__ == "AccountDB": if self.obj.__dbclass__.__name__ == "AccountDB":
# we add to an Account, not an Object # we add to an Account, not an Object
@ -76,21 +79,21 @@ class ScriptHandler(object):
scriptclass, key=key, account=self.obj, autostart=autostart scriptclass, key=key, account=self.obj, autostart=autostart
) )
else: else:
# the normal - adding to an Object. We wait to autostart so we can differentiate # adding to an Object. We wait to autostart so we can differentiate
# a failing creation from a script that immediately starts/stops. # a failing creation from a script that immediately starts/stops.
script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=False) script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=False)
if not script: if not script:
logger.log_err("Script %s failed to be created/started." % scriptclass) logger.log_err(f"Script {scriptclass} failed to be created.")
return False return None
if autostart: if autostart:
script.start() script.start()
if not script.id: if not script.id:
# this can happen if the script has repeats=1 or calls stop() in at_repeat. # this can happen if the script has repeats=1 or calls stop() in at_repeat.
logger.log_info( logger.log_info(
"Script %s started and then immediately stopped; " f"Script {scriptclass} started and then immediately stopped; "
"it could probably be a normal function." % scriptclass "it could probably be a normal function."
) )
return True return script
def start(self, key): def start(self, key):
""" """
@ -118,10 +121,10 @@ class ScriptHandler(object):
key (str): Search criterion, the script's key or dbref. key (str): Search criterion, the script's key or dbref.
Returns: Returns:
scripts (list): The found scripts matching `key`. scripts (queryset): The found scripts matching `key`.
""" """
return list(ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key)) return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=key)
def delete(self, key=None): def delete(self, key=None):
""" """