Merge branch 'turnbattle_refactor' into develop
This commit is contained in:
commit
50e4579529
6 changed files with 1831 additions and 3545 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Simple turn-based combat system
|
Simple turn-based combat system
|
||||||
|
|
||||||
Contrib - Tim Ashley Jenkins 2017
|
Contrib - Tim Ashley Jenkins 2017, Refactor by Griatch 2022
|
||||||
|
|
||||||
This is a framework for a simple turn-based combat system, similar
|
This is a framework for a simple turn-based combat system, similar
|
||||||
to those used in D&D-style tabletop role playing games. It allows
|
to those used in D&D-style tabletop role playing games. It allows
|
||||||
|
|
@ -62,7 +62,13 @@ COMBAT FUNCTIONS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def roll_init(character):
|
class BasicCombatRules:
|
||||||
|
"""
|
||||||
|
Stores all combat rules and helper methods.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def roll_init(self, character):
|
||||||
"""
|
"""
|
||||||
Rolls a number between 1-1000 to determine initiative.
|
Rolls a number between 1-1000 to determine initiative.
|
||||||
|
|
||||||
|
|
@ -88,8 +94,7 @@ def roll_init(character):
|
||||||
"""
|
"""
|
||||||
return randint(1, 1000)
|
return randint(1, 1000)
|
||||||
|
|
||||||
|
def get_attack(self, attacker, defender):
|
||||||
def get_attack(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for an attack roll.
|
Returns a value for an attack roll.
|
||||||
|
|
||||||
|
|
@ -113,8 +118,7 @@ def get_attack(attacker, defender):
|
||||||
attack_value = randint(1, 100)
|
attack_value = randint(1, 100)
|
||||||
return attack_value
|
return attack_value
|
||||||
|
|
||||||
|
def get_defense(self, attacker, defender):
|
||||||
def get_defense(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for defense, which an attack roll must equal or exceed in order
|
Returns a value for defense, which an attack roll must equal or exceed in order
|
||||||
for an attack to hit.
|
for an attack to hit.
|
||||||
|
|
@ -137,8 +141,7 @@ def get_defense(attacker, defender):
|
||||||
defense_value = 50
|
defense_value = 50
|
||||||
return defense_value
|
return defense_value
|
||||||
|
|
||||||
|
def get_damage(self, attacker, defender):
|
||||||
def get_damage(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for damage to be deducted from the defender's HP after abilities
|
Returns a value for damage to be deducted from the defender's HP after abilities
|
||||||
successful hit.
|
successful hit.
|
||||||
|
|
@ -161,8 +164,7 @@ def get_damage(attacker, defender):
|
||||||
damage_value = randint(15, 25)
|
damage_value = randint(15, 25)
|
||||||
return damage_value
|
return damage_value
|
||||||
|
|
||||||
|
def apply_damage(self, defender, damage):
|
||||||
def apply_damage(defender, damage):
|
|
||||||
"""
|
"""
|
||||||
Applies damage to a target, reducing their HP by the damage amount to a
|
Applies damage to a target, reducing their HP by the damage amount to a
|
||||||
minimum of 0.
|
minimum of 0.
|
||||||
|
|
@ -176,8 +178,7 @@ def apply_damage(defender, damage):
|
||||||
if defender.db.hp <= 0:
|
if defender.db.hp <= 0:
|
||||||
defender.db.hp = 0
|
defender.db.hp = 0
|
||||||
|
|
||||||
|
def at_defeat(self, defeated):
|
||||||
def at_defeat(defeated):
|
|
||||||
"""
|
"""
|
||||||
Announces the defeat of a fighter in combat.
|
Announces the defeat of a fighter in combat.
|
||||||
|
|
||||||
|
|
@ -192,8 +193,7 @@ def at_defeat(defeated):
|
||||||
"""
|
"""
|
||||||
defeated.location.msg_contents("%s has been defeated!" % defeated)
|
defeated.location.msg_contents("%s has been defeated!" % defeated)
|
||||||
|
|
||||||
|
def resolve_attack(self, attacker, defender, attack_value=None, defense_value=None):
|
||||||
def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
|
||||||
"""
|
"""
|
||||||
Resolves an attack and outputs the result.
|
Resolves an attack and outputs the result.
|
||||||
|
|
||||||
|
|
@ -208,26 +208,25 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
||||||
"""
|
"""
|
||||||
# Get an attack roll from the attacker.
|
# Get an attack roll from the attacker.
|
||||||
if not attack_value:
|
if not attack_value:
|
||||||
attack_value = get_attack(attacker, defender)
|
attack_value = self.get_attack(attacker, defender)
|
||||||
# Get a defense value from the defender.
|
# Get a defense value from the defender.
|
||||||
if not defense_value:
|
if not defense_value:
|
||||||
defense_value = get_defense(attacker, defender)
|
defense_value = self.get_defense(attacker, defender)
|
||||||
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
||||||
if attack_value < defense_value:
|
if attack_value < defense_value:
|
||||||
attacker.location.msg_contents("%s's attack misses %s!" % (attacker, defender))
|
attacker.location.msg_contents("%s's attack misses %s!" % (attacker, defender))
|
||||||
else:
|
else:
|
||||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
damage_value = self.get_damage(attacker, defender) # Calculate damage value.
|
||||||
# Announce damage dealt and apply damage.
|
# Announce damage dealt and apply damage.
|
||||||
attacker.location.msg_contents(
|
attacker.location.msg_contents(
|
||||||
"%s hits %s for %i damage!" % (attacker, defender, damage_value)
|
"%s hits %s for %i damage!" % (attacker, defender, damage_value)
|
||||||
)
|
)
|
||||||
apply_damage(defender, damage_value)
|
self.apply_damage(defender, damage_value)
|
||||||
# If defender HP is reduced to 0 or less, call at_defeat.
|
# If defender HP is reduced to 0 or less, call at_defeat.
|
||||||
if defender.db.hp <= 0:
|
if defender.db.hp <= 0:
|
||||||
at_defeat(defender)
|
self.at_defeat(defender)
|
||||||
|
|
||||||
|
def combat_cleanup(self, character):
|
||||||
def combat_cleanup(character):
|
|
||||||
"""
|
"""
|
||||||
Cleans up all the temporary combat-related attributes on a character.
|
Cleans up all the temporary combat-related attributes on a character.
|
||||||
|
|
||||||
|
|
@ -242,8 +241,7 @@ def combat_cleanup(character):
|
||||||
if attr.key[:7] == "combat_": # If the attribute name starts with 'combat_'...
|
if attr.key[:7] == "combat_": # If the attribute name starts with 'combat_'...
|
||||||
character.attributes.remove(key=attr.key) # ...then delete it!
|
character.attributes.remove(key=attr.key) # ...then delete it!
|
||||||
|
|
||||||
|
def is_in_combat(self, character):
|
||||||
def is_in_combat(character):
|
|
||||||
"""
|
"""
|
||||||
Returns true if the given character is in combat.
|
Returns true if the given character is in combat.
|
||||||
|
|
||||||
|
|
@ -255,8 +253,7 @@ def is_in_combat(character):
|
||||||
"""
|
"""
|
||||||
return bool(character.db.combat_turnhandler)
|
return bool(character.db.combat_turnhandler)
|
||||||
|
|
||||||
|
def is_turn(self, character):
|
||||||
def is_turn(character):
|
|
||||||
"""
|
"""
|
||||||
Returns true if it's currently the given character's turn in combat.
|
Returns true if it's currently the given character's turn in combat.
|
||||||
|
|
||||||
|
|
@ -270,8 +267,7 @@ def is_turn(character):
|
||||||
currentchar = turnhandler.db.fighters[turnhandler.db.turn]
|
currentchar = turnhandler.db.fighters[turnhandler.db.turn]
|
||||||
return bool(character == currentchar)
|
return bool(character == currentchar)
|
||||||
|
|
||||||
|
def spend_action(self, character, actions, action_name=None):
|
||||||
def spend_action(character, actions, action_name=None):
|
|
||||||
"""
|
"""
|
||||||
Spends a character's available combat actions and checks for end of turn.
|
Spends a character's available combat actions and checks for end of turn.
|
||||||
|
|
||||||
|
|
@ -294,6 +290,8 @@ def spend_action(character, actions, action_name=None):
|
||||||
character.db.combat_turnhandler.turn_end_check(character) # Signal potential end of turn.
|
character.db.combat_turnhandler.turn_end_check(character) # Signal potential end of turn.
|
||||||
|
|
||||||
|
|
||||||
|
COMBAT_RULES = BasicCombatRules()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
CHARACTER TYPECLASS
|
CHARACTER TYPECLASS
|
||||||
|
|
@ -306,6 +304,7 @@ class TBBasicCharacter(DefaultCharacter):
|
||||||
A character able to participate in turn-based combat. Has attributes for current
|
A character able to participate in turn-based combat. Has attributes for current
|
||||||
and maximum HP, and access to combat commands.
|
and maximum HP, and access to combat commands.
|
||||||
"""
|
"""
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -339,7 +338,7 @@ class TBBasicCharacter(DefaultCharacter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Keep the character from moving if at 0 HP or in combat.
|
# Keep the character from moving if at 0 HP or in combat.
|
||||||
if is_in_combat(self):
|
if self.rules.is_in_combat(self):
|
||||||
self.msg("You can't exit a room while in combat!")
|
self.msg("You can't exit a room while in combat!")
|
||||||
return False # Returning false keeps the character from moving.
|
return False # Returning false keeps the character from moving.
|
||||||
if self.db.HP <= 0:
|
if self.db.HP <= 0:
|
||||||
|
|
@ -367,6 +366,8 @@ class TBBasicTurnHandler(DefaultScript):
|
||||||
remaining participants choose to end the combat with the 'disengage' command.
|
remaining participants choose to end the combat with the 'disengage' command.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
"""
|
"""
|
||||||
Called once, when the script is created.
|
Called once, when the script is created.
|
||||||
|
|
@ -388,9 +389,10 @@ class TBBasicTurnHandler(DefaultScript):
|
||||||
# Add a reference to this script to the room
|
# Add a reference to this script to the room
|
||||||
self.obj.db.combat_turnhandler = self
|
self.obj.db.combat_turnhandler = self
|
||||||
|
|
||||||
# Roll initiative and sort the list of fighters depending on who rolls highest to determine turn order.
|
# Roll initiative and sort the list of fighters depending on who rolls highest to determine
|
||||||
# The initiative roll is determined by the roll_init function and can be customized easily.
|
# turn order. The initiative roll is determined by the roll_init method and can be
|
||||||
ordered_by_roll = sorted(self.db.fighters, key=roll_init, reverse=True)
|
# customized easily.
|
||||||
|
ordered_by_roll = sorted(self.db.fighters, key=self.rules.roll_init, reverse=True)
|
||||||
self.db.fighters = ordered_by_roll
|
self.db.fighters = ordered_by_roll
|
||||||
|
|
||||||
# Announce the turn order.
|
# Announce the turn order.
|
||||||
|
|
@ -408,7 +410,7 @@ class TBBasicTurnHandler(DefaultScript):
|
||||||
Called at script termination.
|
Called at script termination.
|
||||||
"""
|
"""
|
||||||
for fighter in self.db.fighters:
|
for fighter in self.db.fighters:
|
||||||
combat_cleanup(fighter) # Clean up the combat attributes for every fighter.
|
self.rules.combat_cleanup(fighter) # Clean up the combat attributes for every fighter.
|
||||||
self.obj.db.combat_turnhandler = None # Remove reference to turn handler in location
|
self.obj.db.combat_turnhandler = None # Remove reference to turn handler in location
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
|
|
@ -423,7 +425,7 @@ class TBBasicTurnHandler(DefaultScript):
|
||||||
if self.db.timer <= 0:
|
if self.db.timer <= 0:
|
||||||
# Force current character to disengage if timer runs out.
|
# Force current character to disengage if timer runs out.
|
||||||
self.obj.msg_contents("%s's turn timed out!" % currentchar)
|
self.obj.msg_contents("%s's turn timed out!" % currentchar)
|
||||||
spend_action(
|
self.rules.spend_action(
|
||||||
currentchar, "all", action_name="disengage"
|
currentchar, "all", action_name="disengage"
|
||||||
) # Spend all remaining actions.
|
) # Spend all remaining actions.
|
||||||
return
|
return
|
||||||
|
|
@ -439,7 +441,8 @@ class TBBasicTurnHandler(DefaultScript):
|
||||||
Args:
|
Args:
|
||||||
character (obj): Character to initialize for combat.
|
character (obj): Character to initialize for combat.
|
||||||
"""
|
"""
|
||||||
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
|
# Clean up leftover combat attributes beforehand, just in case.
|
||||||
|
self.rules.combat_cleanup(character)
|
||||||
character.db.combat_actionsleft = (
|
character.db.combat_actionsleft = (
|
||||||
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
||||||
)
|
)
|
||||||
|
|
@ -560,6 +563,9 @@ class CmdFight(Command):
|
||||||
key = "fight"
|
key = "fight"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
combat_handler_class = TBBasicTurnHandler
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
|
|
@ -570,7 +576,7 @@ class CmdFight(Command):
|
||||||
if not self.caller.db.hp: # If you don't have any hp
|
if not self.caller.db.hp: # If you don't have any hp
|
||||||
self.caller.msg("You can't start a fight if you've been defeated!")
|
self.caller.msg("You can't start a fight if you've been defeated!")
|
||||||
return
|
return
|
||||||
if is_in_combat(self.caller): # Already in a fight
|
if self.rules.is_in_combat(self.caller): # Already in a fight
|
||||||
self.caller.msg("You're already in a fight!")
|
self.caller.msg("You're already in a fight!")
|
||||||
return
|
return
|
||||||
for thing in here.contents: # Test everything in the room to add it to the fight.
|
for thing in here.contents: # Test everything in the room to add it to the fight.
|
||||||
|
|
@ -585,8 +591,7 @@ class CmdFight(Command):
|
||||||
return
|
return
|
||||||
here.msg_contents("%s starts a fight!" % self.caller)
|
here.msg_contents("%s starts a fight!" % self.caller)
|
||||||
# Add a turn handler script to the room, which starts combat.
|
# Add a turn handler script to the room, which starts combat.
|
||||||
here.scripts.add("contrib.game_systems.turnbattle.tb_basic.TBBasicTurnHandler")
|
here.scripts.add(self.command_handler_class)
|
||||||
# Remember you'll have to change the path to the script if you copy this code to your own modules!
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAttack(Command):
|
class CmdAttack(Command):
|
||||||
|
|
@ -603,15 +608,17 @@ class CmdAttack(Command):
|
||||||
key = "attack"
|
key = "attack"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
"Set the attacker to the caller and the defender to the target."
|
"Set the attacker to the caller and the defender to the target."
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't attack.
|
if not self.rules.is_in_combat(self.caller): # If not in combat, can't attack.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't attack.
|
if not self.rules.is_turn(self.caller): # If it's not your turn, can't attack.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -634,8 +641,8 @@ class CmdAttack(Command):
|
||||||
return
|
return
|
||||||
|
|
||||||
"If everything checks out, call the attack resolving function."
|
"If everything checks out, call the attack resolving function."
|
||||||
resolve_attack(attacker, defender)
|
self.rules.resolve_attack(attacker, defender)
|
||||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdPass(Command):
|
class CmdPass(Command):
|
||||||
|
|
@ -653,22 +660,25 @@ class CmdPass(Command):
|
||||||
aliases = ["wait", "hold"]
|
aliases = ["wait", "hold"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
if not is_in_combat(self.caller): # Can only pass a turn in combat.
|
if not self.rules.is_in_combat(self.caller): # Can only pass a turn in combat.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # Can only pass if it's your turn.
|
if not self.rules.is_turn(self.caller): # Can only pass if it's your turn.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.caller.location.msg_contents(
|
self.caller.location.msg_contents(
|
||||||
"%s takes no further action, passing the turn." % self.caller
|
"%s takes no further action, passing the turn." % self.caller
|
||||||
)
|
)
|
||||||
spend_action(self.caller, "all", action_name="pass") # Spend all remaining actions.
|
# Spend all remaining actions.
|
||||||
|
self.rules.spend_action(self.caller, "all", action_name="pass")
|
||||||
|
|
||||||
|
|
||||||
class CmdDisengage(Command):
|
class CmdDisengage(Command):
|
||||||
|
|
@ -687,20 +697,23 @@ class CmdDisengage(Command):
|
||||||
aliases = ["spare"]
|
aliases = ["spare"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
if not is_in_combat(self.caller): # If you're not in combat
|
if not self.rules.is_in_combat(self.caller): # If you're not in combat
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn
|
if not self.rules.is_turn(self.caller): # If it's not your turn
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.caller.location.msg_contents("%s disengages, ready to stop fighting." % self.caller)
|
self.caller.location.msg_contents("%s disengages, ready to stop fighting." % self.caller)
|
||||||
spend_action(self.caller, "all", action_name="disengage") # Spend all remaining actions.
|
# Spend all remaining actions.
|
||||||
|
self.rules.spend_action(self.caller, "all", action_name="disengage")
|
||||||
"""
|
"""
|
||||||
The action_name kwarg sets the character's last action to "disengage", which is checked by
|
The action_name kwarg sets the character's last action to "disengage", which is checked by
|
||||||
the turn handler script to see if all fighters have disengaged.
|
the turn handler script to see if all fighters have disengaged.
|
||||||
|
|
@ -721,10 +734,12 @@ class CmdRest(Command):
|
||||||
key = "rest"
|
key = "rest"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
|
|
||||||
if is_in_combat(self.caller): # If you're in combat
|
if self.rules.is_in_combat(self.caller): # If you're in combat
|
||||||
self.caller.msg("You can't rest while you're in combat.")
|
self.caller.msg("You can't rest while you're in combat.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -748,17 +763,21 @@ class CmdCombatHelp(CmdHelp):
|
||||||
topics related to the game.
|
topics related to the game.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
combat_help_text = (
|
||||||
|
"Available combat commands:|/"
|
||||||
|
"|wAttack:|n Attack a target, attempting to deal damage.|/"
|
||||||
|
"|wPass:|n Pass your turn without further action.|/"
|
||||||
|
"|wDisengage:|n End your turn and attempt to end combat.|/"
|
||||||
|
)
|
||||||
|
|
||||||
# Just like the default help command, but will give quick
|
# Just like the default help command, but will give quick
|
||||||
# tips on combat when used in a fight with no arguments.
|
# tips on combat when used in a fight with no arguments.
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
if is_in_combat(self.caller) and not self.args: # In combat and entered 'help' alone
|
# In combat and entered 'help' alone
|
||||||
self.caller.msg(
|
if self.rules.is_in_combat(self.caller) and not self.args:
|
||||||
"Available combat commands:|/"
|
self.caller.msg(self.combat_help_text)
|
||||||
+ "|wAttack:|n Attack a target, attempting to deal damage.|/"
|
|
||||||
+ "|wPass:|n Pass your turn without further action.|/"
|
|
||||||
+ "|wDisengage:|n End your turn and attempt to end combat.|/"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
super().func() # Call the default help command
|
super().func() # Call the default help command
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Simple turn-based combat system with equipment
|
Simple turn-based combat system with equipment
|
||||||
|
|
||||||
Contrib - Tim Ashley Jenkins 2017
|
Contrib - Tim Ashley Jenkins 2017, Refactor by Griatch 2022
|
||||||
|
|
||||||
This is a version of the 'turnbattle' contrib with a basic system for
|
This is a version of the 'turnbattle' contrib with a basic system for
|
||||||
weapons and armor implemented. Weapons can have unique damage ranges
|
weapons and armor implemented. Weapons can have unique damage ranges
|
||||||
|
|
@ -55,8 +55,8 @@ in your game and using it as-is.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from random import randint
|
from random import randint
|
||||||
from evennia import DefaultCharacter, Command, default_cmds, DefaultScript, DefaultObject
|
from evennia import Command, default_cmds, DefaultObject
|
||||||
from evennia.commands.default.help import CmdHelp
|
from . import tb_basic
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
@ -74,34 +74,13 @@ COMBAT FUNCTIONS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def roll_init(character):
|
class EquipmentCombatRules(tb_basic.BasicCombatRules):
|
||||||
"""
|
"""
|
||||||
Rolls a number between 1-1000 to determine initiative.
|
Has all the methods of the basic combat, with the addition of equipment.
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): The character to determine initiative for
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
initiative (int): The character's place in initiative - higher
|
|
||||||
numbers go first.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
By default, does not reference the character and simply returns
|
|
||||||
a random integer from 1 to 1000.
|
|
||||||
|
|
||||||
Since the character is passed to this function, you can easily reference
|
|
||||||
a character's stats to determine an initiative roll - for example, if your
|
|
||||||
character has a 'dexterity' attribute, you can use it to give that character
|
|
||||||
an advantage in turn order, like so:
|
|
||||||
|
|
||||||
return (randint(1,20)) + character.db.dexterity
|
|
||||||
|
|
||||||
This way, characters with a higher dexterity will go first more often.
|
|
||||||
"""
|
"""
|
||||||
return randint(1, 1000)
|
|
||||||
|
|
||||||
|
def get_attack(self, attacker, defender):
|
||||||
def get_attack(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for an attack roll.
|
Returns a value for an attack roll.
|
||||||
|
|
||||||
|
|
@ -133,8 +112,7 @@ def get_attack(attacker, defender):
|
||||||
attack_value += accuracy_bonus
|
attack_value += accuracy_bonus
|
||||||
return attack_value
|
return attack_value
|
||||||
|
|
||||||
|
def get_defense(self, attacker, defender):
|
||||||
def get_defense(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for defense, which an attack roll must equal or exceed in order
|
Returns a value for defense, which an attack roll must equal or exceed in order
|
||||||
for an attack to hit.
|
for an attack to hit.
|
||||||
|
|
@ -160,8 +138,7 @@ def get_defense(attacker, defender):
|
||||||
defense_value += armor.db.defense_modifier
|
defense_value += armor.db.defense_modifier
|
||||||
return defense_value
|
return defense_value
|
||||||
|
|
||||||
|
def get_damage(self, attacker, defender):
|
||||||
def get_damage(attacker, defender):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for damage to be deducted from the defender's HP after abilities
|
Returns a value for damage to be deducted from the defender's HP after abilities
|
||||||
successful hit.
|
successful hit.
|
||||||
|
|
@ -199,39 +176,7 @@ def get_damage(attacker, defender):
|
||||||
damage_value = 0
|
damage_value = 0
|
||||||
return damage_value
|
return damage_value
|
||||||
|
|
||||||
|
def resolve_attack(self, attacker, defender, attack_value=None, defense_value=None):
|
||||||
def apply_damage(defender, damage):
|
|
||||||
"""
|
|
||||||
Applies damage to a target, reducing their HP by the damage amount to a
|
|
||||||
minimum of 0.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
defender (obj): Character taking damage
|
|
||||||
damage (int): Amount of damage being taken
|
|
||||||
"""
|
|
||||||
defender.db.hp -= damage # Reduce defender's HP by the damage dealt.
|
|
||||||
# If this reduces it to 0 or less, set HP to 0.
|
|
||||||
if defender.db.hp <= 0:
|
|
||||||
defender.db.hp = 0
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
into a dying state or something similar) then this is the place to
|
|
||||||
do it.
|
|
||||||
"""
|
|
||||||
defeated.location.msg_contents("%s has been defeated!" % defeated)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
|
||||||
"""
|
"""
|
||||||
Resolves an attack and outputs the result.
|
Resolves an attack and outputs the result.
|
||||||
|
|
||||||
|
|
@ -251,17 +196,17 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
||||||
attackers_weapon = weapon.db.weapon_type_name
|
attackers_weapon = weapon.db.weapon_type_name
|
||||||
# Get an attack roll from the attacker.
|
# Get an attack roll from the attacker.
|
||||||
if not attack_value:
|
if not attack_value:
|
||||||
attack_value = get_attack(attacker, defender)
|
attack_value = self.get_attack(attacker, defender)
|
||||||
# Get a defense value from the defender.
|
# Get a defense value from the defender.
|
||||||
if not defense_value:
|
if not defense_value:
|
||||||
defense_value = get_defense(attacker, defender)
|
defense_value = self.get_defense(attacker, defender)
|
||||||
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
||||||
if attack_value < defense_value:
|
if attack_value < defense_value:
|
||||||
attacker.location.msg_contents(
|
attacker.location.msg_contents(
|
||||||
"%s's %s misses %s!" % (attacker, attackers_weapon, defender)
|
"%s's %s misses %s!" % (attacker, attackers_weapon, defender)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
damage_value = self.get_damage(attacker, defender) # Calculate damage value.
|
||||||
# Announce damage dealt and apply damage.
|
# Announce damage dealt and apply damage.
|
||||||
if damage_value > 0:
|
if damage_value > 0:
|
||||||
attacker.location.msg_contents(
|
attacker.location.msg_contents(
|
||||||
|
|
@ -272,78 +217,13 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
||||||
attacker.location.msg_contents(
|
attacker.location.msg_contents(
|
||||||
"%s's %s bounces harmlessly off %s!" % (attacker, attackers_weapon, defender)
|
"%s's %s bounces harmlessly off %s!" % (attacker, attackers_weapon, defender)
|
||||||
)
|
)
|
||||||
apply_damage(defender, damage_value)
|
self.apply_damage(defender, damage_value)
|
||||||
# If defender HP is reduced to 0 or less, call at_defeat.
|
# If defender HP is reduced to 0 or less, call at_defeat.
|
||||||
if defender.db.hp <= 0:
|
if defender.db.hp <= 0:
|
||||||
at_defeat(defender)
|
self.at_defeat(defender)
|
||||||
|
|
||||||
|
|
||||||
def combat_cleanup(character):
|
COMBAT_RULES = EquipmentCombatRules()
|
||||||
"""
|
|
||||||
Cleans up all the temporary combat-related attributes on a character.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to have their combat attributes removed
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
Any attribute whose key begins with 'combat_' is temporary and no
|
|
||||||
longer needed once a fight ends.
|
|
||||||
"""
|
|
||||||
for attr in character.attributes.all():
|
|
||||||
if attr.key[:7] == "combat_": # If the attribute name starts with 'combat_'...
|
|
||||||
character.attributes.remove(key=attr.key) # ...then delete it!
|
|
||||||
|
|
||||||
|
|
||||||
def is_in_combat(character):
|
|
||||||
"""
|
|
||||||
Returns true if the given character is in combat.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to determine if is in combat or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(bool): True if in combat or False if not in combat
|
|
||||||
"""
|
|
||||||
return bool(character.db.combat_turnhandler)
|
|
||||||
|
|
||||||
|
|
||||||
def is_turn(character):
|
|
||||||
"""
|
|
||||||
Returns true if it's currently the given character's turn in combat.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to determine if it is their turn or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(bool): True if it is their turn or False otherwise
|
|
||||||
"""
|
|
||||||
turnhandler = character.db.combat_turnhandler
|
|
||||||
currentchar = turnhandler.db.fighters[turnhandler.db.turn]
|
|
||||||
return bool(character == currentchar)
|
|
||||||
|
|
||||||
|
|
||||||
def spend_action(character, actions, action_name=None):
|
|
||||||
"""
|
|
||||||
Spends a character's available combat actions and checks for end of turn.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character spending the action
|
|
||||||
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
|
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
action_name (str or None): If a string is given, sets character's last action in
|
|
||||||
combat to provided string
|
|
||||||
"""
|
|
||||||
if action_name:
|
|
||||||
character.db.combat_lastaction = action_name
|
|
||||||
if actions == "all": # If spending all actions
|
|
||||||
character.db.combat_actionsleft = 0 # Set actions to 0
|
|
||||||
else:
|
|
||||||
character.db.combat_actionsleft -= actions # Use up actions.
|
|
||||||
if character.db.combat_actionsleft < 0:
|
|
||||||
character.db.combat_actionsleft = 0 # Can't have fewer than 0 actions
|
|
||||||
character.db.combat_turnhandler.turn_end_check(character) # Signal potential end of turn.
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
@ -352,7 +232,7 @@ SCRIPTS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TBEquipTurnHandler(DefaultScript):
|
class TBEquipTurnHandler(tb_basic.TBBasicTurnHandler):
|
||||||
"""
|
"""
|
||||||
This is the script that handles the progression of combat through turns.
|
This is the script that handles the progression of combat through turns.
|
||||||
On creation (when a fight is started) it adds all combat-ready characters
|
On creation (when a fight is started) it adds all combat-ready characters
|
||||||
|
|
@ -363,174 +243,7 @@ class TBEquipTurnHandler(DefaultScript):
|
||||||
Fights persist until only one participant is left with any HP or all
|
Fights persist until only one participant is left with any HP or all
|
||||||
remaining participants choose to end the combat with the 'disengage' command.
|
remaining participants choose to end the combat with the 'disengage' command.
|
||||||
"""
|
"""
|
||||||
|
rules = COMBAT_RULES
|
||||||
def at_script_creation(self):
|
|
||||||
"""
|
|
||||||
Called once, when the script is created.
|
|
||||||
"""
|
|
||||||
self.key = "Combat Turn Handler"
|
|
||||||
self.interval = 5 # Once every 5 seconds
|
|
||||||
self.persistent = True
|
|
||||||
self.db.fighters = []
|
|
||||||
|
|
||||||
# Add all fighters in the room with at least 1 HP to the combat."
|
|
||||||
for thing in self.obj.contents:
|
|
||||||
if thing.db.hp:
|
|
||||||
self.db.fighters.append(thing)
|
|
||||||
|
|
||||||
# Initialize each fighter for combat
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
self.initialize_for_combat(fighter)
|
|
||||||
|
|
||||||
# Add a reference to this script to the room
|
|
||||||
self.obj.db.combat_turnhandler = self
|
|
||||||
|
|
||||||
# Roll initiative and sort the list of fighters depending on who rolls highest to determine turn order.
|
|
||||||
# The initiative roll is determined by the roll_init function and can be customized easily.
|
|
||||||
ordered_by_roll = sorted(self.db.fighters, key=roll_init, reverse=True)
|
|
||||||
self.db.fighters = ordered_by_roll
|
|
||||||
|
|
||||||
# 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])
|
|
||||||
|
|
||||||
# Set up the current turn and turn timeout delay.
|
|
||||||
self.db.turn = 0
|
|
||||||
self.db.timer = TURN_TIMEOUT # Set timer to turn timeout specified in options
|
|
||||||
|
|
||||||
def at_stop(self):
|
|
||||||
"""
|
|
||||||
Called at script termination.
|
|
||||||
"""
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
combat_cleanup(fighter) # Clean up the combat attributes for every fighter.
|
|
||||||
self.obj.db.combat_turnhandler = None # Remove reference to turn handler in location
|
|
||||||
|
|
||||||
def at_repeat(self):
|
|
||||||
"""
|
|
||||||
Called once every self.interval seconds.
|
|
||||||
"""
|
|
||||||
currentchar = self.db.fighters[
|
|
||||||
self.db.turn
|
|
||||||
] # Note the current character in the turn order.
|
|
||||||
self.db.timer -= self.interval # Count down the timer.
|
|
||||||
|
|
||||||
if self.db.timer <= 0:
|
|
||||||
# Force current character to disengage if timer runs out.
|
|
||||||
self.obj.msg_contents("%s's turn timed out!" % currentchar)
|
|
||||||
spend_action(
|
|
||||||
currentchar, "all", action_name="disengage"
|
|
||||||
) # Spend all remaining actions.
|
|
||||||
return
|
|
||||||
elif self.db.timer <= 10 and not self.db.timeout_warning_given: # 10 seconds left
|
|
||||||
# Warn the current character if they're about to time out.
|
|
||||||
currentchar.msg("WARNING: About to time out!")
|
|
||||||
self.db.timeout_warning_given = True
|
|
||||||
|
|
||||||
def initialize_for_combat(self, character):
|
|
||||||
"""
|
|
||||||
Prepares a character for combat when starting or entering a fight.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to initialize for combat.
|
|
||||||
"""
|
|
||||||
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
|
|
||||||
character.db.combat_actionsleft = (
|
|
||||||
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
|
||||||
)
|
|
||||||
character.db.combat_turnhandler = (
|
|
||||||
self # Add a reference to this turn handler script to the character
|
|
||||||
)
|
|
||||||
character.db.combat_lastaction = "null" # Track last action taken in combat
|
|
||||||
|
|
||||||
def start_turn(self, character):
|
|
||||||
"""
|
|
||||||
Readies a character for the start of their turn by replenishing their
|
|
||||||
available actions and notifying them that their turn has come up.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to be readied.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
Here, you only get one action per turn, but you might want to allow more than
|
|
||||||
one per turn, or even grant a number of actions based on a character's
|
|
||||||
attributes. You can even add multiple different kinds of actions, I.E. actions
|
|
||||||
separated for movement, by adding "character.db.combat_movesleft = 3" or
|
|
||||||
something similar.
|
|
||||||
"""
|
|
||||||
character.db.combat_actionsleft = ACTIONS_PER_TURN # Replenish actions
|
|
||||||
# Prompt the character for their turn and give some information.
|
|
||||||
character.msg("|wIt's your turn! You have %i HP remaining.|n" % character.db.hp)
|
|
||||||
|
|
||||||
def next_turn(self):
|
|
||||||
"""
|
|
||||||
Advances to the next character in the turn order.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check to see if every character disengaged as their last action. If so, end combat.
|
|
||||||
disengage_check = True
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if (
|
|
||||||
fighter.db.combat_lastaction != "disengage"
|
|
||||||
): # If a character has done anything but disengage
|
|
||||||
disengage_check = False
|
|
||||||
if disengage_check: # All characters have disengaged
|
|
||||||
self.obj.msg_contents("All fighters have disengaged! Combat is over!")
|
|
||||||
self.stop() # Stop this script and end combat.
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check to see if only one character is left standing. If so, end combat.
|
|
||||||
defeated_characters = 0
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if fighter.db.HP == 0:
|
|
||||||
defeated_characters += 1 # Add 1 for every fighter with 0 HP left (defeated)
|
|
||||||
if defeated_characters == (
|
|
||||||
len(self.db.fighters) - 1
|
|
||||||
): # If only one character isn't defeated
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if fighter.db.HP != 0:
|
|
||||||
LastStanding = fighter # Pick the one fighter left with HP remaining
|
|
||||||
self.obj.msg_contents("Only %s remains! Combat is over!" % LastStanding)
|
|
||||||
self.stop() # Stop this script and end combat.
|
|
||||||
return
|
|
||||||
|
|
||||||
# Cycle to the next turn.
|
|
||||||
currentchar = self.db.fighters[self.db.turn]
|
|
||||||
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.
|
|
||||||
|
|
||||||
def turn_end_check(self, character):
|
|
||||||
"""
|
|
||||||
Tests to see if a character's turn is over, and cycles to the next turn if it is.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to test for end of turn
|
|
||||||
"""
|
|
||||||
if not character.db.combat_actionsleft: # Character has no actions remaining
|
|
||||||
self.next_turn()
|
|
||||||
return
|
|
||||||
|
|
||||||
def join_fight(self, character):
|
|
||||||
"""
|
|
||||||
Adds a new character to a fight already in progress.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to be added to the fight.
|
|
||||||
"""
|
|
||||||
# Inserts the fighter to the turn order, right behind whoever's turn it currently is.
|
|
||||||
self.db.fighters.insert(self.db.turn, character)
|
|
||||||
# Tick the turn counter forward one to compensate.
|
|
||||||
self.db.turn += 1
|
|
||||||
# Initialize the character like you do at the start.
|
|
||||||
self.initialize_for_combat(character)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -543,7 +256,9 @@ TYPECLASSES START HERE
|
||||||
class TBEWeapon(DefaultObject):
|
class TBEWeapon(DefaultObject):
|
||||||
"""
|
"""
|
||||||
A weapon which can be wielded in combat with the 'wield' command.
|
A weapon which can be wielded in combat with the 'wield' command.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -592,7 +307,7 @@ class TBEArmor(DefaultObject):
|
||||||
"""
|
"""
|
||||||
Can't drop in combat.
|
Can't drop in combat.
|
||||||
"""
|
"""
|
||||||
if is_in_combat(dropper):
|
if self.rules.is_in_combat(dropper):
|
||||||
dropper.msg("You can't doff armor in a fight!")
|
dropper.msg("You can't doff armor in a fight!")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -609,7 +324,7 @@ class TBEArmor(DefaultObject):
|
||||||
"""
|
"""
|
||||||
Can't give away in combat.
|
Can't give away in combat.
|
||||||
"""
|
"""
|
||||||
if is_in_combat(giver):
|
if self.rules.is_in_combat(giver):
|
||||||
dropper.msg("You can't doff armor in a fight!")
|
dropper.msg("You can't doff armor in a fight!")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -623,7 +338,7 @@ class TBEArmor(DefaultObject):
|
||||||
giver.location.msg_contents("%s removes %s." % (giver, self))
|
giver.location.msg_contents("%s removes %s." % (giver, self))
|
||||||
|
|
||||||
|
|
||||||
class TBEquipCharacter(DefaultCharacter):
|
class TBEquipCharacter(tb_basic.TBBasicCharacter):
|
||||||
"""
|
"""
|
||||||
A character able to participate in turn-based combat. Has attributes for current
|
A character able to participate in turn-based combat. Has attributes for current
|
||||||
and maximum HP, and access to combat commands.
|
and maximum HP, and access to combat commands.
|
||||||
|
|
@ -649,31 +364,6 @@ class TBEquipCharacter(DefaultCharacter):
|
||||||
can be changed at creation and factor into combat calculations.
|
can be changed at creation and factor into combat calculations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def at_pre_move(self, destination):
|
|
||||||
"""
|
|
||||||
Called just before starting to move this object to
|
|
||||||
destination.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (Object): The object we are moving to
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
shouldmove (bool): If we should move or not.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
If this method returns False/None, the move is cancelled
|
|
||||||
before it is even started.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Keep the character from moving if at 0 HP or in combat.
|
|
||||||
if is_in_combat(self):
|
|
||||||
self.msg("You can't exit a room while in combat!")
|
|
||||||
return False # Returning false keeps the character from moving.
|
|
||||||
if self.db.HP <= 0:
|
|
||||||
self.msg("You can't move, you've been defeated!")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
@ -682,7 +372,7 @@ COMMANDS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CmdFight(Command):
|
class CmdFight(tb_basic.CmdFight):
|
||||||
"""
|
"""
|
||||||
Starts a fight with everyone in the same room as you.
|
Starts a fight with everyone in the same room as you.
|
||||||
|
|
||||||
|
|
@ -697,36 +387,11 @@ class CmdFight(Command):
|
||||||
key = "fight"
|
key = "fight"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
command_handler_class = TBEquipTurnHandler
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
here = self.caller.location
|
|
||||||
fighters = []
|
|
||||||
|
|
||||||
if not self.caller.db.hp: # If you don't have any hp
|
|
||||||
self.caller.msg("You can't start a fight if you've been defeated!")
|
|
||||||
return
|
|
||||||
if is_in_combat(self.caller): # Already in a fight
|
|
||||||
self.caller.msg("You're already in a fight!")
|
|
||||||
return
|
|
||||||
for thing in here.contents: # Test everything in the room to add it to the fight.
|
|
||||||
if thing.db.HP: # If the object has HP...
|
|
||||||
fighters.append(thing) # ...then add it to the fight.
|
|
||||||
if len(fighters) <= 1: # If you're the only able fighter in the room
|
|
||||||
self.caller.msg("There's nobody here to fight!")
|
|
||||||
return
|
|
||||||
if here.db.combat_turnhandler: # If there's already a fight going on...
|
|
||||||
here.msg_contents("%s joins the fight!" % self.caller)
|
|
||||||
here.db.combat_turnhandler.join_fight(self.caller) # Join the fight!
|
|
||||||
return
|
|
||||||
here.msg_contents("%s starts a fight!" % self.caller)
|
|
||||||
# Add a turn handler script to the room, which starts combat.
|
|
||||||
here.scripts.add("contrib.game_systems.turnbattle.tb_equip.TBEquipTurnHandler")
|
|
||||||
# Remember you'll have to change the path to the script if you copy this code to your own modules!
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAttack(Command):
|
class CmdAttack(tb_basic.CmdAttack):
|
||||||
"""
|
"""
|
||||||
Attacks another character.
|
Attacks another character.
|
||||||
|
|
||||||
|
|
@ -740,42 +405,10 @@ class CmdAttack(Command):
|
||||||
key = "attack"
|
key = "attack"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"This performs the actual command."
|
|
||||||
"Set the attacker to the caller and the defender to the target."
|
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't attack.
|
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't attack.
|
|
||||||
self.caller.msg("You can only do that on your turn.")
|
|
||||||
return
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
attacker = self.caller
|
|
||||||
defender = self.caller.search(self.args)
|
|
||||||
|
|
||||||
if not defender: # No valid target given.
|
|
||||||
return
|
|
||||||
|
|
||||||
if not defender.db.hp: # Target object has no HP left or to begin with
|
|
||||||
self.caller.msg("You can't fight that!")
|
|
||||||
return
|
|
||||||
|
|
||||||
if attacker == defender: # Target and attacker are the same
|
|
||||||
self.caller.msg("You can't attack yourself!")
|
|
||||||
return
|
|
||||||
|
|
||||||
"If everything checks out, call the attack resolving function."
|
|
||||||
resolve_attack(attacker, defender)
|
|
||||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
|
||||||
|
|
||||||
|
|
||||||
class CmdPass(Command):
|
class CmdPass(tb_basic.CmdPass):
|
||||||
"""
|
"""
|
||||||
Passes on your turn.
|
Passes on your turn.
|
||||||
|
|
||||||
|
|
@ -790,25 +423,10 @@ class CmdPass(Command):
|
||||||
aliases = ["wait", "hold"]
|
aliases = ["wait", "hold"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
if not is_in_combat(self.caller): # Can only pass a turn in combat.
|
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not is_turn(self.caller): # Can only pass if it's your turn.
|
|
||||||
self.caller.msg("You can only do that on your turn.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.location.msg_contents(
|
|
||||||
"%s takes no further action, passing the turn." % self.caller
|
|
||||||
)
|
|
||||||
spend_action(self.caller, "all", action_name="pass") # Spend all remaining actions.
|
|
||||||
|
|
||||||
|
|
||||||
class CmdDisengage(Command):
|
class CmdDisengage(tb_basic.CmdDisengage):
|
||||||
"""
|
"""
|
||||||
Passes your turn and attempts to end combat.
|
Passes your turn and attempts to end combat.
|
||||||
|
|
||||||
|
|
@ -824,27 +442,10 @@ class CmdDisengage(Command):
|
||||||
aliases = ["spare"]
|
aliases = ["spare"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
if not is_in_combat(self.caller): # If you're not in combat
|
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn
|
|
||||||
self.caller.msg("You can only do that on your turn.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.location.msg_contents("%s disengages, ready to stop fighting." % self.caller)
|
|
||||||
spend_action(self.caller, "all", action_name="disengage") # Spend all remaining actions.
|
|
||||||
"""
|
|
||||||
The action_name kwarg sets the character's last action to "disengage", which is checked by
|
|
||||||
the turn handler script to see if all fighters have disengaged.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CmdRest(Command):
|
class CmdRest(tb_basic.CmdRest):
|
||||||
"""
|
"""
|
||||||
Recovers damage.
|
Recovers damage.
|
||||||
|
|
||||||
|
|
@ -858,21 +459,10 @@ class CmdRest(Command):
|
||||||
key = "rest"
|
key = "rest"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"This performs the actual command."
|
|
||||||
|
|
||||||
if is_in_combat(self.caller): # If you're in combat
|
|
||||||
self.caller.msg("You can't rest while you're in combat.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.db.hp = self.caller.db.max_hp # Set current HP to maximum
|
|
||||||
self.caller.location.msg_contents("%s rests to recover HP." % self.caller)
|
|
||||||
"""
|
|
||||||
You'll probably want to replace this with your own system for recovering HP.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CmdCombatHelp(CmdHelp):
|
class CmdCombatHelp(tb_basic.CmdCombatHelp):
|
||||||
"""
|
"""
|
||||||
View help or a list of topics
|
View help or a list of topics
|
||||||
|
|
||||||
|
|
@ -885,19 +475,7 @@ class CmdCombatHelp(CmdHelp):
|
||||||
topics related to the game.
|
topics related to the game.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Just like the default help command, but will give quick
|
rules = COMBAT_RULES
|
||||||
# tips on combat when used in a fight with no arguments.
|
|
||||||
|
|
||||||
def func(self):
|
|
||||||
if is_in_combat(self.caller) and not self.args: # In combat and entered 'help' alone
|
|
||||||
self.caller.msg(
|
|
||||||
"Available combat commands:|/"
|
|
||||||
+ "|wAttack:|n Attack a target, attempting to deal damage.|/"
|
|
||||||
+ "|wPass:|n Pass your turn without further action.|/"
|
|
||||||
+ "|wDisengage:|n End your turn and attempt to end combat.|/"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
super().func() # Call the default help command
|
|
||||||
|
|
||||||
|
|
||||||
class CmdWield(Command):
|
class CmdWield(Command):
|
||||||
|
|
@ -918,13 +496,15 @@ class CmdWield(Command):
|
||||||
key = "wield"
|
key = "wield"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
# If in combat, check to see if it's your turn.
|
# If in combat, check to see if it's your turn.
|
||||||
if is_in_combat(self.caller):
|
if self.rules.is_in_combat(self.caller):
|
||||||
if not is_turn(self.caller):
|
if not self.rules.is_turn(self.caller):
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
if not self.args:
|
if not self.args:
|
||||||
|
|
@ -933,7 +513,8 @@ class CmdWield(Command):
|
||||||
weapon = self.caller.search(self.args, candidates=self.caller.contents)
|
weapon = self.caller.search(self.args, candidates=self.caller.contents)
|
||||||
if not weapon:
|
if not weapon:
|
||||||
return
|
return
|
||||||
if not weapon.is_typeclass("evennia.contrib.game_systems.turnbattle.tb_equip.TBEWeapon", exact=True):
|
if not weapon.is_typeclass("evennia.contrib.game_systems.turnbattle.tb_equip.TBEWeapon",
|
||||||
|
exact=True):
|
||||||
self.caller.msg("That's not a weapon!")
|
self.caller.msg("That's not a weapon!")
|
||||||
# Remember to update the path to the weapon typeclass if you move this module!
|
# Remember to update the path to the weapon typeclass if you move this module!
|
||||||
return
|
return
|
||||||
|
|
@ -948,8 +529,8 @@ class CmdWield(Command):
|
||||||
"%s lowers %s and wields %s." % (self.caller, old_weapon, weapon)
|
"%s lowers %s and wields %s." % (self.caller, old_weapon, weapon)
|
||||||
)
|
)
|
||||||
# Spend an action if in combat.
|
# Spend an action if in combat.
|
||||||
if is_in_combat(self.caller):
|
if self.rules.is_in_combat(self.caller):
|
||||||
spend_action(self.caller, 1, action_name="wield") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="wield") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdUnwield(Command):
|
class CmdUnwield(Command):
|
||||||
|
|
@ -966,13 +547,15 @@ class CmdUnwield(Command):
|
||||||
key = "unwield"
|
key = "unwield"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
# If in combat, check to see if it's your turn.
|
# If in combat, check to see if it's your turn.
|
||||||
if is_in_combat(self.caller):
|
if self.rules.is_in_combat(self.caller):
|
||||||
if not is_turn(self.caller):
|
if not self.rules.is_turn(self.caller):
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
if not self.caller.db.wielded_weapon:
|
if not self.caller.db.wielded_weapon:
|
||||||
|
|
@ -998,12 +581,14 @@ class CmdDon(Command):
|
||||||
key = "don"
|
key = "don"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
# Can't do this in combat
|
# Can't do this in combat
|
||||||
if is_in_combat(self.caller):
|
if self.rules.is_in_combat(self.caller):
|
||||||
self.caller.msg("You can't don armor in a fight!")
|
self.caller.msg("You can't don armor in a fight!")
|
||||||
return
|
return
|
||||||
if not self.args:
|
if not self.args:
|
||||||
|
|
@ -1012,7 +597,8 @@ class CmdDon(Command):
|
||||||
armor = self.caller.search(self.args, candidates=self.caller.contents)
|
armor = self.caller.search(self.args, candidates=self.caller.contents)
|
||||||
if not armor:
|
if not armor:
|
||||||
return
|
return
|
||||||
if not armor.is_typeclass("evennia.contrib.game_systems.turnbattle.tb_equip.TBEArmor", exact=True):
|
if not armor.is_typeclass("evennia.contrib.game_systems.turnbattle.tb_equip.TBEArmor",
|
||||||
|
exact=True):
|
||||||
self.caller.msg("That's not armor!")
|
self.caller.msg("That's not armor!")
|
||||||
# Remember to update the path to the armor typeclass if you move this module!
|
# Remember to update the path to the armor typeclass if you move this module!
|
||||||
return
|
return
|
||||||
|
|
@ -1043,12 +629,14 @@ class CmdDoff(Command):
|
||||||
key = "doff"
|
key = "doff"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""
|
"""
|
||||||
This performs the actual command.
|
This performs the actual command.
|
||||||
"""
|
"""
|
||||||
# Can't do this in combat
|
# Can't do this in combat
|
||||||
if is_in_combat(self.caller):
|
if self.rules.is_in_combat(self.caller):
|
||||||
self.caller.msg("You can't doff armor in a fight!")
|
self.caller.msg("You can't doff armor in a fight!")
|
||||||
return
|
return
|
||||||
if not self.caller.db.worn_armor:
|
if not self.caller.db.worn_armor:
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -101,8 +101,9 @@ in your game and using it as-is.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from random import randint
|
from random import randint
|
||||||
from evennia import DefaultCharacter, DefaultObject, Command, default_cmds, DefaultScript
|
from evennia import DefaultObject, Command, default_cmds, DefaultScript
|
||||||
from evennia.commands.default.help import CmdHelp
|
from evennia.commands.default.help import CmdHelp
|
||||||
|
from . import tb_basic
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
@ -120,34 +121,9 @@ COMBAT FUNCTIONS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def roll_init(character):
|
class RangedCombatRules(tb_basic.BasicCombatRules):
|
||||||
"""
|
|
||||||
Rolls a number between 1-1000 to determine initiative.
|
|
||||||
|
|
||||||
Args:
|
def get_attack(self, attacker, defender, attack_type):
|
||||||
character (obj): The character to determine initiative for
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
initiative (int): The character's place in initiative - higher
|
|
||||||
numbers go first.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
By default, does not reference the character and simply returns
|
|
||||||
a random integer from 1 to 1000.
|
|
||||||
|
|
||||||
Since the character is passed to this function, you can easily reference
|
|
||||||
a character's stats to determine an initiative roll - for example, if your
|
|
||||||
character has a 'dexterity' attribute, you can use it to give that character
|
|
||||||
an advantage in turn order, like so:
|
|
||||||
|
|
||||||
return (randint(1,20)) + character.db.dexterity
|
|
||||||
|
|
||||||
This way, characters with a higher dexterity will go first more often.
|
|
||||||
"""
|
|
||||||
return randint(1, 1000)
|
|
||||||
|
|
||||||
|
|
||||||
def get_attack(attacker, defender, attack_type):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for an attack roll.
|
Returns a value for an attack roll.
|
||||||
|
|
||||||
|
|
@ -178,8 +154,7 @@ def get_attack(attacker, defender, attack_type):
|
||||||
attack_value -= 15
|
attack_value -= 15
|
||||||
return attack_value
|
return attack_value
|
||||||
|
|
||||||
|
def get_defense(self, attacker, defender, attack_type='melee'):
|
||||||
def get_defense(attacker, defender, attack_type):
|
|
||||||
"""
|
"""
|
||||||
Returns a value for defense, which an attack roll must equal or exceed in order
|
Returns a value for defense, which an attack roll must equal or exceed in order
|
||||||
for an attack to hit.
|
for an attack to hit.
|
||||||
|
|
@ -203,101 +178,7 @@ def get_defense(attacker, defender, attack_type):
|
||||||
defense_value = 50
|
defense_value = 50
|
||||||
return defense_value
|
return defense_value
|
||||||
|
|
||||||
|
def get_range(self, obj1, obj2):
|
||||||
def get_damage(attacker, defender):
|
|
||||||
"""
|
|
||||||
Returns a value for damage to be deducted from the defender's HP after abilities
|
|
||||||
successful hit.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
attacker (obj): Character doing the attacking
|
|
||||||
defender (obj): Character being damaged
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
damage_value (int): Damage value, which is to be deducted from the defending
|
|
||||||
character's HP.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
By default, returns a random integer from 15 to 25 without using any
|
|
||||||
properties from either the attacker or defender.
|
|
||||||
|
|
||||||
Again, this can be expanded upon.
|
|
||||||
"""
|
|
||||||
# For this example, just generate a number between 15 and 25.
|
|
||||||
damage_value = randint(15, 25)
|
|
||||||
return damage_value
|
|
||||||
|
|
||||||
|
|
||||||
def apply_damage(defender, damage):
|
|
||||||
"""
|
|
||||||
Applies damage to a target, reducing their HP by the damage amount to a
|
|
||||||
minimum of 0.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
defender (obj): Character taking damage
|
|
||||||
damage (int): Amount of damage being taken
|
|
||||||
"""
|
|
||||||
defender.db.hp -= damage # Reduce defender's HP by the damage dealt.
|
|
||||||
# If this reduces it to 0 or less, set HP to 0.
|
|
||||||
if defender.db.hp <= 0:
|
|
||||||
defender.db.hp = 0
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
into a dying state or something similar) then this is the place to
|
|
||||||
do it.
|
|
||||||
"""
|
|
||||||
defeated.location.msg_contents("%s has been defeated!" % defeated)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_attack(attacker, defender, attack_type, attack_value=None, defense_value=None):
|
|
||||||
"""
|
|
||||||
Resolves an attack and outputs the result.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
attacker (obj): Character doing the attacking
|
|
||||||
defender (obj): Character being attacked
|
|
||||||
attack_type (str): Type of attack (melee or ranged)
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
Even though the attack and defense values are calculated
|
|
||||||
extremely simply, they are separated out into their own functions
|
|
||||||
so that they are easier to expand upon.
|
|
||||||
"""
|
|
||||||
# Get an attack roll from the attacker.
|
|
||||||
if not attack_value:
|
|
||||||
attack_value = get_attack(attacker, defender, attack_type)
|
|
||||||
# Get a defense value from the defender.
|
|
||||||
if not defense_value:
|
|
||||||
defense_value = get_defense(attacker, defender, attack_type)
|
|
||||||
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
|
||||||
if attack_value < defense_value:
|
|
||||||
attacker.location.msg_contents(
|
|
||||||
"%s's %s attack misses %s!" % (attacker, attack_type, defender)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
|
||||||
# Announce damage dealt and apply damage.
|
|
||||||
attacker.location.msg_contents(
|
|
||||||
"%s hits %s with a %s attack for %i damage!"
|
|
||||||
% (attacker, defender, attack_type, damage_value)
|
|
||||||
)
|
|
||||||
apply_damage(defender, damage_value)
|
|
||||||
# If defender HP is reduced to 0 or less, call at_defeat.
|
|
||||||
if defender.db.hp <= 0:
|
|
||||||
at_defeat(defender)
|
|
||||||
|
|
||||||
|
|
||||||
def get_range(obj1, obj2):
|
|
||||||
"""
|
"""
|
||||||
Gets the combat range between two objects.
|
Gets the combat range between two objects.
|
||||||
|
|
||||||
|
|
@ -320,8 +201,7 @@ def get_range(obj1, obj2):
|
||||||
# Return the range between the two objects.
|
# Return the range between the two objects.
|
||||||
return obj1.db.combat_range[obj2]
|
return obj1.db.combat_range[obj2]
|
||||||
|
|
||||||
|
def distance_inc(self, mover, target):
|
||||||
def distance_inc(mover, target):
|
|
||||||
"""
|
"""
|
||||||
Function that increases distance in range field between mover and target.
|
Function that increases distance in range field between mover and target.
|
||||||
|
|
||||||
|
|
@ -332,12 +212,32 @@ def distance_inc(mover, target):
|
||||||
mover.db.combat_range[target] += 1
|
mover.db.combat_range[target] += 1
|
||||||
target.db.combat_range[mover] = mover.db.combat_range[target]
|
target.db.combat_range[mover] = mover.db.combat_range[target]
|
||||||
# Set a cap of 2:
|
# Set a cap of 2:
|
||||||
if get_range(mover, target) > 2:
|
if self.get_range(mover, target) > 2:
|
||||||
target.db.combat_range[mover] = 2
|
target.db.combat_range[mover] = 2
|
||||||
mover.db.combat_range[target] = 2
|
mover.db.combat_range[target] = 2
|
||||||
|
|
||||||
|
def distance_dec(self, mover, target):
|
||||||
|
"""
|
||||||
|
Helper function that decreases distance in range field between mover and target.
|
||||||
|
|
||||||
def approach(mover, target):
|
Args:
|
||||||
|
mover (obj): The object moving
|
||||||
|
target (obj): The object to be moved toward
|
||||||
|
"""
|
||||||
|
mover.db.combat_range[target] -= 1
|
||||||
|
target.db.combat_range[mover] = mover.db.combat_range[target]
|
||||||
|
# If this brings mover to range 0 (Engaged):
|
||||||
|
if self.get_range(mover, target) <= 0:
|
||||||
|
# Reset range to each other to 0 and copy target's ranges to mover.
|
||||||
|
target.db.combat_range[mover] = 0
|
||||||
|
mover.db.combat_range = target.db.combat_range
|
||||||
|
# Assure everything else has the same distance from the mover and target, now that
|
||||||
|
# they're together
|
||||||
|
for thing in mover.location.contents:
|
||||||
|
if thing != mover and thing != target:
|
||||||
|
thing.db.combat_range[mover] = thing.db.combat_range[target]
|
||||||
|
|
||||||
|
def approach(self, mover, target):
|
||||||
"""
|
"""
|
||||||
Manages a character's whole approach, including changes in ranges to other characters.
|
Manages a character's whole approach, including changes in ranges to other characters.
|
||||||
|
|
||||||
|
|
@ -351,41 +251,20 @@ def approach(mover, target):
|
||||||
out close to.
|
out close to.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def distance_dec(mover, target):
|
|
||||||
"""
|
|
||||||
Helper function that decreases distance in range field between mover and target.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mover (obj): The object moving
|
|
||||||
target (obj): The object to be moved toward
|
|
||||||
"""
|
|
||||||
mover.db.combat_range[target] -= 1
|
|
||||||
target.db.combat_range[mover] = mover.db.combat_range[target]
|
|
||||||
# If this brings mover to range 0 (Engaged):
|
|
||||||
if get_range(mover, target) <= 0:
|
|
||||||
# Reset range to each other to 0 and copy target's ranges to mover.
|
|
||||||
target.db.combat_range[mover] = 0
|
|
||||||
mover.db.combat_range = target.db.combat_range
|
|
||||||
# Assure everything else has the same distance from the mover and target, now that they're together
|
|
||||||
for thing in mover.location.contents:
|
|
||||||
if thing != mover and thing != target:
|
|
||||||
thing.db.combat_range[mover] = thing.db.combat_range[target]
|
|
||||||
|
|
||||||
contents = mover.location.contents
|
contents = mover.location.contents
|
||||||
|
|
||||||
for thing in contents:
|
for thing in contents:
|
||||||
if thing != mover and thing != target:
|
if thing != mover and thing != target:
|
||||||
# Move closer to each object closer to the target than you.
|
# Move closer to each object closer to the target than you.
|
||||||
if get_range(mover, thing) > get_range(target, thing):
|
if self.get_range(mover, thing) > self.get_range(target, thing):
|
||||||
distance_dec(mover, thing)
|
self.distance_dec(mover, thing)
|
||||||
# Move further from each object that's further from you than from the target.
|
# Move further from each object that's further from you than from the target.
|
||||||
if get_range(mover, thing) < get_range(target, thing):
|
if self.get_range(mover, thing) < self.get_range(target, thing):
|
||||||
distance_inc(mover, thing)
|
self.distance_inc(mover, thing)
|
||||||
# Lastly, move closer to your target.
|
# Lastly, move closer to your target.
|
||||||
distance_dec(mover, target)
|
self.distance_dec(mover, target)
|
||||||
|
|
||||||
|
def withdraw(self, mover, target):
|
||||||
def withdraw(mover, target):
|
|
||||||
"""
|
"""
|
||||||
Manages a character's whole withdrawal, including changes in ranges to other characters.
|
Manages a character's whole withdrawal, including changes in ranges to other characters.
|
||||||
|
|
||||||
|
|
@ -403,89 +282,60 @@ def withdraw(mover, target):
|
||||||
|
|
||||||
for thing in contents:
|
for thing in contents:
|
||||||
if thing != mover and thing != target:
|
if thing != mover and thing != target:
|
||||||
# Move away from each object closer to the target than you, if it's also closer to you than you are to the target.
|
# Move away from each object closer to the target than you, if it's also closer to
|
||||||
if get_range(mover, thing) >= get_range(target, thing) and get_range(
|
# you than you are to the target.
|
||||||
mover, thing
|
if (self.get_range(mover, thing) >= self.get_range(target, thing)
|
||||||
) < get_range(mover, target):
|
and self.get_range(mover, thing) < self.get_range(mover, target)):
|
||||||
distance_inc(mover, thing)
|
self.distance_inc(mover, thing)
|
||||||
# Move away from anything your target is engaged with
|
# Move away from anything your target is engaged with
|
||||||
if get_range(target, thing) == 0:
|
if self.get_range(target, thing) == 0:
|
||||||
distance_inc(mover, thing)
|
self.distance_inc(mover, thing)
|
||||||
# Move away from anything you're engaged with.
|
# Move away from anything you're engaged with.
|
||||||
if get_range(mover, thing) == 0:
|
if self.get_range(mover, thing) == 0:
|
||||||
distance_inc(mover, thing)
|
self.distance_inc(mover, thing)
|
||||||
# Then, move away from your target.
|
# Then, move away from your target.
|
||||||
distance_inc(mover, target)
|
self.distance_inc(mover, target)
|
||||||
|
|
||||||
|
def resolve_attack(self, attacker, defender, attack_value=None, defense_value=None,
|
||||||
def combat_cleanup(character):
|
attack_type='melee'):
|
||||||
"""
|
"""
|
||||||
Cleans up all the temporary combat-related attributes on a character.
|
Resolves an attack and outputs the result.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
character (obj): Character to have their combat attributes removed
|
attacker (obj): Character doing the attacking
|
||||||
|
defender (obj): Character being attacked
|
||||||
|
attack_type (str): Type of attack (melee or ranged)
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
Any attribute whose key begins with 'combat_' is temporary and no
|
Even though the attack and defense values are calculated
|
||||||
longer needed once a fight ends.
|
extremely simply, they are separated out into their own functions
|
||||||
|
so that they are easier to expand upon.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for attr in character.attributes.all():
|
# Get an attack roll from the attacker.
|
||||||
if attr.key[:7] == "combat_": # If the attribute name starts with 'combat_'...
|
if not attack_value:
|
||||||
character.attributes.remove(key=attr.key) # ...then delete it!
|
attack_value = self.get_attack(attacker, defender, attack_type)
|
||||||
|
# Get a defense value from the defender.
|
||||||
|
if not defense_value:
|
||||||
def is_in_combat(character):
|
defense_value = self.get_defense(attacker, defender, attack_type)
|
||||||
"""
|
# If the attack value is lower than the defense value, miss. Otherwise, hit.
|
||||||
Returns true if the given character is in combat.
|
if attack_value < defense_value:
|
||||||
|
attacker.location.msg_contents(
|
||||||
Args:
|
"%s's %s attack misses %s!" % (attacker, attack_type, defender)
|
||||||
character (obj): Character to determine if is in combat or not
|
)
|
||||||
|
|
||||||
Returns:
|
|
||||||
(bool): True if in combat or False if not in combat
|
|
||||||
"""
|
|
||||||
return bool(character.db.combat_turnhandler)
|
|
||||||
|
|
||||||
|
|
||||||
def is_turn(character):
|
|
||||||
"""
|
|
||||||
Returns true if it's currently the given character's turn in combat.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to determine if it is their turn or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
(bool): True if it is their turn or False otherwise
|
|
||||||
"""
|
|
||||||
turnhandler = character.db.combat_turnhandler
|
|
||||||
currentchar = turnhandler.db.fighters[turnhandler.db.turn]
|
|
||||||
return bool(character == currentchar)
|
|
||||||
|
|
||||||
|
|
||||||
def spend_action(character, actions, action_name=None):
|
|
||||||
"""
|
|
||||||
Spends a character's available combat actions and checks for end of turn.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character spending the action
|
|
||||||
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
|
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
action_name (str or None): If a string is given, sets character's last action in
|
|
||||||
combat to provided string
|
|
||||||
"""
|
|
||||||
if action_name:
|
|
||||||
character.db.combat_lastaction = action_name
|
|
||||||
if actions == "all": # If spending all actions
|
|
||||||
character.db.combat_actionsleft = 0 # Set actions to 0
|
|
||||||
else:
|
else:
|
||||||
character.db.combat_actionsleft -= actions # Use up actions.
|
damage_value = self.get_damage(attacker, defender) # Calculate damage value.
|
||||||
if character.db.combat_actionsleft < 0:
|
# Announce damage dealt and apply damage.
|
||||||
character.db.combat_actionsleft = 0 # Can't have fewer than 0 actions
|
attacker.location.msg_contents(
|
||||||
character.db.combat_turnhandler.turn_end_check(character) # Signal potential end of turn.
|
"%s hits %s with a %s attack for %i damage!"
|
||||||
|
% (attacker, defender, attack_type, damage_value)
|
||||||
|
)
|
||||||
|
self.apply_damage(defender, damage_value)
|
||||||
|
# If defender HP is reduced to 0 or less, call at_defeat.
|
||||||
|
if defender.db.hp <= 0:
|
||||||
|
self.at_defeat(defender)
|
||||||
|
|
||||||
|
def combat_status_message(self, fighter):
|
||||||
def combat_status_message(fighter):
|
|
||||||
"""
|
"""
|
||||||
Sends a message to a player with their current HP and
|
Sends a message to a player with their current HP and
|
||||||
distances to other fighters and objects. Called at turn
|
distances to other fighters and objects. Called at turn
|
||||||
|
|
@ -497,7 +347,7 @@ def combat_status_message(fighter):
|
||||||
|
|
||||||
status_msg = "HP Remaining: %i / %i" % (fighter.db.hp, fighter.db.max_hp)
|
status_msg = "HP Remaining: %i / %i" % (fighter.db.hp, fighter.db.max_hp)
|
||||||
|
|
||||||
if not is_in_combat(fighter):
|
if not self.is_in_combat(fighter):
|
||||||
fighter.msg(status_msg)
|
fighter.msg(status_msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -524,6 +374,8 @@ def combat_status_message(fighter):
|
||||||
fighter.msg(status_msg)
|
fighter.msg(status_msg)
|
||||||
|
|
||||||
|
|
||||||
|
COMBAT_RULES = RangedCombatRules()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
SCRIPTS START HERE
|
SCRIPTS START HERE
|
||||||
|
|
@ -531,7 +383,7 @@ SCRIPTS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TBRangeTurnHandler(DefaultScript):
|
class TBRangeTurnHandler(tb_basic.TBBasicTurnHandler):
|
||||||
"""
|
"""
|
||||||
This is the script that handles the progression of combat through turns.
|
This is the script that handles the progression of combat through turns.
|
||||||
On creation (when a fight is started) it adds all combat-ready characters
|
On creation (when a fight is started) it adds all combat-ready characters
|
||||||
|
|
@ -544,74 +396,7 @@ class TBRangeTurnHandler(DefaultScript):
|
||||||
command.
|
command.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def at_script_creation(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
|
||||||
Called once, when the script is created.
|
|
||||||
"""
|
|
||||||
self.key = "Combat Turn Handler"
|
|
||||||
self.interval = 5 # Once every 5 seconds
|
|
||||||
self.persistent = True
|
|
||||||
self.db.fighters = []
|
|
||||||
|
|
||||||
# Add all fighters in the room with at least 1 HP to the combat."
|
|
||||||
for thing in self.obj.contents:
|
|
||||||
if thing.db.hp:
|
|
||||||
self.db.fighters.append(thing)
|
|
||||||
|
|
||||||
# Initialize each fighter for combat
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
self.initialize_for_combat(fighter)
|
|
||||||
|
|
||||||
# Add a reference to this script to the room
|
|
||||||
self.obj.db.combat_turnhandler = self
|
|
||||||
|
|
||||||
# Initialize range field for all objects in the room
|
|
||||||
for thing in self.obj.contents:
|
|
||||||
self.init_range(thing)
|
|
||||||
|
|
||||||
# Roll initiative and sort the list of fighters depending on who rolls highest to determine turn order.
|
|
||||||
# The initiative roll is determined by the roll_init function and can be customized easily.
|
|
||||||
ordered_by_roll = sorted(self.db.fighters, key=roll_init, reverse=True)
|
|
||||||
self.db.fighters = ordered_by_roll
|
|
||||||
|
|
||||||
# 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])
|
|
||||||
|
|
||||||
# Set up the current turn and turn timeout delay.
|
|
||||||
self.db.turn = 0
|
|
||||||
self.db.timer = TURN_TIMEOUT # Set timer to turn timeout specified in options
|
|
||||||
|
|
||||||
def at_stop(self):
|
|
||||||
"""
|
|
||||||
Called at script termination.
|
|
||||||
"""
|
|
||||||
for thing in self.obj.contents:
|
|
||||||
combat_cleanup(thing) # Clean up the combat attributes for every object in the room.
|
|
||||||
self.obj.db.combat_turnhandler = None # Remove reference to turn handler in location
|
|
||||||
|
|
||||||
def at_repeat(self):
|
|
||||||
"""
|
|
||||||
Called once every self.interval seconds.
|
|
||||||
"""
|
|
||||||
currentchar = self.db.fighters[
|
|
||||||
self.db.turn
|
|
||||||
] # Note the current character in the turn order.
|
|
||||||
self.db.timer -= self.interval # Count down the timer.
|
|
||||||
|
|
||||||
if self.db.timer <= 0:
|
|
||||||
# Force current character to disengage if timer runs out.
|
|
||||||
self.obj.msg_contents("%s's turn timed out!" % currentchar)
|
|
||||||
spend_action(
|
|
||||||
currentchar, "all", action_name="disengage"
|
|
||||||
) # Spend all remaining actions.
|
|
||||||
return
|
|
||||||
elif self.db.timer <= 10 and not self.db.timeout_warning_given: # 10 seconds left
|
|
||||||
# Warn the current character if they're about to time out.
|
|
||||||
currentchar.msg("WARNING: About to time out!")
|
|
||||||
self.db.timeout_warning_given = True
|
|
||||||
|
|
||||||
def init_range(self, to_init):
|
def init_range(self, to_init):
|
||||||
"""
|
"""
|
||||||
|
|
@ -663,23 +448,7 @@ class TBRangeTurnHandler(DefaultScript):
|
||||||
to_init.db.combat_range.update({to_init: 0})
|
to_init.db.combat_range.update({to_init: 0})
|
||||||
# Add additional distance from anchor object, if any.
|
# Add additional distance from anchor object, if any.
|
||||||
for n in range(add_distance):
|
for n in range(add_distance):
|
||||||
withdraw(to_init, anchor_obj)
|
self.rules.withdraw(to_init, anchor_obj)
|
||||||
|
|
||||||
def initialize_for_combat(self, character):
|
|
||||||
"""
|
|
||||||
Prepares a character for combat when starting or entering a fight.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to initialize for combat.
|
|
||||||
"""
|
|
||||||
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
|
|
||||||
character.db.combat_actionsleft = (
|
|
||||||
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
|
||||||
)
|
|
||||||
character.db.combat_turnhandler = (
|
|
||||||
self # Add a reference to this turn handler script to the character
|
|
||||||
)
|
|
||||||
character.db.combat_lastaction = "null" # Track last action taken in combat
|
|
||||||
|
|
||||||
def start_turn(self, character):
|
def start_turn(self, character):
|
||||||
"""
|
"""
|
||||||
|
|
@ -694,64 +463,8 @@ class TBRangeTurnHandler(DefaultScript):
|
||||||
characters to both move and attack in the same turn (or, alternately,
|
characters to both move and attack in the same turn (or, alternately,
|
||||||
move twice or attack twice).
|
move twice or attack twice).
|
||||||
"""
|
"""
|
||||||
character.db.combat_actionsleft = ACTIONS_PER_TURN # Replenish actions
|
super().start_turn(character)
|
||||||
# Prompt the character for their turn and give some information.
|
character.db.combat_actionsleft = ACTIONS_PER_TURN
|
||||||
character.msg("|wIt's your turn!|n")
|
|
||||||
combat_status_message(character)
|
|
||||||
|
|
||||||
def next_turn(self):
|
|
||||||
"""
|
|
||||||
Advances to the next character in the turn order.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check to see if every character disengaged as their last action. If so, end combat.
|
|
||||||
disengage_check = True
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if (
|
|
||||||
fighter.db.combat_lastaction != "disengage"
|
|
||||||
): # If a character has done anything but disengage
|
|
||||||
disengage_check = False
|
|
||||||
if disengage_check: # All characters have disengaged
|
|
||||||
self.obj.msg_contents("All fighters have disengaged! Combat is over!")
|
|
||||||
self.stop() # Stop this script and end combat.
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check to see if only one character is left standing. If so, end combat.
|
|
||||||
defeated_characters = 0
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if fighter.db.HP == 0:
|
|
||||||
defeated_characters += 1 # Add 1 for every fighter with 0 HP left (defeated)
|
|
||||||
if defeated_characters == (
|
|
||||||
len(self.db.fighters) - 1
|
|
||||||
): # If only one character isn't defeated
|
|
||||||
for fighter in self.db.fighters:
|
|
||||||
if fighter.db.HP != 0:
|
|
||||||
LastStanding = fighter # Pick the one fighter left with HP remaining
|
|
||||||
self.obj.msg_contents("Only %s remains! Combat is over!" % LastStanding)
|
|
||||||
self.stop() # Stop this script and end combat.
|
|
||||||
return
|
|
||||||
|
|
||||||
# Cycle to the next turn.
|
|
||||||
currentchar = self.db.fighters[self.db.turn]
|
|
||||||
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.
|
|
||||||
|
|
||||||
def turn_end_check(self, character):
|
|
||||||
"""
|
|
||||||
Tests to see if a character's turn is over, and cycles to the next turn if it is.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
character (obj): Character to test for end of turn
|
|
||||||
"""
|
|
||||||
if not character.db.combat_actionsleft: # Character has no actions remaining
|
|
||||||
self.next_turn()
|
|
||||||
return
|
|
||||||
|
|
||||||
def join_fight(self, character):
|
def join_fight(self, character):
|
||||||
"""
|
"""
|
||||||
|
|
@ -778,51 +491,12 @@ TYPECLASSES START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TBRangeCharacter(DefaultCharacter):
|
class TBRangeCharacter(tb_basic.TBBasicCharacter):
|
||||||
"""
|
"""
|
||||||
A character able to participate in turn-based combat. Has attributes for current
|
A character able to participate in turn-based combat. Has attributes for current
|
||||||
and maximum HP, and access to combat commands.
|
and maximum HP, and access to combat commands.
|
||||||
"""
|
"""
|
||||||
|
rules = COMBAT_RULES
|
||||||
def at_object_creation(self):
|
|
||||||
"""
|
|
||||||
Called once, when this object is first created. This is the
|
|
||||||
normal hook to overload for most object types.
|
|
||||||
"""
|
|
||||||
self.db.max_hp = 100 # Set maximum HP to 100
|
|
||||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
|
||||||
"""
|
|
||||||
Adds attributes for a character's current and maximum HP.
|
|
||||||
We're just going to set this value at '100' by default.
|
|
||||||
|
|
||||||
You may want to expand this to include various 'stats' that
|
|
||||||
can be changed at creation and factor into combat calculations.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def at_pre_move(self, destination):
|
|
||||||
"""
|
|
||||||
Called just before starting to move this object to
|
|
||||||
destination.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (Object): The object we are moving to
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
shouldmove (bool): If we should move or not.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
If this method returns False/None, the move is cancelled
|
|
||||||
before it is even started.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Keep the character from moving if at 0 HP or in combat.
|
|
||||||
if is_in_combat(self):
|
|
||||||
self.msg("You can't exit a room while in combat!")
|
|
||||||
return False # Returning false keeps the character from moving.
|
|
||||||
if self.db.HP <= 0:
|
|
||||||
self.msg("You can't move, you've been defeated!")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class TBRangeObject(DefaultObject):
|
class TBRangeObject(DefaultObject):
|
||||||
|
|
@ -852,7 +526,7 @@ class TBRangeObject(DefaultObject):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Can't drop something if in combat and it's not your turn
|
# Can't drop something if in combat and it's not your turn
|
||||||
if is_in_combat(dropper) and not is_turn(dropper):
|
if self.rules.is_in_combat(dropper) and not self.rules.is_turn(dropper):
|
||||||
dropper.msg("You can only drop things on your turn!")
|
dropper.msg("You can only drop things on your turn!")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -896,11 +570,11 @@ class TBRangeObject(DefaultObject):
|
||||||
before it is even started.
|
before it is even started.
|
||||||
"""
|
"""
|
||||||
# Restrictions for getting in combat
|
# Restrictions for getting in combat
|
||||||
if is_in_combat(getter):
|
if self.rules.is_in_combat(getter):
|
||||||
if not is_turn(getter): # Not your turn
|
if not self.rules.is_turn(getter): # Not your turn
|
||||||
getter.msg("You can only get things on your turn!")
|
getter.msg("You can only get things on your turn!")
|
||||||
return False
|
return False
|
||||||
if get_range(self, getter) > 0: # Too far away
|
if self.rules.get_range(self, getter) > 0: # Too far away
|
||||||
getter.msg("You aren't close enough to get that! (see: help approach)")
|
getter.msg("You aren't close enough to get that! (see: help approach)")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
@ -929,8 +603,8 @@ class TBRangeObject(DefaultObject):
|
||||||
if self in thing.db.combat_range:
|
if self in thing.db.combat_range:
|
||||||
thing.db.combat_range.pop(self, None)
|
thing.db.combat_range.pop(self, None)
|
||||||
# If in combat, getter spends an action
|
# If in combat, getter spends an action
|
||||||
if is_in_combat(getter):
|
if self.rules.is_in_combat(getter):
|
||||||
spend_action(getter, 1, action_name="get") # Use up one action.
|
self.rules.spend_action(getter, 1, action_name="get") # Use up one action.
|
||||||
|
|
||||||
def at_pre_give(self, giver, getter):
|
def at_pre_give(self, giver, getter):
|
||||||
"""
|
"""
|
||||||
|
|
@ -952,11 +626,11 @@ class TBRangeObject(DefaultObject):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Restrictions for giving in combat
|
# Restrictions for giving in combat
|
||||||
if is_in_combat(giver):
|
if self.rules.is_in_combat(giver):
|
||||||
if not is_turn(giver): # Not your turn
|
if not self.rules.is_turn(giver): # Not your turn
|
||||||
giver.msg("You can only give things on your turn!")
|
giver.msg("You can only give things on your turn!")
|
||||||
return False
|
return False
|
||||||
if get_range(giver, getter) > 0: # Too far away from target
|
if self.rules.get_range(giver, getter) > 0: # Too far away from target
|
||||||
giver.msg(
|
giver.msg(
|
||||||
"You aren't close enough to give things to %s! (see: help approach)" % getter
|
"You aren't close enough to give things to %s! (see: help approach)" % getter
|
||||||
)
|
)
|
||||||
|
|
@ -980,8 +654,8 @@ class TBRangeObject(DefaultObject):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Spend an action if in combat
|
# Spend an action if in combat
|
||||||
if is_in_combat(giver):
|
if self.rules.is_in_combat(giver):
|
||||||
spend_action(giver, 1, action_name="give") # Use up one action.
|
self.rules.spend_action(giver, 1, action_name="give") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -991,7 +665,7 @@ COMMANDS START HERE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CmdFight(Command):
|
class CmdFight(tb_basic.CmdFight):
|
||||||
"""
|
"""
|
||||||
Starts a fight with everyone in the same room as you.
|
Starts a fight with everyone in the same room as you.
|
||||||
|
|
||||||
|
|
@ -1006,36 +680,11 @@ class CmdFight(Command):
|
||||||
key = "fight"
|
key = "fight"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
combat_handler_class = TBRangeTurnHandler
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
here = self.caller.location
|
|
||||||
fighters = []
|
|
||||||
|
|
||||||
if not self.caller.db.hp: # If you don't have any hp
|
|
||||||
self.caller.msg("You can't start a fight if you've been defeated!")
|
|
||||||
return
|
|
||||||
if is_in_combat(self.caller): # Already in a fight
|
|
||||||
self.caller.msg("You're already in a fight!")
|
|
||||||
return
|
|
||||||
for thing in here.contents: # Test everything in the room to add it to the fight.
|
|
||||||
if thing.db.HP: # If the object has HP...
|
|
||||||
fighters.append(thing) # ...then add it to the fight.
|
|
||||||
if len(fighters) <= 1: # If you're the only able fighter in the room
|
|
||||||
self.caller.msg("There's nobody here to fight!")
|
|
||||||
return
|
|
||||||
if here.db.combat_turnhandler: # If there's already a fight going on...
|
|
||||||
here.msg_contents("%s joins the fight!" % self.caller)
|
|
||||||
here.db.combat_turnhandler.join_fight(self.caller) # Join the fight!
|
|
||||||
return
|
|
||||||
here.msg_contents("%s starts a fight!" % self.caller)
|
|
||||||
# Add a turn handler script to the room, which starts combat.
|
|
||||||
here.scripts.add("contrib.game_systems.turnbattle.tb_range.TBRangeTurnHandler")
|
|
||||||
# Remember you'll have to change the path to the script if you copy this code to your own modules!
|
|
||||||
|
|
||||||
|
|
||||||
class CmdAttack(Command):
|
class CmdAttack(tb_basic.CmdAttack):
|
||||||
"""
|
"""
|
||||||
Attacks another character in melee.
|
Attacks another character in melee.
|
||||||
|
|
||||||
|
|
@ -1051,15 +700,17 @@ class CmdAttack(Command):
|
||||||
key = "attack"
|
key = "attack"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
"Set the attacker to the caller and the defender to the target."
|
"Set the attacker to the caller and the defender to the target."
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't attack.
|
if not self.rules.is_in_combat(self.caller): # If not in combat, can't attack.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't attack.
|
if not self.rules.is_turn(self.caller): # If it's not your turn, can't attack.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1081,7 +732,7 @@ class CmdAttack(Command):
|
||||||
self.caller.msg("You can't attack yourself!")
|
self.caller.msg("You can't attack yourself!")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not get_range(attacker, defender) == 0: # Target isn't in melee
|
if not self.rules.get_range(attacker, defender) == 0: # Target isn't in melee
|
||||||
self.caller.msg(
|
self.caller.msg(
|
||||||
"%s is too far away to attack - you need to get closer! (see: help approach)"
|
"%s is too far away to attack - you need to get closer! (see: help approach)"
|
||||||
% defender
|
% defender
|
||||||
|
|
@ -1089,8 +740,8 @@ class CmdAttack(Command):
|
||||||
return
|
return
|
||||||
|
|
||||||
"If everything checks out, call the attack resolving function."
|
"If everything checks out, call the attack resolving function."
|
||||||
resolve_attack(attacker, defender, "melee")
|
self.rules.resolve_attack(attacker, defender, "melee")
|
||||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdShoot(Command):
|
class CmdShoot(Command):
|
||||||
|
|
@ -1110,15 +761,17 @@ class CmdShoot(Command):
|
||||||
key = "shoot"
|
key = "shoot"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
"Set the attacker to the caller and the defender to the target."
|
"Set the attacker to the caller and the defender to the target."
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't attack.
|
if not self.rules.is_in_combat(self.caller): # If not in combat, can't attack.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't attack.
|
if not self.rules.is_turn(self.caller): # If it's not your turn, can't attack.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1144,19 +797,21 @@ class CmdShoot(Command):
|
||||||
in_melee = []
|
in_melee = []
|
||||||
for target in attacker.db.combat_range:
|
for target in attacker.db.combat_range:
|
||||||
# Object is engaged and has HP
|
# Object is engaged and has HP
|
||||||
if get_range(attacker, defender) == 0 and target.db.hp and target != self.caller:
|
if (self.rules.get_range(attacker, defender) == 0
|
||||||
|
and target.db.hp and target != self.caller):
|
||||||
in_melee.append(target) # Add to list of targets in melee
|
in_melee.append(target) # Add to list of targets in melee
|
||||||
|
|
||||||
if len(in_melee) > 0:
|
if len(in_melee) > 0:
|
||||||
self.caller.msg(
|
self.caller.msg(
|
||||||
"You can't shoot because there are fighters engaged with you (%s) - you need to retreat! (see: help withdraw)"
|
"You can't shoot because there are fighters engaged with you (%s) - you need "
|
||||||
|
"to retreat! (see: help withdraw)"
|
||||||
% ", ".join(obj.key for obj in in_melee)
|
% ", ".join(obj.key for obj in in_melee)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
"If everything checks out, call the attack resolving function."
|
"If everything checks out, call the attack resolving function."
|
||||||
resolve_attack(attacker, defender, "ranged")
|
self.rules.resolve_attack(attacker, defender, "ranged")
|
||||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdApproach(Command):
|
class CmdApproach(Command):
|
||||||
|
|
@ -1173,14 +828,16 @@ class CmdApproach(Command):
|
||||||
key = "approach"
|
key = "approach"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't approach.
|
if not self.rules.is_in_combat(self.caller): # If not in combat, can't approach.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't approach.
|
if not self.rules.is_turn(self.caller): # If it's not your turn, can't approach.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1202,14 +859,14 @@ class CmdApproach(Command):
|
||||||
self.caller.msg("You can't move toward yourself!")
|
self.caller.msg("You can't move toward yourself!")
|
||||||
return
|
return
|
||||||
|
|
||||||
if get_range(mover, target) <= 0: # Already engaged with target
|
if self.rules.get_range(mover, target) <= 0: # Already engaged with target
|
||||||
self.caller.msg("You're already next to that target!")
|
self.caller.msg("You're already next to that target!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# If everything checks out, call the approach resolving function.
|
# If everything checks out, call the approach resolving function.
|
||||||
approach(mover, target)
|
self.rules.approach(mover, target)
|
||||||
mover.location.msg_contents("%s moves toward %s." % (mover, target))
|
mover.location.msg_contents("%s moves toward %s." % (mover, target))
|
||||||
spend_action(self.caller, 1, action_name="move") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="move") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdWithdraw(Command):
|
class CmdWithdraw(Command):
|
||||||
|
|
@ -1225,14 +882,16 @@ class CmdWithdraw(Command):
|
||||||
key = "withdraw"
|
key = "withdraw"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
|
|
||||||
if not is_in_combat(self.caller): # If not in combat, can't withdraw.
|
if not self.rules.is_in_combat(self.caller): # If not in combat, can't withdraw.
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
self.caller.msg("You can only do that in combat. (see: help fight)")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn, can't withdraw.
|
if not self.rules.is_turn(self.caller): # If it's not your turn, can't withdraw.
|
||||||
self.caller.msg("You can only do that on your turn.")
|
self.caller.msg("You can only do that on your turn.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1259,12 +918,12 @@ class CmdWithdraw(Command):
|
||||||
return
|
return
|
||||||
|
|
||||||
# If everything checks out, call the approach resolving function.
|
# If everything checks out, call the approach resolving function.
|
||||||
withdraw(mover, target)
|
self.rules.withdraw(mover, target)
|
||||||
mover.location.msg_contents("%s moves away from %s." % (mover, target))
|
mover.location.msg_contents("%s moves away from %s." % (mover, target))
|
||||||
spend_action(self.caller, 1, action_name="move") # Use up one action.
|
self.rules.spend_action(self.caller, 1, action_name="move") # Use up one action.
|
||||||
|
|
||||||
|
|
||||||
class CmdPass(Command):
|
class CmdPass(tb_basic.CmdPass):
|
||||||
"""
|
"""
|
||||||
Passes on your turn.
|
Passes on your turn.
|
||||||
|
|
||||||
|
|
@ -1279,25 +938,10 @@ class CmdPass(Command):
|
||||||
aliases = ["wait", "hold"]
|
aliases = ["wait", "hold"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
if not is_in_combat(self.caller): # Can only pass a turn in combat.
|
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not is_turn(self.caller): # Can only pass if it's your turn.
|
|
||||||
self.caller.msg("You can only do that on your turn.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.location.msg_contents(
|
|
||||||
"%s takes no further action, passing the turn." % self.caller
|
|
||||||
)
|
|
||||||
spend_action(self.caller, "all", action_name="pass") # Spend all remaining actions.
|
|
||||||
|
|
||||||
|
|
||||||
class CmdDisengage(Command):
|
class CmdDisengage(tb_basic.CmdDisengage):
|
||||||
"""
|
"""
|
||||||
Passes your turn and attempts to end combat.
|
Passes your turn and attempts to end combat.
|
||||||
|
|
||||||
|
|
@ -1313,27 +957,10 @@ class CmdDisengage(Command):
|
||||||
aliases = ["spare"]
|
aliases = ["spare"]
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"""
|
|
||||||
This performs the actual command.
|
|
||||||
"""
|
|
||||||
if not is_in_combat(self.caller): # If you're not in combat
|
|
||||||
self.caller.msg("You can only do that in combat. (see: help fight)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not is_turn(self.caller): # If it's not your turn
|
|
||||||
self.caller.msg("You can only do that on your turn.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.location.msg_contents("%s disengages, ready to stop fighting." % self.caller)
|
|
||||||
spend_action(self.caller, "all", action_name="disengage") # Spend all remaining actions.
|
|
||||||
"""
|
|
||||||
The action_name kwarg sets the character's last action to "disengage", which is checked by
|
|
||||||
the turn handler script to see if all fighters have disengaged.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CmdRest(Command):
|
class CmdRest(tb_basic.CmdRest):
|
||||||
"""
|
"""
|
||||||
Recovers damage.
|
Recovers damage.
|
||||||
|
|
||||||
|
|
@ -1347,18 +974,7 @@ class CmdRest(Command):
|
||||||
key = "rest"
|
key = "rest"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
def func(self):
|
rules = COMBAT_RULES
|
||||||
"This performs the actual command."
|
|
||||||
|
|
||||||
if is_in_combat(self.caller): # If you're in combat
|
|
||||||
self.caller.msg("You can't rest while you're in combat.")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.caller.db.hp = self.caller.db.max_hp # Set current HP to maximum
|
|
||||||
self.caller.location.msg_contents("%s rests to recover HP." % self.caller)
|
|
||||||
"""
|
|
||||||
You'll probably want to replace this with your own system for recovering HP.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CmdStatus(Command):
|
class CmdStatus(Command):
|
||||||
|
|
@ -1375,12 +991,14 @@ class CmdStatus(Command):
|
||||||
key = "status"
|
key = "status"
|
||||||
help_category = "combat"
|
help_category = "combat"
|
||||||
|
|
||||||
|
rules = COMBAT_RULES
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This performs the actual command."
|
"This performs the actual command."
|
||||||
combat_status_message(self.caller)
|
self.rules.combat_status_message(self.caller)
|
||||||
|
|
||||||
|
|
||||||
class CmdCombatHelp(CmdHelp):
|
class CmdCombatHelp(tb_basic.CmdCombatHelp):
|
||||||
"""
|
"""
|
||||||
View help or a list of topics
|
View help or a list of topics
|
||||||
|
|
||||||
|
|
@ -1395,21 +1013,17 @@ class CmdCombatHelp(CmdHelp):
|
||||||
|
|
||||||
# Just like the default help command, but will give quick
|
# Just like the default help command, but will give quick
|
||||||
# tips on combat when used in a fight with no arguments.
|
# tips on combat when used in a fight with no arguments.
|
||||||
|
rules = COMBAT_RULES
|
||||||
def func(self):
|
combat_help_text = (
|
||||||
if is_in_combat(self.caller) and not self.args: # In combat and entered 'help' alone
|
|
||||||
self.caller.msg(
|
|
||||||
"Available combat commands:|/"
|
"Available combat commands:|/"
|
||||||
+ "|wAttack:|n Attack an engaged target, attempting to deal damage.|/"
|
"|wAttack:|n Attack an engaged target, attempting to deal damage.|/"
|
||||||
+ "|wShoot:|n Attack from a distance, if not engaged with other fighters.|/"
|
"|wShoot:|n Attack from a distance, if not engaged with other fighters.|/"
|
||||||
+ "|wApproach:|n Move one step cloer to a target.|/"
|
"|wApproach:|n Move one step cloer to a target.|/"
|
||||||
+ "|wWithdraw:|n Move one step away from a target.|/"
|
"|wWithdraw:|n Move one step away from a target.|/"
|
||||||
+ "|wPass:|n Pass your turn without further action.|/"
|
"|wPass:|n Pass your turn without further action.|/"
|
||||||
+ "|wStatus:|n View current HP and ranges to other targets.|/"
|
"|wStatus:|n View current HP and ranges to other targets.|/"
|
||||||
+ "|wDisengage:|n End your turn and attempt to end combat.|/"
|
"|wDisengage:|n End your turn and attempt to end combat.|/"
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
super().func() # Call the default help command
|
|
||||||
|
|
||||||
|
|
||||||
class BattleCmdSet(default_cmds.CharacterCmdSet):
|
class BattleCmdSet(default_cmds.CharacterCmdSet):
|
||||||
|
|
|
||||||
|
|
@ -108,53 +108,54 @@ class TestTurnBattleBasicFunc(BaseEvenniaTest):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
self.turnhandler.stop()
|
self.turnhandler.stop()
|
||||||
|
self.testroom.delete()
|
||||||
self.attacker.delete()
|
self.attacker.delete()
|
||||||
self.defender.delete()
|
self.defender.delete()
|
||||||
self.joiner.delete()
|
self.joiner.delete()
|
||||||
self.testroom.delete()
|
|
||||||
|
|
||||||
# Test combat functions
|
# Test combat functions
|
||||||
def test_tbbasicfunc(self):
|
def test_tbbasicfunc(self):
|
||||||
# Initiative roll
|
# Initiative roll
|
||||||
initiative = tb_basic.roll_init(self.attacker)
|
initiative = tb_basic.COMBAT_RULES.roll_init(self.attacker)
|
||||||
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
||||||
# Attack roll
|
# Attack roll
|
||||||
attack_roll = tb_basic.get_attack(self.attacker, self.defender)
|
attack_roll = tb_basic.COMBAT_RULES.get_attack(self.attacker, self.defender)
|
||||||
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
||||||
# Defense roll
|
# Defense roll
|
||||||
defense_roll = tb_basic.get_defense(self.attacker, self.defender)
|
defense_roll = tb_basic.COMBAT_RULES.get_defense(self.attacker, self.defender)
|
||||||
self.assertTrue(defense_roll == 50)
|
self.assertTrue(defense_roll == 50)
|
||||||
# Damage roll
|
# Damage roll
|
||||||
damage_roll = tb_basic.get_damage(self.attacker, self.defender)
|
damage_roll = tb_basic.COMBAT_RULES.get_damage(self.attacker, self.defender)
|
||||||
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
||||||
# Apply damage
|
# Apply damage
|
||||||
self.defender.db.hp = 10
|
self.defender.db.hp = 10
|
||||||
tb_basic.apply_damage(self.defender, 3)
|
tb_basic.COMBAT_RULES.apply_damage(self.defender, 3)
|
||||||
self.assertTrue(self.defender.db.hp == 7)
|
self.assertTrue(self.defender.db.hp == 7)
|
||||||
# Resolve attack
|
# Resolve attack
|
||||||
self.defender.db.hp = 40
|
self.defender.db.hp = 40
|
||||||
tb_basic.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10)
|
tb_basic.COMBAT_RULES.resolve_attack(self.attacker, self.defender,
|
||||||
|
attack_value=20, defense_value=10)
|
||||||
self.assertTrue(self.defender.db.hp < 40)
|
self.assertTrue(self.defender.db.hp < 40)
|
||||||
# Combat cleanup
|
# Combat cleanup
|
||||||
self.attacker.db.Combat_attribute = True
|
self.attacker.db.Combat_attribute = True
|
||||||
tb_basic.combat_cleanup(self.attacker)
|
tb_basic.COMBAT_RULES.combat_cleanup(self.attacker)
|
||||||
self.assertFalse(self.attacker.db.combat_attribute)
|
self.assertFalse(self.attacker.db.combat_attribute)
|
||||||
# Is in combat
|
# Is in combat
|
||||||
self.assertFalse(tb_basic.is_in_combat(self.attacker))
|
self.assertFalse(tb_basic.COMBAT_RULES.is_in_combat(self.attacker))
|
||||||
# Set up turn handler script for further tests
|
# Set up turn handler script for further tests
|
||||||
self.attacker.location.scripts.add(tb_basic.TBBasicTurnHandler)
|
self.attacker.location.scripts.add(tb_basic.TBBasicTurnHandler)
|
||||||
self.turnhandler = self.attacker.db.combat_TurnHandler
|
self.turnhandler = self.attacker.db.combat_turnHandler
|
||||||
self.assertTrue(self.attacker.db.combat_TurnHandler)
|
self.assertTrue(self.attacker.db.combat_turnHandler)
|
||||||
# Set the turn handler's interval very high to keep it from repeating during tests.
|
# Set the turn handler's interval very high to keep it from repeating during tests.
|
||||||
self.turnhandler.interval = 10000
|
self.turnhandler.interval = 10000
|
||||||
# Force turn order
|
# Force turn order
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
# Test is turn
|
# Test is turn
|
||||||
self.assertTrue(tb_basic.is_turn(self.attacker))
|
self.assertTrue(tb_basic.COMBAT_RULES.is_turn(self.attacker))
|
||||||
# Spend actions
|
# Spend actions
|
||||||
self.attacker.db.Combat_ActionsLeft = 1
|
self.attacker.db.Combat_ActionsLeft = 1
|
||||||
tb_basic.spend_action(self.attacker, 1, action_name="Test")
|
tb_basic.COMBAT_RULES.spend_action(self.attacker, 1, action_name="Test")
|
||||||
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
||||||
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
||||||
# Initialize for combat
|
# Initialize for combat
|
||||||
|
|
@ -201,53 +202,53 @@ class TestTurnBattleEquipFunc(BaseEvenniaTest):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
self.turnhandler.stop()
|
self.turnhandler.stop()
|
||||||
|
self.testroom.delete()
|
||||||
self.attacker.delete()
|
self.attacker.delete()
|
||||||
self.defender.delete()
|
self.defender.delete()
|
||||||
self.joiner.delete()
|
self.joiner.delete()
|
||||||
self.testroom.delete()
|
|
||||||
|
|
||||||
# Test the combat functions in tb_equip too. They work mostly the same.
|
# Test the combat functions in tb_equip too. They work mostly the same.
|
||||||
def test_tbequipfunc(self):
|
def test_tbequipfunc(self):
|
||||||
# Initiative roll
|
# Initiative roll
|
||||||
initiative = tb_equip.roll_init(self.attacker)
|
initiative = tb_equip.COMBAT_RULES.roll_init(self.attacker)
|
||||||
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
||||||
# Attack roll
|
# Attack roll
|
||||||
attack_roll = tb_equip.get_attack(self.attacker, self.defender)
|
attack_roll = tb_equip.COMBAT_RULES.get_attack(self.attacker, self.defender)
|
||||||
self.assertTrue(attack_roll >= -50 and attack_roll <= 150)
|
self.assertTrue(attack_roll >= -50 and attack_roll <= 150)
|
||||||
# Defense roll
|
# Defense roll
|
||||||
defense_roll = tb_equip.get_defense(self.attacker, self.defender)
|
defense_roll = tb_equip.COMBAT_RULES.get_defense(self.attacker, self.defender)
|
||||||
self.assertTrue(defense_roll == 50)
|
self.assertTrue(defense_roll == 50)
|
||||||
# Damage roll
|
# Damage roll
|
||||||
damage_roll = tb_equip.get_damage(self.attacker, self.defender)
|
damage_roll = tb_equip.COMBAT_RULES.get_damage(self.attacker, self.defender)
|
||||||
self.assertTrue(damage_roll >= 0 and damage_roll <= 50)
|
self.assertTrue(damage_roll >= 0 and damage_roll <= 50)
|
||||||
# Apply damage
|
# Apply damage
|
||||||
self.defender.db.hp = 10
|
self.defender.db.hp = 10
|
||||||
tb_equip.apply_damage(self.defender, 3)
|
tb_equip.COMBAT_RULES.apply_damage(self.defender, 3)
|
||||||
self.assertTrue(self.defender.db.hp == 7)
|
self.assertTrue(self.defender.db.hp == 7)
|
||||||
# Resolve attack
|
# Resolve attack
|
||||||
self.defender.db.hp = 40
|
self.defender.db.hp = 40
|
||||||
tb_equip.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10)
|
tb_equip.COMBAT_RULES.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10)
|
||||||
self.assertTrue(self.defender.db.hp < 40)
|
self.assertTrue(self.defender.db.hp < 40)
|
||||||
# Combat cleanup
|
# Combat cleanup
|
||||||
self.attacker.db.Combat_attribute = True
|
self.attacker.db.Combat_attribute = True
|
||||||
tb_equip.combat_cleanup(self.attacker)
|
tb_equip.COMBAT_RULES.combat_cleanup(self.attacker)
|
||||||
self.assertFalse(self.attacker.db.combat_attribute)
|
self.assertFalse(self.attacker.db.combat_attribute)
|
||||||
# Is in combat
|
# Is in combat
|
||||||
self.assertFalse(tb_equip.is_in_combat(self.attacker))
|
self.assertFalse(tb_equip.COMBAT_RULES.is_in_combat(self.attacker))
|
||||||
# Set up turn handler script for further tests
|
# Set up turn handler script for further tests
|
||||||
self.attacker.location.scripts.add(tb_equip.TBEquipTurnHandler)
|
self.attacker.location.scripts.add(tb_equip.TBEquipTurnHandler)
|
||||||
self.turnhandler = self.attacker.db.combat_TurnHandler
|
self.turnhandler = self.attacker.db.combat_turnHandler
|
||||||
self.assertTrue(self.attacker.db.combat_TurnHandler)
|
self.assertTrue(self.attacker.db.combat_turnHandler)
|
||||||
# Set the turn handler's interval very high to keep it from repeating during tests.
|
# Set the turn handler's interval very high to keep it from repeating during tests.
|
||||||
self.turnhandler.interval = 10000
|
self.turnhandler.interval = 10000
|
||||||
# Force turn order
|
# Force turn order
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
# Test is turn
|
# Test is turn
|
||||||
self.assertTrue(tb_equip.is_turn(self.attacker))
|
self.assertTrue(tb_equip.COMBAT_RULES.is_turn(self.attacker))
|
||||||
# Spend actions
|
# Spend actions
|
||||||
self.attacker.db.Combat_ActionsLeft = 1
|
self.attacker.db.Combat_ActionsLeft = 1
|
||||||
tb_equip.spend_action(self.attacker, 1, action_name="Test")
|
tb_equip.COMBAT_RULES.spend_action(self.attacker, 1, action_name="Test")
|
||||||
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
||||||
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
||||||
# Initialize for combat
|
# Initialize for combat
|
||||||
|
|
@ -293,55 +294,57 @@ class TestTurnBattleRangeFunc(BaseEvenniaTest):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
self.turnhandler.stop()
|
self.turnhandler.stop()
|
||||||
|
self.testroom.delete()
|
||||||
self.attacker.delete()
|
self.attacker.delete()
|
||||||
self.defender.delete()
|
self.defender.delete()
|
||||||
self.joiner.delete()
|
self.joiner.delete()
|
||||||
self.testroom.delete()
|
|
||||||
|
|
||||||
# Test combat functions in tb_range too.
|
# Test combat functions in tb_range too.
|
||||||
def test_tbrangefunc(self):
|
def test_tbrangefunc(self):
|
||||||
# Initiative roll
|
# Initiative roll
|
||||||
initiative = tb_range.roll_init(self.attacker)
|
initiative = tb_range.COMBAT_RULES.roll_init(self.attacker)
|
||||||
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
||||||
# Attack roll
|
# Attack roll
|
||||||
attack_roll = tb_range.get_attack(self.attacker, self.defender, "test")
|
attack_roll = tb_range.COMBAT_RULES.get_attack(self.attacker, self.defender,
|
||||||
|
attack_type="test")
|
||||||
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
||||||
# Defense roll
|
# Defense roll
|
||||||
defense_roll = tb_range.get_defense(self.attacker, self.defender, "test")
|
defense_roll = tb_range.COMBAT_RULES.get_defense(self.attacker, self.defender,
|
||||||
|
attack_type="test")
|
||||||
self.assertTrue(defense_roll == 50)
|
self.assertTrue(defense_roll == 50)
|
||||||
# Damage roll
|
# Damage roll
|
||||||
damage_roll = tb_range.get_damage(self.attacker, self.defender)
|
damage_roll = tb_range.COMBAT_RULES.get_damage(self.attacker, self.defender)
|
||||||
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
||||||
# Apply damage
|
# Apply damage
|
||||||
self.defender.db.hp = 10
|
self.defender.db.hp = 10
|
||||||
tb_range.apply_damage(self.defender, 3)
|
tb_range.COMBAT_RULES.apply_damage(self.defender, 3)
|
||||||
self.assertTrue(self.defender.db.hp == 7)
|
self.assertTrue(self.defender.db.hp == 7)
|
||||||
# Resolve attack
|
# Resolve attack
|
||||||
self.defender.db.hp = 40
|
self.defender.db.hp = 40
|
||||||
tb_range.resolve_attack(
|
tb_range.COMBAT_RULES.resolve_attack(
|
||||||
self.attacker, self.defender, "test", attack_value=20, defense_value=10
|
self.attacker, self.defender, attack_type="test", attack_value=20, defense_value=10
|
||||||
)
|
)
|
||||||
self.assertTrue(self.defender.db.hp < 40)
|
self.assertTrue(self.defender.db.hp < 40)
|
||||||
# Combat cleanup
|
# Combat cleanup
|
||||||
self.attacker.db.Combat_attribute = True
|
self.attacker.db.Combat_attribute = True
|
||||||
tb_range.combat_cleanup(self.attacker)
|
tb_range.COMBAT_RULES.combat_cleanup(self.attacker)
|
||||||
self.assertFalse(self.attacker.db.combat_attribute)
|
self.assertFalse(self.attacker.db.combat_attribute)
|
||||||
# Is in combat
|
# Is in combat
|
||||||
self.assertFalse(tb_range.is_in_combat(self.attacker))
|
self.assertFalse(tb_range.COMBAT_RULES.is_in_combat(self.attacker))
|
||||||
# Set up turn handler script for further tests
|
# Set up turn handler script for further tests
|
||||||
self.attacker.location.scripts.add(tb_range.TBRangeTurnHandler)
|
self.attacker.location.scripts.add(tb_range.TBRangeTurnHandler)
|
||||||
self.turnhandler = self.attacker.db.combat_TurnHandler
|
self.turnhandler = self.attacker.db.combat_turnHandler
|
||||||
self.assertTrue(self.attacker.db.combat_TurnHandler)
|
self.assertTrue(self.attacker.db.combat_turnHandler)
|
||||||
# Set the turn handler's interval very high to keep it from repeating during tests.
|
# Set the turn handler's interval very high to keep it from repeating during tests.
|
||||||
self.turnhandler.interval = 10000
|
self.turnhandler.interval = 10000
|
||||||
# Force turn order
|
# Force turn order
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
# Test is turn
|
# Test is turn
|
||||||
self.assertTrue(tb_range.is_turn(self.attacker))
|
self.assertTrue(tb_range.COMBAT_RULES.is_turn(self.attacker))
|
||||||
# Spend actions
|
# Spend actions
|
||||||
self.attacker.db.Combat_ActionsLeft = 1
|
self.attacker.db.Combat_ActionsLeft = 1
|
||||||
tb_range.spend_action(self.attacker, 1, action_name="Test")
|
tb_range.COMBAT_RULES.spend_action(self.attacker, 1, action_name="Test")
|
||||||
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
||||||
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
||||||
# Initialize for combat
|
# Initialize for combat
|
||||||
|
|
@ -359,7 +362,7 @@ class TestTurnBattleRangeFunc(BaseEvenniaTest):
|
||||||
# Start turn
|
# Start turn
|
||||||
self.defender.db.Combat_ActionsLeft = 0
|
self.defender.db.Combat_ActionsLeft = 0
|
||||||
self.turnhandler.start_turn(self.defender)
|
self.turnhandler.start_turn(self.defender)
|
||||||
self.assertTrue(self.defender.db.Combat_ActionsLeft == 2)
|
self.assertEqual(self.defender.db.Combat_ActionsLeft, 2)
|
||||||
# Next turn
|
# Next turn
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
|
|
@ -378,13 +381,13 @@ class TestTurnBattleRangeFunc(BaseEvenniaTest):
|
||||||
self.assertTrue(self.turnhandler.db.turn == 1)
|
self.assertTrue(self.turnhandler.db.turn == 1)
|
||||||
self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender])
|
self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender])
|
||||||
# Now, test for approach/withdraw functions
|
# Now, test for approach/withdraw functions
|
||||||
self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 1)
|
self.assertTrue(tb_range.COMBAT_RULES.get_range(self.attacker, self.defender) == 1)
|
||||||
# Approach
|
# Approach
|
||||||
tb_range.approach(self.attacker, self.defender)
|
tb_range.COMBAT_RULES.approach(self.attacker, self.defender)
|
||||||
self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 0)
|
self.assertTrue(tb_range.COMBAT_RULES.get_range(self.attacker, self.defender) == 0)
|
||||||
# Withdraw
|
# Withdraw
|
||||||
tb_range.withdraw(self.attacker, self.defender)
|
tb_range.COMBAT_RULES.withdraw(self.attacker, self.defender)
|
||||||
self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 1)
|
self.assertTrue(tb_range.COMBAT_RULES.get_range(self.attacker, self.defender) == 1)
|
||||||
|
|
||||||
|
|
||||||
class TestTurnBattleItemsFunc(BaseEvenniaTest):
|
class TestTurnBattleItemsFunc(BaseEvenniaTest):
|
||||||
|
|
@ -407,54 +410,55 @@ class TestTurnBattleItemsFunc(BaseEvenniaTest):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
self.turnhandler.stop()
|
self.turnhandler.stop()
|
||||||
|
self.testroom.delete()
|
||||||
self.attacker.delete()
|
self.attacker.delete()
|
||||||
self.defender.delete()
|
self.defender.delete()
|
||||||
self.joiner.delete()
|
self.joiner.delete()
|
||||||
self.user.delete()
|
self.user.delete()
|
||||||
self.testroom.delete()
|
|
||||||
|
|
||||||
# Test functions in tb_items.
|
# Test functions in tb_items.
|
||||||
def test_tbitemsfunc(self):
|
def test_tbitemsfunc(self):
|
||||||
# Initiative roll
|
# Initiative roll
|
||||||
initiative = tb_items.roll_init(self.attacker)
|
initiative = tb_items.COMBAT_RULES.roll_init(self.attacker)
|
||||||
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
||||||
# Attack roll
|
# Attack roll
|
||||||
attack_roll = tb_items.get_attack(self.attacker, self.defender)
|
attack_roll = tb_items.COMBAT_RULES.get_attack(self.attacker, self.defender)
|
||||||
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
||||||
# Defense roll
|
# Defense roll
|
||||||
defense_roll = tb_items.get_defense(self.attacker, self.defender)
|
defense_roll = tb_items.COMBAT_RULES.get_defense(self.attacker, self.defender)
|
||||||
self.assertTrue(defense_roll == 50)
|
self.assertTrue(defense_roll == 50)
|
||||||
# Damage roll
|
# Damage roll
|
||||||
damage_roll = tb_items.get_damage(self.attacker, self.defender)
|
damage_roll = tb_items.COMBAT_RULES.get_damage(self.attacker, self.defender)
|
||||||
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
||||||
# Apply damage
|
# Apply damage
|
||||||
self.defender.db.hp = 10
|
self.defender.db.hp = 10
|
||||||
tb_items.apply_damage(self.defender, 3)
|
tb_items.COMBAT_RULES.apply_damage(self.defender, 3)
|
||||||
self.assertTrue(self.defender.db.hp == 7)
|
self.assertTrue(self.defender.db.hp == 7)
|
||||||
# Resolve attack
|
# Resolve attack
|
||||||
self.defender.db.hp = 40
|
self.defender.db.hp = 40
|
||||||
tb_items.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10)
|
tb_items.COMBAT_RULES.resolve_attack(self.attacker, self.defender, attack_value=20,
|
||||||
|
defense_value=10)
|
||||||
self.assertTrue(self.defender.db.hp < 40)
|
self.assertTrue(self.defender.db.hp < 40)
|
||||||
# Combat cleanup
|
# Combat cleanup
|
||||||
self.attacker.db.Combat_attribute = True
|
self.attacker.db.Combat_attribute = True
|
||||||
tb_items.combat_cleanup(self.attacker)
|
tb_items.COMBAT_RULES.combat_cleanup(self.attacker)
|
||||||
self.assertFalse(self.attacker.db.combat_attribute)
|
self.assertFalse(self.attacker.db.combat_attribute)
|
||||||
# Is in combat
|
# Is in combat
|
||||||
self.assertFalse(tb_items.is_in_combat(self.attacker))
|
self.assertFalse(tb_items.COMBAT_RULES.is_in_combat(self.attacker))
|
||||||
# Set up turn handler script for further tests
|
# Set up turn handler script for further tests
|
||||||
self.attacker.location.scripts.add(tb_items.TBItemsTurnHandler)
|
self.attacker.location.scripts.add(tb_items.TBItemsTurnHandler)
|
||||||
self.turnhandler = self.attacker.db.combat_TurnHandler
|
self.turnhandler = self.attacker.db.combat_turnHandler
|
||||||
self.assertTrue(self.attacker.db.combat_TurnHandler)
|
self.assertTrue(self.attacker.db.combat_turnHandler)
|
||||||
# Set the turn handler's interval very high to keep it from repeating during tests.
|
# Set the turn handler's interval very high to keep it from repeating during tests.
|
||||||
self.turnhandler.interval = 10000
|
self.turnhandler.interval = 10000
|
||||||
# Force turn order
|
# Force turn order
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
# Test is turn
|
# Test is turn
|
||||||
self.assertTrue(tb_items.is_turn(self.attacker))
|
self.assertTrue(tb_items.COMBAT_RULES.is_turn(self.attacker))
|
||||||
# Spend actions
|
# Spend actions
|
||||||
self.attacker.db.Combat_ActionsLeft = 1
|
self.attacker.db.Combat_ActionsLeft = 1
|
||||||
tb_items.spend_action(self.attacker, 1, action_name="Test")
|
tb_items.COMBAT_RULES.spend_action(self.attacker, 1, action_name="Test")
|
||||||
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
||||||
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
||||||
# Initialize for combat
|
# Initialize for combat
|
||||||
|
|
@ -485,29 +489,29 @@ class TestTurnBattleItemsFunc(BaseEvenniaTest):
|
||||||
self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender])
|
self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender])
|
||||||
# Now time to test item stuff.
|
# Now time to test item stuff.
|
||||||
# Spend item use
|
# Spend item use
|
||||||
tb_items.spend_item_use(self.test_healpotion, self.user)
|
tb_items.COMBAT_RULES.spend_item_use(self.test_healpotion, self.user)
|
||||||
self.assertTrue(self.test_healpotion.db.item_uses == 2)
|
self.assertTrue(self.test_healpotion.db.item_uses == 2)
|
||||||
# Use item
|
# Use item
|
||||||
self.user.db.hp = 2
|
self.user.db.hp = 2
|
||||||
tb_items.use_item(self.user, self.test_healpotion, self.user)
|
tb_items.COMBAT_RULES.use_item(self.user, self.test_healpotion, self.user)
|
||||||
self.assertTrue(self.user.db.hp > 2)
|
self.assertTrue(self.user.db.hp > 2)
|
||||||
# Add contition
|
# Add contition
|
||||||
tb_items.add_condition(self.user, self.user, "Test", 5)
|
tb_items.COMBAT_RULES.add_condition(self.user, self.user, "Test", 5)
|
||||||
self.assertTrue(self.user.db.conditions == {"Test": [5, self.user]})
|
self.assertTrue(self.user.db.conditions == {"Test": [5, self.user]})
|
||||||
# Condition tickdown
|
# Condition tickdown
|
||||||
tb_items.condition_tickdown(self.user, self.user)
|
tb_items.COMBAT_RULES.condition_tickdown(self.user, self.user)
|
||||||
self.assertTrue(self.user.db.conditions == {"Test": [4, self.user]})
|
self.assertEqual(self.user.db.conditions, {"Test": [4, self.user]})
|
||||||
# Test item functions now!
|
# Test item functions now!
|
||||||
# Item heal
|
# Item heal
|
||||||
self.user.db.hp = 2
|
self.user.db.hp = 2
|
||||||
tb_items.itemfunc_heal(self.test_healpotion, self.user, self.user)
|
tb_items.COMBAT_RULES.itemfunc_heal(self.test_healpotion, self.user, self.user)
|
||||||
# Item add condition
|
# Item add condition
|
||||||
self.user.db.conditions = {}
|
self.user.db.conditions = {}
|
||||||
tb_items.itemfunc_add_condition(self.test_healpotion, self.user, self.user)
|
tb_items.COMBAT_RULES.itemfunc_add_condition(self.test_healpotion, self.user, self.user)
|
||||||
self.assertTrue(self.user.db.conditions == {"Regeneration": [5, self.user]})
|
self.assertTrue(self.user.db.conditions == {"Regeneration": [5, self.user]})
|
||||||
# Item cure condition
|
# Item cure condition
|
||||||
self.user.db.conditions = {"Poisoned": [5, self.user]}
|
self.user.db.conditions = {"Poisoned": [5, self.user]}
|
||||||
tb_items.itemfunc_cure_condition(self.test_healpotion, self.user, self.user)
|
tb_items.COMBAT_RULES.itemfunc_cure_condition(self.test_healpotion, self.user, self.user)
|
||||||
self.assertTrue(self.user.db.conditions == {})
|
self.assertTrue(self.user.db.conditions == {})
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -526,53 +530,54 @@ class TestTurnBattleMagicFunc(BaseEvenniaTest):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
self.turnhandler.stop()
|
self.turnhandler.stop()
|
||||||
|
self.testroom.delete()
|
||||||
self.attacker.delete()
|
self.attacker.delete()
|
||||||
self.defender.delete()
|
self.defender.delete()
|
||||||
self.joiner.delete()
|
self.joiner.delete()
|
||||||
self.testroom.delete()
|
|
||||||
|
|
||||||
# Test combat functions in tb_magic.
|
# Test combat functions in tb_magic.
|
||||||
def test_tbbasicfunc(self):
|
def test_tbbasicfunc(self):
|
||||||
# Initiative roll
|
# Initiative roll
|
||||||
initiative = tb_magic.roll_init(self.attacker)
|
initiative = tb_magic.COMBAT_RULES.roll_init(self.attacker)
|
||||||
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
self.assertTrue(initiative >= 0 and initiative <= 1000)
|
||||||
# Attack roll
|
# Attack roll
|
||||||
attack_roll = tb_magic.get_attack(self.attacker, self.defender)
|
attack_roll = tb_magic.COMBAT_RULES.get_attack(self.attacker, self.defender)
|
||||||
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
|
||||||
# Defense roll
|
# Defense roll
|
||||||
defense_roll = tb_magic.get_defense(self.attacker, self.defender)
|
defense_roll = tb_magic.COMBAT_RULES.get_defense(self.attacker, self.defender)
|
||||||
self.assertTrue(defense_roll == 50)
|
self.assertTrue(defense_roll == 50)
|
||||||
# Damage roll
|
# Damage roll
|
||||||
damage_roll = tb_magic.get_damage(self.attacker, self.defender)
|
damage_roll = tb_magic.COMBAT_RULES.get_damage(self.attacker, self.defender)
|
||||||
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
|
||||||
# Apply damage
|
# Apply damage
|
||||||
self.defender.db.hp = 10
|
self.defender.db.hp = 10
|
||||||
tb_magic.apply_damage(self.defender, 3)
|
tb_magic.COMBAT_RULES.apply_damage(self.defender, 3)
|
||||||
self.assertTrue(self.defender.db.hp == 7)
|
self.assertTrue(self.defender.db.hp == 7)
|
||||||
# Resolve attack
|
# Resolve attack
|
||||||
self.defender.db.hp = 40
|
self.defender.db.hp = 40
|
||||||
tb_magic.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10)
|
tb_magic.COMBAT_RULES.resolve_attack(self.attacker, self.defender, attack_value=20,
|
||||||
|
defense_value=10)
|
||||||
self.assertTrue(self.defender.db.hp < 40)
|
self.assertTrue(self.defender.db.hp < 40)
|
||||||
# Combat cleanup
|
# Combat cleanup
|
||||||
self.attacker.db.Combat_attribute = True
|
self.attacker.db.Combat_attribute = True
|
||||||
tb_magic.combat_cleanup(self.attacker)
|
tb_magic.COMBAT_RULES.combat_cleanup(self.attacker)
|
||||||
self.assertFalse(self.attacker.db.combat_attribute)
|
self.assertFalse(self.attacker.db.combat_attribute)
|
||||||
# Is in combat
|
# Is in combat
|
||||||
self.assertFalse(tb_magic.is_in_combat(self.attacker))
|
self.assertFalse(tb_magic.COMBAT_RULES.is_in_combat(self.attacker))
|
||||||
# Set up turn handler script for further tests
|
# Set up turn handler script for further tests
|
||||||
self.attacker.location.scripts.add(tb_magic.TBMagicTurnHandler)
|
self.attacker.location.scripts.add(tb_magic.TBMagicTurnHandler)
|
||||||
self.turnhandler = self.attacker.db.combat_TurnHandler
|
self.turnhandler = self.attacker.db.combat_turnHandler
|
||||||
self.assertTrue(self.attacker.db.combat_TurnHandler)
|
self.assertTrue(self.attacker.db.combat_turnHandler)
|
||||||
# Set the turn handler's interval very high to keep it from repeating during tests.
|
# Set the turn handler's interval very high to keep it from repeating during tests.
|
||||||
self.turnhandler.interval = 10000
|
self.turnhandler.interval = 10000
|
||||||
# Force turn order
|
# Force turn order
|
||||||
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
self.turnhandler.db.fighters = [self.attacker, self.defender]
|
||||||
self.turnhandler.db.turn = 0
|
self.turnhandler.db.turn = 0
|
||||||
# Test is turn
|
# Test is turn
|
||||||
self.assertTrue(tb_magic.is_turn(self.attacker))
|
self.assertTrue(tb_magic.COMBAT_RULES.is_turn(self.attacker))
|
||||||
# Spend actions
|
# Spend actions
|
||||||
self.attacker.db.Combat_ActionsLeft = 1
|
self.attacker.db.Combat_ActionsLeft = 1
|
||||||
tb_magic.spend_action(self.attacker, 1, action_name="Test")
|
tb_magic.COMBAT_RULES.spend_action(self.attacker, 1, action_name="Test")
|
||||||
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0)
|
||||||
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
self.assertTrue(self.attacker.db.Combat_LastAction == "Test")
|
||||||
# Initialize for combat
|
# Initialize for combat
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue