Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,23 @@ private void Start()
/// </summary>
private void Update()
{
// If NPC/Monster is dead, stop any further process logic.
// If NPC/Monster is dead, only play out the already queued animations (e.g. the dying animation
// enqueued by FightService), then stop any further process logic.
if (Properties.BodyState == VmGothicEnums.BodyState.BsDead)
{
enabled = false;
Properties.CurrentAction.Tick();

if (Properties.CurrentAction.IsFinished())
{
if (Properties.AnimationQueue.Count > 0)
PlayNextAnimation(Properties.AnimationQueue.Dequeue());
else
enabled = false;
}

return;
}

ExecuteActivePerceptions();
ExecuteStates();

Expand Down Expand Up @@ -89,7 +100,6 @@ private void Update()
Vm.GlobalOther = Vm.GlobalHero;
}

DaedalusSymbol loopSymbol;
switch (Properties.CurrentLoopState)
{
// None means, the NPC is newly created and didn't execute any Routine as of now OR a State was changed via Daedalus scripts.
Expand Down Expand Up @@ -137,7 +147,9 @@ private void Update()
// Go on
else
{
Logger.Log($"Start playing >{Properties.AnimationQueue.Peek().GetType()}< on >{Go.transform.parent.name}<", LogCat.Ai);
// Editor-only: this fires for every dequeued action of every NPC - the string interpolation
// plus file sink would be measurable noise on device.
Logger.LogEditor($"Start playing >{Properties.AnimationQueue.Peek().GetType()}< on >{Go.transform.parent.name}<", LogCat.Ai);
PlayNextAnimation(Properties.AnimationQueue.Dequeue());
}
}
Expand Down Expand Up @@ -182,18 +194,25 @@ private void ExecuteActivePerceptions()
return;
}

_npcAiService.UpdateEnemyNpc(NpcInstance);
var hero = (NpcInstance)_gameStateService.GothicVm.GlobalHero;
var assessPlayerRange = _npcHelperService.GetPerceptionRange(VmGothicEnums.PerceptionType.AssessPlayer);

// FIXME - CanSense is not separating between smell, hear, and see as of now. Please add functionality.
if(_npcHelperService.CanSenseNpc(NpcInstance, (NpcInstance)_gameStateService.GothicVm.GlobalHero, false))
if(_npcHelperService.CanSenseNpc(NpcInstance, hero, false, assessPlayerRange))
{
_npcAiService.ExecutePerception(VmGothicEnums.PerceptionType.AssessPlayer, Properties, NpcInstance,null, (NpcInstance)_gameStateService.GothicVm.GlobalHero);
_npcAiService.ExecutePerception(VmGothicEnums.PerceptionType.AssessPlayer, Properties, NpcInstance, null, hero);
}

// FIXME - Throws a lot of errors and warnings when NPCs are nearby monsters (e.g. Bridge guard next to OC)
if(Properties.EnemyNpc != null)
// Scanning all NPCs for the closest enemy is expensive - only do it for NPCs that react to enemies at all.
if (Properties.Perceptions.TryGetValue(VmGothicEnums.PerceptionType.AssessEnemy, out var enemyPerception) &&
enemyPerception >= 0)
{
_npcAiService.ExecutePerception(VmGothicEnums.PerceptionType.AssessEnemy, Properties, NpcInstance,null, Properties.EnemyNpc);
_npcAiService.UpdateEnemyNpc(NpcInstance);

// FIXME - Throws a lot of errors and warnings when NPCs are nearby monsters (e.g. Bridge guard next to OC)
if(Properties.EnemyNpc != null)
{
_npcAiService.ExecutePerception(VmGothicEnums.PerceptionType.AssessEnemy, Properties, NpcInstance,null, Properties.EnemyNpc);
}
}


Expand Down Expand Up @@ -269,6 +288,10 @@ public void StartRoutine(int action)
var routineSymbol = Vm.GetSymbolByIndex(action)!;
Vob.CurrentStateName = routineSymbol.Name;

// Reset the previous routine's symbols: a new ZS without own _Loop/_End must not call the old ones.
Properties.StateLoop = 0;
Properties.StateEnd = 0;

