public class EntityFactory {

  public static Entity createClick(World world, int x, int y, float startScale, float speed) {
    Entity e = world.createEntity();
    EntityEdit edit = e.edit();

    MutableMapPosition pos = edit.create(MutableMapPosition.class);
    pos.x = x;
    pos.y = y;

    Sprite sprite = new Sprite();
    sprite.name = "click";
    sprite.color = new Color(1f, 1f, 1f, 0.5f);
    sprite.rotation = 0f;
    sprite.scaleX = startScale;
    sprite.scaleY = startScale;
    edit.add(sprite);

    Expires expires = edit.create(Expires.class);
    expires.delay = 1f;

    ScaleAnimation scaleAnimation = edit.create(ScaleAnimation.class);
    scaleAnimation.speed = speed;

    ColorAnimation colorAnimation = edit.create(ColorAnimation.class);
    colorAnimation.alphaAnimate = true;
    colorAnimation.alphaSpeed = -1f;

    return e;
  }

  public static Entity createEmpire(World world, int x, int y, String name, Empire empire) {
    Entity e = world.createEntity();

    EntityEdit edit = e.edit();

    ArmyCommand command = new ArmyCommand();
    edit.add(empire).add(new EventsCount()).add(new Discoveries()).add(new Policies()).add(new Diplomacy())
        .add(command).add(new Score());

    Sprite sprite = new Sprite();
    sprite.name = "camp";
    sprite.rotation = 0f;
    sprite.scaleX = 1f;
    sprite.scaleY = 1f;
    sprite.color = empire.color;
    edit.add(sprite);

    InfluenceSource source = new InfluenceSource();
    edit.add(new MapPosition(x, y)).add(source).add(new Destination(command.forbiddenTiles, 2));

    edit.add(new Name(name)).add(new Description("Tribe of " + name))
        .add(new TextBox(() -> name + ": " + source.power()));

    if (empire.isComputerControlled())
      edit.add(new AIControlled());

    return e;
  }

  public static Entity createArmy(World world, MapPosition pos, String name, Empire empire, Entity source,
      int militaryPower) {
    Entity e = world.createEntity();
    EntityEdit edit = e.edit();

    edit.add(new Counter(Colors.contrast(empire.color), empire.color, militaryPower));

    edit.add(pos).add(new Name(name)).add(new Description(name))
        .add(new Destination(source.getComponent(ArmyCommand.class).forbiddenTiles, 1))
        .add(new Army(source, militaryPower)).add(empire);

    if (empire.isComputerControlled())
      edit.add(new AIControlled());

    return e;
  }

  public static Entity createFadingTileLabel(World world, String label, Color color, float x, float y, float duration) {
    Entity e = world.createEntity();
    EntityEdit edit = e.edit();

    MutableMapPosition position = edit.create(MutableMapPosition.class);
    position.x = x;
    position.y = y;

    FadingMessage fading = edit.create(FadingMessage.class);
    fading.label = label;
    fading.color = color;
    fading.duration = duration;
    fading.vx = 0f;
    fading.vy = 1.3f;

    return e;
  }

