Skip to content

feat: configurable relative head/body angle#320

Merged
tr7zw merged 5 commits into
tr7zw:mainfrom
elenawinters:main
Mar 16, 2026
Merged

feat: configurable relative head/body angle#320
tr7zw merged 5 commits into
tr7zw:mainfrom
elenawinters:main

Conversation

@elenawinters

Copy link
Copy Markdown
Contributor

This PR adds 2 sliders that allow you to configure the relative head/body rotation angle for normal movement and blocking.

@coderabbitai

coderabbitai Bot commented Mar 15, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 83670d52-77c6-4b92-a7a6-9cd9c9b4b5a0

📥 Commits

Reviewing files that changed from the base of the PR and between 96cdd09 and e46961e.

📒 Files selected for processing (1)
  • src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java

Walkthrough

Introduces configurable head rotation angle limits for players with separate values for blocking and normal states. Adds two configuration fields, corresponding UI options, an injected mixin override to apply these limits dynamically, and translation keys.

Changes

Cohort / File(s) Summary
Configuration Fields
NEAVersionless/src/main/java/dev/tr7zw/notenoughanimations/versionless/config/Config.java
Added two new public float fields: maxBlockingAngle (default 15.0f) and maxNormalAngle (default 50.0f).
Configuration UI
src/main/java/dev/tr7zw/notenoughanimations/config/ConfigScreenProvider.java
Added two new configuration options for head rotation angle limits (>= 1.20.4): maxNormalAngle (range 0.0–50.0) and maxBlockingAngle (range 0.0–15.0), each with 0.1 step and corresponding getters/setters.
Head Rotation Override
src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java
Injected mixin method overrideMaxHeadRoationRelativeToBody that clamps config values and returns maxBlockingAngle when player is blocking, otherwise maxNormalAngle.
Localization
src/main/resources/assets/notenoughanimations/lang/en_us.json
Added translation keys for rotation angle limit settings and their tooltips: text.nea.line.rotationAngle, text.nea.maxNormalAngle, text.nea.maxBlockingAngle.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding configurable relative head/body angle with new configuration options and UI sliders.
Description check ✅ Passed The description is directly related to the changeset, explaining that two sliders were added to configure relative head/body rotation angle for normal movement and blocking.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`:
- Around line 55-57: The method overrideMaxHeadRoationRelativeToBody should
validate and sanitize the config angles before returning them: fetch
NEABaseMod.config.maxBlockingAngle / maxNormalAngle inside
overrideMaxHeadRoationRelativeToBody, test for Float.isFinite and non-negative
(and optionally clamp to a sensible upper bound like 180f), and if invalid
replace with a safe fallback (e.g., 0f or a known constant default). Update the
return path in overrideMaxHeadRoationRelativeToBody to use the sanitized/clamped
value instead of returning the raw config field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59204f22-9698-4d4f-8583-4cf5fd9da65e

📥 Commits

Reviewing files that changed from the base of the PR and between 55d0094 and 295815b.

📒 Files selected for processing (4)
  • NEAVersionless/src/main/java/dev/tr7zw/notenoughanimations/versionless/config/Config.java
  • src/main/java/dev/tr7zw/notenoughanimations/config/ConfigScreenProvider.java
  • src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java
  • src/main/resources/assets/notenoughanimations/lang/en_us.json

Comment thread src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java Outdated
also add a lil guard thing

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java (1)

59-73: Extract the repeated angle-clamping logic.

The blocking and normal branches do the same range-normalization work. A small helper would remove the duplication and clean up the current local naming noise in one pass.

♻️ Possible cleanup
+    private static float clampAngle(float value, float max) {
+        if (value < 0.0f) {
+            return 0.0f;
+        }
+        if (value > max) {
+            return max;
+        }
+        return value;
+    }
+
     //? if >= 1.20.4 {
     `@WrapMethod`(method = "getMaxHeadRotationRelativeToBody")
     protected float overrideMaxHeadRoationRelativeToBody(Operation<Float> original) {
         Player player = (Player) (Object) this;
-
-        float blocking_angle = NEABaseMod.config.maxBlockingAngle;
-        if (blocking_angle < 0) {
-            blocking_angle = 0;
-        } else if (blocking_angle > 15) {
-            blocking_angle = 15;
-        }
-
-        float normal_angle = NEABaseMod.config.maxNormalAngle;
-        if (normal_angle < 0) {
-            normal_angle = 0;
-        } else if (normal_angle > 50) {
-            normal_angle = 50;
-        }
-
-        return player.isBlocking() ? blocking_angle : normal_angle;
+        float blockingAngle = clampAngle(NEABaseMod.config.maxBlockingAngle, 15.0f);
+        float normalAngle = clampAngle(NEABaseMod.config.maxNormalAngle, 50.0f);
+        return player.isBlocking() ? blockingAngle : normalAngle;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`
around lines 59 - 73, Extract the repeated clamp logic into a small helper
(e.g., clampAngle or normalizeAngle) and use it for both values: replace the
blocking_angle and normal_angle blocks by calling
clampAngle(NEABaseMod.config.maxBlockingAngle) and
clampAngle(NEABaseMod.config.maxNormalAngle), then return player.isBlocking() ?
clampedBlocking : clampedNormal; ensure the helper enforces the same min/max
bounds currently used (0..15 for blocking if you keep separate bounds or make
the helper accept min/max so you can reuse it for 0..15 and 0..50) and place it
in the PlayerEntityMixin class so it can be referenced directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`:
- Around line 59-73: Extract the repeated clamp logic into a small helper (e.g.,
clampAngle or normalizeAngle) and use it for both values: replace the
blocking_angle and normal_angle blocks by calling
clampAngle(NEABaseMod.config.maxBlockingAngle) and
clampAngle(NEABaseMod.config.maxNormalAngle), then return player.isBlocking() ?
clampedBlocking : clampedNormal; ensure the helper enforces the same min/max
bounds currently used (0..15 for blocking if you keep separate bounds or make
the helper accept min/max so you can reuse it for 0..15 and 0..50) and place it
in the PlayerEntityMixin class so it can be referenced directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09bd626d-d03c-4698-8270-3744fb0e112f

📥 Commits

Reviewing files that changed from the base of the PR and between 295815b and 05fdd19.

📒 Files selected for processing (2)
  • gradle-compose.yml
  • src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java

this does not fix the build issue. waiting till tomorrow to figure that out

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java (1)

56-67: Consider adding a null guard for config access.

Based on the codebase context, NEABaseMod.config is declared without initialization and could theoretically be null if this mixin runs before config loading completes. While unlikely in practice, a defensive null check would prevent potential NPE.

🛡️ Suggested defensive check
     `@WrapMethod`(method = "getMaxHeadRotationRelativeToBody")
     protected float overrideMaxHeadRotationRelativeToBody(Operation<Float> original) {
+        if (NEABaseMod.config == null) {
+            return original.call();
+        }
         Player player = (Player) (Object) this;
         // ... validation and return

This also addresses the unused original parameter by providing a fallback path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`
around lines 56 - 67, The mixin method
PlayerEntityMixin.overrideMaxHeadRoationRelativeToBody should defensively handle
NEABaseMod.config possibly being null and use the provided original
Operation<Float> as a fallback; add a null-check for NEABaseMod.config at the
start of the method and if it's null return original.call() (or
original.apply(...) consistent with Operation API) so you don't NPE, and then
proceed to read/clamp NEABaseMod.config.maxBlockingAngle and maxNormalAngle as
before; also ensure the unused parameter original is used in the fallback path
to preserve expected behavior when config isn't available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`:
- Around line 59-65: The validation conditions for
NEABaseMod.config.maxBlockingAngle and maxNormalAngle are wrong: replace the &&
with || so out-of-range values (less than 0.0f OR greater than the intended
upper bound) are clamped; decide and set the proper upper bounds (e.g.
15.0f/50.0f or 180.0f) and use those in the clamps for maxBlockingAngle and
maxNormalAngle. Also correct the method name typo
overrideMaxHeadRoationRelativeToBody → overrideMaxHeadRotationRelativeToBody and
address the unused parameter original in that method (either use it to preserve
vanilla behavior when appropriate or remove/annotate it if intentional),
ensuring the method behavior is explicit.

