// TODO: refactor and add tests private bool Matches(ISerializableEntity target, SerializableProperty property) { object propertyValue = property.GetValue(target); if (propertyValue == null) { DebugConsole.ThrowError("Couldn't compare " + AttributeValue.ToString() + " (" + AttributeValue.GetType() + ") to property \"" + property.Name + "\" - property.GetValue() returns null!"); return(false); } Type type = propertyValue.GetType(); float?floatProperty = null; if (type == typeof(float) || type == typeof(int)) { floatProperty = (float)propertyValue; } switch (Operator) { case OperatorType.Equals: if (type == typeof(bool)) { return(((bool)propertyValue) == (AttributeValue == "true")); } else if (FloatValue == null) { return(propertyValue.ToString().Equals(AttributeValue)); } else { return(propertyValue.Equals(FloatValue)); } case OperatorType.NotEquals: if (type == typeof(bool)) { return(((bool)propertyValue) != (AttributeValue == "true")); } else if (FloatValue == null) { return(!propertyValue.ToString().Equals(AttributeValue)); } else { return(!propertyValue.Equals(FloatValue)); } case OperatorType.GreaterThan: if (FloatValue == null) { DebugConsole.ThrowError("Couldn't compare " + AttributeValue.ToString() + " (" + AttributeValue.GetType() + ") to property \"" + property.Name + "\" (" + type + ")! " + "Make sure the type of the value set in the config files matches the type of the property."); } else if (floatProperty > FloatValue) { return(true); } break; case OperatorType.LessThan: if (FloatValue == null) { DebugConsole.ThrowError("Couldn't compare " + AttributeValue.ToString() + " (" + AttributeValue.GetType() + ") to property \"" + property.Name + "\" (" + type + ")! " + "Make sure the type of the value set in the config files matches the type of the property."); } else if (floatProperty < FloatValue) { return(true); } break; case OperatorType.GreaterThanEquals: if (FloatValue == null) { DebugConsole.ThrowError("Couldn't compare " + AttributeValue.ToString() + " (" + AttributeValue.GetType() + ") to property \"" + property.Name + "\" (" + type + ")! " + "Make sure the type of the value set in the config files matches the type of the property."); } else if (floatProperty >= FloatValue) { return(true); } break; case OperatorType.LessThanEquals: if (FloatValue == null) { DebugConsole.ThrowError("Couldn't compare " + AttributeValue.ToString() + " (" + AttributeValue.GetType() + ") to property \"" + property.Name + "\" (" + type + ")! " + "Make sure the type of the value set in the config files matches the type of the property."); } else if (floatProperty <= FloatValue) { return(true); } break; } return(false); }
/// <summary> /// Upgrade the properties of an entity saved with an older version of the game. Properties that should be upgraded are defined using "Upgrade" elements in the config file. /// for example, <Upgrade gameversion="0.9.2.0" scale="0.5"/> would force the scale of the entity to 0.5 if it was saved with a version prior to 0.9.2.0. /// </summary> /// <param name="entity">The entity to upgrade</param> /// <param name="configElement">The XML element to get the upgrade instructions from (e.g. the config of an item prefab)</param> /// <param name="savedVersion">The game version the entity was saved with</param> public static void UpgradeGameVersion(ISerializableEntity entity, XElement configElement, Version savedVersion) { foreach (XElement subElement in configElement.Elements()) { if (!subElement.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { continue; } var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0")); if (savedVersion >= upgradeVersion) { continue; } foreach (XAttribute attribute in subElement.Attributes()) { string attributeName = attribute.Name.ToString().ToLowerInvariant(); if (attributeName == "gameversion") { continue; } if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property)) { property.TrySetValue(entity, attribute.Value); } else if (entity is Item item1) { foreach (ISerializableEntity component in item1.AllPropertyObjects) { if (component.SerializableProperties.TryGetValue(attributeName, out SerializableProperty componentProperty)) { componentProperty.TrySetValue(component, attribute.Value); } } } } if (entity is Item item2) { XElement componentElement = subElement.FirstElement(); if (componentElement == null) { continue; } ItemComponent itemComponent = item2.Components.First(c => c.Name == componentElement.Name.ToString()); if (itemComponent == null) { continue; } foreach (XElement element in componentElement.Elements()) { switch (element.Name.ToString().ToLowerInvariant()) { case "requireditem": case "requireditems": itemComponent.requiredItems.Clear(); itemComponent.DisabledRequiredItems.Clear(); itemComponent.SetRequiredItems(element); break; } } } } }
public bool Matches(ISerializableEntity target) { string valStr = AttributeValue.ToString(); switch (Type) { case ConditionType.PropertyValue: SerializableProperty property; if (target.SerializableProperties.TryGetValue(AttributeName, out property)) { return(Matches(target, property)); } return(false); case ConditionType.Name: return((Operator == OperatorType.Equals) == (target.Name == valStr)); case ConditionType.HasTag: { string[] readTags = valStr.Split(','); int matches = 0; foreach (string tag in readTags) { if (((Item)target).HasTag(tag)) { matches++; } } //If operator is == then it needs to match everything, otherwise if its != there must be zero matches. return(Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0); } case ConditionType.HasStatusTag: List <DurationListElement> durations = StatusEffect.DurationList.FindAll(d => d.Targets.Contains(target)); List <DelayedListElement> delays = DelayedEffect.DelayList.FindAll(d => d.Targets.Contains(target)); bool success = false; if (durations.Count > 0 || delays.Count > 0) { string[] readTags = valStr.Split(','); foreach (DurationListElement duration in durations) { int matches = 0; foreach (string tag in readTags) { if (duration.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { StatusEffect.DurationList.Remove(duration); } if (cancelStatusEffect != 2) //cancelStatusEffect 1 = only cancel once, cancelStatusEffect 2 = cancel all of matching tags { return(success); } } foreach (DelayedListElement delay in delays) { int matches = 0; foreach (string tag in readTags) { if (delay.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { DelayedEffect.DelayList.Remove(delay); } if (cancelStatusEffect != 2) //ditto { return(success); } } } else if (Operator == OperatorType.NotEquals) { //no status effects, so the tags cannot be equal -> condition met return(true); } return(success); case ConditionType.SpeciesName: Character targetCharacter = target as Character; if (targetCharacter == null) { return(false); } return((Operator == OperatorType.Equals) == (targetCharacter.SpeciesName == valStr)); case ConditionType.Affliction: if (target is Character targetChar) { var health = targetChar.CharacterHealth; if (health == null) { return(false); } var affliction = health.GetAffliction(AttributeName); if (affliction == null) { return(false); } if (FloatValue.HasValue) { float value = FloatValue.Value; switch (Operator) { case OperatorType.Equals: return(affliction.Strength == value); case OperatorType.GreaterThan: return(affliction.Strength > value); case OperatorType.GreaterThanEquals: return(affliction.Strength >= value); case OperatorType.LessThan: return(affliction.Strength < value); case OperatorType.LessThanEquals: return(affliction.Strength <= value); case OperatorType.NotEquals: return(affliction.Strength != value); } } } return(false); default: return(false); } }
public bool Matches(ISerializableEntity target) { string valStr = AttributeValue.ToString(); switch (Type) { case ConditionType.PropertyValue: SerializableProperty property; if (target?.SerializableProperties == null) { return(Operator == OperatorType.NotEquals); } if (target.SerializableProperties.TryGetValue(AttributeName, out property)) { return(Matches(target, property)); } return(false); case ConditionType.Name: if (target == null) { return(Operator == OperatorType.NotEquals); } return((Operator == OperatorType.Equals) == (target.Name == valStr)); case ConditionType.HasTag: { if (target == null) { return(Operator == OperatorType.NotEquals); } string[] readTags = valStr.Split(','); int matches = 0; foreach (string tag in readTags) { if (target is Item item && item.HasTag(tag)) { matches++; } } //If operator is == then it needs to match everything, otherwise if its != there must be zero matches. return(Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0); } case ConditionType.HasStatusTag: if (target == null) { return(Operator == OperatorType.NotEquals); } bool success = false; if (StatusEffect.DurationList.Any(d => d.Targets.Contains(target)) || DelayedEffect.DelayList.Any(d => d.Targets.Contains(target))) { string[] readTags = valStr.Split(','); foreach (DurationListElement duration in StatusEffect.DurationList) { if (!duration.Targets.Contains(target)) { continue; } int matches = 0; foreach (string tag in readTags) { if (duration.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { StatusEffect.DurationList.Remove(duration); } if (cancelStatusEffect != 2) { //cancelStatusEffect 1 = only cancel once, cancelStatusEffect 2 = cancel all of matching tags return(success); } } foreach (DelayedListElement delay in DelayedEffect.DelayList) { if (!delay.Targets.Contains(target)) { continue; } int matches = 0; foreach (string tag in readTags) { if (delay.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { DelayedEffect.DelayList.Remove(delay); } if (cancelStatusEffect != 2) { //ditto return(success); } } } else if (Operator == OperatorType.NotEquals) { //no status effects, so the tags cannot be equal -> condition met return(true); } return(success); case ConditionType.SpeciesName: if (target == null) { return(Operator == OperatorType.NotEquals); } if (!(target is Character targetCharacter)) { return(false); } return((Operator == OperatorType.Equals) == targetCharacter.SpeciesName.Equals(valStr, StringComparison.OrdinalIgnoreCase)); case ConditionType.EntityType: switch (valStr) { case "character": case "Character": return((Operator == OperatorType.Equals) == target is Character); case "limb": case "Limb": return((Operator == OperatorType.Equals) == target is Limb); case "item": case "Item": return((Operator == OperatorType.Equals) == target is Item); case "structure": case "Structure": return((Operator == OperatorType.Equals) == target is Structure); case "null": return((Operator == OperatorType.Equals) == (target == null)); default: return(false); } case ConditionType.LimbType: { if (!(target is Limb limb)) { return(false); }
public static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault = false) { var saveProperties = GetProperties <Serialize>(obj); foreach (var property in saveProperties) { object value = property.GetValue(obj); if (value == null) { continue; } if (!saveIfDefault) { //only save // - if the attribute is saveable and it's different from the default value // - or can be changed in-game or in the editor bool save = false; foreach (var attribute in property.Attributes.OfType <Serialize>()) { if ((attribute.isSaveable && !attribute.defaultValue.Equals(value)) || property.Attributes.OfType <Editable>().Any()) { save = true; break; } } if (!save) { continue; } } string stringValue; if (!supportedTypes.TryGetValue(value.GetType(), out string typeName)) { if (property.PropertyType.IsEnum) { stringValue = value.ToString(); } else { DebugConsole.ThrowError("Failed to serialize the property \"" + property.Name + "\" of \"" + obj + "\" (type " + property.PropertyType + " not supported)"); continue; } } else { switch (typeName) { case "float": //make sure the decimal point isn't converted to a comma or anything else stringValue = ((float)value).ToString("G", CultureInfo.InvariantCulture); break; case "point": stringValue = XMLExtensions.PointToString((Point)value); break; case "vector2": stringValue = XMLExtensions.Vector2ToString((Vector2)value); break; case "vector3": stringValue = XMLExtensions.Vector3ToString((Vector3)value); break; case "vector4": stringValue = XMLExtensions.Vector4ToString((Vector4)value); break; case "color": stringValue = XMLExtensions.ColorToString((Color)value); break; case "rectangle": stringValue = XMLExtensions.RectToString((Rectangle)value); break; default: stringValue = value.ToString(); break; } } element.Attribute(property.Name)?.Remove(); element.SetAttributeValue(property.NameToLowerInvariant, stringValue); } }
public bool Matches(ISerializableEntity target) { if (TargetContainedItem) { if (target is Item item) { return(item.ContainedItems.Any(it => Matches(it))); } else if (target is Items.Components.ItemComponent ic) { return(ic.Item.ContainedItems.Any(it => Matches(it))); } else if (target is Character character) { return(character.Inventory != null && character.Inventory.AllItems.Any(it => Matches(it))); } } switch (Type) { case ConditionType.PropertyValue: SerializableProperty property; if (target?.SerializableProperties == null) { return(Operator == OperatorType.NotEquals); } if (target.SerializableProperties.TryGetValue(AttributeName, out property)) { return(Matches(target, property)); } return(false); case ConditionType.Name: if (target == null) { return(Operator == OperatorType.NotEquals); } return((Operator == OperatorType.Equals) == (target.Name == AttributeValue)); case ConditionType.HasTag: { if (target == null) { return(Operator == OperatorType.NotEquals); } int matches = 0; foreach (string tag in SplitAttributeValue) { if (target is Item item && item.HasTag(tag)) { matches++; } } //If operator is == then it needs to match everything, otherwise if its != there must be zero matches. return(Operator == OperatorType.Equals ? matches >= SplitAttributeValue.Length : matches <= 0); } case ConditionType.HasStatusTag: if (target == null) { return(Operator == OperatorType.NotEquals); } bool success = false; if (StatusEffect.DurationList.Any(d => d.Targets.Contains(target)) || DelayedEffect.DelayList.Any(d => d.Targets.Contains(target))) { int matches = 0; foreach (DurationListElement durationEffect in StatusEffect.DurationList) { if (!durationEffect.Targets.Contains(target)) { continue; } foreach (string tag in SplitAttributeValue) { if (durationEffect.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= SplitAttributeValue.Length : matches <= 0; } foreach (DelayedListElement delayedEffect in DelayedEffect.DelayList) { if (!delayedEffect.Targets.Contains(target)) { continue; } foreach (string tag in SplitAttributeValue) { if (delayedEffect.Parent.HasTag(tag)) { matches++; } } } return(Operator == OperatorType.Equals ? matches >= SplitAttributeValue.Length : matches <= 0); } else if (Operator == OperatorType.NotEquals) { //no status effects, so the tags cannot be equal -> condition met return(true); } return(success); case ConditionType.SpeciesName: { if (target == null) { return(Operator == OperatorType.NotEquals); } if (!(target is Character targetCharacter)) { return(false); } return(Operator == OperatorType.Equals == targetCharacter.SpeciesName.Equals(AttributeValue, StringComparison.OrdinalIgnoreCase)); } case ConditionType.SpeciesGroup: { if (target == null) { return(Operator == OperatorType.NotEquals); } if (!(target is Character targetCharacter)) { return(false); } return(Operator == OperatorType.Equals == targetCharacter.Params.CompareGroup(AttributeValue)); } case ConditionType.EntityType: switch (AttributeValue) { case "character": case "Character": return((Operator == OperatorType.Equals) == target is Character); case "limb": case "Limb": return((Operator == OperatorType.Equals) == target is Limb); case "item": case "Item": return((Operator == OperatorType.Equals) == target is Item); case "structure": case "Structure": return((Operator == OperatorType.Equals) == target is Structure); case "null": return((Operator == OperatorType.Equals) == (target == null)); default: return(false); } case ConditionType.LimbType: { if (!(target is Limb limb)) { return(false); }
protected bool IsValidTarget(ISerializableEntity entity) { if (targetIdentifiers == null) { return(true); } if (entity is Item item) { if (targetIdentifiers.Contains("item")) { return(true); } if (item.HasTag(targetIdentifiers)) { return(true); } if (targetIdentifiers.Any(id => id == item.Prefab.Identifier)) { return(true); } } else if (entity is ItemComponent itemComponent) { if (targetIdentifiers.Contains("itemcomponent")) { return(true); } if (itemComponent.Item.HasTag(targetIdentifiers)) { return(true); } if (targetIdentifiers.Any(id => id == itemComponent.Item.Prefab.Identifier)) { return(true); } } else if (entity is Structure structure) { if (targetIdentifiers.Contains("structure")) { return(true); } if (targetIdentifiers.Any(id => id == structure.Prefab.Identifier)) { return(true); } } else if (entity is Character character) { if (targetIdentifiers.Contains("character")) { return(true); } if (targetIdentifiers.Any(id => id == character.SpeciesName)) { return(true); } } return(targetIdentifiers.Any(id => id == entity.Name)); }
private GUIComponent CreateColorField(ISerializableEntity entity, SerializableProperty property, Color value, int yPos, GUIComponent parent) { var label = new GUITextBlock(new Rectangle(0, yPos, 0, 18), property.Name, "", Alignment.TopLeft, Alignment.Left, parent, false, GUI.SmallFont); label.ToolTip = property.GetAttribute <Editable>().ToolTip; var colorBoxBack = new GUIFrame(new Rectangle(110 - 1, yPos - 1, 25 + 2, 18 + 2), Color.Black, Alignment.TopLeft, null, parent); var colorBox = new GUIFrame(new Rectangle(110, yPos, 25, 18), value, Alignment.TopLeft, null, parent); for (int i = 0; i < 4; i++) { new GUITextBlock(new Rectangle(140 + i * 70, yPos, 100, 18), colorComponentLabels[i], "", Alignment.TopLeft, Alignment.CenterLeft, parent, false, GUI.SmallFont); GUINumberInput numberInput = new GUINumberInput(new Rectangle(160 + i * 70, yPos, 45, 18), "", GUINumberInput.NumberType.Int, Alignment.Left, parent); numberInput.MinValueInt = 0; numberInput.MaxValueInt = 255; if (i == 0) { numberInput.IntValue = value.R; } else if (i == 1) { numberInput.IntValue = value.G; } else if (i == 2) { numberInput.IntValue = value.B; } else { numberInput.IntValue = value.A; } numberInput.Font = GUI.SmallFont; int comp = i; numberInput.OnValueChanged += (numInput) => { Color newVal = (Color)property.GetValue(); if (comp == 0) { newVal.R = (byte)(numInput.IntValue); } else if (comp == 1) { newVal.G = (byte)(numInput.IntValue); } else if (comp == 2) { newVal.B = (byte)(numInput.IntValue); } else { newVal.A = (byte)(numInput.IntValue); } if (property.TrySetValue(newVal)) { TrySendNetworkUpdate(entity, property); colorBox.Color = newVal; } }; } return(label); }
//private GUIComponent editingHUD; public SerializableEntityEditor(ISerializableEntity entity, bool inGame, GUIComponent parent, bool showName) : base("") { List <SerializableProperty> editableProperties = inGame ? SerializableProperty.GetProperties <InGameEditable>(entity) : SerializableProperty.GetProperties <Editable>(entity); if (parent != null) { parent.AddChild(this); } if (showName) { new GUITextBlock(new Rectangle(0, 0, 100, 20), entity.Name, "", Alignment.TopLeft, Alignment.TopLeft, this, false, GUI.Font); } int y = showName ? 30 : 10, padding = 10; foreach (var property in editableProperties) { //int boxHeight = 18; //var editable = property.Attributes.OfType<Editable>().FirstOrDefault(); //if (editable != null) boxHeight = (int)(Math.Ceiling(editable.MaxLength / 40.0f) * 18.0f); object value = property.GetValue(); GUIComponent propertyField = null; if (value is bool) { propertyField = CreateBoolField(entity, property, (bool)value, y, this); } else if (value.GetType().IsEnum) { propertyField = CreateEnumField(entity, property, value, y, this); } else if (value is string) { propertyField = CreateStringField(entity, property, (string)value, y, this); } else if (value is int) { propertyField = CreateIntField(entity, property, (int)value, y, this); } else if (value is float) { propertyField = CreateFloatField(entity, property, (float)value, y, this); } else if (value is Vector2) { propertyField = CreateVector2Field(entity, property, (Vector2)value, y, this); } else if (value is Vector3) { propertyField = CreateVector3Field(entity, property, (Vector3)value, y, this); } else if (value is Vector4) { propertyField = CreateVector4Field(entity, property, (Vector4)value, y, this); } else if (value is Color) { propertyField = CreateColorField(entity, property, (Color)value, y, this); } else if (value is Rectangle) { propertyField = CreateRectangleField(entity, property, (Rectangle)value, y, this); } if (propertyField != null) { y += propertyField.Rect.Height + padding; } } if (children.Count > 0) { SetDimensions(new Point(Rect.Width, children.Last().Rect.Bottom - Rect.Y + 10), false); } else { SetDimensions(new Point(Rect.Width, 0), false); } if (parent is GUIListBox) { ((GUIListBox)parent).UpdateScrollBarSize(); } }
public bool Matches(ISerializableEntity target) { string valStr = Value.ToString(); switch (Type) { case ConditionType.PropertyValue: SerializableProperty property; if (target.SerializableProperties.TryGetValue(PropertyName.ToLowerInvariant(), out property)) { return(Matches(property)); } return(false); case ConditionType.Name: return((Operator == OperatorType.Equals) == (target.Name == valStr)); case ConditionType.HasTag: { string[] readTags = valStr.Split(','); int matches = 0; foreach (string tag in readTags) { if (((Item)target).HasTag(tag)) { matches++; } } //If operator is == then it needs to match everything, otherwise if its != there must be zero matches. return(Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0); } case ConditionType.HasStatusTag: List <DurationListElement> durations = StatusEffect.DurationList.FindAll(d => d.Targets.Contains(target)); List <DelayedListElement> delays = DelayedEffect.DelayList.FindAll(d => d.Targets.Contains(target)); bool success = false; if (durations.Count > 0 || delays.Count > 0) { string[] readTags = valStr.Split(','); foreach (DurationListElement duration in durations) { int matches = 0; foreach (string tag in readTags) { if (duration.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { StatusEffect.DurationList.Remove(duration); } if (cancelStatusEffect != 2) //cancelStatusEffect 1 = only cancel once, cancelStatusEffect 2 = cancel all of matching tags { return(success); } } foreach (DelayedListElement delay in delays) { int matches = 0; foreach (string tag in readTags) { if (delay.Parent.HasTag(tag)) { matches++; } } success = Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0; if (cancelStatusEffect > 0 && success) { DelayedEffect.DelayList.Remove(delay); } if (cancelStatusEffect != 2) //ditto { return(success); } } } return(success); case ConditionType.SpeciesName: Character targetCharacter = target as Character; if (targetCharacter == null) { return(false); } return((Operator == OperatorType.Equals) == (targetCharacter.SpeciesName == valStr)); default: return(false); } }