refactor legendary battle flags

This commit is contained in:
cawtds 2024-07-23 22:25:34 +02:00
parent 9893bf8ef2
commit 4975ea2144
12 changed files with 45 additions and 391 deletions

View File

@ -1,110 +0,0 @@
#!/usr/bin/env python3
""" Extract sprites from HGSS follower spritesheets. """
import os.path
import subprocess
import sys
from glob import glob
import png
SPRITESHEETS = [('gen1.png', 15, 11, 1)]
output_dir = 'sprites'
index_to_name = {}
with open('names.txt', 'r') as f:
for line in f:
index, name = line.split(' ')[:2]
name = name.strip()
index_to_name[int(index)] = name.lower()
name_to_index = {v: k for k, v in index_to_name.items()}
PKMN_GRAPHICS = os.path.join('graphics', 'pokemon')
def extract_sprites(spritesheet):
path, width, height, offset = spritesheet
for y in range(height):
for x in range(width):
if x == 3 and y == 0 or x == 10 and y == 1:
continue
output_path = os.path.join(output_dir, f'{offset:03d}.png')
subprocess.run(['convert', '-extract', f'64x128+{x*(64+1)}+{y*(128+1)}', path, output_path], check=True)
offset += 1
def stack_sprite(name, path):
joinp = os.path.join
frames = [joinp(path, 'down', name), joinp(path, 'down', 'frame2', name),
joinp(path, 'up', name), joinp(path, 'up', 'frame2', name),
joinp(path, 'left', name), joinp(path, 'left', 'frame2', name)]
output = joinp(path, name)
subprocess.run(['convert'] + frames + ['+append', output], check=True)
print(f'Stacked {output}')
def canonicalize_names():
for path in glob('overworld/**/*.png', recursive=True):
head, tail = os.path.split(path)
name, ext = os.path.splitext(tail)
try:
num = int(name)
except ValueError:
continue
new_name = f'{num:03d}'
new_path = os.path.join(head, new_name+ext)
os.rename(path, new_path)
print(path, '->', new_path)
def closest_color(c, palette):
min_d = float('inf')
best = 0
r1, g1, b1 = c
for i, (r2, g2, b2) in enumerate(palette[1:], 1):
# Color diff from https://stackoverflow.com/questions/1847092/given-an-rgb-value-what-would-be-the-best-way-to-find-the-closest-match-in-the-d
d = ((r2-r1)*0.30)**2 + ((g2-g1)*0.59)**2 + ((b2-b1)*0.11)**2
if d < min_d:
min_d = d
best = i
return best
def apply_palette(palette_file, input_file, output_file): # Apply one file's palette to another
plt = png.Reader(palette_file)
plt.read()
target_palette = tuple(c[:3] for c in plt.palette())
inp = png.Reader(input_file)
w, h, rows, _ = inp.read()
src_palette = tuple(c[:3] for c in inp.palette())
with open(output_file, 'wb') as f:
new_rows = []
for row in rows:
new_rows.append([closest_color(src_palette[c], target_palette) if c else 0 for c in row])
w = png.Writer(width=w, height=h, bitdepth=4, palette=target_palette)
w.write(f, new_rows)
def paletteify(path, output_path=None):
output_path = output_path or path
joinp = os.path.join
_, tail = os.path.split(path)
species, _ = os.path.splitext(tail)
front = png.Reader(joinp(PKMN_GRAPHICS, species, 'anim_front.png'))
front.read()
target_palette = tuple(c[:3] for c in front.palette())
r, g, b = target_palette[0]
color = f'rgb({r},{g},{b})'
# Strip alpha color
subprocess.run(['convert', path, '-background', color, '-alpha', 'remove', output_path], check=True)
apply_palette(joinp(PKMN_GRAPHICS, species, 'anim_front.png'), output_path, output_path)
# Sprites from https://veekun.com/dex/downloads
if __name__ == '__main__':
args = sys.argv[1:]
if args:
paletteify(args[0])
else:
for path in sorted(glob('overworld/*.png')):
_, tail = os.path.split(path)
name, _ = os.path.splitext(tail)
output_path = os.path.join('graphics/object_events/pics/pokemon', f'{name}.png')
try:
paletteify(path, output_path)
except Exception as e:
print(name, e.__class__.__name__, e, file=sys.stderr)

