// Used by "dumb" stations, that just use their abilities whenever possible protected void TickAbilitiesAsStation() { var enemyTargetFound = SectorManager.instance?.current?.type != Sector.SectorType.BattleZone; if (!enemyTargetFound && BattleZoneManager.getTargets() != null && BattleZoneManager.getTargets().Length > 0) { foreach (var target in BattleZoneManager.getTargets()) { if (!FactionManager.IsAllied(target.faction, faction) && !target.GetIsDead()) { enemyTargetFound = true; break; } } } foreach (ActiveAbility active in GetComponentsInChildren <ActiveAbility>()) { if (!(active is SpawnDrone) || enemyTargetFound) { active.Tick(); active.Activate(); } } }
protected override void Update() { if (initialized) { var enemyTargetFound = false; if (BattleZoneManager.getTargets() != null && BattleZoneManager.getTargets().Length > 0) { foreach (var target in BattleZoneManager.getTargets()) { if (!FactionManager.IsAllied(target.faction, faction) && !target.GetIsDead()) { enemyTargetFound = true; break; } } } foreach (ActiveAbility active in GetComponentsInChildren <ActiveAbility>()) { if (!(active is SpawnDrone) || enemyTargetFound) { active.Tick(1); } } base.Update(); TargetManager.Enqueue(targeter); } }
public void SetOwner(IOwner owner) { this.owner = owner; ai.owner = owner; owner.GetUnitsCommanding().Add(this); if (owner as AirCarrier || owner as GroundCarrier) { // GET THE DRONES TO MOVE ai.setMode(AirCraftAI.AIMode.Path); var path = ScriptableObject.CreateInstance <Path>(); path.waypoints = new List <Path.Node>(); var vec = Vector2.zero; if (owner as AirCarrier) { foreach (var ent in BattleZoneManager.getTargets()) { if (ent && ent is ICarrier && !FactionManager.IsAllied(ent.faction, owner.GetFaction()) && ent.transform) { vec = ent.transform.position; } } } // otherwise this is a ground carrier, drones are defensive for them so set a path to the drone position currently else { var angle = Random.Range(0F, 360); vec = owner.GetTransform().position + new Vector3(5 * Mathf.Sin(angle), 5 * Mathf.Cos(angle)); } // TODO: jank, fix this eventually var node = new Path.Node(); node.position = vec; node.ID = 0; node.children = new List <int>(); if (vec != Vector2.zero) { path.waypoints.Add(node); } if (owner as AirCarrier) { ai.setPath(path); } else { NodeEditorFramework.Standard.PathData data = new NodeEditorFramework.Standard.PathData(); data.waypoints = new List <NodeEditorFramework.Standard.PathData.Node>(); // TODO: LOL THESE TWO ARE DIFFERENT, unify them foreach (var point in path.waypoints) { var node2 = new NodeEditorFramework.Standard.PathData.Node(); node2.ID = point.ID; node2.children = point.children; node2.position = point.position; data.waypoints.Add(node2); } ai.setPath(data, null, true); } } }
bool enemyGroundTargets(bool allEntities) { Entity[] targets = allEntities ? AIData.entities.ToArray() : BattleZoneManager.getTargets(); for (int i = 0; i < targets.Length; i++) { if (!FactionManager.IsAllied(targets[i].faction, craft.faction) && targets[i].Terrain == Entity.TerrainType.Ground) { return(true); } } return(false); }
public override void Init() { carriers = new List <Entity>(); Entity[] targetEntities = BattleZoneManager.getTargets(); if (targetEntities == null) { Debug.LogError("Battle zone target list not initialized"); ai.setMode(AirCraftAI.AIMode.Inactive); return; } for (int i = 0; i < targetEntities.Length; i++) { if (targetEntities[i] is ICarrier) { if (targetEntities[i].faction == craft.faction) { carriers.Add(targetEntities[i]); } } } if (craft is ShellCore) { shellcore = craft as ShellCore; } else { Debug.LogError("Battle zone AI should only be used by shellcores!"); } nextSearchTime = Time.time; nextStateCheckTime = Time.time; for (int i = 0; i < AIData.vendors.Count; i++) { int rockCount = 0; for (int j = 0; j < AIData.energyRocks.Count; j++) { if ((AIData.energyRocks[j].transform.position - AIData.vendors[i].transform.position).sqrMagnitude < 100) { rockCount++; } } AITargets.Add(new AITarget(AIData.vendors[i], rockCount + 1f)); } for (int i = 0; i < carriers.Count; i++) { AITargets.Add(new AITarget(carriers[i], 100f)); } }
ICarrier FindCarrier() { if (SectorManager.instance.current.type == Sector.SectorType.BattleZone) { var targets = BattleZoneManager.getTargets(); for (int i = 0; i < targets.Length; i++) { if (targets[i] && !targets[i].GetIsDead() && targets[i] is ICarrier && targets[i].faction == faction) { return(targets[i] as ICarrier); } } } return(null); }
public override void Init() { carriers = new List <Entity>(); harvesterTurrets = new Dictionary <EnergyRock, Turret>(); Entity[] targetEntities = BattleZoneManager.getTargets(); if (targetEntities == null) { Debug.LogError("Battle zone target list not initialized"); ai.setMode(AirCraftAI.AIMode.Inactive); return; } for (int i = 0; i < targetEntities.Length; i++) { if (targetEntities[i] is ICarrier) { if (targetEntities[i].faction == craft.faction) { carriers.Add(targetEntities[i]); } } } if (craft is ShellCore shellCore) { shellcore = shellCore; } else { Debug.LogError("Battle zone AI should only be used by shellcores!"); } foreach (IOwnable ownable in shellcore.GetUnitsCommanding()) { if (ownable is Turret turret && turret.entityName == "Harvester Turret") { foreach (var rock in AIData.energyRocks) { if (!harvesterTurrets.ContainsKey(rock) && Vector3.SqrMagnitude(rock.transform.position - turret.transform.position) <= 200) { harvesterTurrets.Add(rock, turret); break; } } } } nextSearchTime = Time.time; nextStateCheckTime = Time.time; for (int i = 0; i < AIData.vendors.Count; i++) { int rockCount = 0; for (int j = 0; j < AIData.energyRocks.Count; j++) { if ((AIData.energyRocks[j].transform.position - AIData.vendors[i].transform.position).sqrMagnitude < 100) { rockCount++; } } AITargets.Add(new AITarget(AIData.vendors[i], rockCount + 1f)); } for (int i = 0; i < carriers.Count; i++) { AITargets.Add(new AITarget(carriers[i], 100f)); } }
public override void ActionTick() { if (waitingDraggable != null && waitingDraggable) { ai.movement.SetMoveTarget(waitingDraggable.transform.position, 100f); Vector2 delta = waitingDraggable.transform.position - craft.transform.position; if (ai.movement.targetIsInRange() && shellcore.GetTractorTarget() != null && shellcore.GetTractorTarget().gameObject.GetComponent <EnergySphereScript>() == null) { shellcore.SetTractorTarget(waitingDraggable); } } var turretIsHarvester = shellcore.GetTractorTarget() && shellcore.GetTractorTarget().GetComponent <Turret>() && shellcore.GetTractorTarget().GetComponent <Turret>().entityName == "Harvester Turret"; switch (state) { case BattleState.Attack: if (shellcore.GetTractorTarget() == null) { if (attackTurret == null) { Turret t = null; float minDistance = float.MaxValue; for (int i = 0; i < AIData.entities.Count; i++) { if (AIData.entities[i] != null && AIData.entities[i] && AIData.entities[i] is Turret && AIData.entities[i].faction == craft.faction && AIData.entities[i].GetComponentInChildren <WeaponAbility>() != null && AIData.entities[i].GetComponentInChildren <WeaponAbility>().GetID() != 16) { float d = (AIData.entities[i].transform.position - craft.transform.position).sqrMagnitude; if (d < minDistance) { t = AIData.entities[i] as Turret; minDistance = d; } } } attackTurret = t; } else { float d = (attackTurret.transform.position - shellcore.transform.position).sqrMagnitude; if (d < 150) { shellcore.SetTractorTarget(attackTurret.GetComponent <Draggable>()); } } } // go to nearest enemy construct, attack units / turrets if in visual range if ((primaryTarget == null && nextSearchTime < Time.time) || nextSearchTime < Time.time - 3f) { // get nearest construct primaryTarget = AirCraftAI.getNearestEntity <AirConstruct>(craft, true); //TODO: Exclude turrets? nextSearchTime = Time.time + 1f; //if(primaryTarget) // Debug.Log("AggroTarget: " + primaryTarget.name + " Factions: " + primaryTarget.faction + " - " + craft.faction); } if (primaryTarget != null) { ai.movement.SetMoveTarget(primaryTarget.transform.position); //craft.MoveCraft((primaryTarget.transform.position - craft.transform.position).normalized); } //TODO: AI Attack: // action sequences // Use existing turrets: // -Drag torpedo turrets to enemy bunkers // -Drag siege turrets to outposts // ground attack location = own bunker location // drag tanks from one platform to another "LandPlatformGenerator.getEnemiesOnPlatform(Vector2 platformLocation, int faction)"? // how to react to different pressures on land and air? break; case BattleState.Defend: // destroy enemy units around base, ignore everything outside siege range if (primaryTarget && !primaryTarget.GetIsDead()) { ai.movement.SetMoveTarget(primaryTarget.transform.position); } // buy a turret matching the biggest threat's element, if possible break; case BattleState.Collect: // go from outpost to outpost, (also less fortified enemy outposts [count enemy units nearby {TODO}]) and collect energy if (findNewTarget || collectTarget == null) { // Find new target float minD = float.MaxValue; EnergyRock targetRock = null; int maxEnergy = -1; for (int i = 0; i < AIData.energyRocks.Count; i++) { if (AirCraftAI.getEnemyCountInRange(AIData.energyRocks[i].transform.position, 10f, craft.faction) > 2) { continue; } int energy = 0; for (int j = 0; j < AIData.energySpheres.Count; j++) { if ((AIData.energySpheres[j].transform.position - AIData.energyRocks[i].transform.position).sqrMagnitude < 16) { energy++; } } float d = (craft.transform.position - AIData.energyRocks[i].transform.position).sqrMagnitude; if ((maxEnergy < energy || d * 1.5f < minD || (maxEnergy == energy && d < minD)) && AIData.energyRocks[i] != collectTarget) { minD = d; maxEnergy = energy; targetRock = AIData.energyRocks[i]; } } collectTarget = targetRock; if (collectTarget != null) { findNewTarget = false; } //Debug.LogFormat("Faction {0} collect target: {1}", craft.faction, collectTarget); } if (collectTarget != null) { ai.movement.SetMoveTarget(collectTarget.transform.position); if (ai.movement.targetIsInRange()) { if (harvesterTurrets.ContainsKey(collectTarget) && (!harvesterTurrets[collectTarget] || harvesterTurrets[collectTarget].GetIsDead())) { harvesterTurrets.Remove(collectTarget); } if (turretIsHarvester && !harvesterTurrets.ContainsKey(collectTarget)) { harvesterTurrets.Add(collectTarget, shellcore.GetTractorTarget().GetComponent <Turret>()); shellcore.SetTractorTarget(null); state = BattleState.Attack; } findNewTarget = true; } } break; case BattleState.Fortify: // TODO: place turrets // set primary target to an outpost with least defending turrets if (fortificationTarget == null || !fortificationTarget) { UpdateTargetInfluences(); float closestToZero = float.MaxValue; for (int i = 0; i < AITargets.Count; i++) { if (Mathf.Abs(AITargets[i].influence) < closestToZero && AITargets[i].entity.faction == shellcore.faction) { fortificationTarget = AITargets[i].entity; } } } else if (attackTurret == null || !attackTurret) { UpdateTargetInfluences(); float minDistance = float.MaxValue; for (int i = 0; i < AIData.entities.Count; i++) { if (AIData.entities[i] is Turret) { float d = (craft.transform.position - AIData.entities[i].transform.position).sqrMagnitude; float d2 = (fortificationTarget.transform.position - AIData.entities[i].transform.position).sqrMagnitude; if (d < minDistance && d2 > 150f) { minDistance = d; attackTurret = AIData.entities[i] as Turret; } } } if (attackTurret == null) { state = BattleState.Attack; ActionTick(); nextStateCheckTime += 1f; return; } } else if (shellcore.GetTractorTarget() != attackTurret) { ai.movement.SetMoveTarget(attackTurret.transform.position, 100f); if (ai.movement.targetIsInRange()) { var target = shellcore.GetTractorTarget(); if (target != null && target) { if (target.gameObject.GetComponent <EnergySphereScript>() == null) { shellcore.SetTractorTarget(attackTurret.GetComponent <Draggable>()); } } } } else { Vector2 turretDelta = fortificationTarget.transform.position - attackTurret.transform.position; Vector2 targetPosition = (Vector2)fortificationTarget.transform.position + turretDelta.normalized * 16f; Vector2 delta = targetPosition - (Vector2)craft.transform.position; if (turretDelta.sqrMagnitude < 16f) { shellcore.SetTractorTarget(null); } } break; case BattleState.ReinforceGround: //head to nearest bunker to produce tanks float dist = float.MaxValue; int index = -1; for (int i = 0; i < AITargets.Count; i++) { if (AITargets[i].entity.Terrain == Entity.TerrainType.Ground && AITargets[i].entity && AITargets[i].entity.faction == shellcore.faction && Vector2.SqrMagnitude(craft.transform.position - AITargets[i].entity.transform.position) < dist) { dist = Vector2.SqrMagnitude(craft.transform.position - AITargets[i].entity.transform.position); index = i; } } if (index != -1 && dist >= 100f) { ai.movement.SetMoveTarget(AITargets[index].entity.transform.position); dist = Vector2.SqrMagnitude(craft.transform.position - AITargets[index].entity.transform.position); } break; default: break; } // always drop harvester turrets on close energy rocks turretIsHarvester = shellcore.GetTractorTarget() && shellcore.GetTractorTarget().GetComponent <Turret>() && shellcore.GetTractorTarget().GetComponent <Turret>().entityName == "Harvester Turret"; if (turretIsHarvester) { var turret = shellcore.GetTractorTarget().GetComponent <Turret>(); if (harvesterTurrets.ContainsValue(turret)) { harvesterTurrets.Remove(harvesterTurrets.First(kvp => kvp.Value == turret).Key); } foreach (var rock in AIData.energyRocks) { if ((rock.transform.position - shellcore.transform.position).sqrMagnitude > 150f) { continue; } if (harvesterTurrets.ContainsKey(rock) && (!harvesterTurrets[rock] || harvesterTurrets[rock].GetIsDead())) { harvesterTurrets.Remove(rock); } if (!harvesterTurrets.ContainsKey(rock)) { harvesterTurrets.Add(rock, shellcore.GetTractorTarget().GetComponent <Turret>()); shellcore.SetTractorTarget(null); break; } } } int energyCount = 0; // always collect energy if (shellcore.GetTractorTarget() != null && shellcore.GetTractorTarget().gameObject.GetComponent <EnergySphereScript>() == null) { for (int i = 0; i < AIData.energySpheres.Count; i++) { if ((AIData.energySpheres[i].transform.position - shellcore.transform.position).sqrMagnitude < 150) { energyCount++; if (shellcore.GetTractorTarget() != null) { waitingDraggable = shellcore.GetTractorTarget(); shellcore.SetTractorTarget(null); } } } } else if (shellcore.GetTractorTarget() == null && waitingDraggable != null) { for (int i = 0; i < AIData.energySpheres.Count; i++) { if ((AIData.energySpheres[i].transform.position - shellcore.transform.position).sqrMagnitude < 150) { energyCount++; } } if (energyCount == 0) { shellcore.SetTractorTarget(waitingDraggable); if (shellcore.GetTractorTarget() == waitingDraggable) { waitingDraggable = null; } } } // always buy more turrets/tanks if (shellcore.unitsCommanding.Count < shellcore.GetTotalCommandLimit() && energyCount == 0 || state == BattleState.ReinforceGround) { for (int i = 0; i < AIData.vendors.Count; i++) { if ((AIData.vendors[i].transform.position - craft.transform.position).sqrMagnitude <= 100f && AIData.vendors[i].faction == craft.faction) { IVendor vendor = AIData.vendors[i] as IVendor; if (vendor.GetVendingBlueprint() == null) { continue; } int itemIndex = -1; int ownGroundStation = 0; int ownTank = 0; int enemyTank = 0; int enemyGroundStation = 0; for (int j = 0; j < AIData.entities.Count; j++) { if (FactionManager.IsAllied(AIData.entities[j].faction, craft.faction) && AIData.entities[j].Terrain == Entity.TerrainType.Ground && AIData.entities[j].category == Entity.EntityCategory.Station) { ownGroundStation += 1; } if (!FactionManager.IsAllied(AIData.entities[j].faction, craft.faction) && AIData.entities[j].Terrain == Entity.TerrainType.Ground && AIData.entities[j].category == Entity.EntityCategory.Station) { enemyGroundStation += 1; } if (FactionManager.IsAllied(AIData.entities[j].faction, craft.faction) && AIData.entities[j].Terrain == Entity.TerrainType.Ground && AIData.entities[j].category == Entity.EntityCategory.Unit) { ownTank += 1; } if (!FactionManager.IsAllied(AIData.entities[j].faction, craft.faction) && AIData.entities[j].Terrain == Entity.TerrainType.Ground && AIData.entities[j].category == Entity.EntityCategory.Unit) { enemyTank += 1; } } if (mostNeeded == null) { if (enemyTank > ownTank) { if ((shellcore.GetPower() >= 150 && Random.Range(0, 5) < 4) || Random.Range(0, 3) == 1) { if (Random.Range(0, 2) == 0) { mostNeeded = ("Beam Tank"); } else { mostNeeded = ("Laser Tank"); } } else if (shellcore.GetPower() >= 100 || Random.Range(0, 2) == 1) { mostNeeded = ("Bullet Tank"); } else { mostNeeded = ("Speeder Tank"); } } else if (enemyGroundStation + enemyTank > ownTank) { if (shellcore.GetPower() >= 150 || Random.Range(0, 3) == 1) { if (Random.Range(0, 3) == 0) { mostNeeded = ("Beam Tank"); } else { mostNeeded = ("Siege Tank"); } } else if (shellcore.GetPower() >= 100 || Random.Range(0, 2) == 1) { mostNeeded = ("Bullet Tank"); } else { mostNeeded = ("Speeder Tank"); } } else { if ((shellcore.GetPower() >= 200 && Random.Range(0, 4) < 3) || Random.Range(0, 2) == 1) { mostNeeded = ("Missile Tank"); } else { mostNeeded = ("Laser Tank"); } } } if (state == BattleState.Attack) { if (vendor.GetVendingBlueprint().items[0].entityBlueprint.intendedType == EntityBlueprint.IntendedType.Turret) { bool ownGroundExists = false; for (int j = 0; j < AIData.entities.Count; j++) { if (AIData.entities[j].faction == craft.faction && AIData.entities[j].Terrain == Entity.TerrainType.Ground) { ownGroundExists = true; break; } } if (!ownGroundExists && enemyGroundTargets(true) && shellcore.GetPower() >= 150) { // Attack & enemy holds all ground itemIndex = vendor.GetVendingBlueprint().getItemIndex("Torpedo Turret"); } else { if (shellcore.GetPower() >= 200) { itemIndex = vendor.GetVendingBlueprint().getItemIndex("Missile Turret"); } else if (shellcore.GetPower() >= 100) { itemIndex = vendor.GetVendingBlueprint().getItemIndex("Defense Turret"); } } } else if (vendor.GetVendingBlueprint().items[0].entityBlueprint.intendedType == EntityBlueprint.IntendedType.Tank) { itemIndex = vendor.GetVendingBlueprint().getItemIndex(mostNeeded); } } if (state == BattleState.ReinforceGround) { itemIndex = vendor.GetVendingBlueprint().getItemIndex(mostNeeded); } if (itemIndex == -1) { if (harvesterTurrets.Count < Mathf.Min(5, AIData.energyRocks.Count) && shellcore.GetPower() >= 100) { itemIndex = vendor.GetVendingBlueprint().getItemIndex("Harvester Turret"); foreach (var turret in harvesterTurrets.Values) { if (turret && Vector3.SqrMagnitude(turret.transform.position - shellcore.transform.position) <= 200) { itemIndex = -1; } } } else { for (int j = 0; j < vendor.GetVendingBlueprint().items.Count; j++) { if (itemIndex != -1 && vendor.GetVendingBlueprint().items[j].cost <= vendor.GetVendingBlueprint().items[itemIndex].cost && vendor.GetVendingBlueprint().items[0].entityBlueprint.intendedType != EntityBlueprint.IntendedType.Tank) // more expensive => better (TODO: choose based on the situation) { if (itemIndex != -1 && vendor.GetVendingBlueprint().items[j].cost <= vendor.GetVendingBlueprint().items[itemIndex].cost) // more expensive => better (TODO: choose based on the situation) { continue; } if (vendor.GetVendingBlueprint().items[j].entityBlueprint.name != mostNeeded && vendor.GetVendingBlueprint().items[0].entityBlueprint.intendedType == EntityBlueprint.IntendedType.Tank) //TODO: get turret / tank attack category from somewhere else { continue; } itemIndex = j; } } } } if (itemIndex != -1) { if (vendor.GetVendingBlueprint().items[itemIndex].cost <= shellcore.GetPower()) { mostNeeded = null; } else { break; } var ent = VendorUI.BuyItem(shellcore, itemIndex, (AIData.vendors[i] as IVendor)); if (itemIndex == vendor.GetVendingBlueprint().getItemIndex("Harvester Turret")) { EnergyRock closestRock = null; foreach (var rock in AIData.energyRocks.FindAll(e => !harvesterTurrets.ContainsKey(e))) { if (closestRock == null || Vector2.SqrMagnitude(rock.transform.position - shellcore.transform.position) < Vector2.SqrMagnitude(closestRock.transform.position - shellcore.transform.position)) { closestRock = rock; } } harvesterTurrets.Add(closestRock, ent as Turret); shellcore.SetTractorTarget(ent.GetComponent <Draggable>()); } break; } } } } void UpdateTargetInfluences() { for (int i = 0; i < AITargets.Count; i++) { var t = AITargets[i]; if (t.entity == null || t.entity.GetIsDead()) { Debug.LogWarning("AI Warning: AI target null or dead!"); continue; } t.influence = 0f; for (int j = 0; j < AIData.entities.Count; j++) { if (AIData.entities[j] is Turret) { if ((AIData.entities[j].transform.position - t.entity.transform.position).sqrMagnitude < 150f) { t.influence += FactionManager.IsAllied(AIData.entities[j].faction, t.entity.faction) ? 1f : -1f; } } } } } bool enemyGroundTargets(bool allEntities) { Entity[] targets = allEntities ? AIData.entities.ToArray() : BattleZoneManager.getTargets(); for (int i = 0; i < targets.Length; i++) { if (!FactionManager.IsAllied(targets[i].faction, craft.faction) && targets[i].Terrain == Entity.TerrainType.Ground) { return(true); } } return(false); } }