diff --git a/Assets/Gothic-Core/Resources/FontAsset/LiberationSans SDF empty.asset b/Assets/Gothic-Core/Resources/FontAsset/LiberationSans SDF empty.asset index 858a10bdb..c111263b5 100644 --- a/Assets/Gothic-Core/Resources/FontAsset/LiberationSans SDF empty.asset +++ b/Assets/Gothic-Core/Resources/FontAsset/LiberationSans SDF empty.asset @@ -37,6 +37,25 @@ MonoBehaviour: m_TabWidth: 29 m_Material: {fileID: 5152873236376385906} m_SourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 + m_CreationSettings: + sourceFontFileName: + sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 + faceIndex: 0 + pointSizeSamplingMode: 0 + pointSize: 104 + padding: 8 + paddingMode: 1 + packingMode: 0 + atlasWidth: 512 + atlasHeight: 256 + characterSetSelectionMode: 5 + characterSequence: + referencedFontAssetGUID: + referencedTextAssetGUID: + fontStyle: 0 + fontStyleModifier: 0 + renderMode: 4165 + includeFontFeatures: 0 m_SourceFontFile: {fileID: 0} m_SourceFontFilePath: m_AtlasPopulationMode: 0 @@ -67,25 +86,6 @@ MonoBehaviour: m_MarkToMarkAdjustmentRecords: [] m_ShouldReimportFontFeatures: 0 m_FallbackFontAssetTable: [] - m_CreationSettings: - sourceFontFileName: - sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75 - faceIndex: 0 - pointSizeSamplingMode: 0 - pointSize: 104 - padding: 8 - paddingMode: 1 - packingMode: 0 - atlasWidth: 512 - atlasHeight: 256 - characterSetSelectionMode: 5 - characterSequence: - referencedFontAssetGUID: - referencedTextAssetGUID: - fontStyle: 0 - fontStyleModifier: 0 - renderMode: 4165 - includeFontFeatures: 0 m_FontWeightTable: - regularTypeface: {fileID: 0} italicTypeface: {fileID: 0} @@ -151,17 +151,15 @@ Texture2D: m_ImageContentsHash: serializedVersion: 2 Hash: 00000000000000000000000000000000 - m_ForcedFallbackFormat: 4 - m_DownscaleFallback: 0 m_IsAlphaChannelOptional: 0 - serializedVersion: 2 + serializedVersion: 4 m_Width: 512 m_Height: 256 m_CompleteImageSize: 131072 m_MipsStripped: 0 m_TextureFormat: 1 m_MipCount: 1 - m_IsReadable: 0 + m_IsReadable: 1 m_IsPreProcessed: 0 m_IgnoreMipmapLimit: 0 m_MipmapLimitGroupName: @@ -297,3 +295,4 @@ Material: - _SpecularColor: {r: 1, g: 1, b: 1, a: 1} - _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5} m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/Gothic-Core/Resources/Prefabs/UI/StatusBars/StatusBar.prefab b/Assets/Gothic-Core/Resources/Prefabs/UI/StatusBars/StatusBar.prefab index b7cdcf879..8a7cc1894 100644 --- a/Assets/Gothic-Core/Resources/Prefabs/UI/StatusBars/StatusBar.prefab +++ b/Assets/Gothic-Core/Resources/Prefabs/UI/StatusBars/StatusBar.prefab @@ -225,7 +225,7 @@ Canvas: m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 1 - m_AdditionalShaderChannelsFlag: 0 + m_AdditionalShaderChannelsFlag: 25 m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 diff --git a/Assets/Gothic-Core/Scripts/Adapters/Animations/AnimationSystem.cs b/Assets/Gothic-Core/Scripts/Adapters/Animations/AnimationSystem.cs index 357f76a9e..3ca3351ec 100644 --- a/Assets/Gothic-Core/Scripts/Adapters/Animations/AnimationSystem.cs +++ b/Assets/Gothic-Core/Scripts/Adapters/Animations/AnimationSystem.cs @@ -565,13 +565,13 @@ private void ApplyEventTags(AnimationTrackInstance trackInstance) AttackAnimation = trackInstance.AnimationName; break; case EventType.OptimalFrame: - AttackOptFrame = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList(); + AttackOptFrame = eventTag.Slots.Item1.Split(' ').Where(s => !string.IsNullOrEmpty(s)).Select(i => Convert.ToInt32(i)).ToList(); break; case EventType.HitEnd: - AttackHitEnd = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList(); + AttackHitEnd = eventTag.Slots.Item1.Split(' ').Where(s => !string.IsNullOrEmpty(s)).Select(i => Convert.ToInt32(i)).ToList(); break; case EventType.ComboWindow: - AttackWindowFrames = eventTag.Slots.Item1.Split(' ').Select(i => Convert.ToInt32(i)).ToList(); + AttackWindowFrames = eventTag.Slots.Item1.Split(' ').Where(s => !string.IsNullOrEmpty(s)).Select(i => Convert.ToInt32(i)).ToList(); break; // Unused. @see: https://gothic-modding-community.github.io/gmc/zengin/anims/events/#def_dir case EventType.HitDirection: diff --git a/Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs b/Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs index e35458390..9a683c22e 100644 --- a/Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs +++ b/Assets/Gothic-Core/Scripts/Adapters/Npc/AiHandler.cs @@ -79,9 +79,12 @@ private void Update() if (Properties.AnimationQueue.Count == 0) { // We always need to set "self" before executing any Daedalus function. + // "other" defaults to hero here so routine states (ZS_*_Loop) have a sensible fallback. + // Perception calls (ExecutePerception) override GlobalOther themselves with their own save/restore. if (NpcInstance != null) { Vm.GlobalSelf = NpcInstance; + Vm.GlobalOther = Vm.GlobalHero; } DaedalusSymbol loopSymbol; @@ -254,14 +257,6 @@ public void StartRoutine(int action, string wayPointName) public void StartRoutine(int action) { - // End original loop first - // TODO - Calling ClearState(false) was buggy when e.g. Diego dialog "END" was clicked. Then the dialog lines were skipped. - // if (Properties.CurrentLoopState == NpcProperties.LoopState.Loop) - // { - // // We reuse this function as it is doing what we need. - // ClearState(false); - // } - var didRoutineChange = Vob.CurrentStateIndex != action; Vob.LastAiState = Vob.CurrentStateIndex; @@ -295,6 +290,11 @@ public void StartRoutine(int action) // When we reached end of ZS_*_END, we also call this method. Check if we really altered the routine action or just restarted it. if (didRoutineChange) { + if (Properties.CurrentFreePoint != null) + { + Properties.CurrentFreePoint.IsLocked = false; + Properties.CurrentFreePoint = null; + } Logger.Log($"Start new routine >{routineSymbol.Name}< on >{Go.transform.parent.name}<", LogCat.Ai); Properties.StateTime = 0; } @@ -337,8 +337,11 @@ public void ReEnableNpc() var currentRoutine = Properties.RoutineCurrent; if (currentRoutine != null) { - var wpPos = _wayNetService.GetWayNetPoint(currentRoutine.Waypoint).Position; - gameObject.transform.position = _npcService.GetFreeAreaAtSpawnPoint(wpPos); + var wp = _wayNetService.GetWayNetPoint(currentRoutine.Waypoint); + if (wp != null) + gameObject.transform.position = _npcService.GetFreeAreaAtSpawnPoint(wp.Position); + else + Logger.LogWarning($"ReEnableNpc: waypoint '{currentRoutine.Waypoint}' not found for {gameObject.name} — NPC will re-enable at current position.", LogCat.Npc); } // Animation state handling diff --git a/Assets/Gothic-Core/Scripts/Domain/Culling/VobMeshCullingDomain.cs b/Assets/Gothic-Core/Scripts/Domain/Culling/VobMeshCullingDomain.cs index 6b94a11a2..c7c726a92 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Culling/VobMeshCullingDomain.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Culling/VobMeshCullingDomain.cs @@ -572,6 +572,11 @@ private IEnumerator StopVobTrackingBasedOnVelocity() { var key = _pausedVobsToReenable.Keys.ElementAt(i); var rigidBody = _pausedVobsToReenable[key]; + if (rigidBody == null) + { + _pausedVobsToReenable.Remove(key); + continue; + } if (rigidBody.linearVelocity != Vector3.zero) { continue; diff --git a/Assets/Gothic-Core/Scripts/Domain/Meshes/Builder/NpcHeadMeshBuilder.cs b/Assets/Gothic-Core/Scripts/Domain/Meshes/Builder/NpcHeadMeshBuilder.cs index 5b0e7e89d..920fe944e 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Meshes/Builder/NpcHeadMeshBuilder.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Meshes/Builder/NpcHeadMeshBuilder.cs @@ -20,9 +20,14 @@ public override GameObject Build() return RootGo; } - var npcContainer = RootGo.GetComponentInParent().Npc.GetUserData(); + var npcContainer = RootGo.GetComponentInParent()?.Npc?.GetUserData(); + if (npcContainer == null) + { + Logger.LogWarning($"NpcContainer not available during head build for {RootGo.name} — skipping head component setup.", LogCat.Mesh); + return RootGo; + } - // Cache it f1or faster use during runtime + // Cache it for faster use during runtime npcContainer.PrefabProps.Head = headGo.transform; npcContainer.PrefabProps.HeadMorph = headGo.AddComponent().Inject(); npcContainer.PrefabProps.HeadMorph.HeadName = npcContainer.Props.BodyData.Head; diff --git a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/Attack.cs b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/Attack.cs index 0c133159d..32da7ff9f 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/Attack.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/Attack.cs @@ -32,6 +32,13 @@ public Attack(AnimationAction action, NpcContainer npcData) : base(action, npcDa public override void Start() { + if (Vob.GuildTrue < (int)VmGothicEnums.Guild.GIL_SEPERATOR_HUM) + { + Logger.Log($"AI_Attack() on human NPC (guild={Vob.GuildTrue}) — not yet implemented, skipping.", LogCat.Ai); + IsFinishedFlag = true; + return; + } + var aiFunctionTemplate = FindAiFunctionTemplate(); _move = VmCacheService.TryGetFightAiData(aiFunctionTemplate, Vob.FightTactic).GetRandomMove(); StartAttackAction(); diff --git a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToNpc.cs b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToNpc.cs index 29b970bc6..d2a0e2cc5 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToNpc.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToNpc.cs @@ -6,6 +6,8 @@ namespace Gothic.Core.Domain.Npc.Actions.AnimationActions { public class GoToNpc : AbstractWalkAnimationAction { + private const float ConversationDistance = 1.5f; + private Transform _destinationTransform; public GoToNpc(AnimationAction action, NpcContainer npcContainer) : base(action, npcContainer) @@ -21,7 +23,11 @@ public override void Start() protected override Vector3 GetWalkDestination() { - return _destinationTransform.position; + var targetPos = _destinationTransform.position; + var toTarget = targetPos - NpcGo.transform.position; + if (toTarget.sqrMagnitude < 0.001f) + return targetPos; + return targetPos + toTarget.normalized * -ConversationDistance; } diff --git a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToWp.cs b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToWp.cs index e889e8861..2cf9ab28e 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToWp.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Npc/Actions/AnimationActions/GoToWp.cs @@ -33,8 +33,14 @@ public override void Start() } // We need to set the route now to ensure base.Start() can check if NPC is already _on_ the final destination. - _route = new Stack(WayNetService.FindFastestPath(currentWaypoint.Name, - destinationWaypoint.Name)); + var path = WayNetService.FindFastestPath(currentWaypoint.Name, destinationWaypoint.Name); + if (path == null) + { + IsFinishedFlag = true; + return; + } + + _route = new Stack(path); base.Start(); } diff --git a/Assets/Gothic-Core/Scripts/Domain/Vobs/VobInitializerDomain.cs b/Assets/Gothic-Core/Scripts/Domain/Vobs/VobInitializerDomain.cs index 3e37bb0cd..1a1a9110d 100644 --- a/Assets/Gothic-Core/Scripts/Domain/Vobs/VobInitializerDomain.cs +++ b/Assets/Gothic-Core/Scripts/Domain/Vobs/VobInitializerDomain.cs @@ -581,14 +581,18 @@ public AudioClip GetSoundClip(string soundName) if (sfxContainer == null) return null; + var firstSound = sfxContainer.GetFirstSound(); + if (firstSound == null) + return null; + // Instead of decoding nosound.wav which might be decoded incorrectly, just return null. - if (sfxContainer.GetFirstSound().File.EqualsIgnoreCase(AudioService.NoSoundName)) + if (firstSound.File.EqualsIgnoreCase(AudioService.NoSoundName)) return null; if (sfxContainer.Count > 1) - Logger.LogWarning($"Multiple random elements exist for >{sfxContainer.GetFirstSound().File}< but only first is selected.", LogCat.Audio); + Logger.LogWarning($"Multiple random elements exist for >{firstSound.File}< but only first is selected.", LogCat.Audio); - clip = _audioService.CreateAudioClip(sfxContainer.GetFirstSound().File); + clip = _audioService.CreateAudioClip(firstSound.File); } return clip; diff --git a/Assets/Gothic-Core/Scripts/Models/Audio/SfxModel.cs b/Assets/Gothic-Core/Scripts/Models/Audio/SfxModel.cs index c02327a76..b2285618b 100644 --- a/Assets/Gothic-Core/Scripts/Models/Audio/SfxModel.cs +++ b/Assets/Gothic-Core/Scripts/Models/Audio/SfxModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Gothic.Core.Logging; using Gothic.Core.Services; using Gothic.Core.Const; using Gothic.Core.Extensions; @@ -8,6 +9,7 @@ using MyBox; using Reflex.Attributes; using ZenKit.Daedalus; +using Logger = Gothic.Core.Logging.Logger; namespace Gothic.Core.Models.Audio { @@ -64,9 +66,10 @@ private void LoadSoundEffects() var firstSound = _gameStateService.SfxVm.InitInstance(soundKey); sounds.Add(firstSound); } - catch (Exception e) + catch (Exception) { - // If the key itself doesn't exist, then we don't need to look further. + // SFX symbol missing from Gothic's SFX scripts — expected for some broken VOBs (e.g. Wood_Night2 was never defined). + Logger.LogWarning($"SFX symbol not found in VM: '{soundKey}' — no sound will play.", LogCat.Audio); _soundEffects = Array.Empty(); return; } diff --git a/Assets/Gothic-Core/Scripts/Services/Npc/FightService.cs b/Assets/Gothic-Core/Scripts/Services/Npc/FightService.cs index 674d5d0a4..163910d20 100644 --- a/Assets/Gothic-Core/Scripts/Services/Npc/FightService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Npc/FightService.cs @@ -1,11 +1,9 @@ using Gothic.Core.Adapters.UI.StatusBars; -using Gothic.Core.Domain.Npc.Actions.AnimationActions; using Gothic.Core.Logging; using Gothic.Core.Manager; using Gothic.Core.Models.Container; using Gothic.Core.Models.Vm; using Gothic.Core.Services.Config; -using Gothic.Core.Services.Vm; using Gothic.Core.Services.World; using Reflex.Attributes; using UnityEngine; @@ -17,7 +15,6 @@ namespace Gothic.Core.Services.Npc public class FightService { [Inject] private AudioService _audioService; - [Inject] private VmService _vmService; [Inject] private AnimationService _animationService; [Inject] private PhysicsService _physicsService; [Inject] private NpcHelperService _npcHelperService; @@ -33,6 +30,9 @@ public void Init() private void OnHit(NpcContainer attacker, NpcContainer target, Vector3 __) { + if (target.Props.BodyState == VmGothicEnums.BodyState.BsDead) + return; + Logger.Log($"[FightService.OnHit] *** {attacker.Instance.GetName(NpcNameSlot.Slot0)} HIT {target.Instance.GetName(NpcNameSlot.Slot0)}", LogCat.Npc); if (OnHitUpdateHealth(attacker, target)) { @@ -90,21 +90,21 @@ private bool OnHitUpdateHealth(NpcContainer attacker, NpcContainer target) private void OnDyingChangeAnimation(NpcContainer target) { - // Stop current (attack) animation. - target.Props.CurrentAction.StopImmediately(); + // Clear pending AI queue and stop all running animations (e.g. s_walk still looping). + // Death takes priority over everything — bypass the queue and play directly. + target.Props.AnimationQueue.Clear(); + target.PrefabProps.AnimationSystem.StopAllAnimations(); _physicsService.DisablePhysicsForNpc(target.PrefabProps); var animName = _animationService.GetAnimationName(VmGothicEnums.AnimationType.DeadB, target); - target.Props.AnimationQueue.Enqueue(new PlayAni(new(animName), target)); + target.PrefabProps.AnimationSystem.PlayAnimation(animName); } private void OnHitChangeAnimation(NpcContainer target) { - // Stop current (attack) animation. - target.Props.CurrentAction.StopImmediately(); - + // Play hurt on top of whatever is currently running — don't interrupt the current action. var animName = _animationService.GetAnimationName(VmGothicEnums.AnimationType.StumbleA, target); - target.Props.AnimationQueue.Enqueue(new PlayAni(new(animName), target)); + target.PrefabProps.AnimationSystem.PlayAnimation(animName); } private void OnHitPlaySound(NpcContainer target) diff --git a/Assets/Gothic-Core/Scripts/Services/Npc/NpcAiService.cs b/Assets/Gothic-Core/Scripts/Services/Npc/NpcAiService.cs index 971fd2c8e..0adbe470f 100644 --- a/Assets/Gothic-Core/Scripts/Services/Npc/NpcAiService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Npc/NpcAiService.cs @@ -50,12 +50,12 @@ public void ExecutePerception(VmGothicEnums.PerceptionType type, NpcProperties p } var oldSelf = _gameStateService.GothicVm.GlobalSelf; + var oldOther = _gameStateService.GothicVm.GlobalOther; var oldVictim = _gameStateService.GothicVm.GlobalVictim; - var oldOther = _gameStateService.GothicVm.GlobalVictim; _gameStateService.GothicVm.GlobalSelf = self; - - if(other != null) + + if(other != null) { _gameStateService.GothicVm.GlobalOther = other; } @@ -66,10 +66,10 @@ public void ExecutePerception(VmGothicEnums.PerceptionType type, NpcProperties p } _gameStateService.GothicVm.Call(perceptionFunction); - + _gameStateService.GothicVm.GlobalSelf = oldSelf; + _gameStateService.GothicVm.GlobalOther = oldOther; _gameStateService.GothicVm.GlobalVictim = oldVictim; - _gameStateService.GothicVm.GlobalVictim = oldOther; } public void ExtNpcSetPerceptionTime(NpcInstance npc, float time) @@ -341,8 +341,8 @@ public int ExtNpcGetDistToNpc(NpcInstance npc1, NpcInstance npc2) var npc1Pos = npc1.GetUserData().Go.transform.position; Vector3 npc2Pos; - // If hero - if (npc2.Id == 0) + // If hero: use camera position (VR head position is most accurate) + if (npc2.Index == _gameStateService.GothicVm.GlobalHero?.Index) { npc2Pos = Camera.main!.transform.position; } @@ -388,14 +388,14 @@ public void ExtAiDrawWeapon(NpcInstance npc) public bool ExtNpcIsDead(NpcInstance npcInstance) { - // FIXME - We need to implement it properly. Just fixing NPEs for now! - // FIXME - e.g. used for PC_Thief_AFTERTROLL_Condition() from Daedalus. - return false; + // FIXME - BodyState is runtime-only and lost on NPC reload (e.g. world reload respawns the NPC alive). + // A permanent death flag needs to be persisted in SaveGame state and checked here instead. + return npcInstance.GetUserData()?.Props.BodyState == VmGothicEnums.BodyState.BsDead; } public bool ExtNpcIsInState(NpcInstance npc, int state) { - return npc.GetUserData().Vob.CurrentStateIndex == state; + return npc.GetUserData()?.Vob.CurrentStateIndex == state; } public bool ExtNpcIsPlayer(NpcInstance npc) diff --git a/Assets/Gothic-Core/Scripts/Services/Npc/NpcHelperService.cs b/Assets/Gothic-Core/Scripts/Services/Npc/NpcHelperService.cs index ac0354fe6..b3a41e31f 100644 --- a/Assets/Gothic-Core/Scripts/Services/Npc/NpcHelperService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Npc/NpcHelperService.cs @@ -152,7 +152,7 @@ public bool ExtWldDetectNpcEx(NpcInstance npcInstance, int specificNpcIndex, int _gameStateService.GothicVm.GlobalHero!.Index) // if we don't detect player, then skip it .Where(i => specificNpcIndex < 0 || specificNpcIndex == i.Instance.Index) // Specific NPC is found right now? - .Where(i => aiState < 0 || npcVob.CurrentStateIndex == i.Vob.CurrentStateIndex) + .Where(i => aiState < 0 || aiState == i.Vob.CurrentStateIndex) .Where(i => guild < 0 || i.Instance.Guild == guild) // check guild .OrderBy(i => Vector3.Distance(i.Go.transform.position, npcPos)) // get nearest .FirstOrDefault(); diff --git a/Assets/Gothic-Core/Scripts/Services/Npc/NpcInventoryService.cs b/Assets/Gothic-Core/Scripts/Services/Npc/NpcInventoryService.cs index ccd9edb66..0017f8d19 100644 --- a/Assets/Gothic-Core/Scripts/Services/Npc/NpcInventoryService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Npc/NpcInventoryService.cs @@ -137,15 +137,26 @@ public List GetAllInventoryItems(NpcInstance npc) public int ExtNpcHasItems(NpcInstance npc, int itemId) { - var npcVob = npc.GetUserData()!.Vob; var itemInstanceName = _gameStateService.GothicVm.GetSymbolByIndex(itemId)!.Name; - - for (var i = 0; i < npcVob.ItemCount; i++) + + foreach (InvCats cat in System.Enum.GetValues(typeof(InvCats))) { - if (npcVob.GetItem(i).Name == itemInstanceName) - return npcVob.GetItem(i).Amount; + if (cat == InvCats.InvCatMax) + continue; + try + { + foreach (var item in GetInventoryItems(npc, cat)) + { + if (string.Equals(item.Name, itemInstanceName, System.StringComparison.OrdinalIgnoreCase)) + return item.Amount; + } + } + catch + { + // Category slot was never initialized for this NPC + } } - + return 0; } diff --git a/Assets/Gothic-Core/Scripts/Services/Player/DialogService.cs b/Assets/Gothic-Core/Scripts/Services/Player/DialogService.cs index 63cdc66e0..6635f89e6 100644 --- a/Assets/Gothic-Core/Scripts/Services/Player/DialogService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Player/DialogService.cs @@ -99,9 +99,12 @@ public void StartDialog(NpcContainer npcContainer, bool initialDialogStarting) // TODO - Should be outsourced to some VmManager.Call function which sets and resets values. var oldSelf = _gameStateService.GothicVm.GlobalSelf; + var oldOther = _gameStateService.GothicVm.GlobalOther; _gameStateService.GothicVm.GlobalSelf = npcContainer.Instance; + _gameStateService.GothicVm.GlobalOther = _gameStateService.GothicVm.GlobalHero; var conditionResult = _gameStateService.GothicVm.Call(dialog.Condition); _gameStateService.GothicVm.GlobalSelf = oldSelf; + _gameStateService.GothicVm.GlobalOther = oldOther; // Dialog condition is false if (conditionResult == 0) @@ -146,9 +149,12 @@ private bool TryGetImportant(NpcContainer npcContainer, out InfoInstance item) // TODO - Should be outsourced to some VmManager.Call function which sets and resets values. var oldSelf = _gameStateService.GothicVm.GlobalSelf; + var oldOther = _gameStateService.GothicVm.GlobalOther; _gameStateService.GothicVm.GlobalSelf = npcContainer.Instance; + _gameStateService.GothicVm.GlobalOther = _gameStateService.GothicVm.GlobalHero; var conditionResult = _gameStateService.GothicVm.Call(dialog.Condition); _gameStateService.GothicVm.GlobalSelf = oldSelf; + _gameStateService.GothicVm.GlobalOther = oldOther; if (conditionResult == 0) { diff --git a/Assets/Gothic-Core/Scripts/Services/UI/FontService.cs b/Assets/Gothic-Core/Scripts/Services/UI/FontService.cs index 327456552..6796a4104 100644 --- a/Assets/Gothic-Core/Scripts/Services/UI/FontService.cs +++ b/Assets/Gothic-Core/Scripts/Services/UI/FontService.cs @@ -1,4 +1,3 @@ -using System; using System.Reflection; using Gothic.Core.Const; using Gothic.Core.Logging; diff --git a/Assets/Gothic-Core/Scripts/Services/Vobs/VobService.cs b/Assets/Gothic-Core/Scripts/Services/Vobs/VobService.cs index 28b608a1e..c6ea15d55 100644 --- a/Assets/Gothic-Core/Scripts/Services/Vobs/VobService.cs +++ b/Assets/Gothic-Core/Scripts/Services/Vobs/VobService.cs @@ -176,12 +176,19 @@ private IEnumerator InitVobCoroutine() // } var item = _objectsToInitQueue.Dequeue(); - + item.IsLoaded = true; // We assume that each loaded VOB is centered at parent=0,0,0. // Should work smoothly until we start lazy loading sub-vobs ;-) - _initializerDomain.InitVob(item.Container.Vob, item.gameObject, default, true); + try + { + _initializerDomain.InitVob(item.Container.Vob, item.gameObject, default, true); + } + catch (Exception e) + { + Logger.LogError($"Failed to init VOB {item.name}: {e}", LogCat.Vob); + } yield return _frameSkipperService.TrySkipToNextFrameCoroutine(); } diff --git a/Assets/Gothic-VR/Resources/VR/Prefabs/Player-Elements/BackPack.prefab b/Assets/Gothic-VR/Resources/VR/Prefabs/Player-Elements/BackPack.prefab index b6b86c804..07854330c 100644 --- a/Assets/Gothic-VR/Resources/VR/Prefabs/Player-Elements/BackPack.prefab +++ b/Assets/Gothic-VR/Resources/VR/Prefabs/Player-Elements/BackPack.prefab @@ -1293,15 +1293,15 @@ PrefabInstance: m_Modifications: - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_text - value: x/y + value: '' objectReference: {fileID: 0} - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_fontAsset - value: + value: objectReference: {fileID: 11400000, guid: ed34b05229166f3469c21b7208879736, type: 2} - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_sharedMaterial - value: + value: objectReference: {fileID: 5152873236376385906, guid: ed34b05229166f3469c21b7208879736, type: 2} - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_VerticalAlignment @@ -3692,7 +3692,7 @@ PrefabInstance: m_Modifications: - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_text - value: '{{CATEGORY}}' + value: '' objectReference: {fileID: 0} - target: {fileID: 6401121504383089494, guid: 4293d805a850fb84885c55f47b7299bd, type: 3} propertyPath: m_fontSize diff --git a/Assets/Gothic-VR/Resources/VR/Prefabs/Vobs/oCNpc.prefab b/Assets/Gothic-VR/Resources/VR/Prefabs/Vobs/oCNpc.prefab index f0a06aff6..3ea4fa907 100644 --- a/Assets/Gothic-VR/Resources/VR/Prefabs/Vobs/oCNpc.prefab +++ b/Assets/Gothic-VR/Resources/VR/Prefabs/Vobs/oCNpc.prefab @@ -764,6 +764,10 @@ PrefabInstance: propertyPath: m_SizeDelta.y value: 2.8 objectReference: {fileID: 0} + - target: {fileID: 2805224566998289421, guid: 6ce7e724ba52cae47a499f87c612cd47, type: 3} + propertyPath: m_LocalPosition.z + value: 0.02 + objectReference: {fileID: 0} - target: {fileID: 8230739814078363531, guid: 6ce7e724ba52cae47a499f87c612cd47, type: 3} propertyPath: m_Pivot.x value: 0.5 diff --git a/Assets/Gothic-VR/Scripts/Adapters/Player/VRBackpack.cs b/Assets/Gothic-VR/Scripts/Adapters/Player/VRBackpack.cs index 8ad6da4bc..fc5275e31 100644 --- a/Assets/Gothic-VR/Scripts/Adapters/Player/VRBackpack.cs +++ b/Assets/Gothic-VR/Scripts/Adapters/Player/VRBackpack.cs @@ -87,7 +87,7 @@ public void OnItemPutIntoHolster(HVRGrabberBase grabber, HVRGrabbable grabbable) var vobLoader = grabbable.GetComponentInParent(); var vobContainer = vobLoader.Container; - _playerService.AddItem(vobContainer.Vob.Name, vobContainer.VobAs().Amount); + _playerService.AddItem(vobContainer.Vob.Name, Mathf.Max(1, vobContainer.VobAs().Amount)); } @@ -102,8 +102,8 @@ public void OnItemPutIntoBackpack(HVRGrabberBase grabber, HVRGrabbable grabbable _vobMeshCullingService.RemoveCullingEntry(vobContainer); _saveGameService.CurrentWorldData.Vobs.Remove(vobContainer.Vob); - _playerService.AddItem(vobContainer.Vob.Name, vobContainer.VobAs().Amount); - + _playerService.AddItem(vobContainer.Vob.Name, Mathf.Max(1, vobContainer.VobAs().Amount)); + UpdateInventoryView(); } @@ -114,23 +114,25 @@ public void OnItemPutIntoBackpack(HVRGrabberBase grabber, HVRGrabbable grabbable public void OnItemPutOutOfHolster(HVRGrabberBase grabber, HVRGrabbable grabbable) { var vobLoader = grabbable.GetComponentInParent(); + if (vobLoader == null) + return; var vobContainer = vobLoader.Container; - _playerService.RemoveItem(vobContainer.Vob.Name, vobContainer.VobAs().Amount); + _playerService.RemoveItem(vobContainer.Vob.Name, Mathf.Max(1, vobContainer.VobAs().Amount)); } public void OnItemPutOutOfBackpack(HVRGrabberBase grabber, HVRGrabbable grabbable) { if (_tempIgnoreSocketing) return; - + var vobLoader = grabbable.GetComponentInParent(); var vobContainer = vobLoader.Container; _vobMeshCullingService.AddCullingEntry(vobContainer); _saveGameService.CurrentWorldData.Vobs.Add(vobContainer.Vob); - _playerService.RemoveItem(vobContainer.Vob.Name, vobContainer.VobAs().Amount); + _playerService.RemoveItem(vobContainer.Vob.Name, Mathf.Max(1, vobContainer.VobAs().Amount)); UpdateInventoryView(); } diff --git a/Assets/Gothic-VR/Scripts/Adapters/VRNpc.cs b/Assets/Gothic-VR/Scripts/Adapters/VRNpc.cs index f0e0e1eac..8664f2289 100644 --- a/Assets/Gothic-VR/Scripts/Adapters/VRNpc.cs +++ b/Assets/Gothic-VR/Scripts/Adapters/VRNpc.cs @@ -6,6 +6,7 @@ using Gothic.Core.Services; using Gothic.Core.Services.Config; using Gothic.Core.Services.Npc; +using Gothic.Core.Services.World; using Gothic.Core; using Gothic.Core.Extensions; using HurricaneVR.Framework.Core; @@ -22,6 +23,7 @@ public class VRNpc : MonoBehaviour [Inject] private readonly DialogService _dialogService; [Inject] private readonly NpcAiService _npcAiService; [Inject] private readonly ConfigService _configService; + [Inject] private readonly PhysicsService _physicsService; private NpcContainer _npcData; private VRNpcLoot _npcLoot; @@ -39,6 +41,9 @@ public void OnGrabbed(HVRGrabberBase grabber, HVRGrabbable grabbable) if (isDead && _configService.Dev.EnableNpcLooting && _npcLoot != null) { _npcLoot.Toggle(_npcData); + // HVR sets isKinematic=false during grab; release immediately and freeze the corpse + grabber.ForceRelease(); + _physicsService.DisablePhysicsForNpc(_npcData.PrefabProps); return; }