Apply black to evadventure files

This commit is contained in:
Griatch 2022-07-03 11:40:26 +02:00
parent 6e8e3963dd
commit 7815a06e3a
12 changed files with 653 additions and 327 deletions

View file

@ -27,6 +27,7 @@ class EquipmentHandler:
until you clean them. until you clean them.
""" """
save_attribute = "inventory_slots" save_attribute = "inventory_slots"
def __init__(self, obj): def __init__(self, obj):
@ -47,8 +48,8 @@ class EquipmentHandler:
WieldLocation.TWO_HANDS: None, WieldLocation.TWO_HANDS: None,
WieldLocation.BODY: None, WieldLocation.BODY: None,
WieldLocation.HEAD: None, WieldLocation.HEAD: None,
WieldLocation.BACKPACK: [] WieldLocation.BACKPACK: [],
} },
) )
def _count_slots(self): def _count_slots(self):
@ -64,8 +65,7 @@ class EquipmentHandler:
if slot is not WieldLocation.BACKPACK if slot is not WieldLocation.BACKPACK
) )
backpack_usage = sum( backpack_usage = sum(
getattr(slotobj, "size", 0) or 0 getattr(slotobj, "size", 0) or 0 for slotobj in slots[WieldLocation.BACKPACK]
for slotobj in slots[WieldLocation.BACKPACK]
) )
return wield_usage + backpack_usage return wield_usage + backpack_usage
@ -101,9 +101,11 @@ class EquipmentHandler:
current_slot_usage = self._count_slots() current_slot_usage = self._count_slots()
if current_slot_usage + size > max_slots: if current_slot_usage + size > max_slots:
slots_left = max_slots - current_slot_usage slots_left = max_slots - current_slot_usage
raise EquipmentError(f"Equipment full ($int2str({slots_left}) slots " raise EquipmentError(
f"remaining, {obj.key} needs $int2str({size}) " f"Equipment full ($int2str({slots_left}) slots "
f"$pluralize(slot, {size})).") f"remaining, {obj.key} needs $int2str({size}) "
f"$pluralize(slot, {size}))."
)
return True return True
@property @property
@ -120,11 +122,13 @@ class EquipmentHandler:
""" """
slots = self.slots slots = self.slots
return sum(( return sum(
getattr(slots[WieldLocation.BODY], "armor", 0), (
getattr(slots[WieldLocation.SHIELD_HAND], "armor", 0), getattr(slots[WieldLocation.BODY], "armor", 0),
getattr(slots[WieldLocation.HEAD], "armor", 0), getattr(slots[WieldLocation.SHIELD_HAND], "armor", 0),
)) getattr(slots[WieldLocation.HEAD], "armor", 0),
)
)
@property @property
def weapon(self): def weapon(self):
@ -423,6 +427,7 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
EvAdventureCharacter class instead. EvAdventureCharacter class instead.
""" """
hit_dice = AttributeProperty(default=1) hit_dice = AttributeProperty(default=1)
armor = AttributeProperty(default=11) armor = AttributeProperty(default=11)
morale = AttributeProperty(default=9) morale = AttributeProperty(default=9)

View file

@ -42,7 +42,8 @@ class CombatAction:
Inherit from this to make new actions. Inherit from this to make new actions.
""" """
key = 'action'
key = "action"
help_text = "Combat action to perform." help_text = "Combat action to perform."
# action to echo to everyone. # action to echo to everyone.
post_action_text = "{combatant} performed an action." post_action_text = "{combatant} performed an action."
@ -107,6 +108,7 @@ class CombatActionDoNothing(CombatAction):
Do nothing this turn. Do nothing this turn.
""" """
help_text = "Hold you position, doing nothing." help_text = "Hold you position, doing nothing."
post_action_text = "{combatant} does nothing this turn." post_action_text = "{combatant} does nothing this turn."
@ -124,24 +126,29 @@ class CombatActionStunt(CombatAction):
spend a use unless they succeed. spend a use unless they succeed.
""" """
give_advantage = True give_advantage = True
give_disadvantage = False give_disadvantage = False
max_uses = 1 max_uses = 1
priority = -1 priority = -1
attack_type = Ability.DEX attack_type = Ability.DEX
defense_type = Ability.DEX defense_type = Ability.DEX
help_text = ("Perform a stunt against a target. This will give you or an ally advantage " help_text = (
"on your next action against the same target [range 0-1, one use per combat. " "Perform a stunt against a target. This will give you or an ally advantage "
"Bonus lasts for two turns].") "on your next action against the same target [range 0-1, one use per combat. "
"Bonus lasts for two turns]."
)
def use(self, attacker, defender, *args, beneficiary=None, **kwargs): def use(self, attacker, defender, *args, beneficiary=None, **kwargs):
# quality doesn't matter for stunts, they are either successful or not # quality doesn't matter for stunts, they are either successful or not
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw( is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
attacker, defender, attacker,
defender,
attack_type=self.attack_type, attack_type=self.attack_type,
defense_type=self.defense_type, defense_type=self.defense_type,
advantage=False, disadvantage=disadvantage, advantage=False,
disadvantage=disadvantage,
) )
if is_success: if is_success:
beneficiary = beneficiary if beneficiary else attacker beneficiary = beneficiary if beneficiary else attacker
@ -161,6 +168,7 @@ class CombatActionAttack(CombatAction):
melee attack. melee attack.
""" """
key = "attack" key = "attack"
priority = 1 priority = 1
@ -176,14 +184,17 @@ class CombatActionAttack(CombatAction):
disadvantage = bool(self.combathandler.disadvantage_matrix[attacker].pop(defender, False)) disadvantage = bool(self.combathandler.disadvantage_matrix[attacker].pop(defender, False))
is_hit, quality = rules.EvAdventureRollEngine.opposed_saving_throw( is_hit, quality = rules.EvAdventureRollEngine.opposed_saving_throw(
attacker, defender, attacker,
defender,
attack_type=attacker.weapon.attack_type, attack_type=attacker.weapon.attack_type,
defense_type=attacker.weapon.defense_type, defense_type=attacker.weapon.defense_type,
advantage=advantage, disadvantage=disadvantage advantage=advantage,
disadvantage=disadvantage,
) )
if is_hit: if is_hit:
self.combathandler.resolve_damage(attacker, defender, self.combathandler.resolve_damage(
critical=quality == "critical success") attacker, defender, critical=quality == "critical success"
)
# TODO messaging here # TODO messaging here
@ -204,6 +215,7 @@ class CombatActionUseItem(CombatAction):
combat_post_use combat_post_use
""" """
def get_help(self, item, *args): def get_help(self, item, *args):
return item.combat_get_help(*args) return item.combat_get_help(*args)
@ -227,6 +239,7 @@ class CombatActionFlee(CombatAction):
end of the second turn. end of the second turn.
""" """
key = "flee" key = "flee"
priority = -1 priority = -1
@ -234,37 +247,42 @@ class CombatActionFlee(CombatAction):
# it's safe to do this twice # it's safe to do this twice
self.combathandler.flee(combatant) self.combathandler.flee(combatant)
class CombatActionChase(CombatAction): class CombatActionChase(CombatAction):
""" """
Chasing is a way to counter a 'flee' action. It is a maximum movement towards the target Chasing is a way to counter a 'flee' action. It is a maximum movement towards the target
and will mean a DEX contest, if the fleeing target loses, they are moved back from and will mean a DEX contest, if the fleeing target loses, they are moved back from
'disengaging' range and remain in combat at the new distance (likely 2 if max movement 'disengaging' range and remain in combat at the new distance (likely 2 if max movement
is 2). Advantage/disadvantage are considered. is 2). Advantage/disadvantage are considered.
""" """
key = "chase"
priority = -5 # checked last
attack_type = Ability.DEX # or is it CON? key = "chase"
defense_type = Ability.DEX priority = -5 # checked last
def use(self, combatant, fleeing_target, *args, **kwargs): attack_type = Ability.DEX # or is it CON?
defense_type = Ability.DEX
advantage = bool(self.advantage_matrix[attacker].pop(fleeing_target, False)) def use(self, combatant, fleeing_target, *args, **kwargs):
disadvantage = bool(self.disadvantage_matrix[attacker].pop(fleeing_target, False))
is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw( advantage = bool(self.advantage_matrix[attacker].pop(fleeing_target, False))
combatant, fleeing_target, disadvantage = bool(self.disadvantage_matrix[attacker].pop(fleeing_target, False))
attack_type=self.attack_type, defense_type=self.defense_type,
advantage=advantage, disadvantage=disadvantage
)
if is_success: is_success, _ = rules.EvAdventureRollEngine.opposed_saving_throw(
# managed to stop the target from fleeing/disengaging combatant,
self.combatant.unflee(fleeing_target) fleeing_target,
else: attack_type=self.attack_type,
pass # they are getting away! defense_type=self.defense_type,
advantage=advantage,
disadvantage=disadvantage,
)
if is_success:
# managed to stop the target from fleeing/disengaging
self.combatant.unflee(fleeing_target)
else:
pass # they are getting away!
class EvAdventureCombatHandler(DefaultScript): class EvAdventureCombatHandler(DefaultScript):
@ -283,7 +301,7 @@ class EvAdventureCombatHandler(DefaultScript):
CombatActionChase, CombatActionChase,
CombatActionUseItem, CombatActionUseItem,
CombatActionStunt, CombatActionStunt,
CombatActionAttack CombatActionAttack,
] ]
# attributes # attributes
@ -345,7 +363,8 @@ class EvAdventureCombatHandler(DefaultScript):
for combatant in self.combatants: for combatant in self.combatants:
# read the current action type selected by the player # read the current action type selected by the player
action, args, kwargs = self.action_queue.get( action, args, kwargs = self.action_queue.get(
combatant, (CombatActionDoNothing(self, combatant), (), {})) combatant, (CombatActionDoNothing(self, combatant), (), {})
)
# perform the action on the CombatAction instance # perform the action on the CombatAction instance
action.use(combatant, *args, **kwargs) action.use(combatant, *args, **kwargs)
@ -380,11 +399,13 @@ class EvAdventureCombatHandler(DefaultScript):
for combatant in self.combatants: for combatant in self.combatants:
new_advantage_matrix[combatant] = { new_advantage_matrix[combatant] = {
target: set_at_turn for target, turn in advantage_matrix.items() target: set_at_turn
for target, turn in advantage_matrix.items()
if set_at_turn > oldest_stunt_age if set_at_turn > oldest_stunt_age
} }
new_disadvantage_matrix[combatant] = { new_disadvantage_matrix[combatant] = {
target: set_at_turn for target, turn in disadvantage_matrix.items() target: set_at_turn
for target, turn in disadvantage_matrix.items()
if set_at_turn > oldest_stunt_age if set_at_turn > oldest_stunt_age
} }
@ -500,8 +521,11 @@ class EvAdventureCombatHandler(DefaultScript):
if defender.hp > 0: if defender.hp > 0:
# they are weakened, but with hp # they are weakened, but with hp
self.msg("You are alive, but out of the fight. If you want to press your luck, " self.msg(
"you need to rejoin the combat.", targets=defender) "You are alive, but out of the fight. If you want to press your luck, "
"you need to rejoin the combat.",
targets=defender,
)
defender.at_defeat() # note - NPC monsters may still 'die' here defender.at_defeat() # note - NPC monsters may still 'die' here
else: else:
# outright killed # outright killed
@ -537,19 +561,16 @@ combat_script = """
""" """
def _register_action(caller, raw_string, **kwargs): def _register_action(caller, raw_string, **kwargs):
""" """
Register action with handler. Register action with handler.
""" """
action = kwargs.get['action'] action = kwargs.get["action"]
action_args = kwargs['action_args'] action_args = kwargs["action_args"]
action_kwargs = kwargs['action_kwargs'] action_kwargs = kwargs["action_kwargs"]
combat = caller.scripts.get("combathandler") combat = caller.scripts.get("combathandler")
combat.register_action( combat.register_action(caller, action=action, *action_args, **action_kwargs)
caller, action=action, *action_args, **action_kwargs
)
def node_select_target(caller, raw_string, **kwargs): def node_select_target(caller, raw_string, **kwargs):
@ -558,9 +579,9 @@ def node_select_target(caller, raw_string, **kwargs):
with all other actions. with all other actions.
""" """
action = kwargs.get('action') action = kwargs.get("action")
action_args = kwargs.get('action_args') action_args = kwargs.get("action_args")
action_kwargs = kwargs.get('action_kwargs') action_kwargs = kwargs.get("action_kwargs")
combat = caller.scripts.get("combathandler") combat = caller.scripts.get("combathandler")
text = "Select target for |w{action}|n." text = "Select target for |w{action}|n."
@ -568,22 +589,26 @@ def node_select_target(caller, raw_string, **kwargs):
options = [ options = [
{ {
"desc": combatant.key, "desc": combatant.key,
"goto": (_register_action, {"action": action, "goto": (
"args": action_args, _register_action,
"kwargs": action_kwargs}) {"action": action, "args": action_args, "kwargs": action_kwargs},
),
} }
for combatant in combat.combatants] for combatant in combat.combatants
]
# make the apply-self option always the last one # make the apply-self option always the last one
options.append( options.append(
{ {
"desc": "(yourself)", "desc": "(yourself)",
"goto": (_register_action, {"action": action, "goto": (
"args": action_args, _register_action,
"kwargs": action_kwargs}) {"action": action, "args": action_args, "kwargs": action_kwargs},
),
} }
) )
return text, options return text, options
def node_select_action(caller, raw_string, **kwargs): def node_select_action(caller, raw_string, **kwargs):
""" """
Menu node for selecting a combat action. Menu node for selecting a combat action.
@ -593,13 +618,14 @@ def node_select_action(caller, raw_string, **kwargs):
text = combat.get_previous_turn_status(caller) text = combat.get_previous_turn_status(caller)
options = combat.get_available_options(caller) options = combat.get_available_options(caller)
options = { options = {
"desc": action, "desc": action,
"goto": ("node_select_target", {"action": action, "goto": (
}) "node_select_target",
{
"action": action,
},
),
} }
return text, options return text, options

View file

@ -16,11 +16,13 @@ To get the `value` of an enum (must always be hashable, useful for Attribute loo
""" """
from enum import Enum from enum import Enum
class Ability(Enum): class Ability(Enum):
""" """
The six base abilities (defense is always bonus + 10) The six base abilities (defense is always bonus + 10)
""" """
STR = "strength" STR = "strength"
DEX = "dexterity" DEX = "dexterity"
CON = "constitution" CON = "constitution"
@ -45,11 +47,13 @@ class Ability(Enum):
CRITICAL_FAILURE = "critical_failure" CRITICAL_FAILURE = "critical_failure"
CRITICAL_SUCCESS = "critical_success" CRITICAL_SUCCESS = "critical_success"
class WieldLocation(Enum): class WieldLocation(Enum):
""" """
Wield (or wear) locations. Wield (or wear) locations.
""" """
# wield/wear location # wield/wear location
BACKPACK = "backpack" BACKPACK = "backpack"
WEAPON_HAND = "weapon_hand" WEAPON_HAND = "weapon_hand"

View file

@ -13,12 +13,12 @@ from evennia.typeclasses.attributes import AttributeProperty
from .enums import WieldLocation, Ability from .enums import WieldLocation, Ability
class EvAdventureObject(DefaultObject): class EvAdventureObject(DefaultObject):
""" """
Base in-game entity. Base in-game entity.
""" """
# inventory management # inventory management
inventory_use_slot = AttributeProperty(default=WieldLocation.BACKPACK) inventory_use_slot = AttributeProperty(default=WieldLocation.BACKPACK)
# how many inventory slots it uses (can be a fraction) # how many inventory slots it uses (can be a fraction)
@ -41,6 +41,7 @@ class EvAdventureObjectFiller(EvAdventureObject):
meaning it's unusable. meaning it's unusable.
""" """
quality = AttributeProperty(default=0) quality = AttributeProperty(default=0)
@ -49,6 +50,7 @@ class EvAdventureWeapon(EvAdventureObject):
Base weapon class for all EvAdventure weapons. Base weapon class for all EvAdventure weapons.
""" """
inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND) inventory_use_slot = AttributeProperty(WieldLocation.WEAPON_HAND)
attack_type = AttributeProperty(default=Ability.STR) attack_type = AttributeProperty(default=Ability.STR)

View file

@ -31,6 +31,7 @@ class EvAdventureQuest:
check_<name> and complete_<name> check_<name> and complete_<name>
""" """
# name + category must be globally unique. They are # name + category must be globally unique. They are
# queried as name:category or just name, if category is empty. # queried as name:category or just name, if category is empty.
name = "" name = ""
@ -44,11 +45,9 @@ class EvAdventureQuest:
def check(): def check():
pass pass
def progress(self, quester, *args, **kwargs): def progress(self, quester, *args, **kwargs):
""" """ """
"""
class EvAdventureQuestHandler: class EvAdventureQuestHandler:
""" """
@ -63,15 +62,14 @@ class EvAdventureQuestHandler:
``` ```
""" """
quest_storage_attribute = "_quests" quest_storage_attribute = "_quests"
quest_storage_attribute_category = "evadventure" quest_storage_attribute_category = "evadventure"
def __init__(self, obj): def __init__(self, obj):
self.obj = obj self.obj = obj
self.storage = obj.attributes.get( self.storage = obj.attributes.get(
self.quest_storage_attribute, self.quest_storage_attribute, category=self.quest_storage_attribute_category, default={}
category=self.quest_storage_attribute_category,
default={}
) )
def quest_storage_key(self, name, category): def quest_storage_key(self, name, category):
@ -116,6 +114,3 @@ class EvAdventureQuestHandler:
start immediately. start immediately.
""" """

View file

@ -227,26 +227,26 @@ character_generation = {
"suspected", "suspected",
], ],
"alignment": [ "alignment": [
('1-5', "law"), ("1-5", "law"),
('6-15', "neutrality"), ("6-15", "neutrality"),
('16-20', "chaos"), ("16-20", "chaos"),
], ],
"armor": [ "armor": [
('1-3', "no armor"), ("1-3", "no armor"),
('4-14', "gambeson"), ("4-14", "gambeson"),
('15-19', "brigandine"), ("15-19", "brigandine"),
('20', "chain"), ("20", "chain"),
], ],
"helmets and shields": [ "helmets and shields": [
('1-13', "no helmet or shield"), ("1-13", "no helmet or shield"),
('14-16', "helmet"), ("14-16", "helmet"),
('17-19', "shield"), ("17-19", "shield"),
('20', "helmet and shield"), ("20", "helmet and shield"),
], ],
"starting weapon": [ # note: these are all d6 dmg weapons "starting weapon": [ # note: these are all d6 dmg weapons
('1-7', "dagger"), ("1-7", "dagger"),
('8-13', "club"), ("8-13", "club"),
('14-20', "staff"), ("14-20", "staff"),
], ],
"dungeoning gear": [ "dungeoning gear": [
"rope, 50ft", "rope, 50ft",
@ -315,55 +315,303 @@ character_generation = {
"small bell", "small bell",
], ],
"name": [ "name": [
"Abbo", "Adelaide", "Ellis", "Eleanor", "Lief", "Luanda", "Ablerus", "Agatha", "Abbo",
"Eneto", "Elizabeth", "Luke", "Lyra", "Acot", "Aleida", "Enio", "Elspeth", "Martin", "Adelaide",
"Mabel", "Alexander", "Alexia", "Eral", "Emeline", "Merrick", "Maerwynn", "Almanzor", "Ellis",
"Alianor", "Erasmus", "Emma", "Mortimer", "Malkyn", "Althalos", "Aline", "Eustace", "Eleanor",
"Emmony", "Ogden", "Margaret", "Ancelot", "Alma", "Everard", "Enna", "Oliver", "Margery", "Lief",
"Asher", "Alys", "Faustus", "Enndolynn", "Orion", "Maria", "Aster", "Amabel", "Favian", "Luanda",
"Eve", "Oswald", "Marion", "Balan", "Amice", "Fendrel", "Evita", "Pelagon", "Matilda", "Ablerus",
"Balthazar", "Anastas", "Finn", "Felice", "Pello", "Millicent", "Barat", "Angmar", "Agatha",
"Florian", "Fern", "Peyton", "Mirabelle", "Bartholomew", "Annabel", "Francis", "Floria", "Eneto",
"Philip", "Muriel", "Basil", "Arabella", "Frederick", "Fredegonde", "Poeas", "Nabarne", "Elizabeth",
"Benedict", "Ariana", "Gaidon", "Gillian", "Quinn", "Nell", "Berinon", "Ayleth", "Gavin", "Luke",
"Gloriana", "Ralph", "Nesea", "Bertram", "Barberry", "Geoffrey", "Godeleva", "Randolph", "Lyra",
"Niree", "Beves", "Barsaba", "Gerard", "Godiva", "Reginald", "Odette", "Bilmer", "Acot",
"Basilia", "Gervase", "Gunnilda", "Reynold", "Odila", "Blanko", "Beatrix", "Gilbert", "Aleida",
"Gussalen", "Richard", "Oria", "Bodo", "Benevolence", "Giles", "Gwendolynn", "Robert", "Enio",
"Osanna", "Borin", "Bess", "Godfrey", "Hawise", "Robin", "Ostrythe", "Bryce", "Brangian", "Elspeth",
"Gregory", "Helena", "Roger", "Ottilia", "Carac", "Brigida", "Gringoire", "Helewise", "Martin",
"Ronald", "Panope", "Caspar", "Brunhild", "Gunthar", "Hester", "Rowan", "Paternain", "Mabel",
"Cassius", "Camilla", "Guy", "Hildegard", "Rulf", "Pechel", "Cedric", "Canace", "Gyras", "Alexander",
"Idony", "Sabin", "Pepper", "Cephalos", "Cecily", "Hadrian", "Isabella", "Sevrin", "Alexia",
"Petronilla", "Chadwick", "Cedany", "Hedelf", "Iseult", "Silas", "Phrowenia", "Charillos", "Eral",
"Christina", "Hewelin", "Isolde", "Simon", "Poppy", "Charles", "Claramunda", "Hilderith", "Emeline",
"Jacquelyn", "Solomon", "Quenell", "Chermon", "Clarice", "Humbert", "Jasmine", "Stephen", "Merrick",
"Raisa", "Clement", "Clover", "Hyllus", "Jessamine", "Terrowin", "Reyna", "Clifton", "Maerwynn",
"Collette", "Ianto", "Josselyn", "Thomas", "Rixende", "Clovis", "Constance", "Ibykos", "Almanzor",
"Juliana", "Tristan", "Rosamund", "Cyon", "Damaris", "Inigo", "Karitate", "Tybalt", "Alianor",
"Rose", "Dain", "Daphne", "Itylus", "Katelyn", "Ulric", "Ryia", "Dalmas", "Demona", "Erasmus",
"James", "Katja", "Walter", "Sarah", "Danor", "Dimia", "Jasper", "Katrina", "Wander", "Emma",
"Seraphina", "Destrian", "Dione", "Jiles", "Kaylein", "Warin", "Thea", "Domeka", "Mortimer",
"Dorothea", "Joffridus", "Kinna", "Waverly", "Trillby", "Donald", "Douce", "Jordan", "Malkyn",
"Krea", "Willahelm", "Wendel", "Doran", "Duraina", "Joris", "Kypris", "William", "Althalos",
"Wilberga", "Dumphey", "Dyota", "Josef", "Landerra", "Wimarc", "Winifred", "Eadmund", "Aline",
"Eberhild", "Laurence", "Larraza", "Wystan", "Wofled", "Eckardus", "Edelot", "Leofrick", "Eustace",
"Linet", "Xalvador", "Wymarc", "Edward", "Edyva", "Letholdus", "Loreena", "Zane", "Ysmay", "Emmony",
"Ogden",
"Margaret",
"Ancelot",
"Alma",
"Everard",
"Enna",
"Oliver",
"Margery",
"Asher",
"Alys",
"Faustus",
"Enndolynn",
"Orion",
"Maria",
"Aster",
"Amabel",
"Favian",
"Eve",
"Oswald",
"Marion",
"Balan",
"Amice",
"Fendrel",
"Evita",
"Pelagon",
"Matilda",
"Balthazar",
"Anastas",
"Finn",
"Felice",
"Pello",
"Millicent",
"Barat",
"Angmar",
"Florian",
"Fern",
"Peyton",
"Mirabelle",
"Bartholomew",
"Annabel",
"Francis",
"Floria",
"Philip",
"Muriel",
"Basil",
"Arabella",
"Frederick",
"Fredegonde",
"Poeas",
"Nabarne",
"Benedict",
"Ariana",
"Gaidon",
"Gillian",
"Quinn",
"Nell",
"Berinon",
"Ayleth",
"Gavin",
"Gloriana",
"Ralph",
"Nesea",
"Bertram",
"Barberry",
"Geoffrey",
"Godeleva",
"Randolph",
"Niree",
"Beves",
"Barsaba",
"Gerard",
"Godiva",
"Reginald",
"Odette",
"Bilmer",
"Basilia",
"Gervase",
"Gunnilda",
"Reynold",
"Odila",
"Blanko",
"Beatrix",
"Gilbert",
"Gussalen",
"Richard",
"Oria",
"Bodo",
"Benevolence",
"Giles",
"Gwendolynn",
"Robert",
"Osanna",
"Borin",
"Bess",
"Godfrey",
"Hawise",
"Robin",
"Ostrythe",
"Bryce",
"Brangian",
"Gregory",
"Helena",
"Roger",
"Ottilia",
"Carac",
"Brigida",
"Gringoire",
"Helewise",
"Ronald",
"Panope",
"Caspar",
"Brunhild",
"Gunthar",
"Hester",
"Rowan",
"Paternain",
"Cassius",
"Camilla",
"Guy",
"Hildegard",
"Rulf",
"Pechel",
"Cedric",
"Canace",
"Gyras",
"Idony",
"Sabin",
"Pepper",
"Cephalos",
"Cecily",
"Hadrian",
"Isabella",
"Sevrin",
"Petronilla",
"Chadwick",
"Cedany",
"Hedelf",
"Iseult",
"Silas",
"Phrowenia",
"Charillos",
"Christina",
"Hewelin",
"Isolde",
"Simon",
"Poppy",
"Charles",
"Claramunda",
"Hilderith",
"Jacquelyn",
"Solomon",
"Quenell",
"Chermon",
"Clarice",
"Humbert",
"Jasmine",
"Stephen",
"Raisa",
"Clement",
"Clover",
"Hyllus",
"Jessamine",
"Terrowin",
"Reyna",
"Clifton",
"Collette",
"Ianto",
"Josselyn",
"Thomas",
"Rixende",
"Clovis",
"Constance",
"Ibykos",
"Juliana",
"Tristan",
"Rosamund",
"Cyon",
"Damaris",
"Inigo",
"Karitate",
"Tybalt",
"Rose",
"Dain",
"Daphne",
"Itylus",
"Katelyn",
"Ulric",
"Ryia",
"Dalmas",
"Demona",
"James",
"Katja",
"Walter",
"Sarah",
"Danor",
"Dimia",
"Jasper",
"Katrina",
"Wander",
"Seraphina",
"Destrian",
"Dione",
"Jiles",
"Kaylein",
"Warin",
"Thea",
"Domeka",
"Dorothea",
"Joffridus",
"Kinna",
"Waverly",
"Trillby",
"Donald",
"Douce",
"Jordan",
"Krea",
"Willahelm",
"Wendel",
"Doran",
"Duraina",
"Joris",
"Kypris",
"William",
"Wilberga",
"Dumphey",
"Dyota",
"Josef",
"Landerra",
"Wimarc",
"Winifred",
"Eadmund",
"Eberhild",
"Laurence",
"Larraza",
"Wystan",
"Wofled",
"Eckardus",
"Edelot",
"Leofrick",
"Linet",
"Xalvador",
"Wymarc",
"Edward",
"Edyva",
"Letholdus",
"Loreena",
"Zane",
"Ysmay",
], ],
} }
reactions = [ reactions = [
('2', "Hostile"), ("2", "Hostile"),
('3-5', "Unfriendly"), ("3-5", "Unfriendly"),
('6-8', "Unsure"), ("6-8", "Unsure"),
('9-11', "Talkative"), ("9-11", "Talkative"),
('12', "Helpful"), ("12", "Helpful"),
] ]
initiative = [ initiative = [
('1-3', "Enemy acts first"), ("1-3", "Enemy acts first"),
('4-6', "PC acts first"), ("4-6", "PC acts first"),
] ]
@ -377,4 +625,3 @@ death_and_dismemberment = [
"rattled", # -1d4 WIS "rattled", # -1d4 WIS
"disfigured", # -1d4 CHA "disfigured", # -1d4 CHA
] ]

View file

@ -28,12 +28,13 @@ from evennia.utils.evtable import EvTable
from .enums import Ability from .enums import Ability
from .random_tables import ( from .random_tables import (
character_generation as chargen_table, character_generation as chargen_table,
death_and_dismemberment as death_table death_and_dismemberment as death_table,
) )
# Basic rolls # Basic rolls
class EvAdventureRollEngine: class EvAdventureRollEngine:
""" """
This groups all dice rolls of EvAdventure. These could all have been normal functions, but we This groups all dice rolls of EvAdventure. These could all have been normal functions, but we
@ -66,10 +67,11 @@ class EvAdventureRollEngine:
""" """
max_diesize = 1000 max_diesize = 1000
roll_string = roll_string.lower() roll_string = roll_string.lower()
if 'd' not in roll_string: if "d" not in roll_string:
raise TypeError(f"Dice roll '{roll_string}' was not recognized. " raise TypeError(
"Must be `<number>d<dicesize>`.") f"Dice roll '{roll_string}' was not recognized. " "Must be `<number>d<dicesize>`."
number, diesize = roll_string.split('d', 1) )
number, diesize = roll_string.split("d", 1)
try: try:
number = int(number) number = int(number)
diesize = int(diesize) diesize = int(diesize)
@ -104,8 +106,15 @@ class EvAdventureRollEngine:
else: else:
return min(self.roll("1d20"), self.roll("1d20")) return min(self.roll("1d20"), self.roll("1d20"))
def saving_throw(self, character, bonus_type=Ability.STR, target=15, def saving_throw(
advantage=False, disadvantage=False, modifier=0): self,
character,
bonus_type=Ability.STR,
target=15,
advantage=False,
disadvantage=False,
modifier=0,
):
""" """
A saving throw without a clear enemy to beat. In _Knave_ all unopposed saving A saving throw without a clear enemy to beat. In _Knave_ all unopposed saving
throws always tries to beat 15, so (d20 + bonus + modifier) > 15. throws always tries to beat 15, so (d20 + bonus + modifier) > 15.
@ -142,9 +151,15 @@ class EvAdventureRollEngine:
return (dice_roll + bonus + modifier) > target, quality return (dice_roll + bonus + modifier) > target, quality
def opposed_saving_throw( def opposed_saving_throw(
self, attacker, defender, self,
attack_type=Ability.STR, defense_type=Ability.ARMOR, attacker,
advantage=False, disadvantage=False, modifier=0): defender,
attack_type=Ability.STR,
defense_type=Ability.ARMOR,
advantage=False,
disadvantage=False,
modifier=0,
):
""" """
An saving throw that tries to beat an active opposing side. An saving throw that tries to beat an active opposing side.
@ -167,10 +182,14 @@ class EvAdventureRollEngine:
""" """
defender_defense = getattr(defender, defense_type.value, 1) + 10 defender_defense = getattr(defender, defense_type.value, 1) + 10
return self.saving_throw(attacker, bonus_type=attack_type, return self.saving_throw(
target=defender_defense, attacker,
advantage=advantage, disadvantage=disadvantage, bonus_type=attack_type,
modifier=modifier) target=defender_defense,
advantage=advantage,
disadvantage=disadvantage,
modifier=modifier,
)
def roll_random_table(self, dieroll, table_choices): def roll_random_table(self, dieroll, table_choices):
""" """
@ -204,7 +223,7 @@ class EvAdventureRollEngine:
min_range = 10**6 min_range = 10**6
for (valrange, choice) in table_choices: for (valrange, choice) in table_choices:
minval, *maxval = valrange.split('-', 1) minval, *maxval = valrange.split("-", 1)
minval = abs(int(minval)) minval = abs(int(minval))
maxval = abs(int(maxval[0]) if maxval else minval) maxval = abs(int(maxval[0]) if maxval else minval)
@ -240,7 +259,7 @@ class EvAdventureRollEngine:
bool: False if morale roll failed, True otherwise. bool: False if morale roll failed, True otherwise.
""" """
return self.roll('2d6') <= defender.morale return self.roll("2d6") <= defender.morale
def heal(self, character, amount): def heal(self, character, amount):
""" """
@ -265,7 +284,7 @@ class EvAdventureRollEngine:
int: How much HP was healed. This is never more than how damaged we are. int: How much HP was healed. This is never more than how damaged we are.
""" """
self.heal(character, self.roll('1d8') + character.constitution) self.heal(character, self.roll("1d8") + character.constitution)
death_map = { death_map = {
"weakened": "strength", "weakened": "strength",
@ -282,7 +301,7 @@ class EvAdventureRollEngine:
""" """
result = self.roll_random_table('1d8', death_table) result = self.roll_random_table("1d8", death_table)
if result == "dead": if result == "dead":
character.handle_death() character.handle_death()
else: else:
@ -304,16 +323,15 @@ class EvAdventureRollEngine:
character.hp = new_hp character.hp = new_hp
character.msg( character.msg(
"~" * 78 + "~" * 78 + "\n|yYou survive your brush with death, "
"\n|yYou survive your brush with death, "
f"but are |r{result.upper()}|y and permenently |rlose {loss} {abi}|y.|n\n" f"but are |r{result.upper()}|y and permenently |rlose {loss} {abi}|y.|n\n"
f"|GYou recover |g{new_hp}|G health|.\n" f"|GYou recover |g{new_hp}|G health|.\n" + "~" * 78
+ "~" * 78
) )
# character generation # character generation
class EvAdventureCharacterGeneration: class EvAdventureCharacterGeneration:
""" """
This collects all the rules for generating a new character. An instance of this class can be This collects all the rules for generating a new character. An instance of this class can be
@ -341,6 +359,7 @@ class EvAdventureCharacterGeneration:
there is no GM to adjudicate a different choice). there is no GM to adjudicate a different choice).
""" """
def __init__(self): def __init__(self):
""" """
Initialize starting values Initialize starting values
@ -351,7 +370,7 @@ class EvAdventureCharacterGeneration:
roll_engine = EvAdventureRollEngine() roll_engine = EvAdventureRollEngine()
# name will likely be modified later # name will likely be modified later
self.name = roll_engine.roll_random_table('1d282', chargen_table['name']) self.name = roll_engine.roll_random_table("1d282", chargen_table["name"])
# base attribute bonuses (flat +1 bonus) # base attribute bonuses (flat +1 bonus)
self.strength = 2 self.strength = 2
@ -362,17 +381,17 @@ class EvAdventureCharacterGeneration:
self.charisma = 2 self.charisma = 2
# physical attributes (only for rp purposes) # physical attributes (only for rp purposes)
self.physique = roll_engine.roll_random_table('1d20', chargen_table['physique']) self.physique = roll_engine.roll_random_table("1d20", chargen_table["physique"])
self.face = roll_engine.roll_random_table('1d20', chargen_table['face']) self.face = roll_engine.roll_random_table("1d20", chargen_table["face"])
self.skin = roll_engine.roll_random_table('1d20', chargen_table['skin']) self.skin = roll_engine.roll_random_table("1d20", chargen_table["skin"])
self.hair = roll_engine.roll_random_table('1d20', chargen_table['hair']) self.hair = roll_engine.roll_random_table("1d20", chargen_table["hair"])
self.clothing = roll_engine.roll_random_table('1d20', chargen_table['clothing']) self.clothing = roll_engine.roll_random_table("1d20", chargen_table["clothing"])
self.speech = roll_engine.roll_random_table('1d20', chargen_table['speech']) self.speech = roll_engine.roll_random_table("1d20", chargen_table["speech"])
self.virtue = roll_engine.roll_random_table('1d20', chargen_table['virtue']) self.virtue = roll_engine.roll_random_table("1d20", chargen_table["virtue"])
self.vice = roll_engine.roll_random_table('1d20', chargen_table['vice']) self.vice = roll_engine.roll_random_table("1d20", chargen_table["vice"])
self.background = roll_engine.roll_random_table('1d20', chargen_table['background']) self.background = roll_engine.roll_random_table("1d20", chargen_table["background"])
self.misfortune = roll_engine.roll_random_table('1d20', chargen_table['misfortune']) self.misfortune = roll_engine.roll_random_table("1d20", chargen_table["misfortune"])
self.alignment = roll_engine.roll_random_table('1d20', chargen_table['alignment']) self.alignment = roll_engine.roll_random_table("1d20", chargen_table["alignment"])
# same for all # same for all
self.exploration_speed = 120 self.exploration_speed = 120
@ -383,22 +402,23 @@ class EvAdventureCharacterGeneration:
self.level = 1 self.level = 1
# random equipment # random equipment
self.armor = roll_engine.roll_random_table('1d20', chargen_table['armor']) self.armor = roll_engine.roll_random_table("1d20", chargen_table["armor"])
_helmet_and_shield = roll_engine.roll_random_table( _helmet_and_shield = roll_engine.roll_random_table(
'1d20', chargen_table["helmets and shields"]) "1d20", chargen_table["helmets and shields"]
)
self.helmet = "helmet" if "helmet" in _helmet_and_shield else "none" self.helmet = "helmet" if "helmet" in _helmet_and_shield else "none"
self.shield = "shield" if "shield" in _helmet_and_shield else "none" self.shield = "shield" if "shield" in _helmet_and_shield else "none"
self.weapon = roll_engine.roll_random_table('1d20', chargen_table["starting weapon"]) self.weapon = roll_engine.roll_random_table("1d20", chargen_table["starting weapon"])
self.backpack = [ self.backpack = [
"ration", "ration",
"ration", "ration",
roll_engine.roll_random_table('1d20', chargen_table["dungeoning gear"]), roll_engine.roll_random_table("1d20", chargen_table["dungeoning gear"]),
roll_engine.roll_random_table('1d20', chargen_table["dungeoning gear"]), roll_engine.roll_random_table("1d20", chargen_table["dungeoning gear"]),
roll_engine.roll_random_table('1d20', chargen_table["general gear 1"]), roll_engine.roll_random_table("1d20", chargen_table["general gear 1"]),
roll_engine.roll_random_table('1d20', chargen_table["general gear 2"]), roll_engine.roll_random_table("1d20", chargen_table["general gear 2"]),
] ]
def build_desc(self): def build_desc(self):
@ -489,12 +509,14 @@ class EvAdventureCharacterGeneration:
# character improvement # character improvement
class EvAdventureImprovement: class EvAdventureImprovement:
""" """
Handle XP gains and level upgrades. Grouped in a class in order to Handle XP gains and level upgrades. Grouped in a class in order to
make it easier to override the mechanism. make it easier to override the mechanism.
""" """
xp_per_level = 1000 xp_per_level = 1000
amount_of_abilities_to_upgrade = 3 amount_of_abilities_to_upgrade = 3
max_ability_bonus = 10 # bonus +10, defense 20 max_ability_bonus = 10 # bonus +10, defense 20
@ -546,12 +568,14 @@ class EvAdventureImprovement:
except AttributeError: except AttributeError:
pass pass
character.hp_max = max(character.max_hp + 1, character.hp_max = max(
EvAdventureRollEngine.roll(f"{character.level}d8")) character.max_hp + 1, EvAdventureRollEngine.roll(f"{character.level}d8")
)
# character sheet visualization # character sheet visualization
class EvAdventureCharacterSheet: class EvAdventureCharacterSheet:
""" """
Generate a character sheet. This is grouped in a class in order to make Generate a character sheet. This is grouped in a class in order to make
@ -596,9 +620,9 @@ class EvAdventureCharacterSheet:
equipment = character.equipment.wielded + character.equipment.worn + character.carried equipment = character.equipment.wielded + character.equipment.worn + character.carried
# divide into chunks of max 10 length (to go into two columns) # divide into chunks of max 10 length (to go into two columns)
equipment_table = EvTable( equipment_table = EvTable(
table=[equipment[i: i + 10] for i in range(0, len(equipment), 10)] table=[equipment[i : i + 10] for i in range(0, len(equipment), 10)]
) )
form = EvForm({"FORMCHAR": 'x', "TABLECHAR": 'c', "SHEET": sheet}) form = EvForm({"FORMCHAR": "x", "TABLECHAR": "c", "SHEET": sheet})
form.map( form.map(
cells={ cells={
1: character.key, 1: character.key,
@ -610,12 +634,12 @@ class EvAdventureCharacterSheet:
7: f"{character.hp}/{character.hp_max}", 7: f"{character.hp}/{character.hp_max}",
8: character.xp, 8: character.xp,
9: character.exploration_speed, 9: character.exploration_speed,
'A': character.combat_speed, "A": character.combat_speed,
'B': character.db.desc, "B": character.db.desc,
}, },
tables={ tables={
1: equipment_table, 1: equipment_table,
} },
) )
return str(form) return str(form)

View file

@ -14,25 +14,33 @@ class EvAdventureMixin:
Provides a set of pre-made characters. Provides a set of pre-made characters.
""" """
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.character = create.create_object(EvAdventureCharacter, key="testchar") self.character = create.create_object(EvAdventureCharacter, key="testchar")
self.helmet = create.create_object( self.helmet = create.create_object(
EvAdventureObject, key="helmet", EvAdventureObject,
attributes=[("inventory_use_slot", enums.WieldLocation.HEAD), key="helmet",
("armor", 1)]) attributes=[("inventory_use_slot", enums.WieldLocation.HEAD), ("armor", 1)],
)
self.shield = create.create_object( self.shield = create.create_object(
EvAdventureObject, key="shield", EvAdventureObject,
attributes=[("inventory_use_slot", enums.WieldLocation.SHIELD_HAND), key="shield",
("armor", 1)]) attributes=[("inventory_use_slot", enums.WieldLocation.SHIELD_HAND), ("armor", 1)],
)
self.armor = create.create_object( self.armor = create.create_object(
EvAdventureObject, key="armor", EvAdventureObject,
attributes=[("inventory_use_slot", enums.WieldLocation.BODY), key="armor",
("armor", 11)]) attributes=[("inventory_use_slot", enums.WieldLocation.BODY), ("armor", 11)],
)
self.weapon = create.create_object( self.weapon = create.create_object(
EvAdventureObject, key="weapon", EvAdventureObject,
attributes=[("inventory_use_slot", enums.WieldLocation.WEAPON_HAND)]) key="weapon",
attributes=[("inventory_use_slot", enums.WieldLocation.WEAPON_HAND)],
)
self.big_weapon = create.create_object( self.big_weapon = create.create_object(
EvAdventureObject, key="big_weapon", EvAdventureObject,
attributes=[("inventory_use_slot", enums.WieldLocation.TWO_HANDS)]) key="big_weapon",
attributes=[("inventory_use_slot", enums.WieldLocation.TWO_HANDS)],
)
self.item = create.create_object(EvAdventureObject, key="backpack item") self.item = create.create_object(EvAdventureObject, key="backpack item")

View file

@ -8,7 +8,7 @@ from evennia.utils.test_resources import BaseEvenniaTest
from evennia.utils import create from evennia.utils import create
from .mixins import EvAdventureMixin from .mixins import EvAdventureMixin
from .. import combat_turnbased from .. import combat_turnbased
from .. charactersd import EvAdventureCharacter from ..charactersd import EvAdventureCharacter
class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest): class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
@ -16,8 +16,12 @@ class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
Test the turn-based combat-handler implementation. Test the turn-based combat-handler implementation.
""" """
@patch("evennia.contrib.tutorials.evadventure.combat_turnbased"
".EvAdventureCombatHandler.interval", new=-1) @patch(
"evennia.contrib.tutorials.evadventure.combat_turnbased"
".EvAdventureCombatHandler.interval",
new=-1,
)
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.combathandler = combat_turnbased.EvAdventureCombatHandler.objects.create() self.combathandler = combat_turnbased.EvAdventureCombatHandler.objects.create()
@ -50,8 +54,6 @@ class EvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin, BaseEvenniaTest):
def test_attack(self, mock_randint): def test_attack(self, mock_randint):
mock_randint = 8 mock_randint = 8
self.combathandler.register_action( self.combathandler.register_action(
combat_turnbased.CombatActionAttack, combat_turnbased.CombatActionAttack, self.combatant, self.target
self.combatant, self.target) )
self.combathandler._end_turn() self.combathandler._end_turn()

View file

@ -19,6 +19,7 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
Test the roll engine in the rules module. This is the core of any RPG. Test the roll engine in the rules module. This is the core of any RPG.
""" """
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.roll_engine = rules.EvAdventureRollEngine() self.roll_engine = rules.EvAdventureRollEngine()
@ -40,15 +41,15 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
def test_roll_limits(self): def test_roll_limits(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.roll_engine.roll('100d6', max_number=10) # too many die self.roll_engine.roll("100d6", max_number=10) # too many die
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.roll_engine.roll('100') # no d self.roll_engine.roll("100") # no d
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.roll_engine.roll('dummy') # non-numerical self.roll_engine.roll("dummy") # non-numerical
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.roll_engine.roll('Ad4') # non-numerical self.roll_engine.roll("Ad4") # non-numerical
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.roll_engine.roll('1d10000') # limit is d1000 self.roll_engine.roll("1d10000") # limit is d1000
@patch("evennia.contrib.tutorials.evadventure.rules.randint") @patch("evennia.contrib.tutorials.evadventure.rules.randint")
def test_roll_with_advantage_disadvantage(self, mock_randint): def test_roll_with_advantage_disadvantage(self, mock_randint):
@ -61,19 +62,18 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
# cancel each other out # cancel each other out
self.assertEqual( self.assertEqual(
self.roll_engine.roll_with_advantage_or_disadvantage( self.roll_engine.roll_with_advantage_or_disadvantage(disadvantage=True, advantage=True),
disadvantage=True, advantage=True), 9) 9,
)
mock_randint.assert_called_once() mock_randint.assert_called_once()
mock_randint.reset_mock() mock_randint.reset_mock()
# run with advantage/disadvantage # run with advantage/disadvantage
self.assertEqual( self.assertEqual(self.roll_engine.roll_with_advantage_or_disadvantage(advantage=True), 9)
self.roll_engine.roll_with_advantage_or_disadvantage(advantage=True), 9)
mock_randint.assert_has_calls([call(1, 20), call(1, 20)]) mock_randint.assert_has_calls([call(1, 20), call(1, 20)])
mock_randint.reset_mock() mock_randint.reset_mock()
self.assertEqual( self.assertEqual(self.roll_engine.roll_with_advantage_or_disadvantage(disadvantage=True), 9)
self.roll_engine.roll_with_advantage_or_disadvantage(disadvantage=True), 9)
mock_randint.assert_has_calls([call(1, 20), call(1, 20)]) mock_randint.assert_has_calls([call(1, 20), call(1, 20)])
mock_randint.reset_mock() mock_randint.reset_mock()
@ -86,39 +86,40 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
character.dexterity = 1 character.dexterity = 1
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw(character, bonus_type=enums.Ability.STR), self.roll_engine.saving_throw(character, bonus_type=enums.Ability.STR), (False, None)
(False, None)) )
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw(character, bonus_type=enums.Ability.DEX, modifier=1), self.roll_engine.saving_throw(character, bonus_type=enums.Ability.DEX, modifier=1),
(False, None)) (False, None),
)
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw( self.roll_engine.saving_throw(
character, character, advantage=True, bonus_type=enums.Ability.DEX, modifier=6
advantage=True, ),
bonus_type=enums.Ability.DEX, modifier=6), (False, None),
(False, None)) )
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw( self.roll_engine.saving_throw(
character, character, disadvantage=True, bonus_type=enums.Ability.DEX, modifier=7
disadvantage=True, ),
bonus_type=enums.Ability.DEX, modifier=7), (True, None),
(True, None)) )
mock_randint.return_value = 1 mock_randint.return_value = 1
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw( self.roll_engine.saving_throw(
character, character, disadvantage=True, bonus_type=enums.Ability.STR, modifier=2
disadvantage=True, ),
bonus_type=enums.Ability.STR, modifier=2), (False, enums.Ability.CRITICAL_FAILURE),
(False, enums.Ability.CRITICAL_FAILURE)) )
mock_randint.return_value = 20 mock_randint.return_value = 20
self.assertEqual( self.assertEqual(
self.roll_engine.saving_throw( self.roll_engine.saving_throw(
character, character, disadvantage=True, bonus_type=enums.Ability.STR, modifier=2
disadvantage=True, ),
bonus_type=enums.Ability.STR, modifier=2), (True, enums.Ability.CRITICAL_SUCCESS),
(True, enums.Ability.CRITICAL_SUCCESS)) )
@patch("evennia.contrib.tutorials.evadventure.rules.randint") @patch("evennia.contrib.tutorials.evadventure.rules.randint")
def test_opposed_saving_throw(self, mock_randint): def test_opposed_saving_throw(self, mock_randint):
@ -130,18 +131,19 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
self.assertEqual( self.assertEqual(
self.roll_engine.opposed_saving_throw( self.roll_engine.opposed_saving_throw(
attacker, defender, attacker, defender, attack_type=enums.Ability.STR, defense_type=enums.Ability.ARMOR
attack_type=enums.Ability.STR, defense_type=enums.Ability.ARMOR
), ),
(False, None) (False, None),
) )
self.assertEqual( self.assertEqual(
self.roll_engine.opposed_saving_throw( self.roll_engine.opposed_saving_throw(
attacker, defender, attacker,
attack_type=enums.Ability.STR, defense_type=enums.Ability.ARMOR, defender,
modifier=2 attack_type=enums.Ability.STR,
defense_type=enums.Ability.ARMOR,
modifier=2,
), ),
(True, None) (True, None),
) )
@patch("evennia.contrib.tutorials.evadventure.rules.randint") @patch("evennia.contrib.tutorials.evadventure.rules.randint")
@ -150,36 +152,40 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
self.assertEqual( self.assertEqual(
self.roll_engine.roll_random_table( self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['physique']), "1d20", random_tables.character_generation["physique"]
"scrawny" ),
"scrawny",
)
self.assertEqual(
self.roll_engine.roll_random_table("1d20", random_tables.character_generation["vice"]),
"irascible",
) )
self.assertEqual( self.assertEqual(
self.roll_engine.roll_random_table( self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['vice']), "1d20", random_tables.character_generation["alignment"]
"irascible" ),
"neutrality",
) )
self.assertEqual( self.assertEqual(
self.roll_engine.roll_random_table( self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['alignment']), "1d20", random_tables.character_generation["helmets and shields"]
"neutrality" ),
) "no helmet or shield",
self.assertEqual(
self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['helmets and shields']),
"no helmet or shield"
) )
# testing faulty rolls outside of the table ranges # testing faulty rolls outside of the table ranges
mock_randint.return_value = 25 mock_randint.return_value = 25
self.assertEqual( self.assertEqual(
self.roll_engine.roll_random_table( self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['helmets and shields']), "1d20", random_tables.character_generation["helmets and shields"]
"helmet and shield" ),
"helmet and shield",
) )
mock_randint.return_value = -10 mock_randint.return_value = -10
self.assertEqual( self.assertEqual(
self.roll_engine.roll_random_table( self.roll_engine.roll_random_table(
"1d20", random_tables.character_generation['helmets and shields']), "1d20", random_tables.character_generation["helmets and shields"]
"no helmet or shield" ),
"no helmet or shield",
) )
@patch("evennia.contrib.tutorials.evadventure.rules.randint") @patch("evennia.contrib.tutorials.evadventure.rules.randint")
@ -202,11 +208,11 @@ class EvAdventureRollEngineTest(BaseEvenniaTest):
mock_randint.return_value = 5 mock_randint.return_value = 5
self.roll_engine.heal_from_rest(character) self.roll_engine.heal_from_rest(character)
self.assertEqual(character.hp, 7) # hp + 1d8 + consititution bonus self.assertEqual(character.hp, 7) # hp + 1d8 + consititution bonus
mock_randint.assert_called_with(1, 8) # 1d8 mock_randint.assert_called_with(1, 8) # 1d8
self.roll_engine.heal_from_rest(character) self.roll_engine.heal_from_rest(character)
self.assertEqual(character.hp, 8) # can't have more than max hp self.assertEqual(character.hp, 8) # can't have more than max hp
@patch("evennia.contrib.tutorials.evadventure.rules.randint") @patch("evennia.contrib.tutorials.evadventure.rules.randint")
def test_roll_death(self, mock_randint): def test_roll_death(self, mock_randint):
@ -246,32 +252,33 @@ class EvAdventureCharacterGenerationTest(BaseEvenniaTest):
self.assertEqual(self.chargen.misfortune, "exiled") self.assertEqual(self.chargen.misfortune, "exiled")
self.assertEqual(self.chargen.armor, "gambeson") self.assertEqual(self.chargen.armor, "gambeson")
self.assertEqual(self.chargen.shield, "shield") self.assertEqual(self.chargen.shield, "shield")
self.assertEqual(self.chargen.backpack, ['ration', 'ration', 'waterskin', self.assertEqual(
'waterskin', 'drill', 'twine']) self.chargen.backpack, ["ration", "ration", "waterskin", "waterskin", "drill", "twine"]
)
def test_build_desc(self): def test_build_desc(self):
self.assertEqual( self.assertEqual(
self.chargen.build_desc(), self.chargen.build_desc(),
"Herbalist. Wears stained clothes, and has hoarse speech. Has a scrawny physique, " "Herbalist. Wears stained clothes, and has hoarse speech. Has a scrawny physique, "
"a broken face, pockmarked skin and greased hair. Is honest, but irascible. " "a broken face, pockmarked skin and greased hair. Is honest, but irascible. "
"Has been exiled in the past. Favors neutrality." "Has been exiled in the past. Favors neutrality.",
) )
@parameterized.expand([ @parameterized.expand(
# source, target, value, new_source_val, new_target_val [
(enums.Ability.CON, enums.Ability.STR, 1, 1, 3), # source, target, value, new_source_val, new_target_val
(enums.Ability.INT, enums.Ability.DEX, 1, 1, 3), (enums.Ability.CON, enums.Ability.STR, 1, 1, 3),
(enums.Ability.CHA, enums.Ability.CON, 1, 1, 3), (enums.Ability.INT, enums.Ability.DEX, 1, 1, 3),
(enums.Ability.STR, enums.Ability.WIS, 1, 1, 3), (enums.Ability.CHA, enums.Ability.CON, 1, 1, 3),
(enums.Ability.WIS, enums.Ability.CHA, 1, 1, 3), (enums.Ability.STR, enums.Ability.WIS, 1, 1, 3),
(enums.Ability.DEX, enums.Ability.DEX, 1, 2, 2), (enums.Ability.WIS, enums.Ability.CHA, 1, 1, 3),
]) (enums.Ability.DEX, enums.Ability.DEX, 1, 2, 2),
]
)
def test_adjust_attribute(self, source, target, value, new_source_val, new_target_val): def test_adjust_attribute(self, source, target, value, new_source_val, new_target_val):
self.chargen.adjust_attribute(source, target, value) self.chargen.adjust_attribute(source, target, value)
self.assertEqual( self.assertEqual(getattr(self.chargen, source.value), new_source_val, f"{source}->{target}")
getattr(self.chargen, source.value), new_source_val, f"{source}->{target}") self.assertEqual(getattr(self.chargen, target.value), new_target_val, f"{source}->{target}")
self.assertEqual(
getattr(self.chargen, target.value), new_target_val, f"{source}->{target}")
def test_adjust_consecutive(self): def test_adjust_consecutive(self):
# gradually shift all to STR (starts at 2) # gradually shift all to STR (starts at 2)
@ -311,6 +318,7 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
Test the equipment mechanism. Test the equipment mechanism.
""" """
def _get_empty_slots(self): def _get_empty_slots(self):
return { return {
enums.WieldLocation.BACKPACK: [], enums.WieldLocation.BACKPACK: [],
@ -324,15 +332,17 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
def test_equipmenthandler_max_slots(self): def test_equipmenthandler_max_slots(self):
self.assertEqual(self.character.equipment.max_slots, 11) self.assertEqual(self.character.equipment.max_slots, 11)
@parameterized.expand([ @parameterized.expand(
# size, pass_validation? [
(1, True), # size, pass_validation?
(2, True), (1, True),
(11, True), (2, True),
(12, False), (11, True),
(20, False), (12, False),
(25, False) (20, False),
]) (25, False),
]
)
def test_validate_slot_usage(self, size, is_ok): def test_validate_slot_usage(self, size, is_ok):
obj = MagicMock() obj = MagicMock()
obj.size = size obj.size = size
@ -343,15 +353,17 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
with self.assertRaises(characters.EquipmentError): with self.assertRaises(characters.EquipmentError):
self.character.equipment.validate_slot_usage(obj) self.character.equipment.validate_slot_usage(obj)
@parameterized.expand([ @parameterized.expand(
# item, where [
("helmet", enums.WieldLocation.HEAD), # item, where
("shield", enums.WieldLocation.SHIELD_HAND), ("helmet", enums.WieldLocation.HEAD),
("armor", enums.WieldLocation.BODY), ("shield", enums.WieldLocation.SHIELD_HAND),
("weapon", enums.WieldLocation.WEAPON_HAND), ("armor", enums.WieldLocation.BODY),
("big_weapon", enums.WieldLocation.TWO_HANDS), ("weapon", enums.WieldLocation.WEAPON_HAND),
("item", enums.WieldLocation.BACKPACK), ("big_weapon", enums.WieldLocation.TWO_HANDS),
]) ("item", enums.WieldLocation.BACKPACK),
]
)
def test_use(self, itemname, where): def test_use(self, itemname, where):
self.assertEqual(self.character.equipment.slots, self._get_empty_slots()) self.assertEqual(self.character.equipment.slots, self._get_empty_slots())
@ -366,32 +378,32 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
def test_store(self): def test_store(self):
self.character.equipment.store(self.weapon) self.character.equipment.store(self.weapon)
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], None) self.assertEqual(self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], None)
self.assertTrue( self.assertTrue(self.weapon in self.character.equipment.slots[enums.WieldLocation.BACKPACK])
self.weapon in self.character.equipment.slots[enums.WieldLocation.BACKPACK])
def test_two_handed_exclusive(self): def test_two_handed_exclusive(self):
"""Two-handed weapons can't be used together with weapon+shield""" """Two-handed weapons can't be used together with weapon+shield"""
self.character.equipment.use(self.big_weapon) self.character.equipment.use(self.big_weapon)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], self.big_weapon) self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], self.big_weapon
)
# equipping sword or shield removes two-hander # equipping sword or shield removes two-hander
self.character.equipment.use(self.shield) self.character.equipment.use(self.shield)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield) self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield
self.assertEqual( )
self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], None) self.assertEqual(self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], None)
self.character.equipment.use(self.weapon) self.character.equipment.use(self.weapon)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], self.weapon) self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], self.weapon
)
# the two-hander removes the two weapons # the two-hander removes the two weapons
self.character.equipment.use(self.big_weapon) self.character.equipment.use(self.big_weapon)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], self.big_weapon) self.character.equipment.slots[enums.WieldLocation.TWO_HANDS], self.big_weapon
self.assertEqual( )
self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None) self.assertEqual(self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None)
self.assertEqual( self.assertEqual(self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], None)
self.character.equipment.slots[enums.WieldLocation.WEAPON_HAND], None)
def test_remove__with_obj(self): def test_remove__with_obj(self):
self.character.equipment.use(self.shield) self.character.equipment.use(self.shield)
@ -399,16 +411,19 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
self.character.equipment.store(self.weapon) self.character.equipment.store(self.weapon)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield) self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.BACKPACK], )
[self.item, self.weapon]) self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.BACKPACK], [self.item, self.weapon]
)
self.assertEqual(self.character.equipment.remove(self.shield), [self.shield]) self.assertEqual(self.character.equipment.remove(self.shield), [self.shield])
self.assertEqual(self.character.equipment.remove(self.item), [self.item]) self.assertEqual(self.character.equipment.remove(self.item), [self.item])
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None) self.assertEqual(self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None)
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.BACKPACK], self.assertEqual(
[self.weapon]) self.character.equipment.slots[enums.WieldLocation.BACKPACK], [self.weapon]
)
def test_remove__with_slot(self): def test_remove__with_slot(self):
self.character.equipment.use(self.shield) self.character.equipment.use(self.shield)
@ -416,17 +431,20 @@ class EvAdventureEquipmentTest(EvAdventureMixin, BaseEvenniaTest):
self.character.equipment.store(self.helmet) self.character.equipment.store(self.helmet)
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield) self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], self.shield
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.BACKPACK], )
[self.item, self.helmet]) self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.BACKPACK], [self.item, self.helmet]
self.assertEqual(self.character.equipment.remove(enums.WieldLocation.SHIELD_HAND), )
[self.shield])
self.assertEqual(self.character.equipment.remove(enums.WieldLocation.BACKPACK),
[self.item, self.helmet])
self.assertEqual( self.assertEqual(
self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None) self.character.equipment.remove(enums.WieldLocation.SHIELD_HAND), [self.shield]
)
self.assertEqual(
self.character.equipment.remove(enums.WieldLocation.BACKPACK), [self.item, self.helmet]
)
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.SHIELD_HAND], None)
self.assertEqual(self.character.equipment.slots[enums.WieldLocation.BACKPACK], []) self.assertEqual(self.character.equipment.slots[enums.WieldLocation.BACKPACK], [])
def test_properties(self): def test_properties(self):

View file

@ -13,6 +13,3 @@ from . import enums
from . import combat_turnbased from . import combat_turnbased
from . import rules from . import rules
from . import random_tables from . import random_tables

View file

@ -2,5 +2,3 @@
Various utilities. Various utilities.
""" """