Format code with black. Add makefile to run fmt/tests
This commit is contained in:
parent
d00bce9288
commit
c2c7fa311a
299 changed files with 19037 additions and 11611 deletions
|
|
@ -52,8 +52,8 @@ OPTIONS
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -61,6 +61,7 @@ COMBAT FUNCTIONS START HERE
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def roll_init(character):
|
||||
"""
|
||||
Rolls a number between 1-1000 to determine initiative.
|
||||
|
|
@ -175,6 +176,7 @@ def apply_damage(defender, damage):
|
|||
if defender.db.hp <= 0:
|
||||
defender.db.hp = 0
|
||||
|
||||
|
||||
def at_defeat(defeated):
|
||||
"""
|
||||
Announces the defeat of a fighter in combat.
|
||||
|
|
@ -190,6 +192,7 @@ def at_defeat(defeated):
|
|||
"""
|
||||
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.
|
||||
|
|
@ -215,12 +218,15 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
|||
else:
|
||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
||||
# Announce damage dealt and apply damage.
|
||||
attacker.location.msg_contents("%s hits %s for %i damage!" % (attacker, defender, damage_value))
|
||||
attacker.location.msg_contents(
|
||||
"%s hits %s for %i damage!" % (attacker, defender, 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 combat_cleanup(character):
|
||||
"""
|
||||
Cleans up all the temporary combat-related attributes on a character.
|
||||
|
|
@ -279,7 +285,7 @@ def spend_action(character, actions, action_name=None):
|
|||
"""
|
||||
if action_name:
|
||||
character.db.combat_lastaction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
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.
|
||||
|
|
@ -341,6 +347,7 @@ class TBBasicCharacter(DefaultCharacter):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
SCRIPTS START HERE
|
||||
|
|
@ -388,7 +395,7 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
|
||||
# Announce the turn order.
|
||||
self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters))
|
||||
|
||||
|
||||
# Start first fighter's turn.
|
||||
self.start_turn(self.db.fighters[0])
|
||||
|
||||
|
|
@ -408,13 +415,17 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
"""
|
||||
Called once every self.interval seconds.
|
||||
"""
|
||||
currentchar = self.db.fighters[self.db.turn] # Note the current character in the turn order.
|
||||
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.
|
||||
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.
|
||||
|
|
@ -429,8 +440,12 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
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_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):
|
||||
|
|
@ -460,7 +475,9 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
# 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
|
||||
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!")
|
||||
|
|
@ -472,7 +489,9 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
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
|
||||
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
|
||||
|
|
@ -516,7 +535,7 @@ class TBBasicTurnHandler(DefaultScript):
|
|||
# Initialize the character like you do at the start.
|
||||
self.initialize_for_combat(character)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
COMMANDS START HERE
|
||||
|
|
@ -535,6 +554,7 @@ class CmdFight(Command):
|
|||
fight is added to combat, and a turn order is randomly rolled.
|
||||
When it's your turn, you can attack other characters.
|
||||
"""
|
||||
|
||||
key = "fight"
|
||||
help_category = "combat"
|
||||
|
||||
|
|
@ -643,8 +663,10 @@ class CmdPass(Command):
|
|||
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.
|
||||
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):
|
||||
|
|
@ -676,7 +698,7 @@ class CmdDisengage(Command):
|
|||
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.
|
||||
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.
|
||||
|
|
@ -723,15 +745,18 @@ class CmdCombatHelp(CmdHelp):
|
|||
This will search for help on commands and other
|
||||
topics related to the game.
|
||||
"""
|
||||
|
||||
# Just like the default help command, but will give quick
|
||||
# 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.|/")
|
||||
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
|
||||
|
||||
|
|
@ -740,6 +765,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
"""
|
||||
This command set includes all the commmands used in the battle system.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
|
|
@ -751,4 +777,4 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
self.add(CmdRest())
|
||||
self.add(CmdPass())
|
||||
self.add(CmdDisengage())
|
||||
self.add(CmdCombatHelp())
|
||||
self.add(CmdCombatHelp())
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ OPTIONS
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -73,6 +73,7 @@ COMBAT FUNCTIONS START HERE
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def roll_init(character):
|
||||
"""
|
||||
Rolls a number between 1-1000 to determine initiative.
|
||||
|
|
@ -186,7 +187,9 @@ def get_damage(attacker, defender):
|
|||
damage_value = randint(weapon.db.damage_range[0], weapon.db.damage_range[1])
|
||||
# Use attacker's unarmed damage otherwise
|
||||
else:
|
||||
damage_value = randint(attacker.db.unarmed_damage_range[0], attacker.db.unarmed_damage_range[1])
|
||||
damage_value = randint(
|
||||
attacker.db.unarmed_damage_range[0], attacker.db.unarmed_damage_range[1]
|
||||
)
|
||||
# If defender is armored, reduce incoming damage
|
||||
if defender.db.worn_armor:
|
||||
armor = defender.db.worn_armor
|
||||
|
|
@ -211,6 +214,7 @@ def apply_damage(defender, damage):
|
|||
if defender.db.hp <= 0:
|
||||
defender.db.hp = 0
|
||||
|
||||
|
||||
def at_defeat(defeated):
|
||||
"""
|
||||
Announces the defeat of a fighter in combat.
|
||||
|
|
@ -226,6 +230,7 @@ def at_defeat(defeated):
|
|||
"""
|
||||
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.
|
||||
|
|
@ -252,14 +257,21 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
|||
defense_value = get_defense(attacker, defender)
|
||||
# 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 misses %s!" % (attacker, attackers_weapon, defender))
|
||||
attacker.location.msg_contents(
|
||||
"%s's %s misses %s!" % (attacker, attackers_weapon, defender)
|
||||
)
|
||||
else:
|
||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
||||
# Announce damage dealt and apply damage.
|
||||
if damage_value > 0:
|
||||
attacker.location.msg_contents("%s's %s strikes %s for %i damage!" % (attacker, attackers_weapon, defender, damage_value))
|
||||
attacker.location.msg_contents(
|
||||
"%s's %s strikes %s for %i damage!"
|
||||
% (attacker, attackers_weapon, defender, damage_value)
|
||||
)
|
||||
else:
|
||||
attacker.location.msg_contents("%s's %s bounces harmlessly off %s!" % (attacker, attackers_weapon, defender))
|
||||
attacker.location.msg_contents(
|
||||
"%s's %s bounces harmlessly off %s!" % (attacker, attackers_weapon, defender)
|
||||
)
|
||||
apply_damage(defender, damage_value)
|
||||
# If defender HP is reduced to 0 or less, call at_defeat.
|
||||
if defender.db.hp <= 0:
|
||||
|
|
@ -324,7 +336,7 @@ def spend_action(character, actions, action_name=None):
|
|||
"""
|
||||
if action_name:
|
||||
character.db.combat_lastaction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
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.
|
||||
|
|
@ -332,6 +344,7 @@ def spend_action(character, actions, action_name=None):
|
|||
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.
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
SCRIPTS START HERE
|
||||
|
|
@ -379,7 +392,7 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
|
||||
# Announce the turn order.
|
||||
self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters))
|
||||
|
||||
|
||||
# Start first fighter's turn.
|
||||
self.start_turn(self.db.fighters[0])
|
||||
|
||||
|
|
@ -399,13 +412,17 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
"""
|
||||
Called once every self.interval seconds.
|
||||
"""
|
||||
currentchar = self.db.fighters[self.db.turn] # Note the current character in the turn order.
|
||||
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.
|
||||
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.
|
||||
|
|
@ -420,8 +437,12 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
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_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):
|
||||
|
|
@ -451,7 +472,9 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
# 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
|
||||
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!")
|
||||
|
|
@ -463,7 +486,9 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
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
|
||||
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
|
||||
|
|
@ -507,24 +532,30 @@ class TBEquipTurnHandler(DefaultScript):
|
|||
# Initialize the character like you do at the start.
|
||||
self.initialize_for_combat(character)
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
TYPECLASSES START HERE
|
||||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
class TBEWeapon(DefaultObject):
|
||||
"""
|
||||
A weapon which can be wielded in combat with the 'wield' command.
|
||||
"""
|
||||
|
||||
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.damage_range = (15, 25) # Minimum and maximum damage on hit
|
||||
self.db.accuracy_bonus = 0 # Bonus to attack rolls (or penalty if negative)
|
||||
self.db.weapon_type_name = "weapon" # Single word for weapon - I.E. "dagger", "staff", "scimitar"
|
||||
self.db.damage_range = (15, 25) # Minimum and maximum damage on hit
|
||||
self.db.accuracy_bonus = 0 # Bonus to attack rolls (or penalty if negative)
|
||||
self.db.weapon_type_name = (
|
||||
"weapon"
|
||||
) # Single word for weapon - I.E. "dagger", "staff", "scimitar"
|
||||
|
||||
def at_drop(self, dropper):
|
||||
"""
|
||||
Stop being wielded if dropped.
|
||||
|
|
@ -532,6 +563,7 @@ class TBEWeapon(DefaultObject):
|
|||
if dropper.db.wielded_weapon == self:
|
||||
dropper.db.wielded_weapon = None
|
||||
dropper.location.msg_contents("%s stops wielding %s." % (dropper, self))
|
||||
|
||||
def at_give(self, giver, getter):
|
||||
"""
|
||||
Stop being wielded if given.
|
||||
|
|
@ -539,18 +571,23 @@ class TBEWeapon(DefaultObject):
|
|||
if giver.db.wielded_weapon == self:
|
||||
giver.db.wielded_weapon = None
|
||||
giver.location.msg_contents("%s stops wielding %s." % (giver, self))
|
||||
|
||||
|
||||
|
||||
class TBEArmor(DefaultObject):
|
||||
"""
|
||||
A set of armor which can be worn with the 'don' command.
|
||||
"""
|
||||
|
||||
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.damage_reduction = 4 # Amount of incoming damage reduced by armor
|
||||
self.db.defense_modifier = -4 # Amount to modify defense value (pos = harder to hit, neg = easier)
|
||||
self.db.damage_reduction = 4 # Amount of incoming damage reduced by armor
|
||||
self.db.defense_modifier = (
|
||||
-4
|
||||
) # Amount to modify defense value (pos = harder to hit, neg = easier)
|
||||
|
||||
def at_before_drop(self, dropper):
|
||||
"""
|
||||
Can't drop in combat.
|
||||
|
|
@ -559,6 +596,7 @@ class TBEArmor(DefaultObject):
|
|||
dropper.msg("You can't doff armor in a fight!")
|
||||
return False
|
||||
return True
|
||||
|
||||
def at_drop(self, dropper):
|
||||
"""
|
||||
Stop being wielded if dropped.
|
||||
|
|
@ -566,6 +604,7 @@ class TBEArmor(DefaultObject):
|
|||
if dropper.db.worn_armor == self:
|
||||
dropper.db.worn_armor = None
|
||||
dropper.location.msg_contents("%s removes %s." % (dropper, self))
|
||||
|
||||
def at_before_give(self, giver, getter):
|
||||
"""
|
||||
Can't give away in combat.
|
||||
|
|
@ -574,6 +613,7 @@ class TBEArmor(DefaultObject):
|
|||
dropper.msg("You can't doff armor in a fight!")
|
||||
return False
|
||||
return True
|
||||
|
||||
def at_give(self, giver, getter):
|
||||
"""
|
||||
Stop being wielded if given.
|
||||
|
|
@ -582,6 +622,7 @@ class TBEArmor(DefaultObject):
|
|||
giver.db.worn_armor = None
|
||||
giver.location.msg_contents("%s removes %s." % (giver, self))
|
||||
|
||||
|
||||
class TBEquipCharacter(DefaultCharacter):
|
||||
"""
|
||||
A character able to participate in turn-based combat. Has attributes for current
|
||||
|
|
@ -595,11 +636,11 @@ class TBEquipCharacter(DefaultCharacter):
|
|||
"""
|
||||
self.db.max_hp = 100 # Set maximum HP to 100
|
||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
||||
self.db.wielded_weapon = None # Currently used weapon
|
||||
self.db.worn_armor = None # Currently worn armor
|
||||
self.db.unarmed_damage_range = (5, 15) # Minimum and maximum unarmed damage
|
||||
self.db.unarmed_accuracy = 30 # Accuracy bonus for unarmed attacks
|
||||
|
||||
self.db.wielded_weapon = None # Currently used weapon
|
||||
self.db.worn_armor = None # Currently worn armor
|
||||
self.db.unarmed_damage_range = (5, 15) # Minimum and maximum unarmed damage
|
||||
self.db.unarmed_accuracy = 30 # Accuracy bonus for unarmed attacks
|
||||
|
||||
"""
|
||||
Adds attributes for a character's current and maximum HP.
|
||||
We're just going to set this value at '100' by default.
|
||||
|
|
@ -652,6 +693,7 @@ class CmdFight(Command):
|
|||
fight is added to combat, and a turn order is randomly rolled.
|
||||
When it's your turn, you can attack other characters.
|
||||
"""
|
||||
|
||||
key = "fight"
|
||||
help_category = "combat"
|
||||
|
||||
|
|
@ -760,8 +802,10 @@ class CmdPass(Command):
|
|||
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.
|
||||
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):
|
||||
|
|
@ -793,7 +837,7 @@ class CmdDisengage(Command):
|
|||
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.
|
||||
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.
|
||||
|
|
@ -840,18 +884,22 @@ class CmdCombatHelp(CmdHelp):
|
|||
This will search for help on commands and other
|
||||
topics related to the game.
|
||||
"""
|
||||
|
||||
# Just like the default help command, but will give quick
|
||||
# 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.|/")
|
||||
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):
|
||||
"""
|
||||
Wield a weapon you are carrying
|
||||
|
|
@ -866,10 +914,10 @@ class CmdWield(Command):
|
|||
"unwield" command to stop wielding any weapon you are
|
||||
currently wielding.
|
||||
"""
|
||||
|
||||
|
||||
key = "wield"
|
||||
help_category = "combat"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -889,18 +937,21 @@ class CmdWield(Command):
|
|||
self.caller.msg("That's not a weapon!")
|
||||
# Remember to update the path to the weapon typeclass if you move this module!
|
||||
return
|
||||
|
||||
|
||||
if not self.caller.db.wielded_weapon:
|
||||
self.caller.db.wielded_weapon = weapon
|
||||
self.caller.location.msg_contents("%s wields %s." % (self.caller, weapon))
|
||||
else:
|
||||
old_weapon = self.caller.db.wielded_weapon
|
||||
self.caller.db.wielded_weapon = weapon
|
||||
self.caller.location.msg_contents("%s lowers %s and wields %s." % (self.caller, old_weapon, weapon))
|
||||
self.caller.location.msg_contents(
|
||||
"%s lowers %s and wields %s." % (self.caller, old_weapon, weapon)
|
||||
)
|
||||
# Spend an action if in combat.
|
||||
if is_in_combat(self.caller):
|
||||
spend_action(self.caller, 1, action_name="wield") # Use up one action.
|
||||
|
||||
|
||||
|
||||
class CmdUnwield(Command):
|
||||
"""
|
||||
Stop wielding a weapon.
|
||||
|
|
@ -911,10 +962,10 @@ class CmdUnwield(Command):
|
|||
After using this command, you will stop wielding any
|
||||
weapon you are currently wielding and become unarmed.
|
||||
"""
|
||||
|
||||
|
||||
key = "unwield"
|
||||
help_category = "combat"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -930,7 +981,8 @@ class CmdUnwield(Command):
|
|||
old_weapon = self.caller.db.wielded_weapon
|
||||
self.caller.db.wielded_weapon = None
|
||||
self.caller.location.msg_contents("%s lowers %s." % (self.caller, old_weapon))
|
||||
|
||||
|
||||
|
||||
class CmdDon(Command):
|
||||
"""
|
||||
Don armor that you are carrying
|
||||
|
|
@ -942,10 +994,10 @@ class CmdDon(Command):
|
|||
command in the middle of a fight. Use the "doff"
|
||||
command to remove any armor you are wearing.
|
||||
"""
|
||||
|
||||
|
||||
key = "don"
|
||||
help_category = "combat"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -964,15 +1016,18 @@ class CmdDon(Command):
|
|||
self.caller.msg("That's not armor!")
|
||||
# Remember to update the path to the armor typeclass if you move this module!
|
||||
return
|
||||
|
||||
|
||||
if not self.caller.db.worn_armor:
|
||||
self.caller.db.worn_armor = armor
|
||||
self.caller.location.msg_contents("%s dons %s." % (self.caller, armor))
|
||||
else:
|
||||
old_armor = self.caller.db.worn_armor
|
||||
self.caller.db.worn_armor = armor
|
||||
self.caller.location.msg_contents("%s removes %s and dons %s." % (self.caller, old_armor, armor))
|
||||
|
||||
self.caller.location.msg_contents(
|
||||
"%s removes %s and dons %s." % (self.caller, old_armor, armor)
|
||||
)
|
||||
|
||||
|
||||
class CmdDoff(Command):
|
||||
"""
|
||||
Stop wearing armor.
|
||||
|
|
@ -984,10 +1039,10 @@ class CmdDoff(Command):
|
|||
armor you are currently using and become unarmored.
|
||||
You can't use this command in combat.
|
||||
"""
|
||||
|
||||
|
||||
key = "doff"
|
||||
help_category = "combat"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -1002,13 +1057,13 @@ class CmdDoff(Command):
|
|||
old_armor = self.caller.db.worn_armor
|
||||
self.caller.db.worn_armor = None
|
||||
self.caller.location.msg_contents("%s removes %s." % (self.caller, old_armor))
|
||||
|
||||
|
||||
|
||||
class BattleCmdSet(default_cmds.CharacterCmdSet):
|
||||
"""
|
||||
This command set includes all the commmands used in the battle system.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
|
|
@ -1026,61 +1081,58 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
self.add(CmdDon())
|
||||
self.add(CmdDoff())
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
PROTOTYPES START HERE
|
||||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
BASEWEAPON = {
|
||||
"typeclass": "evennia.contrib.turnbattle.tb_equip.TBEWeapon",
|
||||
}
|
||||
|
||||
BASEARMOR = {
|
||||
"typeclass": "evennia.contrib.turnbattle.tb_equip.TBEArmor",
|
||||
}
|
||||
BASEWEAPON = {"typeclass": "evennia.contrib.turnbattle.tb_equip.TBEWeapon"}
|
||||
|
||||
BASEARMOR = {"typeclass": "evennia.contrib.turnbattle.tb_equip.TBEArmor"}
|
||||
|
||||
DAGGER = {
|
||||
"prototype" : "BASEWEAPON",
|
||||
"damage_range" : (10, 20),
|
||||
"accuracy_bonus" : 30,
|
||||
"key": "a thin steel dagger",
|
||||
"weapon_type_name" : "dagger"
|
||||
"prototype": "BASEWEAPON",
|
||||
"damage_range": (10, 20),
|
||||
"accuracy_bonus": 30,
|
||||
"key": "a thin steel dagger",
|
||||
"weapon_type_name": "dagger",
|
||||
}
|
||||
|
||||
BROADSWORD = {
|
||||
"prototype" : "BASEWEAPON",
|
||||
"damage_range" : (15, 30),
|
||||
"accuracy_bonus" : 15,
|
||||
"key": "an iron broadsword",
|
||||
"weapon_type_name" : "broadsword"
|
||||
"prototype": "BASEWEAPON",
|
||||
"damage_range": (15, 30),
|
||||
"accuracy_bonus": 15,
|
||||
"key": "an iron broadsword",
|
||||
"weapon_type_name": "broadsword",
|
||||
}
|
||||
|
||||
GREATSWORD = {
|
||||
"prototype" : "BASEWEAPON",
|
||||
"damage_range" : (20, 40),
|
||||
"accuracy_bonus" : 0,
|
||||
"key": "a rune-etched greatsword",
|
||||
"weapon_type_name" : "greatsword"
|
||||
"prototype": "BASEWEAPON",
|
||||
"damage_range": (20, 40),
|
||||
"accuracy_bonus": 0,
|
||||
"key": "a rune-etched greatsword",
|
||||
"weapon_type_name": "greatsword",
|
||||
}
|
||||
|
||||
LEATHERARMOR = {
|
||||
"prototype" : "BASEARMOR",
|
||||
"damage_reduction" : 2,
|
||||
"defense_modifier" : -2,
|
||||
"key": "a suit of leather armor"
|
||||
"prototype": "BASEARMOR",
|
||||
"damage_reduction": 2,
|
||||
"defense_modifier": -2,
|
||||
"key": "a suit of leather armor",
|
||||
}
|
||||
|
||||
SCALEMAIL = {
|
||||
"prototype" : "BASEARMOR",
|
||||
"damage_reduction" : 4,
|
||||
"defense_modifier" : -4,
|
||||
"key": "a suit of scale mail"
|
||||
"prototype": "BASEARMOR",
|
||||
"damage_reduction": 4,
|
||||
"defense_modifier": -4,
|
||||
"key": "a suit of scale mail",
|
||||
}
|
||||
|
||||
PLATEMAIL = {
|
||||
"prototype" : "BASEARMOR",
|
||||
"damage_reduction" : 6,
|
||||
"defense_modifier" : -6,
|
||||
"key": "a suit of plate mail"
|
||||
"prototype": "BASEARMOR",
|
||||
"damage_reduction": 6,
|
||||
"defense_modifier": -6,
|
||||
"key": "a suit of plate mail",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,22 +79,22 @@ OPTIONS
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
NONCOMBAT_TURN_TIME = 30 # Time per turn count out of combat
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
NONCOMBAT_TURN_TIME = 30 # Time per turn count out of combat
|
||||
|
||||
# Condition options start here.
|
||||
# If you need to make changes to how your conditions work later,
|
||||
# it's best to put the easily tweakable values all in one place!
|
||||
|
||||
REGEN_RATE = (4, 8) # Min and max HP regen for Regeneration
|
||||
POISON_RATE = (4, 8) # Min and max damage for Poisoned
|
||||
ACC_UP_MOD = 25 # Accuracy Up attack roll bonus
|
||||
ACC_DOWN_MOD = -25 # Accuracy Down attack roll penalty
|
||||
DMG_UP_MOD = 5 # Damage Up damage roll bonus
|
||||
DMG_DOWN_MOD = -5 # Damage Down damage roll penalty
|
||||
DEF_UP_MOD = 15 # Defense Up defense bonus
|
||||
DEF_DOWN_MOD = -15 # Defense Down defense penalty
|
||||
REGEN_RATE = (4, 8) # Min and max HP regen for Regeneration
|
||||
POISON_RATE = (4, 8) # Min and max damage for Poisoned
|
||||
ACC_UP_MOD = 25 # Accuracy Up attack roll bonus
|
||||
ACC_DOWN_MOD = -25 # Accuracy Down attack roll penalty
|
||||
DMG_UP_MOD = 5 # Damage Up damage roll bonus
|
||||
DMG_DOWN_MOD = -5 # Damage Down damage roll penalty
|
||||
DEF_UP_MOD = 15 # Defense Up defense bonus
|
||||
DEF_DOWN_MOD = -15 # Defense Down defense penalty
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -102,6 +102,7 @@ COMBAT FUNCTIONS START HERE
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def roll_init(character):
|
||||
"""
|
||||
Rolls a number between 1-1000 to determine initiative.
|
||||
|
|
@ -227,6 +228,7 @@ def apply_damage(defender, damage):
|
|||
if defender.db.hp <= 0:
|
||||
defender.db.hp = 0
|
||||
|
||||
|
||||
def at_defeat(defeated):
|
||||
"""
|
||||
Announces the defeat of a fighter in combat.
|
||||
|
|
@ -242,8 +244,15 @@ def at_defeat(defeated):
|
|||
"""
|
||||
defeated.location.msg_contents("%s has been defeated!" % defeated)
|
||||
|
||||
def resolve_attack(attacker, defender, attack_value=None, defense_value=None,
|
||||
damage_value=None, inflict_condition=[]):
|
||||
|
||||
def resolve_attack(
|
||||
attacker,
|
||||
defender,
|
||||
attack_value=None,
|
||||
defense_value=None,
|
||||
damage_value=None,
|
||||
inflict_condition=[],
|
||||
):
|
||||
"""
|
||||
Resolves an attack and outputs the result.
|
||||
|
||||
|
|
@ -275,7 +284,9 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None,
|
|||
if not damage_value:
|
||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
||||
# Announce damage dealt and apply damage.
|
||||
attacker.location.msg_contents("%s hits %s for %i damage!" % (attacker, defender, damage_value))
|
||||
attacker.location.msg_contents(
|
||||
"%s hits %s for %i damage!" % (attacker, defender, damage_value)
|
||||
)
|
||||
apply_damage(defender, damage_value)
|
||||
# Inflict conditions on hit, if any specified
|
||||
for condition in inflict_condition:
|
||||
|
|
@ -284,6 +295,7 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None,
|
|||
if defender.db.hp <= 0:
|
||||
at_defeat(defender)
|
||||
|
||||
|
||||
def combat_cleanup(character):
|
||||
"""
|
||||
Cleans up all the temporary combat-related attributes on a character.
|
||||
|
|
@ -342,7 +354,7 @@ def spend_action(character, actions, action_name=None):
|
|||
"""
|
||||
if action_name:
|
||||
character.db.combat_lastaction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
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.
|
||||
|
|
@ -350,6 +362,7 @@ def spend_action(character, actions, action_name=None):
|
|||
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.
|
||||
|
||||
|
||||
def spend_item_use(item, user):
|
||||
"""
|
||||
Spends one use on an item with limited uses.
|
||||
|
|
@ -364,28 +377,29 @@ def spend_item_use(item, user):
|
|||
spawn a new object as residue, using the value of item.db.item_consumable
|
||||
as the name of the prototype to spawn.
|
||||
"""
|
||||
item.db.item_uses -= 1 # Spend one use
|
||||
item.db.item_uses -= 1 # Spend one use
|
||||
|
||||
if item.db.item_uses > 0: # Has uses remaining
|
||||
if item.db.item_uses > 0: # Has uses remaining
|
||||
# Inform the player
|
||||
user.msg("%s has %i uses remaining." % (item.key.capitalize(), item.db.item_uses))
|
||||
|
||||
else: # All uses spent
|
||||
else: # All uses spent
|
||||
|
||||
if not item.db.item_consumable: # Item isn't consumable
|
||||
if not item.db.item_consumable: # Item isn't consumable
|
||||
# Just inform the player that the uses are gone
|
||||
user.msg("%s has no uses remaining." % item.key.capitalize())
|
||||
|
||||
else: # If item is consumable
|
||||
if item.db.item_consumable == True: # If the value is 'True', just destroy the item
|
||||
else: # If item is consumable
|
||||
if item.db.item_consumable == True: # If the value is 'True', just destroy the item
|
||||
user.msg("%s has been consumed." % item.key.capitalize())
|
||||
item.delete() # Delete the spent item
|
||||
item.delete() # Delete the spent item
|
||||
|
||||
else: # If a string, use value of item_consumable to spawn an object in its place
|
||||
residue = spawn({"prototype":item.db.item_consumable})[0] # Spawn the residue
|
||||
residue.location = item.location # Move the residue to the same place as the item
|
||||
else: # If a string, use value of item_consumable to spawn an object in its place
|
||||
residue = spawn({"prototype": item.db.item_consumable})[0] # Spawn the residue
|
||||
residue.location = item.location # Move the residue to the same place as the item
|
||||
user.msg("After using %s, you are left with %s." % (item, residue))
|
||||
item.delete() # Delete the spent item
|
||||
item.delete() # Delete the spent item
|
||||
|
||||
|
||||
def use_item(user, item, target):
|
||||
"""
|
||||
|
|
@ -413,7 +427,7 @@ def use_item(user, item, target):
|
|||
# Match item_func string to function
|
||||
try:
|
||||
item_func = ITEMFUNCS[item.db.item_func]
|
||||
except KeyError: # If item_func string doesn't match to a function in ITEMFUNCS
|
||||
except KeyError: # If item_func string doesn't match to a function in ITEMFUNCS
|
||||
user.msg("ERROR: %s not defined in ITEMFUNCS" % item.db.item_func)
|
||||
return
|
||||
|
||||
|
|
@ -432,6 +446,7 @@ def use_item(user, item, target):
|
|||
if is_in_combat(user):
|
||||
spend_action(user, 1, action_name="item")
|
||||
|
||||
|
||||
def condition_tickdown(character, turnchar):
|
||||
"""
|
||||
Ticks down the duration of conditions on a character at the start of a given character's turn.
|
||||
|
|
@ -457,9 +472,12 @@ def condition_tickdown(character, turnchar):
|
|||
character.db.conditions[key][0] -= 1
|
||||
if character.db.conditions[key][0] <= 0:
|
||||
# If the duration is brought down to 0, remove the condition and inform everyone.
|
||||
character.location.msg_contents("%s no longer has the '%s' condition." % (str(character), str(key)))
|
||||
character.location.msg_contents(
|
||||
"%s no longer has the '%s' condition." % (str(character), str(key))
|
||||
)
|
||||
del character.db.conditions[key]
|
||||
|
||||
|
||||
def add_condition(character, turnchar, condition, duration):
|
||||
"""
|
||||
Adds a condition to a fighter.
|
||||
|
|
@ -471,10 +489,11 @@ def add_condition(character, turnchar, condition, duration):
|
|||
duration (int or True): Number of turns the condition lasts, or True for indefinite
|
||||
"""
|
||||
# The first value is the remaining turns - the second value is whose turn to count down on.
|
||||
character.db.conditions.update({condition:[duration, turnchar]})
|
||||
character.db.conditions.update({condition: [duration, turnchar]})
|
||||
# Tell everyone!
|
||||
character.location.msg_contents("%s gains the '%s' condition." % (character, condition))
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
CHARACTER TYPECLASS
|
||||
|
|
@ -495,7 +514,7 @@ class TBItemsCharacter(DefaultCharacter):
|
|||
"""
|
||||
self.db.max_hp = 100 # Set maximum HP to 100
|
||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
||||
self.db.conditions = {} # Set empty dict for conditions
|
||||
self.db.conditions = {} # Set empty dict for conditions
|
||||
# Subscribe character to the ticker handler
|
||||
tickerhandler.add(NONCOMBAT_TURN_TIME, self.at_update, idstring="update")
|
||||
"""
|
||||
|
|
@ -554,15 +573,15 @@ class TBItemsCharacter(DefaultCharacter):
|
|||
"""
|
||||
# Regeneration: restores 4 to 8 HP at the start of character's turn
|
||||
if "Regeneration" in self.db.conditions:
|
||||
to_heal = randint(REGEN_RATE[0], REGEN_RAGE[1]) # Restore HP
|
||||
to_heal = randint(REGEN_RATE[0], REGEN_RAGE[1]) # Restore HP
|
||||
if self.db.hp + to_heal > self.db.max_hp:
|
||||
to_heal = self.db.max_hp - self.db.hp # Cap healing to max HP
|
||||
to_heal = self.db.max_hp - self.db.hp # Cap healing to max HP
|
||||
self.db.hp += to_heal
|
||||
self.location.msg_contents("%s regains %i HP from Regeneration." % (self, to_heal))
|
||||
|
||||
# Poisoned: does 4 to 8 damage at the start of character's turn
|
||||
if "Poisoned" in self.db.conditions:
|
||||
to_hurt = randint(POISON_RATE[0], POISON_RATE[1]) # Deal damage
|
||||
to_hurt = randint(POISON_RATE[0], POISON_RATE[1]) # Deal damage
|
||||
apply_damage(self, to_hurt)
|
||||
self.location.msg_contents("%s takes %i damage from being Poisoned." % (self, to_hurt))
|
||||
if self.db.hp <= 0:
|
||||
|
|
@ -584,7 +603,7 @@ class TBItemsCharacter(DefaultCharacter):
|
|||
"""
|
||||
Fires every 30 seconds.
|
||||
"""
|
||||
if not is_in_combat(self): # Not in combat
|
||||
if not is_in_combat(self): # Not in combat
|
||||
# Change all conditions to update on character's turn.
|
||||
for key in self.db.conditions:
|
||||
self.db.conditions[key][1] = self
|
||||
|
|
@ -593,15 +612,17 @@ class TBItemsCharacter(DefaultCharacter):
|
|||
# Tick down condition durations
|
||||
condition_tickdown(self, self)
|
||||
|
||||
|
||||
class TBItemsCharacterTest(TBItemsCharacter):
|
||||
"""
|
||||
Just like the TBItemsCharacter, but doesn't subscribe to the TickerHandler.
|
||||
This makes it easier to run unit tests on.
|
||||
"""
|
||||
|
||||
def at_object_creation(self):
|
||||
self.db.max_hp = 100 # Set maximum HP to 100
|
||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
||||
self.db.conditions = {} # Set empty dict for conditions
|
||||
self.db.conditions = {} # Set empty dict for conditions
|
||||
|
||||
|
||||
"""
|
||||
|
|
@ -671,13 +692,17 @@ class TBItemsTurnHandler(DefaultScript):
|
|||
"""
|
||||
Called once every self.interval seconds.
|
||||
"""
|
||||
currentchar = self.db.fighters[self.db.turn] # Note the current character in the turn order.
|
||||
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.
|
||||
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.
|
||||
|
|
@ -692,8 +717,12 @@ class TBItemsTurnHandler(DefaultScript):
|
|||
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_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):
|
||||
|
|
@ -723,7 +752,9 @@ class TBItemsTurnHandler(DefaultScript):
|
|||
# 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
|
||||
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!")
|
||||
|
|
@ -735,7 +766,9 @@ class TBItemsTurnHandler(DefaultScript):
|
|||
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
|
||||
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
|
||||
|
|
@ -804,6 +837,7 @@ class CmdFight(Command):
|
|||
fight is added to combat, and a turn order is randomly rolled.
|
||||
When it's your turn, you can attack other characters.
|
||||
"""
|
||||
|
||||
key = "fight"
|
||||
help_category = "combat"
|
||||
|
||||
|
|
@ -866,7 +900,7 @@ class CmdAttack(Command):
|
|||
self.caller.msg("You can't attack, you've been defeated.")
|
||||
return
|
||||
|
||||
if "Frightened" in self.caller.db.conditions: # Can't attack if frightened
|
||||
if "Frightened" in self.caller.db.conditions: # Can't attack if frightened
|
||||
self.caller.msg("You're too frightened to attack!")
|
||||
return
|
||||
|
||||
|
|
@ -916,8 +950,10 @@ class CmdPass(Command):
|
|||
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.
|
||||
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):
|
||||
|
|
@ -949,7 +985,7 @@ class CmdDisengage(Command):
|
|||
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.
|
||||
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.
|
||||
|
|
@ -996,16 +1032,19 @@ class CmdCombatHelp(CmdHelp):
|
|||
This will search for help on commands and other
|
||||
topics related to the game.
|
||||
"""
|
||||
|
||||
# Just like the default help command, but will give quick
|
||||
# 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.|/" +
|
||||
"|wUse:|n Use an item you're carrying.")
|
||||
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.|/"
|
||||
+ "|wUse:|n Use an item you're carrying."
|
||||
)
|
||||
else:
|
||||
super(CmdCombatHelp, self).func() # Call the default help command
|
||||
|
||||
|
|
@ -1047,12 +1086,12 @@ class CmdUse(MuxCommand):
|
|||
self.caller.msg("You can only use items on your turn.")
|
||||
return
|
||||
|
||||
if not item.db.item_func: # Object has no item_func, not usable
|
||||
if not item.db.item_func: # Object has no item_func, not usable
|
||||
self.caller.msg("'%s' is not a usable item." % item.key.capitalize())
|
||||
return
|
||||
|
||||
if item.attributes.has("item_uses"): # Item has limited uses
|
||||
if item.db.item_uses <= 0: # Limited uses are spent
|
||||
if item.attributes.has("item_uses"): # Item has limited uses
|
||||
if item.db.item_uses <= 0: # Limited uses are spent
|
||||
self.caller.msg("'%s' has no uses remaining." % item.key.capitalize())
|
||||
return
|
||||
|
||||
|
|
@ -1064,6 +1103,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
"""
|
||||
This command set includes all the commmands used in the battle system.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
|
|
@ -1078,6 +1118,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
self.add(CmdCombatHelp())
|
||||
self.add(CmdUse())
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
ITEM FUNCTIONS START HERE
|
||||
|
|
@ -1101,6 +1142,7 @@ Each function below contains a description of what kwargs the function will
|
|||
take and the effect they have on the result.
|
||||
"""
|
||||
|
||||
|
||||
def itemfunc_heal(item, user, target, **kwargs):
|
||||
"""
|
||||
Item function that heals HP.
|
||||
|
|
@ -1110,11 +1152,11 @@ def itemfunc_heal(item, user, target, **kwargs):
|
|||
max_healing(int): Maximum amount of HP recovered
|
||||
"""
|
||||
if not target:
|
||||
target = user # Target user if none specified
|
||||
target = user # Target user if none specified
|
||||
|
||||
if not target.attributes.has("max_hp"): # Has no HP to speak of
|
||||
if not target.attributes.has("max_hp"): # Has no HP to speak of
|
||||
user.msg("You can't use %s on that." % item)
|
||||
return False # Returning false aborts the item use
|
||||
return False # Returning false aborts the item use
|
||||
|
||||
if target.db.hp >= target.db.max_hp:
|
||||
user.msg("%s is already at full health." % target)
|
||||
|
|
@ -1128,13 +1170,14 @@ def itemfunc_heal(item, user, target, **kwargs):
|
|||
min_healing = kwargs["healing_range"][0]
|
||||
max_healing = kwargs["healing_range"][1]
|
||||
|
||||
to_heal = randint(min_healing, max_healing) # Restore 20 to 40 hp
|
||||
to_heal = randint(min_healing, max_healing) # Restore 20 to 40 hp
|
||||
if target.db.hp + to_heal > target.db.max_hp:
|
||||
to_heal = target.db.max_hp - target.db.hp # Cap healing to max HP
|
||||
to_heal = target.db.max_hp - target.db.hp # Cap healing to max HP
|
||||
target.db.hp += to_heal
|
||||
|
||||
user.location.msg_contents("%s uses %s! %s regains %i HP!" % (user, item, target, to_heal))
|
||||
|
||||
|
||||
def itemfunc_add_condition(item, user, target, **kwargs):
|
||||
"""
|
||||
Item function that gives the target one or more conditions.
|
||||
|
|
@ -1150,11 +1193,11 @@ def itemfunc_add_condition(item, user, target, **kwargs):
|
|||
conditions = [("Regeneration", 5)]
|
||||
|
||||
if not target:
|
||||
target = user # Target user if none specified
|
||||
target = user # Target user if none specified
|
||||
|
||||
if not target.attributes.has("max_hp"): # Is not a fighter
|
||||
if not target.attributes.has("max_hp"): # Is not a fighter
|
||||
user.msg("You can't use %s on that." % item)
|
||||
return False # Returning false aborts the item use
|
||||
return False # Returning false aborts the item use
|
||||
|
||||
# Retrieve condition / duration from kwargs, if present
|
||||
if "conditions" in kwargs:
|
||||
|
|
@ -1166,6 +1209,7 @@ def itemfunc_add_condition(item, user, target, **kwargs):
|
|||
for condition in conditions:
|
||||
add_condition(target, user, condition[0], condition[1])
|
||||
|
||||
|
||||
def itemfunc_cure_condition(item, user, target, **kwargs):
|
||||
"""
|
||||
Item function that'll remove given conditions from a target.
|
||||
|
|
@ -1176,11 +1220,11 @@ def itemfunc_cure_condition(item, user, target, **kwargs):
|
|||
to_cure = ["Poisoned"]
|
||||
|
||||
if not target:
|
||||
target = user # Target user if none specified
|
||||
target = user # Target user if none specified
|
||||
|
||||
if not target.attributes.has("max_hp"): # Is not a fighter
|
||||
if not target.attributes.has("max_hp"): # Is not a fighter
|
||||
user.msg("You can't use %s on that." % item)
|
||||
return False # Returning false aborts the item use
|
||||
return False # Returning false aborts the item use
|
||||
|
||||
# Retrieve condition(s) to cure from kwargs, if present
|
||||
if "to_cure" in kwargs:
|
||||
|
|
@ -1196,6 +1240,7 @@ def itemfunc_cure_condition(item, user, target, **kwargs):
|
|||
|
||||
user.location.msg_contents(item_msg)
|
||||
|
||||
|
||||
def itemfunc_attack(item, user, target, **kwargs):
|
||||
"""
|
||||
Item function that attacks a target.
|
||||
|
|
@ -1213,7 +1258,7 @@ def itemfunc_attack(item, user, target, **kwargs):
|
|||
"""
|
||||
if not is_in_combat(user):
|
||||
user.msg("You can only use that in combat.")
|
||||
return False # Returning false aborts the item use
|
||||
return False # Returning false aborts the item use
|
||||
|
||||
if not target:
|
||||
user.msg("You have to specify a target to use %s! (use <item> = <target>)" % item)
|
||||
|
|
@ -1223,7 +1268,7 @@ def itemfunc_attack(item, user, target, **kwargs):
|
|||
user.msg("You can't attack yourself!")
|
||||
return False
|
||||
|
||||
if not target.db.hp: # Has no HP
|
||||
if not target.db.hp: # Has no HP
|
||||
user.msg("You can't use %s on that." % item)
|
||||
return False
|
||||
|
||||
|
|
@ -1252,17 +1297,23 @@ def itemfunc_attack(item, user, target, **kwargs):
|
|||
attack_value -= 25
|
||||
|
||||
user.location.msg_contents("%s attacks %s with %s!" % (user, target, item))
|
||||
resolve_attack(user, target, attack_value=attack_value,
|
||||
damage_value=damage_value, inflict_condition=inflict_condition)
|
||||
resolve_attack(
|
||||
user,
|
||||
target,
|
||||
attack_value=attack_value,
|
||||
damage_value=damage_value,
|
||||
inflict_condition=inflict_condition,
|
||||
)
|
||||
|
||||
|
||||
# Match strings to item functions here. We can't store callables on
|
||||
# prototypes, so we store a string instead, matching that string to
|
||||
# a callable in this dictionary.
|
||||
ITEMFUNCS = {
|
||||
"heal":itemfunc_heal,
|
||||
"attack":itemfunc_attack,
|
||||
"add_condition":itemfunc_add_condition,
|
||||
"cure_condition":itemfunc_cure_condition
|
||||
"heal": itemfunc_heal,
|
||||
"attack": itemfunc_attack,
|
||||
"add_condition": itemfunc_add_condition,
|
||||
"cure_condition": itemfunc_cure_condition,
|
||||
}
|
||||
|
||||
"""
|
||||
|
|
@ -1297,101 +1348,110 @@ specifying any of the following:
|
|||
"""
|
||||
|
||||
MEDKIT = {
|
||||
"key" : "a medical kit",
|
||||
"aliases" : ["medkit"],
|
||||
"desc" : "A standard medical kit. It can be used a few times to heal wounds.",
|
||||
"item_func" : "heal",
|
||||
"item_uses" : 3,
|
||||
"item_consumable" : True,
|
||||
"item_kwargs" : {"healing_range":(15, 25)}
|
||||
"key": "a medical kit",
|
||||
"aliases": ["medkit"],
|
||||
"desc": "A standard medical kit. It can be used a few times to heal wounds.",
|
||||
"item_func": "heal",
|
||||
"item_uses": 3,
|
||||
"item_consumable": True,
|
||||
"item_kwargs": {"healing_range": (15, 25)},
|
||||
}
|
||||
|
||||
GLASS_BOTTLE = {
|
||||
"key" : "a glass bottle",
|
||||
"desc" : "An empty glass bottle."
|
||||
}
|
||||
GLASS_BOTTLE = {"key": "a glass bottle", "desc": "An empty glass bottle."}
|
||||
|
||||
HEALTH_POTION = {
|
||||
"key" : "a health potion",
|
||||
"desc" : "A glass bottle full of a mystical potion that heals wounds when used.",
|
||||
"item_func" : "heal",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : "GLASS_BOTTLE",
|
||||
"item_kwargs" : {"healing_range":(35, 50)}
|
||||
"key": "a health potion",
|
||||
"desc": "A glass bottle full of a mystical potion that heals wounds when used.",
|
||||
"item_func": "heal",
|
||||
"item_uses": 1,
|
||||
"item_consumable": "GLASS_BOTTLE",
|
||||
"item_kwargs": {"healing_range": (35, 50)},
|
||||
}
|
||||
|
||||
REGEN_POTION = {
|
||||
"key" : "a regeneration potion",
|
||||
"desc" : "A glass bottle full of a mystical potion that regenerates wounds over time.",
|
||||
"item_func" : "add_condition",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : "GLASS_BOTTLE",
|
||||
"item_kwargs" : {"conditions":[("Regeneration", 10)]}
|
||||
"key": "a regeneration potion",
|
||||
"desc": "A glass bottle full of a mystical potion that regenerates wounds over time.",
|
||||
"item_func": "add_condition",
|
||||
"item_uses": 1,
|
||||
"item_consumable": "GLASS_BOTTLE",
|
||||
"item_kwargs": {"conditions": [("Regeneration", 10)]},
|
||||
}
|
||||
|
||||
HASTE_POTION = {
|
||||
"key" : "a haste potion",
|
||||
"desc" : "A glass bottle full of a mystical potion that hastens its user.",
|
||||
"item_func" : "add_condition",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : "GLASS_BOTTLE",
|
||||
"item_kwargs" : {"conditions":[("Haste", 10)]}
|
||||
"key": "a haste potion",
|
||||
"desc": "A glass bottle full of a mystical potion that hastens its user.",
|
||||
"item_func": "add_condition",
|
||||
"item_uses": 1,
|
||||
"item_consumable": "GLASS_BOTTLE",
|
||||
"item_kwargs": {"conditions": [("Haste", 10)]},
|
||||
}
|
||||
|
||||
BOMB = {
|
||||
"key" : "a rotund bomb",
|
||||
"desc" : "A large black sphere with a fuse at the end. Can be used on enemies in combat.",
|
||||
"item_func" : "attack",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : True,
|
||||
"item_kwargs" : {"damage_range":(25, 40), "accuracy":25}
|
||||
"key": "a rotund bomb",
|
||||
"desc": "A large black sphere with a fuse at the end. Can be used on enemies in combat.",
|
||||
"item_func": "attack",
|
||||
"item_uses": 1,
|
||||
"item_consumable": True,
|
||||
"item_kwargs": {"damage_range": (25, 40), "accuracy": 25},
|
||||
}
|
||||
|
||||
POISON_DART = {
|
||||
"key" : "a poison dart",
|
||||
"desc" : "A thin dart coated in deadly poison. Can be used on enemies in combat",
|
||||
"item_func" : "attack",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : True,
|
||||
"item_kwargs" : {"damage_range":(5, 10), "accuracy":25, "inflict_condition":[("Poisoned", 10)]}
|
||||
"key": "a poison dart",
|
||||
"desc": "A thin dart coated in deadly poison. Can be used on enemies in combat",
|
||||
"item_func": "attack",
|
||||
"item_uses": 1,
|
||||
"item_consumable": True,
|
||||
"item_kwargs": {
|
||||
"damage_range": (5, 10),
|
||||
"accuracy": 25,
|
||||
"inflict_condition": [("Poisoned", 10)],
|
||||
},
|
||||
}
|
||||
|
||||
TASER = {
|
||||
"key" : "a taser",
|
||||
"desc" : "A device that can be used to paralyze enemies in combat.",
|
||||
"item_func" : "attack",
|
||||
"item_kwargs" : {"damage_range":(10, 20), "accuracy":0, "inflict_condition":[("Paralyzed", 1)]}
|
||||
"key": "a taser",
|
||||
"desc": "A device that can be used to paralyze enemies in combat.",
|
||||
"item_func": "attack",
|
||||
"item_kwargs": {
|
||||
"damage_range": (10, 20),
|
||||
"accuracy": 0,
|
||||
"inflict_condition": [("Paralyzed", 1)],
|
||||
},
|
||||
}
|
||||
|
||||
GHOST_GUN = {
|
||||
"key" : "a ghost gun",
|
||||
"desc" : "A gun that fires scary ghosts at people. Anyone hit by a ghost becomes frightened.",
|
||||
"item_func" : "attack",
|
||||
"item_uses" : 6,
|
||||
"item_kwargs" : {"damage_range":(5, 10), "accuracy":15, "inflict_condition":[("Frightened", 1)]}
|
||||
"key": "a ghost gun",
|
||||
"desc": "A gun that fires scary ghosts at people. Anyone hit by a ghost becomes frightened.",
|
||||
"item_func": "attack",
|
||||
"item_uses": 6,
|
||||
"item_kwargs": {
|
||||
"damage_range": (5, 10),
|
||||
"accuracy": 15,
|
||||
"inflict_condition": [("Frightened", 1)],
|
||||
},
|
||||
}
|
||||
|
||||
ANTIDOTE_POTION = {
|
||||
"key" : "an antidote potion",
|
||||
"desc" : "A glass bottle full of a mystical potion that cures poison when used.",
|
||||
"item_func" : "cure_condition",
|
||||
"item_uses" : 1,
|
||||
"item_consumable" : "GLASS_BOTTLE",
|
||||
"item_kwargs" : {"to_cure":["Poisoned"]}
|
||||
"key": "an antidote potion",
|
||||
"desc": "A glass bottle full of a mystical potion that cures poison when used.",
|
||||
"item_func": "cure_condition",
|
||||
"item_uses": 1,
|
||||
"item_consumable": "GLASS_BOTTLE",
|
||||
"item_kwargs": {"to_cure": ["Poisoned"]},
|
||||
}
|
||||
|
||||
AMULET_OF_MIGHT = {
|
||||
"key" : "The Amulet of Might",
|
||||
"desc" : "The one who holds this amulet can call upon its power to gain great strength.",
|
||||
"item_func" : "add_condition",
|
||||
"item_selfonly" : True,
|
||||
"item_kwargs" : {"conditions":[("Damage Up", 3), ("Accuracy Up", 3), ("Defense Up", 3)]}
|
||||
"key": "The Amulet of Might",
|
||||
"desc": "The one who holds this amulet can call upon its power to gain great strength.",
|
||||
"item_func": "add_condition",
|
||||
"item_selfonly": True,
|
||||
"item_kwargs": {"conditions": [("Damage Up", 3), ("Accuracy Up", 3), ("Defense Up", 3)]},
|
||||
}
|
||||
|
||||
AMULET_OF_WEAKNESS = {
|
||||
"key" : "The Amulet of Weakness",
|
||||
"desc" : "The one who holds this amulet can call upon its power to gain great weakness. It's not a terribly useful artifact.",
|
||||
"item_func" : "add_condition",
|
||||
"item_selfonly" : True,
|
||||
"item_kwargs" : {"conditions":[("Damage Down", 3), ("Accuracy Down", 3), ("Defense Down", 3)]}
|
||||
"key": "The Amulet of Weakness",
|
||||
"desc": "The one who holds this amulet can call upon its power to gain great weakness. It's not a terribly useful artifact.",
|
||||
"item_func": "add_condition",
|
||||
"item_selfonly": True,
|
||||
"item_kwargs": {"conditions": [("Damage Down", 3), ("Accuracy Down", 3), ("Defense Down", 3)]},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ OPTIONS
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 1 # Number of actions allowed per turn
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -80,6 +80,7 @@ COMBAT FUNCTIONS START HERE
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def roll_init(character):
|
||||
"""
|
||||
Rolls a number between 1-1000 to determine initiative.
|
||||
|
|
@ -194,6 +195,7 @@ def apply_damage(defender, damage):
|
|||
if defender.db.hp <= 0:
|
||||
defender.db.hp = 0
|
||||
|
||||
|
||||
def at_defeat(defeated):
|
||||
"""
|
||||
Announces the defeat of a fighter in combat.
|
||||
|
|
@ -209,6 +211,7 @@ def at_defeat(defeated):
|
|||
"""
|
||||
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.
|
||||
|
|
@ -234,12 +237,15 @@ def resolve_attack(attacker, defender, attack_value=None, defense_value=None):
|
|||
else:
|
||||
damage_value = get_damage(attacker, defender) # Calculate damage value.
|
||||
# Announce damage dealt and apply damage.
|
||||
attacker.location.msg_contents("%s hits %s for %i damage!" % (attacker, defender, damage_value))
|
||||
attacker.location.msg_contents(
|
||||
"%s hits %s for %i damage!" % (attacker, defender, 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 combat_cleanup(character):
|
||||
"""
|
||||
Cleans up all the temporary combat-related attributes on a character.
|
||||
|
|
@ -300,7 +306,7 @@ def spend_action(character, actions, action_name=None):
|
|||
return
|
||||
if action_name:
|
||||
character.db.combat_lastaction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
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.
|
||||
|
|
@ -335,10 +341,9 @@ class TBMagicCharacter(DefaultCharacter):
|
|||
"""
|
||||
self.db.max_hp = 100 # Set maximum HP to 100
|
||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
||||
self.db.spells_known = [] # Set empty spells known list
|
||||
self.db.max_mp = 20 # Set maximum MP to 20
|
||||
self.db.mp = self.db.max_mp # Set current MP to maximum
|
||||
|
||||
self.db.spells_known = [] # Set empty spells known list
|
||||
self.db.max_mp = 20 # Set maximum MP to 20
|
||||
self.db.mp = self.db.max_mp # Set current MP to maximum
|
||||
|
||||
def at_before_move(self, destination):
|
||||
"""
|
||||
|
|
@ -365,6 +370,7 @@ class TBMagicCharacter(DefaultCharacter):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
SCRIPTS START HERE
|
||||
|
|
@ -412,7 +418,7 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
|
||||
# Announce the turn order.
|
||||
self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters))
|
||||
|
||||
|
||||
# Start first fighter's turn.
|
||||
self.start_turn(self.db.fighters[0])
|
||||
|
||||
|
|
@ -432,13 +438,17 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
"""
|
||||
Called once every self.interval seconds.
|
||||
"""
|
||||
currentchar = self.db.fighters[self.db.turn] # Note the current character in the turn order.
|
||||
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.
|
||||
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.
|
||||
|
|
@ -453,8 +463,12 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
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_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):
|
||||
|
|
@ -484,7 +498,9 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
# 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
|
||||
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!")
|
||||
|
|
@ -496,7 +512,9 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
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
|
||||
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
|
||||
|
|
@ -540,7 +558,7 @@ class TBMagicTurnHandler(DefaultScript):
|
|||
# Initialize the character like you do at the start.
|
||||
self.initialize_for_combat(character)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
COMMANDS START HERE
|
||||
|
|
@ -559,6 +577,7 @@ class CmdFight(Command):
|
|||
fight is added to combat, and a turn order is randomly rolled.
|
||||
When it's your turn, you can attack other characters.
|
||||
"""
|
||||
|
||||
key = "fight"
|
||||
help_category = "combat"
|
||||
|
||||
|
|
@ -667,8 +686,10 @@ class CmdPass(Command):
|
|||
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.
|
||||
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):
|
||||
|
|
@ -700,12 +721,13 @@ class CmdDisengage(Command):
|
|||
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.
|
||||
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 CmdLearnSpell(Command):
|
||||
"""
|
||||
Learn a magic spell.
|
||||
|
|
@ -731,10 +753,10 @@ class CmdLearnSpell(Command):
|
|||
|
||||
|wcactus conjuration|n (2 MP): Creates a cactus.
|
||||
"""
|
||||
|
||||
|
||||
key = "learnspell"
|
||||
help_category = "magic"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -744,38 +766,39 @@ class CmdLearnSpell(Command):
|
|||
args = args.strip(" ")
|
||||
caller = self.caller
|
||||
spell_to_learn = []
|
||||
|
||||
if not args or len(args) < 3: # No spell given
|
||||
|
||||
if not args or len(args) < 3: # No spell given
|
||||
caller.msg("Usage: learnspell <spell name>")
|
||||
return
|
||||
|
||||
for spell in spell_list: # Match inputs to spells
|
||||
|
||||
for spell in spell_list: # Match inputs to spells
|
||||
if args in spell.lower():
|
||||
spell_to_learn.append(spell)
|
||||
|
||||
if spell_to_learn == []: # No spells matched
|
||||
|
||||
if spell_to_learn == []: # No spells matched
|
||||
caller.msg("There is no spell with that name.")
|
||||
return
|
||||
if len(spell_to_learn) > 1: # More than one match
|
||||
matched_spells = ', '.join(spell_to_learn)
|
||||
if len(spell_to_learn) > 1: # More than one match
|
||||
matched_spells = ", ".join(spell_to_learn)
|
||||
caller.msg("Which spell do you mean: %s?" % matched_spells)
|
||||
return
|
||||
|
||||
if len(spell_to_learn) == 1: # If one match, extract the string
|
||||
|
||||
if len(spell_to_learn) == 1: # If one match, extract the string
|
||||
spell_to_learn = spell_to_learn[0]
|
||||
|
||||
if spell_to_learn not in self.caller.db.spells_known: # If the spell isn't known...
|
||||
caller.db.spells_known.append(spell_to_learn) # ...then add the spell to the character
|
||||
|
||||
if spell_to_learn not in self.caller.db.spells_known: # If the spell isn't known...
|
||||
caller.db.spells_known.append(spell_to_learn) # ...then add the spell to the character
|
||||
caller.msg("You learn the spell '%s'!" % spell_to_learn)
|
||||
return
|
||||
if spell_to_learn in self.caller.db.spells_known: # Already has the spell specified
|
||||
if spell_to_learn in self.caller.db.spells_known: # Already has the spell specified
|
||||
caller.msg("You already know the spell '%s'!" % spell_to_learn)
|
||||
"""
|
||||
You will almost definitely want to replace this with your own system
|
||||
for learning spells, perhaps tied to character advancement or finding
|
||||
items in the game world that spells can be learned from.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class CmdCast(MuxCommand):
|
||||
"""
|
||||
Cast a magic spell that you know, provided you have the MP
|
||||
|
|
@ -788,10 +811,10 @@ class CmdCast(MuxCommand):
|
|||
on only yourself, and some don't need a target specified at all.
|
||||
Typing 'cast' by itself will give you a list of spells you know.
|
||||
"""
|
||||
|
||||
|
||||
key = "cast"
|
||||
help_category = "magic"
|
||||
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This performs the actual command.
|
||||
|
|
@ -804,155 +827,169 @@ class CmdCast(MuxCommand):
|
|||
function.
|
||||
"""
|
||||
caller = self.caller
|
||||
|
||||
if not self.lhs or len(self.lhs) < 3: # No spell name given
|
||||
|
||||
if not self.lhs or len(self.lhs) < 3: # No spell name given
|
||||
caller.msg("Usage: cast <spell name> = <target>, <target2>, ...")
|
||||
if not caller.db.spells_known:
|
||||
caller.msg("You don't know any spells.")
|
||||
return
|
||||
else:
|
||||
caller.db.spells_known = sorted(caller.db.spells_known)
|
||||
spells_known_msg = "You know the following spells:|/" + "|/".join(caller.db.spells_known)
|
||||
caller.msg(spells_known_msg) # List the spells the player knows
|
||||
spells_known_msg = "You know the following spells:|/" + "|/".join(
|
||||
caller.db.spells_known
|
||||
)
|
||||
caller.msg(spells_known_msg) # List the spells the player knows
|
||||
return
|
||||
|
||||
|
||||
spellname = self.lhs.lower()
|
||||
spell_to_cast = []
|
||||
spell_targets = []
|
||||
|
||||
|
||||
if not self.rhs:
|
||||
spell_targets = []
|
||||
elif self.rhs.lower() in ['me', 'self', 'myself']:
|
||||
elif self.rhs.lower() in ["me", "self", "myself"]:
|
||||
spell_targets = [caller]
|
||||
elif len(self.rhs) > 2:
|
||||
spell_targets = self.rhslist
|
||||
|
||||
for spell in caller.db.spells_known: # Match inputs to spells
|
||||
|
||||
for spell in caller.db.spells_known: # Match inputs to spells
|
||||
if self.lhs in spell.lower():
|
||||
spell_to_cast.append(spell)
|
||||
|
||||
if spell_to_cast == []: # No spells matched
|
||||
|
||||
if spell_to_cast == []: # No spells matched
|
||||
caller.msg("You don't know a spell of that name.")
|
||||
return
|
||||
if len(spell_to_cast) > 1: # More than one match
|
||||
matched_spells = ', '.join(spell_to_cast)
|
||||
if len(spell_to_cast) > 1: # More than one match
|
||||
matched_spells = ", ".join(spell_to_cast)
|
||||
caller.msg("Which spell do you mean: %s?" % matched_spells)
|
||||
return
|
||||
|
||||
if len(spell_to_cast) == 1: # If one match, extract the string
|
||||
|
||||
if len(spell_to_cast) == 1: # If one match, extract the string
|
||||
spell_to_cast = spell_to_cast[0]
|
||||
|
||||
if spell_to_cast not in SPELLS: # Spell isn't defined
|
||||
|
||||
if spell_to_cast not in SPELLS: # Spell isn't defined
|
||||
caller.msg("ERROR: Spell %s is undefined" % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# Time to extract some info from the chosen spell!
|
||||
spelldata = SPELLS[spell_to_cast]
|
||||
|
||||
|
||||
# Add in some default data if optional parameters aren't specified
|
||||
if "combat_spell" not in spelldata:
|
||||
spelldata.update({"combat_spell":True})
|
||||
spelldata.update({"combat_spell": True})
|
||||
if "noncombat_spell" not in spelldata:
|
||||
spelldata.update({"noncombat_spell":True})
|
||||
spelldata.update({"noncombat_spell": True})
|
||||
if "max_targets" not in spelldata:
|
||||
spelldata.update({"max_targets":1})
|
||||
|
||||
spelldata.update({"max_targets": 1})
|
||||
|
||||
# Store any superfluous options as kwargs to pass to the spell function
|
||||
kwargs = {}
|
||||
spelldata_opts = ["spellfunc", "target", "cost", "combat_spell", "noncombat_spell", "max_targets"]
|
||||
spelldata_opts = [
|
||||
"spellfunc",
|
||||
"target",
|
||||
"cost",
|
||||
"combat_spell",
|
||||
"noncombat_spell",
|
||||
"max_targets",
|
||||
]
|
||||
for key in spelldata:
|
||||
if key not in spelldata_opts:
|
||||
kwargs.update({key:spelldata[key]})
|
||||
|
||||
kwargs.update({key: spelldata[key]})
|
||||
|
||||
# If caster doesn't have enough MP to cover the spell's cost, give error and return
|
||||
if spelldata["cost"] > caller.db.mp:
|
||||
caller.msg("You don't have enough MP to cast '%s'." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# If in combat and the spell isn't a combat spell, give error message and return
|
||||
if spelldata["combat_spell"] == False and is_in_combat(caller):
|
||||
caller.msg("You can't use the spell '%s' in combat." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# If not in combat and the spell isn't a non-combat spell, error ms and return.
|
||||
if spelldata["noncombat_spell"] == False and is_in_combat(caller) == False:
|
||||
caller.msg("You can't use the spell '%s' outside of combat." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# If spell takes no targets and one is given, give error message and return
|
||||
if len(spell_targets) > 0 and spelldata["target"] == "none":
|
||||
caller.msg("The spell '%s' isn't cast on a target." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# If no target is given and spell requires a target, give error message
|
||||
if spelldata["target"] not in ["self", "none"]:
|
||||
if len(spell_targets) == 0:
|
||||
caller.msg("The spell '%s' requires a target." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# If more targets given than maximum, give error message
|
||||
if len(spell_targets) > spelldata["max_targets"]:
|
||||
targplural = "target"
|
||||
if spelldata["max_targets"] > 1:
|
||||
targplural = "targets"
|
||||
caller.msg("The spell '%s' can only be cast on %i %s." % (spell_to_cast, spelldata["max_targets"], targplural))
|
||||
caller.msg(
|
||||
"The spell '%s' can only be cast on %i %s."
|
||||
% (spell_to_cast, spelldata["max_targets"], targplural)
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
# Set up our candidates for targets
|
||||
target_candidates = []
|
||||
|
||||
|
||||
# If spell targets 'any' or 'other', any object in caster's inventory or location
|
||||
# can be targeted by the spell.
|
||||
if spelldata["target"] in ["any", "other"]:
|
||||
target_candidates = caller.location.contents + caller.contents
|
||||
|
||||
|
||||
# If spell targets 'anyobj', only non-character objects can be targeted.
|
||||
if spelldata["target"] == "anyobj":
|
||||
prefilter_candidates = caller.location.contents + caller.contents
|
||||
for thing in prefilter_candidates:
|
||||
if not thing.attributes.has("max_hp"): # Has no max HP, isn't a fighter
|
||||
if not thing.attributes.has("max_hp"): # Has no max HP, isn't a fighter
|
||||
target_candidates.append(thing)
|
||||
|
||||
|
||||
# If spell targets 'anychar' or 'otherchar', only characters can be targeted.
|
||||
if spelldata["target"] in ["anychar", "otherchar"]:
|
||||
prefilter_candidates = caller.location.contents
|
||||
for thing in prefilter_candidates:
|
||||
if thing.attributes.has("max_hp"): # Has max HP, is a fighter
|
||||
if thing.attributes.has("max_hp"): # Has max HP, is a fighter
|
||||
target_candidates.append(thing)
|
||||
|
||||
|
||||
# Now, match each entry in spell_targets to an object in the search candidates
|
||||
matched_targets = []
|
||||
for target in spell_targets:
|
||||
match = caller.search(target, candidates=target_candidates)
|
||||
matched_targets.append(match)
|
||||
spell_targets = matched_targets
|
||||
|
||||
|
||||
# If no target is given and the spell's target is 'self', set target to self
|
||||
if len(spell_targets) == 0 and spelldata["target"] == "self":
|
||||
spell_targets = [caller]
|
||||
|
||||
|
||||
# Give error message if trying to cast an "other" target spell on yourself
|
||||
if spelldata["target"] in ["other", "otherchar"]:
|
||||
if caller in spell_targets:
|
||||
caller.msg("You can't cast '%s' on yourself." % spell_to_cast)
|
||||
return
|
||||
|
||||
|
||||
# Return if "None" in target list, indicating failed match
|
||||
if None in spell_targets:
|
||||
# No need to give an error message, as 'search' gives one by default.
|
||||
return
|
||||
|
||||
|
||||
# Give error message if repeats in target list
|
||||
if len(spell_targets) != len(set(spell_targets)):
|
||||
caller.msg("You can't specify the same target more than once!")
|
||||
return
|
||||
|
||||
|
||||
# Finally, we can cast the spell itself. Note that MP is not deducted here!
|
||||
try:
|
||||
spelldata["spellfunc"](caller, spell_to_cast, spell_targets, spelldata["cost"], **kwargs)
|
||||
spelldata["spellfunc"](
|
||||
caller, spell_to_cast, spell_targets, spelldata["cost"], **kwargs
|
||||
)
|
||||
except Exception:
|
||||
log_trace("Error in callback for spell: %s." % spell_to_cast)
|
||||
|
||||
|
||||
|
||||
class CmdRest(Command):
|
||||
"""
|
||||
|
|
@ -979,7 +1016,8 @@ class CmdRest(Command):
|
|||
self.caller.db.mp = self.caller.db.max_mp # Set current MP to maximum
|
||||
self.caller.location.msg_contents("%s rests to recover HP and MP." % self.caller)
|
||||
# You'll probably want to replace this with your own system for recovering HP and MP.
|
||||
|
||||
|
||||
|
||||
class CmdStatus(Command):
|
||||
"""
|
||||
Gives combat information.
|
||||
|
|
@ -997,15 +1035,19 @@ class CmdStatus(Command):
|
|||
def func(self):
|
||||
"This performs the actual command."
|
||||
char = self.caller
|
||||
|
||||
if not char.db.max_hp: # Character not initialized, IE in unit tests
|
||||
|
||||
if not char.db.max_hp: # Character not initialized, IE in unit tests
|
||||
char.db.hp = 100
|
||||
char.db.max_hp = 100
|
||||
char.db.spells_known = []
|
||||
char.db.max_mp = 20
|
||||
char.db.mp = char.db.max_mp
|
||||
|
||||
char.msg("You have %i / %i HP and %i / %i MP." % (char.db.hp, char.db.max_hp, char.db.mp, char.db.max_mp))
|
||||
|
||||
char.msg(
|
||||
"You have %i / %i HP and %i / %i MP."
|
||||
% (char.db.hp, char.db.max_hp, char.db.mp, char.db.max_mp)
|
||||
)
|
||||
|
||||
|
||||
class CmdCombatHelp(CmdHelp):
|
||||
"""
|
||||
|
|
@ -1019,15 +1061,18 @@ class CmdCombatHelp(CmdHelp):
|
|||
This will search for help on commands and other
|
||||
topics related to the game.
|
||||
"""
|
||||
|
||||
# Just like the default help command, but will give quick
|
||||
# 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.|/")
|
||||
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(CmdCombatHelp, self).func() # Call the default help command
|
||||
|
||||
|
|
@ -1036,6 +1081,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
"""
|
||||
This command set includes all the commmands used in the battle system.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
|
|
@ -1052,6 +1098,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
self.add(CmdCast())
|
||||
self.add(CmdStatus())
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
SPELL FUNCTIONS START HERE
|
||||
|
|
@ -1072,6 +1119,7 @@ These functions also all accept **kwargs, and how these are used is specified
|
|||
in the docstring for each function.
|
||||
"""
|
||||
|
||||
|
||||
def spell_healing(caster, spell_name, targets, cost, **kwargs):
|
||||
"""
|
||||
Spell that restores HP to a target or targets.
|
||||
|
|
@ -1081,29 +1129,30 @@ def spell_healing(caster, spell_name, targets, cost, **kwargs):
|
|||
each target. (20, 40) by default.
|
||||
"""
|
||||
spell_msg = "%s casts %s!" % (caster, spell_name)
|
||||
|
||||
|
||||
min_healing = 20
|
||||
max_healing = 40
|
||||
|
||||
|
||||
# Retrieve healing range from kwargs, if present
|
||||
if "healing_range" in kwargs:
|
||||
min_healing = kwargs["healing_range"][0]
|
||||
max_healing = kwargs["healing_range"][1]
|
||||
|
||||
|
||||
for character in targets:
|
||||
to_heal = randint(min_healing, max_healing) # Restore 20 to 40 hp
|
||||
to_heal = randint(min_healing, max_healing) # Restore 20 to 40 hp
|
||||
if character.db.hp + to_heal > character.db.max_hp:
|
||||
to_heal = character.db.max_hp - character.db.hp # Cap healing to max HP
|
||||
to_heal = character.db.max_hp - character.db.hp # Cap healing to max HP
|
||||
character.db.hp += to_heal
|
||||
spell_msg += " %s regains %i HP!" % (character, to_heal)
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
caster.location.msg_contents(spell_msg) # Message the room with spell results
|
||||
|
||||
if is_in_combat(caster): # Spend action if in combat
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
caster.location.msg_contents(spell_msg) # Message the room with spell results
|
||||
|
||||
if is_in_combat(caster): # Spend action if in combat
|
||||
spend_action(caster, 1, action_name="cast")
|
||||
|
||||
|
||||
|
||||
def spell_attack(caster, spell_name, targets, cost, **kwargs):
|
||||
"""
|
||||
Spell that deals damage in combat. Similar to resolve_attack.
|
||||
|
|
@ -1123,14 +1172,14 @@ def spell_attack(caster, spell_name, targets, cost, **kwargs):
|
|||
attacked once.
|
||||
"""
|
||||
spell_msg = "%s casts %s!" % (caster, spell_name)
|
||||
|
||||
|
||||
atkname_single = "The spell"
|
||||
atkname_plural = "spells"
|
||||
min_damage = 10
|
||||
max_damage = 20
|
||||
accuracy = 0
|
||||
attack_count = 1
|
||||
|
||||
|
||||
# Retrieve some variables from kwargs, if present
|
||||
if "attack_name" in kwargs:
|
||||
atkname_single = kwargs["attack_name"][0]
|
||||
|
|
@ -1142,7 +1191,7 @@ def spell_attack(caster, spell_name, targets, cost, **kwargs):
|
|||
accuracy = kwargs["accuracy"]
|
||||
if "attack_count" in kwargs:
|
||||
attack_count = kwargs["attack_count"]
|
||||
|
||||
|
||||
to_attack = []
|
||||
# If there are more attacks than targets given, attack first target multiple times
|
||||
if len(targets) < attack_count:
|
||||
|
|
@ -1152,24 +1201,23 @@ def spell_attack(caster, spell_name, targets, cost, **kwargs):
|
|||
to_attack.insert(0, targets[0])
|
||||
else:
|
||||
to_attack = to_attack + targets
|
||||
|
||||
|
||||
|
||||
# Set up dictionaries to track number of hits and total damage
|
||||
total_hits = {}
|
||||
total_damage = {}
|
||||
for fighter in targets:
|
||||
total_hits.update({fighter:0})
|
||||
total_damage.update({fighter:0})
|
||||
|
||||
total_hits.update({fighter: 0})
|
||||
total_damage.update({fighter: 0})
|
||||
|
||||
# Resolve attack for each target
|
||||
for fighter in to_attack:
|
||||
attack_value = randint(1, 100) + accuracy # Spell attack roll
|
||||
attack_value = randint(1, 100) + accuracy # Spell attack roll
|
||||
defense_value = get_defense(caster, fighter)
|
||||
if attack_value >= defense_value:
|
||||
spell_dmg = randint(min_damage, max_damage) # Get spell damage
|
||||
spell_dmg = randint(min_damage, max_damage) # Get spell damage
|
||||
total_hits[fighter] += 1
|
||||
total_damage[fighter] += spell_dmg
|
||||
|
||||
|
||||
for fighter in targets:
|
||||
# Construct combat message
|
||||
if total_hits[fighter] == 0:
|
||||
|
|
@ -1178,22 +1226,27 @@ def spell_attack(caster, spell_name, targets, cost, **kwargs):
|
|||
attack_count_str = atkname_single + " hits"
|
||||
if total_hits[fighter] > 1:
|
||||
attack_count_str = "%i %s hit" % (total_hits[fighter], atkname_plural)
|
||||
spell_msg += " %s %s for %i damage!" % (attack_count_str, fighter, total_damage[fighter])
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
caster.location.msg_contents(spell_msg) # Message the room with spell results
|
||||
|
||||
spell_msg += " %s %s for %i damage!" % (
|
||||
attack_count_str,
|
||||
fighter,
|
||||
total_damage[fighter],
|
||||
)
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
caster.location.msg_contents(spell_msg) # Message the room with spell results
|
||||
|
||||
for fighter in targets:
|
||||
# Apply damage
|
||||
apply_damage(fighter, total_damage[fighter])
|
||||
# If fighter HP is reduced to 0 or less, call at_defeat.
|
||||
if fighter.db.hp <= 0:
|
||||
at_defeat(fighter)
|
||||
|
||||
if is_in_combat(caster): # Spend action if in combat
|
||||
|
||||
if is_in_combat(caster): # Spend action if in combat
|
||||
spend_action(caster, 1, action_name="cast")
|
||||
|
||||
|
||||
|
||||
def spell_conjure(caster, spell_name, targets, cost, **kwargs):
|
||||
"""
|
||||
Spell that creates an object.
|
||||
|
|
@ -1207,11 +1260,11 @@ def spell_conjure(caster, spell_name, targets, cost, **kwargs):
|
|||
you may want to modify it to use the spawner (in evennia.utils.spawner)
|
||||
instead of creating objects directly.
|
||||
"""
|
||||
|
||||
|
||||
obj_key = "a nondescript object"
|
||||
obj_desc = "A perfectly generic object."
|
||||
obj_typeclass = "evennia.objects.objects.DefaultObject"
|
||||
|
||||
|
||||
# Retrieve some variables from kwargs, if present
|
||||
if "obj_key" in kwargs:
|
||||
obj_key = kwargs["obj_key"]
|
||||
|
|
@ -1219,14 +1272,19 @@ def spell_conjure(caster, spell_name, targets, cost, **kwargs):
|
|||
obj_desc = kwargs["obj_desc"]
|
||||
if "obj_typeclass" in kwargs:
|
||||
obj_typeclass = kwargs["obj_typeclass"]
|
||||
|
||||
conjured_obj = create_object(obj_typeclass, key=obj_key, location=caster.location) # Create object
|
||||
conjured_obj.db.desc = obj_desc # Add object desc
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
|
||||
conjured_obj = create_object(
|
||||
obj_typeclass, key=obj_key, location=caster.location
|
||||
) # Create object
|
||||
conjured_obj.db.desc = obj_desc # Add object desc
|
||||
|
||||
caster.db.mp -= cost # Deduct MP cost
|
||||
|
||||
# Message the room to announce the creation of the object
|
||||
caster.location.msg_contents("%s casts %s, and %s appears!" % (caster, spell_name, conjured_obj))
|
||||
caster.location.msg_contents(
|
||||
"%s casts %s, and %s appears!" % (caster, spell_name, conjured_obj)
|
||||
)
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -1272,19 +1330,44 @@ dictionary.
|
|||
"""
|
||||
|
||||
SPELLS = {
|
||||
"magic missile":{"spellfunc":spell_attack, "target":"otherchar", "cost":3, "noncombat_spell":False, "max_targets":3,
|
||||
"attack_name":("A bolt", "bolts"), "damage_range":(4, 7), "accuracy":999, "attack_count":3},
|
||||
|
||||
"flame shot":{"spellfunc":spell_attack, "target":"otherchar", "cost":3, "noncombat_spell":False,
|
||||
"attack_name":("A jet of flame", "jets of flame"), "damage_range":(25, 35)},
|
||||
|
||||
"cure wounds":{"spellfunc":spell_healing, "target":"anychar", "cost":5},
|
||||
|
||||
"mass cure wounds":{"spellfunc":spell_healing, "target":"anychar", "cost":10, "max_targets": 5},
|
||||
|
||||
"full heal":{"spellfunc":spell_healing, "target":"anychar", "cost":12, "healing_range":(100, 100)},
|
||||
|
||||
"cactus conjuration":{"spellfunc":spell_conjure, "target":"none", "cost":2, "combat_spell":False,
|
||||
"obj_key":"a cactus", "obj_desc":"An ordinary green cactus with little spines."}
|
||||
"magic missile": {
|
||||
"spellfunc": spell_attack,
|
||||
"target": "otherchar",
|
||||
"cost": 3,
|
||||
"noncombat_spell": False,
|
||||
"max_targets": 3,
|
||||
"attack_name": ("A bolt", "bolts"),
|
||||
"damage_range": (4, 7),
|
||||
"accuracy": 999,
|
||||
"attack_count": 3,
|
||||
},
|
||||
"flame shot": {
|
||||
"spellfunc": spell_attack,
|
||||
"target": "otherchar",
|
||||
"cost": 3,
|
||||
"noncombat_spell": False,
|
||||
"attack_name": ("A jet of flame", "jets of flame"),
|
||||
"damage_range": (25, 35),
|
||||
},
|
||||
"cure wounds": {"spellfunc": spell_healing, "target": "anychar", "cost": 5},
|
||||
"mass cure wounds": {
|
||||
"spellfunc": spell_healing,
|
||||
"target": "anychar",
|
||||
"cost": 10,
|
||||
"max_targets": 5,
|
||||
},
|
||||
"full heal": {
|
||||
"spellfunc": spell_healing,
|
||||
"target": "anychar",
|
||||
"cost": 12,
|
||||
"healing_range": (100, 100),
|
||||
},
|
||||
"cactus conjuration": {
|
||||
"spellfunc": spell_conjure,
|
||||
"target": "none",
|
||||
"cost": 2,
|
||||
"combat_spell": False,
|
||||
"obj_key": "a cactus",
|
||||
"obj_desc": "An ordinary green cactus with little spines.",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,8 +110,8 @@ OPTIONS
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 2 # Number of actions allowed per turn
|
||||
TURN_TIMEOUT = 30 # Time before turns automatically end, in seconds
|
||||
ACTIONS_PER_TURN = 2 # Number of actions allowed per turn
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -119,6 +119,7 @@ COMBAT FUNCTIONS START HERE
|
|||
----------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
|
||||
def roll_init(character):
|
||||
"""
|
||||
Rolls a number between 1-1000 to determine initiative.
|
||||
|
|
@ -241,6 +242,7 @@ def apply_damage(defender, damage):
|
|||
if defender.db.hp <= 0:
|
||||
defender.db.hp = 0
|
||||
|
||||
|
||||
def at_defeat(defeated):
|
||||
"""
|
||||
Announces the defeat of a fighter in combat.
|
||||
|
|
@ -256,6 +258,7 @@ def at_defeat(defeated):
|
|||
"""
|
||||
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.
|
||||
|
|
@ -278,16 +281,22 @@ def resolve_attack(attacker, defender, attack_type, attack_value=None, defense_v
|
|||
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))
|
||||
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))
|
||||
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.
|
||||
|
|
@ -310,7 +319,8 @@ def get_range(obj1, obj2):
|
|||
return None
|
||||
# Return the range between the two objects.
|
||||
return obj1.db.combat_range[obj2]
|
||||
|
||||
|
||||
|
||||
def distance_inc(mover, target):
|
||||
"""
|
||||
Function that increases distance in range field between mover and target.
|
||||
|
|
@ -325,7 +335,8 @@ def distance_inc(mover, target):
|
|||
if get_range(mover, target) > 2:
|
||||
target.db.combat_range[mover] = 2
|
||||
mover.db.combat_range[target] = 2
|
||||
|
||||
|
||||
|
||||
def approach(mover, target):
|
||||
"""
|
||||
Manages a character's whole approach, including changes in ranges to other characters.
|
||||
|
|
@ -339,6 +350,7 @@ def approach(mover, target):
|
|||
target than the mover is. The mover will also move away from anything they started
|
||||
out close to.
|
||||
"""
|
||||
|
||||
def distance_dec(mover, target):
|
||||
"""
|
||||
Helper function that decreases distance in range field between mover and target.
|
||||
|
|
@ -360,7 +372,7 @@ def approach(mover, target):
|
|||
thing.db.combat_range[mover] = thing.db.combat_range[target]
|
||||
|
||||
contents = mover.location.contents
|
||||
|
||||
|
||||
for thing in contents:
|
||||
if thing != mover and thing != target:
|
||||
# Move closer to each object closer to the target than you.
|
||||
|
|
@ -372,6 +384,7 @@ def approach(mover, target):
|
|||
# Lastly, move closer to your target.
|
||||
distance_dec(mover, target)
|
||||
|
||||
|
||||
def withdraw(mover, target):
|
||||
"""
|
||||
Manages a character's whole withdrawal, including changes in ranges to other characters.
|
||||
|
|
@ -387,11 +400,13 @@ def withdraw(mover, target):
|
|||
"""
|
||||
|
||||
contents = mover.location.contents
|
||||
|
||||
|
||||
for thing in contents:
|
||||
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.
|
||||
if get_range(mover, thing) >= get_range(target, thing) and get_range(mover, thing) < get_range(mover, target):
|
||||
if get_range(mover, thing) >= get_range(target, thing) and get_range(
|
||||
mover, thing
|
||||
) < get_range(mover, target):
|
||||
distance_inc(mover, thing)
|
||||
# Move away from anything your target is engaged with
|
||||
if get_range(target, thing) == 0:
|
||||
|
|
@ -402,6 +417,7 @@ def withdraw(mover, target):
|
|||
# Then, move away from your target.
|
||||
distance_inc(mover, target)
|
||||
|
||||
|
||||
def combat_cleanup(character):
|
||||
"""
|
||||
Cleans up all the temporary combat-related attributes on a character.
|
||||
|
|
@ -460,7 +476,7 @@ def spend_action(character, actions, action_name=None):
|
|||
"""
|
||||
if action_name:
|
||||
character.db.combat_lastaction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
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.
|
||||
|
|
@ -468,6 +484,7 @@ def spend_action(character, actions, action_name=None):
|
|||
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.
|
||||
|
||||
|
||||
def combat_status_message(fighter):
|
||||
"""
|
||||
Sends a message to a player with their current HP and
|
||||
|
|
@ -478,16 +495,16 @@ def combat_status_message(fighter):
|
|||
fighter.db.hp = 100
|
||||
fighter.db.max_hp = 100
|
||||
|
||||
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):
|
||||
fighter.msg(status_msg)
|
||||
return
|
||||
|
||||
|
||||
engaged_obj = []
|
||||
reach_obj = []
|
||||
range_obj = []
|
||||
|
||||
|
||||
for thing in fighter.db.combat_range:
|
||||
if thing != fighter:
|
||||
if fighter.db.combat_range[thing] == 0:
|
||||
|
|
@ -496,16 +513,17 @@ def combat_status_message(fighter):
|
|||
reach_obj.append(thing)
|
||||
if fighter.db.combat_range[thing] > 1:
|
||||
range_obj.append(thing)
|
||||
|
||||
|
||||
if engaged_obj:
|
||||
status_msg += "|/Engaged targets: %s" % ", ".join(obj.key for obj in engaged_obj)
|
||||
if reach_obj:
|
||||
status_msg += "|/Reach targets: %s" % ", ".join(obj.key for obj in reach_obj)
|
||||
if range_obj:
|
||||
status_msg += "|/Ranged targets: %s" % ", ".join(obj.key for obj in range_obj)
|
||||
|
||||
|
||||
fighter.msg(status_msg)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
SCRIPTS START HERE
|
||||
|
|
@ -545,7 +563,7 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
|
||||
# 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)
|
||||
|
|
@ -557,7 +575,7 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
|
||||
# Announce the turn order.
|
||||
self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters))
|
||||
|
||||
|
||||
# Start first fighter's turn.
|
||||
self.start_turn(self.db.fighters[0])
|
||||
|
||||
|
|
@ -577,19 +595,23 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
"""
|
||||
Called once every self.interval seconds.
|
||||
"""
|
||||
currentchar = self.db.fighters[self.db.turn] # Note the current character in the turn order.
|
||||
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.
|
||||
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):
|
||||
"""
|
||||
Initializes range values for an object at the start of a fight.
|
||||
|
|
@ -603,14 +625,14 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
for thing in objectlist:
|
||||
# Object always at distance 0 from itself
|
||||
if thing == to_init:
|
||||
rangedict.update({thing:0})
|
||||
rangedict.update({thing: 0})
|
||||
else:
|
||||
if thing.destination or to_init.destination:
|
||||
# Start exits at range 2 to put them at the 'edges'
|
||||
rangedict.update({thing:2})
|
||||
rangedict.update({thing: 2})
|
||||
else:
|
||||
# Start objects at range 1 from other objects
|
||||
rangedict.update({thing:1})
|
||||
rangedict.update({thing: 1})
|
||||
to_init.db.combat_range = rangedict
|
||||
|
||||
def join_rangefield(self, to_init, anchor_obj=None, add_distance=0):
|
||||
|
|
@ -630,15 +652,15 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
contents.remove(to_init)
|
||||
# If no anchor object given, pick one in the room at random.
|
||||
if not anchor_obj:
|
||||
anchor_obj = contents[randint(0, (len(contents)-1))]
|
||||
anchor_obj = contents[randint(0, (len(contents) - 1))]
|
||||
# Copy the range values from the anchor object.
|
||||
to_init.db.combat_range = anchor_obj.db.combat_range
|
||||
# Add the new object to everyone else's ranges.
|
||||
for thing in contents:
|
||||
new_objects_range = thing.db.combat_range[anchor_obj]
|
||||
thing.db.combat_range.update({to_init:new_objects_range})
|
||||
thing.db.combat_range.update({to_init: new_objects_range})
|
||||
# Set the new object's range to itself to 0.
|
||||
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.
|
||||
for n in range(add_distance):
|
||||
withdraw(to_init, anchor_obj)
|
||||
|
|
@ -651,8 +673,12 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
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_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):
|
||||
|
|
@ -681,7 +707,9 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
# 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
|
||||
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!")
|
||||
|
|
@ -693,7 +721,9 @@ class TBRangeTurnHandler(DefaultScript):
|
|||
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
|
||||
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
|
||||
|
|
@ -794,6 +824,7 @@ class TBRangeCharacter(DefaultCharacter):
|
|||
return False
|
||||
return True
|
||||
|
||||
|
||||
class TBRangeObject(DefaultObject):
|
||||
"""
|
||||
An object that is assigned range values in combat. Getting, giving, and dropping
|
||||
|
|
@ -801,6 +832,7 @@ class TBRangeObject(DefaultObject):
|
|||
must be next to your target to give them something, and can only interact with
|
||||
objects on your own turn.
|
||||
"""
|
||||
|
||||
def at_before_drop(self, dropper):
|
||||
"""
|
||||
Called by the default `drop` command before this object has been
|
||||
|
|
@ -865,7 +897,7 @@ class TBRangeObject(DefaultObject):
|
|||
"""
|
||||
# Restrictions for getting in combat
|
||||
if is_in_combat(getter):
|
||||
if not is_turn(getter): # Not your turn
|
||||
if not is_turn(getter): # Not your turn
|
||||
getter.msg("You can only get things on your turn!")
|
||||
return False
|
||||
if get_range(self, getter) > 0: # Too far away
|
||||
|
|
@ -921,11 +953,13 @@ class TBRangeObject(DefaultObject):
|
|||
"""
|
||||
# Restrictions for giving in combat
|
||||
if is_in_combat(giver):
|
||||
if not is_turn(giver): # Not your turn
|
||||
if not is_turn(giver): # Not your turn
|
||||
giver.msg("You can only give things on your turn!")
|
||||
return False
|
||||
if get_range(giver, getter) > 0: # Too far away from target
|
||||
giver.msg("You aren't close enough to give things to %s! (see: help approach)" % getter)
|
||||
giver.msg(
|
||||
"You aren't close enough to give things to %s! (see: help approach)" % getter
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
@ -949,6 +983,7 @@ class TBRangeObject(DefaultObject):
|
|||
if is_in_combat(giver):
|
||||
spend_action(giver, 1, action_name="give") # Use up one action.
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
COMMANDS START HERE
|
||||
|
|
@ -967,6 +1002,7 @@ class CmdFight(Command):
|
|||
fight is added to combat, and a turn order is randomly rolled.
|
||||
When it's your turn, you can attack other characters.
|
||||
"""
|
||||
|
||||
key = "fight"
|
||||
help_category = "combat"
|
||||
|
||||
|
|
@ -1044,15 +1080,19 @@ class CmdAttack(Command):
|
|||
if attacker == defender: # Target and attacker are the same
|
||||
self.caller.msg("You can't attack yourself!")
|
||||
return
|
||||
|
||||
|
||||
if not get_range(attacker, defender) == 0: # Target isn't in melee
|
||||
self.caller.msg("%s is too far away to attack - you need to get closer! (see: help approach)" % defender)
|
||||
self.caller.msg(
|
||||
"%s is too far away to attack - you need to get closer! (see: help approach)"
|
||||
% defender
|
||||
)
|
||||
return
|
||||
|
||||
"If everything checks out, call the attack resolving function."
|
||||
resolve_attack(attacker, defender, "melee")
|
||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||
|
||||
|
||||
|
||||
class CmdShoot(Command):
|
||||
"""
|
||||
Attacks another character from range.
|
||||
|
|
@ -1099,22 +1139,26 @@ class CmdShoot(Command):
|
|||
if attacker == defender: # Target and attacker are the same
|
||||
self.caller.msg("You can't attack yourself!")
|
||||
return
|
||||
|
||||
|
||||
# Test to see if there are any nearby enemy targets.
|
||||
in_melee = []
|
||||
for target in attacker.db.combat_range:
|
||||
# Object is engaged and has HP
|
||||
if 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:
|
||||
self.caller.msg("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))
|
||||
self.caller.msg(
|
||||
"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)
|
||||
)
|
||||
return
|
||||
|
||||
"If everything checks out, call the attack resolving function."
|
||||
resolve_attack(attacker, defender, "ranged")
|
||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||
|
||||
|
||||
|
||||
class CmdApproach(Command):
|
||||
"""
|
||||
Approaches an object.
|
||||
|
|
@ -1157,8 +1201,8 @@ class CmdApproach(Command):
|
|||
if mover == target: # Target and mover are the same
|
||||
self.caller.msg("You can't move toward yourself!")
|
||||
return
|
||||
|
||||
if get_range(mover, target) <= 0: # Already engaged with target
|
||||
|
||||
if get_range(mover, target) <= 0: # Already engaged with target
|
||||
self.caller.msg("You're already next to that target!")
|
||||
return
|
||||
|
||||
|
|
@ -1166,7 +1210,8 @@ class CmdApproach(Command):
|
|||
approach(mover, target)
|
||||
mover.location.msg_contents("%s moves toward %s." % (mover, target))
|
||||
spend_action(self.caller, 1, action_name="move") # Use up one action.
|
||||
|
||||
|
||||
|
||||
class CmdWithdraw(Command):
|
||||
"""
|
||||
Moves away from an object.
|
||||
|
|
@ -1208,8 +1253,8 @@ class CmdWithdraw(Command):
|
|||
if mover == target: # Target and mover are the same
|
||||
self.caller.msg("You can't move away from yourself!")
|
||||
return
|
||||
|
||||
if mover.db.combat_range[target] >= 3: # Already at maximum distance
|
||||
|
||||
if mover.db.combat_range[target] >= 3: # Already at maximum distance
|
||||
self.caller.msg("You're as far as you can get from that target!")
|
||||
return
|
||||
|
||||
|
|
@ -1246,8 +1291,10 @@ class CmdPass(Command):
|
|||
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.
|
||||
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):
|
||||
|
|
@ -1279,7 +1326,7 @@ class CmdDisengage(Command):
|
|||
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.
|
||||
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.
|
||||
|
|
@ -1313,6 +1360,7 @@ class CmdRest(Command):
|
|||
You'll probably want to replace this with your own system for recovering HP.
|
||||
"""
|
||||
|
||||
|
||||
class CmdStatus(Command):
|
||||
"""
|
||||
Gives combat information.
|
||||
|
|
@ -1330,7 +1378,7 @@ class CmdStatus(Command):
|
|||
def func(self):
|
||||
"This performs the actual command."
|
||||
combat_status_message(self.caller)
|
||||
|
||||
|
||||
|
||||
class CmdCombatHelp(CmdHelp):
|
||||
"""
|
||||
|
|
@ -1344,19 +1392,22 @@ class CmdCombatHelp(CmdHelp):
|
|||
This will search for help on commands and other
|
||||
topics related to the game.
|
||||
"""
|
||||
|
||||
# Just like the default help command, but will give quick
|
||||
# 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 an engaged target, attempting to deal damage.|/" +
|
||||
"|wShoot:|n Attack from a distance, if not engaged with other fighters.|/" +
|
||||
"|wApproach:|n Move one step cloer to a target.|/" +
|
||||
"|wWithdraw:|n Move one step away from a target.|/" +
|
||||
"|wPass:|n Pass your turn without further action.|/" +
|
||||
"|wStatus:|n View current HP and ranges to other targets.|/" +
|
||||
"|wDisengage:|n End your turn and attempt to end combat.|/")
|
||||
self.caller.msg(
|
||||
"Available combat commands:|/"
|
||||
+ "|wAttack:|n Attack an engaged target, attempting to deal damage.|/"
|
||||
+ "|wShoot:|n Attack from a distance, if not engaged with other fighters.|/"
|
||||
+ "|wApproach:|n Move one step cloer to a target.|/"
|
||||
+ "|wWithdraw:|n Move one step away from a target.|/"
|
||||
+ "|wPass:|n Pass your turn without further action.|/"
|
||||
+ "|wStatus:|n View current HP and ranges to other targets.|/"
|
||||
+ "|wDisengage:|n End your turn and attempt to end combat.|/"
|
||||
)
|
||||
else:
|
||||
super(CmdCombatHelp, self).func() # Call the default help command
|
||||
|
||||
|
|
@ -1365,6 +1416,7 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
"""
|
||||
This command set includes all the commmands used in the battle system.
|
||||
"""
|
||||
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
|
|
@ -1380,4 +1432,4 @@ class BattleCmdSet(default_cmds.CharacterCmdSet):
|
|||
self.add(CmdApproach())
|
||||
self.add(CmdWithdraw())
|
||||
self.add(CmdStatus())
|
||||
self.add(CmdCombatHelp())
|
||||
self.add(CmdCombatHelp())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue