void assignViolentWork(Unit_local worker) { UnitRelativePositionSorter comparer = new UnitRelativePositionSorter(worker.transform.position); comparer.DistanceMode(); remainingToPerish.Sort(comparer); List <Unit> inRange = new List <Unit>(); Unit closestHoplite = null; Unit closestDog = null; Unit closestCourier = null; for (int i = 0; i < remainingToPerish.Count && comparer.DistanceOf(remainingToPerish[i]) <= worker.stats.weapon_range; ++i) { inRange.Add(remainingToPerish[i]); if (closestHoplite == null && remainingToPerish[i].name.Contains("hoplite")) { closestHoplite = remainingToPerish[i]; } else if (closestDog == null && remainingToPerish[i].name.Contains("dog")) { closestDog = remainingToPerish[i]; } else if (closestCourier == null && remainingToPerish[i].name.Contains("courier")) { closestCourier = remainingToPerish[i]; } } Unit targetUnit; if (closestHoplite != null) { targetUnit = closestHoplite; } else if (closestDog != null) { targetUnit = closestDog; } else if (closestCourier != null) { targetUnit = closestCourier; } else { targetUnit = remainingToPerish[0]; } Task result = new Task(worker, Task.actions.attack, targetUnit.transform.position, targetUnit); worker.work(result); assignments.Add(result); }
public bool assignTransactionWork(Unit_local needsCounterparty, bool assignByTarget = false) { // Debug.Log("AssignTransactionWork"); Hashtable counterParties = null; Unit_local closestCounterparty = null; // We have to get a little weird with these, because closestCounterparty, which one of these two things will reference, can't be decided until we determine what the counterparties are, // which is done in the same IF statement that determines what the provider is. I know it's weird. Func <Unit_local> provider; Func <Unit_local> reciever; if (masterTask.nature == Task.actions.give ^ assignByTarget == true) { counterParties = remainingToAccept; provider = () => needsCounterparty; reciever = () => closestCounterparty; } else { counterParties = remainingToProvide; provider = () => closestCounterparty; reciever = () => needsCounterparty; } float bestDistance = 999999; foreach (Unit_local recieverCandidate in counterParties.Keys) { float distanceTo = Vector2.Distance(recieverCandidate.transform.position, needsCounterparty.transform.position); if (distanceTo < bestDistance) { closestCounterparty = recieverCandidate; bestDistance = distanceTo; } } Hashtable leftoverFactor; Hashtable exhaustedFactor; Unit_local unitWithLeftovers; Unit_local limitingUnit; int quantityToPass = 0; if (reciever().roomForMeat() == provider().meat) { quantityToPass = provider().meat; remainingToAccept.Remove(reciever); remainingToProvide.Remove(provider()); } else { if (reciever().roomForMeat() < provider().meat) { leftoverFactor = remainingToProvide; exhaustedFactor = remainingToAccept; limitingUnit = reciever(); unitWithLeftovers = provider(); quantityToPass = reciever().roomForMeat(); } else { leftoverFactor = remainingToAccept; exhaustedFactor = remainingToProvide; limitingUnit = provider(); unitWithLeftovers = reciever(); quantityToPass = provider().meat; } leftoverFactor[unitWithLeftovers] = (int)leftoverFactor[unitWithLeftovers] - (int)exhaustedFactor[limitingUnit]; exhaustedFactor.Remove(limitingUnit); } Task newTask; if (assignByTarget == false) { newTask = new Task(needsCounterparty, masterTask.nature, Vector2.zero, closestCounterparty, quantityToPass); needsCounterparty.work(newTask); } else { newTask = new Task(closestCounterparty, masterTask.nature, Vector2.zero, needsCounterparty, quantityToPass); closestCounterparty.work(newTask); } assignments.Add(newTask); return(true); }
public void MoveCohort(Vector2 goTo, Unit_local toFollow) { Stop(); masterTask = new Task(null, Task.actions.move, goTo, toFollow); List <Unit_local> thisIsToSupressWarnings = new List <Unit_local>(members); float weakLinkETA = 0; foreach (Unit_local toMove in thisIsToSupressWarnings) { if (toMove.stats.isMobile == false) { throw new InvalidOperationException("Cannot move a cohort that includes immobile members. Units should be sorted out in Gamestate.CombineActiveCohorts."); } else { float thisMoversETA = Vector2.Distance(toMove.transform.position, goTo) / toMove.stats.speed; if (thisMoversETA > weakLinkETA) { weakLinkETA = thisMoversETA; } } } UnitRelativePositionSorter vsGoTo = new UnitRelativePositionSorter(goTo); vsGoTo.DirectionMode(); List <Unit_local> unitsByDirection = new List <Unit_local>(members); // This is a list of units sorted by their compass-direction from the destination point. (unitsByDirection).Sort(vsGoTo); vsGoTo.DistanceMode(); List <Unit_local> unitsByDistance = new List <Unit_local>(members); unitsByDistance.Sort(vsGoTo); // Units are removed from unitsByDirection (and unitsByDistance) as they are assigned to groups, so this is a way of saying "while there are unassigned units." while (unitsByDirection.Count > 0) { // We take the current closest unit to the distination... (previous cycles of grouping will have removed closer units) Unit_local sliceLeader = unitsByDistance[0]; int leaderDirectionIndex = unitsByDirection.IndexOf(sliceLeader); float leaderDistanceFromDestination = vsGoTo.DistanceOf(sliceLeader); float leaderRadius = sliceLeader.bodyCircle.radius; int totalUnaccounted = unitsByDirection.Count; List <Unit_local> slice = new List <Unit_local> { sliceLeader }; // That unit will be the group leader. We check the units clockwise and counter-clockwise from its' position on the imaginary circle of unit positions aronud the destination. for (int sign = -1; sign <= 1 && slice.Count < unitsByDirection.Count; sign = sign + 2) { // this is a loop-breaker variable for (int indexOffset = sign; indexOffset < 1000; indexOffset += sign) { Unit_local inQuestion = unitsByDirection[(leaderDirectionIndex + indexOffset + totalUnaccounted) % totalUnaccounted]; // These are all in radians... float directionOfLeader = vsGoTo.DirectionOf(sliceLeader); float directionInQuestion = vsGoTo.DirectionOf(inQuestion); float circumferencialDistance = Mathf.Abs(directionOfLeader - directionInQuestion); circumferencialDistance = Mathf.Min(circumferencialDistance, 2 * Mathf.PI - circumferencialDistance); // ...until here, when circumferentialDistance becomes a measure of real distance. circumferencialDistance *= leaderDistanceFromDestination; // In both directions, we stop checking when the next-closest (by direction) unit doesn't fall within the shadow cast by the leader, if you imagine the destination as a light source. if (circumferencialDistance < leaderRadius && inQuestion != sliceLeader) { slice.Add(inQuestion); } else { break; } } } slice.Sort(vsGoTo); // This line makes it so that all groups arive at the same time. // PROBLEM: this will make fast units move slugishly for very short journeys. float leaderSpeed = Mathf.Clamp(Vector2.Distance(sliceLeader.transform.position, goTo) / weakLinkETA, 0, sliceLeader.stats.speed); sliceLeader.work(new Task(sliceLeader, Task.actions.move, goTo, toFollow, -1, leaderSpeed)); for (int followerIndex = 1; followerIndex < slice.Count; ++followerIndex) { slice[followerIndex].work(new Task(slice[followerIndex], Task.actions.move, goTo, sliceLeader)); } foreach (MobileUnit_local sliceMember in slice) { assignments.Add(sliceMember.task); unitsByDirection.Remove(sliceMember); unitsByDistance.Remove(sliceMember); } } }