diff --git a/HarmonySrc/FpArms.cs b/HarmonySrc/FpArms.cs
new file mode 100644
index 0000000..dd99db7
--- /dev/null
+++ b/HarmonySrc/FpArms.cs
@@ -0,0 +1,142 @@
+using UnityEngine;
+
+namespace NecromancerTome
+{
+ ///
+ /// Руки одержимого в виде от первого лица (указание 13.10: «делаем так, как у игрока»). У персонажа игры две
+ /// модели: полное тело (его видят другие и он сам со стороны) и отдельные руки для первого лица (baseRigFP,
+ /// SDCSUtils.CreateVizFP) - рисуются только у себя. Здесь собираем такие же руки по тем же шагам игры:
+ /// скелет baseRigFPPrefab с аниматором FPPlayerController, голая кожа (basePartsFP), без одежды и волос
+ /// (указание: «одежду пока не показываем»). Пол - по зомби (IsMale): BaseMale или BaseFemale из
+ /// archetypes.xml. Кожа серая (указание: «руки вообще серые - типа зомби их так видит») - цвет _Tint у
+ /// материала кожи, как игра красит тон кожи.
+ ///
+ /// Руки - под UFPSRoot камеры игрока, как у персонажа; камеру одержимости мы ставим в голову зомби, руки
+ /// едут вместе с ней. Слой HoldingItem - его камера игрока рисует только при ShowHoldingItemLayer(true).
+ ///
+ /// Только гуманоиды обычной комплекции (Allows), указание 13.10.
+ ///
+ /// ПАМЯТЬ. Одна модель рук на время вида от первого лица; Hide выгружает её (SDCSUtils.UnloadViz - материалы
+ /// и ссылки на ассеты освобождаются) - на переключении вида и на выходе из одержимости.
+ ///
+ public static class FpArms
+ {
+ /// Начала имён классов, у кого вид от первого лица (варианты Feral/Radiated/Charged/Infernal - тоже).
+ static readonly string[] Allowed =
+ {
+ "zombieArlene", "zombieBiker", "zombieBoe", "zombieBowler", "zombieBurnt", "zombieBusinessMan", "zombieDarlene",
+ "zombieInmate", "zombieJanitor", "zombieJoe", "zombieLab", "zombieLumberjack", "zombieMaleHazmat", "zombieMarlene",
+ "zombieMoe", "zombieNurse", "zombiePartyGirl", "zombieSkateboarder", "zombieSoldier", "zombieSteve", "zombieTomClark",
+ "zombieUtilityWorker", "zombieYo",
+ };
+
+ public static readonly Color Tint = new Color(0.55f, 0.56f, 0.55f, 1f);
+
+ static GameObject _rig;
+ static SDCSUtils.TransformCatalog _bones;
+ static bool _layerWasShown;
+
+ public static bool Active => _rig != null;
+
+ public static bool Allows(EntityAlive _z)
+ {
+ if (_z == null || !EntityClass.list.TryGetValue(_z.entityClass, out EntityClass cls) || cls?.entityClassName == null)
+ {
+ return false;
+ }
+ string name = cls.entityClassName;
+ if (name.Contains("Crawler"))
+ {
+ return false;
+ }
+ foreach (string a in Allowed)
+ {
+ if (name.StartsWith(a))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void Show(EntityPlayerLocal _player, EntityAlive _z)
+ {
+ if (_rig != null || _player == null || _z == null)
+ {
+ return;
+ }
+ Archetype arch = Archetype.GetArchetype(_z.IsMale ? "BaseMale" : "BaseFemale");
+ Transform cam = _player.cameraTransform;
+ if (arch == null || cam == null)
+ {
+ Debug.LogWarning("[NecromancerTome] FpArms: нет архетипа или камеры - рук не будет");
+ return;
+ }
+ Transform parent = cam.FindInChildren("UFPSRoot") ?? cam;
+ Archetype wasArch = SDCSUtils.tmpArchetype;
+ SDCSUtils.AssetRefs wasOwner = SDCSUtils.tmpAssetsOwner;
+ try
+ {
+ SDCSUtils.tmpArchetype = arch;
+ SDCSUtils.setupRig(ref _rig, ref _bones, SDCSUtils.baseRigFPPrefab, parent, SDCSUtils.FPAnimController);
+ SDCSUtils.tmpAssetsOwner = SDCSUtils.GetAssetOwnerForRig(_rig);
+ SDCSUtils.setupBase(_rig, _bones, SDCSUtils.basePartsFP, true);
+ }
+ finally
+ {
+ SDCSUtils.tmpArchetype = wasArch;
+ SDCSUtils.tmpAssetsOwner = wasOwner;
+ }
+ _rig.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
+ _rig.name = "necroZombieArmsFP";
+ int layer = LayerMask.NameToLayer("HoldingItem");
+ int tinted = 0;
+ foreach (SkinnedMeshRenderer smr in _rig.GetComponentsInChildren(true))
+ {
+ smr.gameObject.layer = layer;
+ // Материалы с _Tint игра уже размножила для этой модели (SDCSUtils.Stitch) - красим свои копии.
+ foreach (Material m in smr.sharedMaterials)
+ {
+ if (m != null && m.HasColor("_Tint"))
+ {
+ m.SetColor("_Tint", Tint);
+ tinted++;
+ }
+ }
+ }
+ // Коллайдеры рук (у женского скелета игра вешает капсулу на таз) - выключены: руки в голове зомби ловили бы
+ // луч прицела и толкали бы предметы.
+ foreach (Collider c in _rig.GetComponentsInChildren(true))
+ {
+ c.enabled = false;
+ }
+ foreach (HingeJoint j in _rig.GetComponentsInChildren(true))
+ {
+ if (j.connectedBody == null)
+ {
+ j.gameObject.SetActive(false);
+ }
+ }
+ _layerWasShown = _player.playerCamera != null && (_player.playerCamera.cullingMask & (1 << layer)) != 0;
+ _player.ShowHoldingItemLayer(true);
+ Debug.Log("[NecromancerTome] FpArms: руки " + arch.Name + " для " + _z.EntityName + ", серых материалов " + tinted +
+ ", родитель " + parent.name);
+ }
+
+ public static void Hide(EntityPlayerLocal _player)
+ {
+ if (_rig == null)
+ {
+ return;
+ }
+ if (!_layerWasShown)
+ {
+ _player?.ShowHoldingItemLayer(false);
+ }
+ SDCSUtils.UnloadViz(_rig);
+ _rig = null;
+ _bones = null;
+ Debug.Log("[NecromancerTome] FpArms: руки убраны");
+ }
+ }
+}
diff --git a/HarmonySrc/Possession.cs b/HarmonySrc/Possession.cs
index 23aad97..c63d35e 100644
--- a/HarmonySrc/Possession.cs
+++ b/HarmonySrc/Possession.cs
@@ -356,6 +356,7 @@ namespace NecromancerTome
Active = false;
FirstPerson = false;
HideBody(null);
+ FpArms.Hide(_player);
RatLoot.CloseIfViewing();
WolfBag.CloseWindow();
RatPhase.Stop(GameManager.Instance?.World, Zombie, "выход из одержимости");
@@ -574,13 +575,20 @@ namespace NecromancerTome
// кнопка геймпада (тест 03.10: «подрыва нету»).
detonate = input.Activate.WasPressed || input.PermanentActions.Activate.WasPressed;
}
- if (input != null && !gui && (input.CameraChange.WasPressed || input.QuickMenu.WasPressed || input.PermanentActions.QuickMenu.WasPressed))
+ // Только гуманоиды обычной комплекции (указание 13.10, FpArms.Allows) - у остальных ~/ё ничего не делает.
+ if (input != null && !gui && FpArms.Allows(z) &&
+ (input.CameraChange.WasPressed || input.QuickMenu.WasPressed || input.PermanentActions.QuickMenu.WasPressed))
{
FirstPerson = !FirstPerson;
_fpEye = Vector3.zero;
- if (!FirstPerson)
+ if (FirstPerson)
+ {
+ FpArms.Show(_player, z);
+ }
+ else
{
HideBody(null);
+ FpArms.Hide(_player);
}
Debug.Log("[NecromancerTome] Possession: вид от " + (FirstPerson ? "первого" : "третьего") + " лица");
}
@@ -1494,7 +1502,11 @@ namespace NecromancerTome
/// Подсказка при вселении: что умеет этот зомби.
static void Hint(EntityPlayerLocal _p, EntityAlive _z)
{
- string text = Localization.Get("necroPossessHintBase") + ", " + Localization.Get("necroPossessHintView");
+ string text = Localization.Get("necroPossessHintBase");
+ if (FpArms.Allows(_z))
+ {
+ text += ", " + Localization.Get("necroPossessHintView");
+ }
if (Flying)
{
text += ", " + Localization.Get("necroPossessHintFly");
diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll
index 1c5b803..6d68f71 100644
Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ
diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb
index 55961fc..4d08845 100644
Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