Made syringes have a "projectile" component for syringe guns Adds several missing signal components to the fabricator, as well as medical syringe to medical fabricator Adds support for status effects applied to characters from onImpact Removed "contained.Condition = 0.0f;" in Projectile.cs due to it limiting the possibilities of the .xml content creation Added a couple null checks in StatusEffect.cs due to crashes caused by the new projectile changes - might need to debug further, perhaps there's a root cause? Added itemlist console command
374 lines
12 KiB
C#
374 lines
12 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
#if CLIENT
|
|
using Barotrauma.Particles;
|
|
#endif
|
|
|
|
namespace Barotrauma
|
|
{
|
|
partial class StatusEffect
|
|
{
|
|
[Flags]
|
|
public enum TargetType
|
|
{
|
|
This = 1, Parent = 2, Character = 4, Contained = 8, Nearby = 16, UseTarget = 32, Hull = 64
|
|
}
|
|
|
|
private TargetType targetTypes;
|
|
private HashSet<string> targetNames;
|
|
|
|
private List<RelatedItem> requiredItems;
|
|
|
|
#if CLIENT
|
|
private List<ParticleEmitter> particleEmitters;
|
|
|
|
private Sound sound;
|
|
private bool loopSound;
|
|
#endif
|
|
|
|
public string[] propertyNames;
|
|
private object[] propertyEffects;
|
|
|
|
private bool setValue;
|
|
|
|
private bool disableDeltaTime;
|
|
|
|
private HashSet<string> onContainingNames;
|
|
|
|
private readonly float duration;
|
|
|
|
private readonly bool useItem;
|
|
|
|
public readonly ActionType type;
|
|
|
|
private Explosion explosion;
|
|
|
|
public readonly float FireSize;
|
|
|
|
public TargetType Targets
|
|
{
|
|
get { return targetTypes; }
|
|
}
|
|
|
|
public HashSet<string> TargetNames
|
|
{
|
|
get { return targetNames; }
|
|
}
|
|
|
|
public HashSet<string> OnContainingNames
|
|
{
|
|
get { return onContainingNames; }
|
|
}
|
|
|
|
public static StatusEffect Load(XElement element)
|
|
{
|
|
if (element.Attribute("delay")!=null)
|
|
{
|
|
return new DelayedEffect(element);
|
|
}
|
|
|
|
return new StatusEffect(element);
|
|
}
|
|
|
|
protected StatusEffect(XElement element)
|
|
{
|
|
requiredItems = new List<RelatedItem>();
|
|
|
|
#if CLIENT
|
|
particleEmitters = new List<ParticleEmitter>();
|
|
#endif
|
|
|
|
IEnumerable<XAttribute> attributes = element.Attributes();
|
|
List<XAttribute> propertyAttributes = new List<XAttribute>();
|
|
|
|
foreach (XAttribute attribute in attributes)
|
|
{
|
|
switch (attribute.Name.ToString())
|
|
{
|
|
case "type":
|
|
try
|
|
{
|
|
type = (ActionType)Enum.Parse(typeof(ActionType), attribute.Value, true);
|
|
}
|
|
|
|
catch
|
|
{
|
|
string[] split = attribute.Value.Split('=');
|
|
type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true);
|
|
|
|
string[] containingNames = split[1].Split(',');
|
|
onContainingNames = new HashSet<string>();
|
|
for (int i = 0; i < containingNames.Length; i++)
|
|
{
|
|
onContainingNames.Add(containingNames[i].Trim());
|
|
}
|
|
}
|
|
|
|
break;
|
|
case "target":
|
|
string[] Flags = attribute.Value.Split(',');
|
|
foreach (string s in Flags)
|
|
{
|
|
targetTypes |= (TargetType)Enum.Parse(typeof(TargetType), s, true);
|
|
}
|
|
|
|
break;
|
|
case "disabledeltatime":
|
|
disableDeltaTime = attribute.GetAttributeBool(false);
|
|
break;
|
|
case "setvalue":
|
|
setValue = attribute.GetAttributeBool(false);
|
|
break;
|
|
case "targetnames":
|
|
string[] names = attribute.Value.Split(',');
|
|
targetNames = new HashSet<string>();
|
|
for (int i=0; i < names.Length; i++ )
|
|
{
|
|
targetNames.Add(names[i].Trim());
|
|
}
|
|
break;
|
|
case "duration":
|
|
duration = attribute.GetAttributeFloat(0.0f);
|
|
break;
|
|
case "sound":
|
|
DebugConsole.ThrowError("Error in StatusEffect " + element.Parent.Name.ToString() +
|
|
" - sounds should be defined as child elements of the StatusEffect, not as attributes.");
|
|
break;
|
|
default:
|
|
propertyAttributes.Add(attribute);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int count = propertyAttributes.Count;
|
|
propertyNames = new string[count];
|
|
propertyEffects = new object[count];
|
|
|
|
int n = 0;
|
|
foreach (XAttribute attribute in propertyAttributes)
|
|
{
|
|
propertyNames[n] = attribute.Name.ToString().ToLowerInvariant();
|
|
propertyEffects[n] = XMLExtensions.GetAttributeObject(attribute);
|
|
n++;
|
|
}
|
|
|
|
foreach (XElement subElement in element.Elements())
|
|
{
|
|
switch (subElement.Name.ToString().ToLowerInvariant())
|
|
{
|
|
case "explosion":
|
|
explosion = new Explosion(subElement);
|
|
break;
|
|
case "fire":
|
|
FireSize = subElement.GetAttributeFloat("size",10.0f);
|
|
break;
|
|
case "use":
|
|
case "useitem":
|
|
useItem = true;
|
|
break;
|
|
case "requireditem":
|
|
case "requireditems":
|
|
RelatedItem newRequiredItem = RelatedItem.Load(subElement);
|
|
|
|
if (newRequiredItem == null) continue;
|
|
|
|
requiredItems.Add(newRequiredItem);
|
|
break;
|
|
#if CLIENT
|
|
case "particleemitter":
|
|
particleEmitters.Add(new ParticleEmitter(subElement));
|
|
break;
|
|
case "sound":
|
|
sound = Sound.Load(subElement);
|
|
loopSound = subElement.GetAttributeBool("loop", false);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool HasRequiredItems(Entity entity)
|
|
{
|
|
if (requiredItems == null) return true;
|
|
foreach (RelatedItem requiredItem in requiredItems)
|
|
{
|
|
Item item = entity as Item;
|
|
if (item != null)
|
|
{
|
|
if (!requiredItem.CheckRequirements(null, item)) return false;
|
|
}
|
|
Character character = entity as Character;
|
|
if (character != null)
|
|
{
|
|
if (!requiredItem.CheckRequirements(character, null)) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public virtual void Apply(ActionType type, float deltaTime, Entity entity, ISerializableEntity target)
|
|
{
|
|
if (this.type != type || !HasRequiredItems(entity)) return;
|
|
|
|
if (targetNames != null && !targetNames.Contains(target.Name)) return;
|
|
|
|
List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
|
targets.Add(target);
|
|
|
|
Apply(deltaTime, entity, targets);
|
|
}
|
|
|
|
public virtual void Apply(ActionType type, float deltaTime, Entity entity, List<ISerializableEntity> targets)
|
|
{
|
|
if (this.type != type || !HasRequiredItems(entity)) return;
|
|
|
|
Apply(deltaTime, entity, targets);
|
|
}
|
|
|
|
protected void Apply(float deltaTime, Entity entity, List<ISerializableEntity> targets)
|
|
{
|
|
#if CLIENT
|
|
if (sound != null)
|
|
{
|
|
if (loopSound)
|
|
{
|
|
if (!Sounds.SoundManager.IsPlaying(sound))
|
|
{
|
|
sound.Play(entity.WorldPosition);
|
|
}
|
|
else
|
|
{
|
|
sound.UpdatePosition(entity.WorldPosition);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sound.Play(entity.WorldPosition);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (useItem)
|
|
{
|
|
foreach (Item item in targets.FindAll(t => t is Item).Cast<Item>())
|
|
{
|
|
item.Use(deltaTime, targets.FirstOrDefault(t => t is Character) as Character);
|
|
}
|
|
}
|
|
|
|
foreach (ISerializableEntity target in targets)
|
|
{
|
|
for (int i = 0; i < propertyNames.Length; i++)
|
|
{
|
|
SerializableProperty property;
|
|
|
|
if (target == null || target.SerializableProperties == null || !target.SerializableProperties.TryGetValue(propertyNames[i], out property)) continue;
|
|
|
|
if (duration > 0.0f)
|
|
{
|
|
CoroutineManager.StartCoroutine(
|
|
ApplyToPropertyOverDuration(duration, property, propertyEffects[i]), "statuseffect");
|
|
|
|
}
|
|
else
|
|
{
|
|
ApplyToProperty(property, propertyEffects[i], deltaTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (explosion != null) explosion.Explode(entity.WorldPosition);
|
|
|
|
|
|
Hull hull = null;
|
|
if (entity is Character)
|
|
{
|
|
hull = ((Character)entity).AnimController.CurrentHull;
|
|
}
|
|
else if (entity is Item)
|
|
{
|
|
hull = ((Item)entity).CurrentHull;
|
|
}
|
|
|
|
if (FireSize > 0.0f)
|
|
{
|
|
var fire = new FireSource(entity.WorldPosition, hull);
|
|
|
|
fire.Size = new Vector2(FireSize, fire.Size.Y);
|
|
}
|
|
|
|
#if CLIENT
|
|
foreach (ParticleEmitter emitter in particleEmitters)
|
|
{
|
|
emitter.Emit(deltaTime, entity.WorldPosition, hull);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private IEnumerable<object> ApplyToPropertyOverDuration(float duration, SerializableProperty property, object value)
|
|
{
|
|
float timer = duration;
|
|
while (timer > 0.0f)
|
|
{
|
|
ApplyToProperty(property, value, CoroutineManager.UnscaledDeltaTime);
|
|
|
|
timer -= CoroutineManager.DeltaTime;
|
|
|
|
yield return CoroutineStatus.Running;
|
|
}
|
|
|
|
yield return CoroutineStatus.Success;
|
|
}
|
|
|
|
private void ApplyToProperty(SerializableProperty property, object value, float deltaTime)
|
|
{
|
|
if (disableDeltaTime || setValue) deltaTime = 1.0f;
|
|
|
|
Type type = value.GetType();
|
|
if (type == typeof(float) ||
|
|
(type == typeof(int) && property.GetValue() is float))
|
|
{
|
|
float floatValue = Convert.ToSingle(value) * deltaTime;
|
|
|
|
if (!setValue) floatValue += (float)property.GetValue();
|
|
property.TrySetValue(floatValue);
|
|
}
|
|
else if (type == typeof(int) && value is int)
|
|
{
|
|
int intValue = (int)((int)value * deltaTime);
|
|
if (!setValue) intValue += (int)property.GetValue();
|
|
property.TrySetValue(intValue);
|
|
}
|
|
else if (type == typeof(bool) && value is bool)
|
|
{
|
|
property.TrySetValue((bool)value);
|
|
}
|
|
else if (type == typeof(string))
|
|
{
|
|
property.TrySetValue((string)value);
|
|
}
|
|
else
|
|
{
|
|
DebugConsole.ThrowError("Couldn't apply value " + value.ToString() + " (" + type + ") to property \"" + property.Name + "\" (" + property.GetValue().GetType() + ")! "
|
|
+ "Make sure the type of the value set in the config files matches the type of the property.");
|
|
}
|
|
}
|
|
|
|
public static void UpdateAll(float deltaTime)
|
|
{
|
|
for (int i = DelayedEffect.List.Count-1; i>= 0; i--)
|
|
{
|
|
DelayedEffect.List[i].Update(deltaTime);
|
|
}
|
|
}
|
|
|
|
public static void StopAll()
|
|
{
|
|
CoroutineManager.StopCoroutines("statuseffect");
|
|
}
|
|
}
|
|
}
|