Skip to content

Commit

Permalink
fix weapon select
Browse files Browse the repository at this point in the history
  • Loading branch information
asahala authored Mar 27, 2020
1 parent e0c57e6 commit 98efa67
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 123 deletions.
130 changes: 73 additions & 57 deletions creature.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
class Basecreature(object):

def __init__(self, name, size, category, cr, ac, hp, speed,
scores, melee_attacks, ranged_attacks=[], actions=[],
scores, melee_attacks, multi_attacks=[], ranged_attacks=[], actions=[],
speed_fly = 0,
saves=None,
passives=[],
Expand All @@ -40,6 +40,7 @@ def __init__(self, name, size, category, cr, ac, hp, speed,

self.melee_attacks = melee_attacks
self.ranged_attacks = ranged_attacks
self.multi_attacks = multi_attacks

self.actions = actions
self.passives = passives
Expand Down Expand Up @@ -95,7 +96,7 @@ def __init__(self, name, size, category, cr, ac, hp, speed,
self.position = (0,0,0)

#TODO movement
self.distance = 30
self.distance = 0

def __repr__(self):
CR = {0.125: "1/8", 0.25: "1/4", 0.5: "1/2"}
Expand Down Expand Up @@ -191,7 +192,7 @@ def begin_turn(self):
actions such as standing up, recharging abilities etc.
Return True if creature did not use its action """

self.distance = 0
self.speed = self.max_speed.copy()

""" Recharge abilities """
Expand Down Expand Up @@ -235,16 +236,32 @@ def end_turn(self):
if R.roll_save(self, ability, dc):
self.set_paralysis(False, dc=0, save=None)

def take_damage(self, damage, dmg_type, crit_multiplier):
def take_damage(self, source, damage, dmg_type, crit_multiplier):

""" Check if creature has vulnerability, resistance or immunity
to the given damage type """
damage = self.check_resistances(dmg_type, damage)
damage = self.check_vulnerabilities(dmg_type, damage)

""" Store damage statistics """
source.damage_dealt += damage

self.hp -= damage

messages.IO.total_damage.setdefault(dmg_type, 0)
messages.IO.total_damage[dmg_type] += damage
messages.IO.hp = self.hp
messages.IO.target_name = self.name
messages.IO.printlog()
""" Check if creature can drop to 1 HP instead of 0 """
if self.hp <= 0 and self.passives:
for passive in self.passives:
if passive.type == 'avoid_death':
self.hp = passive.use(self, damage, dmg_type, crit_multiplier)

if self.is_dead:
messages.IO.printmsg("-> %s is dead. " % self.name, 2, True, False)

def set_grapple(self, state, dc=0, save='str', source=None):
if state:
messages.IO.printmsg("-> %s is restrained. " % self.name, 2, True, False)
Expand Down Expand Up @@ -295,19 +312,21 @@ def set_prone(self, state):
if state:
#messages.IO.conditions.append("-> %s falls prone. " % self.name)
messages.IO.printmsg("-> %s falls prone. " % self.name, 2, True, False)
self.set_advantage('hit', -1)
else:
#messages.IO.conditions.append("%s stands up. " % self.name)
messages.IO.printmsg("%s stands up. " % self.name, 2, True, True)
self.set_advantage('hit', -1)
self.set_advantage('hit', 0)

self.prone = state

def set_poison(self, state, dc=0, save='con'):
if not 'poison' in self.immunities:
if state:
messages.IO.printmsg("-> %s is poisoned. " % self.name, 2, True, False)
self.set_advantage('hit', 0)
else:
self.set_advantage('hit', -1)
else:
self.set_advantage('hit', 0)
messages.IO.printmsg("%s recovers from poison. " % self.name, 2, True, True)
self.poisoned["state"] = state
self.poisoned["dc"] = dc
Expand Down Expand Up @@ -399,34 +418,46 @@ def move(self, enemy, weapon):
return False

def select_weapon(self, enemies):
""" Use melee weapon unless adjacent to an enemy """

def pick():
attacks = []
min_distance = 0
for weapon in self.melee_attacks:
if isinstance(weapon, list):
for subweapon in weapon:
if subweapon.special:
attacks = [weapon]
min_distance = weapon.min_distance
else:
if weapon.special:
attacks = [weapon]
min_distance = weapon.min_distance
return attacks, min_distance

""" Priority 1: Use a weapon with special ability """
attacks, min_distance = pick()

""" Override priority: If ranged weapon is available, use it """
if self.ranged_attacks:
opts = [a for a in self.ranged_attacks if a.ammo > 0]
if opts:
attacks = opts
else:
attacks = self.melee_attacks
else:

""" If not chosen yet, pick one melee attack """
if not attacks:
attacks = self.melee_attacks

""" Override choice if next to enemy """
for e in enemies.members:
if world.is_adjacent(self.position, e.position):
attacks = self.melee_attacks
#print(self.name, ' is next to ', e.name)
if isinstance(attacks[0], list):
pass
else:
attacks = [x for x in self.melee_attacks if x.min_distance <= 5]
break

'''
if True in [world.is_adjacent(self.position, e.position)
for e in enemies.members]:
attacks = self.melee_attacks
else:
if self.ranged_attacks:
options = [a for a in self.ranged_attacks if a.ammo > 0]
if options:
attacks = options
else:
attacks = self.melee_attacks'''
return random.choice(attacks)

def perform_action(self, allies, enemies):
Expand Down Expand Up @@ -541,7 +572,7 @@ def set_formation(self, position):
i += 1

spider_web = Restrain(name="Web", dc=11, save='str', to_hit=5, recharge=5)
# minotaur_gore = Gore(name="Gore", dc=14, save='str', to_hit=6, damage=["4d8+4"], damage_type=["piercing"])
#minotaur_gore = Gore(name="Gore", dc=14, save='str', to_hit=6, damage=["4d8+4"], damage_type=["piercing"])
pack_tactics = PackTactics

wolf_bite = Weapon(name='bite',
Expand Down Expand Up @@ -596,6 +627,7 @@ def set_formation(self, position):
damage=["1d6+3"],
damage_type=["slashing"],
reach=5,
min_distance=20,
to_hit=5,
special=[Knockdown(name="pounce", dc=13, save='str',
charge_distance=20,
Expand Down Expand Up @@ -651,18 +683,27 @@ def set_formation(self, position):
category="beast",
scores={'str': 15, 'dex': 10, 'con': 14,
'int': 2, 'wis': 12, 'cha': 7},
melee_attacks=[(Weapon(name='bite', damage=["1d6+2"], damage_type=["piercing"], reach=5, to_hit=3),
melee_attacks=[[Weapon(name='bite', damage=["1d6+2"], damage_type=["piercing"], reach=5, to_hit=3),
Weapon(name='claws', damage=["2d4+2"], damage_type=["slashing"], reach=5,
to_hit=3))])
to_hit=3)]])

brown_bear = Basecreature(name='brown bear', cr=1, ac=11, hp=34, speed=40,
size='large',
category="beast",
scores={'str': 19, 'dex': 10, 'con': 16,
'int': 2, 'wis': 13, 'cha': 7},
melee_attacks=[(Weapon(name='bite', damage=["1d8+4"], damage_type=["piercing"], reach=5, to_hit=5),
Weapon(name='claws', damage=["2d6+4"], damage_type=["slashing"], reach=5,
to_hit=5))])
melee_attacks=[
[Weapon(name='bite',
damage=["1d8+4"],
damage_type=["piercing"],
reach=5,
to_hit=5),
Weapon(name='claws',
damage=["2d6+4"],
damage_type=["slashing"],
reach=5,
to_hit=5)]
])

crocodile = Basecreature(name='crocodile', cr=0.5, ac=12, hp=19, speed=20,
size='large',
Expand Down Expand Up @@ -692,7 +733,7 @@ def set_formation(self, position):
category="beast",
scores={'str': 17, 'dex': 15, 'con': 13,
'int': 3, 'wis': 12, 'cha': 8},
melee_attacks=[lion_bite, lion_claw, lion_pounce],
melee_attacks=[lion_pounce, lion_bite, lion_claw],
passives=[pack_tactics])

mammoth = Basecreature(name='mammoth', cr=6, ac=13, hp=126, speed=40,
Expand Down Expand Up @@ -860,38 +901,13 @@ def fight(self):
while i < 1:
print("Match %i" % i)
team1 = Party(name='Team A')
team1.add(copy.deepcopy(skeleton))
team1.add(copy.deepcopy(skeleton))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(zombie))
team1.add(copy.deepcopy(black_bear))
team1.add(copy.deepcopy(brown_bear))
team1.set_formation((0,3,0))


team2 = Party(name='Team B')
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(dire_wolf))
team2.add(copy.deepcopy(black_bear))
team2.add(copy.deepcopy(black_bear))
team2.add(copy.deepcopy(minotaur))
team2.set_formation((0,-3,0))


Expand Down
1 change: 1 addition & 0 deletions dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re

def roll(times, sides, bonus, advantage=0):

if advantage == -1:
#print('disadvantage')
return min(roll(times, sides, bonus), roll(times, sides, bonus))
Expand Down
52 changes: 24 additions & 28 deletions mechanics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,6 @@ def __init__(self, x=0, y=0):

class DnDRuleset:

@staticmethod
def parse_damage_type(damage_type):
""" Parse damage types such as poison(DC:con:0.5:11) that
contain, type, saving throw, damage multiplier if
successful and DC """
dmg_type = re.sub("\(.+$", "", damage_type)
return re.sub('.*\((.+)\).*', r'\1', damage_type).split(':') + [dmg_type]

@staticmethod
def roll_hit(source, target, attack):
""" Genera hit roller
Expand All @@ -39,7 +31,6 @@ def roll_hit(source, target, attack):
source.set_advantage('hit', 1)

advantage = source.advantage['hit']

hitroll = dice.roll(1, 20, 0, advantage)

""" Apply critical multiplier """
Expand All @@ -51,19 +42,16 @@ def roll_hit(source, target, attack):
multiplier = 2


critical_failure_effect = False

""" Check if attack hits """
if hitroll == 1:
hit = False
# TODO: CRITICAL FAILURES
roll = dice.roll(times=1, sides=4, bonus=0)
roll = dice.roll(times=1, sides=3, bonus=0)
if roll == 1:
source.set_prone(True)
msg = "falls prone due to critical FAILURE attacking"
elif roll == 2:
dmg = dice.roll(times=1, sides=6, bonus=0)
source.hp -= dmg
msg = "hurts itself (%i dmg) and falls prone due to critical FAILURE attacking" % dmg
source.set_prone(True)
critical_failure_effect = True
else:
msg = "FAILS critically attacking"
elif hitroll == 20:
Expand All @@ -76,16 +64,29 @@ def roll_hit(source, target, attack):
hit = False
msg = "misses"

if advantage == 1:
adv = ' (adv.)'
elif advantage == -1:
adv = ' (disadv.)'
else:
adv = ''

messages.IO.log += "{source} {hit} {target}"\
" with {attackname}.".format(source=source.name,
" with {attackname}{adv}.".format(source=source.name,
target=target.name,
attackname=attack_name,
hit=msg)
hit=msg,
adv=adv)

if not hit:
messages.IO.printlog()
messages.IO.reset()

""" Set critical failure effects """
if critical_failure_effect:
source.set_prone(True)
source.take_damage(source, 4, 'bludgeoning', 1)

return hit, multiplier, hitroll + bonus

@staticmethod
Expand All @@ -112,7 +113,7 @@ def iterate_damage(source, target, weapon, crit_multiplier=1):
dmg = weapon.damage[i]
dmg_type = weapon.damage_type[i]
DnDRuleset.roll_damage(source, target, weapon, dmg, dmg_type, crit_multiplier)
messages.IO.printlog()


@staticmethod
def roll_damage(source, target, weapon, dmg, dmg_type, crit_multiplier=1):
Expand All @@ -132,20 +133,15 @@ def roll_damage(source, target, weapon, dmg, dmg_type, crit_multiplier=1):

""" Check if target has resistance or immunity to
the given damage type """
damage = target.check_resistances(dmg_type, damage)
#damage = target.check_resistances(dmg_type, damage)

""" Check vulnerabilities """
damage = target.check_vulnerabilities(dmg_type, damage)
#damage = target.check_vulnerabilities(dmg_type, damage)

""" Store damage statistics """
source.damage_dealt += damage
#source.damage_dealt += damage

""" Subtract damage from target's HP pool """
target.take_damage(damage, dmg_type, crit_multiplier)

messages.IO.total_damage.setdefault(dmg_type, 0)
messages.IO.total_damage[dmg_type] += damage
messages.IO.hp = target.hp
messages.IO.target_name = target.name
target.take_damage(source, damage, dmg_type, crit_multiplier)


6 changes: 3 additions & 3 deletions messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ def printlog():
for condition in IO.conditions:
IO.printmsg("-> " + condition, 2, True, False)

if IO.hp <= 0 and IO.target_name:
death = "-> %s is dead!" % IO.target_name
IO.printmsg(death, 2, True, False)
#if IO.hp <= 0 and IO.target_name:
# death = "-> %s is dead!" % IO.target_name
# IO.printmsg(death, 2, True, False)

@staticmethod
def center_and_pad(string, padding=":"):
Expand Down
Loading

0 comments on commit 98efa67

Please sign in to comment.