Make trainerproc compatible with partners (#4421)
* Prepare stuff for MGriffin to fix * Remove doubleBattle from migration script * Support PARTNER_NONE in trainerproc * Let script apply to battle_partners.h file * TRAINER_BACK_PIC * Let script fix issues --------- Co-authored-by: Martin Griffin <martinrgriffin@gmail.com>
This commit is contained in:
parent
49c86f86c8
commit
308475a163
7 changed files with 474 additions and 40 deletions
319
migration_scripts/convert_partner_parties.py
Normal file
319
migration_scripts/convert_partner_parties.py
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
# If you have extra members in 'TrainerMon':
|
||||||
|
# 1. Add a regular expression which matches that member (e.g. 'shadow_definition').
|
||||||
|
# 2. Match that regular expression in 'convert' and write into 'attributes' with the key that 'trainerproc' should parse.
|
||||||
|
# 3. Add the key used in 'attributes' to 'pokemon_attribute_order'.
|
||||||
|
# 4. Update 'trainerproc.c' to parse the new key.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
is_blank = re.compile(r'^[ \t]*(//.*)?$')
|
||||||
|
|
||||||
|
begin_party_definition = re.compile(r'struct TrainerMon (\w+)\[\] =')
|
||||||
|
end_party_definition = re.compile(r'^};')
|
||||||
|
begin_pokemon_definition = re.compile(r'^ { *$')
|
||||||
|
end_pokemon_definition = re.compile(r'^ },? *$')
|
||||||
|
level_definition = re.compile(r'\.lvl = (\d+)')
|
||||||
|
species_definition = re.compile(r'\.species = SPECIES_(\w+)')
|
||||||
|
gender_definition = re.compile(r'\.gender = TRAINER_MON_(\w+)')
|
||||||
|
nickname_definition = re.compile(r'\.nickname = COMPOUND_STRING\("([^"]+)"\)')
|
||||||
|
item_definition = re.compile(r'\.heldItem = ITEM_(\w+)')
|
||||||
|
ball_definition = re.compile(r'\.ball = ITEM_(\w+)')
|
||||||
|
ability_definition = re.compile(r'\.ability = ABILITY_(\w+)')
|
||||||
|
friendship_definition = re.compile(r'\.friendship = (\d+)')
|
||||||
|
shiny_definition = re.compile(r'\.isShiny = (\w+)')
|
||||||
|
ivs_definition = re.compile(r'\.iv = TRAINER_PARTY_IVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
|
||||||
|
evs_definition = re.compile(r'\.ev = TRAINER_PARTY_EVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
|
||||||
|
moves_definition = re.compile(r'\.moves = \{([^}]+)\}')
|
||||||
|
move_definition = re.compile(r'MOVE_(\w+)')
|
||||||
|
nature_definition = re.compile(r'\.nature = NATURE_(\w+)')
|
||||||
|
|
||||||
|
# NOTE: These are just for aesthetics, the Pokemon would still compile
|
||||||
|
# without them.
|
||||||
|
species_replacements = {
|
||||||
|
"CHIEN_PAO": "Chien-Pao",
|
||||||
|
"CHI_YU": "Chi-Yu",
|
||||||
|
"HAKAMO_O": "Hakamo-o",
|
||||||
|
"HO_OH": "Ho-Oh",
|
||||||
|
"JANGMO_O": "Jangmo-o",
|
||||||
|
"KOMMO_O": "Kommo-o",
|
||||||
|
"PORYGON_Z": "Porygon-Z",
|
||||||
|
"ROTOM_": "Rotom-",
|
||||||
|
"TING_LU": "Ting-Lu",
|
||||||
|
"TYPE_NULL": "Type: Null",
|
||||||
|
"WO_CHIEN": "Wo-Chien",
|
||||||
|
|
||||||
|
"_ALOLAN": "-Alola",
|
||||||
|
"_AQUA_BREED": "-Aqua",
|
||||||
|
"_BATTLE_BOND": "-Bond",
|
||||||
|
"_BLAZE_BREED": "-Blaze",
|
||||||
|
"_CAP": "",
|
||||||
|
"_CLOAK": "",
|
||||||
|
"_COMBAT_BREED": "-Combat",
|
||||||
|
"_CROWED_SHIELD": "-Crowned",
|
||||||
|
"_CROWED_SWORD": "-Crowned",
|
||||||
|
"_DRIVE": "",
|
||||||
|
"_EAST_SEA": "-East",
|
||||||
|
"_FAMILY_OF_FOUR": "-Four",
|
||||||
|
"_FEMALE": "-F",
|
||||||
|
"_FLOWER": "",
|
||||||
|
"_GALARIAN": "-Galar",
|
||||||
|
"_GIGANTAMAX": "-Gmax",
|
||||||
|
"_HISUIAN": "-Hisui",
|
||||||
|
"_ICE_RIDER": "-Ice",
|
||||||
|
"_NOICE_FACE": "-Noice",
|
||||||
|
"_ORIGIN": "-Origin",
|
||||||
|
"_ORIGINAL_COLOR": "-Original",
|
||||||
|
"_PALDEAN": "-Paldea",
|
||||||
|
"_PLUMAGE": "",
|
||||||
|
"_POKE_BALL": "-Pokeball",
|
||||||
|
"_SHADOW_RIDER": "-Shadow",
|
||||||
|
"_STRIKE_STYLE": "-Style",
|
||||||
|
"_TOTEM": "-Totem",
|
||||||
|
"_ZEN_MODE": "-Zen",
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon_attribute_order = ['Level', 'Ability', 'IVs', 'EVs', 'Happiness', 'Shiny', 'Ball']
|
||||||
|
|
||||||
|
class Pokemon:
|
||||||
|
def __init__(self):
|
||||||
|
self.nickname = None
|
||||||
|
self.species = None
|
||||||
|
self.gender = None
|
||||||
|
self.item = None
|
||||||
|
self.nature = None
|
||||||
|
self.attributes = {}
|
||||||
|
self.attributes['IVs'] = "0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe"
|
||||||
|
self.moves = []
|
||||||
|
|
||||||
|
def convert_parties(in_path, in_h):
|
||||||
|
party_identifier = None
|
||||||
|
party = None
|
||||||
|
pokemon = None
|
||||||
|
parties = {}
|
||||||
|
|
||||||
|
for line_no, line in enumerate(in_h, 1):
|
||||||
|
try:
|
||||||
|
line = line[:-1]
|
||||||
|
if m := begin_party_definition.search(line):
|
||||||
|
if party:
|
||||||
|
raise Exception(f"unexpected start of party")
|
||||||
|
[identifier] = m.groups()
|
||||||
|
party_identifier = identifier
|
||||||
|
party = []
|
||||||
|
elif end_party_definition.search(line):
|
||||||
|
if not party:
|
||||||
|
raise Exception(f"unexpected end of party")
|
||||||
|
parties[party_identifier] = party
|
||||||
|
party = None
|
||||||
|
elif begin_pokemon_definition.search(line):
|
||||||
|
if pokemon:
|
||||||
|
raise Exception(f"unexpected start of Pokemon")
|
||||||
|
pokemon = Pokemon()
|
||||||
|
elif end_pokemon_definition.search(line):
|
||||||
|
if not pokemon:
|
||||||
|
raise Exception(f"unexpected end of Pokemon")
|
||||||
|
else:
|
||||||
|
party.append(pokemon)
|
||||||
|
pokemon = None
|
||||||
|
elif m := level_definition.search(line):
|
||||||
|
[level] = m.groups()
|
||||||
|
pokemon.attributes['Level'] = level
|
||||||
|
elif m := species_definition.search(line):
|
||||||
|
[species_] = m.groups()
|
||||||
|
for match, replacement in species_replacements.items():
|
||||||
|
species_ = species_.replace(match, replacement)
|
||||||
|
pokemon.species = species_.replace("_", " ").title()
|
||||||
|
elif m := gender_definition.search(line):
|
||||||
|
[gender_] = m.groups()
|
||||||
|
if gender_ == 'MALE':
|
||||||
|
pokemon.gender = 'M'
|
||||||
|
elif gender_ == 'FEMALE':
|
||||||
|
pokemon.gender = 'F'
|
||||||
|
else:
|
||||||
|
raise Exception(f"unknown gender: '{gender_}'")
|
||||||
|
elif m := nickname_definition.search(line):
|
||||||
|
[nickname] = m.groups()
|
||||||
|
pokemon.nickname = nickname
|
||||||
|
elif m := item_definition.search(line):
|
||||||
|
[item_] = m.groups()
|
||||||
|
pokemon.item = item_.replace("_", " ").title()
|
||||||
|
elif m := ball_definition.search(line):
|
||||||
|
[ball] = m.groups()
|
||||||
|
pokemon.attributes['Ball'] = ball.replace("_", " ").title()
|
||||||
|
elif m := ability_definition.search(line):
|
||||||
|
[ability] = m.groups()
|
||||||
|
pokemon.attributes['Ability'] = ability.replace("_", " ").title()
|
||||||
|
elif m := friendship_definition.search(line):
|
||||||
|
[friendship] = m.groups()
|
||||||
|
pokemon.attributes['Happiness'] = friendship
|
||||||
|
elif m := shiny_definition.search(line):
|
||||||
|
[shiny] = m.groups()
|
||||||
|
if shiny == 'TRUE':
|
||||||
|
pokemon.attributes['Shiny'] = 'Yes'
|
||||||
|
elif shiny == 'FALSE':
|
||||||
|
pokemon.attributes['Shiny'] = 'No'
|
||||||
|
else:
|
||||||
|
raise Exception(f"unknown isShiny: '{shiny}'")
|
||||||
|
elif m := ivs_definition.search(line):
|
||||||
|
[hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
|
||||||
|
stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
|
||||||
|
pokemon.attributes['IVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items())
|
||||||
|
elif m := evs_definition.search(line):
|
||||||
|
[hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
|
||||||
|
stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
|
||||||
|
pokemon.attributes['EVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items() if value != '0')
|
||||||
|
elif m := moves_definition.search(line):
|
||||||
|
[moves_] = m.groups()
|
||||||
|
pokemon.moves = [move.replace("_", " ").title() for move in move_definition.findall(moves_) if move != "NONE"]
|
||||||
|
elif m := nature_definition.search(line):
|
||||||
|
[nature_] = m.groups()
|
||||||
|
pokemon.nature = nature_.replace("_", " ").title()
|
||||||
|
elif is_blank.search(line):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception(f"could not parse '{line.strip()}'")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{in_path}:{line_no}: {e}")
|
||||||
|
return parties
|
||||||
|
|
||||||
|
is_trainer_skip = re.compile(r'(const struct Trainer gBattlePartners\[\] = \{)|(^ \{$)|(\.partySize =)|(\.party = NULL)|(\.mugshotEnabled = TRUE)|(\};)')
|
||||||
|
|
||||||
|
begin_trainer_definition = re.compile(r' \[(PARTNER_\w+)\] =')
|
||||||
|
end_trainer_definition = re.compile(r' }')
|
||||||
|
trainer_class_definition = re.compile(r'\.trainerClass = TRAINER_CLASS_(\w+)')
|
||||||
|
encounter_music_gender_definition = re.compile(r'\.encounterMusic_gender = (F_TRAINER_FEMALE \| )?TRAINER_ENCOUNTER_MUSIC_(\w+)')
|
||||||
|
trainer_pic_definition = re.compile(r'\.trainerPic = TRAINER_BACK_PIC_(\w+)')
|
||||||
|
trainer_name_definition = re.compile(r'\.trainerName = _\("([^"]*)"\)')
|
||||||
|
trainer_items_definition = re.compile(r'\.items = \{([^}]*)\}')
|
||||||
|
trainer_item_definition = re.compile(r'ITEM_(\w+)')
|
||||||
|
trainer_ai_flags_definition = re.compile(r'\.aiFlags = (.*)')
|
||||||
|
trainer_ai_flag_definition = re.compile(r'AI_FLAG_(\w+)')
|
||||||
|
trainer_party_definition = re.compile(r'\.party = TRAINER_PARTY\((\w+)\)')
|
||||||
|
trainer_mugshot_definition = re.compile(r'\.mugshotColor = MUGSHOT_COLOR_(\w+)')
|
||||||
|
trainer_starting_status_definition = re.compile(r'\.startingStatus = STARTING_STATUS_(\w+)')
|
||||||
|
|
||||||
|
class_fixups = {
|
||||||
|
"Rs": "RS",
|
||||||
|
}
|
||||||
|
|
||||||
|
pic_fixups = {
|
||||||
|
"Rs": "RS",
|
||||||
|
}
|
||||||
|
|
||||||
|
class Trainer:
|
||||||
|
def __init__(self, id_):
|
||||||
|
self.id = id_
|
||||||
|
self.class_ = None
|
||||||
|
self.encounter_music = None
|
||||||
|
self.gender = None
|
||||||
|
self.pic = None
|
||||||
|
self.name = None
|
||||||
|
self.items = []
|
||||||
|
self.ai_flags = None
|
||||||
|
self.mugshot = None
|
||||||
|
self.starting_status = None
|
||||||
|
self.party = None
|
||||||
|
|
||||||
|
def convert_trainers(in_path, in_h, parties, out_party):
|
||||||
|
newlines = 0
|
||||||
|
trainer = None
|
||||||
|
for line_no, line in enumerate(in_h, 1):
|
||||||
|
try:
|
||||||
|
line = line[:-1]
|
||||||
|
if m := begin_trainer_definition.search(line):
|
||||||
|
if trainer:
|
||||||
|
raise Exception(f"unexpected start of trainer")
|
||||||
|
[id_] = m.groups()
|
||||||
|
trainer = Trainer(id_)
|
||||||
|
elif m := trainer_class_definition.search(line):
|
||||||
|
[class_] = m.groups()
|
||||||
|
class_ = class_.replace("_", " ").title()
|
||||||
|
for match, replacement in class_fixups.items():
|
||||||
|
class_ = class_.replace(match, replacement)
|
||||||
|
trainer.class_ = class_
|
||||||
|
elif m := encounter_music_gender_definition.search(line):
|
||||||
|
[is_female, music] = m.groups()
|
||||||
|
trainer.gender = 'Female' if is_female else 'Male'
|
||||||
|
trainer.encounter_music = music.replace("_", " ").title()
|
||||||
|
elif m := trainer_pic_definition.search(line):
|
||||||
|
[pic] = m.groups()
|
||||||
|
pic = pic.replace("_", " ").title()
|
||||||
|
for match, replacement in pic_fixups.items():
|
||||||
|
pic = pic.replace(match, replacement)
|
||||||
|
trainer.pic = pic
|
||||||
|
elif m := trainer_name_definition.search(line):
|
||||||
|
[name] = m.groups()
|
||||||
|
trainer.name = name
|
||||||
|
elif m := trainer_items_definition.search(line):
|
||||||
|
[items] = m.groups()
|
||||||
|
trainer.items = " / ".join(item.replace("_", " ").title() for item in trainer_item_definition.findall(items) if item != "NONE")
|
||||||
|
elif m := trainer_ai_flags_definition.search(line):
|
||||||
|
[ai_flags] = m.groups()
|
||||||
|
trainer.ai_flags = " / ".join(ai_flag.replace("_", " ").title() for ai_flag in trainer_ai_flag_definition.findall(ai_flags))
|
||||||
|
elif m := trainer_mugshot_definition.search(line):
|
||||||
|
[color] = m.groups()
|
||||||
|
trainer.mugshot = color.title()
|
||||||
|
elif m := trainer_starting_status_definition.search(line):
|
||||||
|
[starting_status] = m.groups()
|
||||||
|
trainer.starting_status = starting_status.replace("_", " ").title()
|
||||||
|
elif m := trainer_party_definition.search(line):
|
||||||
|
[party] = m.groups()
|
||||||
|
trainer.party = parties[party]
|
||||||
|
elif end_trainer_definition.search(line):
|
||||||
|
if not trainer:
|
||||||
|
raise Exception(f"unexpected end of trainer")
|
||||||
|
while newlines > 0:
|
||||||
|
out_party.write(f"\n")
|
||||||
|
newlines -= 1
|
||||||
|
newlines = 1
|
||||||
|
out_party.write(f"=== {trainer.id} ===\n")
|
||||||
|
out_party.write(f"Name: {trainer.name}\n")
|
||||||
|
out_party.write(f"Class: {trainer.class_}\n")
|
||||||
|
out_party.write(f"Pic: {trainer.pic}\n")
|
||||||
|
out_party.write(f"Gender: {trainer.gender}\n")
|
||||||
|
out_party.write(f"Music: {trainer.encounter_music}\n")
|
||||||
|
if trainer.items:
|
||||||
|
out_party.write(f"Items: {trainer.items}\n")
|
||||||
|
if trainer.ai_flags:
|
||||||
|
out_party.write(f"AI: {trainer.ai_flags}\n")
|
||||||
|
if trainer.mugshot:
|
||||||
|
out_party.write(f"Mugshot: {trainer.mugshot}\n")
|
||||||
|
if trainer.starting_status:
|
||||||
|
out_party.write(f"Starting Status: {trainer.starting_status}\n")
|
||||||
|
if trainer.party:
|
||||||
|
for i, pokemon in enumerate(trainer.party):
|
||||||
|
out_party.write(f"\n")
|
||||||
|
if pokemon.nickname:
|
||||||
|
out_party.write(f"{pokemon.nickname} ({pokemon.species})")
|
||||||
|
else:
|
||||||
|
out_party.write(f"{pokemon.species}")
|
||||||
|
if pokemon.gender:
|
||||||
|
out_party.write(f" ({pokemon.gender})")
|
||||||
|
if pokemon.item and pokemon.item != 'None':
|
||||||
|
out_party.write(f" @ {pokemon.item}")
|
||||||
|
out_party.write(f"\n")
|
||||||
|
if pokemon.nature:
|
||||||
|
out_party.write(f"{pokemon.nature} Nature\n")
|
||||||
|
for key in pokemon_attribute_order:
|
||||||
|
if key in pokemon.attributes:
|
||||||
|
out_party.write(f"{key}: {pokemon.attributes[key]}\n")
|
||||||
|
for move in pokemon.moves:
|
||||||
|
out_party.write(f"- {move}\n")
|
||||||
|
trainer = None
|
||||||
|
elif is_blank.search(line) or is_trainer_skip.search(line):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise Exception(f"could not parse '{line.strip()}'")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{in_path}:{line_no}: {e}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
[argv0, trainers_in_path, parties_in_path, out_path] = sys.argv
|
||||||
|
except:
|
||||||
|
print(f"usage: python3 {sys.argv[0]} <trainers.h> <trainer_parties.h> <out>")
|
||||||
|
else:
|
||||||
|
with open(trainers_in_path, "r") as trainers_in_h, open(parties_in_path, "r") as parties_in_h, open(out_path, "w") as out_party:
|
||||||
|
parties = convert_parties(parties_in_path, parties_in_h)
|
||||||
|
trainers = convert_trainers(trainers_in_path, trainers_in_h, parties, out_party)
|
|
@ -699,7 +699,10 @@ static const u8 *const *const sPartnerApprenticeTextTables[NUM_APPRENTICES] =
|
||||||
#include "data/battle_frontier/battle_tent.h"
|
#include "data/battle_frontier/battle_tent.h"
|
||||||
|
|
||||||
#include "data/partner_parties.h"
|
#include "data/partner_parties.h"
|
||||||
|
const struct Trainer gBattlePartners[] =
|
||||||
|
{
|
||||||
#include "data/battle_partners.h"
|
#include "data/battle_partners.h"
|
||||||
|
};
|
||||||
|
|
||||||
static void (* const sBattleTowerFuncs[])(void) =
|
static void (* const sBattleTowerFuncs[])(void) =
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,20 +1,105 @@
|
||||||
const struct Trainer gBattlePartners[] = {
|
//
|
||||||
|
// DO NOT MODIFY THIS FILE! It is auto-generated from src/data/battle_partners.party
|
||||||
|
//
|
||||||
|
// If you want to modify this file set COMPETITIVE_PARTY_SYNTAX to FALSE
|
||||||
|
// in include/config.h and remove this notice.
|
||||||
|
// Use sed -i '/^#line/d' 'src/data/battle_partners.h' to remove #line markers.
|
||||||
|
//
|
||||||
|
|
||||||
|
#line 1 "src/data/battle_partners.party"
|
||||||
|
|
||||||
|
#line 1
|
||||||
[PARTNER_NONE] =
|
[PARTNER_NONE] =
|
||||||
{
|
{
|
||||||
.party = NULL,
|
#line 3
|
||||||
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
|
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
|
||||||
.encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE,
|
#line 4
|
||||||
.trainerPic = TRAINER_PIC_HIKER,
|
.trainerPic = TRAINER_BACK_PIC_BRENDAN,
|
||||||
.trainerName = _(""),
|
.encounterMusic_gender =
|
||||||
.items = {},
|
#line 6
|
||||||
|
TRAINER_ENCOUNTER_MUSIC_MALE,
|
||||||
|
.partySize = 0,
|
||||||
|
.party = (const struct TrainerMon[])
|
||||||
|
{
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
#line 8
|
||||||
[PARTNER_STEVEN] =
|
[PARTNER_STEVEN] =
|
||||||
{
|
{
|
||||||
.party = TRAINER_PARTY(sParty_StevenPartner),
|
#line 9
|
||||||
.trainerClass = TRAINER_CLASS_RIVAL,
|
|
||||||
.encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE,
|
|
||||||
.trainerPic = TRAINER_BACK_PIC_STEVEN,
|
|
||||||
.trainerName = _("STEVEN"),
|
.trainerName = _("STEVEN"),
|
||||||
|
#line 10
|
||||||
|
.trainerClass = TRAINER_CLASS_RIVAL,
|
||||||
|
#line 11
|
||||||
|
.trainerPic = TRAINER_BACK_PIC_STEVEN,
|
||||||
|
.encounterMusic_gender =
|
||||||
|
#line 13
|
||||||
|
TRAINER_ENCOUNTER_MUSIC_MALE,
|
||||||
|
.partySize = 3,
|
||||||
|
.party = (const struct TrainerMon[])
|
||||||
|
{
|
||||||
|
{
|
||||||
|
#line 15
|
||||||
|
.species = SPECIES_METANG,
|
||||||
|
.gender = TRAINER_MON_RANDOM_GENDER,
|
||||||
|
#line 19
|
||||||
|
.ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0),
|
||||||
|
#line 18
|
||||||
|
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
||||||
|
#line 17
|
||||||
|
.lvl = 42,
|
||||||
|
#line 16
|
||||||
|
.nature = NATURE_BRAVE,
|
||||||
|
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
|
||||||
|
.moves = {
|
||||||
|
#line 20
|
||||||
|
MOVE_LIGHT_SCREEN,
|
||||||
|
MOVE_PSYCHIC,
|
||||||
|
MOVE_REFLECT,
|
||||||
|
MOVE_METAL_CLAW,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
#line 25
|
||||||
|
.species = SPECIES_SKARMORY,
|
||||||
|
.gender = TRAINER_MON_RANDOM_GENDER,
|
||||||
|
#line 29
|
||||||
|
.ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252),
|
||||||
|
#line 28
|
||||||
|
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
||||||
|
#line 27
|
||||||
|
.lvl = 43,
|
||||||
|
#line 26
|
||||||
|
.nature = NATURE_IMPISH,
|
||||||
|
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
|
||||||
|
.moves = {
|
||||||
|
#line 30
|
||||||
|
MOVE_TOXIC,
|
||||||
|
MOVE_AERIAL_ACE,
|
||||||
|
MOVE_PROTECT,
|
||||||
|
MOVE_STEEL_WING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
#line 35
|
||||||
|
.species = SPECIES_AGGRON,
|
||||||
|
.gender = TRAINER_MON_RANDOM_GENDER,
|
||||||
|
#line 39
|
||||||
|
.ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6),
|
||||||
|
#line 38
|
||||||
|
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
||||||
|
#line 37
|
||||||
|
.lvl = 44,
|
||||||
|
#line 36
|
||||||
|
.nature = NATURE_ADAMANT,
|
||||||
|
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
|
||||||
|
.moves = {
|
||||||
|
#line 40
|
||||||
|
MOVE_THUNDER,
|
||||||
|
MOVE_PROTECT,
|
||||||
|
MOVE_SOLAR_BEAM,
|
||||||
|
MOVE_DRAGON_CLAW,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
43
src/data/battle_partners.party
Normal file
43
src/data/battle_partners.party
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
=== PARTNER_NONE ===
|
||||||
|
Name:
|
||||||
|
Class: Pkmn Trainer 1
|
||||||
|
Pic: Brendan
|
||||||
|
Gender: Male
|
||||||
|
Music: Male
|
||||||
|
|
||||||
|
=== PARTNER_STEVEN ===
|
||||||
|
Name: STEVEN
|
||||||
|
Class: Rival
|
||||||
|
Pic: Steven
|
||||||
|
Gender: Male
|
||||||
|
Music: Male
|
||||||
|
|
||||||
|
Metang
|
||||||
|
Brave Nature
|
||||||
|
Level: 42
|
||||||
|
IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe
|
||||||
|
EVs: 252 Atk / 252 Def / 6 SpA
|
||||||
|
- Light Screen
|
||||||
|
- Psychic
|
||||||
|
- Reflect
|
||||||
|
- Metal Claw
|
||||||
|
|
||||||
|
Skarmory
|
||||||
|
Impish Nature
|
||||||
|
Level: 43
|
||||||
|
IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe
|
||||||
|
EVs: 252 HP / 6 SpA / 252 SpD
|
||||||
|
- Toxic
|
||||||
|
- Aerial Ace
|
||||||
|
- Protect
|
||||||
|
- Steel Wing
|
||||||
|
|
||||||
|
Aggron
|
||||||
|
Adamant Nature
|
||||||
|
Level: 44
|
||||||
|
IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe
|
||||||
|
EVs: 252 Atk / 252 SpA / 6 SpD
|
||||||
|
- Thunder
|
||||||
|
- Protect
|
||||||
|
- Solar Beam
|
||||||
|
- Dragon Claw
|
|
@ -1,26 +1 @@
|
||||||
static const struct TrainerMon sParty_StevenPartner[] = {
|
|
||||||
{
|
|
||||||
.species = SPECIES_METANG,
|
|
||||||
.lvl = 42,
|
|
||||||
.nature = NATURE_BRAVE,
|
|
||||||
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
|
||||||
.ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0),
|
|
||||||
.moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.species = SPECIES_SKARMORY,
|
|
||||||
.lvl = 43,
|
|
||||||
.nature = NATURE_IMPISH,
|
|
||||||
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
|
||||||
.ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252),
|
|
||||||
.moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.species = SPECIES_AGGRON,
|
|
||||||
.lvl = 44,
|
|
||||||
.nature = NATURE_ADAMANT,
|
|
||||||
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
|
|
||||||
.ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6),
|
|
||||||
.moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -143,6 +143,12 @@ static bool is_literal_string(struct String s1, const char *s2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool starts_with(struct String s, const char *prefix)
|
||||||
|
{
|
||||||
|
int n = strlen(prefix);
|
||||||
|
return strncmp((const char *)s.string, prefix, n) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ends_with(struct String s, const char *suffix)
|
static bool ends_with(struct String s, const char *suffix)
|
||||||
{
|
{
|
||||||
int n = strlen(suffix);
|
int n = strlen(suffix);
|
||||||
|
@ -1194,7 +1200,7 @@ static bool parse_trainer(struct Parser *p, const struct Parsed *parsed, struct
|
||||||
while (match_empty_line(p)) {}
|
while (match_empty_line(p)) {}
|
||||||
if (!parse_pokemon_header(p, &nickname, &species, &gender, &item))
|
if (!parse_pokemon_header(p, &nickname, &species, &gender, &item))
|
||||||
{
|
{
|
||||||
if (i > 0 || is_literal_string(trainer->id, "TRAINER_NONE"))
|
if (i > 0 || ends_with(trainer->id, "_NONE"))
|
||||||
break;
|
break;
|
||||||
if (!p->error)
|
if (!p->error)
|
||||||
set_parse_error(p, p->location, "expected nickname or species");
|
set_parse_error(p, p->location, "expected nickname or species");
|
||||||
|
@ -1614,7 +1620,10 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par
|
||||||
{
|
{
|
||||||
fprintf(f, "#line %d\n", trainer->pic_line);
|
fprintf(f, "#line %d\n", trainer->pic_line);
|
||||||
fprintf(f, " .trainerPic = ");
|
fprintf(f, " .trainerPic = ");
|
||||||
fprint_constant(f, "TRAINER_PIC", trainer->pic);
|
if (starts_with(trainer->id, "PARTNER_"))
|
||||||
|
fprint_constant(f, "TRAINER_BACK_PIC", trainer->pic);
|
||||||
|
else
|
||||||
|
fprint_constant(f, "TRAINER_PIC", trainer->pic);
|
||||||
fprintf(f, ",\n");
|
fprintf(f, ",\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue