package games.strategy.triplea.attachments; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import games.strategy.engine.data.Attachable; import games.strategy.engine.data.GameData; import games.strategy.engine.data.GameParseException; import games.strategy.engine.data.IAttachment; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.annotations.GameProperty; import games.strategy.engine.delegate.IDelegateBridge; import games.strategy.triplea.Constants; import games.strategy.triplea.MapSupport; import games.strategy.triplea.delegate.Matches; import games.strategy.triplea.formatter.MyFormatter; import games.strategy.util.CompositeMatchAnd; import games.strategy.util.Match; import games.strategy.util.Tuple; /** * A class of attachments that can be "activated" during a user action delegate. * For now they will just be conditions that can then fire triggers. */ @MapSupport public class UserActionAttachment extends AbstractUserActionAttachment { private static final long serialVersionUID = 5268397563276055355L; public UserActionAttachment(final String name, final Attachable attachable, final GameData gameData) { super(name, attachable, gameData); } public static Collection<UserActionAttachment> getUserActionAttachments(final PlayerID player) { final ArrayList<UserActionAttachment> returnList = new ArrayList<>(); final Map<String, IAttachment> map = player.getAttachments(); for (final Entry<String, IAttachment> entry : map.entrySet()) { final IAttachment a = entry.getValue(); if (a.getName().startsWith(Constants.USERACTION_ATTACHMENT_PREFIX) && a instanceof UserActionAttachment) { returnList.add((UserActionAttachment) a); } } return returnList; } public static UserActionAttachment get(final PlayerID player, final String nameOfAttachment) { final UserActionAttachment rVal = (UserActionAttachment) player.getAttachment(nameOfAttachment); if (rVal == null) { throw new IllegalStateException( "UserActionAttachment: No attachment for:" + player.getName() + " with name: " + nameOfAttachment); } return rVal; } // instance variables: private ArrayList<Tuple<String, String>> m_activateTrigger = new ArrayList<>(); /** * Adds to, not sets. Anything that adds to instead of setting needs a clear function as well. * (same as one in TriggerAttachment) */ @GameProperty(xmlProperty = true, gameProperty = true, adds = true) public void setActivateTrigger(final String value) throws GameParseException { // triggerName:numberOfTimes:useUses:testUses:testConditions:testChance final String[] s = value.split(":"); if (s.length != 6) { throw new GameParseException( "activateTrigger must have 6 parts: triggerName:numberOfTimes:useUses:testUses:testConditions:testChance" + thisErrorMsg()); } TriggerAttachment trigger = null; for (final PlayerID player : getData().getPlayerList().getPlayers()) { for (final TriggerAttachment ta : TriggerAttachment.getTriggers(player, getData(), null)) { if (ta.getName().equals(s[0])) { trigger = ta; break; } } if (trigger != null) { break; } } if (trigger == null) { throw new GameParseException("No TriggerAttachment named: " + s[0] + thisErrorMsg()); } String options = value; options = options.replaceFirst((s[0] + ":"), ""); final int numberOfTimes = getInt(s[1]); if (numberOfTimes < 0) { throw new GameParseException( "activateTrigger must be positive for the number of times to fire: " + s[1] + thisErrorMsg()); } getBool(s[2]); getBool(s[3]); getBool(s[4]); getBool(s[5]); m_activateTrigger.add(Tuple.of(s[0], options)); } @GameProperty(xmlProperty = true, gameProperty = true, adds = false) public void setActivateTrigger(final ArrayList<Tuple<String, String>> value) { m_activateTrigger = value; } public ArrayList<Tuple<String, String>> getActivateTrigger() { return m_activateTrigger; } public void clearActivateTrigger() { m_activateTrigger.clear(); } public void resetActivateTrigger() { m_activateTrigger = new ArrayList<>(); } public static void fireTriggers(final UserActionAttachment actionAttachment, final HashMap<ICondition, Boolean> testedConditionsSoFar, final IDelegateBridge aBridge) { final GameData data = aBridge.getData(); for (final Tuple<String, String> tuple : actionAttachment.getActivateTrigger()) { // numberOfTimes:useUses:testUses:testConditions:testChance TriggerAttachment toFire = null; for (final PlayerID player : data.getPlayerList().getPlayers()) { for (final TriggerAttachment ta : TriggerAttachment.getTriggers(player, data, null)) { if (ta.getName().equals(tuple.getFirst())) { toFire = ta; break; } } if (toFire != null) { break; } } final HashSet<TriggerAttachment> toFireSet = new HashSet<>(); toFireSet.add(toFire); final String[] options = tuple.getSecond().split(":"); final int numberOfTimesToFire = getInt(options[0]); final boolean useUsesToFire = getBool(options[1]); final boolean testUsesToFire = getBool(options[2]); final boolean testConditionsToFire = getBool(options[3]); final boolean testChanceToFire = getBool(options[4]); if (testConditionsToFire) { if (!testedConditionsSoFar.containsKey(toFire)) { // this should directly add the new tests to testConditionsToFire... TriggerAttachment.collectTestsForAllTriggers(toFireSet, aBridge, new HashSet<>(testedConditionsSoFar.keySet()), testedConditionsSoFar); } if (!AbstractTriggerAttachment.isSatisfiedMatch(testedConditionsSoFar).match(toFire)) { continue; } } for (int i = 0; i < numberOfTimesToFire; ++i) { aBridge.getHistoryWriter().startEvent(MyFormatter.attachmentNameToText(actionAttachment.getName()) + " activates a trigger called: " + MyFormatter.attachmentNameToText(toFire.getName())); TriggerAttachment.fireTriggers(toFireSet, testedConditionsSoFar, aBridge, null, null, useUsesToFire, testUsesToFire, testChanceToFire, false); } } } public Set<PlayerID> getOtherPlayers() { final HashSet<PlayerID> otherPlayers = new HashSet<>(); otherPlayers.add((PlayerID) this.getAttachedTo()); otherPlayers.addAll(m_actionAccept); return otherPlayers; } /** * @return gets the valid actions for this player. */ public static Collection<UserActionAttachment> getValidActions(final PlayerID player, final HashMap<ICondition, Boolean> testedConditions, final GameData data) { return Match.getMatches(getUserActionAttachments(player), new CompositeMatchAnd<>( Matches.AbstractUserActionAttachmentCanBeAttempted(testedConditions))); } @Override public void validate(final GameData data) throws GameParseException { super.validate(data); } }