View File

@ -1,50 +0,0 @@
""" Processes & outputs follower emotion messages """
import sys
import re
import textwrap
blank_regex = re.compile(r'\(?_+\)?')
# Converts a series of message lines to a better format
def convert_messages(infile, outfile='emotions.txt'):
with open(infile, 'r') as f_in, open(outfile, 'w') as f_out:
for line in f_in:
line = line.rstrip('\n')
if line and line[0] == '-':
line = line[1:]
line = line.lstrip()
if not line:
continue
line = blank_regex.sub('{STR_VAR_1}', line)
if line[-1] not in ('.', '?', '!', ':'):
line += '.'
print(line)
f_out.write('\n' + line)
# Prepares a string for field-message display, performing line-wrapping, etc
# Does not add a terminator, as this is done by _("")
def prepare_string(s):
lines = textwrap.wrap(s, width=36) # Width of message window
s = lines[0]
for i, line in enumerate(lines[1:]):
ending = r'\p' if i % 2 else r'\n'
s += ending + line
return s
# Exports up to n messages in C format to outfile
def export_messages(infile, outfile, n=None, indent=0, start=0):
with open(infile, 'r') as f_in:
lines = f_in.readlines()
if n is not None:
lines = lines[:n]
with open(outfile, 'w') as f_out:
codelines = [' '*indent + f'static const u8 sCondMsg{start+i:02d}[] = _("{prepare_string(s)}");' for i, s in enumerate(lines)]
f_out.write('\n'.join(codelines))
print(f'{len(lines)} lines written')
return len(lines)
if __name__ == '__main__':
export_messages('emotions.txt', 'emotions.h', n=1, start=7)

View File

@ -1,77 +0,0 @@
#!/usr/bin/env python3
""" Extract sprites from HGSS follower spritesheets. """
import os.path
from os.path import join as joinp
import subprocess
import sys
from glob import glob
import png
from tqdm import tqdm
import shutil
def stack_sprite(name, path):
frames = [joinp(path, 'down', name), joinp(path, 'down', 'frame2', name),
joinp(path, 'up', name), joinp(path, 'up', 'frame2', name),
joinp(path, 'left', name), joinp(path, 'left', 'frame2', name)]
output = joinp(path, name)
subprocess.run(['convert'] + frames + ['+append', output], check=True)
print(f'Stacked {output}')
def closest_color(c, palette):
min_d = float('inf')
best = 0
r1, g1, b1 = c
for i, (r2, g2, b2) in enumerate(palette[1:], 1):
# Color diff from https://stackoverflow.com/questions/1847092/given-an-rgb-value-what-would-be-the-best-way-to-find-the-closest-match-in-the-d
d = ((r2-r1)*0.30)**2 + ((g2-g1)*0.59)**2 + ((b2-b1)*0.11)**2
if d < min_d:
min_d = d
best = i
return best
def apply_palette(palette_file, input_file, output_file): # Apply one file's palette to another
plt = png.Reader(palette_file)
plt.read()
target_palette = tuple(c[:3] for c in plt.palette())
inp = png.Reader(input_file)
w, h, rows, info = inp.read()
src_palette = tuple(c[:3] for c in inp.palette())
new_rows = [[closest_color(src_palette[c][:3], target_palette) if c else 0 for c in row] for row in rows]
with open(output_file, 'wb') as f:
w = png.Writer(width=w, height=h, bitdepth=4, palette=target_palette)
w.write(f, new_rows)
# Sprites from https://veekun.com/dex/downloads
def apply_front_palettes(ow_dir, project_root=''):
mon_graphics = joinp(project_root, 'graphics', 'pokemon')
for x in os.walk(ow_dir):
current_dir = x[0]
sub_dir = current_dir[len(ow_dir) + 1:1000]
t = tqdm(sorted(glob(joinp(current_dir, '*.png'))))
spaces = 0
for path in t:
name, _ = os.path.splitext(os.path.basename(path))
name = joinp(sub_dir, name)
# old_path = joinp(project_root, 'graphics', 'object_events', 'pics', 'pokemon', f'{name}.png')
# new_path = joinp(project_root, 'graphics', 'object_events', 'pics', 'pokemon', name, 'follower.png')
# os.mkdir(joinp(project_root, 'graphics', 'object_events', 'pics', 'pokemon', name))
# shutil.move(old_path, new_path)
spaces = min(max(len(name), spaces), 10)
t.set_description(name + ' '*(spaces-len(name)))
output_path = joinp(project_root, 'graphics', 'object_events', 'pics', 'pokemon', f'{name}.png')
palette_path = joinp(mon_graphics, name, 'anim_front.png')
try:
apply_palette(palette_path, path, output_path)
except Exception as e:
palette_path = joinp(mon_graphics, name, 'front.png')
try:
apply_palette(palette_path, path, output_path)
except Exception as e2:
t.write(f'{name}: {e2.__class__.__name__}: {e2}', file=sys.stderr)
if __name__ == '__main__':
apply_front_palettes('graphics/object_events/pics/pokemon')

