package ai.general;
import java.util.ArrayList;
import rts.units.Unit;
/**
* \brief Manages engaging the enemy
* @author Jeff Bernard
*
*/
public class AttackManager extends TaskManager {
public ArrayList<GeneralAIEnemy> enemies; /**< enemy units */
public float enemy_evaluation; /**< evaluation (or prediction) of the enemy's might */
public float army_evaluation; /**< evaluation of my (attacking units only) army */
private int cheapest_unit; /**< cheapest unit */
private int expensive_unit; /**< most expensive unit */
private int strongest_unit; /**< cheapest unit */
private int weakest_unit; /**< most expensive unit */
/**
* Constructs a new attack manager
* @param units list of all possible units defintions
*/
public AttackManager(ArrayList<GeneralAIProduction> units) {
super();
enemies = new ArrayList<GeneralAIEnemy>();
enemy_evaluation = 0;
army_evaluation = 0;
int minCost = -1;
int maxCost = -1;
float minAtk = -1;
float maxAtk = -1;
for (int i = 0; i < units.size(); i++) {
if (!units.get(i).def.is_worker) {
int cost = 0;
for (int j = 0; j < units.get(i).def.cost.size(); j++) {
cost += units.get(i).def.cost.get(j);
}
float atk = (float)(units.get(i).def.attack_max+units.get(i).def.attack_min)/(float)units.get(i).def.attack_speed;
if (i == 0 || cost < minCost) {
cheapest_unit = i;
minCost = cost;
}
if (i == 0 || cost > maxCost) {
expensive_unit = i;
maxCost = cost;
}
if (i == 0 || atk < minAtk) {
weakest_unit = i;
minAtk = atk;
}
if (i == 0 || atk > maxAtk) {
strongest_unit = i;
maxAtk = atk;
}
}
}
}
@Override
/**
* Requests units to start fighting foes
* @param ai the parent ai
*/
public void manage_units(GeneralAI ai) {
// check if any of the scouted units have become available
for (int i = 0; i < units_scouted.size(); i++) {
GeneralAIUnit unit = units_scouted.get(i);
if (unit.strategy == GeneralAI.STRATEGY_NONE) {
// free agent!!
unit.strategy = GeneralAI.STRATEGY_ATTACK;
unit.wanted_strategy = GeneralAI.STRATEGY_NONE;
unit.object = null;
units.add(unit);
units_scouted.remove(i--);
} else if (unit.wanted_strategy == GeneralAI.STRATEGY_NONE) {
unit.wanted_strategy = GeneralAI.STRATEGY_ATTACK;
}
}
// build an ideal army
if (army_evaluation < enemy_evaluation) {
float difference = enemy_evaluation-army_evaluation;
for (int i = 0; i < ai.production_manager.units_possible.size(); i++) {
GeneralAIProduction production = ai.production_manager.units_possible.get(i);
production.priority = GeneralAI.DISTANCE_IGNORE;
if (!production.def.is_worker) {
if (((ai.getLesion()&GeneralAI.LESION_ONLY_RANGE) != 0 && production.def.attack_range <= 1) ||
((ai.getLesion()&GeneralAI.LESION_NO_RANGE) != 0 && production.def.attack_range > 1) ||
((ai.getLesion()&GeneralAI.LESION_ONLY_FLYING) != 0 && !production.def.is_flying) ||
((ai.getLesion()&GeneralAI.LESION_NO_FLYING) != 0 && production.def.is_flying) ||
((ai.getLesion()&GeneralAI.LESION_CHEAPEST_ARMY) != 0 && i != cheapest_unit) ||
((ai.getLesion()&GeneralAI.LESION_EXPENSIVE_ARMY) != 0 && i != expensive_unit) ||
((ai.getLesion()&GeneralAI.LESION_WEAKEST_ARMY) != 0 && i != weakest_unit) ||
((ai.getLesion()&GeneralAI.LESION_STRONGEST_ARMY) != 0 && i != strongest_unit)) {
continue;
}
}
for (int j = 0; j < production.def.cost.size(); j++) {
if (production.def.cost.get(j) > ai.money.get(j)) {
production.priority = GeneralAI.DISTANCE_IGNORE;
break;
} else {
production.priority += ai.money.get(j)-production.def.cost.get(j);
}
}
if (production.priority == GeneralAI.DISTANCE_IGNORE) {
continue;
}
production.priority = (int)(difference/production.evaluate()*(float)production.priority/production.cost_ratio);
}
}
// scout ALL units possible
for (int i = 0; i < ai.units.size(); i++) {
GeneralAIUnit unit = ai.units.get(i);
if (!unit.stats.isBuilding() && (!unit.stats.isWorker() || !ai.state.isFog() || (ai.getLesion()&GeneralAI.LESION_WORKER_ARMY) != 0)) {
if (unit.wanted_strategy != GeneralAI.STRATEGY_BUILD && unit.wanted_strategy != GeneralAI.STRATEGY_FARM) {
if (unit.strategy == GeneralAI.STRATEGY_NONE) {
units.add(unit);
unit.strategy = GeneralAI.STRATEGY_ATTACK;
unit.object = null;
} else if (unit.strategy == GeneralAI.STRATEGY_EXPLORE && unit.wanted_strategy == GeneralAI.STRATEGY_NONE) {
// pull in the near by searchers?
unit.wanted_strategy = GeneralAI.STRATEGY_ATTACK;
units_scouted.add(unit);
}
}
}
}
// deal with the units already in the army
for (int i = 0; i < units.size(); i++) {
GeneralAIUnit unit = units.get(i);
if (unit.wanted_strategy != GeneralAI.STRATEGY_NONE) {
// yield to all requests
if (unit.object != null) {
unit.object.remove(unit, ai);
unit.strategy = GeneralAI.STRATEGY_NONE;
units.remove(i--);
}
} else if (enemies.size() == 0) { // TODO switch to exploration manager(?), which is basically sub of this...
unit.strategy = GeneralAI.STRATEGY_NONE;
units.remove(i--);
} else if (unit.object == null) {
// needs something to kill
int distance = GeneralAI.DISTANCE_IGNORE;
for (int j = 0; j < enemies.size(); j++) {
int d = enemies.get(j).distance(unit, ai);
if (d != GeneralAI.DISTANCE_IGNORE && (distance == GeneralAI.DISTANCE_IGNORE || d < distance)) {
distance = d;
unit.object = enemies.get(j);
}
}
if (unit.object == null) {
// yield to any other behavior
unit.strategy = GeneralAI.STRATEGY_NONE;
units.remove(i--);
}
} else {
// ensure that the current target is truly the closest
int distance = unit.object.distance(unit, ai);
boolean different = false;
if (distance == GeneralAI.DISTANCE_IGNORE) {
unit.object.remove(unit, ai);
different = true;
}
for (int j = 0; j < enemies.size(); j++) {
int d = enemies.get(j).distance(unit, ai);
if (d != GeneralAI.DISTANCE_IGNORE && (distance == GeneralAI.DISTANCE_IGNORE || d < distance)) {
distance = d;
if (!different && enemies.get(j).stats.getID() != ((GeneralAIEnemy)unit.object).stats.getID()) {
unit.object.remove(unit, ai);
different = true;
}
unit.object = enemies.get(j);
}
}
}
}
}
@Override
/**
* Updates foe knowledge
*/
public void update(GeneralAI ai) {
for (int i = 0; i < ai.state.getOtherUnits().size(); i++) {
Unit unit = ai.state.getOtherUnits().get(i);
boolean found = false;
for (int j = 0; j < enemies.size(); j++) { // check if we already know about this enemy
GeneralAIEnemy enemy = enemies.get(j);
if (enemy.stats.getID() == unit.getID()) {
enemy.stats = unit;
if (enemy.stats.getHP() <= 0) {
enemy.dead = true;
if (enemy.stats.isBuilding()) {
for (int k = 0; k < ai.town_manager.towns.size(); k++) {
if (ai.town_manager.towns.get(k).remove(enemy)) {
break;
}
}
}
enemies.remove(j--);
}
enemy.seen = true;
found = true;
break;
}
}
if (!found && unit.getHP() > 0) {
GeneralAIEnemy enemy = new GeneralAIEnemy(unit);
enemies.add(enemy);
if (enemy.stats.isBuilding()) {
boolean added = false;
for (int j = 0; j < ai.town_manager.towns.size(); j++) {
if (ai.town_manager.towns.get(j).add(enemy)) {
added = true;
break;
}
}
if (!added) {
ai.town_manager.towns.add(new GeneralAITown(enemy, ai.production_manager.buildings_possible.size()));
}
}
}
}
enemy_evaluation = 1;
for (int i = 0; i < enemies.size(); i++) {
GeneralAIEnemy enemy = enemies.get(i);
enemy_evaluation += enemy.evaluate();
}
army_evaluation = 1;
for (int i = 0; i < units.size(); i++) {
GeneralAIUnit unit = units.get(i);
army_evaluation += unit.evaluate();
}
if (ai.state.isFog()) {
// if there's fog, we need to do an estimate about the enemy army's actual might
int enemy_count = 1;
if (enemies.size() != 0) {
enemy_count = enemies.size();
}
enemy_evaluation = (int)((float)ai.units.size()/(float)enemy_count*enemy_evaluation+(float)enemy_count/(float)ai.units.size()*army_evaluation);
}
}
/**
* Removes this unit from the the manager
* @param unit the unit to remove
*/
public void remove_unit(GeneralAIUnit unit) {
super.remove_unit(unit.stats.getID());
}
}