Merge branch 'feats' closes #86

bradl/monsters-adult-gold-dragon
James Miller 2020-04-21 21:44:36 -05:00
commit 847e6fcb82
10 changed files with 614 additions and 271 deletions

View File

@ -236,7 +236,10 @@ ancestries:
to add your level to the Hit Points you regain from their treatment.
feat: null
name: Hillock Halfling
- descr: Your ancestors have traveled from place to place for generations, never content to settle down. You gain two additional languages of your choice, chosen from among the common and uncommon languages available to you, and every time you take the Multilingual feat, you gain another new language.
- descr: Your ancestors have traveled from place to place for generations, never
content to settle down. You gain two additional languages of your choice, chosen
from among the common and uncommon languages available to you, and every time
you take the Multilingual feat, you gain another new language.
feat: null
name: Nomadic Halfling
- descr: Your ancestors performed many secret acts under the concealing cover of
@ -268,16 +271,16 @@ ancestries:
flavor_text: TODO
flaws: null
heritages:
- descr: Either one of your parents was an elf, or one or both were half-elves. You
have pointed ears and other telltale signs of elf heritage. You gain the elf trait
and low-light vision. In addition, you can select elf, half-elf, and human feats
whenever you gain an ancestry feat.
- descr: Either one of your parents was an elf, or one or both were half-elves.
You have pointed ears and other telltale signs of elf heritage. You gain the
elf trait and low-light vision. In addition, you can select elf, half-elf, and
human feats whenever you gain an ancestry feat.
feat: null
name: Half-Elf
- descr: One of your parents was an orc, or one or both were half-orcs. You have a
green tinge to your skin and other indicators of orc heritage. You gain the orc
trait and low-light vision. In addition, you can select orc, half-orc, and human
feats whenever you gain an ancestry feat.
- descr: One of your parents was an orc, or one or both were half-orcs. You have
a green tinge to your skin and other indicators of orc heritage. You gain the
orc trait and low-light vision. In addition, you can select orc, half-orc, and
human feats whenever you gain an ancestry feat.
feat: null
name: Half-Orc
- descr: Your ingenuity allows you to train in a wide variety of skills. You become
@ -286,9 +289,9 @@ ancestries:
feat: null
name: Skilled Heritage
- descr: Humanitys versatility and ambition have fueled its ascendance to be the
most common ancestry in most nations throughout the world. Select a general feat
of your choice for which you meet the prerequisites (as with your ancestry feat,
you can select this general feat at any point during character creation).
most common ancestry in most nations throughout the world. Select a general
feat of your choice for which you meet the prerequisites (as with your ancestry
feat, you can select this general feat at any point during character creation).
feat: null
name: Versatile Heritage
hp: 8

View File

@ -2,21 +2,44 @@
# TO CLEAN UP AND ORDER ALL THE YAML
import yaml
import glob
import os
yfiles = [
"actions.yaml", "ancestries.yaml", "armor.yaml", "backgrounds.yaml",
"basics.yaml", "bulks.yaml", "conditions.yaml", "damage.yaml",
"feats-levels-false-matches.yaml", "feats.yaml", "langs.yaml",
"monsters.yaml", "requirements.yaml", "senses.yaml", "skills.yaml",
"sources.yaml", "spells.yaml", "traits.yaml", "triggers.yaml"
]
def main():
# gets all files with a yaml extension in the directory
yfiles = []
for file in glob.glob("*.yaml"):
yfiles.append(file)
yfiles.sort()
print("Going to clean up the following files: {}".format(yfiles))
for x in yfiles:
print("Doing: {}".format(x))
with open(x, 'r') as r:
data = yaml.full_load(r)
if x == "feats.yaml":
for i in data['feat']:
# This is to clean out smart quotes that made it into the
# yaml file so it matches the requirements.yaml
if i['requirement'] != None:
# print("Before: {}".format(i['requirement']))
i['requirement'] = i['requirement'].replace('', "'")
# print("After: {}".format(i['requirement']))
if i['trigger'] != None:
# print("Before: {}".format(i['trigger']))
i['trigger'] = i['trigger'].replace('', "'")
# print("After: {}".format(i['trigger']))
if x == "triggers.yaml":
for i in data['trigger']:
print(i)
i = i.replace('', "'")
final = yaml.safe_dump(data, allow_unicode=True)
with open(x, 'w') as f:
f.write(final)

File diff suppressed because it is too large Load Diff

View File

