Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 125 additions & 3 deletions data/raw_sources/srd_5_2/scripts/convert_monsters_srd52.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,126 @@ def parse_actions(text: str, creature_pk: str) -> Tuple[List[Dict[str, Any]], Li

return actions, all_attacks

def parse_bonus_actions(text: str, creature_pk: str) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
"""Parse creature bonus actions and return both actions and attacks."""
actions = []
all_attacks = []
order = 0

# Find bonus actions section
bonus_actions_match = re.search(r'### Bonus Actions\s*\n(.*?)(?=### |$)', text, re.DOTALL)
if bonus_actions_match:
bonus_actions_text = bonus_actions_match.group(1)

# Split by action headers (***Name.***)
action_pattern = r'\*\*\*([^*]+)\.\*\*\*\s*(.*?)(?=\*\*\*|$)'
action_matches = re.findall(action_pattern, bonus_actions_text, re.DOTALL)

for action_name, action_desc in action_matches:
action_name = clean_text(action_name)
action_desc = clean_text(action_desc)

if action_name and action_desc:
action_pk = f"{creature_pk}_{action_name.lower().replace(' ', '-').replace('(', '').replace(')', '').replace('/', '-')}"

# Determine uses
uses_type = None
uses_param = None

if 'recharge' in action_name.lower():
uses_type = "RECHARGE"
recharge_match = re.search(r'recharge\s+(\d+)', action_name.lower())
if recharge_match:
uses_param = int(recharge_match.group(1))
elif '/day' in action_name.lower():
uses_type = "PER_DAY"
day_match = re.search(r'(\d+)/day', action_name.lower())
if day_match:
uses_param = int(day_match.group(1))

actions.append({
"model": "api_v2.creatureaction",
"pk": action_pk,
"fields": {
"name": action_name,
"desc": action_desc,
"parent": creature_pk,
"uses_type": uses_type,
"uses_param": uses_param,
"action_type": "BONUS_ACTION",
"form_condition": None,
"order": order
}
})

# Parse attacks from description
attacks = parse_attack_from_description(action_desc, action_name, action_pk)
all_attacks.extend(attacks)

order += 1

return actions, all_attacks

def parse_reactions(text: str, creature_pk: str) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
"""Parse creature reactions and return both actions and attacks."""
actions = []
all_attacks = []
order = 0

# Find reactions section
reactions_match = re.search(r'### Reactions\s*\n(.*?)(?=### |$)', text, re.DOTALL)
if reactions_match:
reactions_text = reactions_match.group(1)

# Split by action headers (***Name.***)
action_pattern = r'\*\*\*([^*]+)\.\*\*\*\s*(.*?)(?=\*\*\*|$)'
action_matches = re.findall(action_pattern, reactions_text, re.DOTALL)

for action_name, action_desc in action_matches:
action_name = clean_text(action_name)
action_desc = clean_text(action_desc)

if action_name and action_desc:
action_pk = f"{creature_pk}_{action_name.lower().replace(' ', '-').replace('(', '').replace(')', '').replace('/', '-')}"

# Determine uses
uses_type = None
uses_param = None

if 'recharge' in action_name.lower():
uses_type = "RECHARGE"
recharge_match = re.search(r'recharge\s+(\d+)', action_name.lower())
if recharge_match:
uses_param = int(recharge_match.group(1))
elif '/day' in action_name.lower():
uses_type = "PER_DAY"
day_match = re.search(r'(\d+)/day', action_name.lower())
if day_match:
uses_param = int(day_match.group(1))

actions.append({
"model": "api_v2.creatureaction",
"pk": action_pk,
"fields": {
"name": action_name,
"desc": action_desc,
"parent": creature_pk,
"uses_type": uses_type,
"uses_param": uses_param,
"action_type": "REACTION",
"form_condition": None,
"order": order
}
})

# Parse attacks from description
attacks = parse_attack_from_description(action_desc, action_name, action_pk)
all_attacks.extend(attacks)

order += 1

return actions, all_attacks

def parse_legendary_actions(text: str, creature_pk: str) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
"""Parse legendary actions and return both actions and attacks."""
actions = []
Expand Down Expand Up @@ -721,17 +841,19 @@ def parse_monster(monster_text: str) -> Tuple[Dict[str, Any], List[Dict[str, Any
traits = parse_traits(full_text, creature_pk)
actions, action_attacks = parse_actions(full_text, creature_pk)
legendary_actions, legendary_attacks = parse_legendary_actions(full_text, creature_pk)
bonus_actions, bonus_action_attacks = parse_bonus_actions(full_text, creature_pk)
reactions, reaction_attacks = parse_reactions(full_text, creature_pk)

all_actions = actions + legendary_actions
all_attacks = action_attacks + legendary_attacks
all_actions = actions + legendary_actions + bonus_actions + reactions
all_attacks = action_attacks + legendary_attacks + bonus_action_attacks + reaction_attacks

return creature_data, traits, all_actions, all_attacks

def main():
"""Main function to convert monsters and animals."""
monster_file = Path("../sections/13_monsters_az.md")
animal_file = Path("../sections/14_animals.md")
output_dir = Path("../../../v2/wizards-of-the-coast/srd-2024/")
output_dir = Path("../../../v2/wizards-of-the-coast/temp/")

if not monster_file.exists():
print(f"Error: Monster file {monster_file} not found")
Expand Down
80 changes: 80 additions & 0 deletions data/v2/wizards-of-the-coast/srd-2024/Creature.json
Original file line number Diff line number Diff line change
Expand Up @@ -27600,5 +27600,85 @@
},
"model": "api_v2.creature",
"pk": "srd-2024_zombie"
},
{
"fields": {
"ability_score_charisma": -3,
"ability_score_constitution": 0,
"ability_score_dexterity": 15,
"ability_score_intelligence": 3,
"ability_score_strength": 4,
"ability_score_wisdom": 10,
"alignment": "unaligned",
"armor_class": 12,
"armor_detail": "",
"blindsight_range": null,
"burrow": null,
"category": "Beast",
"challenge_rating_decimal": "0",
"climb": null,
"condition_immunities": [],
"condition_immunities_display": "",
"damage_immunities": [],
"damage_immunities_display": "",
"damage_resistances": [],
"damage_resistances_display": "",
"damage_vulnerabilities": [],
"damage_vulnerabilities_display": "",
"darkvision_range": 30,
"document": "srd-2024",
"environments": [],
"experience_points_integer": 10,
"fly": null,
"hit_dice": "1d6",
"hit_points": 3,
"hover": false,
"illustration": null,
"initiative_bonus": 2,
"languages": [],
"languages_desc": "",
"name": "Octopus",
"nonmagical_attack_immunity": false,
"nonmagical_attack_resistance": false,
"normal_sight_range": 10560,
"passive_perception": 12,
"proficiency_bonus": 2,
"saving_throw_charisma": -3,
"saving_throw_constitution": 30,
"saving_throw_dexterity": 2,
"saving_throw_intelligence": -4,
"saving_throw_strength": -3,
"saving_throw_wisdom": 0,
"size": "small",
"skill_bonus_acrobatics": null,
"skill_bonus_animal_handling": null,
"skill_bonus_arcana": null,
"skill_bonus_athletics": null,
"skill_bonus_deception": null,
"skill_bonus_history": null,
"skill_bonus_insight": null,
"skill_bonus_intimidation": null,
"skill_bonus_investigation": null,
"skill_bonus_medicine": null,
"skill_bonus_nature": null,
"skill_bonus_perception": 2,
"skill_bonus_performance": null,
"skill_bonus_persuasion": null,
"skill_bonus_religion": null,
"skill_bonus_sleight_of_hand": null,
"skill_bonus_stealth": 6,
"skill_bonus_survival": null,
"subcategory": null,
"swim": 30,
"telepathy_range": null,
"tremorsense_range": null,
"truesight_range": null,
"type": "beast",
"unit": null,
"walk": 5,
"weight": "0.000"
},
"model": "api_v2.creature",
"pk": "srd-2024_octopus"
}
]
Loading