255 lines
8.8 KiB
C#
255 lines
8.8 KiB
C#
using Barotrauma.Networking;
|
|
using FarseerPhysics;
|
|
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
partial class LevelObject : ISpatialEntity, IDamageable, ISerializableEntity
|
|
{
|
|
public readonly LevelObjectPrefab Prefab;
|
|
public Vector3 Position { get; set; }
|
|
|
|
public float NetworkUpdateTimer;
|
|
|
|
public float Scale;
|
|
|
|
public float Rotation;
|
|
|
|
private int spriteIndex;
|
|
|
|
protected bool tookDamage;
|
|
|
|
public LevelObjectPrefab ActivePrefab;
|
|
|
|
public PhysicsBody PhysicsBody
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public List<LevelTrigger> Triggers
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public bool NeedsNetworkSyncing
|
|
{
|
|
get
|
|
{
|
|
return tookDamage || (Triggers != null && Triggers.Any(t => t.NeedsNetworkSyncing));
|
|
}
|
|
set
|
|
{
|
|
if (Triggers == null) { return; }
|
|
Triggers.ForEach(t => t.NeedsNetworkSyncing = false);
|
|
tookDamage = false;
|
|
}
|
|
}
|
|
|
|
public bool NeedsUpdate
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
public float Health
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public Sprite Sprite
|
|
{
|
|
get
|
|
{
|
|
var prefab = ActivePrefab?.Sprites.Count > 0 ? ActivePrefab : Prefab;
|
|
return spriteIndex < 0 || prefab.Sprites.Count == 0 ? null : prefab.Sprites[spriteIndex % prefab.Sprites.Count];
|
|
}
|
|
}
|
|
|
|
Vector2 ISpatialEntity.Position => new Vector2(Position.X, Position.Y);
|
|
|
|
public Vector2 WorldPosition => new Vector2(Position.X, Position.Y);
|
|
|
|
public Vector2 SimPosition => ConvertUnits.ToSimUnits(WorldPosition);
|
|
|
|
public Submarine Submarine => null;
|
|
|
|
public string Name => Prefab?.Name ?? "LevelObject (null)";
|
|
|
|
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; } = new Dictionary<Identifier, SerializableProperty>();
|
|
|
|
public Level.Cave ParentCave;
|
|
|
|
public LevelObject(LevelObjectPrefab prefab, Vector3 position, float scale, float rotation = 0.0f)
|
|
{
|
|
ActivePrefab = Prefab = prefab;
|
|
Position = position;
|
|
Scale = scale;
|
|
Rotation = rotation;
|
|
Health = prefab.Health;
|
|
|
|
spriteIndex = ActivePrefab.Sprites.Any() ? Rand.Int(ActivePrefab.Sprites.Count, Rand.RandSync.ServerAndClient) : -1;
|
|
|
|
if (Sprite != null && prefab.SpriteSpecificPhysicsBodyElements.ContainsKey(Sprite))
|
|
{
|
|
PhysicsBody = new PhysicsBody(prefab.SpriteSpecificPhysicsBodyElements[Sprite], ConvertUnits.ToSimUnits(new Vector2(position.X, position.Y)), Scale);
|
|
}
|
|
else if (prefab.PhysicsBodyElement != null)
|
|
{
|
|
PhysicsBody = new PhysicsBody(prefab.PhysicsBodyElement, ConvertUnits.ToSimUnits(new Vector2(position.X, position.Y)), Scale);
|
|
}
|
|
|
|
if (PhysicsBody != null)
|
|
{
|
|
PhysicsBody.UserData = this;
|
|
PhysicsBody.SetTransformIgnoreContacts(PhysicsBody.SimPosition, -Rotation);
|
|
PhysicsBody.BodyType = BodyType.Static;
|
|
PhysicsBody.CollisionCategories = Physics.CollisionLevel;
|
|
PhysicsBody.CollidesWith = Prefab.TakeLevelWallDamage?
|
|
Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionProjectile :
|
|
Physics.CollisionWall | Physics.CollisionCharacter;
|
|
}
|
|
|
|
foreach (var triggerElement in prefab.LevelTriggerElements)
|
|
{
|
|
Triggers ??= new List<LevelTrigger>();
|
|
Vector2 triggerPosition = triggerElement.GetAttributeVector2("position", Vector2.Zero) * scale;
|
|
|
|
if (rotation != 0.0f)
|
|
{
|
|
var ca = (float)Math.Cos(rotation);
|
|
var sa = (float)Math.Sin(rotation);
|
|
|
|
triggerPosition = new Vector2(
|
|
ca * triggerPosition.X + sa * triggerPosition.Y,
|
|
-sa * triggerPosition.X + ca * triggerPosition.Y);
|
|
}
|
|
|
|
var newTrigger = new LevelTrigger(triggerElement, new Vector2(position.X, position.Y) + triggerPosition, -rotation, scale, prefab.Name);
|
|
if (newTrigger.PhysicsBody != null)
|
|
{
|
|
newTrigger.PhysicsBody.UserData = this;
|
|
}
|
|
int parentTriggerIndex = prefab.LevelTriggerElements.IndexOf(triggerElement.Parent);
|
|
if (parentTriggerIndex > -1) { newTrigger.ParentTrigger = Triggers[parentTriggerIndex]; }
|
|
Triggers.Add(newTrigger);
|
|
}
|
|
|
|
if (spriteIndex == -1)
|
|
{
|
|
foreach (var overrideProperties in prefab.OverrideProperties)
|
|
{
|
|
if (overrideProperties == null) { continue; }
|
|
if (overrideProperties.Sprites.Count > 0)
|
|
{
|
|
spriteIndex = Rand.Int(overrideProperties.Sprites.Count, Rand.RandSync.ServerAndClient);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
NeedsUpdate = NeedsNetworkSyncing || (Triggers != null && Triggers.Any()) || Prefab.PhysicsBodyTriggerIndex > -1;
|
|
|
|
InitProjSpecific();
|
|
}
|
|
|
|
partial void InitProjSpecific();
|
|
|
|
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, Vector2 impulseDirection, float deltaTime, bool playSound = true)
|
|
{
|
|
if (Health <= 0.0f) { return new AttackResult(0.0f); }
|
|
|
|
float damage = 0.0f;
|
|
if (Prefab.TakeLevelWallDamage)
|
|
{
|
|
damage += attack.GetLevelWallDamage(deltaTime);
|
|
}
|
|
damage = Math.Max(Health, damage);
|
|
AddDamage(damage, deltaTime, attacker);
|
|
return new AttackResult(damage);
|
|
}
|
|
|
|
public void AddDamage(float damage, float deltaTime, Entity attacker, bool isNetworkEvent = false)
|
|
{
|
|
if (Health <= 0.0f) { return; }
|
|
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkEvent)
|
|
{
|
|
return;
|
|
}
|
|
tookDamage |= !MathUtils.NearlyEqual(damage, 0.0f);
|
|
Health -= damage;
|
|
if (Health <= 0.0f)
|
|
{
|
|
#if CLIENT
|
|
if (GameMain.GameSession?.Level?.LevelObjectManager != null)
|
|
{
|
|
GameMain.GameSession.Level.LevelObjectManager.ForceRefreshVisibleObjects = true;
|
|
}
|
|
#endif
|
|
if (PhysicsBody != null)
|
|
{
|
|
PhysicsBody.Enabled = false;
|
|
}
|
|
foreach (LevelTrigger trigger in Triggers)
|
|
{
|
|
trigger.PhysicsBody.Enabled = false;
|
|
foreach (StatusEffect effect in trigger.StatusEffects)
|
|
{
|
|
if (effect.type != ActionType.OnBroken) { continue; }
|
|
effect.Apply(effect.type, deltaTime, attacker, this, worldPosition: WorldPosition);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Vector2 LocalToWorld(Vector2 localPosition, float swingState = 0.0f)
|
|
{
|
|
Vector2 emitterPos = localPosition * Scale;
|
|
|
|
if (Rotation != 0.0f || Prefab.SwingAmountRad != 0.0f)
|
|
{
|
|
float rot = Rotation + swingState * Prefab.SwingAmountRad;
|
|
|
|
var ca = (float)Math.Cos(rot);
|
|
var sa = (float)Math.Sin(rot);
|
|
|
|
emitterPos = new Vector2(
|
|
ca * emitterPos.X + sa * emitterPos.Y,
|
|
-sa * emitterPos.X + ca * emitterPos.Y);
|
|
}
|
|
return new Vector2(Position.X, Position.Y) + emitterPos;
|
|
}
|
|
|
|
public void Remove()
|
|
{
|
|
RemoveProjSpecific();
|
|
}
|
|
|
|
partial void RemoveProjSpecific();
|
|
|
|
public override string ToString()
|
|
{
|
|
return "LevelObject (" + ActivePrefab.Name + ")";
|
|
}
|
|
|
|
public void ServerWrite(IWriteMessage msg, Client c)
|
|
{
|
|
if (Triggers == null) { return; }
|
|
if (Prefab.TakeLevelWallDamage)
|
|
{
|
|
msg.WriteRangedSingle(MathHelper.Clamp(Health, 0.0f, Prefab.Health), 0.0f, Prefab.Health, 8);
|
|
}
|
|
for (int j = 0; j < Triggers.Count; j++)
|
|
{
|
|
if (!Triggers[j].UseNetworkSyncing) { continue; }
|
|
Triggers[j].ServerWrite(msg, c);
|
|
}
|
|
}
|
|
}
|
|
}
|