Skip to content
Open
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
5 changes: 5 additions & 0 deletions Assets/Gothic-Core/Scripts/Models/Config/DeveloperConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ public class DebugChannelTypesCollection : CollectionWrapper<DeveloperConfigEnum

[Tooltip("Enable player→NPC melee combat (hit detection, damage, hurt/death animations). WIP - debug damage values only.")]
public bool EnableCombatSystem;

[ConditionalField(fieldToCheck: nameof(EnableCombatSystem), compareValues: true)]
[Tooltip("Call B_DeathXP on kill to grant XP. WIP - XP values may be incorrect.")]

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.

Why does the value might be incorrect? Can you add it to be non-WIP? ;-)

public bool EnableDeathXP;

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.

I don't think this needs to be guarded by a feature flag at all. Should do no harm to the game at runtime.


[ConditionalField(fieldToCheck: nameof(EnableNpcs), compareValues: true)]
public bool EnableNpcMeshCulling = true;
Expand Down Expand Up @@ -291,5 +295,6 @@ public class DebugChannelTypesCollection : CollectionWrapper<DeveloperConfigEnum
public bool EnableDecalVisuals;
public bool EnableParticleEffects;


}
}
34 changes: 34 additions & 0 deletions Assets/Gothic-Core/Scripts/Services/Npc/FightService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class FightService
[Inject] private PhysicsService _physicsService;
[Inject] private NpcHelperService _npcHelperService;
[Inject] private readonly ConfigService _configService;
[Inject] private readonly Gothic.Core.Services.GameStateService _gameStateService;
[Inject] private readonly NpcService _npcService;

public void Init()
{
Expand All @@ -39,6 +41,8 @@ private void OnHit(NpcContainer attacker, NpcContainer target, Vector3 __)
Logger.Log($"[FightService.OnHit] {target.Instance.GetName(NpcNameSlot.Slot0)} is DEAD", LogCat.Npc);
target.Props.BodyState = VmGothicEnums.BodyState.BsDead;
OnDyingChangeAnimation(target);
if (_configService.Dev.EnableDeathXP)
OnNpcDied(target, attacker);
}
else
{
Expand Down Expand Up @@ -96,6 +100,36 @@ private bool OnHitUpdateHealth(NpcContainer attacker, NpcContainer target)
return hitPoints <= 0;
}

private void OnNpcDied(NpcContainer dead, NpcContainer killer)
{
var vm = _gameStateService.GothicVm;
var aivPlundered = vm.GetSymbolByName("AIV_PLUNDERED")?.GetInt(0) ?? 8;

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.

Please put symbol into any of the *Constants. files.

dead.Instance.SetAiVar(aivPlundered, 0);

var oldSelf = vm.GlobalSelf;

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.

Hm. We might have this more often to switch self/other to something different, then switch it back afterwards.
How about having a helper function as ZenKitExtension.cs but like GothicExtension.cs? With a function called ExchangeDaedalus() + ExchangeDaedalueBack() or so? Saves some back and forth code in the future?

var oldOther = vm.GlobalOther;
vm.GlobalSelf = dead.Instance;
vm.GlobalOther = killer.Instance;

if (_configService.Dev.EnableDeathXP && killer.PrefabProps.IsHero())
{
var bDeathXp = vm.GetSymbolByName("B_DeathXP");

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.

Constants.

if (bDeathXp != null)
{
vm.Call(bDeathXp.Index);
_npcService.SyncHeroInstanceToVob();
Logger.Log($"[FightService.OnNpcDied] B_DeathXP: {dead.Instance.GetName(NpcNameSlot.Slot0)} killed by hero", LogCat.Npc);
}
}

var bGiveDeathInv = vm.GetSymbolByName("B_GiveDeathInv");

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.

Please use existing constants file for it:
Assets/Gothic-Core/Scripts/Const/DaedalusConst.cs

if (bGiveDeathInv != null)
vm.Call(bGiveDeathInv.Index);

vm.GlobalSelf = oldSelf;
vm.GlobalOther = oldOther;
}

/// <summary>
/// C_ITEM.damageType is a DAM_* bitmask whose bit positions match the PROT_* indices.
/// Weapons carry one damage type; the first set bit wins.
Expand Down
9 changes: 9 additions & 0 deletions Assets/Gothic-Core/Scripts/Services/Npc/NpcService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ public void ExtNpcChangeAttribute(NpcInstance npc, int attributeId, int value)
vob.Attributes[attributeId] = value;
}

public void SyncHeroInstanceToVob()

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.

Hm. Why is it needed now?
I like it. It reminds me of the sync mechanism which is needed for the Save() logic. Why now? Or would it be sufficient to collect the data at save time?

{
var hero = GetHeroContainer();
hero.Vob.Level = hero.Instance.Level;
hero.Vob.Xp = hero.Instance.Exp;
hero.Vob.XpNextLevel = hero.Instance.ExpNext;
hero.Vob.Lp = hero.Instance.Lp;
}

public NpcContainer GetHeroContainer()
{
return ((NpcInstance)_gameStateService.GothicVm.GlobalHero).GetUserData();
Expand Down