Змея: выход из текстур, полёт на ПКМ; одержимый: приседание, удар по прицелу; коза

- Змея: проверка «внутри текстуры» лучами (земля игры - поверхность), каждый кадр выброс
  на ближайшее свободное место (на земле - из любых текстур, в полёте - из грунта);
  шаг и прыжок в текстуры не пускают. Полёт пока зажата ПКМ: 1.1 м/с (половина шага
  зомби), сквозь постройки можно, в грунт нельзя; Пробел вверх, C/Ctrl вниз.
  E - аварийный выход. Детализация зданий и область тела - как у одержимого.
- Одержимый: будит спящих (комнаты зданий и сам вид одержимого), приседание на C и Ctrl,
  удар по точке под прицелом с наведением на грудь ближнего врага.
- Хваты: толчок руки к прицелу на ЛКМ; «пиф-паф» вселяет на пике толчка.
  «Отрицание» стало «Козой» (13 языков): средний и безымянный поджаты, большой прижат;
  звук - бросок змеи.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-05 16:28:40 +03:00
co-authored by Claude Opus 5.5
parent bb7a66dd8e
commit fdd3782024
6 changed files with 429 additions and 60 deletions
+38 -11
View File
@@ -36,7 +36,10 @@ namespace NecromancerTome
/// <summary>Скорость бегства - выше бега зомби-людей (1.5), как у медведя (указание: «дать ему ускорение»).</summary>
public const float DenySpeed = 2.2f;
public const float Cooldown = 0.6f;
public const string DenySound = "twitch_celebrate";
/// <summary>Звук «козы» (05.10: «нужен короткий звук» - вместо twitch_celebrate): бросок змеи - тот же,
/// что у рывка духа змеи (SnakeControl.AttackSound): «раз зомби боятся духа змеи, некромант может
/// пугать им и в телесной форме».</summary>
public const string DenySound = SnakeControl.AttackSound;
/// <summary>«Пиф-паф» (указание 03.10): не вселился - звон разбитого стекла, вселился - щелчок
/// удавшейся отмычки (sounds.xml: glassdestroy, lockpick_success).</summary>
public const string PifPafFailSound = "glassdestroy";
@@ -47,6 +50,10 @@ namespace NecromancerTome
static int _applied = -1;
static int _playerId = -1;
static float _nextCast;
/// <summary>Вселение - на пике толчка руки (05.10): иначе вид сразу уходит к зомби и толчка не видно.
/// Цель берётся в момент щелчка.</summary>
static EntityAlive _pifPafVictim;
static float _pifPafAt = -1f;
public static bool IsHand(ItemClass _item) => _item != null && _item.Name == HandItem;
@@ -74,9 +81,15 @@ namespace NecromancerTome
_applied = None;
_target = null;
_targetFrame = -1;
_pifPafVictim = null;
_pifPafAt = -1f;
HandPose.ResetAll();
return;
}
if (_pifPafAt >= 0f && Time.time >= _pifPafAt)
{
PifPafShot(player);
}
int want = Current(player);
if (want == _applied)
{
@@ -97,6 +110,26 @@ namespace NecromancerTome
}
}
static void PifPafShot(EntityPlayerLocal _player)
{
EntityAlive victim = _pifPafVictim;
_pifPafVictim = null;
_pifPafAt = -1f;
if (Current(_player) != PifPaf)
{
return; // за полтика сменил хват или руку - выстрела нет
}
if (victim != null && !victim.IsDead() && Possession.CanPossess(victim) && Possession.Enter(_player, victim))
{
_player.PlayOneShot(PifPafPossessSound);
}
else
{
_player.PlayOneShot(PifPafFailSound);
Debug.Log("[NecromancerTome] HandHolds: пиф-паф - " + (victim == null ? "в прицеле нет зомби" : victim.EntityName + " не гуманоид"));
}
}
public static void OnRadialCommand(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context)
{
if (_commandIndex >= None && _commandIndex <= PifPaf)
@@ -116,18 +149,12 @@ namespace NecromancerTome
if (_hold == PifPaf)
{
// Одержимость (решение 03.10): «пиф-паф» по зомби-гуманоиду - управление им.
EntityAlive victim = Target(_player);
if (Possession.CanPossess(victim) && Possession.Enter(_player, victim))
{
_player.PlayOneShot(PifPafPossessSound);
}
else
{
_player.PlayOneShot(PifPafFailSound);
Debug.Log("[NecromancerTome] HandHolds: пиф-паф - " + (victim == null ? "в прицеле нет зомби" : victim.EntityName + " не гуманоид"));
}
HandPose.Jab();
_pifPafVictim = Target(_player);
_pifPafAt = Time.time + HandPose.JabTime * 0.5f;
return;
}
HandPose.Jab(); // толчок к прицелу, как у «пиф-пафа» (05.10)
_player.PlayOneShot(DenySound);
World world = _player.world;
if (world.IsRemote())