Skip to content

A port's tail tip: whole until the cut, then it drops - #63

Merged
sp00ktober merged 20 commits into
mainfrom
tail/attach
Oct 9, 2026
Merged

sp00ktober merged 20 commits into
mainfrom
tail/attach

Conversation

@sp00ktober

Copy link
Copy Markdown
Contributor

A port's tail is whole until it is cut; then the tip lies on the ground beside it, as on a native monster. For sp00ktober/MHFU-ModKit#121.

  • Converter:
    • The tip is PMO mesh record 1, with its cut face as material Workspace skeleton: uv, ruff, pytest, pre-commit, CI, REUSE #1, which em75 hides until the cut.
    • Its chain stays a root at params[1]; ADOPTER is gone.
    • rig.tip_of derives the tip-to-tail joint map from the skeleton (it reproduces the game's own attach joints on all 35 native rigs with a tip), and the moves module carries it as tip.
    • mesh.build requires the tip, so fidelity and the Blender writer split it too.
    • verify fails a port that a tail-dropping host would crash on: a one-mesh model makes PPSSPP die when the dropped tip draws.
  • Framework (api 3):
    • mhfu_em_tip / mhfu.em_tip: em_vhook wraps the species OBJ_VTABLE.BRAIN and copies each tip joint's JOINT.POSE from its tail joint every frame, until the drop. mhfu_port installs it from the moves module.
    • Rules, requests and own moves wait while the cut's (4,4) runs to the drop, so a mod can no longer skip it.
  • Manifest: a clip's start cuts its source, so one source fills several entries. The layout.clips helper is now the one way to get the clip an entry plays.
  • Zinogre: its tail-cut flinch is frames 0–2 on entry 65 and the rest on 83, so the tail drops 0.2 s after the cut and the flinch plays on through it. The tail_cut rule is gone, and walk_forwards_faster is pinned at 73 for the village NPC.
  • Studio: a render poses the tip on the tail; --severed shows the stump.

Live in lane 3:

  • the tip joints equal their carriers on 60 of 60 frames, and the tail is whole in idle, walk and attack;
  • the drop comes 0.200 s after SEVERED;
  • the tip stays on the ground for 3 min, with no crash;
  • the village follower still walks.

Tests: 2531 passed with game data; the CI job in a Linux container passes.

Follow-ups: carving, a thrown fall, and making the studio's clip preview aware of start.

🤖 Generated with Claude Code

sp00ktober and others added 20 commits October 9, 2026 13:13
…or its drop

mhfu_em_tip / mhfu.em_tip pose a port's tip joints from carriers after the
species brain until TAIL_DROP. While em75's cut (4,4) waits for its drop on a
model with mesh 1, requests (now issued by the C brain), plays, rules and the
move player's start wait. Names the pose, the mesh mask, the PMO mesh count
and the tail-drop functions in addresses.toml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…several entries

`[clips.x] start` pins source frames start..start + frames in the clip's entry; the cuts of one
source free its packer entry once. `layout.clips` is the entry -> clip map build and pose read.
The spline helpers move from travel to motion, which cuts with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p; tests for the tip and the cut

Host tests: the tip after the species brain, stopped by the drop, other
vtables and joints skipped; the cut holding a rule, a request, a play and a
tail_cut event until the drop; a one-mesh model leaving the cut as before; the
slot-29 stub leaving the request to the brain before the step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the tail by one offset

rig.from_donor no longer adopts the donor's second root onto joint 1: the chain stays
unparented at params[1], as on native rigs, and a root anywhere else raises. rig.tip_of
derives the tip map from the skeleton: each chain joint pairs with the body joint at its bind
plus one offset, the root with its first child's carrier. It reproduces ATTACH's carriers on
all 35 native rigs with a chain. fk.attach poses joints as their carriers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rries its map

mesh.build puts the parts riding only the tip chain in mesh record 1, which the host's dropped
tail draws: material 0 the tip, 1 its cut face (em75 hides it until the cut), then the tip's
other textures. The cut face is the tip part that welds to the body at the tip's offset and
faces along the tail: Zinogre group 180, Brute 87, and em75/em11/em1's native material-1
groups. Built carries the tip and <port>_moves.lua emits it as
`tip = { { joint, carrier }, ... }`. Posed as their carriers, the tip's seam vertices stay
within 0.04 u of the stump's through the idle (test_tip).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tail_t size check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mits it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…crash on

With --manifest (or --host SPECIES), verify checks the host's own model: when it has a tail tip
(a chain at params[1] and mesh record 1), the port needs its chain, the map rig.tip_of derives
and a mesh record 1 on that chain alone, since the host's dropped tail draws record 1. The donor
correspondence walks the tip's chain from its own root, and the vertex comparison follows the
build's group order (body, then tip).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cut's entry is its pin; `Layout.ids` names only a cut source's first cut. The own move's
entry, the studio's attack check, the clip names and the authored turns all ask Layout.entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ives it

A cut entry holds frames of its source, not the whole clip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…any reader

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the stump

Scene.world poses the tip's chain as its carriers (rig.tip_of, fk.attach), the bind pose too,
so a port or a native renders its whole tail; `studio render monster --severed` hides the
tip's groups (mesh.tip_parts) and leaves the chain unposed instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s tip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ports' PACs (rig and mesh record 1) and the studio's render goldens of the Tigrex and both
ports (the tip on the tail), the goldens rewritten in apps/studio/docker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They compare each entry with what `layout.played` gives it, so a cut entry is CARRIED; a
plain entry -> id map lost the cuts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…drop

The flinch is cut at frame 2: entry 65, which (4,4) plays and whose end fires TAIL_DROP, and
entry 83, which the drop enters as (4,15). The own `tail_cut` move and its rule go: leaving
(4,4) early removed the drop. `walk_forwards_faster` is pinned at 73, under 100, the only
entries a village NPC plays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… required

mesh.build takes `tip` keyword-only and required (None for a rig without a chain), and
mesh.split is the one place that says which parts go in mesh 0 and in mesh 1. fidelity and
verify compare in that order; the Blender writer passes the tip of the skeleton it exports, so
an exported MHP3rd model keeps its tail tip in mesh record 1. test_write pins that the Zinogre's
export, from its MHP3rd model or from its port, passes verify's tip check against the Tigrex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Zinogre's build digest covers both the tip and the flinch slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sp00ktober
sp00ktober merged commit 394f2a2 into main Oct 9, 2026
5 checks passed
@sp00ktober
sp00ktober deleted the tail/attach branch October 9, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant