Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs
2019-03-27 20:52:47 +02:00

354 lines
12 KiB
C#

using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
#if CLIENT
using Barotrauma.Sounds;
using Barotrauma.Lights;
using Barotrauma.Particles;
#endif
namespace Barotrauma
{
partial class FireSource
{
const float OxygenConsumption = 50.0f;
const float GrowSpeed = 5.0f;
private Hull hull;
private Vector2 position;
private Vector2 size;
private Entity Submarine;
private bool removed;
#if CLIENT
private List<Decal> burnDecals = new List<Decal>();
#endif
public Vector2 Position
{
get { return position; }
set
{
if (!MathUtils.IsValid(value)) return;
position = value;
}
}
public Vector2 WorldPosition
{
get { return Submarine == null ? position : Submarine.Position + position; }
}
public Vector2 Size
{
get { return size; }
set
{
if (value == size) return;
Vector2 sizeChange = value - size;
size = value;
position.X -= sizeChange.X * 0.5f;
LimitSize();
}
}
public float DamageRange
{
get { return (float)Math.Sqrt(size.X) * 20.0f; }
}
public Hull Hull
{
get { return hull; }
}
public FireSource(Vector2 worldPosition, Hull spawningHull = null, bool isNetworkMessage = false)
{
hull = Hull.FindHull(worldPosition, spawningHull);
if (hull == null || worldPosition.Y < hull.WorldSurface) return;
#if CLIENT
if (!isNetworkMessage && GameMain.Client != null) return;
#endif
hull.AddFireSource(this);
position = worldPosition - new Vector2(-5.0f, 5.0f);
if (hull.Submarine != null)
{
Submarine = hull.Submarine;
position -= Submarine.Position;
}
#if CLIENT
lightSource = new LightSource(this.position, 50.0f, new Color(1.0f, 0.9f, 0.7f), hull?.Submarine);
#endif
size = new Vector2(10.0f, 10.0f);
}
private void LimitSize()
{
if (hull == null) return;
position.X = Math.Max(hull.Rect.X, position.X);
position.Y = Math.Min(hull.Rect.Y, position.Y);
size.X = Math.Min(hull.Rect.Width - (position.X - hull.Rect.X), size.X);
size.Y = Math.Min(hull.Rect.Height - (hull.Rect.Y - position.Y), size.Y);
}
public static void UpdateAll(List<FireSource> fireSources, float deltaTime)
{
for (int i = fireSources.Count - 1; i >= 0; i--)
{
fireSources[i].Update(deltaTime);
}
//combine overlapping fires
for (int i = fireSources.Count - 1; i >= 0; i--)
{
for (int j = i - 1; j >= 0; j--)
{
i = Math.Min(i, fireSources.Count - 1);
j = Math.Min(j, i - 1);
if (!fireSources[i].CheckOverLap(fireSources[j])) continue;
float leftEdge = Math.Min(fireSources[i].position.X, fireSources[j].position.X);
fireSources[j].size.X =
Math.Max(fireSources[i].position.X + fireSources[i].size.X, fireSources[j].position.X + fireSources[j].size.X)
- leftEdge;
fireSources[j].position.X = leftEdge;
#if CLIENT
fireSources[j].burnDecals.AddRange(fireSources[i].burnDecals);
fireSources[j].burnDecals.Sort((d1, d2) => { return Math.Sign(d1.WorldPosition.X - d2.WorldPosition.X); });
#endif
fireSources[i].Remove();
}
}
}
private bool CheckOverLap(FireSource fireSource)
{
return !(position.X > fireSource.position.X + fireSource.size.X ||
position.X + size.X < fireSource.position.X);
}
public void Update(float deltaTime)
{
//the firesource will start to shrink if oxygen percentage is below 10
float growModifier = Math.Min((hull.OxygenPercentage / 10.0f) - 1.0f, 1.0f);
DamageCharacters(deltaTime);
DamageItems(deltaTime);
if (hull.WaterVolume > 0.0f)
{
HullWaterExtinguish(deltaTime);
if (removed) { return; }
}
hull.Oxygen -= size.X * deltaTime * OxygenConsumption;
position.X -= GrowSpeed * growModifier * 0.5f * deltaTime;
size.X += GrowSpeed * growModifier * deltaTime;
size.Y = MathHelper.Clamp(size.Y + GrowSpeed * growModifier * deltaTime, 10.0f, 50.0f);
if (size.X > 50.0f)
{
this.position.Y = MathHelper.Lerp(this.position.Y, hull.Rect.Y - hull.Rect.Height + size.Y, deltaTime);
}
LimitSize();
UpdateProjSpecific(growModifier);
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
}
partial void UpdateProjSpecific(float growModifier);
private void OnChangeHull(Vector2 pos, Hull particleHull)
{
if (particleHull == hull || particleHull == null) return;
//hull already has a firesource roughly at the particles position -> don't create a new one
if (particleHull.FireSources.Find(fs => pos.X > fs.position.X - 100.0f && pos.X < fs.position.X + fs.size.X + 100.0f) != null) return;
new FireSource(new Vector2(pos.X, particleHull.WorldRect.Y - particleHull.Rect.Height + 5.0f));
}
private void DamageCharacters(float deltaTime)
{
if (size.X <= 0.0f) return;
for (int i = 0; i < Character.CharacterList.Count; i++)
{
Character c = Character.CharacterList[i];
if (c.AnimController.CurrentHull == null || c.IsDead) continue;
if (!IsInDamageRange(c, DamageRange)) continue;
float dmg = (float)Math.Sqrt(size.X) * deltaTime / c.AnimController.Limbs.Length;
foreach (Limb limb in c.AnimController.Limbs)
{
c.LastDamageSource = null;
c.DamageLimb(WorldPosition, limb, new List<Affliction>() { AfflictionPrefab.Burn.Instantiate(dmg) }, 0.0f, false, 0.0f);
}
c.ApplyStatusEffects(ActionType.OnFire, deltaTime);
}
}
public bool IsInDamageRange(Character c, float damageRange)
{
if (c.Position.X < position.X - damageRange || c.Position.X > position.X + size.X + damageRange) return false;
if (c.Position.Y < position.Y - size.Y || c.Position.Y > hull.Rect.Y) return false;
return true;
}
public bool IsInDamageRange(Vector2 worldPosition, float damageRange)
{
if (worldPosition.X < WorldPosition.X - damageRange || worldPosition.X > WorldPosition.X + size.X + damageRange) return false;
if (worldPosition.Y < WorldPosition.Y - size.Y || worldPosition.Y > hull.WorldRect.Y) return false;
return true;
}
private void DamageItems(float deltaTime)
{
if (size.X <= 0.0f) return;
#if CLIENT
if (GameMain.Client != null) return;
#endif
foreach (Item item in Item.ItemList)
{
if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f) continue;
//don't apply OnFire effects if the item is inside a fireproof container
//(or if it's inside a container that's inside a fireproof container, etc)
Item container = item.Container;
while (container != null)
{
if (container.FireProof) return;
container = container.Container;
}
float range = (float)Math.Sqrt(size.X) * 10.0f;
if (item.Position.X < position.X - range || item.Position.X > position.X + size.X + range) continue;
if (item.Position.Y < position.Y - size.Y || item.Position.Y > hull.Rect.Y) continue;
item.ApplyStatusEffects(ActionType.OnFire, deltaTime);
if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
}
}
}
private void HullWaterExtinguish(float deltaTime)
{
//the higher the surface of the water is relative to the firesource, the faster it puts out the fire
float extinguishAmount = (hull.Surface - (position.Y - size.Y)) * deltaTime;
if (extinguishAmount < 0.0f) return;
#if CLIENT
float steamCount = Rand.Range(-5.0f, Math.Min(extinguishAmount * 100.0f, 10));
for (int i = 0; i < steamCount; i++)
{
Vector2 spawnPos = new Vector2(
WorldPosition.X + Rand.Range(0.0f, size.X),
WorldPosition.Y + 10.0f);
Vector2 speed = new Vector2((spawnPos.X - (WorldPosition.X + size.X / 2.0f)), (float)Math.Sqrt(size.X) * Rand.Range(20.0f, 25.0f));
var particle = GameMain.ParticleManager.CreateParticle("steam",
spawnPos, speed, 0.0f, hull);
}
#endif
extinguishAmount = Math.Min(size.X, extinguishAmount);
position.X += extinguishAmount / 2.0f;
size.X -= extinguishAmount;
//evaporate some of the water
hull.WaterVolume -= extinguishAmount;
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
}
public void Extinguish(float deltaTime, float amount)
{
float extinguishAmount = amount * deltaTime;
#if CLIENT
float steamCount = Rand.Range(-5.0f, (float)Math.Sqrt(amount));
for (int i = 0; i < steamCount; i++)
{
Vector2 spawnPos = new Vector2(Rand.Range(position.X, position.X + size.X), Rand.Range(position.Y - size.Y, position.Y) + 10.0f);
Vector2 speed = new Vector2((spawnPos.X - (position.X + size.X / 2.0f)), (float)Math.Sqrt(size.X) * Rand.Range(20.0f, 25.0f));
var particle = GameMain.ParticleManager.CreateParticle("steam",
spawnPos, speed, 0.0f, hull);
}
#endif
extinguishAmount = Math.Min(size.X, extinguishAmount);
position.X += extinguishAmount / 2.0f;
size.X -= extinguishAmount;
hull.WaterVolume -= extinguishAmount;
if (size.X < 1.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
{
Remove();
}
}
public void Extinguish(float deltaTime, float amount, Vector2 worldPosition)
{
if (IsInDamageRange(worldPosition, 100.0f))
{
Extinguish(deltaTime, amount);
}
}
public void Remove()
{
#if CLIENT
lightSource?.Remove();
lightSource = null;
foreach (Decal d in burnDecals)
{
d.StopFadeIn();
}
#endif
hull?.RemoveFire(this);
removed = true;
}
}
}