422 lines
15 KiB
Python
422 lines
15 KiB
Python
|
|
#!/usr/bin/python
|
||
|
|
|
||
|
|
import string
|
||
|
|
import sys
|
||
|
|
import getopt
|
||
|
|
|
||
|
|
# THACO charts for character classes and monstrous races
|
||
|
|
# [class name, THAC0 at 1st level, 2nd level, .... 20th
|
||
|
|
# level]
|
||
|
|
|
||
|
|
brief = False
|
||
|
|
|
||
|
|
thaco_charts = {
|
||
|
|
"fighter": [ 20, 20,
|
||
|
|
18, 18,
|
||
|
|
16, 16,
|
||
|
|
14, 14,
|
||
|
|
12, 12,
|
||
|
|
10, 10,
|
||
|
|
8, 8,
|
||
|
|
6, 6,
|
||
|
|
4, 4,
|
||
|
|
2, 2],
|
||
|
|
"paladin" : [ 20, 20,
|
||
|
|
18, 18,
|
||
|
|
16, 16,
|
||
|
|
14, 14,
|
||
|
|
12, 12,
|
||
|
|
10, 10,
|
||
|
|
8, 8,
|
||
|
|
6, 6,
|
||
|
|
4, 4,
|
||
|
|
2, 2],
|
||
|
|
"ranger" : [ 20, 20,
|
||
|
|
18, 18,
|
||
|
|
16, 16,
|
||
|
|
14, 14,
|
||
|
|
12, 12,
|
||
|
|
10, 10,
|
||
|
|
8, 8,
|
||
|
|
6, 6,
|
||
|
|
4, 4,
|
||
|
|
2, 2],
|
||
|
|
"bard" : [ 20, 20,
|
||
|
|
18, 18,
|
||
|
|
16, 16,
|
||
|
|
14, 14,
|
||
|
|
12, 12,
|
||
|
|
10, 10,
|
||
|
|
8, 8,
|
||
|
|
6, 6,
|
||
|
|
4, 4,
|
||
|
|
2, 2],
|
||
|
|
"cleric" : [ 20, 20, 20,
|
||
|
|
18, 18, 18,
|
||
|
|
16, 16, 16,
|
||
|
|
14, 14, 14,
|
||
|
|
12, 12, 12,
|
||
|
|
10, 10, 10,
|
||
|
|
9, 9],
|
||
|
|
"druid" : [ 20, 20, 20,
|
||
|
|
18, 18, 18,
|
||
|
|
16, 16, 16,
|
||
|
|
14, 14, 14,
|
||
|
|
12, 12, 12,
|
||
|
|
10, 10, 10,
|
||
|
|
9, 9],
|
||
|
|
"monk" : [ 20, 20, 20,
|
||
|
|
18, 18, 18,
|
||
|
|
16, 16, 16,
|
||
|
|
14, 14, 14,
|
||
|
|
12, 12, 12,
|
||
|
|
10, 10, 10,
|
||
|
|
9, 9],
|
||
|
|
"magic user" : [20, 20, 20, 20, 20,
|
||
|
|
19, 19, 19, 19, 19,
|
||
|
|
16, 16, 16, 16, 16,
|
||
|
|
13, 13, 13, 13, 13],
|
||
|
|
"illusionist" :[20, 20, 20, 20, 20,
|
||
|
|
19, 19, 19, 19, 19,
|
||
|
|
16, 16, 16, 16, 16,
|
||
|
|
13, 13, 13, 13, 13],
|
||
|
|
"thief" : [ 20, 20, 20, 20,
|
||
|
|
19, 19, 19, 19,
|
||
|
|
16, 16, 16, 16,
|
||
|
|
14, 14, 14, 14,
|
||
|
|
12, 12, 12, 12],
|
||
|
|
"assassin" : [ 20, 20, 20, 20,
|
||
|
|
19, 19, 19, 19,
|
||
|
|
16, 16, 16, 16,
|
||
|
|
14, 14, 14, 14,
|
||
|
|
12, 12, 12, 12],
|
||
|
|
"monster" : [ 19,
|
||
|
|
16, 16, 16,
|
||
|
|
15, 15,
|
||
|
|
13, 13,
|
||
|
|
12, 12,
|
||
|
|
10, 10,
|
||
|
|
9, 9,
|
||
|
|
8, 8,
|
||
|
|
7, 7, 7, 7]}
|
||
|
|
|
||
|
|
melee_weapon_charts = [
|
||
|
|
# name AC 10 to 2 (from 10 to 2)
|
||
|
|
["unarmed", 4, 0, 2, 0, 0, -1, -3, -5, -7],
|
||
|
|
["battle axe", 2, 1, 1, 0, 0, -1, -2, -2, -2],
|
||
|
|
["hand axe", 1, 1, 1, 0, 0, -1, -2, -2, -2],
|
||
|
|
["bardiche", 3, 2, 2, 1, 1, 0, 0, -1, -2],
|
||
|
|
["bec de corbin",
|
||
|
|
-1, 0, 0, 0, 0, 0, 2, 2, 2],
|
||
|
|
["bill-guisarme",
|
||
|
|
0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||
|
|
["bo stick", 3, 0, 1, 0, -1, -3, -5, -7, -9],
|
||
|
|
["club", 1, 0, 0, -1, -1, -2, -3, -4, -5],
|
||
|
|
["dagger", 3, 1, 1, 0, 0, -2, -2, -3, -3],
|
||
|
|
["fauchard", -1, 1, 0, 0, 0, -1, -1, -2, -2],
|
||
|
|
["fauchard-fork",
|
||
|
|
1, 0, 1, 0, 0, 0, -1, -1, -1],
|
||
|
|
["footman's flail",
|
||
|
|
-1, 1, 1, 1, 1, 2, 1, 2, 2],
|
||
|
|
["horseman's flail",
|
||
|
|
0, 1, 1, 1, 0, 0, 0, 0, 0],
|
||
|
|
["military fork",
|
||
|
|
1, 0, 1, 1, 0, 0, -1, -2, -2],
|
||
|
|
["glaive", 0, 0, 0, 0, 0, 0, 0, -1, -1],
|
||
|
|
["glaive-guisarme",
|
||
|
|
0, 0, 0, 0, 0, 0, 0, -1, -1],
|
||
|
|
["guisarme", -1, -1, 0, 0, 0, -1, -1, -2, -2],
|
||
|
|
["guisarme-voulge",
|
||
|
|
0, 0, 0, 1, 1, 1, 0, -1, -1],
|
||
|
|
["halberd", 0, 1, 1, 2, 2, 2, 1, 1, 1],
|
||
|
|
["lucern hammer",
|
||
|
|
0, 0, 1, 1, 2, 2, 2, 1, 1],
|
||
|
|
["hammer", 0, 0, 0, 0, 0, 1, 0, 1, 0],
|
||
|
|
["jo stick", 2, 0, 1, 0, -1, -2, -4, -6, -8],
|
||
|
|
["heavy lance", 0, 0, 1, 1, 2, 2, 2, 3, 3],
|
||
|
|
["light lance", 0, 0, 0, 0, 0, 0, -1, -2, -2],
|
||
|
|
["medium lance",0, 0, 0, 0, 1, 1, 1, 1, 0],
|
||
|
|
["footman's mace",
|
||
|
|
-1, 1, 0, 0, 0, 0, 0, 1, 1],
|
||
|
|
["horseman's mace",
|
||
|
|
0, 0, 0, 0, 0, 0, 0, 1, 1],
|
||
|
|
["morning star",2, 2, 1, 1, 1, 1, 1, 1, 0],
|
||
|
|
["partisan", 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||
|
|
["military footman's pick",
|
||
|
|
-2, -1, -1, -1, 0, 1, 1, 2, 2],
|
||
|
|
["military horseman's pick",
|
||
|
|
-1, -1, -1, 0, 0, 1, 1, 1, 1],
|
||
|
|
["awl pike", -2, -1, 0, 0, 0, 0, 0, 0, -1],
|
||
|
|
["ranseur", 1, 0, 0, 0, 0, 0, -1, -1, -2],
|
||
|
|
["scimitar", 3, 1, 1, 0, 0, -1, -2, -2, -3],
|
||
|
|
["spear", 0, 0, 0, 0, 0, -1, -1, -1, -2],
|
||
|
|
["spetum", 2, 1, 0, 0, 0, 0, 0, -1, -2],
|
||
|
|
["quarter staff",
|
||
|
|
1, 1, 1, 0, 0, -1, -3, -5, -7],
|
||
|
|
["bastard sword",
|
||
|
|
0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||
|
|
["broad sword", 2, 1, 1, 1, 0, 0, -1, -2, -3],
|
||
|
|
["long sword", 2, 1, 0, 0, 0, 0, 0, -1, -2],
|
||
|
|
["short sword", 2, 0, 1, 0, 0, 0, -1, -2, -3],
|
||
|
|
["two handed sword",
|
||
|
|
0, 1, 3, 3, 3, 2, 2, 2, 2],
|
||
|
|
["trident", 1, 0, 1, 0, 0, -1, -1, -2, -3],
|
||
|
|
["voulge", 0, 0, 0, 1, 1, 1, 0, -1, -1],
|
||
|
|
# placeholder...
|
||
|
|
["", 0, 0, 0, 0, 0, 0, 0, 0, 0]]
|
||
|
|
|
||
|
|
ranged_weapon_charts = [
|
||
|
|
["hand axe", 1, 0, 0, 0, -1, -1, -2, -3, -4],
|
||
|
|
["composite long bow",
|
||
|
|
3, 3, 2, 2, 1, 0, 0, -1, -2],
|
||
|
|
["composite short bow",
|
||
|
|
3, 2, 2, 2, 1, 0, -1, -3, -3],
|
||
|
|
["long bow", 3, 3, 3, 3, 2, 1, 0, 0, -1],
|
||
|
|
["short bow", 2, 2, 2, 1, 0, 0, -1, -4, -5],
|
||
|
|
["club", 0, 0, -1, -1, -1, -2, -3, -5, -7],
|
||
|
|
["heavy crossbow",
|
||
|
|
4, 4, 4, 3, 3, 2, 1, 0, -1],
|
||
|
|
["light crossbow",
|
||
|
|
3, 3, 3, 2, 1, 0, 0, -1, -2],
|
||
|
|
["dagger", 1, 0, 0, -1, -1, -2, -3, -4, -5],
|
||
|
|
["dart", 1, 0, 1, 0, -1, -2, -3, -4, -5],
|
||
|
|
["hammer", 1, 0, 0, 0, 0, 0, 0, -2, -2],
|
||
|
|
["javelin", 1, 0, 1, 0, -1, -2, -3, -4, -5],
|
||
|
|
["sling w/bullet",
|
||
|
|
3, 1, 2, 0, 0, 0, -1, -2, -2],
|
||
|
|
["sling w/stone",
|
||
|
|
3, 1, 2, 0, 0, -1, -2, -4, -5],
|
||
|
|
["spear", 0, 0, 0, 0, -1, -2, -2, -3, -3]]
|
||
|
|
|
||
|
|
strength_bonuses = [ 0, 0, 0, -3, -2, -2,
|
||
|
|
-1, -1, 0, 0, 0,
|
||
|
|
0, 0, 0, 0, 0,
|
||
|
|
0, 1, 1]
|
||
|
|
strength_bonuses_18 = { 50 : 1, 75: 2, 90 : 2, 99 : 2, 100: 3}
|
||
|
|
|
||
|
|
dexterity_bonuses = [ 0, 0, 0, -3, -2, -1,
|
||
|
|
0, 0, 0, 0, 0,
|
||
|
|
0, 0, 0, 0, 0,
|
||
|
|
1, 2, 3]
|
||
|
|
|
||
|
|
def findWeapon(type, name):
|
||
|
|
wlist = []
|
||
|
|
if ( type == "ranged" ):
|
||
|
|
wlist = ranged_weapon_charts
|
||
|
|
elif ( type == "melee" ):
|
||
|
|
wlist = melee_weapon_charts
|
||
|
|
else :
|
||
|
|
return []
|
||
|
|
for weapon in wlist:
|
||
|
|
#print "trying weapon %s (%s) against name %s" % (weapon, weapon[0], name)
|
||
|
|
if ( weapon[0].lower() == name.lower() ):
|
||
|
|
return weapon
|
||
|
|
return []
|
||
|
|
|
||
|
|
def strBonus(strength):
|
||
|
|
percent = 0
|
||
|
|
if ( string.find(str(type(strength)), "str") != -1) :
|
||
|
|
if ( strength.find("/") != -1):
|
||
|
|
tlist = strength.split("/")
|
||
|
|
percent = int(tlist[1])
|
||
|
|
if ( percent == 0 ) :
|
||
|
|
percent = 100
|
||
|
|
for perc in strength_bonuses_18.keys():
|
||
|
|
if ( perc >= percent ):
|
||
|
|
return strength_bonuses_18[perc]
|
||
|
|
else:
|
||
|
|
# print "recursing with strength %d..." % int(strength)
|
||
|
|
return strBonus(int(strength.strip()))
|
||
|
|
else:
|
||
|
|
if ( strength <= 17):
|
||
|
|
# print "trying to return from strength_bonuses (len %d ) %d" % (len(strength_bonuses), strength)
|
||
|
|
return strength_bonuses[strength-1]
|
||
|
|
if ( strength >= 19):
|
||
|
|
return strength_bonuses_18[100] + ((strength-19)/2)
|
||
|
|
return 0
|
||
|
|
|
||
|
|
def dexBonus(score):
|
||
|
|
# print "trying to return from dexterity_bonuses ( len %d ) %d" % (len(dexterity_bonuses), score)
|
||
|
|
if ( score <= 18):
|
||
|
|
return dexterity_bonuses[score]
|
||
|
|
else :
|
||
|
|
return dexterity_bonuses[18] + (score-18)
|
||
|
|
|
||
|
|
class character :
|
||
|
|
weapons = []
|
||
|
|
strength = 0
|
||
|
|
dexterity = 0
|
||
|
|
charts = {}
|
||
|
|
chartSteps = {}
|
||
|
|
pclass = ""
|
||
|
|
level = 0
|
||
|
|
|
||
|
|
def __init__ (self, className, str, dex, level):
|
||
|
|
self.pclass = className
|
||
|
|
self.level = level
|
||
|
|
self.weapons = []
|
||
|
|
self.strength = str
|
||
|
|
self.dexterity = dex
|
||
|
|
self.charts = {}
|
||
|
|
self.chartSteps = {}
|
||
|
|
|
||
|
|
# work out the THAC0 chart
|
||
|
|
THAC0chart = []
|
||
|
|
baseTHAC0 = thaco_charts[self.pclass][self.level]
|
||
|
|
for i in range(2, 11):
|
||
|
|
THAC0chart.append(baseTHAC0 - i)
|
||
|
|
#print "wrote thac0 chart %s" % THAC0chart
|
||
|
|
self.charts["Base To Hit"] = THAC0chart
|
||
|
|
|
||
|
|
|
||
|
|
def chartWeapon(self, weaponInfo):
|
||
|
|
"""chart a weapon for THACO given its type
|
||
|
|
and name"""
|
||
|
|
self.chartSteps[weaponInfo[1]] = {}
|
||
|
|
weaponModifiers = findWeapon(weaponInfo[0], weaponInfo[1])
|
||
|
|
weaponModifiers.reverse()
|
||
|
|
|
||
|
|
if ( len(weaponModifiers) == 0):
|
||
|
|
self.chartSteps[weaponInfo[1]]["base weapon modifiers"] = "Unable to chart %s" % weaponInfo[1]
|
||
|
|
return
|
||
|
|
else:
|
||
|
|
self.chartSteps[weaponInfo[1]]["base weapon modifiers"] = weaponModifiers[:9]
|
||
|
|
|
||
|
|
newChart = []
|
||
|
|
thacoChart = self.charts["Base To Hit"]
|
||
|
|
#print "weaponModifiers is : %s", str(weaponModifiers)
|
||
|
|
for ac in range(0, 9):
|
||
|
|
#print "trying ac of %d and modifier of %d : length of weaponModifiers is %d, THAC0 is %d" % (ac, ac, len(weaponModifiers), len(thacoChart))
|
||
|
|
newChart.append( thacoChart[ac] + -weaponModifiers[ac])
|
||
|
|
|
||
|
|
self.chartSteps[weaponInfo[1]]["applied to base THAC0"] = newChart
|
||
|
|
newestChart = []
|
||
|
|
for ac in newChart:
|
||
|
|
if ( weaponInfo[0] == "ranged"):
|
||
|
|
newestChart.append(ac + -dexBonus(self.dexterity))
|
||
|
|
else:
|
||
|
|
newestChart.append(ac + -strBonus(self.strength))
|
||
|
|
self.chartSteps[weaponInfo[1]]["after ability bonuses"] = newestChart
|
||
|
|
self.charts[weaponInfo[1]] = newestChart
|
||
|
|
weaponModifiers.reverse()
|
||
|
|
return 0
|
||
|
|
|
||
|
|
def printChart(self, weapName, chart, fileHandle, makeDashes = False):
|
||
|
|
printedChart = string.center(weapName, 30)
|
||
|
|
# print "chart is %d elements long : %s" % (len(chart), str(chart))
|
||
|
|
for ac in range(0, 9):
|
||
|
|
if ( chart[ac] <= 0 and makeDashes):
|
||
|
|
chart[ac] = "-"
|
||
|
|
printedChart += string.center(str(chart[ac]), 4)
|
||
|
|
printedChart += "\n"
|
||
|
|
fileHandle.write(printedChart)
|
||
|
|
|
||
|
|
def writeCharts(self, fileHandle):
|
||
|
|
global brief
|
||
|
|
for weapon in self.weapons:
|
||
|
|
self.chartWeapon(weapon)
|
||
|
|
ACChart = string.center("1st Edition AD&D To Hit Charts", 70) + "\n"
|
||
|
|
ACChart += string.center("%d level %s : strength %s (to hit +%d) dexterity %d ( RAA +%d)" % (self.level, self.pclass,
|
||
|
|
self.strength, strBonus(self.strength), self.dexterity, dexBonus(self.dexterity)), 70) + "\n\n"
|
||
|
|
ACChart += " "*30 + string.center("OPPONENT ARMOR CLASS", 40) + "\n"
|
||
|
|
ACChart += string.center("WEAPON", 30)
|
||
|
|
for ac in range(2, 11):
|
||
|
|
ACChart += string.center(str(ac), 4)
|
||
|
|
ACChart += "\n"
|
||
|
|
fileHandle.write(ACChart)
|
||
|
|
fileHandle.write("="*70 + "\n")
|
||
|
|
thecharts = self.charts.keys()
|
||
|
|
thecharts.sort()
|
||
|
|
for chart in thecharts:
|
||
|
|
self.printChart(chart.upper(), self.charts[chart], fileHandle, True)
|
||
|
|
#print "brief was %s" % str(brief)
|
||
|
|
if ( (chart != "Base To Hit") and (not brief)):
|
||
|
|
self.printChart("(Base To Hit)", self.charts["Base To Hit"], fileHandle)
|
||
|
|
self.printChart("(base modifiers)", self.chartSteps[chart]["base weapon modifiers"], fileHandle)
|
||
|
|
self.printChart("(applied to Base To Hit)", self.chartSteps[chart]["applied to base THAC0"], fileHandle)
|
||
|
|
self.printChart("(after ability bonuses)", self.chartSteps[chart]["after ability bonuses"], fileHandle)
|
||
|
|
if ( not brief) :
|
||
|
|
fileHandle.write("\n")
|
||
|
|
if ( not brief):
|
||
|
|
fileHandle.write("\n" + string.center("For ACs below 2, add that much to the indicated difficulty.", 70) + "\n")
|
||
|
|
fileHandle.write("\n" + string.center("A difficulty of \"-\" indicates that you can only miss", 70) + "\n" + string.center("this armor class with the given weapon on a botch.", 70) + "\n")
|
||
|
|
def main(argc, argv):
|
||
|
|
global brief
|
||
|
|
longopts = ["class=", "level=", "strength=",
|
||
|
|
"dexterity=", "ranged=", "melee=", "help",
|
||
|
|
"brief", "weaponlist"]
|
||
|
|
shortopts = "c:l:s:d:r:m:hbw"
|
||
|
|
usage = """
|
||
|
|
1eHitChart.py (2006 Gamecube)
|
||
|
|
usage: 1eHitChart.py <options>
|
||
|
|
options:
|
||
|
|
--class | -c : class name
|
||
|
|
--level | -l : level number
|
||
|
|
--strength | -s : strength score
|
||
|
|
--dexterity | -d : dexterity score
|
||
|
|
--ranged | -r : ranged weapons; use a comma
|
||
|
|
separated list
|
||
|
|
--melee | -m : melee weapons; use a comma
|
||
|
|
separated list
|
||
|
|
--brief | -b : do not print an explanatory
|
||
|
|
verbose summary; just print
|
||
|
|
the charts.
|
||
|
|
--weaponlist | -w : print out a list of all
|
||
|
|
weapons this program recognises
|
||
|
|
--help | -h : this help
|
||
|
|
"""
|
||
|
|
|
||
|
|
pclass = ""
|
||
|
|
level = 0
|
||
|
|
strength = ""
|
||
|
|
dexterity = 0
|
||
|
|
weapons = []
|
||
|
|
|
||
|
|
#print "got sys.argv %s" % str(sys.argv)
|
||
|
|
opts, args = getopt.getopt(argv[1:], shortopts, longopts)
|
||
|
|
#print "got opts %s args %s" % ( str(opts), str(args))
|
||
|
|
for pair in opts:
|
||
|
|
#print "checking pair %s" % str(pair)
|
||
|
|
if (pair[0] == "--class" or pair[0] == "-c"):
|
||
|
|
pclass = pair[1]
|
||
|
|
elif ( pair[0] == "--level" or pair[0] == "-l"):
|
||
|
|
level = int(pair[1])
|
||
|
|
elif ( pair[0] == "--strength" or pair[0] == "-s"):
|
||
|
|
strength = pair[1]
|
||
|
|
elif ( pair[0] == "--dexterity" or pair[0] == "-d"):
|
||
|
|
dexterity = int(pair[1])
|
||
|
|
elif ( pair[0] == "--ranged" or pair[0] == "-r"):
|
||
|
|
for item in pair[1].split(","):
|
||
|
|
weapons.append(["ranged", item.strip()])
|
||
|
|
elif ( pair[0] == "--melee" or pair[0] == "-m"):
|
||
|
|
for item in pair[1].split(","):
|
||
|
|
weapons.append(["melee", item.strip()])
|
||
|
|
elif ( pair[0] == "--help" or pair[0] == "-h"):
|
||
|
|
print usage
|
||
|
|
sys.exit(1)
|
||
|
|
elif ( pair[0] == "--brief" or pair[0] == "-b"):
|
||
|
|
brief = True
|
||
|
|
elif ( pair[0] == "--weaponlist" or pair[0] == "-w"):
|
||
|
|
print "Valid melee weapons:"
|
||
|
|
for i in range(0, len(melee_weapon_charts), 3):
|
||
|
|
print "%s%s%s" % ( string.center(melee_weapon_charts[i][0], 70/3), string.center(melee_weapon_charts[i+1][0], 70/3), string.center(melee_weapon_charts[i+2][0], 70/3))
|
||
|
|
print "\nValid ranged weapons:"
|
||
|
|
for i in range(0, len(ranged_weapon_charts), 3):
|
||
|
|
print "%s%s%s" % ( string.center(ranged_weapon_charts[i][0], 70/3), string.center(ranged_weapon_charts[i+1][0], 70/3), string.center(ranged_weapon_charts[i+2][0], 70/3))
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
if ( not strength or not dexterity or not pclass or not level):
|
||
|
|
print usage
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
#print "Trying with pclass %s level %d strength %s dex %d weapons %s" % (pclass, level, strength, dexterity, weapons)
|
||
|
|
|
||
|
|
pc = character(pclass, strength, dexterity, level)
|
||
|
|
pc.weapons = weapons
|
||
|
|
pc.writeCharts(sys.stdout)
|
||
|
|
#pc.weapons = [["ranged", "long bow"], ["melee", "long sword"]]
|
||
|
|
#pc.writeCharts(sys.stdout)
|
||
|
|
|
||
|
|
if ( __name__ == "__main__" ):
|
||
|
|
main(len(sys.argv), sys.argv)
|