Files
2025-07-06 00:23:46 +02:00

150 lines
6.1 KiB
C#

using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
using NitroxClient.Communication.Packets.Processors;
using NitroxClient.MonoBehaviours;
using NitroxModel.DataStructures;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures.GameLogic;
using NitroxModel_Subnautica.Packets;
using UnityEngine;
namespace NitroxClient.GameLogic
{
/// <summary>
/// Handles all of the <see cref="Fire"/>s in the game. Currently, the only known Fire spawning is in <see cref="SubFire.CreateFire(SubFire.RoomFire)"/>. The
/// fires in the Aurora come loaded with the map and do not grow in size. If we want to create a Fire spawning mechanic outside of Cyclops fires, it should be
/// added to <see cref="Fires.Create(CyclopsFireData)"/>. Fire dousing goes by Id and does not need to be
/// modified
/// </summary>
public class Fires
{
private readonly IPacketSender packetSender;
/// <summary>
/// Used to reduce the <see cref="FireDoused"/> packet spam as fires are being doused. A packet is only sent after
/// the douse amount surpasses <see cref="FIRE_DOUSE_AMOUNT_TRIGGER"/>
/// </summary>
private readonly Dictionary<NitroxId, float> fireDouseAmount = new Dictionary<NitroxId, float>();
/// <summary>
/// Each extinguisher hit is from 0.15 to 0.25. 5 is a bit less than half a second of full extinguishing
/// </summary>
private const float FIRE_DOUSE_AMOUNT_TRIGGER = 5f;
public Fires(IPacketSender packetSender)
{
this.packetSender = packetSender;
}
/// <summary>
/// Triggered when <see cref="SubFire.CreateFire(SubFire.RoomFire)"/> is executed. To create a new fire manually,
/// call <see cref="Create(CyclopsFireData)"/>
/// </summary>
public void OnCreate(Fire fire, SubFire.RoomFire room, int nodeIndex)
{
if (!fire.TryGetIdOrWarn(out NitroxId fireId))
{
return;
}
if (!fire.fireSubRoot.TryGetIdOrWarn(out NitroxId subRootId))
{
return;
}
CyclopsFireCreated packet = new CyclopsFireCreated(fireId, subRootId, room.roomLinks.room, nodeIndex);
packetSender.Send(packet);
}
/// <summary>
/// Triggered when <see cref="Fire.Douse(float)"/> is executed. To Douse a fire manually, retrieve the <see cref="Fire"/> call the Douse method
/// </summary>
public void OnDouse(Fire fire, float douseAmount)
{
if (!fire.TryGetIdOrWarn(out NitroxId fireId))
{
return;
}
// Temporary packet limiter
if (!fireDouseAmount.ContainsKey(fireId))
{
fireDouseAmount.Add(fireId, douseAmount);
}
else
{
float summedDouseAmount = fireDouseAmount[fireId] + douseAmount;
if (summedDouseAmount > FIRE_DOUSE_AMOUNT_TRIGGER)
{
// It is significantly faster to keep the key as a 0 value than to remove it and re-add it later.
fireDouseAmount[fireId] = 0;
FireDoused packet = new FireDoused(fireId, douseAmount);
packetSender.Send(packet);
}
}
}
/// <summary>
/// Create a new <see cref="Fire"/>. Majority of code copied from <see cref="SubFire.CreateFire(SubFire.RoomFire)"/>. Currently does not support Fires created outside of a Cyclops
/// </summary>
public void Create(CyclopsFireData fireData)
{
SubFire subFire = NitroxEntity.RequireObjectFrom(fireData.CyclopsId).GetComponent<SubRoot>().damageManager.subFire;
Dictionary<CyclopsRooms, SubFire.RoomFire> roomFiresDict = subFire.roomFires;
// Copied from SubFire_CreateFire_Patch, which copies from SubFire.CreateFire()
Transform transform2 = roomFiresDict[fireData.Room].spawnNodes[fireData.NodeIndex];
// If a fire already exists at the node, replace the old Id with the new one
if (transform2.childCount > 0)
{
Fire existingFire = transform2.GetComponentInChildren<Fire>();
if (existingFire.TryGetNitroxId(out NitroxId existingFireId) && existingFireId != fireData.CyclopsId)
{
Log.Error($"[Fires.Create Fire already exists at node index {fireData.NodeIndex}! Replacing existing Fire Id {existingFireId} with Id {fireData.CyclopsId}]");
NitroxEntity.SetNewId(existingFire.gameObject, fireData.CyclopsId);
}
return;
}
List<Transform> availableNodes = subFire.availableNodes;
availableNodes.Clear();
foreach (Transform transform in roomFiresDict[fireData.Room].spawnNodes)
{
if (transform.childCount == 0)
{
availableNodes.Add(transform);
}
}
roomFiresDict[fireData.Room].fireValue++;
PrefabSpawn component = transform2.GetComponent<PrefabSpawn>();
if (!component)
{
return;
}
else
{
Log.Error(
$"[{nameof(CyclopsFireCreatedProcessor)} Cannot create new Cyclops fire! PrefabSpawn component could not be found in fire node! Fire Id: {fireData.FireId} SubRoot Id: {fireData.CyclopsId} Room: {fireData.Room} NodeIndex: {fireData.NodeIndex}]");
}
component.SpawnManual(delegate(GameObject fireGO)
{
Fire componentInChildren = fireGO.GetComponentInChildren<Fire>();
if (componentInChildren)
{
componentInChildren.fireSubRoot = subFire.subRoot;
NitroxEntity.SetNewId(componentInChildren.gameObject, fireData.FireId);
}
});
subFire.roomFires = roomFiresDict;
subFire.availableNodes = availableNodes;
}
}
}