diff --git a/Config/Localization.csv b/Config/Localization.csv index e9d0242..de2b146 100644 --- a/Config/Localization.csv +++ b/Config/Localization.csv @@ -221,6 +221,7 @@ necroHoldNone,items,Item,,,,Bare hand,,Bloße Hand,Mano desnuda,Main nue,Mano nu necroHoldDeny,items,Item,,,,Horns,,Pommesgabel,Cuernos,Cornes,Corna,メロイックサイン,악마의 뿔,Rogi,Chifres,Коза,Boynuzlar,恶魔角,惡魔角 necroHoldPifPaf,items,Item,,,,Bang-bang,,Peng-peng,Pum-pum,Pan-pan,Bang-bang,バンバン,빵야빵야,Pif-paf,Pou-pou,Пиф-паф,Bang-bang,砰砰,砰砰 necroPossessHintBase,items,Item,,,,"LMB - strike, R - leave the body",,"LMT - Schlag, R - Körper verlassen","LMB: golpe, R: salir del cuerpo","Clic gauche : frapper, R : quitter le corps","LMB - colpo, R - lascia il corpo",左クリック: 攻撃、R: 体から出る,"좌클릭 - 공격, R - 몸에서 나가기","LPM - cios, R - opuść ciało","LMB - golpe, R - sair do corpo","ЛКМ - удар, R - выйти из тела","Sol tık - vuruş, R - bedenden çık",左键 - 攻击,R - 离开身体,左鍵 - 攻擊,R - 離開身體 +necroPossessHintView,items,Item,,,,~ - first-person view,,~ - Ich-Perspektive,~: vista en primera persona,~ : vue à la première personne,~ - visuale in prima persona,~: 一人称視点,~ - 1인칭 시점,~ - widok z pierwszej osoby,~ - visão em primeira pessoa,~ (Ё) - вид от первого лица,~ - birinci şahıs görünümü,~ - 第一人称视角,~ - 第一人稱視角 necroPossessHintRanged,items,Item,,,,RMB - ranged attack,,RMT - Fernangriff,RMB: ataque a distancia,Clic droit : attaque à distance,RMB - attacco a distanza,右クリック: 遠距離攻撃,우클릭 - 원거리 공격,PPM - atak dystansowy,RMB - ataque à distância,ПКМ - дальняя атака,Sağ tık - menzilli saldırı,右键 - 远程攻击,右鍵 - 遠程攻擊 necroPossessHintDetonate,items,Item,,,,E - detonate,,E - Sprengen,E: detonar,E : exploser,E - detonare,E: 起爆,E - 폭발,E - detonacja,E - detonar,E - подрыв,E - patlat,E - 引爆,E - 引爆 necroPossessHintScream,items,Item,,,,E - scream (summons a horde under Deviation),,E - Schrei (ruft eine Horde unter Abweichung),E: grito (invoca una horda bajo desviación),E : cri (appelle une horde sous déviation),E - urlo (evoca un'orda sotto deviazione),E: 叫び(逸脱したホードを呼ぶ),E - 비명 (일탈한 무리를 부름),E - krzyk (wzywa hordę pod dewiacją),E - grito (invoca uma horda sob desvio),E - крик (созвать орду под девиацией),E - çığlık (sapkın bir sürü çağırır),E - 尖叫(召唤逸脱的尸潮),E - 尖叫(召喚逸脫的屍潮) diff --git a/HarmonySrc/Possession.cs b/HarmonySrc/Possession.cs index c78bd54..d2e03c4 100644 --- a/HarmonySrc/Possession.cs +++ b/HarmonySrc/Possession.cs @@ -143,6 +143,14 @@ namespace NecromancerTome /// Камера - позади и выше головы зомби. public const float CamBack = 2.6f; + /// Вид от первого лица (указание 13.10): клавиша ~/ё - как переключение вида у персонажа в игре + /// (QuickMenu, по умолчанию Backquote; и CameraChange геймпада), каждое вселение начинается от третьего лица. Камера - в голове, голова сжата + /// (HideHead), положение глаз сглажено (FpSmooth) - голова зомби на ходу мотается. + public static bool FirstPerson; + public const float FpForward = 0.12f, FpSmooth = 12f; + static Vector3 _fpEye; + static Transform _hiddenHead; + static Vector3 _hiddenHeadScale; public const float CamUp = 0.6f; /// Как далеко зомби замечают одержимого: зрение зомби - 30 м (entityclasses.xml SightRange). public const float TauntRange = 30f; @@ -296,6 +304,7 @@ namespace NecromancerTome _nextTaunt = 0f; _nextScream = 0f; _loggedCamera = false; + FirstPerson = false; // Пояс одержимому питомцу ни к чему (указание 06.10); открыться заново не даёт // Patch_GUIWindowManager_openInternal_SnakeToolbelt, после выхода игра откроет его сама. if (_pet) @@ -344,6 +353,8 @@ namespace NecromancerTome return; } Active = false; + FirstPerson = false; + HideHead(null); RatLoot.CloseIfViewing(); WolfBag.CloseWindow(); RatPhase.Stop(GameManager.Instance?.World, Zombie, "выход из одержимости"); @@ -562,6 +573,16 @@ 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)) + { + FirstPerson = !FirstPerson; + _fpEye = Vector3.zero; + if (!FirstPerson) + { + HideHead(null); + } + Debug.Log("[NecromancerTome] Possession: вид от " + (FirstPerson ? "первого" : "третьего") + " лица"); + } if (input != null && !gui && input.ToggleCrouch.WasPressed) { _crouchToggled = !_crouchToggled; @@ -1449,7 +1470,7 @@ namespace NecromancerTome /// Подсказка при вселении: что умеет этот зомби. static void Hint(EntityPlayerLocal _p, EntityAlive _z) { - string text = Localization.Get("necroPossessHintBase"); + string text = Localization.Get("necroPossessHintBase") + ", " + Localization.Get("necroPossessHintView"); if (Flying) { text += ", " + Localization.Get("necroPossessHintFly"); @@ -1570,7 +1591,7 @@ namespace NecromancerTome for (int i = 0; i < n; i++) { RaycastHit hit = _aimHits[i]; - if (hit.distance <= CamBack + 0.5f || hit.distance >= best || + if (hit.distance <= (FirstPerson ? 0.1f : CamBack + 0.5f) || hit.distance >= best || self != null && (hit.transform == self || hit.transform.IsChildOf(self))) { continue; @@ -2003,6 +2024,28 @@ namespace NecromancerTome _flyZoomHeld = false; } + /// Голова одержимого сжата почти в точку, пока вид от первого лица, - иначе камера внутри черепа + /// (у зомби модель цельная, отдельных рук, как у персонажа, нет). Ставится каждый кадр после аниматора; + /// null - вернуть прежний масштаб. + static void HideHead(Transform _head) + { + if (_hiddenHead != null && _hiddenHead != _head) + { + _hiddenHead.localScale = _hiddenHeadScale; + _hiddenHead = null; + } + if (_head == null) + { + return; + } + if (_hiddenHead == null) + { + _hiddenHead = _head; + _hiddenHeadScale = _head.localScale; + } + _head.localScale = Vector3.one * 0.001f; + } + /// Из постфикса vp_FPCamera.LateUpdate: камера позади и выше головы зомби, не сквозь стены. public static void PlaceCamera(Transform _camera) { @@ -2020,6 +2063,18 @@ namespace NecromancerTome Transform head = z.emodel != null ? z.emodel.GetHeadTransform() : null; Vector3 anchor = head != null ? head.position : z.position - Origin.position + Vector3.up * 1.6f; Quaternion rot = Quaternion.Euler(-_pitch, _yaw, 0f); + if (FirstPerson) + { + // Положение головы - уже после аниматора (мы в LateUpdate камеры), до сжатия: сжатие масштаба + // самой кости её положения не меняет. + Vector3 want = anchor + rot * Vector3.forward * FpForward; + _fpEye = _fpEye == Vector3.zero || (want - _fpEye).sqrMagnitude > 4f + ? want + : Vector3.Lerp(_fpEye, want, 1f - Mathf.Exp(-FpSmooth * Time.deltaTime)); + HideHead(head); + _camera.SetPositionAndRotation(_fpEye, rot); + return; + } Vector3 eye = anchor + Vector3.up * CamUp - rot * Vector3.forward * CamBack; Vector3 to = eye - anchor; if (Physics.Raycast(anchor, to.normalized, out RaycastHit hit, to.magnitude + 0.2f, SnakeControl.WallMask, QueryTriggerInteraction.Ignore)) diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 81b2746..f8f0108 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index f464227..d9ea6a3 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