mirror of https://github.com/FreeCol/freecol.git
503 lines
18 KiB
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));
|
|
}
|
|
}
|