package com.galvarez.ttw.model; import static java.util.EnumSet.allOf; import static java.util.EnumSet.complementOf; import static java.util.EnumSet.of; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.function.Predicate; 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.model.components.AIControlled; import com.galvarez.ttw.model.components.Diplomacy; import com.galvarez.ttw.rendering.IconsSystem.Type; import com.galvarez.ttw.rendering.NotificationsSystem; import com.galvarez.ttw.rendering.components.Description; import com.galvarez.ttw.rendering.components.Name; import com.galvarez.ttw.screens.overworld.OverworldScreen; /** * For every empire, apply diplomatic modifiers. * <p> * Each empire can make different proposals to other empires. When the are * compatible then the proposal is enacted. Some require both empires to agree, * like signing a treaty. Obviously one empire can declare war to another * whatever the peer wants to do. * * @author Guillaume Alvarez */ @Wire public final class DiplomaticSystem extends EntitySystem { private static final Logger log = LoggerFactory.getLogger(DiplomaticSystem.class); public enum State { NONE, WAR, TREATY, PROTECTORATE, TRIBUTE; } 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); } } private NotificationsSystem notifications; private ComponentMapper<Diplomacy> relations; private ComponentMapper<AIControlled> ai; private final OverworldScreen screen; private final boolean startWithDiplomacy; @SuppressWarnings("unchecked") public DiplomaticSystem(OverworldScreen screen, boolean startWithDiplomacy) { super(Aspect.getAspectForAll(Diplomacy.class)); this.screen = screen; this.startWithDiplomacy = startWithDiplomacy; } @Override protected void inserted(Entity e) { super.inserted(e); if (startWithDiplomacy) relations.get(e).knownStates.addAll(Arrays.asList(State.values())); } @Override protected void removed(Entity removed) { super.removed(removed); log.debug("Remove {} from all empires relations.", removed.getComponent(Description.class)); for (Entity e : getActives()) { Diplomacy diplo = relations.get(e); diplo.lastChange.remove(removed, 0); diplo.proposals.remove(removed); diplo.relations.remove(removed); } } @Override protected boolean checkProcessing() { return true; } @Override protected void processEntities(ImmutableBag<Entity> entities) { for (Entity entity : entities) { Diplomacy diplo = relations.get(entity); for (Iterator<Entry<Entity, Action>> it = diplo.proposals.entrySet().iterator(); it.hasNext();) { Entry<Entity, Action> proposal = it.next(); Entity target = proposal.getKey(); Action action = proposal.getValue(); Diplomacy targetDiplo = relations.get(target); if (action.compatibleWith(targetDiplo.proposals.get(entity))) { // remove the accepted proposal it.remove(); changeRelation(entity, diplo, target, targetDiplo, action); } } } } void changeRelation(Entity entity, Diplomacy diplo, Entity target, Diplomacy targetDiplo, Action action) { diplo.relations.put(target, action.afterMe); targetDiplo.relations.put(entity, action.afterYou); targetDiplo.proposals.remove(entity); // prevent changing state for a few turns diplo.lastChange.put(target, screen.getTurnNumber()); targetDiplo.lastChange.put(entity, screen.getTurnNumber()); // notify the player log.info("Relation between {} and {} is now {}/{}", entity.getComponent(Name.class), target.getComponent(Name.class), diplo.getRelationWith(target), targetDiplo.getRelationWith(entity)); if (!ai.has(entity)) notifications.addNotification(screen::diplomacyMenu, null, Type.DIPLOMACY, "Your relation with %s is now %s!", target.getComponent(Name.class), action.afterMe); else if (!ai.has(target)) notifications.addNotification(screen::diplomacyMenu, null, Type.DIPLOMACY, "Your relation with %s is now %s!", entity.getComponent(Name.class), action.afterYou); } public void clearRelations(Entity empire, Diplomacy diplo) { diplo.proposals.clear(); for (Entity e : diplo.relations.keySet()) { relations.get(e).relations.remove(empire); } diplo.relations.clear(); } public Collection<Action> getPossibleActions(Diplomacy diplo, Entity target) { Set<Action> actions = EnumSet.of(Action.NO_CHANGE); for (Action action : Action.values()) { if (diplo.knownStates.contains(action.afterMe) && action.before.contains(diplo.getRelationWith(target))) actions.add(action); } return actions; } }