Introducing adventures.

This commit is contained in:
marcins78
2014-07-25 15:05:58 +00:00
parent 46d64f1dea
commit 82a5250ac9
15 changed files with 406 additions and 14 deletions

View File

@@ -0,0 +1,22 @@
package com.gempukku.lotro.cards;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.actions.CostToEffectAction;
public class AbstractAdventure extends AbstractLotroCardBlueprint {
public AbstractAdventure(String name, String subTitle) {
super(null, CardType.ADVENTURE, null, name, subTitle, true);
}
@Override
public CostToEffectAction getPlayCardAction(String playerId, LotroGame game, PhysicalCard self, int twilightModifier, boolean ignoreRoamingPenalty) {
return null;
}
@Override
public int getTwilightCost() {
return 0;
}
}

View File

@@ -1,5 +1,5 @@
package com.gempukku.lotro.common;
public enum CardType implements Filterable {
THE_ONE_RING, SITE, COMPANION, ALLY, MINION, POSSESSION, ARTIFACT, EVENT, CONDITION, FOLLOWER
THE_ONE_RING, SITE, COMPANION, ALLY, MINION, POSSESSION, ARTIFACT, EVENT, CONDITION, FOLLOWER, ADVENTURE
}

View File

@@ -0,0 +1,94 @@
package com.gempukku.lotro.game.adventure;
import com.gempukku.lotro.common.Zone;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.GameState;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.GameUtils;
import com.gempukku.lotro.logic.actions.SystemQueueAction;
import com.gempukku.lotro.logic.timing.PlayerOrderFeedback;
import com.gempukku.lotro.logic.timing.UnrespondableEffect;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
import com.gempukku.lotro.logic.timing.processes.pregame.SetupSoloAdventureGameProcess;
import com.gempukku.lotro.logic.timing.processes.turn.ShadowPhaseOfAIPlayerGameProcess;
import com.gempukku.lotro.logic.timing.processes.turn.ai.AIPlayerAssignsArcheryTotalGameProcess;
import com.gempukku.lotro.logic.timing.results.PlayCardResult;
import java.util.Set;
public class DefaultSoloAdventure extends SoloAdventure {
private LotroCardBlueprintLibrary _library;
private SiteSelection _siteSelection;
private ShadowAI _shadowAI;
private String _adventureCard;
private String _startingSite;
public DefaultSoloAdventure(LotroCardBlueprintLibrary library,
SiteSelection siteSelection, ShadowAI shadowAI,
String adventureCard, String startingSite) {
_library = library;
_siteSelection = siteSelection;
_shadowAI = shadowAI;
_adventureCard = adventureCard;
_startingSite = startingSite;
}
@Override
public void appendNextSiteAction(SystemQueueAction action) {
action.appendEffect(
new UnrespondableEffect() {
@Override
protected void doPlayEffect(LotroGame game) {
final GameState gameState = game.getGameState();
try {
PhysicalCard newSite = gameState.createPhysicalCard("AI", _library, _siteSelection.getNextSite(game));
newSite.setSiteNumber(gameState.getCurrentSiteNumber() + 1);
gameState.addCardToZone(game, newSite, Zone.ADVENTURE_PATH);
gameState.sendMessage(newSite.getOwner() + " plays " + GameUtils.getCardLink(newSite));
game.getActionsEnvironment().emitEffectResult(new PlayCardResult(Zone.ADVENTURE_DECK, newSite, null, null, false));
} catch (CardNotFoundException exp) {
throw new RuntimeException("Unable to create a requested card", exp);
}
}
});
}
@Override
public GameProcess getStartingGameProcess(Set<String> players, PlayerOrderFeedback playerOrderFeedback) {
final String player = players.iterator().next();
return new SetupSoloAdventureGameProcess(_adventureCard, _startingSite, player, playerOrderFeedback);
}
@Override
public GameProcess getAfterFellowshipPhaseGameProcess() {
return new ShadowPhaseOfAIPlayerGameProcess(_shadowAI);
}
@Override
public GameProcess getAfterFellowshipArcheryGameProcess(int fellowshipArcheryTotal, GameProcess followingProcess) {
return new AIPlayerAssignsArcheryTotalGameProcess(fellowshipArcheryTotal, followingProcess);
}
@Override
public GameProcess getAfterFellowshipAssignmentGameProcess(Set<PhysicalCard> leftoverMinions, GameProcess followingProcess) {
return null;
}
@Override
public GameProcess getBeforeFellowshipChooseToMoveGameProcess(GameProcess followingProcess) {
return null;
}
@Override
public GameProcess getPlayerStaysGameProcess(LotroGame game, GameProcess followingProcess) {
return null;
}
@Override
public boolean isSolo() {
return false;
}
}

View File

@@ -0,0 +1,8 @@
package com.gempukku.lotro.game.adventure;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.timing.Action;
public interface ShadowAI {
public Action getNextShadowAction(LotroGame game);
}

View File

@@ -0,0 +1,7 @@
package com.gempukku.lotro.game.adventure;
import com.gempukku.lotro.game.state.LotroGame;
public interface SiteSelection {
public String getNextSite(LotroGame game);
}

View File

@@ -0,0 +1,20 @@
package com.gempukku.lotro.game.adventure;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.Adventure;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.game.state.actions.DefaultActionsEnvironment;
import com.gempukku.lotro.logic.modifiers.CantDiscardFromPlayModifier;
import com.gempukku.lotro.logic.modifiers.CantReturnToHandModifier;
import com.gempukku.lotro.logic.modifiers.ModifiersLogic;
public abstract class SoloAdventure implements Adventure {
@Override
public void applyAdventureRules(LotroGame game, DefaultActionsEnvironment actionsEnvironment, ModifiersLogic modifiersLogic) {
modifiersLogic.addAlwaysOnModifier(
new CantDiscardFromPlayModifier(null, "Can't be discarded from play", CardType.ADVENTURE, Filters.any));
modifiersLogic.addAlwaysOnModifier(
new CantReturnToHandModifier(null, "Can't be returned to hand", CardType.ADVENTURE, Filters.any));
}
}

View File

@@ -1,15 +1,34 @@
package com.gempukku.lotro.game.state;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.common.Block;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.common.Phase;
import com.gempukku.lotro.common.Side;
import com.gempukku.lotro.common.Token;
import com.gempukku.lotro.common.Zone;
import com.gempukku.lotro.communication.GameStateListener;
import com.gempukku.lotro.game.*;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.LotroCardBlueprint;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.PhysicalCardImpl;
import com.gempukku.lotro.game.PhysicalCardVisitor;
import com.gempukku.lotro.logic.PlayerOrder;
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
import com.gempukku.lotro.logic.modifiers.ModifierFlag;
import com.gempukku.lotro.logic.timing.GameStats;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class GameState {
private static Logger _log = Logger.getLogger(GameState.class);
@@ -85,8 +104,8 @@ public class GameState {
addPlayerCards(playerId, decks, library);
try {
_ringBearers.put(playerId, createPhysicalCard(playerId, library, ringBearers.get(playerId)));
_rings.put(playerId, createPhysicalCard(playerId, library, rings.get(playerId)));
_ringBearers.put(playerId, createPhysicalCardImpl(playerId, library, ringBearers.get(playerId)));
_rings.put(playerId, createPhysicalCardImpl(playerId, library, rings.get(playerId)));
} catch (CardNotFoundException exp) {
throw new RuntimeException("Unable to create game, due to either ring-bearer or ring being invalid cards");
}
@@ -110,7 +129,7 @@ public class GameState {
private void addPlayerCards(String playerId, List<String> cards, LotroCardBlueprintLibrary library) {
for (String blueprintId : cards) {
try {
PhysicalCardImpl physicalCard = createPhysicalCard(playerId, library, blueprintId);
PhysicalCardImpl physicalCard = createPhysicalCardImpl(playerId, library, blueprintId);
if (physicalCard.getBlueprint().getCardType() == CardType.SITE) {
physicalCard.setZone(Zone.ADVENTURE_DECK);
_adventureDecks.get(playerId).add(physicalCard);
@@ -124,7 +143,11 @@ public class GameState {
}
}
private PhysicalCardImpl createPhysicalCard(String playerId, LotroCardBlueprintLibrary library, String blueprintId) throws CardNotFoundException {
public PhysicalCard createPhysicalCard(String ownerPlayerId, LotroCardBlueprintLibrary library, String blueprintId) throws CardNotFoundException {
return createPhysicalCardImpl(ownerPlayerId, library, blueprintId);
}
private PhysicalCardImpl createPhysicalCardImpl(String playerId, LotroCardBlueprintLibrary library, String blueprintId) throws CardNotFoundException {
LotroCardBlueprint card = library.getLotroCardBlueprint(blueprintId);
int cardId = nextCardId();

View File

@@ -3,6 +3,7 @@ package com.gempukku.lotro.game.state;
import com.gempukku.lotro.common.Phase;
import com.gempukku.lotro.communication.UserFeedback;
import com.gempukku.lotro.game.ActionsEnvironment;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.LotroFormat;
import com.gempukku.lotro.logic.modifiers.ModifiersEnvironment;
import com.gempukku.lotro.logic.modifiers.ModifiersQuerying;
@@ -10,6 +11,8 @@ import com.gempukku.lotro.logic.modifiers.ModifiersQuerying;
public interface LotroGame {
public GameState getGameState();
public LotroCardBlueprintLibrary getLotroCardBlueprintLibrary();
public ModifiersEnvironment getModifiersEnvironment();
public ModifiersQuerying getModifiersQuerying();

View File

@@ -1,15 +1,17 @@
package com.gempukku.lotro.logic.modifiers;
import com.gempukku.lotro.common.Filterable;
import com.gempukku.lotro.filters.Filter;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.GameState;
public class CantReturnToHandModifier extends AbstractModifier {
private Filter _sourceFilter;
public CantReturnToHandModifier(PhysicalCard source, String text, Filter affectFilter, Filter sourceFilter) {
public CantReturnToHandModifier(PhysicalCard source, String text, Filterable affectFilter, Filterable sourceFilter) {
super(source, text, affectFilter, ModifierEffect.RETURN_TO_HAND_MODIFIER);
_sourceFilter = sourceFilter;
_sourceFilter = Filters.and(sourceFilter);
}
@Override

View File

@@ -51,8 +51,10 @@ public class DefaultLotroGame implements LotroGame {
private Set<GameResultListener> _gameResultListeners = new HashSet<GameResultListener>();
private Set<String> _requestedCancel = new HashSet<String>();
private LotroCardBlueprintLibrary _library;
public DefaultLotroGame(LotroFormat format, Map<String, LotroDeck> decks, UserFeedback userFeedback, final LotroCardBlueprintLibrary library) {
_library = library;
_adventure = format.getAdventure();
_format = format;
_actionStack = new ActionStack();
@@ -97,6 +99,8 @@ public class DefaultLotroGame implements LotroGame {
_adventure.applyAdventureRules(this, _actionsEnvironment, _modifiersLogic);
}
@Override
public boolean shouldAutoPass(String playerId, Phase phase) {
final Set<Phase> passablePhases = _autoPassConfiguration.get(playerId);
@@ -231,6 +235,11 @@ public class DefaultLotroGame implements LotroGame {
return _gameState;
}
@Override
public LotroCardBlueprintLibrary getLotroCardBlueprintLibrary() {
return _library;
}
@Override
public ActionsEnvironment getActionsEnvironment() {
return _actionsEnvironment;

View File

@@ -0,0 +1,50 @@
package com.gempukku.lotro.logic.timing.processes.pregame;
import com.gempukku.lotro.common.Zone;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.GameState;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.PlayerOrder;
import com.gempukku.lotro.logic.timing.PlayerOrderFeedback;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
import java.util.Arrays;
import java.util.Collections;
public class SetupSoloAdventureGameProcess implements GameProcess {
private String _startingSite;
private String _player;
private PlayerOrderFeedback _playerOrderFeedback;
private String _adventureCard;
public SetupSoloAdventureGameProcess(String adventureCard, String startingSite, String player, PlayerOrderFeedback playerOrderFeedback) {
_adventureCard = adventureCard;
_startingSite = startingSite;
_player = player;
_playerOrderFeedback = playerOrderFeedback;
}
@Override
public void process(LotroGame game) {
_playerOrderFeedback.setPlayerOrder(new PlayerOrder(Arrays.asList(_player)), _player);
final GameState gameState = game.getGameState();
try {
final PhysicalCard adventureCard = gameState.createPhysicalCard(_player, game.getLotroCardBlueprintLibrary(), _adventureCard);
gameState.addCardToZone(game, adventureCard, Zone.SUPPORT);
final PhysicalCard startingSite = gameState.createPhysicalCard("AI", game.getLotroCardBlueprintLibrary(), _startingSite);
startingSite.setSiteNumber(1);
gameState.addCardToZone(game, startingSite, Zone.ADVENTURE_PATH);
game.getGameState().setPlayerPosition(_player, 1);
} catch (CardNotFoundException exp) {
// Ignore
}
}
@Override
public GameProcess getNextProcess() {
return new PlayRingBearerRingAndAddBurdersGameProcess(Collections.singletonMap(_player, 0), _player);
}
}

View File

@@ -0,0 +1,34 @@
package com.gempukku.lotro.logic.timing.processes.turn;
import com.gempukku.lotro.common.Phase;
import com.gempukku.lotro.game.adventure.ShadowAI;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
import com.gempukku.lotro.logic.timing.processes.turn.ai.PlayAdventureShadowActionsGameProcess;
import com.gempukku.lotro.logic.timing.processes.turn.general.EndOfPhaseGameProcess;
import com.gempukku.lotro.logic.timing.processes.turn.general.StartOfPhaseGameProcess;
public class ShadowPhaseOfAIPlayerGameProcess implements GameProcess {
private GameProcess _followingGameProcess;
private ShadowAI _shadowAI;
public ShadowPhaseOfAIPlayerGameProcess(ShadowAI shadowAI) {
_shadowAI = shadowAI;
}
@Override
public void process(LotroGame game) {
if (game.getModifiersQuerying().shouldSkipPhase(game.getGameState(), Phase.SHADOW, "AI"))
_followingGameProcess = new ManeuverGameProcess();
else
_followingGameProcess = new StartOfPhaseGameProcess(Phase.SHADOW, "AI",
new PlayAdventureShadowActionsGameProcess(_shadowAI,
new EndOfPhaseGameProcess(Phase.SHADOW,
new ManeuverGameProcess())));
}
@Override
public GameProcess getNextProcess() {
return _followingGameProcess;
}
}

View File

@@ -0,0 +1,89 @@
package com.gempukku.lotro.logic.timing.processes.turn.ai;
import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.filters.Filter;
import com.gempukku.lotro.filters.Filters;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.GameState;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.actions.SubAction;
import com.gempukku.lotro.logic.actions.SystemQueueAction;
import com.gempukku.lotro.logic.effects.WoundCharactersEffect;
import com.gempukku.lotro.logic.modifiers.ModifiersQuerying;
import com.gempukku.lotro.logic.timing.UnrespondableEffect;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
public class AIPlayerAssignsArcheryTotalGameProcess implements GameProcess {
private int _woundsToAssign;
private GameProcess _followingProcess;
public AIPlayerAssignsArcheryTotalGameProcess(int woundsToAssign, GameProcess followingProcess) {
_woundsToAssign = woundsToAssign;
_followingProcess = followingProcess;
}
@Override
public void process(LotroGame game) {
if (_woundsToAssign > 0) {
final Filter filterPriority =
Filters.and(
CardType.MINION,
Filters.owner("AI"),
new Filter() {
@Override
public boolean accepts(GameState gameState, ModifiersQuerying modifiersQuerying, PhysicalCard physicalCard) {
return modifiersQuerying.canTakeArcheryWound(gameState, physicalCard)
&& gameState.getWounds(physicalCard) < modifiersQuerying.getVitality(gameState, physicalCard) - 1;
}
});
final Filter filterFallback =
Filters.and(
CardType.MINION,
Filters.owner("AI"),
new Filter() {
@Override
public boolean accepts(GameState gameState, ModifiersQuerying modifiersQuerying, PhysicalCard physicalCard) {
return modifiersQuerying.canTakeArcheryWound(gameState, physicalCard);
}
}
);
final SystemQueueAction action = new SystemQueueAction();
for (int i = 0; i < _woundsToAssign; i++) {
UnrespondableEffect chooseRandomMinionAndWound = new UnrespondableEffect() {
@Override
protected void doPlayEffect(LotroGame game) {
Collection<PhysicalCard> acceptableCards = Filters.filterActive(game.getGameState(), game.getModifiersQuerying(), filterPriority);
if (acceptableCards.size() == 0)
acceptableCards = Filters.filterActive(game.getGameState(), game.getModifiersQuerying(), filterFallback);
List<PhysicalCard> possibleChoices = new ArrayList<PhysicalCard>(acceptableCards);
if (possibleChoices.size()>0) {
SubAction subAction = new SubAction(action);
Random rnd = new Random();
final int randomIndex = rnd.nextInt(possibleChoices.size());
WoundCharactersEffect woundCharacter = new WoundCharactersEffect((PhysicalCard) null, possibleChoices.get(randomIndex));
woundCharacter.setSourceText("Archery Fire");
subAction.appendEffect(woundCharacter);
game.getActionsEnvironment().addActionToStack(subAction);
}
}
};
action.appendEffect(chooseRandomMinionAndWound);
}
game.getActionsEnvironment().addActionToStack(action);
}
}
@Override
public GameProcess getNextProcess() {
return _followingProcess;
}
}

View File

@@ -0,0 +1,34 @@
package com.gempukku.lotro.logic.timing.processes.turn.ai;
import com.gempukku.lotro.game.adventure.ShadowAI;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.timing.Action;
import com.gempukku.lotro.logic.timing.processes.GameProcess;
public class PlayAdventureShadowActionsGameProcess implements GameProcess {
private ShadowAI _shadowAI;
private GameProcess _followingGameProcess;
private GameProcess _nextProcess;
public PlayAdventureShadowActionsGameProcess(ShadowAI shadowAI, GameProcess followingGameProcess) {
_shadowAI = shadowAI;
_followingGameProcess = followingGameProcess;
}
@Override
public void process(LotroGame game) {
Action action = _shadowAI.getNextShadowAction(game);
if (action != null) {
game.getActionsEnvironment().addActionToStack(action);
_nextProcess = new PlayAdventureShadowActionsGameProcess(_shadowAI, _followingGameProcess);
} else {
_nextProcess = _followingGameProcess;
}
}
@Override
public GameProcess getNextProcess() {
return _nextProcess;
}
}

View File

@@ -16,8 +16,6 @@ public class ShadowPlayerAssignsArcheryDamageGameProcess implements GameProcess
private int _woundsToAssign;
private GameProcess _followingGameProcess;
private GameProcess _nextProcess;
public ShadowPlayerAssignsArcheryDamageGameProcess(String playerId, int woundsToAssign, GameProcess followingGameProcess) {
_playerId = playerId;
_woundsToAssign = woundsToAssign;
@@ -49,11 +47,10 @@ public class ShadowPlayerAssignsArcheryDamageGameProcess implements GameProcess
game.getActionsEnvironment().addActionToStack(action);
}
_nextProcess = _followingGameProcess;
}
@Override
public GameProcess getNextProcess() {
return _nextProcess;
return _followingGameProcess;
}
}