  public static Entity createFadingTileIcon(World world, TextureRegion icon, Color color, float x, float y,
      float duration) {
    Entity e = world.createEntity();
    EntityEdit edit = e.edit();

    MutableMapPosition position = edit.create(MutableMapPosition.class);
    position.x = x;
    position.y = y;

    FadingMessage fading = edit.create(FadingMessage.class);
    fading.icon = icon;
    fading.color = color;
    fading.duration = duration;
    fading.vx = 0f;
    fading.vy = 1.3f;

    return e;
  }

} public enum Action {

  NO_CHANGE("no change", null, null, allOf(State.class), a -> false),

  DECLARE_WAR("declare war", State.WAR, State.WAR, complementOf(of(State.WAR)), a -> true),

  MAKE_PEACE("make peace", State.NONE, State.NONE, of(State.WAR), a -> false),

  SIGN_TREATY("sign a treaty", State.TREATY, State.TREATY, of(State.WAR, State.NONE, State.TRIBUTE,
      State.PROTECTORATE), a -> a == MAKE_PEACE),

  SURRENDER("surrender", State.TRIBUTE, State.PROTECTORATE, of(State.WAR), a -> a == MAKE_PEACE || a == SIGN_TREATY);

  public final String str;

  public final Set<State> before;

  private final State afterMe;

  private final State afterYou;

  private final Predicate<Action> compatibleWith;

  private Action(String str, State afterMe, State afterYou, Set<State> before, Predicate<Action> compatibleWith) {
    this.str = str;
    this.afterMe = afterMe;
    this.afterYou = afterYou;
    this.before = before;
    this.compatibleWith = compatibleWith;
  }

  public boolean compatibleWith(Action targetProposal) {
    return targetProposal == this || compatibleWith.test(targetProposal);
  }

} public enum State {
  NONE, WAR, TREATY, PROTECTORATE, TRIBUTE;
} public final class Army extends Component {

  public final int maxPower;

  public int currentPower;

  public final Entity source;

  public Army(Entity source, int militaryPower) {
    this.source = source;
    this.maxPower = militaryPower;
    this.currentPower = militaryPower;
  }

} public final class ArmyCommand extends Component {

  private static final int INITIAL_MILITARY_POWER = 10;

  public final Set<Terrain> forbiddenTiles = EnumSet.of(Terrain.SHALLOW_WATER, Terrain.DEEP_WATER, Terrain.ARCTIC);

  public int militaryPower = INITIAL_MILITARY_POWER;

  public int usedPower = 0;

  public int counter = 0;

} public final class Diplomacy extends Component {

  public final Map<Entity, State> relations = new HashMap<>();

  public final ObjectIntMap<Entity> lastChange = new ObjectIntMap<>();

  public final Map<Entity, Action> proposals = new HashMap<>();

  public final EnumSet<State> knownStates = EnumSet.of(State.NONE);

  public Diplomacy() {
  }

  public State getRelationWith(Entity other) {
    if (other == null)
      return State.NONE;
    State state = relations.get(other);
    return state != null ? state : State.NONE;
  }

  public boolean hasRelationWith(Entity other) {
    if (other == null)
      return false;
    else
      return relations.containsKey(other) || proposals.containsKey(other) || lastChange.containsKey(other);
  }

  public Action getProposalTo(Entity other) {
    Action action = proposals.get(other);
    return action != null ? action : Action.NO_CHANGE;
  }

  /** Get all empires whose state is the one passed as a parameter. */
  public List<Entity> getEmpires(State state) {
    List<Entity> others = new ArrayList<Entity>();
    for (Entry<Entity, State> e : relations.entrySet()) {
      if (e.getValue() == state)
        others.add(e.getKey());
    }
    return others;
  }

} public final class InfluenceSource extends Component {

  private float power = InfluenceSystem.INITIAL_POWER;

  /** In per mille. */
  public int growth;

  /** Usually compared to power. */
  public int health = InfluenceSystem.INITIAL_POWER;

  public final Set<MapPosition> influencedTiles = new HashSet<>();

  public final List<Entity> secondarySources = new ArrayList<>();

  public final Modifiers modifiers = new Modifiers();

  public static final class Modifiers {

    public final Map<Terrain, Integer> terrainBonus = new EnumMap<Terrain, Integer>(Terrain.class);

    public Modifiers() {
      for (Terrain t : Terrain.values())
        terrainBonus.put(t, Integer.valueOf(0));
    }

  }

  public int power() {
    return (int) power;
  }

  public void addToPower(float f) {
    power += f;
  }

  public void setPower(int power) {
    this.power = power;
  }

} public static final class Modifiers {

  public final Map<Terrain, Integer> terrainBonus = new EnumMap<Terrain, Integer>(Terrain.class);

  public Modifiers() {
    for (Terrain t : Terrain.values())
      terrainBonus.put(t, Integer.valueOf(0));
  }

} public final class Empire extends Component {

  private static final AtomicInteger COUNTER = new AtomicInteger(0);

  public final int id;

  public final Color color;

  public final Color backColor;

  public final Culture culture;

  private final boolean computer;

  public Empire(Color color, Culture culture, boolean computer) {
    this.id = COUNTER.getAndIncrement();
    this.color = color;
    this.backColor = Colors.contrast(color);
    this.culture = culture;
    this.computer = computer;
  }

  public boolean isComputerControlled() {
    return computer;
  }

  public Color getColor() {
    return color;
  }

  public Culture getCulture() {
    return culture;
  }

  public String newCityName() {
    return culture.newCityName();
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this)
      return true;
    else if (obj instanceof Empire)
      return id == ((Empire) obj).id;
    else
      return false;
  }

  @Override
  public int hashCode() {
    return id;
  }

  @Override
  public String toString() {
    return culture.name + "(ia=" + computer + ")";
  }

} public final class GameMap {

  public final Terrain[][] map;

  private final Entity[][] entityByCoord;

  private final MapPosition[][] posByCoord;

  private final Influence[][] influenceByCoord;

  public final int width, height;

  /** Represents the whole map as a single image. */
  public final Texture texture;

  public final Empire[] empires;

  public GameMap(Terrain[][] map, Collection<Empire> empires) {
    this.map = map;
    this.empires = empires.toArray(new Empire[0]);
    width = map.length;
    height = map[0].length;

    entityByCoord = new Entity[width][height];
    influenceByCoord = new Influence[width][height];
    posByCoord = new MapPosition[width][height];

    Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);

    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        MapPosition pos = posByCoord[x][y] = new MapPosition(x, y);
        influenceByCoord[x][y] = new Influence(pos, map[x][y]);
        pixmap.setColor(map[x][y].getColor());
        pixmap.drawPixel(x, y);
      }
    }

    texture = new Texture(pixmap);
    pixmap.dispose();
  }

  public Entity getEntityAt(int x, int y) {
    if (isOnMap(x, y))
      return entityByCoord[x][y];
    else
      return null;
  }

  public Entity getEntityAt(MapPosition pos) {
    return getEntityAt(pos.x, pos.y);
  }

  public boolean hasEntity(MapPosition pos) {
    return getEntityAt(pos) != null;
  }

  public Terrain getTerrainAt(MapPosition pos) {
    return getTerrainAt(pos.x, pos.y);
  }

  public Terrain getTerrainAt(int x, int y) {
    if (isOnMap(x, y))
      return map[x][y];
    else
      throw new IllegalStateException("(" + x + ", " + y + ") is outside map boundaries.");
  }

  public Influence getInfluenceAt(MapPosition pos) {
    return getInfluenceAt(pos.x, pos.y);
  }

  public Influence getInfluenceAt(int x, int y) {
    if (isOnMap(x, y))
      return influenceByCoord[x][y];
    else
      throw new IllegalStateException("(" + x + ", " + y + ") is outside map boundaries.");
  }

  public boolean isOnMap(MapPosition p) {
    return isOnMap(p.x, p.y);
  }

  public boolean isOnMap(int x, int y) {
    return x >= 0 && x < posByCoord.length && y >= 0 && y < posByCoord[0].length;
  }

  public MapPosition getPositionAt(int x, int y) {
    if (isOnMap(x, y))
      return posByCoord[x][y];
    else
      throw new IllegalStateException("(" + x + ", " + y + ") is outside map boundaries.");
  }

  public void setEntity(Entity e, int x, int y) {
    entityByCoord[x][y] = e;
  }

  public void setEntity(Entity e, MapPosition p) {
    setEntity(e, p.x, p.y);
  }

  public void moveEntity(Entity e, MapPosition from, MapPosition to) {
    entityByCoord[from.x][from.y] = null;
    entityByCoord[to.x][to.y] = e;
  }

  public Array<MapPosition> getNeighbors(int x, int y, int n) {
    Array<MapPosition> coordinates = new Array<MapPosition>();
    int min;
    int myrow;
    for (int row = y - n; row < y + n + 1; row++) {
      min = MyMath.min(2 * (row - y + n), n, -2 * (row - y - n) + 1);
      for (int col = x - min; col < x + min + 1; col++) {
        if ((col < 0) || (col >= width))
          continue;
        if (x == col && y == row)
          continue;
        else if (x % 2 == 0)
          myrow = 2 * y - row;
        else
          myrow = row;
        if ((myrow < 0) || (myrow >= height))
          continue;
        coordinates.add(new MapPosition(col, myrow));
      }
    }
    return coordinates;
  }

  public Array<MapPosition> getNeighbors(MapPosition pos) {
    return getNeighbors(pos.x, pos.y, 1);
  }

} public final class Influence implements Iterable<IntIntMap.Entry> {

  public final MapPosition position;

  public final Terrain terrain;

  /** Entity id to influence score. */
  private final IntIntMap influence = new IntIntMap();

  /**
   * Entity id to influence delta from last turn. Only for display when starting
   * a new turn, player needs to know how things evolve.
   */
  private final IntIntMap influenceDelta = new IntIntMap();

  /**
   * Entity id to influence target for next turn. Used to compute the increment
   * at each turn.
   */
  private final IntIntMap influenceTarget = new IntIntMap();

  private int mainInfluence = 0;

  private int mainInfluenceSource = -1;

  private int secondInfluenceDiff = 0;

  public Influence(MapPosition position, Terrain terrain) {
    this.position = position;
    this.terrain = terrain;
  }

  /** Set the source influence to the tile. */
  public void setInfluence(Entity source, int inf) {
    influence.put(source.getId(), inf);
    recomputeMain();
  }

  /** Move the source influence on the tile. */
  public void moveInfluence(Entity from, Entity to) {
    influence.getAndIncrement(to.getId(), 0, influence.remove(from.getId(), 0));
    recomputeMain();
  }

  /** Remove the source influence from the tile. */
  public void removeInfluence(Entity source) {
    influence.remove(source.getId(), 0);
    influenceDelta.remove(source.getId(), 0);
    recomputeMain();
  }

  public Iterable<IntIntMap.Entry> getDelta() {
    return influenceDelta;
  }

  public int getDelta(Entity source) {
    return influenceDelta.get(source.getId(), 0);
  }

  public void clearInfluenceTarget() {
    influenceTarget.clear();
  }

  public void increaseTarget(Entity source, int target) {
    influenceTarget.getAndIncrement(source.getId(), 0, target);
  }

  /** Make each participant influence closer to target and compute delta. */
  public void computeNewInfluence() {
    influenceDelta.clear();

    // make sure all involved empires have a target influence
    for (Entry e : influence) {
      if (!influenceTarget.containsKey(e.key))
        influenceTarget.put(e.key, 0);
    }

    // then compute the delta and apply it
    for (Entry e : influenceTarget) {
      final int current = influence.get(e.key, 0);
      final int target = e.value;
      int delta = 0;
      if (target > current)
        delta = max(1, (target - current) / 10);
      else if (target < current)
        delta = -max(1, (current - target) / 10);

      if (delta != 0) {
        // do nothing if same obviously
        influence.put(e.key, max(0, current + delta));
        influenceDelta.put(e.key, delta);
      }
    }
    recomputeMain();
  }

  private void recomputeMain() {
    int secondInfluence = Integer.MIN_VALUE;
    mainInfluence = terrain.moveCost() - 1;
    mainInfluenceSource = -1;
    if (influence.size > 0) {
      for (Iterator<Entry> it = influence.iterator(); it.hasNext();) {
        Entry e = it.next();
        if (e.value > mainInfluence) {
          secondInfluence = mainInfluence;
          mainInfluenceSource = e.key;
          mainInfluence = e.value;
        } else if (e.value <= 0) {
          it.remove();
        } else if (e.value > secondInfluence) {
          secondInfluence = e.value;
        }
      }
    }
    if (secondInfluence > 0)
      secondInfluenceDiff = mainInfluence - secondInfluence;
  }

  public int getInfluence(Entity e) {
    return influence.get(e.getId(), 0);
  }

  public int getSecondInfluenceDiff() {
    return secondInfluenceDiff;
  }

  public int getMainInfluenceSource() {
    return mainInfluenceSource;
  }

  public Entity getMainInfluenceSource(World world) {
    if (hasMainInfluence())
      return world.getEntity(mainInfluenceSource);
    else
      return null;
  }

  public int requiredInfluence(Entity e) {
    if (isMainInfluencer(e))
      return 0;
    int current = influence.get(e.getId(), 0);
    return max(terrain.moveCost(), mainInfluence + 1) - current;
  }

  public boolean isMainInfluencer(Entity e) {
    return e.getId() == mainInfluenceSource;
  }

  public boolean hasMainInfluence() {
    return mainInfluenceSource >= 0;
  }

  public boolean hasInfluence(Entity source) {
    return influence.get(source.getId(), 0) > 0;
  }

  public int getMaxInfluence() {
    int max = 0;
    for (Entry e : influence)
      if (e.value > max)
        max = e.value;
    return max(max, terrain.moveCost());
  }

  public int getTotalInfluence() {
    int total = 0;
    for (Entry e : influence)
      total += e.value;
    return max(total, terrain.moveCost());
  }

  @Override
  public Iterator<Entry> iterator() {
    return influence.iterator();
  }

  @Override
  public int hashCode() {
    return position.hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    else
      return (obj instanceof Influence) && position.equals(((Influence) obj).position);
  }

  @Override
  public String toString() {
    return terrain + influence.toString();
  }

} public final class MapPosition extends Component {

  public final int x, y;

  public MapPosition(int x, int y) {
    this.x = x;
    this.y = y;
  }

  @Override
  public int hashCode() {
    return x + y * 31;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this)
      return true;
    else if (obj instanceof MapPosition) {
      MapPosition p = (MapPosition) obj;
      return x == p.x && y == p.y;
    }
    return false;
  }

  @Override
  public String toString() {
    return "(" + x + "," + y + ")";
  }
}
 public enum Border {
  BOTTOM_RIGHT {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x + 1, y - ((x + 1) % 2));
    }
  },
  BOTTOM {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x, y - 1);
    }
  },
  BOTTOM_LEFT {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x - 1, y - ((x + 1) % 2));
    }
  },
  TOP_LEFT {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x - 1, y + (x % 2));
    }
  },
  TOP {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x, y + 1);
    }
  },
  TOP_RIGHT {
    @Override
    public MapPosition getNeighbor(int x, int y) {
      return new MapPosition(x + 1, y + (x % 2));
    }
  };

  abstract public MapPosition getNeighbor(int x, int y);

  public final MapPosition getNeighbor(MapPosition pos) {
    return getNeighbor(pos.x, pos.y);
  }
} public enum Terrain {

  DEEP_WATER("deep water", Color.NAVY, 50, false),

  SHALLOW_WATER("shallow water", Color.BLUE, 30, false),

  DESERT("desert", Color.YELLOW, 50, false),

  PLAIN("plain", Color.GREEN, 10, true),

  GRASSLAND("grassland", new Color(0, 0.5f, 0, 1), 15, true),

  FOREST("forest", Color.OLIVE, 30, true),

  HILLS("hills", Color.ORANGE, 30, true),

  MOUNTAIN("mountain", Color.MAROON, 50, false),

  ARCTIC("arctic", Color.WHITE, 100, false);

  private final Color color;

  private final int moveCost;

  private final String desc;

  private final boolean canStart;

  private Terrain(String desc, Color color, int moveCost, boolean canStart) {
    this.desc = desc;
    this.moveCost = moveCost;
    this.color = color;
    this.canStart = canStart;
  }

  public Color getColor() {
    return color;
  }

  public boolean moveBlock() {
    return false;
  }

  public boolean canStart() {
    return canStart;
  }

  public int moveCost() {
    return moveCost;
  }

  public int getTexture() {
    return ordinal();
  }

  public String getDesc() {
    return desc;
  }

} public final class Description extends Component {

  public String desc;

  public final AtlasRegion texture;

  public Description(String desc) {
    this(desc, null);
  }

  public Description(String desc, AtlasRegion texture) {
    this.desc = desc;
    this.texture = texture;
  }

  @Override
  public String toString() {
    return desc;
  }

}
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
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.badlogic.gdx.utils.IntIntMap;
import com.badlogic.gdx.utils.IntIntMap.Entry;
import com.badlogic.gdx.utils.ObjectIntMap;
import com.galvarez.ttw.EntityFactory;
import com.galvarez.ttw.model.DiplomaticSystem.Action;
import com.galvarez.ttw.model.DiplomaticSystem.State;
import com.galvarez.ttw.model.components.Army;
import com.galvarez.ttw.model.components.ArmyCommand;
import com.galvarez.ttw.model.components.Diplomacy;
import com.galvarez.ttw.model.components.InfluenceSource;
import com.galvarez.ttw.model.components.InfluenceSource.Modifiers;
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.model.map.MapTools.Border;
import com.galvarez.ttw.model.map.Terrain;
import com.galvarez.ttw.rendering.components.Description;
package com.galvarez.ttw.model;





/**
 * This classes computes the influence from the different sources (i.e. cities)
 * and update it every turn.
 * <p>
 * The game is centered on the influence idea. It starts from a source with a
 * certain power and flow on the neighboring map tiles. The throughput depends
 * on the source power, distance to the source and terrain cost/difficulty. When
 * multiple sources influence the same tile, it belongs to the source with the
 * highest influence score. For ease of understanding, influence is expressed as
 * percentages.
 * <p>
 * Ideally the influence progression should be:
 * <ul>
 * <li>at a constant pace and continuous from a turn to the next (for instance
 * one tile per turn)
 * <li>with decreasing values from the civilized center to the wild or disputed
 * border
 * <li>on a small scale so that close influence sources can fight for tiles
 * <p>
 * The model that works the best seems to be a linear interpolation of the
 * target influence. The target influence is computed as a waterfall algorithm
 * from the source.
 * 
 * @author Guillaume Alvarez
 */
@Wire
public final class InfluenceSystem extends EntitySystem {

  private static final Logger log = LoggerFactory.getLogger(InfluenceSystem.class);

  public static final int INITIAL_POWER = 100;

  private ComponentMapper<Empire> data;

  private ComponentMapper<InfluenceSource> sources;

  private ComponentMapper<Diplomacy> relations;