View File

@ -1,26 +0,0 @@
#!/usr/bin/python3
""" Extract a GBA-compatible palette from a PNG. """
import sys
import os.path
import png
PAL_PRELUDE = 'JASC-PAL\n0100\n'
def extract_palette(path):
r = png.Reader(path)
r.read()
root, _ = os.path.splitext(path)
out_path = root + '.pal'
with open(out_path, 'w', newline='\r\n') as f:
f.write(PAL_PRELUDE)
colors = r.palette()
if len(colors) < 16:
colors += [(0, 0, 0) for _ in range(16-len(colors))]
f.write(f'{len(colors)}\n')
for r, g, b in colors:
f.write(f'{r} {g} {b}\n')
if __name__ == '__main__':
extract_palette(*sys.argv[1:])

View File

@ -1,11 +0,0 @@
#!/bin/bash
lalala1="graphics/object_events/pics/pokemon/"
lalala2="a2"
for file in graphics/object_events/pics/pokemon/*.png; do
name=${file##*/}
base=${name%.png}
#echo "${file}"
#echo "${base}"
echo "graphics/pokemon/${base}/follower.png"
mv -- "$file" "graphics/pokemon/${base}/follower.png"
done

View File

@ -1,24 +0,0 @@
import glob
import re
import json
import os
import subprocess
# THIS IS A TEMPORARY SCRIPT MADE TO RENAME FILES WITH THE "FOLLOWER" NAME TO "OVERWORLD",
# AS THESE GRAPHICS CAN ALSO BE USED OUTSIDE THE FOLLOWER FEATURE.
#
# I'M SAVING IT HERE IN CASE IT'S NEEDED SOMEWHERE IN THE FUTURE, THOUGH TWEAKING MIGHT BE NEEDED.
# - AsparagusEduardo
def rename_files(dir, old, new):
for root, dirs, files in os.walk(dir):
for name in files:
if name.endswith(old):
originalName = os.path.join(root, name)
newName = originalName.replace(old, new)
print(originalName + " -> " + newName)
os.rename(originalName, newName)
rename_files("graphics/pokemon", 'follower.png', "overworld.png")
rename_files("graphics/pokemon", 'follow_normal.pal', "overworld_normal.pal")
rename_files("graphics/pokemon", 'follow_shiny.pal', "overworld_shiny.pal")

View File

