public ActionUtility(EntityAction action, Tuple <Need, double>[] values) { Action = action; UtilityDeltas = values; action.SetUtilityDeltas(UtilityDeltas); }
public override void Do(Entity entity) { double squaredDistance = Utilities.SquaredDistance(entity.Position, fleeObject.Position); if (squaredDistance < entity.VisionRange * entity.VisionRange) { double x = entity.Position.X + entity.Position.X - fleeObject.Position.X; double y = entity.Position.Y + entity.Position.Y - fleeObject.Position.Y; activeAction = new GotoAction(x, y, entity.Size); activeAction.Do(entity); } else { entity.ApplyNeedDeltas(UtilityDeltas); End(); } if (activeAction != null && activeAction.State == ActionState.Failure) // If the entity has hit a wall while running away, head to a random point. { OrderedPair <int> randomPoint = Utilities.GetDirectionalPoint(entity.CollisionDirection, entity.Position); activeAction = new GotoAction(randomPoint.X, randomPoint.Y, entity.Size); } }
public virtual bool TrySetAction(EntityAction newAction, bool locked = false) { if (Utilities.AreSameBaseType(Action, newAction)) // Don't assign an action of the same type. { return(false); } else { if (Action != null && !Action.IsActive) { Action.End(); } Action = newAction; ActionLocked = locked; return(true); } }
public void DecideAction(Animal animal, List <GameObject> nearbyObjects) { List <ActionUtility> actionAds = nearbyObjects. Where(o => o != animal) .SelectMany(o => o.GetAdvertisedActions(animal, 0.0)).ToList(); actionAds.Add(new ActionUtility(animal.IdleAction, new Tuple <Need, double>[] { ActionUtility.NewPair(Need.JobFullfilment, 0.002) })); EntityAction attackerResponseAction; // Add actions to respond to the attacker being nearby. // Bears will always attack nearby entities, but wolves might not, and goats and hogs will not. if (animal.Attacker != null && nearbyObjects.Contains(animal.Attacker)) { if (animal.Type == AnimalType.Bear) { attackerResponseAction = new AttackAction(animal.Attacker); } // Wolves will fight back if it can win. I.e. it will take less ticks for the wolf to kill the attacker than for the // attacker to kill the wolf. else if (animal.Type == AnimalType.Wolf && animal.GetHealth() / animal.Attacker.Strength > animal.GetHealth() / animal.Strength) { attackerResponseAction = new AttackAction(animal.Attacker); } else { attackerResponseAction = new FleeAction(animal.Attacker); } actionAds.Add(new ActionUtility(attackerResponseAction, new Tuple <Need, double>[] { ActionUtility.NewPair(Need.JobFullfilment, DDeltaConfig.attackerResponseDelta) })); } EntityAction bestAction = utilityDecider.GetBestAction(animal, actionAds); if (!Utilities.AreSameBaseType(animal.Action, bestAction)) { animal.TrySetAction(bestAction); } }
// Need to select action that gives the highest change to the lowest need values. 0.0 is max, 1.0 is min need. // Returns best action for now... public EntityAction GetBestAction(Entity entity, List <ActionUtility> actionAds) { double attenuator(double input) { return(5.0 / (input == 0.0 ? 0.000001 : input)); } double attenuatorDelta(Tuple <Need, double> deltaPair) { double currentValue = entity.GetNeedValue(deltaPair.Item1); double futureValue = currentValue + deltaPair.Item2; return(attenuator(currentValue) - attenuator(futureValue)); } Tuple <ActionUtility, double> createWeightPairs(ActionUtility actionUtility) { double weight = actionUtility.UtilityDeltas.Sum(deltaPair => attenuatorDelta(deltaPair)); Tuple <ActionUtility, double> weightedActionUtility = new Tuple <ActionUtility, double>(actionUtility, weight); return(weightedActionUtility); } // Pair each ActionUtility from actionAds with its score from sumFunction. This applies the attenuation function f(x) = 10.0 / x // to prioritize needs that are more important and favor actions that will reduce that need the most. List <Tuple <ActionUtility, double> > weightedActionAds = actionAds .Select(actionAd => createWeightPairs(actionAd)) .OrderByDescending(weightPair => weightPair.Item2) // Sort action advertisements by their weight value. Highest first. .Take(3) // Take the top 3 from the list. .ToList(); EntityAction action = WeightedRandomChoice(weightedActionAds).Action; return(action); // Assign a value to each action based on the increase in utility. Weight values nonlinearly based on the urgency of the need // List.Sum(attenuator(currentNeed) - attenuator(futureNeed)) }
public void DoActionOnce(EntityAction action) { action.Do(this); }
public override bool TrySetAction(EntityAction newAction, bool locked = false) { OnUpdateElement(ChangeType.UpdateElement, "Action", Action != null ? Action.GetName() : "None"); return(base.TrySetAction(newAction, locked)); }
// Uses the person's needs and the list of GameObjects that are nearby public void DecideAction(Person person, List <GameObject> nearbyObjects, int personCount, int food, int mouseX, int mouseY) { //person.SetAction(new FollowMouseAction(mouseX, mouseY, housePosition, houseRectangle)); double personCountMod = (personCount == 0 ? 1.0 : food / (2 * personCount)); List <ActionUtility> actionAds = nearbyObjects .Where(o => o != person) // Kinda clunky but this will allow extra data to control action advertisements. .SelectMany(o => o is Person ? o.GetAdvertisedActions(person, personCountMod) : o.GetAdvertisedActions(person, 0.0)) .ToList(); //if (personCount >= personLimit) // actionAds = actionAds.Where(ad => !(ad.Action is MateAction)).ToList(); // Limit the number of people that can be present at one time. This would be more efficient as a check inside Person to prevent // people from advertising a mate action if their are too many people. if (personCount >= personLimit) { actionAds = actionAds.Where(au => !(au.Action is MateAction)).ToList(); } double deliverFoodDelta = person.GetItemCount(ItemType.Apple) / 2; // Return food to house. actionAds.Add(new ActionUtility(new DeliverFoodAction(houseRectangle), new Tuple <Need, double>[] { ActionUtility.NewPair(Need.JobFullfilment, deliverFoodDelta) })); // Wander around. actionAds.Add(new ActionUtility(person.IdleAction, new Tuple <Need, double>[] { ActionUtility.NewPair(Need.JobFullfilment, DDeltaConfig.idleActionAdvertisement) })); // Hunger need. if ((person.GetItemCount(ItemType.Apple) > 0 || food > 0) && person.Hunger <= 0.5) { actionAds.Add(new ActionUtility(new EatAction(), new Tuple <Need, double>[] { ActionUtility.NewPair(Need.Hunger, 1.0 - person.Hunger) })); } // Tiredness need. if ((person.Tiredness < 0.6 || person.Health < 50) && !person.AttackerInRange()) // Sleep required. ** Add check to see if attacker is nearby **. { actionAds.Add(new ActionUtility(new SleepAction(Person.sleepSeconds, true), new Tuple <Need, double>[] { ActionUtility.NewPair(Need.Tiredness, DDeltaConfig.sleepActionAdvertisement), ActionUtility.NewPair(Need.Health, DDeltaConfig.sleepHealthAdvertisement) })); } else // Napping optional. This should be picked more often unless there are a lot of other actions to pick. { actionAds.Add(new ActionUtility(new SleepAction(Person.napSeconds, false), new Tuple <Need, double>[] { ActionUtility.NewPair(Need.Tiredness, DDeltaConfig.napActionDelta) })); } if (person.GetRectangleF().IntersectsWith(houseRectangle)) { actionAds.Add(dropUtility); } EntityAction attackerResponseAction; if (person.Attacker != null && nearbyObjects.Contains(person.Attacker)) { if (person.Sex == Sex.Male && person.GetHealth() / person.Attacker.Strength > person.GetHealth() / person.Strength) { attackerResponseAction = new AttackAction(person.Attacker); } else { attackerResponseAction = new FleeAction(person.Attacker); } actionAds.Add(new ActionUtility(attackerResponseAction, new Tuple <Need, double>[] { ActionUtility.NewPair(Need.JobFullfilment, DDeltaConfig.attackerResponseDelta) })); } // *** Apply action ad filters here from EntitController's tasks *** ApplyTaskFilters(actionAds); // NOTE: Doesn't filter actions that give 0.0 delta or a negative! EntityAction bestAction = utilityDecider.GetBestAction(person, actionAds); if (!Utilities.AreSameBaseType(person.Action, bestAction)) { if (!(bestAction is WanderAction)) // Remove leftover velocity from a WanderAction. { person.Stop(); } person.TrySetAction(bestAction); } }
public void AddTask(EntityAction taskAction, double mod) { Tasks.Add(new Task(taskAction, mod)); }
public void ForceAction(Entity entity, EntityAction action) { action.Do(entity); }
public Task(EntityAction action, double mod) { Action = action; ActionMod = mod; }
public FleeAction(GameObject entity) : base(true) { fleeObject = entity; idleAction = new WanderAction(); }
// Cause the entity to wait and schedule when it can move again with an additional action. public WaitAction(int seconds, EntityAction action, bool interruptible = true) : base(seconds, interruptible) { IsInterruptible = interruptible; finalAction = action; }
// Cause the entity to wait and schedule when it can move again. public WaitAction(int seconds, bool interruptible = true) : base(seconds, interruptible) { IsInterruptible = interruptible; finalAction = null; }