From 741e26251d80297f53519891d7fd3992d197ecb0 Mon Sep 17 00:00:00 2001 From: Alex Noir Date: Thu, 28 Dec 2017 13:21:32 +0300 Subject: [PATCH] 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 --- .../Content/Items/Medical/medical.xml | 22 +++ .../Source/Characters/DelayedEffect.cs | 17 ++- .../Source/Characters/StatusEffect.cs | 138 +++++++++++++----- 3 files changed, 137 insertions(+), 40 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Content/Items/Medical/medical.xml b/Barotrauma/BarotraumaShared/Content/Items/Medical/medical.xml index bf4f57da8..3ff97f98b 100644 --- a/Barotrauma/BarotraumaShared/Content/Items/Medical/medical.xml +++ b/Barotrauma/BarotraumaShared/Content/Items/Medical/medical.xml @@ -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"> @@ -201,6 +202,27 @@ + + + + + + + + + + + + + + List = new List(); + public static List DelayList = new List(); private float delay; @@ -25,27 +25,34 @@ namespace Barotrauma public override void Apply(ActionType type, float deltaTime, Entity entity, List 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); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/Characters/StatusEffect.cs index 9d3a9b7eb..5b5aa1ec3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/StatusEffect.cs @@ -9,6 +9,13 @@ using Barotrauma.Particles; namespace Barotrauma { + class DurationListElement + { + public StatusEffect Parent; + public Entity Entity; + public List Targets; + public float StartTimer; + } partial class PropertyConditional { public string Attribute; @@ -119,8 +126,14 @@ namespace Barotrauma private bool disableDeltaTime; private HashSet onContainingNames; + private HashSet tags; private readonly float duration; + public static List DurationList = new List(); + + 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(); + tags = new HashSet(element.GetAttributeString("tags", "").Split(',')); #if CLIENT particleEmitters = new List(); #endif - IEnumerable attributes = element.Attributes(); + IEnumerable attributes = element.Attributes(); List propertyAttributes = new List(); propertyConditionals = new List(); @@ -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 targets = new List(); 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 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())); + } } }