@ -1,64 +0,0 @@
import glob
import re
import json
import os
import subprocess
# THIS IS A TEMPORARY SCRIPT MADE TO MOVE EXISTING FOLLOWER GRAPHICS FROM A SINGLE DIRECTORY.
# IT TAKES FOLLOWER GRAPHICS FROM a 'followers' FOLDER IN THE ROOT FOLDER AND MOVES THEM BASED ON THEIR NAME.
# EG. 'followers/bulbasaur.png' WILL BE MOVED to 'graphics/pokemon/bulbasaur/follower.png'.
#
# I'M SAVING IT HERE IN CASE IT'S NEEDED SOMEWHERE IN THE FUTURE, THOUGH TWEAKING MIGHT BE NEEDED.
# - AsparagusEduardo
def rellocate_follower_graphics():
dict_out = {}
count = 0
for pth in sorted(glob.glob('followers/*.png')):
name = pth.replace(".png", "").replace("followers/", "")
count+=1
#if (count == 2):
# break
print(name)
newname = name
newname = newname.replace("_female", "/female")
newname = newname.replace("_hisuian", "/hisuian")
newname = newname.replace("_galarian", "/galarian")
newname = newname.replace("_origin", "/origin")
newname = newname.replace("_therian", "/therian")
newname = newname.replace("_east_sea", "/east_sea")
newname = newname.replace("_crowned", "/crowned")
newname = newname.replace("arceus_", "arceus/")
newname = newname.replace("burmy_", "burmy/")
newname = newname.replace("basculin_", "basculin/")
newname = newname.replace("castform_", "castform/")
newname = newname.replace("calyrex_", "calyrex/")
newname = newname.replace("deerling_", "deerling/")
newname = newname.replace("deoxys_", "deoxys/")
newname = newname.replace("flabebe_", "flabebe/")
newname = newname.replace("floette_", "floette/")
newname = newname.replace("florges_", "florges/")
newname = newname.replace("furfrou_", "furfrou/")
newname = newname.replace("hoopa_", "hoopa/")
newname = newname.replace("lycanroc_", "lycanroc/")
newname = newname.replace("meloetta_", "meloetta/")
newname = newname.replace("necrozma_", "necrozma/")
newname = newname.replace("pichu_", "pichu/")
newname = newname.replace("rotom_", "rotom/")
newname = newname.replace("sawsbuck_", "sawsbuck/")
newname = newname.replace("toxtricity_", "toxtricity/")
newname = newname.replace("unown_", "unown/")
newname = newname.replace("ursaluna_", "ursaluna/")
newname = newname.replace("vivillon_", "vivillon/")
newname = newname.replace("wormadam_", "wormadam/")
if (os.path.exists('followers/' + newname) == False):
os.mkdir('followers/' + newname)
os.rename('followers/' + name + '.png', 'followers/' + newname + '/follower.png')
#os.popen('cp followers/' + name + '.png followers/' + name + '/follower.png')
#os.remove('followers/' + name + '.png')
#print(pth)
#subprocess.run(["tools/gbagfx/gbagfx " + name +".png " + name + "_normal.pal'" + str(count) + "'"])
rellocate_follower_graphics()

View File

@ -57,14 +57,14 @@
#define BATTLE_TYPE_OLD_MAN_TUTORIAL (1 << 9) // Used in pokeemerald as BATTLE_TYPE_WALLY_TUTORIAL.
#define BATTLE_TYPE_ROAMER (1 << 10)
#define BATTLE_TYPE_EREADER_TRAINER (1 << 11)
#define BATTLE_TYPE_KYOGRE_GROUDON (1 << 12)
#define BATTLE_TYPE_REMOVED1 (1 << 12)
#define BATTLE_TYPE_LEGENDARY (1 << 13)
#define BATTLE_TYPE_GHOST_UNVEILED (1 << 13) // Re-use of BATTLE_TYPE_LEGENDARY, when combined with BATTLE_TYPE_GHOST
#define BATTLE_TYPE_REGI (1 << 14)
#define BATTLE_TYPE_REMOVED2 (1 << 14)
#define BATTLE_TYPE_GHOST (1 << 15) // Used in pokeemerald as BATTLE_TYPE_TWO_OPPONENTS.
#define BATTLE_TYPE_POKEDUDE (1 << 16) // Used in pokeemerald as BATTLE_TYPE_DOME.
#define BATTLE_TYPE_WILD_SCRIPTED (1 << 17) // Used in pokeemerald as BATTLE_TYPE_PALACE.
#define BATTLE_TYPE_LEGENDARY_FRLG (1 << 18) // Used in pokeemerald as BATTLE_TYPE_ARENA.
#define BATTLE_TYPE_REMOVED_3 (1 << 18) // Used in pokeemerald as BATTLE_TYPE_ARENA.
#define BATTLE_TYPE_TRAINER_TOWER (1 << 19) // Used in pokeemerald as BATTLE_TYPE_FACTORY.
// pokeemerald
#define BATTLE_TYPE_INGAME_PARTNER (1 << 20)

View File

