Gets rid of coroutine spam by turning Duration into its own DurationListElement similar to how Delays were handled.
Added tags to Status Effects, which also allows for fancy stuff like checking for tags over duration elements for more interesting interactions. Adds "stackable" variable which dictates whether or not the same duration/delay effect can be applied to the same target(s) at the same time. This is a bit imperfect at the moment. Adds Chloromydride which is a non-stackable chem stabilizing critical health which stops its effects once the health is stabilized. TODO: Remove target from Targets if he already has it, and if targets becomes empty, return; TODO: Conditional tag-checking and status effect tag-checking, plus more "special" checks like SpeciesName and other non-serialized options. TODO: StatusEffect Cancel component to stop delayed/duration effects
This commit is contained in:
@@ -82,6 +82,7 @@
|
||||
spritecolor="1.0,1.0,0.7,1.0"
|
||||
Tags="smallitem,chem,medical"
|
||||
description="A mild stimulant which is used as an ingredient in the manufacture of various medicines."
|
||||
canuseonself="true"
|
||||
price="10">
|
||||
|
||||
<Sprite texture ="med.png" sourcerect="15,15,8,17" depth="0.6" />
|
||||
@@ -201,6 +202,27 @@
|
||||
</Item>
|
||||
|
||||
<!-- Helpful chems -->
|
||||
<Item
|
||||
name="Chloromydride"
|
||||
category="Material"
|
||||
spritecolor="0.7,0.9,1,1.0"
|
||||
Tags="smallitem,chem,medical"
|
||||
description="A strong synaptic stimulant and cardiostimulant which is used as a preventative measure against critical condition. Should be injected once every 30 seconds."
|
||||
price="50">
|
||||
|
||||
<Sprite texture ="med.png" sourcerect="15,15,8,17" depth="0.6" />
|
||||
|
||||
<Body width="8" height="16" density="20"/>
|
||||
|
||||
<Throwable canBeCombined="true" slots="Any,RightHand,LeftHand" throwforce="4.0" aimpos="35,-10">
|
||||
<StatusEffect type="OnUse" target="Character" bleeding="-0.2" Oxygen="0.5" duration="30" stackable="false" checkconditionalalways="true">
|
||||
<!-- Rapidly removes bleeding and stabilizes oxygen intake so they're "frozen" in a stasis. Doesn't prevent poisons.
|
||||
EXTREMELY useful to follow up with a CPR while keeping them alive long enough. -->
|
||||
<Conditional Health="lte 0.0"/>
|
||||
<RequiredItem name="Medical Syringe" type="Container"/>
|
||||
</StatusEffect>
|
||||
</Throwable>
|
||||
</Item>
|
||||
<Item
|
||||
name="Erythrozine"
|
||||
category="Material"
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
}
|
||||
class DelayedEffect : StatusEffect
|
||||
{
|
||||
public static List<DelayedListElement> List = new List<DelayedListElement>();
|
||||
public static List<DelayedListElement> DelayList = new List<DelayedListElement>();
|
||||
|
||||
private float delay;
|
||||
|
||||
@@ -25,27 +25,34 @@ namespace Barotrauma
|
||||
public override void Apply(ActionType type, float deltaTime, Entity entity, List<ISerializableEntity> targets)
|
||||
{
|
||||
if (this.type != type || !HasRequiredItems(entity)) return;
|
||||
if (!base.Stackable && DelayList.Find(d => d.Parent == this && d.Entity == entity && d.Targets == targets) != null) return;
|
||||
|
||||
DelayedListElement element = new DelayedListElement();
|
||||
element.Parent = this;
|
||||
element.StartTimer = delay;
|
||||
element.Entity = entity;
|
||||
element.Targets = targets;
|
||||
|
||||
List.Add(element);
|
||||
DelayList.Add(element);
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
{
|
||||
for (int i = DelayedEffect.List.Count - 1; i >= 0; i--)
|
||||
for (int i = DelayList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
DelayedListElement element = DelayedEffect.List[i];
|
||||
DelayedListElement element = DelayList[i];
|
||||
if (element.Parent.CheckConditionalAlways && !element.Parent.HasRequiredConditions(element.Targets))
|
||||
{
|
||||
DelayList.Remove(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
element.StartTimer -= deltaTime;
|
||||
|
||||
if (element.StartTimer > 0.0f) continue;
|
||||
|
||||
element.Parent.Apply(1.0f, element.Entity, element.Targets);
|
||||
List.Remove(element);
|
||||
DelayList.Remove(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@ using Barotrauma.Particles;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class DurationListElement
|
||||
{
|
||||
public StatusEffect Parent;
|
||||
public Entity Entity;
|
||||
public List<ISerializableEntity> Targets;
|
||||
public float StartTimer;
|
||||
}
|
||||
partial class PropertyConditional
|
||||
{
|
||||
public string Attribute;
|
||||
@@ -119,8 +126,14 @@ namespace Barotrauma
|
||||
private bool disableDeltaTime;
|
||||
|
||||
private HashSet<string> onContainingNames;
|
||||
private HashSet<string> tags;
|
||||
|
||||
private readonly float duration;
|
||||
public static List<DurationListElement> DurationList = new List<DurationListElement>();
|
||||
|
||||
public bool CheckConditionalAlways; //Always do the conditional checks for the duration/delay. If false, only check conditional on apply.
|
||||
|
||||
public bool Stackable; //Can the same status effect be applied several times to the same targets?
|
||||
|
||||
private readonly bool useItem;
|
||||
|
||||
@@ -145,6 +158,24 @@ namespace Barotrauma
|
||||
get { return onContainingNames; }
|
||||
}
|
||||
|
||||
public string Tags
|
||||
{
|
||||
get { return string.Join(",", tags); }
|
||||
set
|
||||
{
|
||||
tags.Clear();
|
||||
if (value == null) return;
|
||||
|
||||
string[] newTags = value.Split(',');
|
||||
foreach (string tag in newTags)
|
||||
{
|
||||
string newTag = tag.Trim();
|
||||
if (!tags.Contains(newTag)) tags.Add(newTag);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static StatusEffect Load(XElement element)
|
||||
{
|
||||
if (element.Attribute("delay")!=null)
|
||||
@@ -158,12 +189,13 @@ namespace Barotrauma
|
||||
protected StatusEffect(XElement element)
|
||||
{
|
||||
requiredItems = new List<RelatedItem>();
|
||||
tags = new HashSet<string>(element.GetAttributeString("tags", "").Split(','));
|
||||
|
||||
#if CLIENT
|
||||
particleEmitters = new List<ParticleEmitter>();
|
||||
#endif
|
||||
|
||||
IEnumerable<XAttribute> attributes = element.Attributes();
|
||||
IEnumerable<XAttribute> attributes = element.Attributes();
|
||||
List<XAttribute> propertyAttributes = new List<XAttribute>();
|
||||
propertyConditionals = new List<PropertyConditional>();
|
||||
|
||||
@@ -216,12 +248,16 @@ namespace Barotrauma
|
||||
case "duration":
|
||||
duration = attribute.GetAttributeFloat(0.0f);
|
||||
break;
|
||||
case "stackable":
|
||||
Stackable = attribute.GetAttributeBool(true);
|
||||
break;
|
||||
case "checkconditionalalways":
|
||||
CheckConditionalAlways = attribute.GetAttributeBool(false);
|
||||
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;
|
||||
case "if":
|
||||
break;
|
||||
default:
|
||||
propertyAttributes.Add(attribute);
|
||||
break;
|
||||
@@ -380,6 +416,8 @@ namespace Barotrauma
|
||||
|
||||
if (targetNames != null && !targetNames.Contains(target.Name)) return;
|
||||
|
||||
if (duration > 0.0f && !Stackable && DurationList.Find(d => d.Parent == this && d.Entity == entity && d.Targets.Contains(target)) != null) return;
|
||||
|
||||
List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
targets.Add(target);
|
||||
|
||||
@@ -426,26 +464,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ISerializableEntity target in targets)
|
||||
if (duration > 0.0f)
|
||||
{
|
||||
for (int i = 0; i < propertyNames.Length; i++)
|
||||
{
|
||||
SerializableProperty property;
|
||||
DurationListElement element = new DurationListElement();
|
||||
element.Parent = this;
|
||||
element.StartTimer = duration;
|
||||
element.Entity = entity;
|
||||
element.Targets = targets;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
DurationList.Add(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
ApplyToProperty(property, propertyEffects[i], deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (explosion != null) explosion.Explode(entity.WorldPosition);
|
||||
|
||||
@@ -475,21 +518,6 @@ namespace Barotrauma
|
||||
#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;
|
||||
@@ -527,11 +555,51 @@ namespace Barotrauma
|
||||
public static void UpdateAll(float deltaTime)
|
||||
{
|
||||
DelayedEffect.Update(deltaTime);
|
||||
for (int i = DurationList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
DurationListElement element = DurationList[i];
|
||||
|
||||
if (element.Parent.CheckConditionalAlways && !element.Parent.HasRequiredConditions(element.Targets))
|
||||
{
|
||||
DurationList.Remove(element);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (ISerializableEntity target in element.Targets)
|
||||
{
|
||||
for (int n = 0; n < element.Parent.propertyNames.Length; n++)
|
||||
{
|
||||
SerializableProperty property;
|
||||
|
||||
if (target == null || target.SerializableProperties == null || !target.SerializableProperties.TryGetValue(element.Parent.propertyNames[n], out property)) continue;
|
||||
|
||||
element.Parent.ApplyToProperty(property, element.Parent.propertyEffects[n], CoroutineManager.UnscaledDeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
element.StartTimer -= deltaTime;
|
||||
|
||||
if (element.StartTimer > 0.0f) continue;
|
||||
DurationList.Remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopAll()
|
||||
{
|
||||
CoroutineManager.StopCoroutines("statuseffect");
|
||||
}
|
||||
|
||||
public void AddTag(string tag)
|
||||
{
|
||||
if (tags.Contains(tag)) return;
|
||||
tags.Add(tag);
|
||||
}
|
||||
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
if (tag == null) return true;
|
||||
|
||||
return (tags.Contains(tag) || tags.Contains(tag.ToLowerInvariant()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user