//Check the creature's stats + priorities to see if they need to change private void CheckBehaviourState() { //Check stats + priorities //If priorities/Stats change, change FSM //Dont forget to change "currentState" either! //Priotity Order: // 1. SAFETY 2. WATER 3. FOOD 4. SPACE //First check if traits are ok BEHAVIOUR_STATE targetState = currentState; //If current "Engaged" (Actively reproducing etc) do not change state if (currentState == BEHAVIOUR_STATE.ENGAGED) { return; } if (GetComponent <Stats>().canMate&& InTheMood()) //3 core stats below are at 75% or higher AND is of age { targetState = BEHAVIOUR_STATE.REPRODUCE; } else { float currentVal = traits["Energy Level"]; if (currentVal <= capacityTraits["Energy Level"] * 0.15) { targetState = BEHAVIOUR_STATE.SLEEP; } currentVal = traits["Food Level"]; if (currentVal <= capacityTraits["Food Level"] * 0.15) { targetState = BEHAVIOUR_STATE.EAT; } currentVal = traits["Water Level"]; if (currentVal <= capacityTraits["Water Level"] * 0.25) { targetState = BEHAVIOUR_STATE.DRINK; //Only override IF is less than Water val and in immindent danger - prevent lfip flopping if (traits["Food Level"] < currentVal) { if (traits["Food Level"] <= capacityTraits["Food Level"] * 0.15f) { targetState = BEHAVIOUR_STATE.EAT; } } } } float energyVal = traits["Energy Level"]; if (energyVal <= 0) { traits["Energy Level"] = 0.0f; targetState = BEHAVIOUR_STATE.SLEEP; } //Check make sure the water + food levels aren't at dangerous levels if (GetComponent <CreatureManager>().population.averageIntellect >= GlobalGEPSettings.MIN_WATER_STORE_INTELLECT && GetComponent <CreatureManager>().population.popWaterStores.currentAmount <= 0.20f) { currentPriority = PRIORITY.WATER; } else if (GetComponent <CreatureManager>().population.popFoodStores.currentAmount <= 0.20f) { currentPriority = PRIORITY.FOOD; } //Then check the Priorities. Do space first...check POPULATION class for space avialability (Could ignore this for first prototype) //Do Food Storage, Water Storage and Space availability Checks here //PRIORITY tempPriority = CheckPriority(); //For Fitness evaluation, keep the fitness in that state for 1 period (otherwise it wll be constantly changing) // Possibly a setting for "sensitivity"? //This focuses on CONSTITUTION ATTRIBUTE for reproduction/Fitness/Attractiveness etc //This focus will be linked directly with the Population class that has a Population_Status variable, tracking whether the pop is colonizing // or not. If colonizing then the population may only be in "SPACE" Priority //Extremely Vain creatures will still take Charisma into account (IE if Vanity >= averageVanity + 25-50%, add Charisma to the fitness calculations) if (GetComponent <CreatureManager>().population.popState == POPULATION_STATE.MIGRATE) { //Explore for a new population location // A random number of creatures from the population (between 25-50%) will split off and create a new population centre // They will then choose a random location a minimum distance away from the original location (AILERP) // They will not Hunt, or gather until a new location is found // If not enough space will never find a new location and will eventually starve // Alternatively, any other population becomes an enemy so may attack // If this is the case, will need to make sure they don't attack original population (and vice versa) // Possibly add a check of "immunity" for 1 period, or until far enough away // During immunity, either ALL species are non-threat (including food so will not hunt them) // OR can set it so only their own species is a non-threat (will have to program additional check if they can hunt yet etc) // But can be attacked and killed whilst searching elsewhere if this is the case. // This priority will be active and be linked with the population until the pop has settled. // //When inthis state, a creature is part of a "New Population" (colony) and is preparing to move out //Each pop will set their energy, food and water levels to max, and then head out. //Whilst in this state, they are moving, and if one stops to sleep, all stop to sleep. Same with eat + drink until food runs out. //Can still mate en-route to new location, and will look at the constitution attribute //Check if there is sufficient time to live (IE minimum of 1.5-2 periods) //If is in Space mode/Migrating, then make population.population[0] act as the leader/alpha, and have every other population follow them // Swarm behaviour? (0<-1-<-2<-3<-4....) if x distance from 0, start a new column? // OR move randomly, but when gets certain distance from Alpha, make goal to move to their last position (AILERP) // then continue random movement etc //Still need to check hunger etc - but apply Swarm AI behaviours energyVal = traits["Energy Level"]; if (energyVal <= 0) { traits["Energy Level"] = 0.0f; targetState = BEHAVIOUR_STATE.SLEEP; } } else { //This focuses on STRENGTH/CONSTITUTION ATTRIBUTES for reproduction/Fitness/Attractiveness etc //Extremely Vain creatures will still take Charisma into account (IE if Vanity >= averageVanity + 25-50%, add Charisma to the fitness calculations) if (currentPriority == PRIORITY.SAFETY) { //Determine Fight vs Flight // Use Wisdom + intellect to compare their strength with the average strenght of population // If one of the strongest members in the population, more likely to fight // If one ofthe weaker members, more likely to flee // If has low wisdom or intellect, AND one of weaker members, increased chance they will fight (despite risk of losing) // If wins...increase their Charisma??? // If has low wisdom/intellect, And one of stronger members, possibly increased chance will flee? (Wisdom/Intellect acting as 'courage' etc) //Could combine ATTACK/FLEE states into one substate ("COMBAT") which then divides into ATTACK/FLEE? // Potentially more optimized // Could perform these fight vs flight checks in there rather than in AI behaviour //Check if there is a threat near by (IE another pop within a certain distance AND is Omnivore/Carnivore) if (threatNearby) { float wisdom = GetComponent <Stats>().attributes["wisdom"]; float intellect = GetComponent <Stats>().attributes["intellect"]; float strength = GetComponent <Stats>().attributes["strength"]; //If one of the strongest in the population then fight //Otherwise, if Wisdom * Intellect (/10) added to strength is STILL less than average strength, Fight //Otherwise flee // IE W=30, I=25, S=8. Avg = 15. // (3 * 2.5) + 8 = 15.5 // Results in Fleeing (smart enough to know not strong enough) // // IE W=10, I=25, S=8. Avg = 15 // (1 * 2.5) + 8 = 10.5 // Results in fighting (Too irrational/not smart enough to realize weaker and less chance of winning etc) bool fightFlight = strength >= GetComponent <CreatureManager>().population.averageStrength ? true : ((wisdom / 10) * (intellect / 10) + strength) <= GetComponent <CreatureManager>().population.averageStrength ? true : false; if (fightFlight) { targetState = BEHAVIOUR_STATE.ATTACK; } else { //Temporarily set to Attack. Eventually will be Flee // This will need to have it so other pop members come to help if in range (which requires 'Update Priorities' for Safety to work // Regarding checking nearby threats (Detection radius) // During fleeing, use 2x energy targetState = BEHAVIOUR_STATE.ATTACK; } } //Cause creature to pass out from exhaustion, regardless of the other stats //Difference between this and the first is the first instance of energy level is by the creature's choice //This will be forced on the entity regardless and cannot be overridden (aside from imminent danger - ADRENALINE!) // Will only "pass out" if energy is at negative capacity (absolutely no energy that even adrenaline does nothing) // Can result in being killed if still being pursued energyVal = traits["Energy Level"]; if (energyVal <= 0 && energyVal >= -capacityTraits["Energy Level"]) { traits["Energy Level"] = 0.0f; targetState = BEHAVIOUR_STATE.FLEE; } else if (energyVal < -capacityTraits["Energy Level"]) { traits["Energy Level"] = capacityTraits["Energy Level"]; targetState = BEHAVIOUR_STATE.SLEEP; } } //This focuses on INTELLECT ATTRIBUTE for reproduction/Fitness/Attractiveness etc //Extremely Vain creatures will still take Charisma into account (IE if Vanity >= averageVanity + 25-50%, add Charisma to the fitness calculations) else if (currentPriority == PRIORITY.WATER) { //Find fresh water and/or refille supplies //Drink FSM will use water sources but find it's own if null //This priority will update list of water sources before assigning this individual to gather water //Only assign a hunter if current water level is lower than 80% // AND if there are less than 5 gatherers // Otherwise allow them to do whatever they were doing before if (GetComponent <CreatureManager>().population.popWaterStores.currentAmount < 0.80f && GetComponent <CreatureManager>().population.gatherers < 2) { if (targetState != BEHAVIOUR_STATE.EAT && targetState != BEHAVIOUR_STATE.DRINK) { //Change to GATHER - Gather = Gather up water FindWaterSources(); //targetState = BEHAVIOUR_STATE.HUNT; } } energyVal = traits["Energy Level"]; if (energyVal <= 0) { traits["Energy Level"] = 0.0f; targetState = BEHAVIOUR_STATE.SLEEP; } } //This focuses on INTELLECT/STRENGTH ATTRIBUTES for reproduction/Fitness/Attractiveness etc //Extremely Vain creatures will still take Charisma into account (IE if Vanity >= averageVanity + 25-50%, add Charisma to the fitness calculations) else if (currentPriority == PRIORITY.FOOD) { //Find Food sources/Hunt and/or gather more food // If this creature is not already eating, and not too many hunters, assign this pop to a hunter //HERBIVORES DO NOT HUNT - no need to store food because can just eat from the ground // Later on could add in Trees/fruits etc that they can gather //Hunting: // 1. Find nearest population (ideally threatening/enemy) // 2. Find nearest creature from that population // 3. Move to that creature // 4. Start attacking creature // 5. If victorious return to pop centre with food //Only assign a hunter if current food level is lower than 80% // AND if there are less than 5 hunters // Otherwise allow them to do whatever they were doing before float val = GetComponent <CreatureManager>().population.hunters; float amt = GetComponent <CreatureManager>().population.popFoodStores.currentAmount; if (GetComponent <CreatureManager>().population.popFoodStores.currentAmount < 0.60f && GetComponent <CreatureManager>().population.hunters < 2) { //Has minimum HP to right AND not pregnant if (GetComponent <Stats>().currHP >= GetComponent <Stats>().maxHP * 0.25f && !GetComponent <Stats>().pregnant) { //Is at least of average strength if (attributes["strength"] >= (GetComponent <CreatureManager>().population.averageStrength - (GetComponent <CreatureManager>().population.averageStrength * 0.25f))) { isHunter = true; GetComponent <CreatureManager>().population.hunters++; } } } else if (GetComponent <CreatureManager>().population.popFoodStores.currentAmount >= 0.65f && isHunter) { isHunter = false; GetComponent <CreatureManager>().population.hunters--; } if (isHunter) { if (targetState != BEHAVIOUR_STATE.EAT && targetState != BEHAVIOUR_STATE.DRINK) { targetState = BEHAVIOUR_STATE.HUNT; } } energyVal = traits["Energy Level"]; if (energyVal <= 0) { traits["Energy Level"] = 0.0f; targetState = BEHAVIOUR_STATE.SLEEP; } } //This focuses on Vanity attribute, ignoring all Survival attributes (even if REALLY good). The population is complacent and cares about // physical attractiveness of mates rather than practical/survival attractiveness of mates else if (currentPriority == PRIORITY.QUALITY) { //Do whatever - Don't change the FSM/Activity, just change the Fitness evaluation //Check that all needs are met (as with all other priorities where relevant) } } //Change the current state //Defaults to Idle if (currentState != targetState) { //Force an update to the creature window if its open updateStats = true; switch (targetState) { case BEHAVIOUR_STATE.SLEEP: ChangeState(new Sleep(), BEHAVIOUR_STATE.SLEEP); break; case BEHAVIOUR_STATE.EAT: ChangeState(new Eat(), BEHAVIOUR_STATE.EAT); break; case BEHAVIOUR_STATE.DRINK: ChangeState(new Drink(), BEHAVIOUR_STATE.DRINK); break; case BEHAVIOUR_STATE.REPRODUCE: ChangeState(new Reproduce(), BEHAVIOUR_STATE.REPRODUCE); break; case BEHAVIOUR_STATE.HUNT: ChangeState(new Hunt(), BEHAVIOUR_STATE.HUNT); break; case BEHAVIOUR_STATE.EXPLORE: ChangeState(new Sleep(), BEHAVIOUR_STATE.EXPLORE); break; case BEHAVIOUR_STATE.ATTACK: ChangeState(new Attack(), BEHAVIOUR_STATE.ATTACK); break; case BEHAVIOUR_STATE.FLEE: ChangeState(new Sleep(), BEHAVIOUR_STATE.FLEE); break; default: ChangeState(new Idle(), BEHAVIOUR_STATE.IDLE); break; } } }
//Change FSM state (Behaviours) public void ChangeState(FSMState <AIBehaviour> state, BEHAVIOUR_STATE newState) { previousState = currentState; currentState = newState; fsm.ChangeState(state); }