r2mods/ilspy_dump/ror2_csproj/RoR2/BlastAttack.cs

473 lines
13 KiB
C#

using System;
using HG;
using RoR2.Networking;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Networking;
namespace RoR2;
public class BlastAttack
{
public enum FalloffModel
{
None,
Linear,
SweetSpot
}
public enum LoSType
{
None,
NearestHit
}
public struct HitPoint
{
public HurtBox hurtBox;
public Vector3 hitPosition;
public Vector3 hitNormal;
public float distanceSqr;
}
public struct Result
{
public int hitCount;
public HitPoint[] hitPoints;
}
private struct BlastAttackDamageInfo
{
public GameObject attacker;
public GameObject inflictor;
public bool crit;
public float damage;
public DamageColorIndex damageColorIndex;
public HurtBox.DamageModifier damageModifier;
public DamageTypeCombo damageType;
public Vector3 force;
public Vector3 position;
public ProcChainMask procChainMask;
public float procCoefficient;
public HealthComponent hitHealthComponent;
public bool canRejectForce;
public void Write(NetworkWriter writer)
{
writer.Write(attacker);
writer.Write(inflictor);
writer.Write(crit);
writer.Write(damage);
writer.Write(damageColorIndex);
writer.Write((byte)damageModifier);
writer.WriteDamageType(damageType);
writer.Write(force);
writer.Write(position);
writer.Write(procChainMask);
writer.Write(procCoefficient);
writer.Write(hitHealthComponent.netId);
writer.Write(canRejectForce);
}
public void Read(NetworkReader reader)
{
attacker = reader.ReadGameObject();
inflictor = reader.ReadGameObject();
crit = reader.ReadBoolean();
damage = reader.ReadSingle();
damageColorIndex = reader.ReadDamageColorIndex();
damageModifier = (HurtBox.DamageModifier)reader.ReadByte();
damageType = reader.ReadDamageType();
force = reader.ReadVector3();
position = reader.ReadVector3();
procChainMask = reader.ReadProcChainMask();
procCoefficient = reader.ReadSingle();
GameObject gameObject = reader.ReadGameObject();
hitHealthComponent = (gameObject ? gameObject.GetComponent<HealthComponent>() : null);
canRejectForce = reader.ReadBoolean();
}
}
public GameObject attacker;
public GameObject inflictor;
public TeamIndex teamIndex;
public AttackerFiltering attackerFiltering;
public Vector3 position;
public float radius;
public FalloffModel falloffModel = FalloffModel.Linear;
public float baseDamage;
public float baseForce;
public Vector3 bonusForce;
public bool crit;
public DamageTypeCombo damageType = DamageType.Generic;
public DamageColorIndex damageColorIndex;
public LoSType losType;
public EffectIndex impactEffect = EffectIndex.Invalid;
public bool canRejectForce = true;
public ProcChainMask procChainMask;
public float procCoefficient = 1f;
private static readonly int initialBufferSize = 256;
private static HitPoint[] hitPointsBuffer = new HitPoint[initialBufferSize];
private static int[] hitOrderBuffer = new int[initialBufferSize];
private static HealthComponent[] encounteredHealthComponentsBuffer = new HealthComponent[initialBufferSize];
public static event Action<HitPoint[], GameObject> BlastAttackMultiHit;
public Result Fire()
{
HitPoint[] array = CollectHits();
HandleHits(array);
Result result = default(Result);
result.hitCount = array.Length;
result.hitPoints = array;
return result;
}
[NetworkMessageHandler(msgType = 75, client = false, server = true)]
private static void HandleReportBlastAttackDamage(NetworkMessage netMsg)
{
NetworkReader reader = netMsg.reader;
BlastAttackDamageInfo blastAttackDamageInfo = default(BlastAttackDamageInfo);
blastAttackDamageInfo.Read(reader);
PerformDamageServer(in blastAttackDamageInfo);
}
public void Reset()
{
attacker = null;
inflictor = null;
teamIndex = TeamIndex.Neutral;
position = Vector3.zero;
radius = 0f;
falloffModel = FalloffModel.Linear;
baseDamage = 0f;
baseForce = 0f;
bonusForce = Vector3.zero;
crit = false;
damageType = DamageType.Generic;
damageColorIndex = DamageColorIndex.Default;
procChainMask.mask = 0u;
procCoefficient = 1f;
canRejectForce = true;
}
private HitPoint[] CollectHits()
{
Vector3 vector = position;
Collider[] array = Physics.OverlapSphere(vector, radius, LayerIndex.entityPrecise.mask);
int num = array.Length;
int count = 0;
int encounteredHealthComponentsLength = 0;
int hitOrderBufferLength = 0;
ArrayUtils.EnsureCapacity(ref hitPointsBuffer, num);
ArrayUtils.EnsureCapacity(ref hitOrderBuffer, num);
ArrayUtils.EnsureCapacity(ref encounteredHealthComponentsBuffer, num);
for (int i = 0; i < num; i++)
{
Collider collider = array[i];
HurtBox component = collider.GetComponent<HurtBox>();
if (!component)
{
continue;
}
HealthComponent healthComponent2 = component.healthComponent;
if (!healthComponent2)
{
continue;
}
bool flag = true;
switch (attackerFiltering)
{
case AttackerFiltering.Default:
flag = true;
break;
case AttackerFiltering.AlwaysHitSelf:
flag = true;
if (healthComponent2.gameObject == attacker)
{
flag = false;
}
break;
case AttackerFiltering.AlwaysHit:
flag = false;
break;
case AttackerFiltering.NeverHitSelf:
flag = true;
if (healthComponent2.gameObject == attacker)
{
continue;
}
break;
}
if (!flag || FriendlyFireManager.ShouldSplashHitProceed(healthComponent2, teamIndex))
{
Vector3 vector2 = collider.transform.position;
Vector3 hitNormal = vector - vector2;
float sqrMagnitude = hitNormal.sqrMagnitude;
hitPointsBuffer[count++] = new HitPoint
{
hurtBox = component,
hitPosition = vector2,
hitNormal = hitNormal,
distanceSqr = sqrMagnitude
};
}
}
if (true)
{
for (int j = 0; j < count; j++)
{
ref HitPoint reference = ref hitPointsBuffer[j];
if ((object)reference.hurtBox != null && reference.distanceSqr > 0f && reference.hurtBox.collider.Raycast(new Ray(vector, -reference.hitNormal), out var hitInfo, radius))
{
reference.distanceSqr = (vector - hitInfo.point).sqrMagnitude;
reference.hitPosition = hitInfo.point;
reference.hitNormal = hitInfo.normal;
}
}
}
hitOrderBufferLength = count;
for (int k = 0; k < count; k++)
{
hitOrderBuffer[k] = k;
}
for (int l = 1; l < count; l++)
{
int num2 = l;
while (num2 > 0 && hitPointsBuffer[hitOrderBuffer[num2 - 1]].distanceSqr > hitPointsBuffer[hitOrderBuffer[num2]].distanceSqr)
{
int num3 = hitOrderBuffer[num2 - 1];
hitOrderBuffer[num2 - 1] = hitOrderBuffer[num2];
hitOrderBuffer[num2] = num3;
num2--;
}
}
bool flag2 = losType == LoSType.None || losType == LoSType.NearestHit;
for (int m = 0; m < hitOrderBufferLength; m++)
{
int num4 = hitOrderBuffer[m];
ref HitPoint reference2 = ref hitPointsBuffer[num4];
HealthComponent healthComponent3 = reference2.hurtBox.healthComponent;
if (!EntityIsMarkedEncountered(healthComponent3))
{
MarkEntityAsEncountered(healthComponent3);
}
else if (flag2)
{
reference2.hurtBox = null;
}
}
ClearEncounteredEntities();
CondenseHitOrderBuffer();
LoSType loSType = losType;
if (loSType != 0 && loSType == LoSType.NearestHit)
{
NativeArray<RaycastCommand> commands = new NativeArray<RaycastCommand>(hitOrderBufferLength, Allocator.TempJob);
NativeArray<RaycastHit> results = new NativeArray<RaycastHit>(hitOrderBufferLength, Allocator.TempJob);
int n = 0;
int num5 = 0;
for (; n < hitOrderBufferLength; n++)
{
int num6 = hitOrderBuffer[n];
ref HitPoint reference3 = ref hitPointsBuffer[num6];
commands[num5++] = new RaycastCommand(vector, reference3.hitPosition - vector, Mathf.Sqrt(reference3.distanceSqr), LayerIndex.world.mask);
}
bool queriesHitTriggers = Physics.queriesHitTriggers;
Physics.queriesHitTriggers = true;
RaycastCommand.ScheduleBatch(commands, results, 1).Complete();
Physics.queriesHitTriggers = queriesHitTriggers;
int num7 = 0;
int num8 = 0;
for (; num7 < hitOrderBufferLength; num7++)
{
int num9 = hitOrderBuffer[num7];
ref HitPoint reference4 = ref hitPointsBuffer[num9];
if ((object)reference4.hurtBox != null && (bool)results[num8++].collider)
{
reference4.hurtBox = null;
}
}
results.Dispose();
commands.Dispose();
CondenseHitOrderBuffer();
}
HitPoint[] array2 = new HitPoint[hitOrderBufferLength];
for (int num10 = 0; num10 < hitOrderBufferLength; num10++)
{
int num11 = hitOrderBuffer[num10];
array2[num10] = hitPointsBuffer[num11];
}
ArrayUtils.Clear(hitPointsBuffer, ref count);
ClearEncounteredEntities();
return array2;
void ClearEncounteredEntities()
{
Array.Clear(encounteredHealthComponentsBuffer, 0, encounteredHealthComponentsLength);
encounteredHealthComponentsLength = 0;
}
void CondenseHitOrderBuffer()
{
for (int num12 = 0; num12 < hitOrderBufferLength; num12++)
{
int num13 = 0;
for (int num14 = num12; num14 < hitOrderBufferLength; num14++)
{
int num15 = hitOrderBuffer[num14];
if ((object)hitPointsBuffer[num15].hurtBox != null)
{
break;
}
num13++;
}
if (num13 > 0)
{
ArrayUtils.ArrayRemoveAt(hitOrderBuffer, ref hitOrderBufferLength, num12, num13);
}
}
}
bool EntityIsMarkedEncountered(HealthComponent healthComponent)
{
for (int num16 = 0; num16 < encounteredHealthComponentsLength; num16++)
{
if ((object)encounteredHealthComponentsBuffer[num16] == healthComponent)
{
return true;
}
}
return false;
}
void MarkEntityAsEncountered(HealthComponent healthComponent)
{
encounteredHealthComponentsBuffer[encounteredHealthComponentsLength++] = healthComponent;
}
}
private void HandleHits(HitPoint[] hitPoints)
{
Vector3 vector = position;
for (int i = 0; i < hitPoints.Length; i++)
{
HitPoint hitPoint = hitPoints[i];
float num = Mathf.Sqrt(hitPoint.distanceSqr);
float num2 = 0f;
Vector3 vector2 = ((num > 0f) ? ((hitPoint.hitPosition - vector) / num) : Vector3.zero);
HealthComponent healthComponent = (hitPoint.hurtBox ? hitPoint.hurtBox.healthComponent : null);
if ((bool)healthComponent)
{
switch (falloffModel)
{
case FalloffModel.None:
num2 = 1f;
break;
case FalloffModel.Linear:
num2 = 1f - Mathf.Clamp01(num / radius);
break;
case FalloffModel.SweetSpot:
num2 = 1f - ((num > radius / 2f) ? 0.75f : 0f);
break;
}
BlastAttackDamageInfo blastAttackDamageInfo = default(BlastAttackDamageInfo);
blastAttackDamageInfo.attacker = attacker;
blastAttackDamageInfo.inflictor = inflictor;
blastAttackDamageInfo.crit = crit;
blastAttackDamageInfo.damage = baseDamage * num2;
blastAttackDamageInfo.damageColorIndex = damageColorIndex;
blastAttackDamageInfo.damageModifier = hitPoint.hurtBox.damageModifier;
blastAttackDamageInfo.damageType = damageType | DamageType.AOE;
blastAttackDamageInfo.force = bonusForce * num2 + baseForce * num2 * vector2;
blastAttackDamageInfo.position = hitPoint.hitPosition;
blastAttackDamageInfo.procChainMask = procChainMask;
blastAttackDamageInfo.procCoefficient = procCoefficient;
blastAttackDamageInfo.hitHealthComponent = healthComponent;
blastAttackDamageInfo.canRejectForce = canRejectForce;
BlastAttackDamageInfo blastAttackDamageInfo2 = blastAttackDamageInfo;
if (NetworkServer.active)
{
PerformDamageServer(in blastAttackDamageInfo2);
}
else
{
ClientReportDamage(in blastAttackDamageInfo2);
}
if (impactEffect != EffectIndex.Invalid)
{
EffectData effectData = new EffectData();
effectData.origin = hitPoint.hitPosition;
effectData.rotation = Quaternion.LookRotation(-vector2);
EffectManager.SpawnEffect(impactEffect, effectData, transmit: true);
}
}
}
BlastAttack.BlastAttackMultiHit?.Invoke(hitPoints, attacker);
}
private static void ClientReportDamage(in BlastAttackDamageInfo blastAttackDamageInfo)
{
NetworkWriter networkWriter = new NetworkWriter();
networkWriter.StartMessage(75);
blastAttackDamageInfo.Write(networkWriter);
networkWriter.FinishMessage();
PlatformSystems.networkManager.client.connection.SendWriter(networkWriter, QosChannelIndex.defaultReliable.intVal);
}
private static void PerformDamageServer(in BlastAttackDamageInfo blastAttackDamageInfo)
{
if ((bool)blastAttackDamageInfo.hitHealthComponent)
{
DamageInfo damageInfo = new DamageInfo();
damageInfo.attacker = blastAttackDamageInfo.attacker;
damageInfo.inflictor = blastAttackDamageInfo.inflictor;
damageInfo.damage = blastAttackDamageInfo.damage;
damageInfo.crit = blastAttackDamageInfo.crit;
damageInfo.force = blastAttackDamageInfo.force;
damageInfo.procChainMask = blastAttackDamageInfo.procChainMask;
damageInfo.procCoefficient = blastAttackDamageInfo.procCoefficient;
damageInfo.damageType = blastAttackDamageInfo.damageType;
damageInfo.damageColorIndex = blastAttackDamageInfo.damageColorIndex;
damageInfo.position = blastAttackDamageInfo.position;
damageInfo.canRejectForce = blastAttackDamageInfo.canRejectForce;
damageInfo.ModifyDamageInfo(blastAttackDamageInfo.damageModifier);
blastAttackDamageInfo.hitHealthComponent.TakeDamage(damageInfo);
GlobalEventManager.instance.OnHitEnemy(damageInfo, blastAttackDamageInfo.hitHealthComponent.gameObject);
GlobalEventManager.instance.OnHitAll(damageInfo, blastAttackDamageInfo.hitHealthComponent.gameObject);
}
}
}