/* CVS: $Id: WorldController.java,v 1.17 2001/03/19 10:29:31 gvijf Exp $ */ package evolution; import java.io.*; import java.util.*; import evolution.lands.*; import evolution.actions.*; import evolution.constructions.*; import evolution.events.*; /** * A class of world controllers. * This is a facade. */ public class WorldController implements evolution.events.Observer { /** * Create a new worldcontroller (and a new world). * The world will be created in the pause mode, so one needs * to call startGame() to start playing. * @param width The width of the game board * @param height The height of the game board * @param configFile The main configuration file on which this world * should be based. From this configuration file the path is stripped * and used as the resource path to load other config files and images. */ public WorldController(String configFile, int width, int height) throws FileNotFoundException, IOException { world = new World(this, configFile, width, height); initState(); EventManager.getInst().subscribe(this, HumanDiedEvt.class); } /** * Called when an event happens this observer is interested in. */ public void update(Event evt) { if(evt instanceof HumanDiedEvt) { SystemMessage.message("One died! You have " + getHumanSelection().size() + " humans left in your selection"); } } // actions which control the game /** * Start the game. * Does nothing if the game is already started. */ public void startGame() { continueGame(); } /** * Resume the game. */ public void continueGame() { Evolution.getInst().resume(); } /** * Pause the game. */ public void pauseGame() { Evolution.getInst().pause(); } /** * Is the game running? */ public boolean isRunning() { return Evolution.getInst().isRunning(); } // actions which manipulate the humans on the board /** * Select an action to be performed. * @exception NoHumanSelectionException If there are no humans selected * to perform the given action when a square would be selected. */ public void selectActionType(String actionName) { if(!ActionKnowledgeCatalog.getInst().actionExists(actionName)) throw new RuntimeException("Unknown action '" + actionName + "' selected"); if(getHumanSelection().empty()) SystemMessage.message("You must select some humans to perform " + actionName + ",\nbut I think you know what you are doing, right?"); setActionName(actionName); setConstructionName(null); SystemMessage.message("action = " + actionName); } /** * Select a construction to be constructed. * @exception NoHumanSelectionException If there are no humans selected * to perform the action when a square would be selected. */ public void selectConstructionType(String constructionName) { selectActionType("Constructing"); if(!ConstructionKnowledgeCatalog.getInst().constructionExists(constructionName)) throw new RuntimeException("Unknown construction '" + constructionName + "' selected"); setConstructionName(constructionName); SystemMessage.message("construction = " + constructionName); } /** * Specify the shape the construction should have. * e.g. "1x1:1", "2x2:1101", "3x2:111111101" */ public void selectConstructionShape(String shape) { setConstructionShape(shape); } /** * Toggle creating a human. */ public void createHuman() { setCreateHuman(!isCreateHuman()); } /** * Select a square of land. *
     * If there is no human on the square
     *   if we were to create a human
     *      => the human is created
     *   elseif there are humans selected
     *      => one or more humans will be popped of the selection and
     *         start performing the action.
     *   else
     *      => the square is selected
     * else
     *   if the human is selected
     *      => let it perform the selected action
     *   else
     *      => the human is added to the selection and the square is selected
     * 
     * Precondition: the x, y must denote a valid square.
     * @exception IllegalPlacementException The popped human can not stand
     * on the given type of land. The human will stay were ever he was and
     * stays selected.
     */
    public void selectSquareOfLand(int x, int y) throws IllegalPlacementException {
        setSelectedSquare(world.getGameBoard().getSquare(x, y));
        //System.err.println("Selected + " + getSelectedSquare() + ", human = " +
        //    getSelectedSquare().getHuman() +  ", construction = " +
        //    getSelectedSquare().getConstruction());
        if(getSelectedSquare().getHuman() == null) {
            if(isCreateHuman()) {
                try {
                    world.createHuman(getSelectedSquare());
                    getSelectedSquare().getHuman().setAction(getActionName());
                    getHumanSelection().select(getSelectedSquare().getHuman());
                } catch(CreationPowerInsufficientException e) {
                    SystemMessage.message(e.getMessage());
                }
            } else {
                if(!getHumanSelection().empty()) {
                    startAction();
                } else {
                    // nothing to do
                }
            }
        } else {
            //System.err.println("a human");
            if(getHumanSelection().isSelected(getSelectedSquare().getHuman())) {
                //System.err.println("is selected, let him perform " + getActionName());
                startAction();
                //System.err.println("ok");
            } else {
                //System.err.println("not selected, select");
                getHumanSelection().select(getSelectedSquare().getHuman());
                //System.err.println("ok");
                SystemMessage.message("You have " + getHumanSelection().size() + " humans selected");
            }
        }
        setCreateHuman(false);
    }
    /**
     * Start the selected action.
     */
    protected void startAction() throws IllegalPlacementException {
        if(getConstructionName() != null) {
            if(getConstructionShape() == null) {
                SystemMessage.message("Please select the shape for your " + getConstructionName());
                return;
            }
            //System.err.println("start building a " + getConstructionName());
            //SystemMessage.message("Hey jules, I don't know how to build a construction yet! But I'll try...");
            int n = Math.min(ConstructionKnowledgeCatalog.getInst().
                countSquares(getConstructionShape()), getHumanSelection().size());
            SystemMessage.message("Ok, using " + n + " humans to construct a faboulous " + getConstructionName());
            List humans = new ArrayList();
            for(int i = 0; i < n; i++) humans.add(getHumanSelection().pop());
            try {
                ConstructionKnowledgeCatalog.getInst().build(
                    getConstructionName(), getConstructionShape(),
                    humans, getSelectedSquare());
            } catch(IllegalPlacementException e) {
                for(int i = 0; i < n; i++)
                    getHumanSelection().select((Human) humans.get(n-i-1));
                throw e;
            }
            setConstructionName(null);
            setConstructionShape(null);
            //resetActionName();
        } else {
            Human human = getHumanSelection().pop();
            SquareOfLand oldSquare = human.getSquareOfLand();
            try {
                if(getSelectedSquare() != oldSquare) {
                    getSelectedSquare().place(human);
                }
                SystemMessage.message("I command you, go " + getActionName());
                human.setAction(getActionName());
            } catch(IllegalPlacementException e) {
                // add the human back to the selection
                getHumanSelection().select(human);
                throw e;
            }
        }
        if(getHumanSelection().empty()) {
            SystemMessage.message("If I were you, I would search some lazy humans to command...");
        } else {
            SystemMessage.message("Still " + getHumanSelection().size() + " humans to command!");
        }
    }
    /**
     * Clear the selected humans.
     */
    public void clearSelection() {
        getHumanSelection().clear();
    }
    /** 
     * Modify the visionrange of this worldcontroller.
     */
    public void modifyVisionRange(int value) {
	while (!humanSelection.empty()) {
	    Human human = humanSelection.pop();
	    try {
		human.setVisionRange(human.getVisionRange() + value);
	    }
	    catch (InvalidVisionRangeException exc) {
		SystemMessage.message(exc.getMessage());
	    }
	}
    }
    // information requests about the current selected square
    /**
     * Get an infolist with all the world resources.
     */
    public InfoList getResourcesInfo() {
        return getWorld().getResourcesInfo();
    }
    /**
     * Get an infolist with info about the selected square.
     */
    public InfoList getSquareInfo() {
        if(getSelectedSquare() == null) return new InfoList();
        return getSelectedSquare().getInfo();
    }
    /**
     * Get an infolist with info about the selected human.
     */
    public InfoList getHumanInfo() {
        if((getSelectedSquare() == null) || (getSelectedSquare().getHuman() == null))
            return new InfoList();
        return getSelectedSquare().getHuman().getInfo();
    }
    /**
     * Get an infolist with info about the selected construction.
     */
    public InfoList getConstructionInfo() {
        if((getSelectedSquare() == null) || (getSelectedSquare().getConstruction() == null))
            return new InfoList();
        return getSelectedSquare().getConstruction().getInfo();
    }
    /**
     * The shapes the given construction can be build in.
     * Returns a List of Strings according to the shape spec.
     * e.g. {"1x1:1", "2x2:1101", "3x2:111111101"}
     */
    public List getConstructionShapes(String constructionName) {
        return ConstructionKnowledgeCatalog.getInst().getConstructionShapes(constructionName);
    }
    // information about the objects in this game
    /**
     * All available actions.
     */
    public InfoList getActionsInfo() {
        return ActionKnowledgeCatalog.getInst().getActionsInfo();
    }
    /**
     * All available constructions.
     */
    public InfoList getConstructionsInfo() {
        // the action to build a construction is hardwired as 'Constructing'
        if(!ActionKnowledgeCatalog.getInst().actionExists("Constructing")) {
            return new InfoList();
        }
        return ConstructionKnowledgeCatalog.getInst().getConstructionsInfo();
    }
    /**
     * The resource path.
     */
    public String getResourcePath() {
        return KnowledgeCatalog.getResourcePath();
    }
    /**
     * Get the width of this world.
     */
    public int getWidth() {
        return getWorld().getWidth();
    }
    /**
     * Get the height of this world.
     */
    public int getHeight() {
        return getWorld().getHeight();
    }
    /**
     * Let all the squares fire a changed event.
     */
    public void refresh() {
        getWorld().refresh();
    }
    /**
     * Is the given human selected?
     */
    public boolean isSelected(Human human) {
        return getHumanSelection().isSelected(human);
    }
    /**
     * Is the given human actively selected?
     */
    public boolean isActiveSelected(Human human) {
        return getHumanSelection().isActiveSelected(human);
    }
    // the things we use to do this
    /**
     * Return to the initial state of this worldcontroller.
     */
    protected void initState() {
        humanSelection = new HumanSelection();
        selectedSquare = world.getGameBoard().getSquare(0, 0);
        resetActionName();
        setConstructionName(null);
        setCreateHuman(false);
    }
    /**
     * Return the human selection of this human.
     */
    protected HumanSelection getHumanSelection() {
        return humanSelection;
    }
    /**
     * Return the square of land that is currently selected.
     */
    protected SquareOfLand getSelectedSquare() {
        return selectedSquare;
    }
    /**
     * Set the selected square of land to the given square of land.
     */
    protected void setSelectedSquare(SquareOfLand square) {
        selectedSquare = square;
    }
    /**
     * Return the name of the currently selected action.
     */ 
    protected String getActionName() {
        return actionName;
    }
    /**
     * Set the selected action to the given action.
     */
    protected void setActionName(String actionName) {
        this.actionName = actionName;
    }
    /**
     * Set the selected action to the default action.
     */
    protected void resetActionName() {
        String actionName = ActionKnowledgeCatalog.getInst().getDefaultActionType();
        setActionName(actionName);
        SystemMessage.message("action = " + actionName);
    }
    /**
     * Return the currently selected construction name.
     */
    protected String getConstructionName() {
        return constructionName;
    }
    /**
     * Set the selected construction name to the given construction name.
     */
    protected void setConstructionName(String constructionName) {
        this.constructionName = constructionName;
    }
    /**
     * Return the selected construction shape.
     */
    protected String getConstructionShape() {
        return constructionShape;
    }
    /**
     * Set the selected construction shape to the given construction shape.
     */
    protected void setConstructionShape(String constructionShape) {
        this.constructionShape = constructionShape;
    }
    /**
     * Return whether we are going to create a human with the next 
     * square selection 
     */
    protected boolean isCreateHuman() {
        return createHuman;
    }
    /**
     * Set the boolean value that determines whether we are going 
     * to create a human with the next square selection
     */
    protected void setCreateHuman(boolean b) {
        createHuman = b;
    }
    /**
     * return the world (= singleton !) of this worldcontroller.
     */ 
    protected World getWorld() {
        return world;
    }
    /**
     * The human selection.
     */
    private HumanSelection humanSelection;
    /**
     * The selected square of land.
     */
    private SquareOfLand selectedSquare;
    /**
     * The action name which was selected.
     */
    private String actionName;
    /**
     * The construction name which was selected.
     */
    private String constructionName = null;
    /**
     * The construction shape which was selected.
     */
    private String constructionShape = null;
    /**
     * Are we gonna create a human with the next square selection?
     */
    private boolean createHuman;
    /**
     * Just for easy referencing, but remember: world is a singleton.
     */
    private World world;
}