// Store information on whether each red unit is carrying a civilian or not void updateRedUnitDatabase() { // Clear redUnitDatabase.Clear(); // Get all red units redUnits = new List <GameObject>(); redUnits.AddRange(GameObject.FindGameObjectsWithTag("RedTruck")); redUnits.AddRange(GameObject.FindGameObjectsWithTag("RedDismount")); // Loop through all actor beliefs and update the database foreach (Belief_Actor b in actorBeliefs) { // We care only about red units Belief_Actor ba = (Belief_Actor)b; if (ba.getAffiliation().Equals((int)Affiliation.RED)) { // Save the info if the belief is new enough // Note: Watch out for the difference of two unsigned numbers! if (thisSoaActor.getCurrentTime_ms() <= ba.getBeliefTime() || (float)(thisSoaActor.getCurrentTime_ms() - ba.getBeliefTime()) <= beliefTimeout_ms) { RedUnitInfo redUnitInfo = new RedUnitInfo(ba, redUnits, redBases, protectedSites); if (redUnitInfo.distToClosestRedBase > redBaseKeepoutDist * SimControl.KmToUnity) { redUnitDatabase.Add(ba.getId(), new RedUnitInfo(ba, redUnits, redBases, protectedSites)); } } } } }
// Keeps track of a pursue list (really map) with hysterisis distances to closest base as condition // for being added/removed from map. Each key is an actor ID for a red unit and the value is the // updated distance to closest base void markPursueCandidates() { // Look at each red unit in the database and determine if it should be a candidate to be pursued foreach (int id in redUnitDatabase.Keys) { // Get the record RedUnitInfo redUnitInfo = redUnitDatabase[id]; // Estimate how long it takes for me to get there float myTravelTime = GetRangeTo(redUnitInfo.closestRedBasePos) / thisNavAgent.speed; // Estimate red unit round trip time float redRoundTripTime; if (redUnitInfo.hasCivilian) { // Guess that it is going straight to closest base redRoundTripTime = redUnitInfo.distToClosestRedBase / redUnitInfo.maxSpeed; // Determine if I can get it before it returns to base (include some inefficiencies) if (myTravelTime < redRoundTripTime) { // Mark as someone who can be caught in time redUnitInfo.isCatchable = true; } } else { if (redUnitInfo.distToClosestRedBase > redBaseKeepoutDist * SimControl.KmToUnity) { // Guess that it will first go to closest protected site and then to closest base redRoundTripTime = (redUnitInfo.distToClosestProtectedSite + Vector3.Distance(redUnitInfo.closestProtectedSitePos, redUnitInfo.closestRedBasePos)) / redUnitInfo.maxSpeed; // Determine if I can get it before it returns to base (include some inefficiencies) if (myTravelTime < redRoundTripTime) { // Mark as someone who can be caught in time redUnitInfo.isCatchable = true; } } } } }
// Assigns a task to current police unit based on the pursueList public void assignTask() { if (redUnitDatabase.Keys.Count == 0) { // Transition from pursue to patrol if (currTask != Task.PATROL) { // Pick a new patrol target currTask = Task.PATROL; patrolTarget = assignNewProtectedSite(patrolTarget); } else if (Vector3.Distance(transform.position, patrolTarget.transform.position) <= clearPatrolRange * SimControl.KmToUnity) { // We are close enough to a target to clear it, choose to stay or leave with some probability if (Random.value <= transitionProbability) { patrolTarget = assignNewProtectedSite(patrolTarget); } else { // We choose to stay here for another update, keep the same task } } else { // We are patrolling but have not gotten to target yet, keep the same task } } else { // Set intent to pursue currTask = Task.PURSUE; // Create 4 lists for determining who to chase after List <int> isCatchableHasCivilian = new List <int>(); List <int> isCatchableNoCivilian = new List <int>(); List <int> notCatchableHasCivilian = new List <int>(); List <int> notCatchableNoCivilian = new List <int>(); // Pick a pursuit candidate with civilian to pursue float minCost = float.PositiveInfinity; // Categorize the candidates foreach (int id in redUnitDatabase.Keys) { // Get the record RedUnitInfo redUnitInfo = redUnitDatabase[id]; if (redUnitInfo.isCatchable && redUnitInfo.hasCivilian) { isCatchableHasCivilian.Add(id); } else if (redUnitInfo.isCatchable && !redUnitInfo.hasCivilian) { isCatchableNoCivilian.Add(id); } else if (!redUnitInfo.isCatchable && redUnitInfo.hasCivilian) { notCatchableHasCivilian.Add(id); } else { notCatchableNoCivilian.Add(id); } } // Find the pursuit target based on priority and min cost if (isCatchableHasCivilian.Count != 0) { // First priority: is catchable and has civilian foreach (int id in isCatchableHasCivilian) { float actorCost = GetRangeTo(redUnitDatabase[id].pos); if (actorCost < minCost) { minCost = actorCost; pursueActorID = id; } } } if (float.IsInfinity(minCost) && isCatchableNoCivilian.Count != 0) { // Second priority: is catchable but no civilian foreach (int id in isCatchableNoCivilian) { float actorCost = GetRangeTo(redUnitDatabase[id].pos); if (actorCost < minCost) { minCost = actorCost; pursueActorID = id; } } } if (float.IsInfinity(minCost) && notCatchableHasCivilian.Count != 0) { // Third priority: not catchable but has civilian foreach (int id in notCatchableHasCivilian) { float actorCost = GetRangeTo(redUnitDatabase[id].pos); if (actorCost < minCost) { minCost = actorCost; pursueActorID = id; } } } if (float.IsInfinity(minCost) && notCatchableNoCivilian.Count != 0) { // Last priority: not catchable and no civilian foreach (int id in notCatchableNoCivilian) { float actorCost = GetRangeTo(redUnitDatabase[id].pos); if (actorCost < minCost) { minCost = actorCost; pursueActorID = id; } } } } }