Одержимость: прицел мимо своего тела, плевок мутанта в прицел
- AimPoint пропускает тело одержимого: у крупных зомби луч камеры упирался в свою спину - прицел не краснел, удары и выстрелы шли в себя. - Плевок мутанта (E): скорость снарядов пересчитывается навесом в цель под прицелом (до 15 м), вместо ванильного веера под ноги; подсказка на 13 языках. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5b69e0c04f
commit
85a7c2721e
@@ -592,7 +592,7 @@ namespace NecromancerTome
|
||||
}
|
||||
break;
|
||||
case PossessionPowers.Kind.Mutant:
|
||||
Ranged(z, detonate, detonate); // E - плевок веером вокруг себя (указание 13.10)
|
||||
Ranged(z, detonate, detonate); // E - плевок (указание 13.10), в прицел - Patch_ProjectileMoveScript_Fire_PossessedSpit
|
||||
break;
|
||||
default:
|
||||
Ranged(z, ranged, rangedHeld);
|
||||
@@ -1523,7 +1523,9 @@ namespace NecromancerTome
|
||||
return best != null ? best.getChestPosition() : aim;
|
||||
}
|
||||
|
||||
/// <summary>Точка под прицелом камеры (в мировых координатах), до 50 м.</summary>
|
||||
/// <summary>Точка под прицелом камеры (в мировых координатах), до 50 м. Тело самого одержимого луч
|
||||
/// пропускает (13.10: «у всех зомби должен краснеть прицел», «у мутанта атака не целится»): камера
|
||||
/// за головой, и у крупных зомби луч упирался в собственную спину - точка прицела была на себе.</summary>
|
||||
static Vector3 AimPoint()
|
||||
{
|
||||
Transform cam = _player?.cameraTransform;
|
||||
@@ -1531,14 +1533,42 @@ namespace NecromancerTome
|
||||
{
|
||||
return Zombie.position + Quaternion.Euler(-_pitch, _yaw, 0f) * Vector3.forward * 30f;
|
||||
}
|
||||
if (Physics.Raycast(cam.position, cam.forward, out RaycastHit hit, 50f, -5, QueryTriggerInteraction.Ignore) &&
|
||||
hit.distance > CamBack + 0.5f)
|
||||
int n = Physics.RaycastNonAlloc(cam.position, cam.forward, _aimHits, 50f, -5, QueryTriggerInteraction.Ignore);
|
||||
Transform self = Zombie != null ? Zombie.transform : null;
|
||||
float best = float.MaxValue;
|
||||
Vector3 point = Vector3.zero;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
return hit.point + Origin.position;
|
||||
RaycastHit hit = _aimHits[i];
|
||||
if (hit.distance <= CamBack + 0.5f || hit.distance >= best ||
|
||||
self != null && (hit.transform == self || hit.transform.IsChildOf(self)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
best = hit.distance;
|
||||
point = hit.point;
|
||||
}
|
||||
if (best < float.MaxValue)
|
||||
{
|
||||
return point + Origin.position;
|
||||
}
|
||||
return cam.position + Origin.position + cam.forward * 50f;
|
||||
}
|
||||
|
||||
static readonly RaycastHit[] _aimHits = new RaycastHit[32];
|
||||
|
||||
/// <summary>Куда летит плевок мутанта (указание 13.10: «у мутанта атака не целится»): грудь врага под
|
||||
/// прицелом, иначе точка под прицелом; дальше SpitRange - точка на луче к ней.</summary>
|
||||
public static Vector3 SpitTarget(Vector3 _from)
|
||||
{
|
||||
EntityAlive foe = Zombie != null && Zombie.world != null ? GroundAimTarget(Zombie.world, Zombie) : null;
|
||||
Vector3 to = foe != null ? foe.getChestPosition() : AimPoint();
|
||||
Vector3 d = to - _from;
|
||||
return d.magnitude > SpitRange ? _from + d.normalized * SpitRange : to;
|
||||
}
|
||||
|
||||
public const float SpitRange = 15f;
|
||||
|
||||
/// <summary>ПКМ - дальняя атака зомби по шагам ванильной EAIRangedAttackTarget.</summary>
|
||||
static void Ranged(EntityAlive _z, bool _pressed, bool _held)
|
||||
{
|
||||
|
||||
@@ -352,4 +352,28 @@ namespace NecromancerTome
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Плевок одержимого мутанта - в прицел (указание 13.10: «у мутанта атака не целится»). Ванильный
|
||||
/// ItemActionVomit мутанта бросает снаряды веером под ноги (SpreadVerticalOffset -50, разброс 90° вбок) со
|
||||
/// скоростью 4.5 м/с - ИИ так поливает того, кто вплотную. Здесь после вылета скорость каждого снаряда
|
||||
/// пересчитывается навесом в Possession.SpitTarget, как ProjectileMoveScript.Fire делает для _isBallistic, с
|
||||
/// небольшим разбросом между снарядами очереди.</summary>
|
||||
[HarmonyPatch(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.Fire))]
|
||||
public static class Patch_ProjectileMoveScript_Fire_PossessedSpit
|
||||
{
|
||||
public static void Postfix(ProjectileMoveScript __instance, Vector3 _idealStartPos, Entity _firingEntity)
|
||||
{
|
||||
if (!Possession.IsPossessed(_firingEntity) || PossessionPowers.Current != PossessionPowers.Kind.Mutant ||
|
||||
__instance.itemActionProjectile == null || __instance.itemActionProjectile.FlyTime < 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vector3 from = _idealStartPos.y != 0f ? _idealStartPos : __instance.transform.position + Origin.position;
|
||||
Vector3 d = Possession.SpitTarget(from) + Random.insideUnitSphere * 0.4f - from;
|
||||
float t = Mathf.Clamp(d.magnitude / 12f, 0.2f, 1.2f);
|
||||
__instance.velocity = d / t;
|
||||
__instance.velocity.y -= __instance.gravity * 0.5f * t;
|
||||
__instance.flyDirection = __instance.velocity.normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user