Package com.jcloisterzone.game

Source Code of com.jcloisterzone.game.Game

package com.jcloisterzone.game;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.MutableClassToInstanceMap;
import com.google.common.eventbus.EventBus;
import com.jcloisterzone.EventBusExceptionHandler;
import com.jcloisterzone.Player;
import com.jcloisterzone.PointCategory;
import com.jcloisterzone.action.PlayerAction;
import com.jcloisterzone.board.Board;
import com.jcloisterzone.board.Location;
import com.jcloisterzone.board.Position;
import com.jcloisterzone.board.Tile;
import com.jcloisterzone.board.TilePack;
import com.jcloisterzone.board.pointer.FeaturePointer;
import com.jcloisterzone.bugreport.ReportingTool;
import com.jcloisterzone.config.Config;
import com.jcloisterzone.event.Event;
import com.jcloisterzone.event.Idempotent;
import com.jcloisterzone.event.PlayEvent;
import com.jcloisterzone.event.PlayerTurnEvent;
import com.jcloisterzone.event.ScoreEvent;
import com.jcloisterzone.event.TileEvent;
import com.jcloisterzone.event.Undoable;
import com.jcloisterzone.feature.City;
import com.jcloisterzone.feature.Farm;
import com.jcloisterzone.feature.Feature;
import com.jcloisterzone.feature.visitor.score.CompletableScoreContext;
import com.jcloisterzone.feature.visitor.score.ScoreContext;
import com.jcloisterzone.figure.Follower;
import com.jcloisterzone.figure.Meeple;
import com.jcloisterzone.figure.predicate.MeeplePredicates;
import com.jcloisterzone.game.capability.FairyCapability;
import com.jcloisterzone.game.capability.PrincessCapability;
import com.jcloisterzone.game.phase.CreateGamePhase;
import com.jcloisterzone.game.phase.GameOverPhase;
import com.jcloisterzone.game.phase.Phase;


/**
* Other information than board needs in game. Contains players with their
* points, followers ... and game rules of current game.
*/
public class Game extends GameSettings {

    protected final transient Logger logger = LoggerFactory.getLogger(getClass());
    private ReportingTool reportingTool;

    private Config config;

    /** pack of remaining tiles */
    private TilePack tilePack;
    /** pack of remaining tiles */
    private Tile currentTile;
    /** game board, contains placed tiles */
    private Board board;

    /** list of players in game */
    private Player[] plist;
    /** rules of current game */

    /** player in turn */
    private Player turnPlayer;

    private final ClassToInstanceMap<Phase> phases = MutableClassToInstanceMap.create();
    private Phase phase;

    private List<Capability> capabilities = new ArrayList<>();
    private FairyCapability fairyCapability; //shortcut

    private Undoable lastUndoable;
    private Phase lastUndoablePhase;

    private final EventBus eventBus = new EventBus(new EventBusExceptionHandler("game event bus"));
    //events are delayed and fired after phase is handled (and eventually switched to the new one) - important especially for AI handlers to not start before swithc is done
    private final Deque<Event> eventQueue = new ArrayDeque<>();

    private int idSequenceCurrVal = 0;


    public Game(String gameId) {
        super(gameId);
    }

    public EventBus getEventBus() {
        return eventBus;
    }

    public void post(Event event) {
        eventQueue.add(event);
        for (Capability capability: capabilities) {
            capability.handleEvent(event);
        }
        if (event instanceof PlayEvent) {
            if (event instanceof TileEvent && event.getType() == TileEvent.PLACEMENT) {
                lastUndoable = (Undoable) event;
                lastUndoablePhase = phase;
            } else {
                if (event.getClass().getAnnotation(Idempotent.class) == null) {
                    lastUndoable = null;
                    lastUndoablePhase = null;
                }
            }
        }
    }

    public void flushEventQueue() {
        Event event;
        while ((event = eventQueue.poll()) != null) {
            eventBus.post(event);
        }
    }

    public boolean isUndoAllowed() {
        return lastUndoable != null;
    }

    public void undo() {
        //proof of concept
        if (lastUndoable instanceof TileEvent) {
            Tile tile = ((TileEvent)lastUndoable).getTile();
            Position pos = tile.getPosition();

            lastUndoable.undo(this);
            phase = lastUndoablePhase;
            lastUndoable = null;
            lastUndoablePhase = null;

            //post should be in event undo. silent vs firing undo ?
            post(new TileEvent(TileEvent.REMOVE, getActivePlayer(), tile, pos));
            phase.enter();
        }
    }

    public Config getConfig() {
        return config;
    }

    public void setConfig(Config config) {
        this.config = config;
    }

    public Tile getCurrentTile() {
        return currentTile;
    }

