package com.galvarez.ttw.model; import java.util.ArrayList; 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.annotations.Wire; import com.artemis.systems.EntityProcessingSystem; import com.badlogic.gdx.utils.IntIntMap; import com.badlogic.gdx.utils.IntIntMap.Entry; import com.galvarez.ttw.model.DiplomaticSystem.Action; import com.galvarez.ttw.model.DiplomaticSystem.State; import com.galvarez.ttw.model.components.AIControlled; import com.galvarez.ttw.model.components.Diplomacy; import com.galvarez.ttw.model.components.InfluenceSource; 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.components.Name; @Wire public final class AIDiplomaticSystem extends EntityProcessingSystem { private static final Logger log = LoggerFactory.getLogger(AIDiplomaticSystem.class); private ComponentMapper<Diplomacy> relations; private ComponentMapper<MapPosition> positions; private ComponentMapper<InfluenceSource> influences; private DiplomaticSystem diplomaticSystem; private final GameMap map; @SuppressWarnings("unchecked") public AIDiplomaticSystem(GameMap map) { super(Aspect.getAspectForAll(AIControlled.class, Diplomacy.class)); this.map = map; } @Override protected boolean checkProcessing() { return true; } @Override protected void process(Entity entity) { Diplomacy diplo = relations.get(entity); // first check if we could revolt from overlord List<Entity> lords = diplo.getEmpires(State.TRIBUTE); if (lords.size() > 0) for (Entity lord : lords) if (isWeaker(lord, entity)) if (makeProposal(entity, diplo, lord, Action.DECLARE_WAR)) return; // TODO have a real AI algorithm for diplomacy // neighbors from the nicest to the baddest List<Entity> neighbors = getNeighboringSources(entity); // sign treaty with half the neighbors, not the last one (WAR for him!) if (diplo.knownStates.contains(State.TREATY) && neighbors.size() > 2) { for (int i = 0; i < neighbors.size() / 2 && i < neighbors.size() - 1; i++) { Entity target = neighbors.get(i); makeProposal(entity, diplo, target, Action.SIGN_TREATY); } } // try to be at war with somebody, and only that somebody if (diplo.knownStates.contains(State.WAR)) { List<Entity> atWarWith = diplo.getEmpires(State.WAR); Entity target = neighbors.isEmpty() ? null : neighbors.get(neighbors.size() - 1); // to change our war target, first make peace with preceding one for (Entity war : atWarWith) { if (target != war) makeProposal(entity, diplo, war, Action.MAKE_PEACE); } if (target != null && !atWarWith.contains(target)) makeProposal(entity, diplo, target, Action.DECLARE_WAR); } } private boolean isWeaker(Entity lord, Entity entity) { return influences.get(entity).power() >= influences.get(lord).power(); } private boolean makeProposal(Entity entity, Diplomacy diplo, Entity target, Action action) { if (diplomaticSystem.getPossibleActions(diplo, target).contains(action) // do not change status if same proposal is already on the table && diplo.proposals.get(target) != action // do no make proposal to ourself && entity != target) { log.debug("{} wants to {} with {}", entity.getComponent(Name.class), action.str, target.getComponent(Name.class)); diplo.proposals.put(target, action); return true; } return false; } /** Neighbors from the nicest to the worst. */ private List<Entity> getNeighboringSources(Entity entity) { IntIntMap neighbors = new IntIntMap(16); MapPosition pos = positions.get(entity); for (int i = 1; i < 10; i++) { // TODO really search for all tiles addInfluencer(neighbors, entity, pos.x + i, pos.y + i); addInfluencer(neighbors, entity, pos.x - i, pos.y + i); addInfluencer(neighbors, entity, pos.x + i, pos.y - i); addInfluencer(neighbors, entity, pos.x - i, pos.y - i); } List<Entry> entries = new ArrayList<>(); neighbors.entries().forEach(e -> entries.add(e)); entries.sort((e1, e2) -> Integer.compare(e1.value, e2.value)); List<Entity> res = new ArrayList<>(entries.size()); entries.forEach(e -> res.add(world.getEntity(e.key))); return res; } private void addInfluencer(IntIntMap neighbors, Entity capital, int x, int y) { Influence inf = map.getInfluenceAt(x, y); if (inf != null && !inf.isMainInfluencer(capital) && inf.hasMainInfluence()) neighbors.getAndIncrement(inf.getMainInfluenceSource(), 0, 1); } }