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