@ -68,10 +68,12 @@ requirement:
- When you make your daily preparations, you must specify a trigger for this reaction
using the same restrictions as the triggers for the Ready action. You also choose
a single spell from the arcane, divine, occult, or primal list of 4th level or lower.
The spell cant have a cost, nor can its casting time be more than 10 minutes. The
The spell can't have a cost, nor can its casting time be more than 10 minutes. The
spell must be able to target a single creature, and you must be a valid target for
it.
- You have Perfect Distraction ready to use.
- You have an unexpended spell slot you could use to cast the triggered spell.
- Your most recent action was to cast a non-cantrip spell.
- The last action you used was Drain Bonded Item.
- You have a focus pool, and you have spent at least 1 Focus Point since you last
regained any Focus Points.

View File

@ -1,7 +1,7 @@
trigger:
- An ally ends a move action adjacent to you.
- You fail a skill check or saving throw.
- You attempt a check using a skill youre untrained in.
- You attempt a check using a skill you're untrained in.
- You would be reduced to 0 Hit Points but not immediately killed.
- You attempt a saving throw against a spell or magical effect, before rolling.
- You bring a foe to 0 Hit Points.
@ -13,12 +13,12 @@ trigger:
than your advanced alchemy level.
- You craft an elixer of life using Quick Alchemy, and that elixir is at least 2 levels
lower than your advanced alchemy level.
- You craft an alchemical bomb using Quick Alchemy thats at least 2 levels lower
- You craft an alchemical bomb using Quick Alchemy that's at least 2 levels lower
than your advanced alchemy level.
- A foe within reach attempts to move away from you.
- You take damage and are capable of entering a rage.
- A creature within your reach uses a manipulate action or a move action, makes a
ranged attack, or leaves a square during a move action its using.
ranged attack, or leaves a square during a move action it's using.
- Your melee Strike kills a creature or knocks it unconscious, and another foe is
adjacent to that creature.
- A creature within your reach succeeds or critically succeeds at an attack against
@ -49,7 +49,7 @@ trigger:
- You are targeted with a melee attack by an attacker you can see.
- Your turn ends and you have a status penalty to your Speed or are immobilized or
slowed.
- An enemy's attack hits you or you fail a saving throw against an enemys ability.
- An enemy's attack hits you or you fail a saving throw against an enemy's ability.
- Your hunted prey is within your reach, and it uses a manipulate action, uses a move
action, or leaves a square during a move action it's using.
- You are about to roll a Perception or Survival check for initiative.
@ -71,10 +71,12 @@ trigger:
area.
- A creature casts a spell that you have in your repertoire.
- A creature Casts a Spell that you have prepared.
- A creature within line of sight casts a spell that you dont have prepared or in
- A creature within line of sight casts a spell that you don't have prepared or in
your spell repertoire, or a trap or similar object casts such a spell. You must
be aware of the casting.
- While you have your shield raised, you would take damage from a physical attack.
- An adjacent creature you are hunting attempts to move away from you using an action
that has the move trait.
- You attempt a saving throw against a magical effect, but you haven't rolled yet.
- You are unarmored and touching the ground.
- You are unarmored.

View File

