using System;
using UnityEngine;
public partial class EntityAliveSDXV4
{
// =========================================================================
// Weapon management
// =========================================================================
public void RefreshWeapon()
{
var item = ItemClass.GetItem(_currentWeapon);
UpdateWeapon(item);
}
public void UpdateWeapon(string itemName = "")
{
if (string.IsNullOrEmpty(itemName))
itemName = _currentWeapon;
var item = ItemClass.GetItem(itemName);
UpdateWeapon(item);
EntityUtilities.UpdateHandItem(entityId, itemName);
}
///
/// Changes the NPC's held item and refreshes the animator's right-hand transform.
/// Must be called after so _defaultWeapon is set.
///
public void UpdateWeapon(ItemValue item, bool force = false)
{
if (item == null) return;
if (item.GetItemId() < 0) return;
_currentWeapon = item.ItemClass.GetItemName();
if (!FindWeapon(_currentWeapon))
{
if (string.IsNullOrEmpty(_defaultWeapon)) return;
item = ItemClass.GetItem(_defaultWeapon);
}
if (item.GetItemId() == inventory.holdingItemItemValue.GetItemId() && !force) return;
_currentWeapon = item.ItemClass.GetItemName();
Buffs.SetCustomVar("CurrentWeaponID", item.GetItemId());
inventory.SetItem(0, item, 1);
foreach (var action in item.ItemClass.Actions)
{
if (action is ItemActionRanged ranged)
ranged.AutoFire = new DataItem(true);
}
// Refresh the animator after the weapon switch so the right-hand transform is correct.
var entityClassType = EntityClass.list[entityClass];
var meshName2 = entityClassType.Properties.Values.ContainsKey("Mesh") ? entityClassType.Properties.Values["Mesh"] : entityClassType.entityClassName;
emodel.avatarController.SwitchModelAndView(meshName2, emodel.IsFPV, IsMale);
if (emodel.avatarController is AvatarZombieController zombieController)
zombieController.rightHandT = zombieController.FindTransform(GetRightHandTransformName());
// Item update must happen after SwitchModelAndView so the weapon attaches to the updated hand.
inventory.OnUpdate();
inventory.ForceHoldingItemUpdate();
}
///
/// Returns true if is available to this NPC (loot container,
/// enter-game items, or current hand item).
///
public bool FindWeapon(string weapon)
{
// Check starting items first — items like meleeNPCEmptyHand have no CompatibleWeapon property
// but are always available because they're part of the NPC's base kit.
for (int i = 0; i < itemsOnEnterGame.Count; i++)
{
if (itemsOnEnterGame[i].itemValue.ItemClass.GetItemName()
.Equals(weapon, StringComparison.InvariantCultureIgnoreCase))
return true;
}
if (GetHandItem().ItemClass.GetItemName()
.Equals(weapon, StringComparison.InvariantCultureIgnoreCase))
return true;
// For NPC weapons that map to a player-held counterpart (via CompatibleWeapon property),
// verify the player version is present in the accessible inventory.
var currentWeapon = ItemClass.GetItem(weapon);
if (currentWeapon == null) return false;
if (!currentWeapon.ItemClass.Properties.Contains("CompatibleWeapon")) return false;
var playerWeapon = currentWeapon.ItemClass.Properties.GetString("CompatibleWeapon");
if (string.IsNullOrEmpty(playerWeapon)) return false;
var playerWeaponItem = ItemClass.GetItem(playerWeapon);
if (playerWeaponItem == null) return false;
// EntityTrader-based NPCs store their accessible inventory in HarvestManager.
if (this is EntityTrader && HarvestManager.Has(entityId))
return HarvestManager.GetOrCreate(entityId).HasItem(playerWeaponItem);
return lootContainer != null && lootContainer.HasItem(playerWeaponItem);
}
///
/// Harmony redirects AvatarAnimator.GetRightHandTransformName() to this method so
/// that per-weapon hand-joint overrides work correctly on NPC entities.
///
public new string GetRightHandTransformName()
{
var currentItemHand = inventory.holdingItem;
if (currentItemHand.Properties.Contains(EntityClass.PropRightHandJointName))
currentItemHand.Properties.ParseString(EntityClass.PropRightHandJointName, ref rightHandTransformName);
else
{
rightHandTransformName = "Gunjoint";
EntityClass.list[entityClass].Properties
.ParseString(EntityClass.PropRightHandJointName, ref rightHandTransformName);
}
return rightHandTransformName;
}
// =========================================================================
// Inventory setup
// =========================================================================
public override void SetupStartingItems()
{
for (int i = 0; i < itemsOnEnterGame.Count; i++)
{
var itemStack = itemsOnEnterGame[i];
var forId = ItemClass.GetForId(itemStack.itemValue.type);
if (forId.HasQuality)
itemStack.itemValue = new ItemValue(itemStack.itemValue.type, 1, 6, false, null, 1f);
else
itemStack.count = forId.Stacknumber.Value;
if (i == 0) _defaultWeapon = forId.GetItemName();
inventory.SetItem(i, itemStack);
}
}
private void AddToInventory()
{
// Gate on a cvar so the initial inventory is only populated once per entity.
if (Buffs.GetCustomVar("InitialInventory") > 0) return;
Buffs.SetCustomVar("InitialInventory", 1);
var ec = EntityClass.list[entityClass];
if (!ec.Properties.Values.ContainsKey("StartingItems")) return;
foreach (var item in ec.Properties.Values["StartingItems"].Split(','))
{
var itemStack = ItemStack.FromString(item.Trim());
if (itemStack.itemValue.IsEmpty()) continue;
var forId = ItemClass.GetForId(itemStack.itemValue.type);
if (forId.HasQuality)
itemStack.itemValue = new ItemValue(itemStack.itemValue.type, 1, 6, false, null, 1f);
lootContainer.AddItem(itemStack);
}
}
// =========================================================================
// Quests
// =========================================================================
public void GiveQuest(string strQuest)
{
var questIdLower = strQuest.ToLower();
foreach (var quest in questJournal.quests)
if (quest.ID == questIdLower) return; // no duplicates
var newQuest = QuestClass.CreateQuest(strQuest);
if (newQuest == null) return;
newQuest.SharedOwnerID = entityId;
newQuest.QuestGiverID = -1;
questJournal.AddQuest(newQuest);
}
// =========================================================================
// Trader ID toggling — temporarily zeroed during damage so NPCInfo
// properties are evaluated correctly, then restored afterwards.
// =========================================================================
public void ToggleTraderID(bool restore)
{
if (NPCInfo == null) return;
NPCInfo.TraderID = restore ? _defaultTraderID : 0;
}
// =========================================================================
// Mission (hide/show NPC)
// =========================================================================
///
/// Hides the NPC and marks it as on a mission (send = true), or
/// restores its visibility when the mission ends (send = false).
///
public void SendOnMission(bool send)
{
if (send)
{
var enemy = GetRevengeTarget();
if (enemy != null)
{
SetAttackTarget(null, 0);
enemy.SetAttackTarget(null, 0);
enemy.SetRevengeTarget(null);
enemy.DoRagdoll(new DamageResponse());
SetRevengeTarget(null);
}
isIgnoredByAI = true;
transform.localScale = Vector3.zero;
emodel.SetVisible(false, false);
enabled = false;
Buffs.AddCustomVar("onMission", 1f);
if (NavObject != null) NavObject.IsActive = false;
DebugNameInfo = "";
emodel.enabled = false;
SetupDebugNameHUD(false);
}
else
{
transform.localScale = _scale;
emodel.SetVisible(true, true);
enabled = true;
Buffs.RemoveCustomVar("onMission");
if (NavObject != null) NavObject.IsActive = true;
isIgnoredByAI = false;
emodel.enabled = true;
}
}
}