Upload from upload_mods.ps1
This commit is contained in:
786
Score/Fire/Scripts/FireManager.cs
Normal file
786
Score/Fire/Scripts/FireManager.cs
Normal file
@@ -0,0 +1,786 @@
|
||||
using Audio;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
/// <summary>
|
||||
/// SCore's FireManager allows flammable blocks to catch and spread fire.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Fire Manager allows blocks to catch on fire, and allows them to spread to other blocks around it at a timed interval.
|
||||
/// The following parameters are configured through the blocks.xml
|
||||
/// </remarks>
|
||||
|
||||
public class FireManager
|
||||
{
|
||||
private const string AdvFeatureClass = "FireManagement";
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
private static readonly ConcurrentDictionary<Vector3i, BlockValue>
|
||||
FireMap = new ConcurrentDictionary<Vector3i, BlockValue>();
|
||||
|
||||
private static readonly ConcurrentDictionary<Vector3i, float> ExtinguishPositions =
|
||||
new ConcurrentDictionary<Vector3i, float>();
|
||||
|
||||
private float _checkTime = 120f;
|
||||
private float _currentTime;
|
||||
private const float CheckTimeLights = 0.8f;
|
||||
private float _currentTimeLights;
|
||||
private float _fireDamage = 1f;
|
||||
|
||||
private float _smokeTime = 60f;
|
||||
private readonly GameRandom _random = GameManager.Instance.World.GetGameRandom();
|
||||
private bool _fireSpread = true;
|
||||
|
||||
|
||||
// Used to throttle sounds playing.
|
||||
private static readonly List<Vector3i> SoundPlaying = new List<Vector3i>();
|
||||
private static readonly List<Vector3i> ParticlePlaying = new List<Vector3i>();
|
||||
private const int MaxLights = 32;
|
||||
|
||||
private static readonly HashSet<Light> Shutoff =
|
||||
new HashSet<Light>();
|
||||
|
||||
private string _fireSound;
|
||||
private string _fireParticle;
|
||||
private string _smokeParticle;
|
||||
private const string SaveFile = "FireManager.dat";
|
||||
private ThreadManager.ThreadInfo _dataSaveThreadInfo;
|
||||
private float _chanceToExtinguish = 0.05f;
|
||||
public bool Enabled; // { private set; get; }
|
||||
|
||||
public static FireManager Instance { get; private set; }
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
Instance = new FireManager();
|
||||
Instance.ReadConfig();
|
||||
}
|
||||
|
||||
private void ReadConfig()
|
||||
{
|
||||
var fireSpreadString = "true";
|
||||
if (!StringParsers.ParseBool(fireSpreadString))
|
||||
{
|
||||
Debug.Log("Fire Spread Disabled.");
|
||||
_fireSpread = false;
|
||||
}
|
||||
|
||||
|
||||
Enabled = true;
|
||||
string option = "";
|
||||
bool optionFireManager = CustomGameOptions.GetBool("CustomFireManager");
|
||||
if (!optionFireManager)
|
||||
{
|
||||
Log.Out("Fire Manager is disabled.");
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
option = "20";
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
_checkTime = StringParsers.ParseFloat(option);
|
||||
|
||||
var strDamage = "50";
|
||||
if (!string.IsNullOrWhiteSpace(strDamage))
|
||||
_fireDamage = StringParsers.ParseFloat(strDamage);
|
||||
_currentTime = -1;
|
||||
|
||||
var smoke = "60";
|
||||
if (!string.IsNullOrWhiteSpace(smoke))
|
||||
_smokeTime = StringParsers.ParseFloat(smoke);
|
||||
|
||||
var strFireSound = "SCoreMediumLoop";
|
||||
if (!string.IsNullOrWhiteSpace(strFireSound))
|
||||
_fireSound = strFireSound;
|
||||
|
||||
if (_fireSound == "None")
|
||||
_fireSound = string.Empty;
|
||||
|
||||
Log.Out("Starting Fire Manager");
|
||||
|
||||
_fireParticle = "#@modfolder(zzz_REBIRTH__Utils):Resources/gupFireParticles.unity3d?gupBeavis05-Heavy";
|
||||
_smokeParticle = "#@modfolder(zzz_REBIRTH__Utils):Resources/PathSmoke.unity3d?P_PathSmoke_X";
|
||||
|
||||
var optionChanceToExtinguish = "0.05";
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
_chanceToExtinguish = StringParsers.ParseFloat(optionChanceToExtinguish);
|
||||
|
||||
// Read the FireManager
|
||||
Load();
|
||||
|
||||
// Only run the Update on the server, then just distribute the data to the clients using NetPackages.
|
||||
if (!SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer) return;
|
||||
Log.Out($" :: Fire Interval Check time: {_checkTime}");
|
||||
|
||||
ModEvents.GameUpdate.RegisterHandler(FireUpdate);
|
||||
ModEvents.GameUpdate.RegisterHandler(LightsUpdate);
|
||||
}
|
||||
|
||||
#region LightCode
|
||||
|
||||
private void LightsUpdate()
|
||||
{
|
||||
// Make sure to only run it once
|
||||
lock (Locker)
|
||||
{
|
||||
_currentTimeLights -= Time.deltaTime;
|
||||
if (_currentTimeLights > 0f) return;
|
||||
_currentTimeLights = CheckTimeLights;
|
||||
ShutoffLights();
|
||||
CheckLights();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void ShutoffLights()
|
||||
{
|
||||
if (Shutoff.Count == 0) return;
|
||||
if (GameManager.Instance.IsPaused()) return;
|
||||
Shutoff.RemoveWhere(light => light == null);
|
||||
foreach (var light in Shutoff)
|
||||
{
|
||||
light.intensity -= Time.deltaTime;
|
||||
// Log.Out("Toning down {0}", light.intensity);
|
||||
if (light.intensity > 0f) continue;
|
||||
// Log.Out("Culling light out of this world");
|
||||
var gameObject = light.gameObject;
|
||||
gameObject.transform.parent = null;
|
||||
UnityEngine.Object.Destroy(gameObject);
|
||||
}
|
||||
|
||||
Shutoff.RemoveWhere(light => light.intensity <= 0f);
|
||||
}
|
||||
|
||||
private static void CheckLights()
|
||||
{
|
||||
if (GameManager.Instance.IsPaused()) return;
|
||||
var allLights = UnityEngine.Object.FindObjectsOfType<Light>();
|
||||
if (allLights.Length <= MaxLights) return;
|
||||
var curLights = new List<Light>();
|
||||
for (var n = 0; n < allLights.Length; n += 1)
|
||||
{
|
||||
if (allLights[n].name != "FireLight") continue;
|
||||
if (Shutoff.Contains(allLights[n])) continue;
|
||||
curLights.Add(allLights[n]);
|
||||
}
|
||||
|
||||
// AdvLogging.DisplayLog(AdvFeatureClass,$"Detect currently {curLights.Count} lights, {Shutoff.Count} being culled");
|
||||
// Do nothing if we are (or will be) within limits
|
||||
if (curLights.Count <= MaxLights) return;
|
||||
// Otherwise choose more lights to shutoff
|
||||
while (curLights.Count >= MaxLights)
|
||||
{
|
||||
var idx = GameManager.Instance.World.GetGameRandom().RandomRange(curLights.Count);
|
||||
if (Shutoff.Contains(curLights[idx])) continue;
|
||||
// Log.Out("Selected {0} for culling", idx);
|
||||
Shutoff.Add(curLights[idx]);
|
||||
curLights.RemoveAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Poor man's timed cache
|
||||
private void CheckExtinguishedPosition()
|
||||
{
|
||||
var worldTime = GameManager.Instance.World.GetWorldTime();
|
||||
foreach (var position in ExtinguishPositions)
|
||||
{
|
||||
Remove(position.Key);
|
||||
if (!(position.Value < worldTime) &&
|
||||
!GameManager.Instance.World.GetBlock(position.Key + Vector3i.down).isair) continue;
|
||||
ExtinguishPositions.TryRemove(position.Key, out var _);
|
||||
ClearPos(position.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public ConcurrentDictionary<Vector3i, BlockValue> GetFireMap()
|
||||
{
|
||||
return FireMap;
|
||||
}
|
||||
|
||||
public int CloseFires(Vector3i position, int range = 5)
|
||||
{
|
||||
var count = 0;
|
||||
for (var x = position.x - range; x <= position.x + range; x++)
|
||||
{
|
||||
for (var z = position.z - range; z <= position.z + range; z++)
|
||||
{
|
||||
for (int y = position.y - 2; y <= position.y + 2; y++)
|
||||
{
|
||||
var localPosition = new Vector3i(x, y, z);
|
||||
if (IsBurning(localPosition)) count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public bool IsPositionCloseToFire(Vector3i position, int range = 5)
|
||||
{
|
||||
for (var x = position.x - range; x <= position.x + range; x++)
|
||||
{
|
||||
for (var z = position.z - range; z <= position.z + range; z++)
|
||||
{
|
||||
for (var y = position.y - 2; y <= position.y + 2; y++)
|
||||
{
|
||||
var localPosition = new Vector3i(x, y, z);
|
||||
if (IsBurning(localPosition)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private void FireUpdate()
|
||||
{
|
||||
// Make sure to only run it once
|
||||
lock (Locker)
|
||||
{
|
||||
_currentTime -= Time.deltaTime;
|
||||
if (_currentTime > 0f) return;
|
||||
|
||||
GameManager.Instance.StopCoroutine(CheckBlocks());
|
||||
GameManager.Instance.StartCoroutine(CheckBlocks());
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator CheckBlocks()
|
||||
{
|
||||
if (GameManager.Instance.IsPaused()) yield break;
|
||||
if (!GameManager.Instance.gameStateManager.IsGameStarted()) yield break;
|
||||
|
||||
//Log.Out(AdvFeatureClass,
|
||||
//$"Checking Blocks for Fire: {FireMap.Count} Blocks registered. Extinguished Blocks: {ExtinguishPositions.Count}");
|
||||
_currentTime = _checkTime;
|
||||
|
||||
|
||||
var changes = new List<BlockChangeInfo>();
|
||||
var neighbors = new List<Vector3i>();
|
||||
|
||||
CheckExtinguishedPosition();
|
||||
|
||||
var chunkCluster = GameManager.Instance.World.ChunkClusters[0];
|
||||
if (chunkCluster == null) yield break;
|
||||
|
||||
var counter = 0;
|
||||
|
||||
var rainfallValue = WeatherManager.Instance.GetCurrentRainfallValue();
|
||||
|
||||
foreach (var posDict in FireMap)
|
||||
{
|
||||
counter++;
|
||||
// only process 10 blocks per frame.
|
||||
if (counter % 10 == 0)
|
||||
yield return null;
|
||||
|
||||
var blockPos = posDict.Key;
|
||||
if (!IsFlammable(blockPos))
|
||||
{
|
||||
Remove(blockPos);
|
||||
continue;
|
||||
}
|
||||
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
// Get block specific damages
|
||||
var damage = (int)_fireDamage;
|
||||
if (block.Block.Properties.Contains("FireDamage"))
|
||||
damage = block.Block.Properties.GetInt("FireDamage");
|
||||
|
||||
if (block.Block.blockMaterial.Properties.Contains("FireDamage"))
|
||||
damage = block.Block.blockMaterial.Properties.GetInt("FireDamage");
|
||||
|
||||
if (block.Block.Properties.Contains("ChanceToExtinguish"))
|
||||
block.Block.Properties.ParseFloat("ChanceToExtinguish", ref _chanceToExtinguish);
|
||||
|
||||
block.damage += damage;
|
||||
|
||||
if (block.damage >= block.Block.MaxDamage)
|
||||
{
|
||||
block.Block.SpawnDestroyParticleEffect(GameManager.Instance.World, block, blockPos, 1f,
|
||||
block.Block.tintColor, -1);
|
||||
var blockValue2 = block.Block.DowngradeBlock;
|
||||
|
||||
if (block.Block.Properties.Values.ContainsKey("FireDowngradeBlock"))
|
||||
blockValue2 = Block.GetBlockValue(block.Block.Properties.Values["FireDowngradeBlock"]);
|
||||
|
||||
if (block.Block.Properties.Values.ContainsKey("Explosion.ParticleIndex") ||
|
||||
block.Block.Properties.Classes.ContainsKey("Explosion"))
|
||||
block.Block.OnBlockDestroyedByExplosion(GameManager.Instance.World, 0, blockPos, block, -1);
|
||||
|
||||
// Check if there's another placeholder for this block.
|
||||
if (!blockValue2.isair)
|
||||
blockValue2 = BlockPlaceholderMap.Instance.Replace(blockValue2,
|
||||
GameManager.Instance.World.GetGameRandom(), blockPos.x, blockPos.z);
|
||||
blockValue2.rotation = block.rotation;
|
||||
blockValue2.meta = block.meta;
|
||||
|
||||
block = blockValue2;
|
||||
}
|
||||
|
||||
if (!block.isair)
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageAddFirePosition>().Setup(blockPos, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageRemoveFirePosition>().Setup(blockPos, -1));
|
||||
}
|
||||
|
||||
changes.Add(new BlockChangeInfo(0, blockPos, block));
|
||||
|
||||
var rand = _random.RandomRange(0f, 1f);
|
||||
|
||||
var extinguishChange = _chanceToExtinguish;
|
||||
if (rainfallValue > 0.25)
|
||||
{
|
||||
extinguishChange = _chanceToExtinguish * 2f;
|
||||
}
|
||||
var blchanceToExtinguish = rand < extinguishChange;
|
||||
|
||||
// If the new block has changed, check to make sure the new block is flammable. Note: it checks the blockValue, not blockPos, since the change hasn't been committed yet.
|
||||
if (!IsFlammable(block) || block.isair || blchanceToExtinguish)
|
||||
{
|
||||
// queue up the change
|
||||
if (DynamicMeshManager.Instance != null)
|
||||
DynamicMeshManager.Instance.AddChunk(blockPos, true);
|
||||
|
||||
Extinguish(blockPos);
|
||||
continue;
|
||||
}
|
||||
|
||||
ToggleSound(blockPos, rand < 0.10);
|
||||
ToggleParticle(blockPos, true);
|
||||
|
||||
// If we are damaging a block, allow the fire to spread.
|
||||
if (_fireSpread)
|
||||
neighbors.AddRange(CheckNeighbors(blockPos));
|
||||
|
||||
FireMap[blockPos] = block;
|
||||
}
|
||||
|
||||
// Send all the changes in one shot
|
||||
if (changes.Count > 0)
|
||||
GameManager.Instance.SetBlocksRPC(changes);
|
||||
|
||||
if (_fireSpread == false) yield break;
|
||||
|
||||
// Spread the fire to the neighbors. We delay this here so the fire does not spread too quickly or immediately, getting stuck in the above loop.
|
||||
foreach (var pos in neighbors)
|
||||
Add(pos);
|
||||
|
||||
//Debug.Log($"Sound Counter: {SoundPlaying.Count}, Fire Counter: {FireMap.Count} Particle Count: {ParticlePlaying.Count}: Heat: {heatMeter}");
|
||||
}
|
||||
|
||||
private void ToggleSound(Vector3i blockPos, bool turnOn)
|
||||
{
|
||||
//if (SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer) return;
|
||||
if (!ThreadManager.IsMainThread()) return;
|
||||
// If there's a lot of fires going on, turn off the sounds.
|
||||
if (turnOn && FireMap.Count > 60)
|
||||
{
|
||||
turnOn = false;
|
||||
}
|
||||
|
||||
var sound = GetFireSound(blockPos);
|
||||
|
||||
if (string.IsNullOrEmpty(sound)) return;
|
||||
|
||||
if (turnOn)
|
||||
{
|
||||
if (SoundPlaying.Contains(blockPos))
|
||||
return;
|
||||
SoundPlaying.Add(blockPos);
|
||||
Manager.Play(blockPos, sound);
|
||||
return;
|
||||
}
|
||||
|
||||
// No sound?
|
||||
if (!SoundPlaying.Contains(blockPos)) return;
|
||||
Manager.Stop(blockPos, sound);
|
||||
SoundPlaying.Remove(blockPos);
|
||||
}
|
||||
|
||||
private void ToggleParticle(Vector3i blockPos, bool turnOn)
|
||||
{
|
||||
// if (SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer) return;
|
||||
if (!ThreadManager.IsMainThread()) return;
|
||||
|
||||
var randomFireParticle = GetRandomFireParticle(blockPos);
|
||||
if (turnOn)
|
||||
{
|
||||
if (ParticlePlaying.Contains(blockPos)) return;
|
||||
if (GameManager.Instance.HasBlockParticleEffect(blockPos)) return;
|
||||
ParticlePlaying.Add(blockPos);
|
||||
RebirthUtilities.addParticlesCentered(randomFireParticle, blockPos);
|
||||
return;
|
||||
}
|
||||
|
||||
ParticlePlaying.Remove(blockPos);
|
||||
RebirthUtilities.removeParticles(blockPos);
|
||||
}
|
||||
|
||||
private string GetFireSound(Vector3i blockPos)
|
||||
{
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
|
||||
var fireSound = _fireSound;
|
||||
if (block.Block.Properties.Contains("FireSound"))
|
||||
fireSound = block.Block.Properties.GetString("FireSound");
|
||||
return fireSound;
|
||||
}
|
||||
|
||||
private string GetRandomFireParticle(Vector3i blockPos)
|
||||
{
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
|
||||
var fireParticle = _fireParticle;
|
||||
if (block.Block.Properties.Contains("FireParticle"))
|
||||
fireParticle = block.Block.Properties.GetString("FireParticle");
|
||||
|
||||
if (block.Block.blockMaterial.Properties.Contains("FireParticle"))
|
||||
fireParticle = block.Block.blockMaterial.Properties.GetString("FireParticle");
|
||||
|
||||
var randomFire = ""; // Configuration.GetPropertyValue(AdvFeatureClass, "RandomFireParticle");
|
||||
if (string.IsNullOrEmpty(randomFire)) return fireParticle;
|
||||
var fireParticles = randomFire.Split(',');
|
||||
var randomIndex = _random.RandomRange(0, fireParticles.Length);
|
||||
fireParticle = fireParticles[randomIndex];
|
||||
|
||||
return fireParticle;
|
||||
}
|
||||
|
||||
private string GetRandomSmokeParticle(Vector3i blockPos)
|
||||
{
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
|
||||
var smokeParticle = _smokeParticle;
|
||||
if (block.Block.Properties.Contains("SmokeParticle"))
|
||||
smokeParticle = block.Block.Properties.GetString("SmokeParticle");
|
||||
|
||||
if (block.Block.blockMaterial.Properties.Contains("SmokeParticle"))
|
||||
smokeParticle = block.Block.blockMaterial.Properties.GetString("SmokeParticle");
|
||||
|
||||
var randomSmoke = ""; // Configuration.GetPropertyValue(AdvFeatureClass, "RandomSmokeParticle");
|
||||
if (string.IsNullOrEmpty(randomSmoke)) return smokeParticle;
|
||||
var smokeParticles = randomSmoke.Split(',');
|
||||
var randomIndex = GameManager.Instance.World.GetGameRandom().RandomRange(0, smokeParticles.Length);
|
||||
smokeParticle = smokeParticles[randomIndex];
|
||||
|
||||
return smokeParticle;
|
||||
}
|
||||
|
||||
// Check to see if the nearby blocks can catch fire.
|
||||
private static List<Vector3i> CheckNeighbors(Vector3i blockPos)
|
||||
{
|
||||
var neighbors = new List<Vector3i>();
|
||||
foreach (var direction in Vector3i.AllDirections)
|
||||
{
|
||||
var position = blockPos + direction;
|
||||
if (FireMap.ContainsKey(position))
|
||||
continue;
|
||||
if (IsFlammable(position))
|
||||
neighbors.Add(position);
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
private static bool IsNearWater(Vector3i blockPos)
|
||||
{
|
||||
foreach (var direction in Vector3i.AllDirections)
|
||||
{
|
||||
var position = blockPos + direction;
|
||||
var blockValue = GameManager.Instance.World.GetBlock(position);
|
||||
if (blockValue.isWater) return true;
|
||||
if (blockValue.Block is BlockLiquidv2) return true;
|
||||
// A21 water check.
|
||||
if (GameManager.Instance.World.GetWaterPercent(position) > 0.25f) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsFlammable(BlockValue blockValue)
|
||||
{
|
||||
if (blockValue.Block.HasAnyFastTags(FastTags<TagGroup.Global>.Parse("inflammable"))) return false;
|
||||
if (blockValue.ischild) return false;
|
||||
if (blockValue.isair) return false;
|
||||
if (blockValue.isWater) return false;
|
||||
|
||||
// if (blockValue.Block.Properties.Values.ContainsKey("Explosion.ParticleIndex")) return true;
|
||||
|
||||
if (blockValue.Block.HasAnyFastTags(FastTags<TagGroup.Global>.Parse("flammable"))) return true;
|
||||
var blockMaterial = blockValue.Block.blockMaterial;
|
||||
|
||||
var matID = "Mplants, Mcorn, Mhay"; // Configuration.GetPropertyValue(AdvFeatureClass, "MaterialID");
|
||||
if (matID.Contains(blockMaterial.id)) return true;
|
||||
|
||||
var matDamage = "wood, cloth, corn, grass, plastic, leaves, cactus, mushroom, hay, paper, trash, backpack, organic"; // Configuration.GetPropertyValue(AdvFeatureClass, "MaterialDamage");
|
||||
if (!string.IsNullOrEmpty(matDamage) && blockMaterial.DamageCategory != null)
|
||||
if (matDamage.Contains(blockMaterial.DamageCategory))
|
||||
return true;
|
||||
|
||||
var matSurface = "wood, cloth, corn, grass, plastic, leaves, cactus, mushroom, hay, paper, trash, backpack, organic"; // Configuration.GetPropertyValue(AdvFeatureClass, "MaterialSurface");
|
||||
if (string.IsNullOrEmpty(matSurface) || blockMaterial.SurfaceCategory == null) return false;
|
||||
return matSurface.Contains(blockMaterial.SurfaceCategory);
|
||||
}
|
||||
|
||||
private static bool IsFlammable(Vector3i blockPos)
|
||||
{
|
||||
if (GameManager.Instance.World.IsWithinTraderArea(blockPos)) return false;
|
||||
if (ExtinguishPositions.ContainsKey(blockPos)) return false;
|
||||
|
||||
// If its already burning, then don't do any other check
|
||||
if (IsBurning(blockPos)) return true;
|
||||
if (IsNearWater(blockPos)) return false;
|
||||
|
||||
// Check the block value.
|
||||
var blockValue = GameManager.Instance.World.GetBlock(blockPos);
|
||||
return IsFlammable(blockValue);
|
||||
}
|
||||
|
||||
private static void Write(BinaryWriter bw)
|
||||
{
|
||||
// Save the burning blocks.
|
||||
var writeOut = "";
|
||||
foreach (var temp in FireMap)
|
||||
writeOut += $"{temp.Key};";
|
||||
writeOut = writeOut.TrimEnd(';');
|
||||
bw.Write(writeOut);
|
||||
|
||||
// Save the blocks we've put out
|
||||
var writeOut2 = "";
|
||||
foreach (var temp in ExtinguishPositions.Keys)
|
||||
writeOut2 += $"{temp};";
|
||||
writeOut2 = writeOut2.TrimEnd(';');
|
||||
bw.Write(writeOut2);
|
||||
}
|
||||
|
||||
private void Read(BinaryReader br)
|
||||
{
|
||||
// Read burning blocks
|
||||
var positions = br.ReadString();
|
||||
foreach (var position in positions.Split(';'))
|
||||
{
|
||||
if (string.IsNullOrEmpty(position)) continue;
|
||||
var vector = StringParsers.ParseVector3i(position);
|
||||
AddBlock(vector);
|
||||
}
|
||||
|
||||
// Read extinguished blocks.
|
||||
var extingished = br.ReadString();
|
||||
foreach (var position in extingished.Split(';'))
|
||||
{
|
||||
if (string.IsNullOrEmpty(position)) continue;
|
||||
var vector = StringParsers.ParseVector3i(position);
|
||||
ExtinguishBlock(vector);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearPosOnly(Vector3i blockPos)
|
||||
{
|
||||
ToggleSound(blockPos, false);
|
||||
ToggleParticle(blockPos, false);
|
||||
|
||||
}
|
||||
public void ClearPos(Vector3i blockPos)
|
||||
{
|
||||
ClearPosOnly(blockPos);
|
||||
if (!SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer)
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendToServer(
|
||||
NetPackageManager.GetPackage<NetPackageRemoveParticleEffect>().Setup(blockPos, -1));
|
||||
return;
|
||||
}
|
||||
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageRemoveParticleEffect>().Setup(blockPos, -1));
|
||||
}
|
||||
|
||||
public void Add(Vector3i blockPos, int entityID = -1)
|
||||
{
|
||||
if (!IsFlammable(blockPos))
|
||||
return;
|
||||
|
||||
AddBlock(blockPos);
|
||||
|
||||
if (!SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer)
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendToServer(
|
||||
NetPackageManager.GetPackage<NetPackageAddFirePosition>().Setup(blockPos, entityID));
|
||||
return;
|
||||
}
|
||||
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageAddFirePosition>().Setup(blockPos, entityID));
|
||||
}
|
||||
|
||||
|
||||
// General call to remove the fire from a block, and add an extinguished counter, so blocks can be temporarily immune to restarting.
|
||||
public void Extinguish(Vector3i blockPos, int entityID = -1)
|
||||
{
|
||||
ExtinguishBlock(blockPos);
|
||||
if (!SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer)
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendToServer(
|
||||
NetPackageManager.GetPackage<NetPackageAddExtinguishPosition>().Setup(blockPos, entityID));
|
||||
return;
|
||||
}
|
||||
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageAddExtinguishPosition>().Setup(blockPos, entityID));
|
||||
}
|
||||
|
||||
public void Remove(Vector3i blockPos, int entityID = -1)
|
||||
{
|
||||
RemoveFire(blockPos);
|
||||
|
||||
if (!SingletonMonoBehaviour<ConnectionManager>.Instance.IsServer)
|
||||
{
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendToServer(
|
||||
NetPackageManager.GetPackage<NetPackageRemoveFirePosition>().Setup(blockPos, entityID));
|
||||
return;
|
||||
}
|
||||
|
||||
SingletonMonoBehaviour<ConnectionManager>.Instance.SendPackage(
|
||||
NetPackageManager.GetPackage<NetPackageRemoveFirePosition>().Setup(blockPos, entityID));
|
||||
}
|
||||
|
||||
|
||||
public void RemoveFire(Vector3i blockPos)
|
||||
{
|
||||
//if (!FireMap.ContainsKey(blockPos)) return;
|
||||
ToggleSound(blockPos, false);
|
||||
ToggleParticle(blockPos, false);
|
||||
FireMap.TryRemove(blockPos, out _);
|
||||
}
|
||||
|
||||
public void ExtinguishBlock(Vector3i blockPos)
|
||||
{
|
||||
//Log.Out("FireManager-ExtinguishBlock START");
|
||||
var worldTime = GameManager.Instance.World.GetWorldTime();
|
||||
var expiry = worldTime + _smokeTime;
|
||||
|
||||
// Seems like sometimes the dedicated and clients are out of sync, so this is a shot in the dark to see if we just skip the expired position check, and just
|
||||
// keep resetting the expired time.
|
||||
ExtinguishPositions[blockPos] = expiry;
|
||||
FireMap.TryRemove(blockPos, out _);
|
||||
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
ToggleSound(blockPos, false);
|
||||
|
||||
if (block.isair || !(_smokeTime > 0))
|
||||
{
|
||||
//Log.Out("FireManager-ExtinguishBlock 1");
|
||||
return;
|
||||
}
|
||||
var randomSmokeParticle = GetRandomSmokeParticle(blockPos);
|
||||
//Log.Out("FireManager-ExtinguishBlock randomSmokeParticle: " + randomSmokeParticle);
|
||||
RebirthUtilities.addParticlesCentered(randomSmokeParticle, blockPos);
|
||||
}
|
||||
|
||||
|
||||
// Add flammable blocks to the Fire Map
|
||||
public void AddBlock(Vector3i blockPos)
|
||||
{
|
||||
var block = GameManager.Instance.World.GetBlock(blockPos);
|
||||
if (!FireMap.TryAdd(blockPos, block)) return;
|
||||
|
||||
ToggleSound(blockPos, true);
|
||||
ToggleParticle(blockPos, true);
|
||||
}
|
||||
|
||||
public static bool IsBurning(Vector3i blockPos)
|
||||
{
|
||||
return Instance.Enabled && FireMap.ContainsKey(blockPos);
|
||||
}
|
||||
|
||||
private int SaveDataThreaded(ThreadManager.ThreadInfo threadInfo)
|
||||
{
|
||||
var pooledExpandableMemoryStream =
|
||||
(PooledExpandableMemoryStream)threadInfo.parameter;
|
||||
var text = $"{GameIO.GetSaveGameDir()}/{SaveFile}";
|
||||
if (File.Exists(text))
|
||||
File.Copy(text, $"{GameIO.GetSaveGameDir()}/{SaveFile}.bak", true);
|
||||
|
||||
pooledExpandableMemoryStream.Position = 0L;
|
||||
StreamUtils.WriteStreamToFile(pooledExpandableMemoryStream, text);
|
||||
Log.Out("FireManager saved {0} bytes", new object[]
|
||||
{
|
||||
pooledExpandableMemoryStream.Length
|
||||
});
|
||||
MemoryPools.poolMemoryStream.FreeSync(pooledExpandableMemoryStream);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void Save()
|
||||
{
|
||||
if (_dataSaveThreadInfo == null || !ThreadManager.ActiveThreads.ContainsKey("silent_FireDataSave"))
|
||||
{
|
||||
Log.Out($"FireManager saving {FireMap.Count} Fires...");
|
||||
var pooledExpandableMemoryStream = MemoryPools.poolMemoryStream.AllocSync(true);
|
||||
using (var pooledBinaryWriter = MemoryPools.poolBinaryWriter.AllocSync(false))
|
||||
{
|
||||
pooledBinaryWriter.SetBaseStream(pooledExpandableMemoryStream);
|
||||
Write(pooledBinaryWriter);
|
||||
}
|
||||
|
||||
_dataSaveThreadInfo = ThreadManager.StartThread("silent_FireDataSave", null,
|
||||
SaveDataThreaded, null,
|
||||
System.Threading.ThreadPriority.Normal, pooledExpandableMemoryStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Out("Not Saving. Thread still running?");
|
||||
}
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
var path = $"{GameIO.GetSaveGameDir()}/{SaveFile}";
|
||||
if (!Directory.Exists(GameIO.GetSaveGameDir()) || !File.Exists(path)) return;
|
||||
|
||||
try
|
||||
{
|
||||
using var fileStream = File.OpenRead(path);
|
||||
using var pooledBinaryReader = MemoryPools.poolBinaryReader.AllocSync(false);
|
||||
pooledBinaryReader.SetBaseStream(fileStream);
|
||||
Read(pooledBinaryReader);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
path = $"{GameIO.GetSaveGameDir()}/{SaveFile}.bak";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
using var fileStream2 = File.OpenRead(path);
|
||||
using var pooledBinaryReader2 = MemoryPools.poolBinaryReader.AllocSync(false);
|
||||
pooledBinaryReader2.SetBaseStream(fileStream2);
|
||||
Read(pooledBinaryReader2);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Out($"Fire Manager {path} Loaded: {FireMap.Count}");
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
//Log.Out("Removing all blocks that are on fire and smoke.");
|
||||
lock (Locker)
|
||||
{
|
||||
foreach (var position in FireMap.Keys)
|
||||
Remove(position);
|
||||
|
||||
foreach (var position in ExtinguishPositions.Keys)
|
||||
RebirthUtilities.removeParticles(position);
|
||||
|
||||
FireMap.Clear();
|
||||
ExtinguishPositions.Clear();
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user