var symbolLoop = Vm.GetSymbolByName($"{routineSymbol.Name}_Loop");
if (symbolLoop != null)
{
Expand Down
102 changes: 0 additions & 102 deletions Assets/Gothic-Core/Scripts/Adapters/Npc/RoutineHandler.cs

This file was deleted.

11 changes: 0 additions & 11 deletions Assets/Gothic-Core/Scripts/Adapters/Npc/RoutineHandler.cs.meta

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Gothic.Core.Models.Vm;
using Gothic.Core.Services;
using Gothic.Core.Services.Npc;
using Reflex.Attributes;
using UnityEngine;

namespace Gothic.Core.Domain.Npc.Actions.AnimationActions
Expand Down Expand Up @@ -53,9 +52,9 @@ public override void Start()
return;
}

// https://discussions.unity.com/t/determining-whether-to-rotate-left-or-right/44021
var cross = Vector3.Cross(NpcGo.transform.forward, _finalRotation.eulerAngles);
_isRotateLeft = cross.y >= 0;
// Negative signed angle around the up axis means the target direction is to our left.
var targetForward = _finalRotation * Vector3.forward;
_isRotateLeft = Vector3.SignedAngle(NpcGo.transform.forward, targetForward, Vector3.up) < 0;

if (PlayAnimation)
{
Expand Down Expand Up @@ -92,7 +91,8 @@ private void HandleRotation(Transform npcTransform)
// Check if rotation is done.
if (Quaternion.Angle(npcTransform.rotation, _finalRotation) < 1f)
{
PrefabProps.AnimationSystem.StopAnimation(_rotationAnimationName);
if (_rotationAnimationName != null)
PrefabProps.AnimationSystem.StopAnimation(_rotationAnimationName);

IsFinishedFlag = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ public override void Tick()
private string GetWalkModeAnimationString()
{
var fightMode = (VmGothicEnums.WeaponState)Vob.FightMode;
var weaponState = fightMode == VmGothicEnums.WeaponState.NoWeapon
? ""
: fightMode.ToString();
var weaponState = Services.Npc.AnimationService.GetWeaponAnimationPrefix(fightMode);
var walkMode = (VmGothicEnums.WalkMode)Vob.AiHuman.WalkMode;
switch (walkMode)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Gothic.Core.Models.Container;
using Gothic.Core.Models.Vm;
using Gothic.Core.Services.Npc;
using Reflex.Attributes;
using UnityEngine;

namespace Gothic.Core.Domain.Npc.Actions.AnimationActions
Expand All @@ -12,6 +11,11 @@ public abstract class AbstractWalkAnimationAction2 : AbstractAnimationAction
protected Transform NpcTransform => NpcGo.transform;
protected bool IsDestReached;

// Name of the animation StartWalk() actually played. StopWalk() must stop exactly this one:
// recalculating the name would stop the wrong animation when walk/fight mode changed mid-walk
// (e.g. via an immediately executed AI_SetWalkmode), leaving the walk loop sliding the NPC forever.
private string _startedWalkAnimationName;

protected AbstractWalkAnimationAction2(AnimationAction action, NpcContainer npcContainer) : base(action, npcContainer)
{
}
Expand All @@ -34,6 +38,16 @@ public override void Start()
if (IsDestinationReached())
{
OnDestinationReached();

// Already at the final destination (e.g. a FP_ROAM FreePoint right next to the NPC):
// never start the walk loop - nobody would stop it again and its root motion
// would slide the NPC around (visible e.g. on roaming Molerats).
// IsDestReached covers subclasses which continue at the spot without finishing
// (e.g. UseMob playing its transition animation) - the walk loop would blend
// that animation out again. Only a multi-stop route (GoToWp) resets the flag
// and walks on.
if (IsFinishedFlag || IsDestReached)
return;
}

StartWalk();
Expand Down Expand Up @@ -64,17 +78,19 @@ protected virtual void StartWalk()
? VmGothicEnums.BodyState.BsWalk
: VmGothicEnums.BodyState.BsRun;

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

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

var animName = AnimationService.GetAnimationName(VmGothicEnums.AnimationType.Move, NpcContainer);
PrefabProps.AnimationSystem.StopAnimation(animName);
if (_startedWalkAnimationName != null)
{
PrefabProps.AnimationSystem.StopAnimation(_startedWalkAnimationName);
}
}

private bool IsDestinationReached()
Expand Down
Loading