@ -124,6 +124,10 @@ def main():
data = yaml.full_load(yl)
do_gear(data, conn)
with open('feats.yaml') as yl:
data = yaml.full_load(yl)
do_feats(data, conn)
with open('ancestriesheritages.yaml') as yl:
data = yaml.full_load(yl)
do_ancestries(data, conn)
@ -132,6 +136,309 @@ def main():
data = yaml.full_load(yl)
do_heritages(data, conn)
def do_feats(data, conn):
table = """
CREATE TABLE feat (
feat_id INTEGER PRIMARY KEY,
actioncost_id INTEGER,
descr TEXT NOT NULL,
freq_id INTEGER,
level INTEGER, -- TODO Make not null once issue 88 is resolved
name TEXT NOT NULL UNIQUE,
requirement_id INTEGER,
trigger_id INTEGER,
FOREIGN KEY (actioncost_id) REFERENCES actioncost(actioncost_id),
FOREIGN KEY (freq_id) REFERENCES frequency(freq_id),
FOREIGN KEY (requirement_id) REFERENCES requirement(requirement_id),
FOREIGN KEY (trigger_id) REFERENCES trigger(trigger_id)
);
"""
c = conn.cursor()
c.execute(table)
table = """
CREATE TABLE sourceentry_feat (
id INTEGER PRIMARY KEY,
sourceentry_id INTEGER NOT NULL,
feat_id INTEGER NOT NULL,
UNIQUE (sourceentry_id, feat_id), -- prevent duplicates
FOREIGN KEY (sourceentry_id) REFERENCES sourceentry(sourceentry_id),
FOREIGN KEY (feat_id) REFERENCES feat(feat_id)
);
"""
c = conn.cursor()
c.execute(table)
table = """
CREATE TABLE trait_feat (
id INTEGER PRIMARY KEY,
trait_id INTEGER NOT NULL,
feat_id INTEGER NOT NULL,
UNIQUE(trait_id, feat_id),
FOREIGN KEY (feat_id) REFERENCES feat(feat_id),
FOREIGN KEY (trait_id) REFERENCES trait(trait_id)
);
"""
c.execute(table)
table = """
CREATE TABLE featprereq (
featprereq_id INTEGER PRIMARY KEY,
descr TEXT NOT NULL,
parent_feat_id INTEGER NOT NULL, -- THE FEAT THAT REQUIRES THE PREREQ
is_prereq_feat_bool BOOL NOT NULL, -- THIS TELLS YOU THAT THE PREREQ ITSELF IS A FEAT
prereq_feat_id INTEGER, -- THIS IS THE PREREQ FEAT, NOT THE FEAT THAT REQUIRES THE PREREQ, if is_prereq_feat_bool == FALSE then this will also be null
FOREIGN KEY (prereq_feat_id) REFERENCES feat(feat_id),
FOREIGN KEY (parent_feat_id) REFERENCES feat(feat_id)
);
"""
c.execute(table)
feat_result_list = []
feats_no_levels = []
for i in data['feat']:
if i['actioncost'] == None:
ac_id = None
else:
ac_id = get_actioncost_id_by_name(i['actioncost'], conn)
# print("ac_id for {} is {}".format(i['actioncost'], ac_id))
if i['frequency'] == None:
f_id = None
else:
f_id = get_freq_id_by_descr(i['frequency'], conn)
# print("f_id for {} is {}".format(i['frequency'], f_id))
if i['requirement'] == None:
r_id = None
else:
r_id = get_requirement_id_by_descr(i['requirement'], conn)
if i['trigger'] == None:
t_id = None
else:
t_id = get_trigger_id_by_descr(i['trigger'], conn)
if 'level' not in i:
resl = {'name': i['name'], 'source': i['source']}
feats_no_levels.append(resl)
i['level'] = None
res = (ac_id, i['descr'], f_id, i['level'], i['name'], r_id, t_id)
feat_result_list.append(res)
insert_stmt = "INSERT INTO feat (actioncost_id, descr, freq_id, level, name, requirement_id, trigger_id) VALUES (?,?,?,?,?,?,?);"
try:
conn.executemany(insert_stmt, feat_result_list)
except sqlite3.Error as e:
print("Error creating feats: {}".format(e))
except:
print("Error creating feats something other than sqlite3 error")
else:
conn.commit()
print("\n\nWARNING!\n\nThe following feats do not have level information and need to be manually checked!:\n")
for i in feats_no_levels:
print(i)
# go through and do source entry linking
for i in data['feat']:
# print("\n\nDoing the skill: {}".format(i['name']))
srcs = []
# TODO refactor this inner loop for sources out
for j in i['source']:
abbr = j['abbr']
page_start = j['page_start']
if 'page_stop' in j:
page_stop = j['page_stop']
else:
page_stop = page_start
srcs.append([i['name'], abbr, page_start, page_stop])
# print("srcs: {}".format(srcs))
do_sourceentry_to_feats(srcs, conn)
# do traits
for i in data['feat']:
traitlist = []
if i['traits'] != None:
for j in i['traits']:
traitlist.append((i['name'], j))
# print("traitlist is:\t{}".format(traitlist))
stmt = """
INSERT INTO trait_feat (feat_id, trait_id) VALUES (
(SELECT feat_id FROM feat WHERE name=?),
(SELECT trait_id FROM trait WHERE short_name=?)
);
"""
try:
conn.executemany(stmt, traitlist)
except sqlite3.Error as e:
print("Error creating feat_trait: {}".format(e))
except:
print(
"Error creating feat_trait something other than sqlite3 error"
)
else:
conn.commit()
# do prereqs
for i in data['feat']:
# print("\nDoing prereq:\t{}".format(i['name']))
if i['prereqs'] == None:
# do nothing and start on next 'i'
# print("Continuing prereqs, skipping:\t{}".format(i['name']))
continue
preqlist = []
for j in i['prereqs']:
descr = j['descr']
pfeat = j['feat']
if pfeat == None:
is_feat_bool = False
else:
is_feat_bool = True
res = (descr, i['name'], is_feat_bool, pfeat)
preqlist.append(res)
# print(preqlist)
# now to do the insert
istmt = """
INSERT INTO featprereq (descr, parent_feat_id, is_prereq_feat_bool, prereq_feat_id)
VALUES (
?,
(SELECT feat_id FROM feat WHERE name=?),
?,
(SELECT feat_id FROM feat WHERE name=?)
)
"""
try:
conn.executemany(istmt, preqlist)
except sqlite3.Error as e:
print("Error creating featprereq {}".format(e))
except:
print(
"Error creating featprereq something other than sqlite3 error"
)
else:
conn.commit()
# TODO ugggh;;; this is soooo ugly and needs refactoring but it's working
def do_sourceentry_to_feats(srcs, conn):
c = conn.cursor()
stmt = "SELECT source.source_id, feat.feat_id FROM source, feat WHERE source.abbr=? AND feat.name=?"
istmt = "INSERT INTO sourceentry (source_id, page_start, page_stop) VALUES (?,?,?)"
for i in srcs:
# print("i in srcs: {}".format(i))
inp_data = (i[1], i[0])
# print("inp data: {}".format(inp_data))
for row in c.execute(stmt, inp_data):
# print("source_id:{} skill_id:{}".format(row[0], row[1]))
iinp_data = (row[0], i[2], i[3])
# print("iinp data: {}".format(iinp_data))
try:
c.execute(istmt, iinp_data)
except sqlite3.IntegrityError as e:
if "UNIQUE" in str(e):
# we fully expect UNIQUE constraint to fail on some of these so it's fine
conn.commit()
# print("committed istmt")
else:
# but we still want to know what's going on if there's some other error
print("Something went wrong with istmt: {}".format(e))
except sqlite3.Error as e:
print("Error inserting a sourceentry for skill: {}".format(e))
else:
conn.commit()
# print("committed istmt")
linkstmt = "INSERT INTO sourceentry_feat (sourceentry_id, feat_id) VALUES ((SELECT sourceentry_id from sourceentry WHERE source_id=? AND page_start=? AND page_stop=?), ?)"
linkinp_data = (row[0], i[2], i[3], row[1])
# print(linkinp_data)
try:
c.execute(linkstmt, linkinp_data)
except sqlite3.IntegrityError as e:
if "UNIQUE" in str(e):
# we fully expect UNIQUE constraint to fail on some of these so it's fine
conn.commit()
# print("committed linkstmt")
pass
else:
# but we still want to know what's going on if there's some other error
print(e)
except sqlite3.Error as e:
print("Error inserting a sourceentry for feat: {}".format(e))
else:
# print("committed linkstmt")
conn.commit()
def get_trigger_id_by_descr(t, conn):
qstmt = "SELECT trigger_id FROM trigger WHERE descr=?;"
try:
c = conn.cursor()
c.execute(qstmt, (t,))
except sqlite3.Error as e:
print("Error getting an trigger_id by name: {} Error: {}".format(t, e))
except:
print("Error getting an trigger_id_by_name something other than sqlite3 error")
else:
x = c.fetchone()
if x == None:
raise AssertionError('there was no trigger_id for given trigger name: {}\nYou should check to see if this trigger is in triggers.yaml and sometimes it is a straight apostrophe versus uni-code curly apostrophe.'.format(t))
else:
return x[0]
def get_requirement_id_by_descr(r, conn):
qstmt = "SELECT requirement_id FROM requirement WHERE descr=?;"
try:
c = conn.cursor()
c.execute(qstmt, (r,))
except sqlite3.Error as e:
print("Error getting an requirement_id by name: {} Error: {}".format(r, e))
except:
print("Error getting an requirement_id_by_name something other than sqlite3 error")
else:
x = c.fetchone()
if x == None:
raise AssertionError('there was no requirement_id for given requirement name: {}\nYou should check to see if this requirement is in requirements.yaml and sometimes it is a straight apostrophe versus uni-code curly apostrophe.'.format(r))
else:
return x[0]
def get_freq_id_by_descr(f, conn):
qstmt = "SELECT freq_id FROM frequency WHERE freq_descr=?;"
try:
c = conn.cursor()
c.execute(qstmt, (f,))
except sqlite3.Error as e:
print("Error getting an freq_id_id by name: {} Error: {}".format(f, e))
except:
print("Error getting an freq_id_id_by_name something other than sqlite3 error")
else:
x = c.fetchone()
if x == None:
raise AssertionError('there was no freq_id_id for given freq_id name: {}'.format(f))
else:
return x[0]
def get_actioncost_id_by_name(ac, conn):
qstmt = "SELECT actioncost_id FROM actioncost WHERE name=?;"
try:
c = conn.cursor()
c.execute(qstmt, (ac,))
except sqlite3.Error as e:
print("Error getting an actioncost_id by name: {} Error: {}".format(ac, e))
except:
print("Error getting an actioncost_id_by_name something other than sqlite3 error")
else:
x = c.fetchone()
if x == None:
raise AssertionError('there was no actioncost_id for given actioncost name: {}'.format(ac))
else:
return x[0]
def do_heritages(data, conn):
table = """
CREATE TABLE heritages (
@ -153,7 +460,7 @@ def do_heritages(data, conn):
rowid = c.fetchone()
#FOR EACH HERITAGE, INSERT INTO TABLE USING ANCESTRY ID
for j in i['heritages']:
print("doing this heritage: {}".format(j['name']))
# print("doing this heritage: {}".format(j['name']))
stmt = "INSERT INTO heritages (name, descr, ancestry_id) VALUES (?,?,?);"
c.execute(stmt, (j['name'], j['descr'], rowid[0]))
conn.commit()
@ -205,7 +512,7 @@ def do_ancestries(data, conn):
c.execute(table)
table = """
CREATE TABLE ancestries_traits (
CREATE TABLE trait_ancestries (
id INTEGER PRIMARY KEY,
ancestry_id INTEGER NOT NULL,
trait_id INTEGER NOT NULL,
@ -216,6 +523,7 @@ def do_ancestries(data, conn):
"""
c.execute(table)
# insert basics into ancestries table
inp_data = []
for i in data['ancestries']:
@ -226,7 +534,7 @@ def do_ancestries(data, conn):
sinp_data = (i['size'], )
sres = c.execute(sstmt, sinp_data).fetchall()
sid = sres[0][0]
print(sid)
# print(sid)
# Get the vision_id
vstmt = """
@ -234,12 +542,12 @@ def do_ancestries(data, conn):
"""
vinp_data = (i['senses'], )
vres = c.execute(vstmt, vinp_data).fetchall()
print(vres)
# print(vres)
if len(vres) > 0:
vid = vres[0][0]
else:
vid = None
print(vid)
# print(vid)
#print(i)
inp_data.append(
@ -261,7 +569,7 @@ def do_ancestries(data, conn):
if i['boosts'] != None:
for j in i['boosts']:
boostlist.append((i['name'], j))
print("boostlist is:\t{}".format(boostlist))
# print("boostlist is:\t{}".format(boostlist))
stmt = """
INSERT INTO ancestries_boosts (ancestry_id, abilityscore_id) VALUES (
@ -286,7 +594,7 @@ def do_ancestries(data, conn):
if i['flaws'] != None:
for j in i['flaws']:
flawlist.append((i['name'], j))
print("flawlist is:\t{}".format(flawlist))
# print("flawlist is:\t{}".format(flawlist))
stmt = """
INSERT INTO ancestries_flaws (ancestry_id, abilityscore_id) VALUES (
@ -311,10 +619,10 @@ def do_ancestries(data, conn):
if i['traits'] != None:
for j in i['traits']:
traitlist.append((i['name'], j))
print("traitlist is:\t{}".format(traitlist))
# print("traitlist is:\t{}".format(traitlist))
stmt = """
INSERT INTO ancestries_traits (ancestry_id, trait_id) VALUES (
INSERT INTO trait_ancestries (ancestry_id, trait_id) VALUES (
(SELECT ancestry_id FROM ancestries WHERE name=?),
(SELECT trait_id FROM trait WHERE short_name=?)
);
@ -322,15 +630,16 @@ def do_ancestries(data, conn):
try:
conn.executemany(stmt, traitlist)
except sqlite3.Error as e:
print("Error creating ancestries_traits: {}".format(e))
print("Error creating trait_ancestries {}".format(e))
except:
print(
"Error creating ancestries_traits something other than sqlite3 error"
"Error creating trait_ancestries something other than sqlite3 error"
)
else:
conn.commit()
def do_gear(data, conn):
table = """
CREATE TABLE gear(
@ -605,7 +914,7 @@ def do_armor(data, conn):
# insert basics into armorcategory table
inp_data = []
for i in data['armorcategory']:
print(i)
# print(i)
inp_data.append((i, ))
stmt = "INSERT INTO armorcategory(name) VALUES (?)"