Skip to content

Conversation

@Snowsita
Copy link

Implemented the nape hook removal feature. On Hard and Abnormal difficulties, titans will now react to hooks attached to their neck.

The reused animation is "Amarture_VER2|grab.head.back.l", which is assigned to a new UnhookNape animation property.

How It Works

  • Hook.cs:

    • When FixedUpdateHooking confirms a successful hit (foundHit), it now notifies the titan that it has been hooked and sends a reference to the specific collider that was hit.
    • Helper functions were added to Hook.cs to allow the titan to check if the hook is still attached and to force it to detach.
  • BaseTitan.cs:

    • A new method, OnHooked(), receives the notification from the hook.
    • It first checks if the game difficulty is Hard or greater.
    • It then checks if the collider that was hit is the neck. If both conditions are met, it starts the NapeHookRemovalCoroutine.
  • The Coroutine:

    • The UnhookNape animation is played immediately.
    • The code waits for 55% of the animation's total duration, which is right when the titan's hand reaches its neck.
    • At that moment, the hook is detached.
    • If the animation fails to play for any reason, a fallback timer will detach the hook after a short delay.

Notes

  • Collider Choice: I initially tried to use the NapeHurtbox, but debugging showed that the hook's raycast always detects the larger neck collider first. Tagging the neck instead makes the feature work reliably without affecting the existing damage logic.
  • Gameplay Choice: I considered slowing the player down after the hook detaches to be more realistic, but decided against it as it could negatively affect playability.

}
}

protected virtual IEnumerator NapeHookRemovalCoroutine(Hook hook)
Copy link
Collaborator

Choose a reason for hiding this comment

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

We generally don't do timers/state transitions outside the main loop or physics loop as it 1. disconnects the logic from the main state machine and 2. the timer for coroutines isn't consistent.

public virtual void OnHooked(Hook hook, Collider part)
{
bool isHardDifficulty = (int)SettingsManager.InGameCurrent.General.Difficulty.Value >= (int)GameDifficulty.Hard;
bool isNapeHit = part.gameObject.CompareTag("TitanNape");
Copy link
Collaborator

Choose a reason for hiding this comment

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

We only really want to unhook players who directly hook the nape.
image

@Snowsita
Copy link
Author

Used angles to find the nape hitbox now, exactly the back side.
Removed coroutines and added the logic onto the main loop, at the very beginning so the checks work with a bit priority, can move it if needed, right now the timing is immediate but we can change it depending on the difficulty, also added a method to get the world position of the hook when needed

@Snowsita Snowsita requested a review from AutumnThyme October 21, 2025 21:46
Copy link
Collaborator

@AutumnThyme AutumnThyme left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, there's other issues in the code.
I'm not sure if your solution will work in multiplayer.
It only handles one hook at a time and it makes assumptions about the owner of objects (local player owns hooks, masterclient owns all titans and controls the ai)

if a local player hooks a titan and notifies its instance of the titan that its hooked, the host has no idea and this state will just be ignored or corrupt some code later on due to mismatched state.
I'd recommend breaking this down into 4 things.

Hook signals to owner of titan its state on Hooked and if it was hooked, it must signal an unhook to the owner of the titan as well.

Titan owner sees this information and does its state management and disconnects hooks.

Titan signals back to hook and says it was removed.

Hook must determine if locally the hook that was removed was the hook on the titan or if the hook had already been released and/or attached to something new.


float angle = Vector3.Angle(Cache.Transform.forward, hitDirection);

bool hitTheBack = angle > 120f;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Move this value into the titan config json

}
else
{
_napeHookAnimDuration = 1.21f;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Move timing/size/attr constants into the titan config json for ease of modification by playtesters

{
_napeHookToRemove.Detach();
}
_napeHookToRemove = null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens in multiplayer when multiple people hook the neck?


var titan = obj.transform.root.GetComponent<BaseTitan>();
if (titan != null)
titan.OnHooked(this, finalHit.collider);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this work off host? From a glance, This code only runs if you're the owner of the hook and all titans actions are controlled by the host.

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.

3 participants