/// <summary> /// Gets an ability value. /// If the stacking rule in the mod is DoNotStack, an arbitrary matching ability will be chosen. /// If there are no values, null will be returned. /// </summary> /// <param name="name">The name of the ability.</param> /// <param name="obj">The object from which to get the value.</param> /// <param name="index">The ability value index (usually 1 or 2).</param> /// <param name="filter">A filter for the abilities. For instance, you might want to filter by the ability grouping rule's value.</param> /// <returns>The ability value.</returns> public static string GetAbilityValue(this IAbilityObject obj, string name, int index = 1, bool includeShared = true, bool includeEmpireCommon = true, Func <Ability, bool> filter = null) { if (obj == null) { return(null); } var abils = obj.Abilities(); if (includeShared) { abils = abils.Union(obj.SharedAbilities()); } if (includeEmpireCommon) { abils = abils.Union(obj.EmpireCommonAbilities()); } abils = abils.Where(a => a.Rule != null && a.Rule.Matches(name) && a.Rule.CanTarget(obj.AbilityTarget) && (filter == null || filter(a))); abils = abils.Stack(obj); if (!abils.Any()) { return(null); } return(abils.First().Values[index - 1]); }
/// <summary> /// All abilities belonging to an object, before stacking. /// </summary> /// <param name="obj"></param> /// <param name="includeShared"></param> /// <returns></returns> public static IEnumerable <Ability> UnstackedAbilities(this IAbilityObject obj, bool includeShared, Func <IAbilityObject, bool> sourceFilter = null) { if (obj == null) { return(Enumerable.Empty <Ability>()); } IEnumerable <Ability> result; if (sourceFilter == null || sourceFilter(obj)) { result = obj.IntrinsicAbilities.Concat(obj.DescendantAbilities(sourceFilter)).Concat(obj.AncestorAbilities(sourceFilter)); } else { result = obj.DescendantAbilities(sourceFilter).Concat(obj.AncestorAbilities(sourceFilter)); } if (includeShared) { result = result.Concat(obj.SharedAbilities(sourceFilter)); } return(result); }
private void CreateProjectiles(float angleToTarget, AbilityInput input) { foreach (Muzzle muzzle in Ability.Behaviour.Muzzles) { IAbilityObject abilityObject = muzzle.Spawner.Spawn(Ability, muzzle.Multiplier); abilityObject.GameObject.transform.position = Ability.Owner.transform.position; float angle = GetAngle(muzzle, angleToTarget); Vector2 direction = angle.GetDirection(); if (Ability.Behaviour.UseOrthographicScaling) { direction = Utility.ScaleToOrthographicVector(direction); } if (abilityObject is ISetTargetDirection directionSetter) { directionSetter.SetTargetDirection(direction); } if (abilityObject is ISetTargetPosition positionSetter) { positionSetter.SetTargetPosition(input.TargetPosition); } if (abilityObject is IPersistentAbilityObject persistentObject) { PersistentObjects.Add(persistentObject); } } }
/// <summary> /// Gets abilities that have been shared to an object. /// </summary> /// <param name="obj"></param> /// <returns></returns> public static IEnumerable <Ability> SharedAbilities(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { // Unowned objects cannot have abilities shared to them. var ownable = obj as IOwnableAbilityObject; if (ownable == null || ownable.Owner == null) { yield break; } // update cache if necessary foreach (var clause in ownable.Owner.ReceivedTreatyClauses.Flatten().OfType <ShareAbilityClause>()) { var tuple = Tuple.Create(ownable, clause.Owner); if (Empire.Current == null || !Galaxy.Current.SharedAbilityCache.ContainsKey(tuple)) { Galaxy.Current.SharedAbilityCache[tuple] = FindSharedAbilities(ownable, clause).ToArray(); } } // get cached abilities foreach (var keyTuple in Galaxy.Current.SharedAbilityCache.Keys.Where(k => k.Item1 == ownable && (sourceFilter == null || sourceFilter(k.Item2)))) { foreach (var abil in Galaxy.Current.SharedAbilityCache[keyTuple]) { yield return(abil); } } }
public IAbilityObject Spawn(Ability ability, float multiplier) { IAbilityObject abilityObject = Value.Spawn(ability, multiplier); HierarchyManager.Add(abilityObject.GameObject, HierarchyCategory.AbilityObjects); return(abilityObject); }
/// <summary> /// Abilities passed up from descendant objects. /// </summary> /// <param name="obj"></param> /// <param name="includeShared"></param> /// <returns></returns> public static IEnumerable <Ability> DescendantAbilities(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { var abils = new List <Ability>(); foreach (var c in obj.Descendants(sourceFilter)) { abils.AddRange(c.IntrinsicAbilities); } return(abils.Where(a => a.Rule == null || a.Rule.CanTarget(obj.AbilityTarget))); }
/// <summary> /// Abilities inherited from ancestor objects. /// </summary> /// <param name="obj"></param> /// <param name="includeShared"></param> /// <returns></returns> public static IEnumerable <Ability> AncestorAbilities(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { var abils = new List <Ability>(); foreach (var p in obj.Ancestors(sourceFilter).ExceptSingle(null)) { abils.AddRange(p.IntrinsicAbilities); } return(abils.Where(a => a.Rule == null || a.Rule.CanTarget(obj.AbilityTarget))); }
protected override void DoDamage(RaycastHit2D hit) { IAbilityObject obj = explosionObject.Spawn(Ability, Multiplier); if (obj is ISetTargetPosition positionSetter) { positionSetter.SetTargetPosition(transform.position); } Destroy(gameObject); }
/// <summary> /// Determines if an object has a specified ability. /// </summary> /// <param name="obj"></param> /// <param name="abilityName"></param> /// <returns></returns> public static bool HasAbility(this IAbilityObject obj, string abilityName, bool includeShared = true) { IEnumerable <Ability> abils; if (includeShared && obj is IOwnableAbilityObject) { abils = obj.UnstackedAbilities(true).Union(obj.SharedAbilities()); } else { abils = obj.UnstackedAbilities(true); } return(abils.Any(abil => abil.Rule != null && abil.Rule.Matches(abilityName))); }
/// <summary> /// All abilities belonging to an object. /// </summary> /// <param name="obj"></param> /// <returns></returns> public static IEnumerable <Ability> Abilities(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { if (obj == null) { return(Enumerable.Empty <Ability>()); } if (sourceFilter == null && Galaxy.Current != null && Galaxy.Current.IsAbilityCacheEnabled) { // use the ability cache if (Galaxy.Current.AbilityCache[obj] == null) { Galaxy.Current.AbilityCache[obj] = obj.UnstackedAbilities(true, sourceFilter).Stack(obj).ToArray(); } return(Galaxy.Current.AbilityCache[obj]); } return(obj.UnstackedAbilities(true, sourceFilter).Stack(obj)); }
public static IEnumerable <IAbilityObject> Ancestors(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { if (obj == null) { yield break; } // TODO - filter out duplicate ancestors foreach (var p in obj.Parents.ExceptSingle(null)) { if (p != null && (sourceFilter == null || sourceFilter(p))) { yield return(p); foreach (var x in p.Ancestors(sourceFilter)) { yield return(x); } } } }
public static IEnumerable <IAbilityObject> Descendants(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { var result = new HashSet <IAbilityObject>(); if (obj != null) { foreach (var c in obj.Children) { if (c != null && !result.Contains(c) && (sourceFilter == null || sourceFilter(c))) { result.Add(c); foreach (var x in c.Descendants(sourceFilter)) { result.Add(x); } } } } return(result); }
public static IEnumerable <IAbilityObject> Descendants(this IAbilityObject obj, Func <IAbilityObject, bool>?sourceFilter = null) { // we can't use a HashSet here because e.g. ships can have multiples of the same engine template installed List <IAbilityObject> result = new(); if (obj != null) { foreach (var c in obj.Children) { if (c is not null && (sourceFilter is null || sourceFilter(c))) { result.Add(c); foreach (var x in c.Descendants(sourceFilter)) { result.Add(x); } } } } return(result); }
/// <summary> /// Gets any abilities that can be activated. /// </summary> /// <param name="obj"></param> /// <returns></returns> public static IDictionary <Ability, IAbilityObject> ActivatableAbilities(this IAbilityObject o) { if (o is Vehicle) { return(((Vehicle)o).ActivatableAbilities()); } if (o is Planet) { return(((Planet)o).ActivatableAbilities()); } var dict = new Dictionary <Ability, IAbilityObject>(); foreach (var a in o.Abilities()) { if (a.Rule.IsActivatable) { dict.Add(a, o); } } return(dict); }
/// <summary> /// Finds empire-common abilities inherited by an object (e.g. empire abilities of a sector in which a ship resides). /// </summary> /// <param name="obj"></param> /// <param name="sourceFilter"></param> /// <returns></returns> public static IEnumerable <Ability> EmpireCommonAbilities(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { // Unowned objects cannot empire common abilities. var ownable = obj as IOwnableAbilityObject; if (ownable == null || ownable.Owner == null) { yield break; } // Where are these abilities coming from? // Right now they can only come from ancestors, since sectors and star systems are the only common ability objects. // TODO - Would it make sense for them to come from descendants? What kind of common ability object could be used as a descendant of an owned object? var ancestors = obj.Ancestors(sourceFilter).OfType <ICommonAbilityObject>(); // What abilities do we have? foreach (var ancestor in ancestors) { foreach (var abil in ancestor.EmpireAbilities(ownable.Owner, sourceFilter)) { yield return(abil); } } }
public static ILookup <Ability, Ability> AbilityTree(this IAbilityObject obj, Func <IAbilityObject, bool> sourceFilter = null) { return(obj.UnstackedAbilities(true, sourceFilter).StackToTree(obj)); }
public static string GetAbilityValue(this IEnumerable <IAbilityObject> objs, string name, IAbilityObject stackTo, int index = 1, bool includeShared = true, bool includeEmpireCommon = true, Func <Ability, bool> filter = null) { var tuples = objs.Squash(o => o.Abilities()); if (includeShared) { tuples = tuples.Union(objs.Squash(o => o.SharedAbilities())); } if (includeEmpireCommon) { tuples = tuples.Union(objs.Squash(o => o.EmpireCommonAbilities())); } var abils = tuples.GroupBy(t => new { Rule = t.Item2.Rule, Object = t.Item1 }).Where(g => g.Key.Rule.Matches(name) && g.Key.Rule.CanTarget(g.Key.Object.AbilityTarget)).SelectMany(x => x).Select(t => t.Item2).Where(a => filter == null || filter(a)).Stack(stackTo); if (!abils.Any()) { return(null); } return(abils.First().Values[index - 1]); }
public static ILookup <Ability, Ability> StackAbilitiesToTree(this IEnumerable <IAbilityObject> objs, IAbilityObject stackTo) { return(objs.SelectMany(obj => obj.Abilities()).StackToTree(stackTo)); }
private ILookup <Ability, Ability> Stack(IEnumerable <Ability> abilities, IAbilityObject stackingTo, bool groupStacking) { if (abilities.Count() <= 1) { return(abilities.ToLookup(a => a, a => a)); } var results = new SafeDictionary <Ability, Ability>(); // keys = original abilities, values = stacked abilities foreach (var abil in abilities) { for (int i = 0; i < abil.Values.Count; i++) { AbilityValueRule rule; if (groupStacking) { rule = GroupRules.ElementAtOrDefault(i); } else { rule = ValueRules.ElementAtOrDefault(i); } // TODO - don't repeatedly convert to/from strings, just do it once outside the loop double?oldval = null; if (results[abil] != null) { oldval = results[abil].Values.Count > i ? (double?)results[abil].Values[i].Value.ToDouble() : null; } else { var match = results.Values.Distinct().Where(a => a != null).SingleOrDefault(a => a.Rule == abil.Rule && a.Values.Select((val, idx) => rule != AbilityValueRule.Group && rule != AbilityValueRule.None || a.Values.Count >= abil.Values.Count && a.Values[idx] == abil.Values[idx]).All(b => b)); if (match != null) { results[abil] = match; oldval = results[abil].Values.Count > i ? (double?)results[abil].Values[i].Value.ToDouble() : null; } else { results[abil] = new Ability(stackingTo, abil.Rule); } } double incoming = abil.Values.Count > i ? abil.Values[i].Value.ToDouble() : 0; double newval = oldval ?? 0; if (rule == AbilityValueRule.Add) { newval = (oldval ?? 0) + incoming; } else if (rule == AbilityValueRule.TakeAverage) { newval = (oldval ?? 0) + incoming / abilities.Count(); } else if (rule == AbilityValueRule.TakeHighest) { if (oldval == null) { newval = incoming; } else { newval = Math.Max(oldval.Value, incoming); } } else if (rule == AbilityValueRule.TakeLowest) { if (oldval == null) { newval = incoming; } else { newval = Math.Min(oldval.Value, incoming); } } else // group or none { newval = incoming; } if (results[abil].Values.Count > i) { results[abil].Values[i] = newval.ToString(CultureInfo.InvariantCulture); } else { while (results[abil].Values.Count < i) { results[abil].Values.Add(null); } results[abil].Values.Add(newval.ToString(CultureInfo.InvariantCulture)); } } } foreach (var kvp in results) { if (results.Values.Where(a => a == kvp.Value).Count() == 1) { // ability is "stacked" alone, just use the original ability description results[kvp.Key].Description = kvp.Key.Description; } } return(results.ToLookup(kvp => kvp.Value, kvp => kvp.Key)); }
/// <summary> /// Groups and stacks abilities. /// </summary> /// <param name="abilities"></param> /// <returns></returns> public ILookup <Ability, Ability> GroupAndStack(IEnumerable <Ability> abilities, IAbilityObject stackingTo) { var ours = abilities.Where(a => a.Rule == this).ToArray(); // group abilities var dict = new SafeDictionary <IList <string>, ICollection <Ability> >(); var groupIndices = new List <int>(); for (int i = 0; i < ValueRules.Count; i++) { if (ValueRules[i] == AbilityValueRule.Group) { groupIndices.Add(i); } } foreach (var a in ours) { // treat non-group indices as "equal" for grouping purposes var key = a.Values.Select((v, i) => groupIndices.Contains(i) ? v.Value : "").ToList(); var existingKey = dict.Keys.SingleOrDefault(k => k.SequenceEqual(key)); if (existingKey == null) { dict[key] = new List <Ability>(); dict[key].Add(a); } else { dict[existingKey].Add(a); } } // stack abilities var stackedInGroups = new SafeDictionary <Ability, IEnumerable <Ability> >(); foreach (var group in dict.Values) { var stacked = Stack(group, stackingTo, false); foreach (var stack in stacked) { stackedInGroups.Add(stack.Key, stack); } } var final = new List <Tuple <Ability, Ability> >(); // stack grouped abilities if needed if (ValueRules.Any(r => r == AbilityValueRule.Group)) { var groupLeaders = stackedInGroups.Select(g => g.Key); var stacked = Stack(groupLeaders, stackingTo, true); foreach (var stack in stacked) { foreach (var a in stack) { final.Add(Tuple.Create(stack.Key, a)); } } } else { foreach (var group in stackedInGroups) { final.Add(Tuple.Create(group.Key, group.Key)); } } return(final.ToLookup(t => t.Item1, t => t.Item2)); }
public static IEnumerable <Ability> Stack(this IEnumerable <Ability> abilities, IAbilityObject stackTo) { return(abilities.StackToTree(stackTo).Select(g => g.Key)); }
public static void ClearAbilityCache(this IAbilityObject o) { Galaxy.Current.AbilityCache.Remove(o); }
/// <summary> /// Stacks any abilities of the same type according to the current mod's stacking rules. /// Keeps the original abilities in a handy tree format under the stacked abilities /// so you can tell which abilities contributed to which stacked abilities. /// </summary> /// <param name="abilities"></param> /// <param name="stackTo">The object which should own the stacked abilities.</param> /// <returns></returns> public static ILookup <Ability, Ability> StackToTree(this IEnumerable <Ability> abilities, IAbilityObject stackTo) { var stacked = new List <Tuple <Ability, Ability> >(); var grouped = abilities.GroupBy(a => a.Rule); foreach (var group in grouped) { if (group.Key == null) { continue; // invalid ability rule } var lookup = group.Key.GroupAndStack(group, stackTo); foreach (var lgroup in lookup) { foreach (var abil in group) { stacked.Add(Tuple.Create(lgroup.Key, abil)); } } } return(stacked.ToLookup(t => t.Item1, t => t.Item2)); }
private void btnOK_Click(object sender, EventArgs e) { var selRows = gridAbilities.SelectedRows.Cast <DataGridViewRow>(); if (selRows.Count() != 1) { // TODO - allow activation of multiple abilities at once MessageBox.Show("Please select a single ability to activate."); } else { dynamic sel = selRows.Single().DataBoundItem; DialogResult result; // TODO - "Space Object Destroyed On Use" ability if (sel.IsDestroyedOnUse) { IAbilityObject toBeDestroyed = sel.Source is IHull ? sobj : sel.Source; result = MessageBox.Show("Activate this ability of " + sel.Source + "?\n" + sel.Ability + "\n" + toBeDestroyed + " will be destroyed!", "Confirm Activation", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } else { result = MessageBox.Show("Activate this ability of " + sel.Source + "?\n" + sel.Ability, "Confirm Activation", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } if (result == DialogResult.Yes) { bool needsTarget; IEnumerable <IReferrable> targets = Enumerable.Empty <IReferrable>(); IReferrable target = null; string targetType = "targets"; var abil = sel.Ability as Ability; var src = sel.Source as IAbilityObject; Func <string> customCheck = () => null; // custom check for space objects being able to do stuff // TODO - move custom check functions into a utility class for use on the server side too if (abil.Rule.Matches("Emergency Resupply")) { // TODO - allow resupplying other ships nearby? needsTarget = false; } else if (abil.Rule.Matches("Emergency Energy")) { needsTarget = false; } else if (abil.Rule.Matches("Self-Destruct")) { needsTarget = false; } else if (abil.Rule.Matches("Open Warp Point")) { // find systems in range needsTarget = true; targets = Galaxy.Current.StarSystemLocations.Where(l => sobj.StarSystem != l.Item && sobj.StarSystem.Coordinates.EightWayDistance(l.Location) <= abil.Value1.ToInt()).Select(l => l.Item); targetType = "star systems within {0} light-years".F(abil.Value1); } else if (abil.Rule.Matches("Close Warp Point")) { // find warp points in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <WarpPoint>(); targetType = "warp points"; } else if (abil.Rule.Matches("Create Planet")) { // find asteroids in sector, and make sure there's at least one star needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <AsteroidField>(); targetType = "asteroid fields"; customCheck = () => { if (!sobj.StarSystem.FindSpaceObjects <Star>().Any()) { return("We can't create a planet in a system without a star."); } return(null); }; } else if (abil.Rule.Matches("Destroy Planet")) { // find planets in sector that are small enough to destroy needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Planet>().Where(p => (int)p.StellarSize <= abil.Value1.ToInt()); targetType = "planets not exceeding size {0}".F(abil.Value1); } else if (abil.Rule.Matches("Create Star")) { // make sure this isn't a special star system but it doesn't have any stars in it needsTarget = false; customCheck = () => { // TODO - implement star system physical type /* * if (sobj.StarSystem.PhysicalType != StarSystemPhysicalType.Empty) * return "We can only create a star in an empty system."; */ return(null); }; } else if (abil.Rule.Matches("Destroy Star")) { // find stars in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Star>(); targetType = "stars"; } else if (abil.Rule.Matches("Create Storm")) { needsTarget = false; } else if (abil.Rule.Matches("Destroy Storm")) { // find storms in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Storm>(); targetType = "storms"; } else if (abil.Rule.Matches("Create Nebula")) { // don't need to pick a star, just make sure one exists needsTarget = false; customCheck = () => { if (!sobj.Sector.SpaceObjects.OfType <Star>().Any()) { return("Creating a nebula requires a star in the sector."); } return(null); }; } else if (abil.Rule.Matches("Destroy Nebula")) { needsTarget = false; customCheck = () => { // TODO - implement star system physical type /* * if (sobj.StarSystem.PhysicalType != StarSystemPhysicalType.Nebulae) * return "There is no nebula in this system to destroy."; */ return(null); }; } else if (abil.Rule.Matches("Create Black Hole")) { // don't need to pick a star, just make sure one exists needsTarget = false; customCheck = () => { if (!sobj.Sector.SpaceObjects.OfType <Star>().Any()) { return("Creating a black hole requires a star in the sector."); } return(null); }; } else if (abil.Rule.Matches("Destroy Black Hole")) { needsTarget = false; customCheck = () => { // TODO - implement star system physical type /* * if (sobj.StarSystem.PhysicalType != StarSystemPhysicalType.BlackHole) * return "There is no black hole in this system to destroy."; */ return(null); }; } else if (abil.Rule.Matches("Create Constructed Planet From Star")) { // find stars in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Star>(); targetType = "stars"; } else if (abil.Rule.Matches("Create Constructed Planet From Planet")) { // find planets in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Planet>(); targetType = "planets"; } else if (abil.Rule.Matches("Create Constructed Planet From Storm")) { // find storms in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <Storm>(); targetType = "storms"; } else if (abil.Rule.Matches("Create Constructed Planet From Warp Point")) { // find warp points in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <WarpPoint>(); targetType = "warp points"; } else if (abil.Rule.Matches("Create Constructed Planet From Asteroids")) { // find asteroids in sector needsTarget = true; targets = sobj.Sector.SpaceObjects.OfType <AsteroidField>(); } else if (abil.Rule.Matches("Create Constructed Planet From Space")) { needsTarget = false; } else { MessageBox.Show(abil + " cannot be activated."); return; } // check for targets if (needsTarget) { if (!targets.Any()) { MessageBox.Show("There are no suitable {0} available.".F(targetType)); return; } } // check custom conditions var error = customCheck(); if (error != null) { MessageBox.Show(error); return; } // pick target if (needsTarget) { var picker = new GenericPickerForm(targets); this.ShowChildForm(picker); if (picker.DialogResult == DialogResult.OK) { target = picker.SelectedObject as IReferrable; } else { return; } } // issue command var order = new ActivateAbilityOrder(sel.Source, sel.Ability, target); var cmd = new AddOrderCommand(sobj, order); cmd.Execute(); Empire.Current.Commands.Add(cmd); Close(); } } }