support modded NPC sprites (#1146)

* support modded NPC sprites

- improve compatibility with Star Rod SpriteSheet.xml files
  - SR's intention is to move to the decomp xml but the current release of SR emitted incompatible xml
- use npc.xml instead of npc_sprite_names.yaml to generate linker entries. this allows mods to add new sprites

* black

why does it want two spaces before line comments!?

* Doxygen (#1142)

* use doxygen
* add documenting guide based on https://github.com/zeldaret/oot/blob/main/docs/Documenting.md
* exclude stdlib readme from doxygen
* refuse to configure matching iQue on macOS (EGCS compiler is not built for macOS, so iQue won't build. We still enable iQue builds on macOS by using gcc-papermario via --non-matching.)
* use proper doxygen bug comment style
* document common EVT API funcs nicely
* add doxygen ci
* add \vars command

* s/master/main

* use Doxygen 1.10.0

* fix doxygen ci

* fix doxygen ci

* fix doxygen (final)

* fix doxygen (final real)

* Fix Doxygen CI (#1147)

* remove old doxygen ci line

* fix warns

Thanks @Ponmander for reporting this
This commit is contained in:
Alex Bates 2024-01-11 10:33:39 +00:00 committed by GitHub
parent fdb4428cc6
commit 82b09bd69e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 7 deletions

View File

@ -3,7 +3,6 @@ on:
push:
branches:
- main
- i-love-continuous-deployment # TEMP obviously
jobs:
doxygen:
runs-on: ubuntu-latest

View File

@ -36,6 +36,21 @@ def pack_color(r, g, b, a):
return s
def resolve_image_path(
sprite_dir: Path,
sub_dir: str,
img_name: str,
asset_stack: Tuple[Path, ...],
) -> str:
try:
img_path = get_asset_path(sprite_dir / sub_dir / img_name, asset_stack)
except FileNotFoundError:
# Allow missing subdirectory for backwards compatibility with Star Rod
img_path = get_asset_path(sprite_dir / img_name, asset_stack)
print(f"warning: please move {sprite_dir}/{img_path.name} to '{sub_dir}' subdirectory")
return str(img_path)
def from_dir(
sprite_name: str,
asset_stack: Tuple[Path, ...],
@ -66,7 +81,7 @@ def from_dir(
for Palette in SpriteSheet.findall("./PaletteList/Palette"):
if asset_stack is not None and load_images:
img_name = Palette.attrib["src"]
img_path = str(get_asset_path(sprite_dir / "palettes" / img_name, asset_stack))
img_path = resolve_image_path(sprite_dir, "palettes", img_name, asset_stack)
img = png.Reader(img_path)
img.preamble(True)
palette = img.palette(alpha="force")
@ -83,7 +98,7 @@ def from_dir(
for Raster in SpriteSheet.findall("./RasterList/Raster"):
if asset_stack is not None and load_images:
img_name = Raster.attrib["src"]
img_path = str(get_asset_path(sprite_dir / "rasters" / img_name, asset_stack))
img_path = resolve_image_path(sprite_dir, "rasters", img_name, asset_stack)
width, height, raster, info = png.Reader(img_path).read_flat()
palette_index = int(Raster.attrib["palette"], base=16)

View File

@ -19,6 +19,9 @@ from splat.util.color import unpack_color
sys.path.insert(0, str(Path(__file__).parent))
from sprite_common import AnimComponent, iter_in_groups, read_offset_list
sys.path.insert(0, str(Path(__file__).parent.parent))
from common import get_asset_path
# TODO move into yaml
PLAYER_PAL_TO_RASTER: Dict[str, int] = {
"8bit": 0x57C90,
@ -832,11 +835,17 @@ class N64SegPm_sprites(N64Segment):
def get_linker_entries(self):
from splat.segtypes.linker_entry import LinkerEntry
import splat.scripts.split as split
src_paths = [options.opts.asset_path / "sprite"]
# for NPC
src_paths += [options.opts.asset_path / "sprite" / "npc" / sprite_name for sprite_name in self.npc_cfg]
# read npc.xml - we can't use self.npc_cfg because nonvanilla asset packs can change it
# for each sprite, add to src_paths
asset_stack = tuple(Path(p) for p in split.config["asset_stack"])
orderings_tree = ET.parse(get_asset_path(Path("sprite") / NPC_SPRITE_MEDADATA_XML_FILENAME, asset_stack))
for sprite_tag in orderings_tree.getroot()[0]:
name = sprite_tag.attrib["name"]
src_paths.append(options.opts.asset_path / "sprite" / "npc" / name)
return [LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section(), self.get_linker_section())]

View File

@ -416,6 +416,8 @@ class AnimComponent:
commands.append(0x8100 + (int(cmd.attrib[XML_ATTR_INDEX]) & 0xFF))
elif cmd.tag == "SetNotify":
commands.append(0x8200 + (int(cmd.attrib[XML_ATTR_VALUE]) & 0xFF))
elif cmd.tag == "Command": # old Star Rod compatibility
commands.append(int(cmd.attrib["val"], 16))
else:
raise ValueError(f"unknown command {cmd.tag}")
x, y, z = xml.attrib[XML_ATTR_XYZ].split(",")

View File

@ -422,13 +422,13 @@ class TexImage:
palette_count = (0x20 if fmt_str == "CI4" else 0x200) // 2
if len(palette) > palette_count:
palette = palette[:palette_count]
self.warn(f"{self.img_name} has more than {palette_count} colors, truncating")
print(f"warning: {self.img_name} has more than {palette_count} colors, truncating")
elif len(palette) < palette_count:
palette += [(0, 0, 0, 0)] * (palette_count - len(palette))
for rgba in palette:
if rgba[3] not in (0, 0xFF):
self.warn("alpha mask mode but translucent pixels used")
print(f"warning: alpha mask mode but {self.img_name} has translucent pixels")
color = pack_color(*rgba)
out_pal += color.to_bytes(2, byteorder="big")