Created
September 3, 2010 03:40
-
-
Save justinabrahms/563386 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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