---

Nitpick comments:
In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`:
- Around line 56-67: The mixin method
PlayerEntityMixin.overrideMaxHeadRoationRelativeToBody should defensively handle
NEABaseMod.config possibly being null and use the provided original
Operation<Float> as a fallback; add a null-check for NEABaseMod.config at the
start of the method and if it's null return original.call() (or
original.apply(...) consistent with Operation API) so you don't NPE, and then
proceed to read/clamp NEABaseMod.config.maxBlockingAngle and maxNormalAngle as
before; also ensure the unused parameter original is used in the fallback path
to preserve expected behavior when config isn't available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8dda45fb-0166-411d-9597-49f07f221f0e

📥 Commits

Reviewing files that changed from the base of the PR and between 05fdd19 and 47e0bfc.

📒 Files selected for processing (1)
  • src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java

Comment on lines +59 to +65
if (NEABaseMod.config.maxBlockingAngle < 0.0f && NEABaseMod.config.maxBlockingAngle > 15.0f) {
NEABaseMod.config.maxBlockingAngle = 15.0f;
}

if (NEABaseMod.config.maxNormalAngle < 0.0f && NEABaseMod.config.maxNormalAngle > 50.0f) {
NEABaseMod.config.maxNormalAngle = 50.0f;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Logic error: validation conditions will never trigger.

The conditions use && instead of ||, making them always evaluate to false. No value can be both less than 0 AND greater than 15/50 simultaneously, so these guards never apply.

Additionally:

  • Method name typo: overrideMaxHeadRoationRelativeToBodyoverrideMaxHeadRotationRelativeToBody
  • The original parameter is unused, meaning the vanilla behavior is completely bypassed (verify this is intentional)
🐛 Proposed fix
     `@WrapMethod`(method = "getMaxHeadRotationRelativeToBody")
-    protected float overrideMaxHeadRoationRelativeToBody(Operation<Float> original) {
+    protected float overrideMaxHeadRotationRelativeToBody(Operation<Float> original) {
         Player player = (Player) (Object) this;

-        if (NEABaseMod.config.maxBlockingAngle < 0.0f && NEABaseMod.config.maxBlockingAngle > 15.0f) {
+        if (NEABaseMod.config.maxBlockingAngle < 0.0f || NEABaseMod.config.maxBlockingAngle > 180.0f) {
             NEABaseMod.config.maxBlockingAngle = 15.0f;
         }

-        if (NEABaseMod.config.maxNormalAngle < 0.0f && NEABaseMod.config.maxNormalAngle > 50.0f) {
+        if (NEABaseMod.config.maxNormalAngle < 0.0f || NEABaseMod.config.maxNormalAngle > 180.0f) {
             NEABaseMod.config.maxNormalAngle = 50.0f;
         }

         return player.isBlocking() ? NEABaseMod.config.maxBlockingAngle : NEABaseMod.config.maxNormalAngle;
     }

Note: I adjusted the upper bound to 180.0f assuming that's a reasonable max rotation angle. If the intent was to clamp to the default values (15/50), use those as upper bounds instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/dev/tr7zw/notenoughanimations/mixins/PlayerEntityMixin.java`
around lines 59 - 65, The validation conditions for
NEABaseMod.config.maxBlockingAngle and maxNormalAngle are wrong: replace the &&
with || so out-of-range values (less than 0.0f OR greater than the intended
upper bound) are clamped; decide and set the proper upper bounds (e.g.
15.0f/50.0f or 180.0f) and use those in the clamps for maxBlockingAngle and
maxNormalAngle. Also correct the method name typo
overrideMaxHeadRoationRelativeToBody → overrideMaxHeadRotationRelativeToBody and
address the unused parameter original in that method (either use it to preserve
vanilla behavior when appropriate or remove/annotate it if intentional),
ensuring the method behavior is explicit.

@sonarqubecloud

Copy link
Copy Markdown

@tr7zw tr7zw merged commit 001e1e2 into tr7zw:main Mar 16, 2026
3 of 4 checks passed
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.

2 participants