evennia.contrib.tutorials.evadventure.combat_turnbased

EvAdventure turn-based combat

This implements a turn-based combat style, where both sides have a little longer time to choose their next action. If they don’t react before a timer runs out, the previous action will be repeated. This means that a ‘twitch’ style combat can be created using the same mechanism, by just speeding up each ‘turn’.

The combat is handled with a Script shared between all combatants; this tracks the state of combat and handles all timing elements.

Unlike in base _Knave_, the MUD version’s combat is simultaneous; everyone plans and executes their turns simultaneously with minimum downtime.

This version is simplified to not worry about things like optimal range etc. So a bow can be used the same as a sword in battle. One could add a 1D range mechanism to add more strategy by requiring optimizal positioning.

The combat is controlled through a menu:

——————- main menu Combat

You have 30 seconds to choose your next action. If you don’t decide, you will hesitate and do nothing. Available actions:

1. [A]ttack/[C]ast spell at <target> using your equipped weapon/spell 3. Make [S]tunt <target/yourself> (gain/give advantage/disadvantage for future attacks) 4. S[W]ap weapon / spell rune 5. [U]se <item> 6. [F]lee/disengage (takes two turns) 7. [B]lock <target> from fleeing 8. [H]esitate/Do nothing

You can also use say/emote between rounds. As soon as all combatants have made their choice (or time out), the round will be resolved simultaneusly.

——————– attack/cast spell submenu

Choose the target of your attack/spell: 0: Yourself 3: <enemy 3> (wounded) 1: <enemy 1> (hurt) 2: <enemy 2> (unharmed)

——————- make stunt submenu

Stunts are special actions that don’t cause damage but grant advantage for you or an ally for future attacks - or grant disadvantage to your enemy’s future attacks. The effects of stunts start to apply next round. The effect does not stack, can only be used once and must be taken advantage of within 5 rounds.

Choose stunt: 1: Trip <target> (give disadvantage DEX) 2: Feint <target> (get advantage DEX against target) 3: …

——————– make stunt target submenu

Choose the target of your stunt: 0: Yourself 3: <combatant 3> (wounded) 1: <combatant 1> (hurt) 2: <combatant 2> (unharmed)

——————- swap weapon or spell run

Choose the item to wield. 1: <item1> 2: <item2> (two hands) 3: <item3> 4: …

——————- use item

Choose item to use. 1: Healing potion (+1d6 HP) 2: Magic pebble (gain advantage, 1 use) 3: Potion of glue (give disadvantage to target)

——————- Hesitate/Do nothing

You hang back, passively defending.

——————- Disengage

You retreat, getting ready to get out of combat. Use two times in a row to leave combat. You flee last in a round. If anyone Blocks your retreat, this counter resets.

——————- Block Fleeing

You move to block the escape route of an opponent. If you win a DEX challenge, you’ll negate the target’s disengage action(s).

Choose who to block: 1: <enemy 1> 2: <enemy 2> 3: …

exception evennia.contrib.tutorials.evadventure.combat_turnbased.CombatFailure[source]

Bases: RuntimeError

Some failure during actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction(combathandler, combatant)[source]

Bases: object

This is the base of a combat-action, like ‘attack’ Inherit from this to make new actions.

Note

We want to store initialized version of this objects in the CombatHandler (in order to track usages, time limits etc), so we need to make sure we can serialize it into an Attribute. See Attribute documentation for more about __serialize_dbobjs__ and __deserialize_dbobjs__.

key = 'Action'
desc = 'Option text'
aliases = []
help_text = 'Combat action to perform.'
next_menu_node = 'node_select_action'
max_uses = None
priority = 0
__init__(combathandler, combatant)[source]

Initialize self. See help(type(self)) for accurate signature.

msg(message, broadcast=True)[source]

Convenience route to the combathandler msg-sender mechanism.

Parameters

message (str) – Message to send; use $You() and $You(other.key) to refer to the combatant doing the action and other combatants, respectively.

get_help(*args, **kwargs)[source]

Allows to customize help message on the fly. By default, just returns .help_text.

can_use(*args, **kwargs)[source]

Determine if combatant can use this action. In this implementation, it fails if already used up all of a usage-limited action.

Parameters
  • *args – Any optional arguments.

  • **kwargs – Any optional keyword arguments.

Returns

tuple

(bool, motivation) - if not available, will describe why,

if available, should describe what the action does.

pre_use(*args, **kwargs)[source]

Called just before the main action.

use(*args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

post_use(*args, **kwargs)[source]

Called just after the action has been taken.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionAttack(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

A regular attack, using a wielded weapon. Depending on weapon type, this will be a ranged or melee attack.

key = 'Attack or Cast'
desc = '[A]ttack/[C]ast spell at <target>'
aliases = ('a', 'c', 'attack', 'cast')
help_text = 'Make an attack using your currently equipped weapon/spell rune'
next_menu_node = 'node_select_enemy_target'
priority = 1
use(defender, *args, **kwargs)[source]

Make an attack against a defender.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionStunt(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Perform a stunt. A stunt grants an advantage to yours or another player for their next action, or a disadvantage to yours or an enemy’s next action.

Note that while the check happens between the user and a target, another (the ‘beneficiary’ could still gain the effect. This allows for boosting allies or making them better defend against an enemy.

Note: We only count a use if the stunt is successful; they will still spend their turn, but won’t spend a use unless they succeed.

key = 'Perform a Stunt'
desc = 'Make [S]tunt against <target>'
aliases = ('s', 'stunt')
next_menu_node = 'node_select_enemy_target'
give_advantage = True
max_uses = 1
priority = -1
attack_type = 'dexterity'
defense_type = 'dexterity'
help_text = 'Perform a stunt against a target. This will give you an advantage or an enemy disadvantage on your next action.'
use(defender, *args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionUseItem(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Use an item in combat. This is meant for one-off or limited-use items, like potions, scrolls or wands. We offload the usage checks and usability to the item’s own hooks. It’s generated dynamically from the items in the character’s inventory (you could also consider using items in the room this way).

Each usable item results in one possible action.

It relies on the combat_* hooks on the item:

combat_get_help combat_can_use combat_pre_use combat_pre combat_post_use

key = 'Use Item'
desc = '[U]se item'
aliases = ('u', 'item', 'use item')
help_text = 'Use an item from your inventory.'
next_menu_node = 'node_select_friendly_target'
get_help(item, *args)[source]

Allows to customize help message on the fly. By default, just returns .help_text.

use(item, target, *args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

post_use(item, *args, **kwargs)[source]

Called just after the action has been taken.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionSwapWieldedWeaponOrSpell(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Swap Wielded weapon or spell.

key = 'Swap weapon/rune/shield'
desc = 'Swap currently wielded weapon, shield or spell-rune.'
aliases = ('s', 'swap', 'draw', 'swap weapon', 'draw weapon', 'swap rune', 'draw rune', 'swap spell', 'draw spell')
help_text = 'Draw a new weapon or spell-rune from your inventory, replacing your current loadout'
next_menu_node = 'node_select_wield_from_inventory'
use(_, item, *args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionFlee(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Fleeing/disengaging from combat means doing nothing but ‘running away’ for two turn. Unless someone attempts and succeeds in their ‘block’ action, you will leave combat by fleeing at the end of the second turn.

key = 'Flee/Disengage'
desc = '[F]lee/disengage from combat (takes two turns)'
aliases = ('d', 'disengage', 'flee')
next_menu_node = 'node_confirm_register_action'
help_text = 'Disengage from combat. Use successfully two times in a row to leave combat at the end of the second round. If someone Blocks you successfully, this counter is reset.'
priority = -5
use(*args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionBlock(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Blocking is, in this context, a way to counter an enemy’s ‘Flee/Disengage’ action.

key = 'Block'
desc = '[B]lock <target> from fleeing'
aliases = ('b', 'block', 'chase')
help_text = "Move to block a target from fleeing combat. If you succeed in a DEX vs DEX challenge, they don't get away."
next_menu_node = 'node_select_enemy_target'
priority = -1
attack_type = 'dexterity'
defense_type = 'dexterity'
use(fleeing_target, *args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionDoNothing(combathandler, combatant)[source]

Bases: evennia.contrib.tutorials.evadventure.combat_turnbased.CombatAction

Do nothing this turn.

key = 'Hesitate'
desc = 'Do [N]othing/Hesitate'
aliases = ('n', 'hesitate', 'nothing', 'do nothing')
help_text = 'Hold you position, doing nothing.'
next_menu_node = 'node_confirm_register_action'
post_action_text = '{combatant} does nothing this turn.'
use(*args, **kwargs)[source]

Main activation of the action. This happens simultaneously to other actions.

class evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureCombatHandler(*args, **kwargs)[source]

Bases: evennia.scripts.scripts.DefaultScript

This script is created when combat is initialized and stores a queue of all active participants.

It’s also possible to join (or leave) the fray later.

stunt_duration = 3
default_action_classes = [<class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionAttack'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionStunt'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionSwapWieldedWeaponOrSpell'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionUseItem'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionFlee'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionBlock'>, <class 'evennia.contrib.tutorials.evadventure.combat_turnbased.CombatActionDoNothing'>]
combatants

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
combatant_actions

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
action_queue

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
turn_stats

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
turn

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
advantage_matrix

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
disadvantage_matrix

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
fleeing_combatants

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
defeated_combatants

Attribute property descriptor. Allows for specifying Attributes as Django-like ‘fields’ on the class level. Note that while one can set a lock on the Attribute, there is no way to check said lock when accessing via the property - use the full AttributeHandler if you need to do access checks.

Example:

class Character(DefaultCharacter):
    foo = AttributeProperty(default="Bar")
at_script_creation()[source]

Only called once, when script is first created.

at_repeat(**kwargs)[source]

Called every self.interval seconds. The main tick of the script.

add_combatant(combatant, session=None)[source]

Add combatant to battle.

Parameters
  • combatant (Object) – The combatant to add.

  • session (Session, optional) – Session to use.

Notes

This adds them to the internal list and initiates all possible actions. If the combatant as an Attribute list custom_combat_actions containing CombatAction items, this will injected and if the .key matches, will replace the default action classes.

remove_combatant(combatant)[source]

Remove combatant from battle.

Parameters

combatant (Object) – The combatant to remove.

start_combat()[source]

Start the combat timer and get everyone going.

stop_combat()[source]

This is used to stop the combat immediately.

It can also be called from external systems, for example by monster AI can do this when only allied players remain.

get_enemy_targets(combatant, excluded=None, all_combatants=None)[source]

Get all valid targets the given combatant can target for an attack. This does not apply for ‘friendly’ targeting (like wanting to cast a heal on someone). We assume there are two types of combatants - PCs (player-controlled characters and NPCs (AI-controlled). Here, we assume npcs can never attack one another (or themselves)

For PCs to be able to target each other, the allow_pvp Attribute flag must be set on the current Room.

Parameters
  • combatant (Object) – The combatant looking for targets.

  • excluded (list, optional) – If given, these are not valid targets - this can be used to avoid friendly NPCs.

  • all_combatants (list, optional) – If given, use this list to get all combatants, instead of using self.combatants.

get_friendly_targets(combatant, extra=None, all_combatants=None)[source]

Get a list of all ‘friendly’ or neutral targets a combatant may target, including themselves.

Parameters
  • combatant (Object) – The combatant looking for targets.

  • extra (list, optional) – If given, these are additional targets that can be considered target for allied effects (could be used for a friendly NPC).

  • all_combatants (list, optional) – If given, use this list to get all combatants, instead of using self.combatants.

get_combat_summary(combatant)[source]

Get a summary of the current combat state from the perspective of a given combatant.

Parameters

combatant (Object) – The combatant to get the summary for

Returns

str – The summary.

Example

You (5/10 health) Foo (Hurt) [Running away - use ‘block’ to stop them!] Bar (Perfect health)

msg(message, combatant=None, broadcast=True)[source]

Central place for sending messages to combatants. This allows for adding any combat-specific text-decoration in one place.

Parameters
  • message (str) – The message to send.

  • combatant (Object) – The ‘You’ in the message, if any.

  • broadcast (bool) – If False, combatant must be included and will be the only one to see the message. If True, send to everyone in the location.

Notes

If combatant is given, use $You/you() markup to create a message that looks different depending on who sees it. Use $You(combatant_key) to refer to other combatants.

gain_advantage(combatant, target)[source]

Gain advantage against target. Spent by actions.

gain_disadvantage(combatant, target)[source]

Gain disadvantage against target. Spent by actions.

flee(combatant)[source]
unflee(combatant)[source]
register_action(combatant, action_key, *args, **kwargs)[source]

Register an action based on its .key.

Parameters
  • combatant (Object) – The one performing the action.

  • action_key (str) – The action to perform, by its .key.

  • *args – Arguments to pass to action.use.

  • **kwargs – Kwargs to pass to action.use.

get_available_actions(combatant, *args, **kwargs)[source]

Get only the actions available to a combatant.

Parameters
  • combatant (Object) – The combatant to get actions for.

  • *args – Passed to action.can_use()

  • **kwargs – Passed to action.can_use()

Returns

list

The initiated CombatAction instances available to the

combatant right now.

Note

We could filter this by .can_use return already here, but then it would just be removed from the menu. Instead we return all and use .can_use in the menu so we can include the option but gray it out.

exception DoesNotExist

Bases: evennia.scripts.scripts.DefaultScript.DoesNotExist

exception MultipleObjectsReturned

Bases: evennia.scripts.scripts.DefaultScript.MultipleObjectsReturned

path = 'evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureCombatHandler'
typename = 'EvAdventureCombatHandler'
evennia.contrib.tutorials.evadventure.combat_turnbased.node_confirm_register_action(caller, raw_string, **kwargs)[source]

Node where one can confirm registering the action or change one’s mind.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_select_enemy_target(caller, raw_string, **kwargs)[source]

Menu node allowing for selecting an enemy target among all combatants. This combines with all other actions.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_select_friendly_target(caller, raw_string, **kwargs)[source]

Menu node for selecting a friendly target among combatants (including oneself).

evennia.contrib.tutorials.evadventure.combat_turnbased.node_select_wield_from_inventory(caller, raw_string, **kwargs)[source]

Menu node allowing for wielding item(s) from inventory.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_select_use_item_from_inventory(caller, raw_string, **kwargs)[source]

Menu item allowing for using usable items (like potions) from inventory.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_select_action(caller, raw_string, **kwargs)[source]

Menu node for selecting a combat action.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_wait_turn(caller, raw_string, **kwargs)[source]

Menu node routed to waiting for the round to end (for everyone to choose their actions).

All menu actions route back to the same node. The CombatHandler will handle moving everyone back to the node_select_action node when the next round starts.

evennia.contrib.tutorials.evadventure.combat_turnbased.node_wait_start(caller, raw_string, **kwargs)[source]

Menu node entered when waiting for the combat to start. New players joining an existing combat will end up here until the previous round is over, at which point the combat handler will goto everyone to node_select_action.

evennia.contrib.tutorials.evadventure.combat_turnbased.join_combat(caller, *targets, session=None)[source]

Join or create a new combat involving caller and at least one target. The combat is started on the current room location - this means there can only be one combat in each room (this is not hardcoded in the combat per-se, but it makes sense for this implementation).

Parameters
  • caller (Object) – The one starting the combat.

  • *targets (Objects) – Any other targets to pull into combat. At least one target is required if combathandler is not given (a new combat must have at least one opponent!).

Keyword Arguments

session (Session, optional) – A player session to use. This is useful for multisession modes.

Returns

EvAdventureCombatHandler – A created or existing combat handler.