@ -749,15 +749,22 @@ void DrawBattleEntryBackground(void)
{
LoadBattleTerrainEntryGfx(BATTLE_TERRAIN_BUILDING);
}
else if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON)
else if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY)
{
if (gGameVersion == VERSION_FIRE_RED)
switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES))
{
case SPECIES_GROUDON:
LoadBattleTerrainEntryGfx(BATTLE_TERRAIN_CAVE);
}
else
{
break;
case SPECIES_KYOGRE:
LoadBattleTerrainEntryGfx(BATTLE_TERRAIN_WATER);
break;
default:
if (GetCurrentMapBattleScene() == MAP_BATTLE_SCENE_NORMAL)
LoadBattleTerrainEntryGfx(gBattleTerrain);
else
LoadBattleTerrainEntryGfx(BATTLE_TERRAIN_BUILDING);
break;
}
}
else

View File

@ -98,7 +98,7 @@ void HandleIntroSlide(u8 terrain)
{
taskId = CreateTask(BattleIntroSlideLink, 0);
}
else if ((gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON) && gGameVersion != VERSION_RUBY)
else if ((gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) && GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL) == SPECIES_KYOGRE)
{
terrain = BATTLE_TERRAIN_UNDERWATER;
taskId = CreateTask(BattleIntroSlide2, 0);

View File

@ -403,25 +403,26 @@ void StartLegendaryBattle(void)
LockPlayerFieldControls();
gMain.savedCallback = CB2_EndScriptedWildBattle;
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_LEGENDARY_FRLG;
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES);
switch (species)
{
case SPECIES_MEWTWO:
case SPECIES_MEWTWO_MEGA_X:
case SPECIES_MEWTWO_MEGA_Y:
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_MEWTWO);
break;
case SPECIES_DEOXYS:
case SPECIES_DEOXYS_NORMAL:
case SPECIES_DEOXYS_ATTACK:
case SPECIES_DEOXYS_DEFENSE:
case SPECIES_DEOXYS_SPEED:
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_DEOXYS);
break;
case SPECIES_MOLTRES:
case SPECIES_ARTICUNO:
case SPECIES_ZAPDOS:
case SPECIES_HO_OH:
case SPECIES_LUGIA:
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_LEGEND);
break;
default:
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RS_VS_TRAINER);
if (gSpeciesInfo[species].isLegendary || gSpeciesInfo[species].isMythical)
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_LEGEND);
else
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RS_VS_TRAINER);
break;
}
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
@ -432,11 +433,8 @@ void StartGroudonKyogreBattle(void)
{
LockPlayerFieldControls();
gMain.savedCallback = CB2_EndScriptedWildBattle;
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_KYOGRE_GROUDON;
if (gGameVersion == VERSION_FIRE_RED)
CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_RS_VS_TRAINER);
else // pointless, exactly the same
CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_RS_VS_TRAINER);
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_RS_VS_TRAINER);
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
IncrementGameStat(GAME_STAT_WILD_BATTLES);
}
@ -445,7 +443,7 @@ void StartRegiBattle(void)
{
LockPlayerFieldControls();
gMain.savedCallback = CB2_EndScriptedWildBattle;
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_REGI;
gBattleTypeFlags = BATTLE_TYPE_LEGENDARY;
CreateBattleStartTask(B_TRANSITION_BLUR, MUS_RS_VS_TRAINER);
IncrementGameStat(GAME_STAT_TOTAL_BATTLES);
IncrementGameStat(GAME_STAT_WILD_BATTLES);

View File

@ -5348,10 +5348,21 @@ u16 SpeciesToPokedexNum(u16 species)
static u16 GetBattleBGM(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON)
return MUS_VS_WILD;
if (gBattleTypeFlags & BATTLE_TYPE_REGI)
return MUS_RS_VS_TRAINER;
if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY)
{
switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES))
{
case SPECIES_REGIROCK:
case SPECIES_REGICE:
case SPECIES_REGISTEEL:
case SPECIES_REGIGIGAS:
case SPECIES_REGIELEKI:
case SPECIES_REGIDRAGO:
return MUS_RS_VS_TRAINER;
default:
return MUS_VS_WILD;
}
}
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
return MUS_RS_VS_TRAINER;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)