public static void AttachNewSquadTo(CommanderAgent commander, GameObject[] unitPrefabs, HashSet <Vector2Int> tiles, Vector2Int minMax, System.Random random, Room room, Vector2Int offset, GameObject player) { if (unitPrefabs == null) { Debug.LogError("create squad need unit prefab"); return; } //Cleanup of previous squad============================ foreach (var unit in commander.squad.units) { if (unit != null) { commander.squad.MarkDead(unit.squadUnitIndex); Object.Destroy(unit.gameObject); } } //commander.squad.units.Clear(); -> after array conversion we leave garbage here... //===================================================== commander.squad = new Squad() { currentRoom = room, squadSensor = commander.GetComponent <SquadSensor>(), }; commander.squadColor = Random.ColorHSV(0, 1, 0.25f, 1f); int squadSize = random.NextInclusive(minMax.x, minMax.y); int tilesPerUnit = 6; int maxUnits = tiles.Count / tilesPerUnit; if (squadSize > maxUnits) { squadSize = maxUnits; } if (squadSize > CommanderAgent.MAX_SQUAD_SIZE) { squadSize = CommanderAgent.MAX_SQUAD_SIZE; } for (int i = 0; i < squadSize; i++) { Vector2 tile = tiles.GetRandom(random); Vector3 position = new Vector3((tile.x * 2) + offset.x, 1.055f, (tile.y * 2) + offset.y); var newUnit = Object.Instantiate(unitPrefabs.GetRandom(random), position, Quaternion.identity, commander.transform).GetComponent <SquadUnit>(); var unitSensor = newUnit.GetComponent <UnitSensor>(); unitSensor.player = player; unitSensor.headColliderTransform = player.transform; SetMaterialColor(newUnit, commander.squadColor); commander.squad.AddUnit(newUnit); } return; }
//TODO add support for multiple public static void CreateNewSquad(GameObject squad, GameObject unitPrefab, HashSet <Vector2Int> tiles, Vector2Int minMax, System.Random random, MLAgents.Brain Brain, Room room, Vector2Int offset, GameObject player) { if (unitPrefab == null) { Debug.LogError("create squad need unit prefab"); return; } CommanderAgent prefabCommander = squad.GetComponent <CommanderAgent>(); if (prefabCommander == null) { Debug.LogError("create squad need squad prefab"); return; } if (prefabCommander.squad.units.Length > 0) { Debug.LogError("squad prefab needs to contain no units"); return; } if (player == null) { Debug.LogError("Squad needs to be initialized with player reference"); return; } GameObject newSquadObject = Object.Instantiate(squad, Vector3.zero, Quaternion.identity); CommanderAgent commanderAgent = newSquadObject.GetComponent <CommanderAgent>(); commanderAgent.squad.currentRoom = room; commanderAgent.GiveBrain(Brain); commanderAgent.squadColor = Random.ColorHSV(0, 1, 0.25f, 1f); int squadSize = random.NextInclusive(minMax.x, minMax.y); int tilesPerUnit = 6; int maxUnits = tiles.Count / tilesPerUnit; if (squadSize > maxUnits) { squadSize = maxUnits; } if (squadSize > CommanderAgent.MAX_SQUAD_SIZE) { squadSize = CommanderAgent.MAX_SQUAD_SIZE; } for (int i = 0; i < squadSize; i++) { Vector2 tile = tiles.GetRandom(random); Vector3 position = new Vector3((tile.x * 2) + offset.x, 1.055f, (tile.y * 2) + offset.y); var unit = Object.Instantiate(unitPrefab, position, Quaternion.identity, newSquadObject.transform); SquadUnit newUnit = unit.GetComponent <SquadUnit>(); var unitSensor = newUnit.GetComponent <UnitSensor>(); unitSensor.player = player; unitSensor.headColliderTransform = player.transform; SetMaterialColor(newUnit, commanderAgent.squadColor); commanderAgent.squad.AddUnit(newUnit); } return; }
// This function is tasked with spawning items relative to already placed item // will return a list of spawned items so that SpawnItemsInRoom know what's left to spawn public static int SpawnRelatives(Spawnable spawned, Vector2 spawnedPos, Quaternion spawnedRot, Random rand, ref HashSet <GameObject> spawnedObjects, ref List <RoomType.SpawnListItem> availableItems, int min, int max, int currentRecusion = 0) { int spawnedOfSameCount = 0; if (currentRecusion < 2) { int spawnedIndex = availableItems.FindIndex(spawnableItem => spawnableItem.Spawnable == spawned); for (int i = 0; i < spawned.relativeSpawnList.Length; i++) { int relationIndex = availableItems.FindIndex(spawnableItem => spawnableItem.Spawnable == spawned.relativeSpawnList[i].Spawnable); if (relationIndex != -1 && spawnedIndex <= relationIndex) { int spawnCount = rand.NextInclusive(min, max); int spawnedObjectsCount = 0; // if the rotation is set (not random) if (spawned.relativeSpawnList[i].AngleIteration != 0) { for (int j = 0; j < spawnCount && Math.Abs(j * spawned.relativeSpawnList[i].AngleIteration) < 360; j++) { Quaternion objectQuaternion = Quaternion.Euler(0, j * spawned.relativeSpawnList[i].AngleIteration + spawnedRot.eulerAngles.y, 0); Vector3 spawnPosition = new Vector3(spawnedPos.x, 0, spawnedPos.y); Vector3 forward = objectQuaternion * new Vector3(0, 0, 1); if (TryResolvePositionPhysically(spawned.relativeSpawnList[i].Spawnable, ref spawnPosition, -forward, objectQuaternion)) { spawnedObjects.Add(GameObject.Instantiate(spawned.relativeSpawnList[i].Spawnable.gameObject, spawnPosition, objectQuaternion)); SpawnRelatives(spawned.relativeSpawnList[i].Spawnable, new Vector2Int((int)spawnPosition.x, (int)spawnPosition.z), objectQuaternion, rand, ref spawnedObjects, ref availableItems, min, max, ++currentRecusion); } spawnedObjectsCount = j; } int newMin = ((min - spawnedObjectsCount) > 0) ? min - spawnedObjectsCount : 0; int newMax = ((max - spawnedObjectsCount) > 0) ? max - spawnedObjectsCount : 0; RoomType.SpawnListItem updatedItem = new RoomType.SpawnListItem(availableItems[relationIndex].AngleIteration, new Vector2Int(newMin, newMax), availableItems[relationIndex].Spawnable); availableItems[relationIndex] = updatedItem; if (spawnedIndex == relationIndex) { spawnedOfSameCount++; } } else { for (int j = 0; j < spawnCount; j++) { Quaternion objectQuaternion = Quaternion.Euler(0, rand.NextInclusive(0, availableItems[relationIndex].Spawnable.angleMaxOffset), 0); float xPos = spawnedPos.x + (rand.NextFloat() * (spawned.neededSpawnSpace.extents.x * 0.75f)) * (rand.NextBool() ? -1 : 1); float zPos = spawnedPos.y + (rand.NextFloat() * (spawned.neededSpawnSpace.extents.z * 0.75f)) * (rand.NextBool() ? -1 : 1); Vector3 spawnPosition = new Vector3(xPos, 0, zPos); if (TryResolvePositionPhysically(spawned.relativeSpawnList[i].Spawnable, ref spawnPosition, null, objectQuaternion)) { spawnedObjects.Add(GameObject.Instantiate(spawned.relativeSpawnList[i].Spawnable.gameObject, spawnPosition, objectQuaternion)); SpawnRelatives(spawned.relativeSpawnList[i].Spawnable, new Vector2Int((int)spawnPosition.x, (int)spawnPosition.z), objectQuaternion, rand, ref spawnedObjects, ref availableItems, min, max, ++currentRecusion); } spawnedObjectsCount = j; } int newMin = ((min - spawnedObjectsCount) > 0) ? min - spawnedObjectsCount : 0; int newMax = ((max - spawnedObjectsCount) > 0) ? max - spawnedObjectsCount : 0; RoomType.SpawnListItem updatedItem = new RoomType.SpawnListItem(availableItems[relationIndex].AngleIteration, new Vector2Int(newMin, newMax), availableItems[relationIndex].Spawnable); availableItems[relationIndex] = updatedItem; if (spawnedIndex == relationIndex) { spawnedOfSameCount++; } } } } } return(spawnedOfSameCount); }
const int MAX_ITEM_STACK = 3; // TODO: evaluate where to put this variable // place all the required items within a room public static HashSet <GameObject> SpawnItemsInRoom(Random rand, RoomType roomType, RoomType.RoomSet roomSet, HashSet <Vector2Int> floorPos, HashSet <Vector2Int> wallPos, Vector2Int finalizeOffset = default(Vector2Int)) //Defaults to zero { HashSet <GameObject> spawnedObjects = new HashSet <GameObject>(); // we copying the list as we are changing the data of roomtype otherwise List <RoomType.SpawnListItem> workingList = new List <RoomType.SpawnListItem>(roomType.spawnList); if (roomSet.extraSpawnList != null) { workingList.AddRange(roomSet.extraSpawnList); } HashSet <Vector2Int> onTopPos = new HashSet <Vector2Int>(); HashSet <Vector2Int> workingFloor = new HashSet <Vector2Int>(floorPos); HashSet <Vector2Int> workingWall = new HashSet <Vector2Int>(wallPos); for (int i = 0; i < workingList.Count; i++) { float relativeSpawnAmount = (500 / (workingFloor.Count + workingWall.Count)); int min = Mathf.RoundToInt(workingList[i].Min / relativeSpawnAmount); int max = Mathf.RoundToInt(workingList[i].Max / relativeSpawnAmount); Debug.Log("onTop:" + workingList[i].Spawnable.spawnOnTop); HashSet <Vector2Int> allowedSpawnPos = new HashSet <Vector2Int>(); // HashSet has optimal lookuptimes O(1) // TODO: validate that probability sum is one int totalPos = workingFloor.Count + wallPos.Count + onTopPos.Count; allowedSpawnPos.UnionWith(FindNewPos((int)Math.Round((totalPos * workingList[i].Spawnable.notWallProbability)), workingFloor, finalizeOffset, rand, 2)); allowedSpawnPos.UnionWith(FindNewPos((int)Math.Round((totalPos * workingList[i].Spawnable.byWallProbability)), workingWall, finalizeOffset, rand, 2)); allowedSpawnPos.UnionWith(FindNewPos((int)Math.Round((totalPos * workingList[i].Spawnable.spawnOnTop)), onTopPos, Vector2Int.zero, rand)); //NOTE CHANGE OFFSET TO 0 // chose an amount to spawn a Spawnable type int spawnCount = rand.NextInclusive(min, max); for (int j = 0; j < spawnCount && allowedSpawnPos.Count > 0; j++) { // TODO: roll pos in tile Vector2Int tile = allowedSpawnPos.GetRandom(rand); Vector3 spawnPosition = new Vector3(tile.x, 0, tile.y); Quaternion objectQuaternion = Quaternion.Euler(0, rand.NextInclusive(0, workingList[i].Spawnable.angleMaxOffset), 0); Vector3? forward = null; // if we are about to spawn on the side of a object, we need a forward vector bool isOnTopPos = onTopPos.Contains(tile); if (!isOnTopPos) { forward = objectQuaternion * new Vector3(0, 0, 1); } if (TryResolvePositionPhysically(workingList[i].Spawnable, ref spawnPosition, forward, objectQuaternion)) { spawnedObjects.Add(GameObject.Instantiate(workingList[i].Spawnable.gameObject, spawnPosition, objectQuaternion)); // TODO: rounding bugs!!! Vector2Int tileToRemove = new Vector2Int((int)(tile.x * 0.5f), (int)(tile.y * 0.5f)); workingFloor.RemoveWhere(pos => pos == tileToRemove); workingWall.RemoveWhere(pos => pos == tileToRemove); allowedSpawnPos.RemoveWhere(pos => pos == tile); if (!workingList[i].Spawnable.allowOtherOnTop && isOnTopPos) { onTopPos.Remove(tile); } else if (workingList[i].Spawnable.allowOtherOnTop) { onTopPos.Add(tile); if (workingList[i].Spawnable.spawnOnTop > rand.NextFloat()) { allowedSpawnPos.Add(tile); } } spawnCount -= SpawnRelatives(workingList[i].Spawnable, new Vector2(spawnPosition.x, spawnPosition.z), objectQuaternion, rand, ref spawnedObjects, ref workingList, min, max); } } } // if we are not debugging, we delete scripts used for placement if (Application.isPlaying) { foreach (var spawnedObject in spawnedObjects) { UnityEngine.Object.Destroy(spawnedObject.GetComponent <Spawnable>()); } } return(spawnedObjects); }