Skip to content

Instantly share code, notes, and snippets.

@justinabrahms
Created September 3, 2010 03:40
Show Gist options
  • Save justinabrahms/563386 to your computer and use it in GitHub Desktop.
Save justinabrahms/563386 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2.6
#
# Simple class I used for a little while to play my D&D character when I didn't have dice. Worked okay, but I then found dice.
#
from collections import defaultdict
from random import randint
def roll(dice_type):
rolls = []
mod = 0
if '+' in dice_type:
dice, mod = dice_type.split('+')
mod = int(mod)
elif '-' in dice_type:
dice, mod = dice_type.split('-')
mod = int(mod)
mod = -mod
else:
dice = dice_type
if 'd' in dice:
count, sides = dice.split('d')
sides = int(sides)
count = int(count)
for roll in xrange(count):
rolls.append(randint(1, sides))
for count, i in enumerate(rolls):
print "%d: %s (%s + %s)" % (count + 1, i + mod, i, mod)
return map(lambda x: x + mod, rolls)
class WeaponNotWielded(Exception):
pass
class Weapon(object):
is_2hd = True
dmg_dr = 0
dmg_mod = 0
hit_mod = 0
crit_range = (20, 20)
crit_modifier = 2
def __init__(self, hit_mod, dmg_mod):
self.dmg_mod, self.hit_mod = dmg_mod, hit_mod
def __repr__(self):
return "<%s object>" % self.name
def swing(self, misc_hit_mod=0, misc_dmg_mod=0, critable=True):
base_hit_die = randint(1, 20)
crit = True if base_hit_die >= self.crit_range[0] else False
to_hit = base_hit_die + self.hit_mod + misc_hit_mod
base_damage = randint(1, self.dmg_dr)
damage = base_damage + self.dmg_mod + misc_dmg_mod
print "To Hit: %d (%d + %d)" % (to_hit, base_hit_die, misc_hit_mod + self.hit_mod)
print "Damage: %d (%d + %d)" % (damage, base_damage, misc_dmg_mod + self.dmg_mod)
if crit and critable:
# FIXME: Need to take into account 2x 3x weapons
print "**Possible Critical**"
print "Follow-up:"
self.swing(misc_hit_mod, misc_dmg_mod, critable=False)
def power_attack(self, amount):
pattack_modifier = 1
if self.is_2hd:
pattack_modifier = 2
total_mod = amount * pattack_modifier
return self.swing(misc_hit_mod= -(amount), misc_dmg_mod=total_mod)
class Kopesh(Weapon):
name = "kopesh"
is_2hd = True
dmg_dr = 10
crit_range = (19, 20)
class Glaive(Weapon):
name = "glaive"
is_2hd = True
dmg_dr = 10
class Arsenal(object):
"""
A container for a list of Weapon objects.
"""
_weapon_rack = {}
def __init__(self, *weapon_objs):
for weapon in weapon_objs:
self.add(weapon)
def __getattribute__(self, attr):
try:
return super(Arsenal, self).__getattribute__(attr)
except AttributeError:
return self._weapon_rack[attr]
def __repr__(self):
return ', '.join([repr(x) for x in self._weapon_rack.values()]) or "Empty."
def add(self, weapon):
if not isinstance(weapon, Weapon):
raise TypeError("Can't add %s. You can only add weapons to your arsenal." % weapon)
try:
return self._weapon_rack[weapon.name]
except KeyError:
self._weapon_rack[weapon.name] = weapon
return weapon
def remove(self, weapon):
try:
self._weapon_rack.remove(weapon)
except ValueError:
print "%s isn't in your arsenal." % weapon
class Spell(object):
def __init__(self, name, level):
self.name, self.level = name, level
class Spellbook(object):
_spells = defaultdict(list)
def learn(self, spell):
if not isinstance(spell, Spell):
raise TypeError("You can only add spells to your spellbook.")
self._spells['%s' % spell.level].append(spell)
def forget(self, spell):
self._spells['%s' % spell.level].remove(spell)
class Stat(object):
name = None
score = 10
def __init__(self, name, score):
self.name, self.score = name, score
def __unicode__(self):
return "%d" % self.score
def __repr__(self):
return "%d" % self.score
@property
def modifier(self):
stats_mods = {
'0-1': -5,
'2-3': -4,
'4-5': -3,
'6-7': -2,
'8-9': -1,
'10-11': 0,
'12-13': 1,
'14-15': 2,
'16-17': 3,
'18-19': 4,
'20-21': 5,
}
for k,v in stats_mods.items():
low, high = k.split('-')
if self.score >= int(low) and self.score <= int(high):
return v
class Character(object):
"""
TODOs:
* Abstract Ramatan out to Character
- Add database support (couchdb) to preserve ramatan's stats
* Abstract out a "Class" with archtypes of caster or melee
* Implement spell casting.
"""
name = "Ramatan"
player_class = "Fighter"
hp = 42
initiative = None
level = 4
str = Stat('str', 18)
dex = Stat('dex', 13)
int = Stat('int', 8)
wis = Stat('wis', 10)
con = Stat('con', 14)
cha = Stat('cha', 8)
spells = Spellbook()
weapons = Arsenal(Kopesh(10,9), Glaive(8,6))
current_weapon = None
def __init__(self):
print "Welcome, %s!" % self.name
self.current_hp = self.hp
def __repr__(self):
return "<%s lvl %s %s>" % (self.name, self.level, self.player_class)
def new_initiative(self):
# FIXME: Need to add support for feats, hence the +4
self.initiative = randint(1, 20) + self.dex.modifier + 4
def wield(self, weapon):
self.current_weapon = weapon
def drop(self, weapon):
self.current_weapon = None
def attack(self, *args, **kwargs):
try:
attack_type = kwargs['attack_type']
except KeyError:
attack_type = 'swing'
if self.current_weapon is None:
raise WeaponNotWielded("You do not have a weapon wielded")
return getattr(self.current_weapon, attack_type)(*args)
def hit_for(self, amount):
self.current_hp = self.current_hp - amount
if self.current_hp <= -10:
print "DEAD!!! Took you to: %d" % self.current_hp
elif self.current_hp <= 0:
print "Unconcious. Now at: %d" % self.current_hp
else:
print "Ouch! Now at: %d" % self.current_hp
def healed_for(self, amount):
self.current_hp = self.current_hp + amount
if self.current_hp > self.hp:
self.current_hp = self.hp
print "Wee! Now at: %d" % self.current_hp
def status(self):
print "%s has %d/%d HP" % (self.name, self.current_hp, self.hp)
print "He is wielding %s" % self.current_weapon
if self.initiative:
print "His initiative for this combat is: %d" % self.initiative
else:
print "He has not rolled initiative."
print "HP: %d/%d" % (self.current_hp, self.hp)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment