diff --git a/evennia/contrib/turnbattle/tb_items.py b/evennia/contrib/turnbattle/tb_items.py index d0e9fe8e34..cfb511b4ad 100644 --- a/evennia/contrib/turnbattle/tb_items.py +++ b/evennia/contrib/turnbattle/tb_items.py @@ -28,7 +28,7 @@ This module includes a number of example conditions: Haste: +1 action per turn Paralyzed: No actions per turn Frightened: Character can't use the 'attack' command - + Since conditions can have a wide variety of effects, their code is scattered throughout the other functions wherever they may apply. @@ -70,7 +70,7 @@ from random import randint from evennia import DefaultCharacter, Command, default_cmds, DefaultScript from evennia.commands.default.muxcommand import MuxCommand from evennia.commands.default.help import CmdHelp -from evennia.utils.spawner import spawn +from evennia.prototypes.spawner import spawn from evennia import TICKER_HANDLER as tickerhandler """ @@ -230,10 +230,10 @@ def apply_damage(defender, damage): def at_defeat(defeated): """ Announces the defeat of a fighter in combat. - + Args: defeated (obj): Fighter that's been defeated. - + Notes: All this does is announce a defeat message by default, but if you want anything else to happen to defeated fighters (like putting them @@ -250,7 +250,7 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None, Args: attacker (obj): Character doing the attacking defender (obj): Character being attacked - + Options: attack_value (int): Override for attack roll defense_value (int): Override for defense value @@ -353,11 +353,11 @@ def spend_action(character, actions, action_name=None): def spend_item_use(item, user): """ Spends one use on an item with limited uses. - + Args: item (obj): Item being used user (obj): Character using the item - + Notes: If item.db.item_consumable is 'True', the item is destroyed if it runs out of uses - if it's a string instead of 'True', it will also @@ -365,32 +365,32 @@ def spend_item_use(item, user): as the name of the prototype to spawn. """ item.db.item_uses -= 1 # Spend one use - + if item.db.item_uses > 0: # Has uses remaining # Inform the player user.msg("%s has %i uses remaining." % (item.key.capitalize(), item.db.item_uses)) - + else: # All uses spent - + if not item.db.item_consumable: # Item isn't consumable # Just inform the player that the uses are gone user.msg("%s has no uses remaining." % item.key.capitalize()) - + else: # If item is consumable if item.db.item_consumable == True: # If the value is 'True', just destroy the item user.msg("%s has been consumed." % item.key.capitalize()) item.delete() # Delete the spent item - + else: # If a string, use value of item_consumable to spawn an object in its place residue = spawn({"prototype":item.db.item_consumable})[0] # Spawn the residue residue.location = item.location # Move the residue to the same place as the item user.msg("After using %s, you are left with %s." % (item, residue)) item.delete() # Delete the spent item - + def use_item(user, item, target): """ Performs the action of using an item. - + Args: user (obj): Character using the item item (obj): Item being used @@ -399,53 +399,53 @@ def use_item(user, item, target): # If item is self only and no target given, set target to self. if item.db.item_selfonly and target == None: target = user - + # If item is self only, abort use if used on others. if item.db.item_selfonly and user != target: user.msg("%s can only be used on yourself." % item) return - + # Set kwargs to pass to item_func kwargs = {} - if item.db.item_kwargs: - kwargs = item.db.item_kwargs - + if item.db.item_kwargs: + kwargs = item.db.item_kwargs + # Match item_func string to function try: item_func = ITEMFUNCS[item.db.item_func] except KeyError: # If item_func string doesn't match to a function in ITEMFUNCS user.msg("ERROR: %s not defined in ITEMFUNCS" % item.db.item_func) return - + # Call the item function - abort if it returns False, indicating an error. # This performs the actual action of using the item. # Regardless of what the function returns (if anything), it's still executed. if item_func(item, user, target, **kwargs) == False: return - + # If we haven't returned yet, we assume the item was used successfully. # Spend one use if item has limited uses if item.db.item_uses: spend_item_use(item, user) - + # Spend an action if in combat if is_in_combat(user): spend_action(user, 1, action_name="item") - + def condition_tickdown(character, turnchar): """ Ticks down the duration of conditions on a character at the start of a given character's turn. - + Args: character (obj): Character to tick down the conditions of turnchar (obj): Character whose turn it currently is - + Notes: In combat, this is called on every fighter at the start of every character's turn. Out of combat, it's instead called when a character's at_update() hook is called, which is every 30 seconds by default. """ - + for key in character.db.conditions: # The first value is the remaining turns - the second value is whose turn to count down on. condition_duration = character.db.conditions[key][0] @@ -459,11 +459,11 @@ def condition_tickdown(character, turnchar): # If the duration is brought down to 0, remove the condition and inform everyone. character.location.msg_contents("%s no longer has the '%s' condition." % (str(character), str(key))) del character.db.conditions[key] - + def add_condition(character, turnchar, condition, duration): """ Adds a condition to a fighter. - + Args: character (obj): Character to give the condition to turnchar (obj): Character whose turn to tick down the condition on in combat @@ -501,7 +501,7 @@ class TBItemsCharacter(DefaultCharacter): """ Adds attributes for a character's current and maximum HP. We're just going to set this value at '100' by default. - + An empty dictionary is created to store conditions later, and the character is subscribed to the Ticker Handler, which will call at_update() on the character, with the interval @@ -536,17 +536,17 @@ class TBItemsCharacter(DefaultCharacter): self.msg("You can't move, you've been defeated!") return False return True - + def at_turn_start(self): """ Hook called at the beginning of this character's turn in combat. """ # Prompt the character for their turn and give some information. self.msg("|wIt's your turn! You have %i HP remaining.|n" % self.db.hp) - + # Apply conditions that fire at the start of each turn. self.apply_turn_conditions() - + def apply_turn_conditions(self): """ Applies the effect of conditions that occur at the start of each @@ -559,7 +559,7 @@ class TBItemsCharacter(DefaultCharacter): to_heal = self.db.max_hp - self.db.hp # Cap healing to max HP self.db.hp += to_heal self.location.msg_contents("%s regains %i HP from Regeneration." % (self, to_heal)) - + # Poisoned: does 4 to 8 damage at the start of character's turn if "Poisoned" in self.db.conditions: to_hurt = randint(POISON_RATE[0], POISON_RATE[1]) # Deal damage @@ -568,18 +568,18 @@ class TBItemsCharacter(DefaultCharacter): if self.db.hp <= 0: # Call at_defeat if poison defeats the character at_defeat(self) - + # Haste: Gain an extra action in combat. if is_in_combat(self) and "Haste" in self.db.conditions: self.db.combat_actionsleft += 1 self.msg("You gain an extra action this turn from Haste!") - + # Paralyzed: Have no actions in combat. if is_in_combat(self) and "Paralyzed" in self.db.conditions: self.db.combat_actionsleft = 0 self.location.msg_contents("%s is Paralyzed, and can't act this turn!" % self) self.db.combat_turnhandler.turn_end_check(self) - + def at_update(self): """ Fires every 30 seconds. @@ -602,7 +602,7 @@ class TBItemsCharacterTest(TBItemsCharacter): self.db.max_hp = 100 # Set maximum HP to 100 self.db.hp = self.db.max_hp # Set current HP to maximum self.db.conditions = {} # Set empty dict for conditions - + """ ---------------------------------------------------------------------------- @@ -651,7 +651,7 @@ class TBItemsTurnHandler(DefaultScript): # Announce the turn order. self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters)) - + # Start first fighter's turn. self.start_turn(self.db.fighters[0]) @@ -748,14 +748,14 @@ class TBItemsTurnHandler(DefaultScript): self.db.turn += 1 # Go to the next in the turn order. if self.db.turn > len(self.db.fighters) - 1: self.db.turn = 0 # Go back to the first in the turn order once you reach the end. - + newchar = self.db.fighters[self.db.turn] # Note the new character - + self.db.timer = TURN_TIMEOUT + self.time_until_next_repeat() # Reset the timer. self.db.timeout_warning_given = False # Reset the timeout warning. self.obj.msg_contents("%s's turn ends - %s's turn begins!" % (currentchar, newchar)) self.start_turn(newchar) # Start the new character's turn. - + # Count down condition timers. for fighter in self.db.fighters: condition_tickdown(fighter, newchar) @@ -785,7 +785,7 @@ class TBItemsTurnHandler(DefaultScript): # Initialize the character like you do at the start. self.initialize_for_combat(character) - + """ ---------------------------------------------------------------------------- COMMANDS START HERE @@ -865,7 +865,7 @@ class CmdAttack(Command): if not self.caller.db.hp: # Can't attack if you have no HP. self.caller.msg("You can't attack, you've been defeated.") return - + if "Frightened" in self.caller.db.conditions: # Can't attack if frightened self.caller.msg("You're too frightened to attack!") return @@ -1033,29 +1033,29 @@ class CmdUse(MuxCommand): item = self.caller.search(self.lhs, candidates=self.caller.contents) if not item: return - + # Search for target, if any is given target = None if self.rhs: target = self.caller.search(self.rhs) if not target: return - + # If in combat, can only use items on your turn if is_in_combat(self.caller): if not is_turn(self.caller): self.caller.msg("You can only use items on your turn.") return - + if not item.db.item_func: # Object has no item_func, not usable self.caller.msg("'%s' is not a usable item." % item.key.capitalize()) return - + if item.attributes.has("item_uses"): # Item has limited uses if item.db.item_uses <= 0: # Limited uses are spent self.caller.msg("'%s' has no uses remaining." % item.key.capitalize()) return - + # If everything checks out, call the use_item function use_item(self.caller, item, target) @@ -1077,7 +1077,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet): self.add(CmdDisengage()) self.add(CmdCombatHelp()) self.add(CmdUse()) - + """ ---------------------------------------------------------------------------- ITEM FUNCTIONS START HERE @@ -1091,7 +1091,7 @@ Every item function must take the following arguments: item (obj): The item being used user (obj): The character using the item target (obj): The target of the item use - + Item functions must also accept **kwargs - these keyword arguments can be used to define how different items that use the same function can have different effects (for example, different attack items doing different @@ -1104,25 +1104,25 @@ take and the effect they have on the result. def itemfunc_heal(item, user, target, **kwargs): """ Item function that heals HP. - + kwargs: min_healing(int): Minimum amount of HP recovered max_healing(int): Maximum amount of HP recovered """ - if not target: + if not target: target = user # Target user if none specified - + if not target.attributes.has("max_hp"): # Has no HP to speak of user.msg("You can't use %s on that." % item) return False # Returning false aborts the item use - + if target.db.hp >= target.db.max_hp: user.msg("%s is already at full health." % target) return False - + min_healing = 20 max_healing = 40 - + # Retrieve healing range from kwargs, if present if "healing_range" in kwargs: min_healing = kwargs["healing_range"][0] @@ -1132,62 +1132,62 @@ def itemfunc_heal(item, user, target, **kwargs): if target.db.hp + to_heal > target.db.max_hp: to_heal = target.db.max_hp - target.db.hp # Cap healing to max HP target.db.hp += to_heal - + user.location.msg_contents("%s uses %s! %s regains %i HP!" % (user, item, target, to_heal)) - + def itemfunc_add_condition(item, user, target, **kwargs): """ Item function that gives the target one or more conditions. - + kwargs: conditions (list): Conditions added by the item formatted as a list of tuples: (condition (str), duration (int or True)) - + Notes: Should mostly be used for beneficial conditions - use itemfunc_attack for an item that can give an enemy a harmful condition. """ conditions = [("Regeneration", 5)] - - if not target: + + if not target: target = user # Target user if none specified - + if not target.attributes.has("max_hp"): # Is not a fighter user.msg("You can't use %s on that." % item) return False # Returning false aborts the item use - + # Retrieve condition / duration from kwargs, if present if "conditions" in kwargs: conditions = kwargs["conditions"] - + user.location.msg_contents("%s uses %s!" % (user, item)) - + # Add conditions to the target - for condition in conditions: + for condition in conditions: add_condition(target, user, condition[0], condition[1]) - + def itemfunc_cure_condition(item, user, target, **kwargs): """ Item function that'll remove given conditions from a target. - + kwargs: to_cure(list): List of conditions (str) that the item cures when used """ to_cure = ["Poisoned"] - - if not target: + + if not target: target = user # Target user if none specified - + if not target.attributes.has("max_hp"): # Is not a fighter user.msg("You can't use %s on that." % item) return False # Returning false aborts the item use - + # Retrieve condition(s) to cure from kwargs, if present if "to_cure" in kwargs: to_cure = kwargs["to_cure"] - + item_msg = "%s uses %s! " % (user, item) - + for key in target.db.conditions: if key in to_cure: # If condition specified in to_cure, remove it. @@ -1195,11 +1195,11 @@ def itemfunc_cure_condition(item, user, target, **kwargs): del target.db.conditions[key] user.location.msg_contents(item_msg) - + def itemfunc_attack(item, user, target, **kwargs): """ Item function that attacks a target. - + kwargs: min_damage(int): Minimum damage dealt by the attack max_damage(int): Maximum damage dealth by the attack @@ -1207,31 +1207,31 @@ def itemfunc_attack(item, user, target, **kwargs): inflict_condition(list): List of conditions inflicted on hit, formatted as a (str, int) tuple containing condition name and duration. - + Notes: Calls resolve_attack at the end. """ if not is_in_combat(user): user.msg("You can only use that in combat.") return False # Returning false aborts the item use - - if not target: + + if not target: user.msg("You have to specify a target to use %s! (use = )" % item) return False - + if target == user: user.msg("You can't attack yourself!") - return False - + return False + if not target.db.hp: # Has no HP user.msg("You can't use %s on that." % item) return False - + min_damage = 20 max_damage = 40 accuracy = 0 inflict_condition = [] - + # Retrieve values from kwargs, if present if "damage_range" in kwargs: min_damage = kwargs["damage_range"][0] @@ -1240,17 +1240,17 @@ def itemfunc_attack(item, user, target, **kwargs): accuracy = kwargs["accuracy"] if "inflict_condition" in kwargs: inflict_condition = kwargs["inflict_condition"] - + # Roll attack and damage attack_value = randint(1, 100) + accuracy damage_value = randint(min_damage, max_damage) - + # Account for "Accuracy Up" and "Accuracy Down" conditions if "Accuracy Up" in user.db.conditions: attack_value += 25 if "Accuracy Down" in user.db.conditions: attack_value -= 25 - + user.location.msg_contents("%s attacks %s with %s!" % (user, target, item)) resolve_attack(user, target, attack_value=attack_value, damage_value=damage_value, inflict_condition=inflict_condition) @@ -1283,14 +1283,14 @@ Only "item_func" is required, but item behavior can be further modified by specifying any of the following: item_uses (int): If defined, item has a limited number of uses - + item_selfonly (bool): If True, user can only use the item on themself - + item_consumable(True or str): If True, item is destroyed when it runs out of uses. If a string is given, the item will spawn a new object as it's destroyed, with the string specifying what prototype to spawn. - + item_kwargs (dict): Keyword arguments to pass to the function defined in item_func. Unique to each function, and can be used to make multiple items using the same function work differently.