    public void setCurrentTile(Tile currentTile) {
        this.currentTile = currentTile;
    }

    public Phase getPhase() {
        return phase;
    }

    public void setPhase(Phase phase) {
        phase.setEntered(false);
        this.phase = phase;
    }

    public ClassToInstanceMap<Phase> getPhases() {
        return phases;
    }

    public Iterable<Meeple> getDeployedMeeples() {
        Iterable<Meeple> iter = Collections.emptyList();
        for (Player player : plist) {
            iter = Iterables.concat(iter, player.getFollowers(), player.getSpecialMeeples());
        }
        return Iterables.filter(iter, MeeplePredicates.deployed());
    }

    public Player getTurnPlayer() {
        return turnPlayer;
    }

    public void setTurnPlayer(Player turnPlayer) {
        this.turnPlayer = turnPlayer;
        post(new PlayerTurnEvent(turnPlayer));
    }

    /**
     * Returns player who is allowed to make next action.
     * @return
     */
    public Player getActivePlayer() {
        Phase phase = getPhase();
        return phase == null ? null : phase.getActivePlayer();
    }


    /**
     * Ends turn of current active player and make active the next.
     */
    public Player getNextPlayer() {
        return getNextPlayer(turnPlayer);
    }

    public Player getNextPlayer(Player p) {
        int playerIndex = p.getIndex();
        int nextPlayerIndex = playerIndex == (plist.length - 1) ? 0 : playerIndex + 1;
        return getPlayer(nextPlayerIndex);
    }


    /**
     * Return player with the given index.
     * @param i player index
     * @return demand player
     */
    public Player getPlayer(int i) {
        return plist[i];
    }

    /**
     * Returns whole player list
     * @return player list
     */
    public Player[] getAllPlayers() {
        return plist;
    }

    public TilePack getTilePack() {
        return tilePack;
    }

    public void setTilePack(TilePack tilePack) {
        this.tilePack = tilePack;
    }

    public Board getBoard() {
        return board;
    }


    public Meeple getMeeple(final Position p, final Location loc, Class<? extends Meeple> meepleType, Player owner) {
        for (Meeple m : getDeployedMeeples()) {
            if (m.at(p) && m.getLocation().equals(loc)) {
                if (m.getClass().equals(meepleType) && m.getPlayer().equals(owner)) {
                    return m;
                }
            }
        }
        return null;
    }

    public void setPlayers(List<Player> players, int turnPlayer) {
        Player[] plist = players.toArray(new Player[players.size()]);
        this.plist = plist;
        this.turnPlayer = getPlayer(turnPlayer);
    }

    private void createCapabilityInstance(Class<? extends Capability> clazz) {
        if (clazz == null) return;
        try {
            Capability capability = clazz.getConstructor(Game.class).newInstance(this);
            capabilities.add(capability);
        } catch (Exception e) {
            logger.error(e.getMessage(), e); //should never happen
        }
    }

    public List<Capability> getCapabilities() {
        return capabilities;
    }

    @SuppressWarnings("unchecked")
    public <T extends Capability> T getCapability(Class<T> clazz) {
        for (Capability c : capabilities) {
            if (c.getClass().equals(clazz)) return (T) c;
        }
        return null;
    }

    public void start() {
        for (Class<? extends Capability> capability: getCapabilityClasses()) {
            createCapabilityInstance(capability);
        }
        board = new Board(this);
    }

    public boolean isStarted() {
        return !(phase instanceof CreateGamePhase);
    }

    public boolean isOver() {
        return phase instanceof GameOverPhase;
    }


    public Set<FeaturePointer> prepareFollowerLocations() {
        Set<FeaturePointer> followerOptions = prepareFollowerLocations(currentTile, false);
        for (Capability cap: capabilities) {
            cap.extendFollowOptions(followerOptions);
        }
        return followerOptions;
    }

    public Set<FeaturePointer> prepareFollowerLocations(Tile tile, boolean excludeFinished) {
        if (!isDeployAllowed(tile, Follower.class)) return Collections.emptySet();
        Set<FeaturePointer> pointers = new HashSet<>();
        for (Location loc: tile.getUnoccupiedScoreables(excludeFinished)) {
            //exclude finished == false -> just placed tile - it means do not check princess for magic portal
            //TODO very cryptic, refactor
            if (!excludeFinished && hasCapability(PrincessCapability.class) && hasRule(CustomRule.PRINCESS_MUST_REMOVE_KNIGHT)) {
                City princessCity = tile.getCityWithPrincess();
                if (princessCity != null) {
                    continue;
                }
            }
            pointers.add(new FeaturePointer(tile.getPosition(), loc));
        }
        return pointers;
    }

    //scoring helpers

    public void scoreFeature(int points, ScoreContext ctx, Player p) {
        PointCategory pointCategory = ctx.getMasterFeature().getPointCategory();
        p.addPoints(points, pointCategory);
        Follower follower = ctx.getSampleFollower(p);
        boolean isFinalScoring = getPhase() instanceof GameOverPhase;
        ScoreEvent scoreEvent;
        if (fairyCapability != null && follower.at(fairyCapability.getFairyPosition())) {
            p.addPoints(FairyCapability.FAIRY_POINTS_FINISHED_OBJECT, PointCategory.FAIRY);
            scoreEvent = new ScoreEvent(follower.getFeature(), points+FairyCapability.FAIRY_POINTS_FINISHED_OBJECT, pointCategory, follower);
            scoreEvent.setLabel(points+" + "+FairyCapability.FAIRY_POINTS_FINISHED_OBJECT);
        } else {
            scoreEvent = new ScoreEvent(follower.getFeature(), points, pointCategory, follower);
        }
        scoreEvent.setFinal(isFinalScoring);
        post(scoreEvent);
    }

    public void scoreCompletableFeature(CompletableScoreContext ctx) {
        Set<Player> players = ctx.getMajorOwners();
        if (players.isEmpty()) return;
        int points = ctx.getPoints();
        for (Player p : players) {
            scoreFeature(points, ctx, p);
        }
    }

    public int idSequnceNextVal() {
        return ++idSequenceCurrVal;
    }

    // delegation to capabilities

    public void initTile(Tile tile, Element xml) {
        for (Capability cap: capabilities) {
            cap.initTile(tile, xml);
        }
    }

    public void initFeature(Tile tile, Feature feature, Element xml) {
        if (feature instanceof Farm && tile.getId().startsWith("CO.")) {
            //this is not part of Count capability because it is integral behaviour valid also when capability is off
            ((Farm) feature).setAdjoiningCityOfCarcassonne(true);
        }
        for (Capability cap: capabilities) {
            cap.initFeature(tile, feature, xml);
        }
    }

    public void initPlayer(Player player) {
        for (Capability cap: capabilities) {
            cap.initPlayer(player);
        }
    }

    public String getTileGroup(Tile tile) {
        for (Capability cap: capabilities) {
            String group = cap.getTileGroup(tile);
            if (group != null) return group;
        }
        return null;
    }

    public void begin() {
        fairyCapability = getCapability(FairyCapability.class);
        for (Capability cap: capabilities) {
            cap.begin();
        }
    }

    public void prepareActions(List<PlayerAction<?>> actions, Set<FeaturePointer> followerOptions) {
        for (Capability cap: capabilities) {
            cap.prepareActions(actions, followerOptions);
        }
        for (Capability cap: capabilities) { //TODO hack for flier
            cap.postPrepareActions(actions, followerOptions);
        }
    }

//    public void prepareAnyTimeActions(List<PlayerAction> actions) {
//        for (Capability cap: capabilities) {
//            cap.prepareAnyTimeActions(actions);
//        }
//    }

    public boolean isDeployAllowed(Tile tile, Class<? extends Meeple> meepleType) {
        for (Capability cap: capabilities) {
            if (!cap.isDeployAllowed(tile, meepleType)) return false;
        }
        return true;
    }

    public void scoreCompleted(CompletableScoreContext ctx) {
        for (Capability cap: capabilities) {
            cap.scoreCompleted(ctx);
        }
    }

    public void turnPartCleanUp() {
        for (Capability cap: capabilities) {
            cap.turnPartCleanUp();
        }
    }

    public void turnCleanUp() {
        for (Capability cap: capabilities) {
            cap.turnCleanUp();
        }
    }

    public void finalScoring() {
        for (Capability cap: capabilities) {
            cap.finalScoring();
        }
    }

    public boolean isTilePlacementAllowed(Tile tile, Position p) {
        for (Capability cap: capabilities) {
            if (!cap.isTilePlacementAllowed(tile, p)) return false;
        }
        return true;
    }

    public void saveTileToSnapshot(Tile tile, Document doc, Element tileNode) {
        for (Capability cap: capabilities) {
            cap.saveTileToSnapshot(tile, doc, tileNode);
        }
    }

    public void loadTileFromSnapshot(Tile tile, Element tileNode) {
        for (Capability cap: capabilities) {
            cap.loadTileFromSnapshot(tile, tileNode);
        }
    }

    @Override
    public String toString() {
        return "Game in " + phase.getClass().getSimpleName() + " phase.";
    }

    public void setReportingTool(ReportingTool reportingTool) {
        this.reportingTool = reportingTool;
    }

    public ReportingTool getReportingTool() {
        return reportingTool;
    }
}
TOP

Related Classes of com.jcloisterzone.game.Game

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.