247 lines
6.0 KiB
C#
247 lines
6.0 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using RoR2.Navigation;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Events;
|
||
|
using UnityEngine.Networking;
|
||
|
|
||
|
namespace RoR2;
|
||
|
|
||
|
public class MeteorStormController : MonoBehaviour
|
||
|
{
|
||
|
private class Meteor
|
||
|
{
|
||
|
public Vector3 impactPosition;
|
||
|
|
||
|
public float startTime;
|
||
|
|
||
|
public bool didTravelEffect;
|
||
|
|
||
|
public bool valid = true;
|
||
|
}
|
||
|
|
||
|
private class MeteorWave
|
||
|
{
|
||
|
private readonly CharacterBody[] targets;
|
||
|
|
||
|
private int currentStep;
|
||
|
|
||
|
private float hitChance = 0.4f;
|
||
|
|
||
|
private readonly Vector3 center;
|
||
|
|
||
|
public float timer;
|
||
|
|
||
|
private NodeGraphSpider nodeGraphSpider;
|
||
|
|
||
|
public MeteorWave(CharacterBody[] targets, Vector3 center)
|
||
|
{
|
||
|
this.targets = new CharacterBody[targets.Length];
|
||
|
targets.CopyTo(this.targets, 0);
|
||
|
Util.ShuffleArray(targets);
|
||
|
this.center = center;
|
||
|
nodeGraphSpider = new NodeGraphSpider(SceneInfo.instance.groundNodes, HullMask.Human);
|
||
|
nodeGraphSpider.AddNodeForNextStep(SceneInfo.instance.groundNodes.FindClosestNode(center, HullClassification.Human));
|
||
|
int i = 0;
|
||
|
for (int num = 20; i < num; i++)
|
||
|
{
|
||
|
if (!nodeGraphSpider.PerformStep())
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Meteor GetNextMeteor()
|
||
|
{
|
||
|
if (currentStep >= targets.Length)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
CharacterBody characterBody = targets[currentStep];
|
||
|
Meteor meteor = new Meteor();
|
||
|
if ((bool)characterBody && UnityEngine.Random.value < hitChance)
|
||
|
{
|
||
|
meteor.impactPosition = characterBody.corePosition;
|
||
|
Vector3 origin = meteor.impactPosition + Vector3.up * 6f;
|
||
|
Vector3 onUnitSphere = UnityEngine.Random.onUnitSphere;
|
||
|
onUnitSphere.y = -1f;
|
||
|
if (Physics.Raycast(origin, onUnitSphere, out var hitInfo, 12f, LayerIndex.world.mask, QueryTriggerInteraction.Ignore))
|
||
|
{
|
||
|
meteor.impactPosition = hitInfo.point;
|
||
|
}
|
||
|
else if (Physics.Raycast(meteor.impactPosition, Vector3.down, out hitInfo, float.PositiveInfinity, LayerIndex.world.mask, QueryTriggerInteraction.Ignore))
|
||
|
{
|
||
|
meteor.impactPosition = hitInfo.point;
|
||
|
}
|
||
|
}
|
||
|
else if (nodeGraphSpider.collectedSteps.Count != 0)
|
||
|
{
|
||
|
int index = UnityEngine.Random.Range(0, nodeGraphSpider.collectedSteps.Count);
|
||
|
SceneInfo.instance.groundNodes.GetNodePosition(nodeGraphSpider.collectedSteps[index].node, out meteor.impactPosition);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
meteor.valid = false;
|
||
|
}
|
||
|
meteor.startTime = Run.instance.time;
|
||
|
currentStep++;
|
||
|
return meteor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int waveCount;
|
||
|
|
||
|
public float waveMinInterval;
|
||
|
|
||
|
public float waveMaxInterval;
|
||
|
|
||
|
public GameObject warningEffectPrefab;
|
||
|
|
||
|
public GameObject travelEffectPrefab;
|
||
|
|
||
|
public float travelEffectDuration;
|
||
|
|
||
|
public GameObject impactEffectPrefab;
|
||
|
|
||
|
public float impactDelay;
|
||
|
|
||
|
public float blastDamageCoefficient;
|
||
|
|
||
|
public float blastRadius;
|
||
|
|
||
|
public float blastForce;
|
||
|
|
||
|
public DamageTypeCombo blastDamageType;
|
||
|
|
||
|
[NonSerialized]
|
||
|
public GameObject owner;
|
||
|
|
||
|
[NonSerialized]
|
||
|
public float ownerDamage;
|
||
|
|
||
|
[NonSerialized]
|
||
|
public bool isCrit;
|
||
|
|
||
|
public UnityEvent onDestroyEvents;
|
||
|
|
||
|
private List<Meteor> meteorList;
|
||
|
|
||
|
private List<MeteorWave> waveList;
|
||
|
|
||
|
private int wavesPerformed;
|
||
|
|
||
|
private float waveTimer;
|
||
|
|
||
|
private void Start()
|
||
|
{
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
meteorList = new List<Meteor>();
|
||
|
waveList = new List<MeteorWave>();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void FixedUpdate()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
waveTimer -= Time.fixedDeltaTime;
|
||
|
if (waveTimer <= 0f && wavesPerformed < waveCount)
|
||
|
{
|
||
|
wavesPerformed++;
|
||
|
waveTimer = UnityEngine.Random.Range(waveMinInterval, waveMaxInterval);
|
||
|
MeteorWave item = new MeteorWave(CharacterBody.readOnlyInstancesList.ToArray(), base.transform.position);
|
||
|
waveList.Add(item);
|
||
|
}
|
||
|
for (int num = waveList.Count - 1; num >= 0; num--)
|
||
|
{
|
||
|
MeteorWave meteorWave = waveList[num];
|
||
|
meteorWave.timer -= Time.fixedDeltaTime;
|
||
|
if (meteorWave.timer <= 0f)
|
||
|
{
|
||
|
meteorWave.timer = UnityEngine.Random.Range(0.05f, 1f);
|
||
|
Meteor nextMeteor = meteorWave.GetNextMeteor();
|
||
|
if (nextMeteor == null)
|
||
|
{
|
||
|
waveList.RemoveAt(num);
|
||
|
}
|
||
|
else if (nextMeteor.valid)
|
||
|
{
|
||
|
meteorList.Add(nextMeteor);
|
||
|
EffectManager.SpawnEffect(warningEffectPrefab, new EffectData
|
||
|
{
|
||
|
origin = nextMeteor.impactPosition,
|
||
|
scale = blastRadius
|
||
|
}, transmit: true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
float num2 = Run.instance.time - impactDelay;
|
||
|
float num3 = num2 - travelEffectDuration;
|
||
|
for (int num4 = meteorList.Count - 1; num4 >= 0; num4--)
|
||
|
{
|
||
|
Meteor meteor = meteorList[num4];
|
||
|
if (meteor.startTime < num3 && !meteor.didTravelEffect)
|
||
|
{
|
||
|
DoMeteorEffect(meteor);
|
||
|
}
|
||
|
if (meteor.startTime < num2)
|
||
|
{
|
||
|
meteorList.RemoveAt(num4);
|
||
|
DetonateMeteor(meteor);
|
||
|
}
|
||
|
}
|
||
|
if (wavesPerformed == waveCount && meteorList.Count == 0)
|
||
|
{
|
||
|
UnityEngine.Object.Destroy(base.gameObject);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnDestroy()
|
||
|
{
|
||
|
onDestroyEvents.Invoke();
|
||
|
}
|
||
|
|
||
|
private void DoMeteorEffect(Meteor meteor)
|
||
|
{
|
||
|
meteor.didTravelEffect = true;
|
||
|
if ((bool)travelEffectPrefab)
|
||
|
{
|
||
|
EffectManager.SpawnEffect(travelEffectPrefab, new EffectData
|
||
|
{
|
||
|
origin = meteor.impactPosition
|
||
|
}, transmit: true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DetonateMeteor(Meteor meteor)
|
||
|
{
|
||
|
EffectData effectData = new EffectData
|
||
|
{
|
||
|
origin = meteor.impactPosition
|
||
|
};
|
||
|
EffectManager.SpawnEffect(impactEffectPrefab, effectData, transmit: true);
|
||
|
BlastAttack blastAttack = new BlastAttack();
|
||
|
blastAttack.inflictor = base.gameObject;
|
||
|
blastAttack.baseDamage = blastDamageCoefficient * ownerDamage;
|
||
|
blastAttack.baseForce = blastForce;
|
||
|
blastAttack.attackerFiltering = AttackerFiltering.AlwaysHit;
|
||
|
blastAttack.crit = isCrit;
|
||
|
blastAttack.falloffModel = BlastAttack.FalloffModel.Linear;
|
||
|
blastAttack.attacker = owner;
|
||
|
blastAttack.bonusForce = Vector3.zero;
|
||
|
blastAttack.damageColorIndex = DamageColorIndex.Item;
|
||
|
blastAttack.position = meteor.impactPosition;
|
||
|
blastAttack.procChainMask = default(ProcChainMask);
|
||
|
blastAttack.procCoefficient = 1f;
|
||
|
blastAttack.teamIndex = TeamIndex.None;
|
||
|
blastAttack.radius = blastRadius;
|
||
|
blastAttack.damageType = blastDamageType;
|
||
|
blastAttack.Fire();
|
||
|
}
|
||
|
}
|