public virtual float CalculateEquationDriveLevelChange(Agent agent, DriveType driveType, Mapping mapping, OutputChange outputChange) { float amount = 0f; if (outputChange != null) { amount = ChangeInOutputChange(agent, driveType, outputChange, mapping); } else { amount = mapping.EquationDriveTypeChangeInTree(agent, driveType); } float min = GetEquationMin(agent); float max = GetEquationMax(agent); float range = max - min; float rawAmount = Mathf.Clamp(GetEquationRawLevel(agent), min, max); float rawNewAmount = Mathf.Clamp(rawAmount + amount, min, max); float oldLevel = 100 - (rawAmount - min) / range * 100; float newLevel = 100 - (rawNewAmount - min) / range * 100; //Debug.Log(name + " CalcEquDriveLvl: mapping = " + mapping.mappingType.name + " outputChange = " + outputChange + " level = " + (newLevel - oldLevel)); return(newLevel - oldLevel); }
// Starts at this rootMapping and returns [driveAmount, timeEst, sideEffectsUtility] in the float array (only go through tree once) // If the drive uses an equation the entire drive level change is calculated above (see DriveType.CalculateEquationDriveLevelChange) public void CalcUtilityInfoForTree(Agent agent, DriveType driveType, float[] results, bool firstCall = true) { if (firstCall || driveType.syncType != DriveType.SyncType.Equation) { results[0] += CalcDriveReduction(agent, driveType); } results[1] += CalcTimeToComplete(agent); results[2] += CalcSideEffectsUtility(agent, driveType); if (children != null) { foreach (Mapping child in children) { if (child.children == null || child.children.Count == 0) { if (driveType.syncType != DriveType.SyncType.Equation) { results[0] += child.CalcDriveReduction(agent, driveType); } results[1] += child.CalcTimeToComplete(agent); results[2] += child.CalcSideEffectsUtility(agent, driveType); } else { child.CalcUtilityInfoForTree(agent, driveType, results, false); } } } }
// TODO: Should this not change plans if the new plan is the same? // Maybe Changes plans when between Mappings of the current plan public virtual int MaybeChangePlan(Agent agent, Mapping currentMapping, DriveType newDriveType, Dictionary <DriveType, Plans> newPlans, long timeToPlan, out DriveType selectedDriveType, out Mapping newMapping, out float bestUtility) { newMapping = null; selectedDriveType = newDriveType; int rootMappingIndex = -1; Mapping bestRootMapping = GetBestPlan(agent, newPlans[newDriveType], out bestUtility); if (bestRootMapping != null) { rootMappingIndex = newPlans[newDriveType].GetRootMappingIndex(bestRootMapping); // Start with first leave node on left most branch newMapping = bestRootMapping.GetLeftmostLeaf(); // Log the Changed Plans agent.historyType.RecordDeciderLog(agent, HistoryType.DeciderRunType.SwitchedPlans, newMapping); // Log the plans agent.historyType.RecordPlansLog(agent, null, agent.decider.currentDriveTypesRanked, newPlans, selectedDriveType, rootMappingIndex, timeToPlan); return(rootMappingIndex); } return(-1); }
// What should agent do when they have no valid plans? public virtual Dictionary <DriveType, Plans> DefaultPlans(Agent agent, out DriveType currentDriveType, out Mapping newMapping) { if (agent.noneDriveType != null && agent.noPlansMappingType != null) { currentDriveType = agent.noneDriveType; newMapping = new Mapping(agent.noPlansMappingType) { isComplete = true }; Plans plans = new Plans(currentDriveType, newMapping) { statuses = new List <Plans.Status>() { Plans.Status.Complete } }; return(new Dictionary <DriveType, Plans>() { { currentDriveType, plans } }); } currentDriveType = null; newMapping = null; return(null); }
public override void SetContext(Agent agent, Behavior.Context behaviorContext) { DriveType driveType = null; Agent targetAgent = null; if (useTargetsDriveLevel) { if (behaviorContext.target == null || behaviorContext.target.GetComponent <Agent>() == null) { Debug.LogError("WaitForDriveBehavior using targets drive is missing target or target is not an Agent."); } else { targetAgent = behaviorContext.target.GetComponent <Agent>(); driveType = (DriveType)agent.decider.CurrentMapping.mappingType.inputConditions[0].levelType; } } else { targetAgent = agent; driveType = agent.decider.CurrentDriveType; } if (!agentsContexts.TryGetValue(agent, out Context context)) { context = new Context(); agentsContexts[agent] = context; } context.attributeSelectors = behaviorContext.attributeSelectors; context.driveTypeToWaitFor = driveType; context.targetAgent = targetAgent; }
public float CalculateSEUtility(DriveType mainDriveType, Agent agent, Mapping mapping) { Entity target = GetEntityTarget(agent, mapping); float amount = outputChangeType.CalculateAmount(agent, target, this, mapping); mapping.previousOutputChangeAmount = amount; float utility = outputChangeType.CalculateSEUtility(agent, target, this, mapping, mainDriveType, amount); // See if this change will also change any of the usesEquation Drives float equationDrivesUtility = 0f; foreach (DriveType driveType in agent.ActiveDrives().Keys) { // If this drive is the main drive for the plan it should not be considered a side-effect // it will be considered in the rate of drive change for the plan if (driveType.syncType == DriveType.SyncType.Equation && driveType != mainDriveType && driveType.includeInSECalculations) { // Only looks at this specfic OutputChange float equationDrivesAmount = driveType.driveTypeEquation.CalculateEquationDriveLevelChange(agent, driveType, mapping, this); // DriveType to figure this change of drive level into the side effect utility // TODO: Just use value for now since the other side effects use value - add in utility later equationDrivesUtility += equationDrivesAmount * (driveType.sideEffectValue < 0.01f ? 1f : driveType.sideEffectValue); } } //Debug.Log(agent.name + ": " + mapping.mappingType.name + ": val: " + value + " amt: " + amount + " equ utl: " + equationDrivesUtility + " tot: " + (value * amount - equationDrivesUtility)); //Debug.Log(value + " * " + amount + " - " + equationDrivesUtility); // Subtract the equationDrivesUtility since a positive value is bad (drive increases) and a negative value is good (drive decreases) return(utility - equationDrivesUtility); }
public override string DisplayEquation(Agent agent, Mapping rootMapping, DriveType driveType, float driveAmount, float timeEst, float sideEffectsUtility) { float driveRate = -driveAmount / timeEst; float driveUtility = agent.drives[driveType].GetDriveUtility(); return("(" + driveUtility + " DU + " + DU_INFLU + " DU_INFLU * (1 - " + driveUtility + " DU)) * " + driveRate + " Drive Rate + " + SE_INFLU + " SE_INFLU * " + sideEffectsUtility + " SE"); }
// A member calls this when they change a faction drive level public void UpdateOtherMembersDriveLevel(Agent updatingAgent, DriveType driveType, float amount) { foreach (Agent agent in agents) { if (updatingAgent != agent) { agent.drives[driveType].ChangeFactionDriveLevel(amount); } } }
public float EquationDriveTypeChangeInMapping(Agent agent, DriveType driveType) { float amount = 0f; foreach (OutputChange outputChange in mappingType.outputChanges) { amount += driveType.driveTypeEquation.ChangeInOutputChange(agent, driveType, outputChange, this); } return(amount); }
private float CalcSideEffectsUtility(Agent agent, DriveType driveType) { float utility = 0f; foreach (OutputChange outputChange in mappingType.outputChanges) { utility += outputChange.CalculateSEUtility(driveType, agent, this); } sideEffectUtility = utility; return(utility); }
private void ResetDrives() { drives = new Dictionary <DriveType, Drive>(); foreach (AgentType.DefaultDrive defaultDrive in agentType.defaultDrives) { DriveType driveType = defaultDrive.driveType; Drive newDrive; if (defaultDrive.overrideDriveType) { newDrive = new Drive(this, driveType, defaultDrive.startingLevel, driveType.utilityCurve, defaultDrive.changePerGameHour, defaultDrive.rateTimeCurve, defaultDrive.minTimeCurve, defaultDrive.maxTimeCurve); } else { newDrive = new Drive(this, driveType, defaultDrive.startingLevel, driveType.utilityCurve, driveType.changePerGameHour, driveType.rateTimeCurve, driveType.minTimeCurve, driveType.maxTimeCurve); } drives.Add(defaultDrive.driveType, newDrive); } // Handle Overrides - goes from first one to last one foreach (AgentTypeOverride agentTypeOverride in entityTypeOverrides) { if (agentTypeOverride != null && agentTypeOverride.overrideDrives == EntityOverrideType.AddOrReplace) { for (int i = 0; i < agentTypeOverride.defaultDriveTypes.Count; i++) { // If drive already exists this will overwrite the attribute's value drives[agentTypeOverride.defaultDriveTypes[i]] = new Drive(this, agentTypeOverride.defaultDriveTypes[i], agentTypeOverride.defaultDriveLevels[i], agentTypeOverride.defaultDriveTypes[i].utilityCurve, agentTypeOverride.defaultDriveChangesPerHour[i]); } } else if (agentTypeOverride != null && agentTypeOverride.overrideDrives == EntityOverrideType.Remove) { for (int i = 0; i < agentTypeOverride.defaultDriveTypes.Count; i++) { drives.Remove(agentTypeOverride.defaultDriveTypes[i]); } } } // Make sure DriveTypes have a set utility curve // TODO: This should not be here - maybe in AgentType inspector? foreach (DriveType driveType in drives.Keys) { if (driveType.utilityCurve == null || driveType.utilityCurve.length == 0) { Debug.LogError("Utility curve is not set for " + driveType.name + " - Please fix this."); } } }
public Drive(Agent agent, DriveType driveType, float level, AnimationCurve utilityCurve, float changePerGameHour, AnimationCurve rateTimeCurve = null, float minTimeCurve = 0f, float maxTimeCurve = 0f) { this.agent = agent; levelType = driveType; this.driveType = driveType; this.level = level; this.utilityCurve = utilityCurve; this.changePerGameHour = changePerGameHour; this.rateTimeCurve = rateTimeCurve; this.minTimeCurve = minTimeCurve; this.maxTimeCurve = maxTimeCurve; }
// driveRate should be positive public override float Evaluate(Agent agent, Mapping rootMapping, DriveType driveType, float driveAmount, float timeEst, float sideEffectsUtility) { float driveRate = -driveAmount / timeEst; float driveUtility = agent.drives[driveType].GetDriveUtility(); if (verboseLogging) { Debug.Log("(" + driveUtility + " + " + DU_INFLU + " * (1 - " + driveUtility + ")) * " + driveRate + " + " + SE_INFLU + " * " + sideEffectsUtility); Debug.Log("DriveType Utility Multiplier = " + (driveUtility + DU_INFLU * (1 - driveUtility))); } return((driveUtility + DU_INFLU * (1 - driveUtility)) * driveRate + SE_INFLU * sideEffectsUtility); }
public virtual void RecordPlansLog(Agent agent, Dictionary <DriveType, float> driveTypesLevels, Dictionary <DriveType, float> driveTypesRanked, Dictionary <DriveType, Plans> allPlans, DriveType chosenDriveType, int chosenPlanIndex, long timeToPlan) { agentsPlansLogs[agent].Add(new PlansLog() { time = Time.time, driveTypesLevels = driveTypesLevels, driveTypesRanked = driveTypesRanked, allPlans = allPlans, chosenDriveType = chosenDriveType, chosenPlanIndex = chosenPlanIndex, timeToPlan = timeToPlan }); }
public override float ChangeInOutputChange(Agent agent, DriveType driveType, OutputChange outputChange, Mapping mapping) { if (storageTagType != null) { if (!mapping.target.HasTag(storageTagType)) { return(0f); } if (entityTaggedSameFaction) { if (agent.faction == null) { Debug.LogError(agent.name + ": StoredEntitiesDTE - entityTaggedSameFaction is checked but Agent is not in a Faction."); return(0f); } List <Entity> factionMembers = agent.faction.GetAllAgents().Cast <Entity>().ToList(); if (!factionMembers.Any(x => mapping.target.tags[storageTagType].Select(y => y.relatedEntity).Contains(x))) { return(0f); } } } // Want to find any inventory changes for storageCategory EntityTypes - where they gained or lost inventoryCategory EntityTypes // TODO: Handle parent Categories? if (inventoryIncreasesOTCs.Contains(outputChange.outputChangeType)) { // The Target EntityType has to have the storageCategory // The outputChange.inputOutputType has to have the inventoryCategory if (mapping.target != null && mapping.target.entityType.typeCategories.Contains(storageCategory) && mapping.inventoryTargets[outputChange.inventoryTypeGroupMatchIndex].entityType.typeCategories.Contains(entityStoredCategory)) { return(outputChange.outputChangeType.CalculateAmount(agent, mapping.target, outputChange, mapping)); } } else if (inventoryDecreasesOTCs.Contains(outputChange.outputChangeType)) { // The Target EntityType has to have the storageCategory // The outputChange.inputOutputType has to have the inventoryCategory if (mapping.target != null && mapping.target.entityType.typeCategories.Contains(storageCategory) && mapping.inventoryTargets[outputChange.inventoryTypeGroupMatchIndex].entityType.typeCategories.Contains(entityStoredCategory)) { return(-outputChange.outputChangeType.CalculateAmount(agent, mapping.target, outputChange, mapping)); } } return(0); }
public override float CalculateSEUtility(Agent agent, Entity target, OutputChange outputChange, Mapping mapping, DriveType mainDriveType, float amount) { // Negative amount is good (so flip sign) unless the target is an Enemy if (outputChange.targetType == OutputChange.TargetType.ToEntityTarget && agent.memoryType.KnownEntity(agent, target).rLevel < 50) { return(amount); } else if (outputChange.targetType == OutputChange.TargetType.ToSelf && mainDriveType == (DriveType)outputChange.levelType) { return(0f); } return(-amount); }
private void StartPlan(DriveType selectedDriveType, int newPlanIndex, float newPlanUtility, Mapping newMapping) { // Starting new plan - either because there was no plan or it decided to change plans between actions CurrentDriveType = selectedDriveType; currentPlans = AllCurrentPlans[selectedDriveType]; PreviousMapping = CurrentMapping; CurrentMapping = newMapping; CurrentPlanIndex = newPlanIndex; currentPlanUtility = newPlanUtility; PlanStartTime = Time.time; currentPlans.statuses[CurrentPlanIndex] = Plans.Status.Running; // This is for the drive modifyBonus for continuing to reduce the same drive PreviousDriveType = CurrentDriveType; }
public Plans(DriveType driveType, List <Mapping> mappings) { // Does this need to be copied? (ref vs val) this.driveType = driveType; rootMappings = mappings; driveAmountEstimates = new List <float>(); timeEstimates = new List <float>(); sideEffectsUtility = new List <float>(); utility = new List <float>(); foreach (Mapping mapping in mappings) { driveAmountEstimates.Add(-1f); timeEstimates.Add(-1f); sideEffectsUtility.Add(-1f); utility.Add(-1f); } }
public override bool Check(InputCondition inputCondition, Agent agent, Mapping mapping, Entity target, bool isRecheck) { Agent targetAgent = target as Agent; if (targetAgent != null) { DriveType driveType = (DriveType)inputCondition.levelType; if (targetAgent.ActiveDrives().ContainsKey(driveType)) { float driveLevel = targetAgent.drives[driveType].GetLevel(); if (driveLevel >= inputCondition.min && driveLevel <= inputCondition.max) { return(true); } } } return(false); }
public virtual bool GetPlans(Agent agent, Plans previousPlans, int previousPlansIndex, bool previouslyInterrupted, out DriveType currentDriveType, out Dictionary <DriveType, float> currentDriveTypesRanked, out Dictionary <DriveType, Plans> allPlans) { currentDriveType = null; Plans currentPlans = null; // Find mapping for highest priority drive currentDriveTypesRanked = RankDrives(agent.ActiveDrives()); allPlans = new Dictionary <DriveType, Plans>(); foreach (DriveType driveType in currentDriveTypesRanked.Keys) { currentPlans = plannerTypes[0].CreatePlansForDriveType(agent, driveType, false); if (currentPlans == null) { return(false); } allPlans.Add(driveType, currentPlans); //Debug.Log(agent.name + ": GetPlans for " + driveType.name + " found " + currentPlans.rootMappings.Count + " plans."); // If there is at least one complete plan we quit early // TODO: Improve this - planner should mark the Plans statuses // TODO: Add a minimum utility threshold so it doesn't quit and go with a bad plan List <Mapping> excludeRootMappings = null; if (previouslyInterrupted && previousPlans != null) { excludeRootMappings = new List <Mapping>() { previousPlans.rootMappings[previousPlansIndex] } } ; if (currentPlans.GetCompletePlans(excludeRootMappings).Count > 0) { //Debug.Log(agent.name + ": GetPlans for " + driveType.name + " found a completed plan."); currentDriveType = driveType; return(true); } } return(false); }
private void ResetNoPlans() { noPlansMappingType = agentType.defaultNoPlansMappingType; foreach (AgentTypeOverride agentTypeOverride in entityTypeOverrides) { if (agentTypeOverride != null && agentTypeOverride.defaultNoPlansMappingType != null) { noPlansMappingType = agentTypeOverride.defaultNoPlansMappingType; } } noneDriveType = agentType.defaultNoPlansDriveType; foreach (AgentTypeOverride agentTypeOverride in entityTypeOverrides) { if (agentTypeOverride != null && agentTypeOverride.defaultNoPlansDriveType != null) { noneDriveType = agentTypeOverride.defaultNoPlansDriveType; } } }
public override float ChangeLevel(float amount) { float initialValue = GetLevel(); float newValue = amount; if (attributeType is OneFloatAT) { newValue = ((OneFloatAT.OneFloatData)attributeType.SetData(entity, new OneFloatAT.OneFloatData() { floatValue = amount })).floatValue; } else if (attributeType is MinMaxFloatAT) { MinMaxFloatAT.MinMaxFloatData data = new MinMaxFloatAT.MinMaxFloatData() { floatValue = amount }; data = (MinMaxFloatAT.MinMaxFloatData)attributeType.SetData(entity, data); if (entity is Agent agent) { DriveType syncedDriveType = GetSyncedDriveType(agent); if (syncedDriveType != null) { agent.drives[syncedDriveType].ChangeAttributeSyncedDriveLevel(amount, GetMin(), GetMax()); } } newValue = data.floatValue; // TODO: Make 0.01f an option in Settings if (Mathf.Abs(newValue - initialValue) > 0.01f) { entity.RunEntityTriggers(null, EntityTrigger.TriggerType.LevelChange); } } else { return(0f); } return(newValue); }
public override bool GetPlans(Agent agent, Plans previousPlans, int previousPlansIndex, bool previouslyInterrupted, out DriveType currentDriveType, out Dictionary <DriveType, float> currentDriveTypesRanked, out Dictionary <DriveType, Plans> allPlans) { currentDriveType = defaultDriveType; currentDriveTypesRanked = new Dictionary <DriveType, float>() { { defaultDriveType, 1f } }; allPlans = new Dictionary <DriveType, Plans>(); Plans currentPlans = null; currentPlans = plannerTypes[0].CreatePlansForDriveType(agent, defaultDriveType, false); if (currentPlans == null) { return(false); } allPlans.Add(defaultDriveType, currentPlans); //Debug.Log(agent.name + ": GetPlans for " + driveType.name + " found " + currentPlans.rootMappings.Count + " plans."); List <Mapping> excludeRootMappings = null; if (previouslyInterrupted && previousPlans != null) { excludeRootMappings = new List <Mapping>() { previousPlans.rootMappings[previousPlansIndex] } } ; // TODO: Figure out the statuses mess - when should they be set? if (currentPlans.GetCompletePlans(excludeRootMappings).Count > 0) { return(true); } return(false); }
// This is called on root mappings and will traverse the plan tree figuring out the utility of all the side effects public float CalcSideEffectsUtilityForTree(Agent agent, DriveType driveType) { float utility = CalcSideEffectsUtility(agent, driveType); if (children != null) { foreach (Mapping child in children) { if (child.children == null || child.children.Count == 0) { utility += child.CalcSideEffectsUtility(agent, driveType); } else { utility += child.CalcSideEffectsUtilityForTree(agent, driveType); } } } return(utility); }
// Starts at this rootMapping and finds the Equation DriveType change for the rest of the tree public float EquationDriveTypeChangeInTree(Agent agent, DriveType driveType) { float amount = EquationDriveTypeChangeInMapping(agent, driveType); if (children != null) { foreach (Mapping child in children) { if (child.children == null || child.children.Count == 0) { amount += child.EquationDriveTypeChangeInMapping(agent, driveType); } else { amount += child.EquationDriveTypeChangeInTree(agent, driveType); } } } return(amount); }
public override float CalculateAmount(Agent agent, Entity target, OutputChange outputChange, Mapping mapping) { if (outputChange.valueType == OutputChange.ValueType.ActionSkillCurve) { return(outputChange.actionSkillCurve.Eval0to100(agent.ActionSkillWithItemModifiers(mapping.mappingType.actionType))); } else if (outputChange.valueType == OutputChange.ValueType.Selector) { return(outputChange.selector.GetFloatValue(agent, mapping)); } // This looks at the ENTIRE Plan for an equation DriveType // will only do it for root mappings (mapping.parent == null) so there's no double counting the changes DriveType driveType = (DriveType)outputChange.levelType; if (mapping != null && mapping.parent == null && driveType.syncType == DriveType.SyncType.Equation) { return(driveType.driveTypeEquation.CalculateEquationDriveLevelChange(agent, driveType, mapping, null)); } return(outputChange.floatValue); }
public float CalcDriveReduction(Agent agent, DriveType driveType) { float driveAmount = 0f; foreach (OutputChange outputChange in mappingType.outputChanges) { // Find output change that changes the main drive if (driveType == outputChange.levelType && outputChange.targetType == OutputChange.TargetType.ToSelf) { if (outputChange.timing == OutputChange.Timing.Repeating) { driveAmount += outputChange.changeEstimateForPlanner; } else { driveAmount += outputChange.outputChangeType.CalculateAmount(agent, agent, outputChange, this); } } } return(driveAmount); }
// Starts at this rootMapping and finds the DriveType change for the rest of the tree public float CalcDriveChangeForTree(Agent agent, DriveType driveType) { float driveChange = CalcDriveReduction(agent, driveType); // If the drive uses an equation the entire drive level change is calculated above - see DriveLevelOCT.CalculateAmount if (children != null && driveType.syncType != DriveType.SyncType.Equation) { foreach (Mapping child in children) { if (child.children == null || child.children.Count == 0) { driveChange += child.CalcDriveReduction(agent, driveType); } else { driveChange += child.CalcDriveChangeForTree(agent, driveType); } } } return(driveChange); }
public override float Evaluate(Agent agent, Mapping rootMapping, DriveType driveType, float driveAmount, float timeEst, float sideEffectsUtility) { if (rootMapping.mappingType.utilityModifierInfos == null || rootMapping.mappingType.utilityModifierInfos.Count == 0) { Debug.LogError(agent.name + ": UtilityAIUFT - Mapping Type " + rootMapping.mappingType.name + " has no Utility Modifiers."); return(0f); } float totalUtility = 0f; float totalWeight = 0f; foreach (MappingType.UtilityModifierInfo utilityModifierInfo in rootMapping.mappingType.utilityModifierInfos) { UtilityModifier utilityModifier = utilityModifierInfo.utilityModifier; float weight = utilityModifierInfo.weight; float utility = utilityModifier.utilityModifierType.Evaluate(utilityModifier, agent, rootMapping, out bool veto); if (verboseLogging) { Debug.Log(agent.name + ": UtilityAIUFT - utilityModifier = " + utilityModifier.name + ": utility = " + utility + " - weight = " + weight); } if (veto) { return(0f); } totalUtility += utility * weight; totalWeight += weight; } if (verboseLogging) { Debug.Log(agent.name + ": UtilityAIUFT - totalUtility = " + totalUtility + " - totalWeight = " + totalWeight); } return(totalUtility / totalWeight); }
public Plans(DriveType driveType, Mapping singleMapping) { this.driveType = driveType; rootMappings = new List <Mapping>() { singleMapping }; driveAmountEstimates = new List <float>() { -1f }; timeEstimates = new List <float>() { -1f }; sideEffectsUtility = new List <float>() { -1f }; utility = new List <float>() { -1f }; }