package com.galvarez.ttw.model;
import static java.lang.Math.min;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.EntitySystem;
import com.artemis.annotations.Wire;
import com.artemis.utils.ImmutableBag;
import com.galvarez.ttw.EntityFactory;
import com.galvarez.ttw.model.components.AIControlled;
import com.galvarez.ttw.model.components.Army;
import com.galvarez.ttw.model.components.ArmyCommand;
import com.galvarez.ttw.model.components.InfluenceSource;
import com.galvarez.ttw.model.data.Empire;
import com.galvarez.ttw.model.map.GameMap;
import com.galvarez.ttw.model.map.Influence;
import com.galvarez.ttw.model.map.MapPosition;
import com.galvarez.ttw.rendering.IconsSystem.Type;
import com.galvarez.ttw.rendering.NotificationsSystem;
import com.galvarez.ttw.rendering.components.Counter;
import com.galvarez.ttw.rendering.components.Description;
import com.galvarez.ttw.rendering.components.Name;
import com.galvarez.ttw.rendering.components.TextBox;
import com.galvarez.ttw.screens.overworld.OverworldScreen;
import com.galvarez.ttw.utils.RomanNumbers;
/**
* Moves the entities being armies across the map.
*
* @author Guillaume Alvarez
*/
@Wire
public final class ArmiesSystem extends EntitySystem {
private static final Logger log = LoggerFactory.getLogger(ArmiesSystem.class);
private ComponentMapper<Counter> counters;
private ComponentMapper<InfluenceSource> sources;
private ComponentMapper<ArmyCommand> commands;
private ComponentMapper<Army> armies;
private ComponentMapper<Description> descriptions;
private ComponentMapper<MapPosition> positions;
private ComponentMapper<Name> names;
private ComponentMapper<Empire> empires;
private ComponentMapper<TextBox> boxes;
private ComponentMapper<AIControlled> ai;
private DestinationSystem destinationSystem;
private NotificationsSystem notifications;
private final GameMap map;
private final OverworldScreen screen;
@SuppressWarnings("unchecked")
public ArmiesSystem(GameMap map, OverworldScreen screen) {
super(Aspect.getAspectForAll(InfluenceSource.class));
this.map = map;
this.screen = screen;
}
@Override
protected void processEntities(ImmutableBag<Entity> entities) {
for (Entity city : entities) {
InfluenceSource source = sources.get(city);
for (Iterator<Entity> it = source.secondarySources.iterator(); it.hasNext();) {
Entity s = it.next();
Army army = armies.get(s);
if (onOtherInfluence(city, s)) {
army.currentPower--;
if (!ai.has(city))
notifications.addNotification(() -> screen.select(s, true), null, Type.MILITARY,
"%s is depleting in other empire!", names.get(s));
} else {
if (army.currentPower < army.maxPower)
army.currentPower += getArmyPowerIncrease(city, 1);
else if (army.currentPower > army.maxPower)
army.currentPower--;
}
if (army.currentPower <= 0) {
// army is defeated, move it to capital
army.currentPower = 0;
MapPosition newPos = emptyPositionNextTo(city);
if (newPos != null) {
destinationSystem.moveTo(s, newPos);
if (!ai.has(city))
notifications.addNotification(() -> screen.select(s, true), null, Type.MILITARY,
"Depleted %s moved to capital.", names.get(s));
} else {
s.deleteFromWorld();
it.remove();
if (!ai.has(city))
notifications.addNotification(() -> screen.select(s, true), null, Type.MILITARY,
"Depleted %s destroyed.", names.get(s));
}
}
counters.get(s).value = army.currentPower;
}
}
}
private boolean onOtherInfluence(Entity city, Entity army) {
Influence inf = map.getInfluenceAt(positions.get(army));
return inf.hasMainInfluence() && !inf.isMainInfluencer(city);
}
public List<Entity> getArmies(Entity empire) {
InfluenceSource source = sources.get(empire);
return source.secondarySources;
}
public Entity createNewArmy(Entity empire, int power) {
MapPosition pos = emptyPositionNextTo(empire);
if (pos != null) {
power = getArmyPowerIncrease(empire, power);
ArmyCommand command = commands.get(empire);
Entity army = EntityFactory.createArmy(world, pos, armyName(command, empire), empires.get(empire), empire, power);
sources.get(empire).secondarySources.add(army);
command.usedPower += power;
map.setEntity(army, pos);
return army;
} else {
log.warn("Cannot create an army near {}, no empty tile.", descriptions.get(empire));
return null;
}
}
private int getArmyPowerIncrease(Entity empire, int increase) {
InfluenceSource source = sources.get(empire);
increase = min(increase, source.power());
source.addToPower(-increase);
// update power displayed on screen
TextBox box = boxes.get(empire);
box.text = box.generator.get();
return increase;
}
private String armyName(ArmyCommand command, Entity empire) {
int nb = ++command.counter;
return "Army of " + names.get(empire).name + " " + RomanNumbers.toRoman(nb);
}
private MapPosition emptyPositionNextTo(Entity empire) {
MapPosition pos = positions.get(empire);
for (int dist = 1; dist < 4; dist++) {
for (MapPosition neighbor : map.getNeighbors(pos.x, pos.y, dist))
if (!map.hasEntity(neighbor) && map.getInfluenceAt(neighbor).isMainInfluencer(empire))
return neighbor;
}
// should rarely happen
return null;
}
}