freecol/src/net/sf/freecol/client/control/MapEditorController.java

375 lines
13 KiB
Java

/**
* Copyright (C) 2002-2022 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.freecol.client.control;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.xml.stream.XMLStreamException;
import net.sf.freecol.FreeCol;
import net.sf.freecol.client.ClientOptions;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.client.gui.GUI;
import net.sf.freecol.client.gui.panel.MiniMap; // FIXME: should go away
import net.sf.freecol.common.FreeColException;
import net.sf.freecol.common.i18n.Messages;
import net.sf.freecol.common.io.FreeColDirectories;
import net.sf.freecol.common.io.FreeColModFile;
import net.sf.freecol.common.io.FreeColSavegameFile;
import net.sf.freecol.common.model.Area;
import net.sf.freecol.common.model.Game;
import net.sf.freecol.common.model.Map;
import net.sf.freecol.common.model.Nation;
import net.sf.freecol.common.model.Player;
import net.sf.freecol.common.model.Specification;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.common.option.MapGeneratorOptions;
import net.sf.freecol.common.resources.AudioResource;
import net.sf.freecol.common.resources.ResourceManager;
import net.sf.freecol.server.FreeColServer;
import net.sf.freecol.server.generator.MapGenerator;
import net.sf.freecol.server.model.ServerGame;
import net.sf.freecol.server.model.ServerPlayer;
/**
* The map editor controller.
*/
public final class MapEditorController extends FreeColClientHolder {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(MapEditorController.class.getName());
/** Map height in MapGeneratorOptionsDialog. */
private static final int MINI_MAP_THUMBNAIL_FINAL_HEIGHT = 64;
private MapEditorTool currentTool = MapEditorTool.PAINTBRUSH;
/**
* The transform that should be applied to a {@code Tile}
* that is clicked on the map.
*/
private MapTransform currentMapTransform = null;
private Area currentArea = null;
/**
* Creates a new {@code MapEditorController}.
*
* @param freeColClient The {@code FreeColClient} for the game.
*/
public MapEditorController(FreeColClient freeColClient) {
super(freeColClient);
}
/**
* Create a thumbnail for the minimap.
*
* FIXME: Delete all code inside this method and replace it with
* sensible code directly drawing in necessary size,
* without creating a throwaway GUI panel, drawing in wrong
* size and immediately resizing.
* Consider moving to ImageLibrary in due course, but not
* until the MiniMap dependency is gone.
*
* @return The created {@code BufferedImage}.
*/
private BufferedImage createMiniMapThumbNail() {
MiniMap miniMap = new MiniMap(getFreeColClient());
miniMap.setTileSize(MiniMap.MAX_TILE_SIZE);
final Map map = getMap();
int width = map.getWidth() * MiniMap.MAX_TILE_SIZE
+ MiniMap.MAX_TILE_SIZE / 2;
int height = map.getHeight() * MiniMap.MAX_TILE_SIZE / 4;
miniMap.setSize(width, height);
BufferedImage image = new BufferedImage(
width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g1 = image.createGraphics();
miniMap.paintEntireMinimap(g1, MiniMap.MAX_TILE_SIZE, new Dimension(width, height));
g1.dispose();
int scaledWidth = Math.min((int)((64 * width) / (float)height), 128);
BufferedImage scaledImage = new BufferedImage(scaledWidth,
MINI_MAP_THUMBNAIL_FINAL_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = scaledImage.createGraphics();
g2.drawImage(image, 0, 0, scaledWidth, MINI_MAP_THUMBNAIL_FINAL_HEIGHT,
null);
g2.dispose();
return scaledImage;
}
/**
* Require all native nation players to be present in a game.
*
* @param game The {@code Game} to add native nations to.
*/
private void requireNativeNations(Game game) {
final Specification spec = game.getSpecification();
for (Nation n : spec.getIndianNations()) {
Player p = game.getPlayerByNation(n);
if (p == null) {
p = new ServerPlayer(game, false, n);
game.addPlayer(p);
}
}
}
/**
* Enters map editor mode.
*
* FIXME: The TC and difficulty level can now be set at the
* command line, but we should do better.
*/
public void startMapEditor() {
final FreeColClient fcc = getFreeColClient();
final GUI gui = getGUI();
try {
final Specification specification = getDefaultSpecification();
final List<FreeColModFile> mods = getClientOptions().getActiveMods();
final boolean specificationChanges = mods.stream().anyMatch(m -> m.hasSpecification());
specification.loadMods(mods);
fcc.setMapEditor(true);
final FreeColServer freeColServer
= new FreeColServer(false, false, specification, null, 0, null);
fcc.setFreeColServer(freeColServer);
ServerGame serverGame = freeColServer.getGame();
requireNativeNations(serverGame);
fcc.setGame(serverGame);
fcc.setMyPlayer(null);
gui.playSound(null);
gui.closeMainPanel();
gui.closeMenus();
//fcc.changeClientState(true);
//gui.changeView((Tile)null);
gui.startMapEditorGUI();
final AudioResource defaultPlaylist = ResourceManager.getAudioResource("sound.music.playlist.default", true);
if (defaultPlaylist != null) {
getFreeColClient().getSoundController().setDefaultPlaylist(defaultPlaylist.getAllAudio());
}
getFreeColClient().getSoundController().playMusic(null);
if (specificationChanges) {
gui.showInformationPanel("mapEditor.loadedWithMods");
}
} catch (IOException e) {
gui.showErrorPanel(StringTemplate
.template("server.initialize"));
return;
}
}
/**
* Get the default specification from the default rules.
*
* @return A {@code Specification} to use in the map editor.
*/
public Specification getDefaultSpecification() {
return FreeCol.loadSpecification(FreeCol.getRulesFile(),
FreeCol.getAdvantages(), FreeCol.getDifficulty());
}
/**
* Sets the currently chosen {@code MapTransform}.
*
* @param mt The transform that should be applied to a
* {@code Tile} that is clicked on the map.
*/
public void setMapTransform(MapTransform mt) {
currentMapTransform = mt;
getGUI().changeView(mt);
}
/**
* Gets the current {@code MapTransform}.
*
* @return The transform that should be applied to a
* {@code Tile} that is clicked on the map.
*/
public MapTransform getMapTransform() {
return currentMapTransform;
}
public MapEditorTool getCurrentTool() {
return currentTool;
}
public void setCurrentArea(Area currentArea) {
this.currentArea = currentArea;
}
public Area getCurrentArea() {
return currentArea;
}
/**
* Transforms the given {@code Tile} using the
* {@link #getMapTransform()} current {@code MapTransform}.
*
* @param t The {@code Tile} to be modified.
*/
public void transform(Tile t) {
if (currentMapTransform != null) {
currentMapTransform.transform(t);
}
}
/**
* Creates a new map using a {@code MapGenerator}. A panel
* with the {@code MapGeneratorOptions} is first displayed.
*
* @see MapGenerator
* @see MapGeneratorOptions
*/
public void newMap() {
final FreeColServer freeColServer = getFreeColServer();
final ServerGame serverGame = freeColServer.getGame();
getGUI().removeInGameComponents();
getGUI().showMapGeneratorOptionsDialog(true, mgo -> {
if (mgo != null) {
serverGame.setMapGeneratorOptions(mgo);
freeColServer.generateMap(false);
requireNativeNations(serverGame);
getGUI().setFocus(serverGame.getMap().getTile(1,1));
getGUI().updateMenuBar();
getGUI().startMapEditorGUI();
getGUI().refresh();
}
});
}
/**
* Opens a dialog where the user should specify the filename
* and saves the game.
*/
public void saveMapEditorGame() {
File dir = FreeColDirectories.getUserMapsDirectory();
if (dir == null) dir = FreeColDirectories.getSaveDirectory();
File file = getGUI()
.showSaveDialog(dir, FreeColDirectories.MAP_EDITOR_FILE_NAME);
if (file != null) saveMapEditorGame(file);
}
/**
* Saves the game to the given file.
*
* @param file The {@code File}.
*/
public void saveMapEditorGame(final File file) {
final GUI gui = getGUI();
final Game game = getGame();
Map map = game.getMap();
map.resetContiguity();
map.resetHighSeasCount();
map.resetLayers();
gui.showStatusPanel(Messages.message("status.savingGame"));
new Thread(FreeCol.CLIENT_THREAD + "-Saving-Map") {
@Override
public void run() {
try {
BufferedImage thumb = createMiniMapThumbNail();
getFreeColServer().saveMapEditorGame(file, thumb);
SwingUtilities.invokeLater(() -> {
gui.closeStatusPanel();
});
} catch (IOException e) {
SwingUtilities.invokeLater(() -> {
gui.closeStatusPanel();
gui.showErrorPanel(FreeCol.badFile("error.couldNotSave", file),
(e == null) ? null : e.getMessage());
});
}
}
}.start();
}
/**
* Opens a dialog where the user should specify the filename and loads the
* game.
*/
public void loadGame() {
File file = getGUI()
.showLoadSaveFileDialog(FreeColDirectories.getUserMapsDirectory(),
FreeCol.FREECOL_MAP_EXTENSION, "*");
if (file != null) loadGame(file);
}
/**
* Loads a game from the given file.
*
* @param file The {@code File}.
*/
private void loadGame(File file) {
final FreeColClient fcc = getFreeColClient();
final GUI gui = getGUI();
fcc.setMapEditor(true);
gui.showStatusPanel(Messages.message("status.loadingGame"));
final File theFile = file;
new Thread(FreeCol.CLIENT_THREAD + "Loading-Map") {
@Override
public void run() {
final FreeColServer freeColServer = getFreeColServer();
try {
Specification spec = getDefaultSpecification();
Game game = FreeColServer.readGame(new FreeColSavegameFile(theFile),
spec, freeColServer);
game.generateDefaultAreas();
fcc.setGame(game);
requireNativeNations(game);
SwingUtilities.invokeLater(() -> {
gui.closeStatusPanel();
gui.setFocus(game.getMap().getTile(1,1));
gui.enableEditorTransformPanel(true);
gui.enableMapControls(getClientOptions().getBoolean(ClientOptions.DISPLAY_MAP_CONTROLS));
gui.updateMenuBar();
gui.refresh();
});
} catch (FileNotFoundException fnfe) {
gui.showErrorPanel(fnfe,
FreeCol.badFile("error.couldNotFind", theFile));
} catch (IOException | FreeColException ioe) {
gui.showErrorPanel(ioe,
StringTemplate.key("server.initialize"));
} catch (XMLStreamException xse) {
gui.showErrorPanel(xse,
FreeCol.badFile("error.couldNotLoad", theFile));
}
}
}.start();
}
}