public static bool FindASpot(Vector3 centerStartPos, float radiusToPlace, float radiusCircleToFitObjectOn, out Vector3 resultingPos) { bool locationFound = false; resultingPos = centerStartPos; //default for (int attemptCount = 0; attemptCount < Level.PLACEMENT_ATTEMPTS; attemptCount++) { //find a random spot Vector2 spotOnACircle = Random.insideUnitCircle.normalized; if (spotOnACircle == Vector2.zero) { //edge possibility, probably so rare it "impossible" but saw a hole at dead center once... spotOnACircle = Vector2.one * radiusToPlace; } Vector2 randomSpot = spotOnACircle * (radiusToPlace); Vector3 prospectiveSpot = new Vector3(randomSpot.x, 0, randomSpot.y) + centerStartPos; if (Level.GetIfSpotFree(prospectiveSpot, radiusCircleToFitObjectOn, out RaycastHit landHit)) { if (Level.GetIfSpotOnNavMesh(landHit.point)) { locationFound = true; resultingPos = landHit.point; //Debug.DrawLine(centerStartPos, prospectiveSpot, Color.green, 3.0f); break; } } //Debug.DrawLine(centerStartPos, prospectiveSpot, Color.red, 1.5f); } return(locationFound); }
void Update() //TODO: move this to user controls somewhere at some point, would really make more sense hah { //DEBUG if (Input.GetKeyDown(KeyCode.Plus) || Input.GetKeyDown(KeyCode.Equals)) //win { CurrentLevel.DebugKillOffSpawners(Teams.Baddies); } if (Input.GetKeyDown(KeyCode.Minus)) //lose { CurrentLevel.DebugKillOffSpawners(Teams.Townsfolk); } if (Input.GetKey("escape")) { Application.Quit(); } if (Input.GetKeyDown(KeyCode.V)) //Deselect All Units { UnitMgr.GatheredUnits.RemoveAll(item => item == null); //https://stackoverflow.com/questions/3069431/listobject-removeall-how-to-create-an-appropriate-predicate foreach (var u in UnitMgr.GatheredUnits) { //if (u == null) continue; u.SelectionRing?.gameObject.SetActive(false); } UnitMgr.GatheredUnits.Clear(); } if (Input.GetKeyDown(KeyCode.C)) //Collect All Units { foreach (var u in UnitMgr.Units) { if (u == null || u.Team != Teams.Townsfolk) { continue; } u.SelectionRing?.gameObject.SetActive(true); UnitMgr.GatheredUnits.Add(u); } } if (Input.GetKeyDown(KeyCode.G)) //Gather Units { foreach (var u in UnitMgr.Units) { if (u == null || u.Team != Teams.Townsfolk) { continue; } if (!((u.transform.position - PlayerUser.PlayerObject.transform.position).sqrMagnitude < UnitMgr.UNIT_GATHER_RADIUS_SQUARED)) { continue; } u.SelectionRing?.gameObject.SetActive(true); UnitMgr.GatheredUnits.Add(u); } } if (Input.GetKeyDown(KeyCode.T)) //gathered villagers attack enemy Hole closest to you the user { //Find closest spawner to player Spawner closestHoleSpawner = null; float closestBaddySpawnerDistanceSquared = float.MaxValue; foreach (var s in LevelsMgr.CurrentLevel.Spawners) { if (s == null) { continue; } float testDistance = Vector3.SqrMagnitude(s.transform.position - PlayerUser.PlayerObject.transform.position); if (s.Team == Teams.Baddies && testDistance < closestBaddySpawnerDistanceSquared) { closestBaddySpawnerDistanceSquared = testDistance; closestHoleSpawner = s; } } UnitMgr.GatheredUnits.RemoveAll(item => item == null); //https://stackoverflow.com/questions/3069431/listobject-removeall-how-to-create-an-appropriate-predicate foreach (var u in UnitMgr.GatheredUnits) { //if (u == null) continue; if (u.Team == Teams.Townsfolk) { u.SelectionRing?.gameObject.SetActive(false); u.SetAttack(closestHoleSpawner); } } UnitMgr.GatheredUnits.Clear(); if (closestHoleSpawner != null) { PlayerUser.VisualUnitsTargetArrow.GetComponent <ObjectColor>()?.SetOriginalColor(Color.red); PlayerUser.VisualUnitsTargetArrow.SetTarget(PlayerUser.PlayerObject.transform.position, closestHoleSpawner.transform.position, 3f); } } if (Input.GetKeyDown(KeyCode.P)) //gathered villagers protect the rally point of your position { //TODO: make this dependent on mode to select the point (user might be top-down) UnitMgr.GatheredUnits.RemoveAll(item => item == null); //https://stackoverflow.com/questions/3069431/listobject-removeall-how-to-create-an-appropriate-predicate foreach (var u in UnitMgr.GatheredUnits) { //if (u == null) continue; if (u.Team == Teams.Townsfolk) { u.SelectionRing?.gameObject.SetActive(false); u.SetRallyPoint(PlayerUser.PlayerObject.transform.position); } } UnitMgr.GatheredUnits.Clear(); } if (Input.GetKeyDown(KeyCode.M)) //show or hide menu { _menuActive = !_menuActive && CurrentLevel != Level.LobbyLevel; MenuInGameObject?.GetComponent <MenuInGame>().Show(_menuActive); } if (Input.GetKeyDown(KeyCode.Y)) //grab a tower for placement mode { if (!_towerPlacementMode) { _towerToBePlaced = TowerMgr.PickATower(Teams.Townsfolk, TowerTypes.Round); } _towerPlacementMode = true; } if (Input.GetKeyUp(KeyCode.Y)) //place the tower { if (_towerPlacementMode) { if (_towerPlacementOk) { _towerToBePlaced?.SetDownActivate(); PlayerUser.ResourceUnits -= TowerMgr.TOWER_RESOURCE_COST; } else { Destroy(_towerToBePlaced.gameObject); } _towerToBePlaced = null; //release handle _towerPlacementMode = false; PlayerUser.VisualNewTowerDistanceArrow?.ClearTarget(); TowerMgr.ExplanationText.text = ""; } } if (_towerPlacementMode) { _towerPlacementOk = false; TowerMgr.ExplanationText.text = ""; //raycast from cursor, get nearest grid spot on Level if (_cam == null) { _cam = FindObjectOfType <Camera>(); } Ray ray = _cam.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { //check distances bool closeEnoughToPlayer = (hit.point - _cam.transform.position).magnitude < TowerMgr.TOWER_PLACEMENT_DISTANCE; Spawner nearestSpawner = GetClosestMatchingSpawner(hit.point); if (nearestSpawner != null) { float distanceSquaredToNearestMatchingSpawner = (nearestSpawner.transform.position - hit.point).sqrMagnitude; bool closeEnoughToAVillage = distanceSquaredToNearestMatchingSpawner < (TowerMgr.TOWER_PLACEMENT_DISTANCE + _towerToBePlaced.Radius) * (TowerMgr.TOWER_PLACEMENT_DISTANCE + _towerToBePlaced.Radius); bool leavesEnoughGrowthRoomForAVillage = distanceSquaredToNearestMatchingSpawner > (Level.SPAWN_LAND_FIT_RADIUS + _towerToBePlaced.Radius) * (Level.SPAWN_LAND_FIT_RADIUS + _towerToBePlaced.Radius); bool enoughResources = PlayerUser.ResourceUnits >= TowerMgr.TOWER_RESOURCE_COST; bool accessibleViaNavMesh = Level.GetIfSpotOnNavMesh(hit.point, 0.25f); if (closeEnoughToPlayer && closeEnoughToAVillage && leavesEnoughGrowthRoomForAVillage && enoughResources && accessibleViaNavMesh) { //sphere-cast to ensure clear for placement if (Level.GetIfSpotFree(hit.point, _towerToBePlaced.Radius, out RaycastHit landHit)) { //if clear, move to new temporary grid location _towerToBePlaced.transform.position = landHit.point; _towerPlacementOk = true; } else { TowerMgr.ExplanationText.text = "Spot not clear!\n"; } } else { TowerMgr.ExplanationText.text = (closeEnoughToPlayer ? "" : "Too far from you!\n") + (closeEnoughToAVillage ? "" : "Too far from village!\n") + (leavesEnoughGrowthRoomForAVillage ? "" : ("Too close to village!\n")) + (enoughResources ? "" : "Need " + TowerMgr.TOWER_RESOURCE_COST + " resources!\n") + (accessibleViaNavMesh ? "" : "Units can't reach!\n"); } //show distance reach visual (i.e. arrow) PlayerUser.VisualNewTowerDistanceArrow?.SetTarget(hit.point, nearestSpawner.transform.position); PlayerUser.VisualNewTowerDistanceArrow?.GetComponent <ObjectColor>() ?.SetColor(_towerPlacementOk ? Color.white : Color.red); TowerMgr.ExplanationText.transform.position = hit.point + Vector3.up * 2f; } } //adjust temporary tower visuals if (_towerToBePlaced != null) { _towerToBePlaced?.GetComponent <ObjectColor>()?.SetColor(_towerPlacementOk ? Color.white : Color.red); _towerToBePlaced.GetComponent <Renderer>().enabled = _towerPlacementOk; } } }