Skip to content

fancynpcs: Fix horse pose attribute forcing the rearing animation - #330

Open
ThoriaDevelopment wants to merge 1 commit into
FancyInnovations:mainfrom
ThoriaDevelopment:fix/fancynpcs-horse-pose-rearing
Open

ThoriaDevelopment wants to merge 1 commit into
FancyInnovations:mainfrom
ThoriaDevelopment:fix/fancynpcs-horse-pose-rearing

Conversation

@ThoriaDevelopment

Copy link
Copy Markdown

📋 Description

Fixes #312.

A horse, donkey, mule, skeleton horse or zombie horse NPC shows the rearing pose whichever pose value is set. standing and eating both end up rearing, which matches the report that the pose attribute appears to do nothing.

The cause is in HorseAttributes#setPose. The standing and eating branches call horse.setStanding(0), and on AbstractHorse that method always sets the FLAG_STANDING (rearing) flag:

public void setStanding(final int ticks) {
    this.setEating(false);
    this.setFlag(FLAG_STANDING, true);
    this.standCounter = ticks;
}

public void clearStanding() {
    this.setFlag(FLAG_STANDING, false);
    this.standCounter = 0;
}

setStanding(0) stores a counter of zero, and AbstractHorse#tick only clears the flag while that counter runs down:

if (this.standCounter > 0 && --this.standCounter <= 0) {
    this.clearStanding();
}

With a counter of zero the flag is never cleared, so the NPC keeps the rearing pose. NPC entities are packet only and are never ticked, so nothing else clears it either. clearStanding() is the method that resets the flag.

✅ Checklist

  • My code follows the project's coding style and guidelines
  • I have tested my changes locally and they work as expected
  • I have added necessary documentation (if applicable)
  • I have linked related issues using Fixes #issue_number
  • I have rebased/merged with the latest main branch

🔍 Changes

In HorseAttributes#setPose, the standing and eating branches now call horse.clearStanding() instead of horse.setStanding(0). The rearing branch (setStanding(20)) is unchanged.

Applied to every implementation that had the bug:

  • plugins/fancynpcs-v2/implementation_26_3
  • plugins/fancynpcs-v2/implementation_26_2
  • plugins/fancynpcs-v2/implementation_26_1_2
  • plugins/fancynpcs-v2/implementation_1_21_11
  • plugins/fancynpcs-v2/implementation_1_21_9
  • plugins/fancynpcs-v2/implementation_1_21_6

implementation_1_21_5 is deliberately untouched. It already uses setForceStanding(boolean) for the same three states, and 1.21.5 does not have clearStanding(): the 1.21.5 mappings list setStanding(boolean), while setStanding(int) and clearStanding() first appear in 1.21.7.

🧪 How to Test

  1. Create a horse NPC and set its pose: /npc create horse, then /npc attribute <npc> set pose standing.
  2. The NPC stands normally instead of rearing.
  3. Repeat with pose eating (it eats, no rearing) and pose rearing (it rears, as before).

What I tested

  • Compilation on JDK 25 with the repo's Gradle wrapper: :plugins:fancynpcs-v2:implementation_1_21_11:compileJava, :plugins:fancynpcs-v2:implementation_26_1_2:compileJava, :plugins/fancynpcs-v2:implementation_26_2:compileJava and :plugins/fancynpcs-v2:implementation_26_3:compileJava all succeed with this change, which confirms clearStanding() resolves in those dev bundles.
  • implementation_1_21_6 and implementation_1_21_9 build against the 1.21.7 and 1.21.9 dev bundles. clearStanding() is present in the official Mojang mappings for both versions, but I could not compile those two modules locally. The reason is in the notes below.
  • Runtime check on Paper 26.2 build 123. I spawned real horses, applied the old and the new call sequences, waited 3 seconds and then read the flags back, so nothing could have self cleared in between:
Calls applied isStanding() isEating()
none (fresh horse) false false
setEating(false); setStanding(0) (old standing) true false
setStanding(0); setEating(true) (old eating) true true
setEating(false); clearStanding() (new standing) false false
clearStanding(); setEating(true) (new eating) false true
setStanding(20); setEating(false) (unchanged rearing) true false

What I did not test

  • I did not render the NPC in a Minecraft client, so I have not seen the pose change on screen. The flags above are the server side state that drives the animation.
  • I did not run /fancynpcs run_tests. The harness needs a player, and I worked on a headless test server. Please run it before merging.

Notes

Two things I noticed while working on this, neither of them part of the change.

  1. Removing the pose attribute from an already spawned NPC does not reset the flags it set, so an NPC left in rearing keeps rearing until it is recreated. Fixing that properly needs an attribute reset path, which is a larger change than this bug report.
  2. Building :plugins:fancynpcs-v2:shadowJar from scratch fails in this repo, on an unmodified checkout as well. On a machine with a cold Paperweight cache, paperweightUserdevSetup fails for the 1.21.5, 1.21.6 and 1.21.9 modules with Unsupported class file major version 69 from io.papermc.codebook. Paperweight 2.0.0-beta.23 resolves codebook-cli 1.0.14, whose bundled ASM 9.6 cannot read Java 25 class files, and every module here sets a Java 25 toolchain. CI stays green because ~/.gradle is cached: the Paperweight work directories are restored and those setup tasks report up to date. Happy to open a separate issue for it if that is useful.

@ThoriaDevelopment
ThoriaDevelopment force-pushed the fix/fancynpcs-horse-pose-rearing branch from 71076ae to 58a1de1 Compare September 11, 2026 22:33
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.

Bug: Horse, Mule and Donkey pose attribute not working

1 participant