Skip to content
7 changes: 7 additions & 0 deletions Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Gothic.Core.Extensions;
using Gothic.Core.Logging;
using Gothic.Core.Models.Vm;
using FreePoint = Gothic.Core.Models.Vob.WayNet.FreePoint;
using WayPoint = Gothic.Core.Models.Vob.WayNet.WayPoint;
using Gothic.Core.Services;
using Gothic.Core.Services.Config;
using Gothic.Core.Services.Npc;
Expand Down Expand Up @@ -316,6 +318,7 @@ public void ClearState(bool callEndFunction)
Properties.AnimationQueue.Clear();
Properties.CurrentAction = new None(new AnimationAction(), NpcData);
Properties.CurrentLoopState = NpcProperties.LoopState.None; // i.e. call StartNextState() next frame
Properties.BodyState = VmGothicEnums.BodyState.BsStand;

PrefabProps.AnimationSystem.StopAllAnimations();
}
Expand All @@ -339,7 +342,11 @@ public void ReEnableNpc()
{
var wp = _wayNetService.GetWayNetPoint(currentRoutine.Waypoint);
if (wp != null)
{
gameObject.transform.position = _npcService.GetFreeAreaAtSpawnPoint(wp.Position);
Properties.CurrentFreePoint = wp as FreePoint;
Properties.CurrentWayPoint = wp as WayPoint;
}
else
Logger.LogWarning($"ReEnableNpc: waypoint '{currentRoutine.Waypoint}' not found for {gameObject.name} — NPC will re-enable at current position.", LogCat.Npc);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ protected virtual void StartWalk()
{
PhysicsService.EnablePhysicsForNpc(PrefabProps);

var walkMode = (VmGothicEnums.WalkMode)Vob.AiHuman.WalkMode;
Props.BodyState = walkMode == VmGothicEnums.WalkMode.Walk
? VmGothicEnums.BodyState.BsWalk
: VmGothicEnums.BodyState.BsRun;

var animName = AnimationService.GetAnimationName(VmGothicEnums.AnimationType.Move, NpcContainer);
PrefabProps.AnimationSystem.PlayAnimation(animName);
}

protected virtual void StopWalk()
{
PhysicsService.EnablePhysicsForNpc(PrefabProps);
Props.BodyState = VmGothicEnums.BodyState.BsStand;

var animName = AnimationService.GetAnimationName(VmGothicEnums.AnimationType.Move, NpcContainer);
PrefabProps.AnimationSystem.StopAnimation(animName);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Gothic.Core.Adapters.Npc;
using Gothic.Core.Logging;
using Gothic.Core.Models.Container;

namespace Gothic.Core.Domain.Npc.Actions.AnimationActions
Expand All @@ -10,7 +12,14 @@ public ContinueRoutine(AnimationAction action, NpcContainer npcContainer) : base

public override void Start()
{
var ai = PrefabProps.AiHandler;
var ai = PrefabProps.AiHandler ?? NpcGo.GetComponent<AiHandler>();

if (ai == null)
{
Logger.LogWarning($"[ContinueRoutine] AiHandler null on {NpcGo.name} — skipping routine restart", LogCat.Ai);
IsFinishedFlag = true;
return;
}

ai.ClearState(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public override void Start()
_mobContainer = container;
_mobsiScheme = _mobContainer?.Props.GetVisualScheme();

if (container!.Go == null)
if (container == null || container.Go == null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same fix in #372, we'll have to solve the conflict regardless of which PR gets merged first

{
IsFinishedFlag = true;
return;
Expand Down
22 changes: 18 additions & 4 deletions Assets/Gothic-Core/Scripts/Services/Npc/NpcAiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,21 @@ public void ExtAiStartState(NpcInstance npc, int action, bool stopCurrentState,
{
var other = (NpcInstance)_gameStateService.GothicVm.GlobalOther;
var victim = (NpcInstance)_gameStateService.GothicVm.GlobalOther;

npc.GetUserData().Props.AnimationQueue.Enqueue(new StartState(

var container = npc.GetUserData();

if (stopCurrentState)
{
// Abandon current state immediately so the new one starts next frame, not after the whole queue drains.
container.PrefabProps?.AiHandler?.ClearState(false);
container.Props.StateEnd = 0;
container.Props.CurrentWayPoint = null; // forces GoToWp to use nearest WP, not stale pre-interrupt WP

}

container.Props.AnimationQueue.Enqueue(new StartState(
new AnimationAction(int0: action, bool0: stopCurrentState, string0: wayPointName, instance0: other, instance1: victim),
npc.GetUserData()));
container));
}

public void ExtAiLookAt(NpcInstance npc, string wayPointName)
Expand Down Expand Up @@ -259,7 +270,10 @@ public void ExtAiStandUp(NpcInstance npc)
// FIXME - Implement remaining tasks from G1 documentation:
// * Ist der Nsc in einem Animatinsstate, wird die passende Rücktransition abgespielt.
// * Benutzt der NSC gerade ein MOBSI, poppt er ins stehen.
npc.GetUserData().Props.AnimationQueue.Enqueue(new StandUp(new AnimationAction(), npc.GetUserData()));
var container = npc.GetUserData();
// Reset immediately (not via queue) so Daedalus C_BodyStateContains checks in the same ZS_*_Loop tick see BsStand.
container.Props.BodyState = VmGothicEnums.BodyState.BsStand;
container.Props.AnimationQueue.Enqueue(new StandUp(new AnimationAction(), container));
}

public void ExtAiTurnToNpc(NpcInstance npc, NpcInstance other)
Expand Down
8 changes: 2 additions & 6 deletions Assets/Gothic-Core/Scripts/Services/Npc/NpcHelperService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,13 @@ public bool ExtWldDetectNpcEx(NpcInstance npcInstance, int specificNpcIndex, int
public int ExtNpcGetDistToWp(NpcInstance npc, string waypointName)
{
var npcGo = GetNpc(npc);
var npcPos = npcGo.transform.position;

var waypoint = _wayNetService.GetWayNetPoint(waypointName);

if (waypoint == null || !npcGo)
{
if (!npcGo || waypoint == null)
return int.MaxValue;
}

// *100 as Gothic metrics are in cm, not m.
return (int)(Vector3.Distance(npcPos, waypoint.Position) * 100);
return (int)(Vector3.Distance(npcGo.transform.position, waypoint.Position) * 100);
}

public int ExtNpcGetTalentSkill(NpcInstance npc, int skillId)
Expand Down