mirror of
https://github.com/evennia/evennia.git
synced 2026-03-26 09:46:32 +01:00
New functions + Change install method
Created a number of new functions for redundant code, including to initialize characters for combat and spending actions - also changed the suggested method of import, having the user import and add the commands. Adding the command set to BattleCharacter at creation was not persistent across reloads.
This commit is contained in:
parent
4e3b59e486
commit
9e6627aabd
1 changed files with 97 additions and 69 deletions
|
|
@ -16,7 +16,8 @@ is easily extensible and can be used as the foundation for implementing
|
|||
the rules from your turn-based tabletop game of choice or making your
|
||||
own battle system.
|
||||
|
||||
To install and test, import this module's BattleCharacter object:
|
||||
To install and test, import this module's BattleCharacter object into
|
||||
your game's character.py module:
|
||||
|
||||
from evennia.contrib.turnbattle import BattleCharacter
|
||||
|
||||
|
|
@ -24,6 +25,21 @@ And change your game's character typeclass to inherit from BattleCharacter
|
|||
instead of the default:
|
||||
|
||||
class Character(BattleCharacter):
|
||||
|
||||
Next, import the combat commands into your default_cmdsets.py module:
|
||||
|
||||
from evennia.contrib.turnbattle import CmdFight, CmdAttack, CmdRest, CmdPass, CmdDisengage
|
||||
|
||||
And add the commands to your default command set:
|
||||
|
||||
#
|
||||
# any commands you add below will overload the default ones.
|
||||
#
|
||||
self.add(CmdFight())
|
||||
self.add(CmdAttack())
|
||||
self.add(CmdRest())
|
||||
self.add(CmdPass())
|
||||
self.add(CmdDisengage())
|
||||
|
||||
This module is meant to be heavily expanded on, so you may want to copy it
|
||||
to your game's 'world' folder and modify it there rather than importing it
|
||||
|
|
@ -65,25 +81,6 @@ def roll_init(character):
|
|||
This way, characters with a higher dexterity will go first more often.
|
||||
"""
|
||||
|
||||
def start_turn(character):
|
||||
"""
|
||||
Readies a character for the start of their turn by replenishing their
|
||||
available actions and notifying them that their turn has come up.
|
||||
|
||||
Args:
|
||||
character (obj): Character to be readied.
|
||||
"""
|
||||
character.db.Combat_ActionsLeft = 1 # 1 action per turn.
|
||||
"""
|
||||
Here, you only get one action per turn, but you might want to allow more than
|
||||
one per turn, or even grant a number of actions based on a character's
|
||||
attributes. You can even add multiple different kinds of actions, I.E. actions
|
||||
separated for movement, by adding "character.db.Combat_MovesLeft = 3" or
|
||||
something similar.
|
||||
"""
|
||||
# Prompt the character for their turn and give some information.
|
||||
character.msg("|wIt's your turn! You have %i HP remaining.|n" % character.db.hp)
|
||||
|
||||
def get_attack(attacker, defender):
|
||||
"""
|
||||
Returns a value for an attack roll.
|
||||
|
|
@ -243,6 +240,29 @@ def is_turn(character):
|
|||
if character == currentchar:
|
||||
return True
|
||||
return False
|
||||
|
||||
def spend_action(character, actions, action_name=None):
|
||||
"""
|
||||
Spends a character's available combat actions and checks for end of turn.
|
||||
|
||||
Args:
|
||||
character (obj): Character spending the action
|
||||
actions (int) or 'all': Number of actions to spend, or 'all' to spend all actions
|
||||
|
||||
Kwargs:
|
||||
action_name (str or None): If a string is given, sets character's last action in
|
||||
combat to provided string
|
||||
"""
|
||||
if action_name:
|
||||
character.db.Combat_LastAction = action_name
|
||||
if actions == 'all': # If spending all actions
|
||||
character.db.Combat_ActionsLeft = 0 # Set actions to 0
|
||||
else:
|
||||
character.db.Combat_ActionsLeft -= actions # Use up actions.
|
||||
if character.db.Combat_ActionsLeft < 0:
|
||||
character.db.Combat_ActionsLeft = 0 # Can't have fewer than 0 actions
|
||||
character.db.Combat_TurnHandler.turn_end_check(character) # Signal potential end of turn.
|
||||
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
|
|
@ -261,7 +281,6 @@ class BattleCharacter(DefaultCharacter):
|
|||
Called once, when this object is first created. This is the
|
||||
normal hook to overload for most object types.
|
||||
"""
|
||||
self.cmdset.add('contrib.turnbattle.BattleCmdSet') # Add combat commands
|
||||
self.db.max_hp = 100 # Set maximum HP to 100
|
||||
self.db.hp = self.db.max_hp # Set current HP to maximum
|
||||
"""
|
||||
|
|
@ -295,24 +314,7 @@ class BattleCharacter(DefaultCharacter):
|
|||
self.caller.msg("You can't move, you've been defeated!")
|
||||
return False
|
||||
return True
|
||||
|
||||
class BattleCmdSet(default_cmds.CharacterCmdSet):
|
||||
"""
|
||||
Adds combat commands to the default command set.
|
||||
"""
|
||||
key = "BattleCmdSet"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"""
|
||||
Populates the cmdset
|
||||
"""
|
||||
super(default_cmds.CharacterCmdSet, self).at_cmdset_creation()
|
||||
self.add(CmdFight())
|
||||
self.add(CmdAttack())
|
||||
self.add(CmdRest())
|
||||
self.add(CmdPass())
|
||||
self.add(CmdDisengage())
|
||||
|
||||
"""
|
||||
----------------------------------------------------------------------------
|
||||
COMMANDS START HERE
|
||||
|
|
@ -401,9 +403,7 @@ class CmdAttack(Command):
|
|||
|
||||
"If everything checks out, call the attack resolving function."
|
||||
resolve_attack(attacker, defender)
|
||||
self.caller.db.Combat_LastAction = "attack"
|
||||
self.caller.db.Combat_ActionsLeft -= 1 # Use up one action.
|
||||
self.caller.db.Combat_TurnHandler.turn_end_check(self.caller) # Signal potential end of turn.
|
||||
spend_action(self.caller, 1, action_name="attack") # Use up one action.
|
||||
|
||||
class CmdPass(Command):
|
||||
"""
|
||||
|
|
@ -433,9 +433,7 @@ class CmdPass(Command):
|
|||
return
|
||||
|
||||
self.caller.location.msg_contents("%s takes no further action, passing the turn." % self.caller)
|
||||
self.caller.db.Combat_LastAction = "pass"
|
||||
self.caller.db.Combat_ActionsLeft = 0
|
||||
self.caller.db.Combat_TurnHandler.turn_end_check(self.caller) # Signal end of turn.
|
||||
spend_action(self.caller, 'all', action_name="pass") # Spend all remaining actions.
|
||||
|
||||
class CmdDisengage(Command):
|
||||
"""
|
||||
|
|
@ -466,9 +464,11 @@ class CmdDisengage(Command):
|
|||
return
|
||||
|
||||
self.caller.location.msg_contents("%s disengages, ready to stop fighting." % self.caller)
|
||||
self.caller.db.Combat_LastAction = "disengage" # This is checked by the turn handler to end combat if all disengage.
|
||||
self.caller.db.Combat_ActionsLeft = 0
|
||||
self.caller.db.Combat_TurnHandler.turn_end_check(self.caller) # Signal end of turn.
|
||||
spend_action(self.caller, 'all', action_name="disengage") # Spend all remaining actions.
|
||||
"""
|
||||
The action_name kwarg sets the character's last action to "disengage", which is checked by
|
||||
the turn handler script to see if all fighters have disengaged.
|
||||
"""
|
||||
|
||||
class CmdRest(Command):
|
||||
"""
|
||||
|
|
@ -523,23 +523,25 @@ class TurnHandler(DefaultScript):
|
|||
self.interval = 10 # Once every 10 seconds
|
||||
self.persistent = True
|
||||
self.db.fighters = []
|
||||
|
||||
# Add all fighters in the room with at least 1 HP to the combat."
|
||||
for object in self.obj.contents:
|
||||
if object.db.hp:
|
||||
self.db.fighters.append(object)
|
||||
|
||||
# Initialize each fighter for combat
|
||||
for fighter in self.db.fighters:
|
||||
combat_cleanup(fighter) #Clean up leftover combat attributes beforehand, just in case.
|
||||
fighter.db.Combat_ActionsLeft = 1 #Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
||||
fighter.db.Combat_TurnHandler = self #Add a reference to this script to the character
|
||||
fighter.db.Combat_LastAction = "null" #Track last action taken in combat
|
||||
self.initialize_for_combat(fighter)
|
||||
|
||||
# Roll initiative and sort the list of fighters depending on who rolls highest to determine turn order.
|
||||
# The initiative roll is determined by the roll_init function and can be customized easily.
|
||||
ordered_by_roll = sorted(self.db.fighters, key=roll_init, reverse=True)
|
||||
self.db.fighters = ordered_by_roll
|
||||
|
||||
# Announce the turn order.
|
||||
self.obj.msg_contents("Turn order is: %s " % ", ".join(obj.key for obj in self.db.fighters))
|
||||
"Set up the current turn and turn timeout delay."
|
||||
|
||||
#Set up the current turn and turn timeout delay.
|
||||
self.db.turn = 0
|
||||
self.db.timer = 30 # 30 seconds
|
||||
|
||||
|
|
@ -563,21 +565,37 @@ class TurnHandler(DefaultScript):
|
|||
|
||||
# Force current character to disengage if timer runs out.
|
||||
if self.db.timer <= 0:
|
||||
currentchar.db.Combat_LastAction = "disengage" # Set last action to 'disengage'
|
||||
currentchar.db.Combat_ActionsLeft = 0 # Set actions remaining to 0
|
||||
self.obj.msg_contents("%s's turn timed out!" % currentchar)
|
||||
self.next_turn()
|
||||
|
||||
def turn_end_check(self, character):
|
||||
spend_action(currentchar, 'all', action_name="disengage") # Spend all remaining actions.
|
||||
|
||||
|
||||
def initialize_for_combat(self, character):
|
||||
"""
|
||||
Tests to see if a character's turn is over, and cycles to the next turn if it is.
|
||||
Prepares a character for combat when starting or entering a fight.
|
||||
"""
|
||||
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
|
||||
character.db.Combat_ActionsLeft = 0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
||||
character.db.Combat_TurnHandler = self # Add a reference to this turn handler script to the character
|
||||
character.db.Combat_LastAction = "null" # Track last action taken in combat
|
||||
|
||||
def start_turn(self, character):
|
||||
"""
|
||||
Readies a character for the start of their turn by replenishing their
|
||||
available actions and notifying them that their turn has come up.
|
||||
|
||||
Args:
|
||||
character (obj): Character to test for end of turn
|
||||
character (obj): Character to be readied.
|
||||
"""
|
||||
if not character.db.Combat_ActionsLeft: # Character has no actions remaining
|
||||
self.next_turn()
|
||||
return
|
||||
character.db.Combat_ActionsLeft = 1 # 1 action per turn.
|
||||
"""
|
||||
Here, you only get one action per turn, but you might want to allow more than
|
||||
one per turn, or even grant a number of actions based on a character's
|
||||
attributes. You can even add multiple different kinds of actions, I.E. actions
|
||||
separated for movement, by adding "character.db.Combat_MovesLeft = 3" or
|
||||
something similar.
|
||||
"""
|
||||
# Prompt the character for their turn and give some information.
|
||||
character.msg("|wIt's your turn! You have %i HP remaining.|n" % character.db.hp)
|
||||
|
||||
def next_turn(self):
|
||||
"""
|
||||
|
|
@ -613,9 +631,22 @@ class TurnHandler(DefaultScript):
|
|||
if self.db.turn > len(self.db.fighters) - 1:
|
||||
self.db.turn = 0 # Go back to the first in the turn order once you reach the end.
|
||||
newchar = self.db.fighters[self.db.turn]
|
||||
self.db.timer = 30 # Reset the timer.
|
||||
# Reset the timer.
|
||||
self.db.timer = 30 + self.interval
|
||||
self.force_repeat()
|
||||
self.obj.msg_contents("%s's turn ends - %s's turn begins!" % (currentchar, newchar))
|
||||
start_turn(newchar) # Start the new character's turn.
|
||||
self.start_turn(newchar) # Start the new character's turn.
|
||||
|
||||
def turn_end_check(self, character):
|
||||
"""
|
||||
Tests to see if a character's turn is over, and cycles to the next turn if it is.
|
||||
|
||||
Args:
|
||||
character (obj): Character to test for end of turn
|
||||
"""
|
||||
if not character.db.Combat_ActionsLeft: # Character has no actions remaining
|
||||
self.next_turn()
|
||||
return
|
||||
|
||||
def join_fight(self, character):
|
||||
"""
|
||||
|
|
@ -624,12 +655,9 @@ class TurnHandler(DefaultScript):
|
|||
Args:
|
||||
character (obj): Character to be added to the fight.
|
||||
"""
|
||||
# Inserts the fighter to the turn order behind whoever's turn it currently is.
|
||||
# Inserts the fighter to the turn order, right behind whoever's turn it currently is.
|
||||
self.db.fighters.insert(self.db.turn, character)
|
||||
# Tick the turn counter forward one to compensate.
|
||||
self.db.turn += 1
|
||||
# Initialize the character like you do at the start.
|
||||
combat_cleanup(fighter) # Clean up leftover combat attributes beforehand, just in case.
|
||||
fighter.db.Combat_ActionsLeft = 0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
|
||||
fighter.db.Combat_TurnHandler = self # Add a reference to this scrip to the character
|
||||
fighter.db.Combat_LastAction = "null" # Track last action taken in combat
|
||||
self.initialize_for_combat(character)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue