freecol/src/net/sf/freecol/client/gui/panel/MapEditorTransformPanel.java

503 lines
18 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.gui.panel;
import static net.sf.freecol.common.util.CollectionUtils.alwaysTrue;
import static net.sf.freecol.common.util.CollectionUtils.first;
import static net.sf.freecol.common.util.CollectionUtils.transform;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.client.control.MapEditorController;
import net.sf.freecol.client.control.MapTransform;
import net.sf.freecol.client.gui.ChoiceItem;
import net.sf.freecol.client.gui.DialogHandler;
import net.sf.freecol.client.gui.ImageLibrary;
import net.sf.freecol.client.gui.SwingGUI.PopupPosition;
import net.sf.freecol.client.gui.panel.WrapLayout.HorizontalAlignment;
import net.sf.freecol.client.gui.panel.WrapLayout.HorizontalGap;
import net.sf.freecol.client.gui.panel.mapeditor.ChooseAreaModificationPanel;
import net.sf.freecol.common.i18n.Messages;
import net.sf.freecol.common.model.Area;
import net.sf.freecol.common.model.Direction;
import net.sf.freecol.common.model.IndianNationType;
import net.sf.freecol.common.model.LostCityRumour;
import net.sf.freecol.common.model.ModelMessage;
import net.sf.freecol.common.model.Nation;
import net.sf.freecol.common.model.Player;
import net.sf.freecol.common.model.Resource;
import net.sf.freecol.common.model.ResourceType;
import net.sf.freecol.common.model.SettlementType;
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.model.TileImprovement;
import net.sf.freecol.common.model.TileImprovementType;
import net.sf.freecol.common.model.TileType;
import net.sf.freecol.common.model.UnitType;
import net.sf.freecol.server.model.ServerIndianSettlement;
/**
* A panel for choosing the current {@code MapTransform}.
*
* This panel is only used when running in
* {@link net.sf.freecol.client.FreeColClient#isMapEditor() map editor mode}.
*
* @see MapEditorController#getMapTransform()
* @see MapTransform
*/
public final class MapEditorTransformPanel extends FreeColPanel {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(MapEditorTransformPanel.class.getName());
private static final class RiverTransform extends MapTransform {
private final int magnitude;
private RiverTransform(int magnitude) {
this.magnitude = magnitude;
}
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile tile) {
final TileImprovementType riverType = tile.getSpecification()
.getTileImprovementType("model.improvement.river");
if (!riverType.isTileTypeAllowed(tile.getType())) return;
if (tile.hasRiver()) {
tile.removeRiver();
} else {
StringBuilder sb = new StringBuilder(64);
for (Direction direction : Direction.longSides) {
Tile t = tile.getNeighbourOrNull(direction);
TileImprovement otherRiver = (t == null) ? null
: t.getRiver();
if (t == null || (t.isLand() && otherRiver == null)) {
sb.append('0');
} else {
sb.append(magnitude);
}
}
tile.addRiver(magnitude, sb.toString());
}
}
}
private static final class RiverStyleTransform extends MapTransform {
public static final int CHANGE_CONNECTIONS = 0;
public static final int SET_STYLE = 1;
private String style;
private int type;
private RiverStyleTransform(int type) {
this.style = null;
this.type = type;
}
public void setStyle(String style) {
this.style = style;
}
public int getType() {
return type;
}
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile tile) {
TileImprovement river = tile.getRiver();
if (river != null) {
if (type == CHANGE_CONNECTIONS)
river.updateRiverConnections(style);
else
river.setRiverStyle(style);
}
}
}
/**
* Adds, removes or cycles through the available resources for
* this Tile. Cycles through the ResourceTypeList and picks the
* next valid, or removes if end of list.
*/
private class ResourceTransform extends MapTransform {
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile t) {
// Check if there is a resource already
Resource resource = null;
if (t.getTileItemContainer() != null) {
resource = t.getTileItemContainer().getResource();
}
if (resource != null) {
t.getTileItemContainer().removeTileItem(resource);
} else {
List<ResourceType> resList = t.getType().getResourceTypeValues();
switch (resList.size()) {
case 0:
return;
case 1:
ResourceType resourceType = first(resList);
// FIXME: create GUI for setting the quantity
t.addResource(new Resource(t.getGame(), t, resourceType,
resourceType.getMaxValue()));
return;
default:
ResourceType choice = getResourceChoice(resList);
if (choice != null) {
t.addResource(new Resource(t.getGame(), t, choice,
choice.getMaxValue()));
}
}
}
}
}
private static class LostCityRumourTransform extends MapTransform {
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile t) {
if (t.isLand()) {
LostCityRumour rumour = t.getLostCityRumour();
if (rumour == null) {
t.addLostCityRumour(new LostCityRumour(t.getGame(), t));
} else {
t.removeLostCityRumour();
}
}
}
}
private class SettlementTransform extends MapTransform {
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile t) {
if (!t.isLand() || t.hasSettlement()) return;
UnitType skill = first(((IndianNationType)getNativeNation().getType())
.getSkills()).getObject();
Player nativePlayer = getGame().getPlayerByNation(getNativeNation());
if (nativePlayer == null) return;
String name = nativePlayer.getSettlementName(null);
ServerIndianSettlement sis
= new ServerIndianSettlement(t.getGame(),
nativePlayer, name, t, false, skill, null);
nativePlayer.addSettlement(sis);
sis.placeSettlement(true);
sis.addUnits(null);
logger.info("Add settlement " + sis.getName() + " to tile " + t);
}
}
public static final class TileTypeTransform extends MapTransform {
private final TileType tileType;
private TileTypeTransform(TileType tileType) {
this.tileType = tileType;
}
public TileType getTileType() {
return tileType;
}
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile t) {
t.changeType(tileType);
t.removeLostCityRumour();
}
}
public static final class AssignAreaTransform extends MapTransform {
private Area area;
private AssignAreaTransform(Area area) {
this.area = area;
}
public Area getArea() {
return area;
}
public void setArea(Area area) {
this.area = area;
}
/**
* {@inheritDoc}
*/
@Override
public void transform(Tile t) {
if (area == null) {
return;
}
if (area.containsTile(t)) {
area.removeTile(t);
} else {
area.addTile(t);
}
}
}
/** A native nation to use for native settlement type and skill. */
private static Nation nativeNation;
private final JPanel listPanel;
private JToggleButton settlementButton;
private final ButtonGroup group;
private final ChooseAreaModificationPanel chooseAreaModificationPanel;
/**
* Creates a panel to choose a map transform.
*
* @param freeColClient The {@code FreeColClient} for the game.
*/
public MapEditorTransformPanel(FreeColClient freeColClient) {
super(freeColClient, null, new BorderLayout());
final MapEditorController ctlr = getFreeColClient().getMapEditorController();
final DialogHandler<Area> areaModificationHandler = (area) -> {
ctlr.setCurrentArea(area);
final MapTransform currentMapTransform = ctlr.getMapTransform();
if (currentMapTransform instanceof AssignAreaTransform) {
final AssignAreaTransform assignAreaTransform = (AssignAreaTransform) currentMapTransform;
assignAreaTransform.setArea(area);
}
};
this.chooseAreaModificationPanel = new ChooseAreaModificationPanel(freeColClient, areaModificationHandler );
final Dimension terrainSize = ImageLibrary.scaleDimension(getImageLibrary().scale(ImageLibrary.TILE_OVERLAY_SIZE), ImageLibrary.SMALLER_SCALE);
listPanel = new JPanel(new WrapLayout()
.withForceComponentSize(terrainSize)
.withHorizontalAlignment(HorizontalAlignment.LEFT)
.withHorizontalGap(HorizontalGap.AUTO));
group = new ButtonGroup();
//Add an invisible, move button to de-select all others
group.add(new JToggleButton());
buildList();
JScrollPane sl = new JScrollPane(listPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sl.getViewport().setOpaque(false);
listPanel.setSize(new Dimension(terrainSize.width * 3, 0));
add(sl, BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder());
revalidate();
repaint();
}
@Override
public String getFrameTitle() {
return Messages.message("mapEditorTransformPanel.title");
}
@Override
public PopupPosition getFramePopupPosition() {
return PopupPosition.RIGHT;
}
/**
* Get the last used native nation.
*
* @return A suitable {@code Nation}.
*/
private Nation getNativeNation() {
if (nativeNation == null) { // Initialize if not yet set
nativeNation = first(getSpecification().getIndianNations());
}
return nativeNation;
}
/**
* Set the native nation.
*
* @param newNativeNation The new native {@code Nation}.
*/
public static void setNativeNation(Nation newNativeNation) {
nativeNation = newNativeNation;
}
/**
* Builds the buttons for all the terrains.
*/
private void buildList() {
final Specification spec = getSpecification();
final Dimension terrainSize = ImageLibrary
.scaleDimension(getImageLibrary().scale(ImageLibrary.TILE_OVERLAY_SIZE),
ImageLibrary.SMALLER_SCALE);
final Dimension riverSize = ImageLibrary
.scaleDimension(getImageLibrary().scale(ImageLibrary.TILE_SIZE),
ImageLibrary.SMALLER_SCALE);
for (TileType type : spec.getTileTypeList()) {
BufferedImage tileImage = getImageLibrary()
.getTileImageWithOverlayAndForest(type, terrainSize);
listPanel.add(buildButton(tileImage, Messages.getName(type),
new TileTypeTransform(type)));
}
listPanel.add(buildButton(getImageLibrary().getRiverImage("0101", riverSize),
Messages.message("mapEditorTransformPanel.minorRiver"),
new RiverTransform(TileImprovement.SMALL_RIVER)));
listPanel.add(buildButton(getImageLibrary().getRiverImage("0202", riverSize),
Messages.message("mapEditorTransformPanel.majorRiver"),
new RiverTransform(TileImprovement.LARGE_RIVER)));
listPanel.add(buildButton(getImageLibrary().getRiverImage("2022", riverSize),
Messages.message("mapEditorTransformPanel.changeRiverConnections"),
new RiverStyleTransform(RiverStyleTransform.CHANGE_CONNECTIONS)));
listPanel.add(buildButton(getImageLibrary().getRiverImage("1022", riverSize),
Messages.message("mapEditorTransformPanel.setRiverStyle"),
new RiverStyleTransform(RiverStyleTransform.SET_STYLE)));
final ResourceType rt = first(spec.getResourceTypeList());
listPanel.add(buildButton(getImageLibrary().getResourceTypeImage(rt,
riverSize, false),
Messages.message("mapEditorTransformPanel.resource"),
new ResourceTransform()));
listPanel.add(buildButton(getImageLibrary().getLCRImage(riverSize),
Messages.getName(ModelMessage.MessageType.LOST_CITY_RUMOUR),
new LostCityRumourTransform()));
SettlementType settlementType = getNativeNation().getType()
.getCapitalType();
listPanel.add(buildButton(getImageLibrary().getSettlementTypeImage(settlementType, riverSize),
Messages.message("settlement"),
new SettlementTransform()));
listPanel.add(buildButton(getImageLibrary().getScaledImage("image.icon.mapEditor.selectArea"),
Messages.message("mapEditorTransformPanel.selectArea"),
new AssignAreaTransform(null)));
}
/**
* Builds the button for the given terrain.
*
* @param image an {@code Image} value
* @param text a {@code String} value
* @param mt a {@code MapTransform} value
* @return A suitable button.
*/
private JToggleButton buildButton(Image image, String text,
final MapTransform mt) {
final MapEditorController ctlr
= getFreeColClient().getMapEditorController();
JPanel descriptionPanel = new JPanel(new BorderLayout());
descriptionPanel.add(new JLabel(new ImageIcon(image)),
BorderLayout.CENTER);
descriptionPanel.add(new JLabel(text, JLabel.CENTER),
BorderLayout.PAGE_END);
descriptionPanel.setBackground(Color.RED);
mt.setDescriptionPanel(descriptionPanel);
ImageIcon icon = new ImageIcon(image);
final JToggleButton button = new JToggleButton(icon);
button.setToolTipText(text);
button.setOpaque(false);
group.add(button);
button.addActionListener((ActionEvent ae) -> {
MapTransform newMapTransform = null;
if (ctlr.getMapTransform() != mt) {
if (mt instanceof RiverStyleTransform) {
RiverStyleTransform rst = (RiverStyleTransform)mt;
boolean all = rst.getType() != RiverStyleTransform.CHANGE_CONNECTIONS;
String style = getGUI()
.showRiverStyleDialog(ImageLibrary
.getRiverStyleKeys(all));
if (style != null) rst.setStyle(style);
}
if (mt instanceof AssignAreaTransform) {
getGUI().showFreeColPanel(chooseAreaModificationPanel, true, null, true);
newMapTransform = null;
ctlr.setDisplayAreas(true);
}
newMapTransform = mt;
}
ctlr.setMapTransform(newMapTransform);
if (newMapTransform == null && mt != null) {
// Select the invisible button, de-selecting all others
group.setSelected(group.getElements().nextElement()
.getModel(), true);
}
});
button.setBorder(null);
return button;
}
/**
* Ask the user for a resource choice from a list.
*
* Ripped out of ResourceTransform to dodge the name clash with
* CollectionUtils.transform which we want to use here.
*
* @param resources A list of {@code ResourceType}s to choose from.
* @return The chosen {@code ResourceType}.
*/
private ResourceType getResourceChoice(List<ResourceType> resources) {
final Function<ResourceType, ChoiceItem<ResourceType>> mapper
= rt -> new ChoiceItem<ResourceType>(Messages.getName(rt), rt);
StringTemplate tmpl = StringTemplate
.template("mapEditorTransformPanel.chooseResource");
return getGUI().getChoice(tmpl, "cancel",
transform(resources, alwaysTrue(), mapper));
}
}