public void AutoArrangeAnimals(IServerSocket serverSocket, AnimalManager animalManager, bool triggerChoice, bool discardExcess, bool isBreeding) { //Entry room holds 2 of same type //Pasture holds 2 of same type //Large pasture holds 4 of same time //Stable on pasture doubles capacity //Stable on meadow holds 1 animal //Ore/Ruby mine -> 1 donkey each //Stable on forest holds 1 pig //Dogs go anywhere... but preferably on meadow/pasture //dog(s) on meadow holds dogs+1 sheep OR meadow capacity //dog(s) on pasture holder dogs+1 sheep OR pasture capacity //other areas exist as 'blocks' //dogs can cause these to be variable... there will be P*D combinations, where //P = number of meadows/pastures, D = number of dogs //maybe just do these if we have excess sheep... which will be allocated last anyway. List<AnimalHolder> animalsHolders = new List<AnimalHolder>(); //add starting tile AnimalHolder startingTile = new AnimalHolder(); startingTile.TileType = TileTypes.StartingTile; startingTile.genericCapacity = 2; startingTile.position = new Vector2(0f, 0f); startingTile.isCave = true; animalsHolders.Add(startingTile); //add single fenced areas List<Vector2> smallFencedAreas = GetMatchingForestTileSpaces(TileTypes.SmallFence); foreach (Vector2 smallPasture in smallFencedAreas) { AnimalHolder pasture = new AnimalHolder(); pasture.TileType = TileTypes.SmallFence; pasture.isCave = false; pasture.genericCapacity = 2; pasture.position = smallPasture; if (StableSpaces[(int)smallPasture.x, (int)smallPasture.y] == TileTypes.Stable) pasture.genericCapacity = pasture.genericCapacity*2; animalsHolders.Add(pasture); } //add double fenced areas for(int i=0; i< _doubleFencedPastures.Count; i+=2) { AnimalHolder pasture = new AnimalHolder(); pasture.TileType = TileTypes.BigFence1; pasture.isCave = false; pasture.genericCapacity = 4; pasture.position = _doubleFencedPastures[i]; if (StableSpaces[(int)_doubleFencedPastures[i].x, (int)_doubleFencedPastures[i].y] == TileTypes.Stable) pasture.genericCapacity = pasture.genericCapacity * 2; if (StableSpaces[(int)_doubleFencedPastures[i+1].x, (int)_doubleFencedPastures[i+1].y] == TileTypes.Stable) pasture.genericCapacity = pasture.genericCapacity * 2; animalsHolders.Add(pasture); } //add meadows with stables List<Vector2> meadows = GetMatchingForestTileSpaces(TileTypes.Clearing); foreach (Vector2 meadow in meadows) { AnimalHolder clearing = new AnimalHolder(); clearing.TileType = TileTypes.Clearing; clearing.isCave = false; clearing.genericCapacity = 0; clearing.position = meadow; if (StableSpaces[(int)meadow.x, (int)meadow.y] == TileTypes.Stable) clearing.genericCapacity = 1; animalsHolders.Add(clearing); } //add forests with stables List<Vector2> forestPositions = GetMatchingForestTileSpaces(null); foreach (Vector2 position in forestPositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = null; holder.isCave = false; holder.genericCapacity = 0; holder.position = position; if (StableSpaces[(int)position.x, (int)position.y] == TileTypes.Stable) holder.pigCapacity = 1; animalsHolders.Add(holder); } //add ore mines List<Vector2> oreMinePositions = GetMatchingCaveTileSpaces(TileTypes.OreMine); foreach (Vector2 position in oreMinePositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = TileTypes.OreMine; holder.isCave = true; holder.donkeyCapacity = 1; holder.position = position; animalsHolders.Add(holder); } //add ruby mines List<Vector2> rubyMinePositions = GetMatchingCaveTileSpaces(TileTypes.RubyMine); foreach (Vector2 position in rubyMinePositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = TileTypes.RubyMine; holder.isCave = true; holder.donkeyCapacity = 1; holder.position = position; animalsHolders.Add(holder); } //add special buildings List<Vector2> mixedDwellingPositions = GetMatchingCaveTileSpaces(BuildingTypes.MixedDwelling); foreach (Vector2 position in mixedDwellingPositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = BuildingTypes.MixedDwelling; holder.isCave = true; holder.genericCapacity = 2; holder.position = position; animalsHolders.Add(holder); } List<Vector2> breakfastRoomPositions = GetMatchingCaveTileSpaces(BuildingTypes.BreakfastRoom); foreach (Vector2 position in breakfastRoomPositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = BuildingTypes.BreakfastRoom; holder.isCave = true; holder.cowCapacity = 3; holder.position = position; animalsHolders.Add(holder); } List<Vector2> cuddleRoomPositions = GetMatchingCaveTileSpaces(BuildingTypes.CuddleRoom); foreach (Vector2 position in cuddleRoomPositions) { AnimalHolder holder = new AnimalHolder(); holder.TileType = BuildingTypes.CuddleRoom; holder.isCave = true; holder.sheepCapacity = _player.GetDwarfStatus().Count; holder.position = position; animalsHolders.Add(holder); } //add whatever the dogs are doing int cowsToAllocate = animalManager.Cows; int pigsToAllocate = animalManager.Pigs; int donkeysToAllocate = animalManager.Donkeys; int sheepToAllocate = animalManager.Sheep; int dogsToAllocate = animalManager.Dogs; //Dogs spread between meadows as evenly as possible //sheepWatched = Math.Min(dogs, meadows)*2 //if dogs > meadows //sheepWatched += dogs-meadows //e.g, 5 dogs 2 meadows --> 7 sheep //---> possible exception for stabled meadow... assign dogs to this last (generic capacity 1) // //If no meadows, dogs cluster in lowest capacity pasture to maximise extra sheep capacity // //If no pastures, dogs can live in the home dwelling or near the house or something // //When filling areas, do not assign sheep/generic areas until after all sheep only areas. //Then assign sheep only if there are MORE sheep than generic capacity (ie, extra space will get used) //Otherwise, treat as normal //ASSIGN DOGS if (dogsToAllocate > 0) { if (animalsHolders.Count(x => x.TileType == TileTypes.Clearing) > 0) { List<AnimalHolder> clearings = animalsHolders.Where(x => x.TileType == TileTypes.Clearing).OrderBy(x => x.genericCapacity).ToList(); while (dogsToAllocate > 0) { foreach (AnimalHolder clearing in clearings) { clearing.AddDog(ref dogsToAllocate); } } } else if (animalsHolders.Count(x => x.TileType == TileTypes.SmallFence || x.TileType == TileTypes.BigFence1) > 0) { AnimalHolder smallestCapacity = animalsHolders.Where(x => x.TileType == TileTypes.SmallFence || x.TileType == TileTypes.BigFence1).OrderBy(x => x.genericCapacity).First(); smallestCapacity.FillAnimals(ResourceTypes.Dogs, ref dogsToAllocate); } else { //put them in the house AnimalHolder tile = animalsHolders.First(x => x.TileType == TileTypes.StartingTile); tile.FillAnimals(ResourceTypes.Dogs, ref dogsToAllocate); } } //FIRST PASS - areas only for specific animals while (cowsToAllocate > 0) { //first find spaces that only hold cows List<AnimalHolder> cowHolders = animalsHolders.FindAll(x => x.IsUnfilledCowHolder); if (cowHolders.Count > 0) { cowHolders[0].FillAnimals(ResourceTypes.Cows, ref cowsToAllocate); continue; } //nowhere to put them! break; } while (pigsToAllocate > 0) { //first find spaces that only hold pigs List<AnimalHolder> pigHolders = animalsHolders.FindAll(x => x.IsUnfilledPigHolder); if (pigHolders.Count > 0) { pigHolders[0].FillAnimals(ResourceTypes.Pigs, ref pigsToAllocate); continue; } //nowhere to put them! break; } while (donkeysToAllocate > 0) { //first find spaces that only hold donkeys List<AnimalHolder> donkeyHolders = animalsHolders.FindAll(x => x.IsUnfilledDonkeyHolder); if (donkeyHolders.Count > 0) { donkeyHolders[0].FillAnimals(ResourceTypes.Donkeys, ref donkeysToAllocate); continue; } //nowhere to put them! break; } //When filling areas, do not assign sheep & generic areas until after all sheep only areas. //Then assign sheep only if there are MORE sheep than generic capacity (ie, extra space will get used) while (sheepToAllocate > 0) { //first find spaces that ONLY hold sheep List<AnimalHolder> sheepHolders = animalsHolders.FindAll(x => x.IsUnfilledSheepHolder && x.genericCapacity ==0); if (sheepHolders.Count > 0) { sheepHolders[0].FillAnimals(ResourceTypes.Sheep, ref sheepToAllocate); continue; } //then places that will get more sheep assigned than generic capacity sheepHolders = animalsHolders.FindAll(x => x.IsUnfilledSheepHolder && sheepToAllocate > x.genericCapacity); if (sheepHolders.Count > 0) { sheepHolders[0].FillAnimals(ResourceTypes.Sheep, ref sheepToAllocate); continue; } //other places get assigned as normal break; } //SECOND PASS - take group with most animals, put in biggest pen. Repeat. bool fillMade = true; while (fillMade) { int maxAnimals = new[] {cowsToAllocate, pigsToAllocate, donkeysToAllocate, sheepToAllocate}.Max(); fillMade = false; if (cowsToAllocate > 0 && cowsToAllocate == maxAnimals) { //find the space that holds most cows, and put them there List<AnimalHolder> primaryHolders = animalsHolders.FindAll(x => x.RemainingCowCapacity > 0).OrderByDescending(x => x.RemainingCowCapacity) .ToList(); if (primaryHolders.Count > 0) { primaryHolders[0].FillAnimals(ResourceTypes.Cows, ref cowsToAllocate); fillMade = true; } } else if (pigsToAllocate > 0 && pigsToAllocate == maxAnimals) { //find the space that holds most cows, and put them there List<AnimalHolder> primaryHolders = animalsHolders.FindAll(x => x.RemainingPigCapacity > 0).OrderByDescending(x => x.RemainingPigCapacity) .ToList(); if (primaryHolders.Count > 0) { primaryHolders[0].FillAnimals(ResourceTypes.Pigs, ref pigsToAllocate); fillMade = true; } } else if (donkeysToAllocate > 0 && donkeysToAllocate == maxAnimals) { //find the space that holds most cows, and put them there List<AnimalHolder> primaryHolders = animalsHolders.FindAll(x => x.RemainingDonkeyCapacity > 0).OrderByDescending(x => x.RemainingDonkeyCapacity) .ToList(); if (primaryHolders.Count > 0) { primaryHolders[0].FillAnimals(ResourceTypes.Donkeys, ref donkeysToAllocate); fillMade = true; } } else if (sheepToAllocate > 0 && sheepToAllocate == maxAnimals) { //find the space that holds most cows, and put them there List<AnimalHolder> primaryHolders = animalsHolders.FindAll(x => x.RemainingSheepCapacity > 0).OrderByDescending(x => x.RemainingSheepCapacity) .ToList(); if (primaryHolders.Count > 0) { primaryHolders[0].FillAnimals(ResourceTypes.Sheep, ref sheepToAllocate); fillMade = true; } } } //now draw it to the screen foreach (AnimalHolder holder in animalsHolders) { serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Dogs, 0); serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Sheep, 0); serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Donkeys, 0); serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Pigs, 0); serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Cows, 0); if (holder.GetDogs() > 0) serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Dogs, holder.GetDogs()); if (holder.GetSheep() > 0) serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Sheep, holder.GetSheep()); if (holder.GetDonkeys() > 0) serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Donkeys, holder.GetDonkeys()); if (holder.GetPigs() > 0) serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Pigs, holder.GetPigs()); if (holder.GetCows() > 0) serverSocket.SetTileAnimals(holder.position, holder.isCave, ResourceTypes.Cows, holder.GetCows()); } //did we have leftovers? if so, need to warn the player ArrangeAnimalsTriggered = false; if (cowsToAllocate + pigsToAllocate + donkeysToAllocate + sheepToAllocate == 0) return; ArrangeAnimalsTriggered = true; if (discardExcess) { //do not trigger the normal checks with this, use aninal manager to avoid animalManager.Cows -= cowsToAllocate; animalManager.Pigs -= pigsToAllocate; animalManager.Donkeys -= donkeysToAllocate; animalManager.Sheep -= sheepToAllocate; } if (!triggerChoice) return; string result = "Unassigned animals: "; if (cowsToAllocate > 0) result += cowsToAllocate + " cows, "; if (pigsToAllocate > 0) result += pigsToAllocate + " pigs, "; if (donkeysToAllocate > 0) result += donkeysToAllocate + " donkeys, "; if (sheepToAllocate > 0) result += sheepToAllocate + " sheep, "; List<string> options = new List<string>(); options.Add(DiscardActions.DiscardAllUnassignedAnimals); //are we in the breeding phase? if (isBreeding) { if (animalManager.Cows > 0) options.Add(DiscardActions.DiscardCow); if (animalManager.Pigs > 0) options.Add(DiscardActions.DiscardPig); if (animalManager.Donkeys > 0) options.Add(DiscardActions.DiscardDonkey); if (animalManager.Sheep > 0) options.Add(DiscardActions.DiscardSheep); } else { if (HasTile(BuildingTypes.SlaughteringCave)) { if (animalManager.Cows > 0) options.Add(FoodActions.SlaughteringCaveConvertCow); if (animalManager.Pigs > 0) options.Add(FoodActions.SlaughteringCaveConvertPig); if (animalManager.Donkeys > 1) options.Add(FoodActions.SlaughteringCaveConvertDonkeyPair); if (animalManager.Donkeys > 0) options.Add(FoodActions.SlaughteringCaveConvertDonkey); if (animalManager.Sheep > 0) options.Add(FoodActions.SlaughteringCaveConvertSheep); } else { if (animalManager.Cows > 0) options.Add(FoodActions.ConvertCow); if (animalManager.Pigs > 0) options.Add(FoodActions.ConvertPig); if (animalManager.Donkeys > 1) options.Add(FoodActions.ConvertDonkeyPair); if (animalManager.Donkeys > 0) options.Add(FoodActions.ConvertDonkey); if (animalManager.Sheep > 0) options.Add(FoodActions.ConvertSheep); } } serverSocket.GetPlayerChoice("playerID", "Allocate Animals", result, options); }