Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camo rotation and scaling #4716

Merged
merged 3 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions megamek/i18n/megamek/client/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -570,14 +570,12 @@ BoardView1.Tooltip.HiddenActivating=<FONT COLOR=RED>Hidden unit will activate in
BombPayloadDialog.FighterBombDesc=Select the number and type of bombs you wish to drop.
BombPayloadDialog.SquadronBombDesc=<html>Select the number of salvos for each bomb type you wish to drop.<br> The number in parenthesis represents the number of bombs that will be dropped.</html>

#Camo Choices
CamoChoiceDialog.error_getting_camo=Error getting camo
CamoChoiceDialog.keep_old_camo=Keep\nOld Camo
CamoChoiceDialog.no_camo=No Camo
btnParent.text=Use Parent Camouflage
btnParent.toolTipText=This reverts the selected item's camouflage to their parent item's camouflage.
#CamoChooser
CamoChoiceDialog.btnParent.text=Reset
CamoChoiceDialog.btnParent.toolTipText=Reverts the camouflage to the player's camouflage
CamoChoiceDialog.select_camo_pattern=Select Camo Pattern
CamoChoiceDialog.select_new_camo=Select\nNew Camo
CamoChoiceDialog.rotation=Rotation
CamoChoiceDialog.scale=Scale

#Chat Lounge
ChatLounge.0=Infantry
Expand Down
13 changes: 7 additions & 6 deletions megamek/i18n/megamek/client/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,13 @@ BoardView1.TipJungle=Jungle ({0})
BoardView1.TipIce=Eis({0})
BoardView1.Unload=Abladen
BoardView1.Vibrabomb=Vibrabomb
CamoChoiceDialog.error_getting_camo=Konnte Tarnung nicht laden
CamoChoiceDialog.keep_old_camo=Alte Tarnung\nbeibehalten
CamoChoiceDialog.no_camo=Kaine Tarnung
CamoChoiceDialog.select_camo_pattern=Tarnmuster W�hlen
CamoChoiceDialog.select_new_camo=Neue Tarnung\nausw�hlen
CamoChoiceListener.NoCammo=Kein Tarnung

#CamoChooser
CamoChoiceDialog.select_camo_pattern=Tarnmuster w�hlen
CamoChoiceDialog.btnParent.text=Zur�cksetzen
CamoChoiceDialog.btnParent.toolTipText=Entfernt das individuelle Tarnmuster der ausgew�hlten Einheiten
CamoChoiceDialog.rotation=Drehung
CamoChoiceDialog.scale=Gr��enanpassung

ChatLounge.0=Infanterie
ChatLounge.1=Protomech
Expand Down
12 changes: 5 additions & 7 deletions megamek/i18n/megamek/client/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -628,14 +628,12 @@ BoardView1.Tooltip.HiddenActivating=<FONT COLOR=RED>¡La unidad oculta se activa
BombPayloadDialog.FighterBombDesc=Seleccione el número y tipo de bombas que desea lanzar.
BombPayloadDialog.SquadronBombDesc=<html>Seleccione el número de salvas para cada tipo de bomba que desee lanzar.<br>El número entre paréntesis representa el número de bombas que se lanzarán.</html>

#Camo Choices
CamoChoiceDialog.error_getting_camo=Error al obtener camuflaje
CamoChoiceDialog.keep_old_camo=Conserva\nCamuflaje antiguo
CamoChoiceDialog.no_camo=Sin camuflaje
btnParent.text=Usar Camuflaje Principal
btnParent.toolTipText=Esto revierte el camuflaje del elemento seleccionado al camuflaje del elemento principal.
#CamoChooser
CamoChoiceDialog.btnParent.text=Usar Camuflaje Principal
CamoChoiceDialog.btnParent.toolTipText=Esto revierte el camuflaje del elemento seleccionado al camuflaje del elemento principal
CamoChoiceDialog.select_camo_pattern=Seleccionar patrón de camuflaje
CamoChoiceDialog.select_new_camo=Seleccionar\nNuevo camuflaje
CamoChoiceDialog.rotation=Rotaci�n
CamoChoiceDialog.scale=Tama�o

#Chat Lounge
ChatLounge.0=Infantería
Expand Down
9 changes: 2 additions & 7 deletions megamek/i18n/megamek/client/messages_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,9 @@ BoardView1.ROLLED=ВЫБРОСИЛ
BombPayloadDialog.FighterBombDesc=Выберите количество и тип бомб, которые хотите сбросить.
BombPayloadDialog.SquadronBombDesc=<html>Выберите количество залпов для каждого типа бомб, которые хотите сбросить.<br> Число в кавычках означает количество бомб, которое будет сброшено.</html>

CamoChoiceDialog.error_getting_camo=Ошибка при загрузке раскраски
CamoChoiceDialog.Cancel=Отмена
CamoChoiceDialog.keep_old_camo=Оставить\nстарую раскраску
CamoChoiceDialog.no_camo=Без раскраски
#CamoChooser
CamoChoiceDialog.select_camo_pattern=Выбрать узор раскраски
CamoChoiceDialog.select_new_camo=Выбрать\nНовую раскраску
CamoChoiceDialog.Select=Выбрать
CamoChoiceListener.NoCammo=Не менять раскраску

ChatLounge.0=Пехота
ChatLounge.1=Протомех
ChatLounge.2=Огневая точка
Expand Down
134 changes: 114 additions & 20 deletions megamek/src/megamek/client/ui/dialogs/CamoChooserDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@
*/
package megamek.client.ui.dialogs;

import megamek.client.ui.baseComponents.MMButton;
import megamek.client.ui.Messages;
import megamek.client.ui.WrapLayout;
import megamek.client.ui.panels.CamoChooser;
import megamek.client.ui.panels.EntityImagePanel;
import megamek.client.ui.swing.dialog.DialogButton;
import megamek.client.ui.swing.util.UIUtil;
import megamek.common.Entity;
import megamek.common.annotations.Nullable;
import megamek.common.icons.AbstractIcon;
import megamek.common.icons.Camouflage;

import javax.swing.*;
import java.awt.*;
import java.util.Hashtable;

/**
* This dialog allows players to select the camouflage pattern (or colour) used by their units. It
Expand All @@ -34,6 +39,11 @@
public class CamoChooserDialog extends AbstractIconChooserDialog {
//region Variable Declarations
private boolean useDefault = false;
private JSlider rotationSlider;
private JSlider scaleSlider;
private final Camouflage originalCamo;
private Entity entity;
private EntityImagePanel entityImage;
//endregion Variable Declarations

//region Constructors
Expand All @@ -45,6 +55,7 @@ public CamoChooserDialog(final JFrame frame, final @Nullable AbstractIcon camouf
final boolean canHaveIndividualCamouflage) {
super(frame, "CamoChooserDialog", "CamoChoiceDialog.select_camo_pattern",
new CamoChooser(frame, camouflage, canHaveIndividualCamouflage), true);
originalCamo = (Camouflage) camouflage;
}
//endregion Constructors

Expand All @@ -53,6 +64,10 @@ public boolean isUseDefault() {
return useDefault;
}

public void setDisplayedEntity(Entity entity) {
this.entity = entity;
}

public void setUseDefault(final boolean useDefault) {
this.useDefault = useDefault;
}
Expand All @@ -61,25 +76,80 @@ public void setUseDefault(final boolean useDefault) {
//region Initialization
@Override
protected JPanel createButtonPanel() {
final JPanel panel = new JPanel(new GridLayout(1, 3));
panel.setName("buttonPanel");

panel.add(new MMButton("btnOk", resources, "Ok.text", "Ok.toolTipText",
this::okButtonActionPerformed));
if (getChooser().canHaveIndividualCamouflage()) {
panel.add(new MMButton("btnParent", resources, "btnParent.text",
"btnParent.toolTipText", evt -> {
setUseDefault(true);
okButtonActionPerformed(evt);
}));
}
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));

rotationSlider = new JSlider(-180, 180);
rotationSlider.setMajorTickSpacing(90);
rotationSlider.setMinorTickSpacing(10);
rotationSlider.setPaintTicks(true);
rotationSlider.setSnapToTicks(true);
rotationSlider.addChangeListener(e -> updatePreview());
rotationSlider.setPaintLabels(true);

scaleSlider = new JSlider(3, 15);
scaleSlider.setSnapToTicks(true);
scaleSlider.setPaintTicks(true);
scaleSlider.setMajorTickSpacing(1);
scaleSlider.setPaintLabels(true);
Hashtable<Integer, JComponent> labelTable = new Hashtable<>();
labelTable.put(5, new JLabel("0.5"));
labelTable.put(10, new JLabel("1"));
labelTable.put(15, new JLabel("1.5"));
scaleSlider.setLabelTable(labelTable);
scaleSlider.addChangeListener(e -> updatePreview());

entityImage = new EntityImagePanel(null, null);

JPanel rotationPanel = new JPanel();
rotationPanel.add(UIUtil.scaledHorizontalSpacer(20));
rotationPanel.add(new JLabel(Messages.getString("CamoChoiceDialog.rotation") + ":"));
rotationPanel.add(rotationSlider);

JPanel scalePanel = new JPanel();
scalePanel.add(UIUtil.scaledHorizontalSpacer(30));
scalePanel.add(new JLabel(Messages.getString("CamoChoiceDialog.scale") + ":"));
scalePanel.add(scaleSlider);

JPanel modifierPanel = new JPanel();
modifierPanel.add(entityImage);
modifierPanel.add(rotationPanel);
modifierPanel.add(scalePanel);

JScrollPane modifierScrollPane = new JScrollPane(modifierPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

var okButton = new DialogButton(Messages.getString("Ok.text"));
okButton.addActionListener(this::okButtonActionPerformed);
getRootPane().setDefaultButton(okButton);

var cancelButton = new DialogButton(Messages.getString("Cancel.text"));
cancelButton.addActionListener(this::cancelActionPerformed);

panel.add(new MMButton("btnCancel", resources, "Cancel.text", "Cancel.toolTipText",
this::cancelActionPerformed));
panel.add(new MMButton("btnRefresh", resources, "RefreshDirectory.text",
"RefreshDirectory.toolTipText", evt -> getChooser().refreshDirectory()));
var refreshButton = new DialogButton(Messages.getString("RefreshDirectory.text"));
refreshButton.setToolTipText(Messages.getString("RefreshDirectory.toolTipText"));
refreshButton.addActionListener(evt -> getChooser().refreshDirectory());

return panel;
var parentCamoButton = new DialogButton(Messages.getString("CamoChoiceDialog.btnParent.text"));
parentCamoButton.setToolTipText(Messages.getString("CamoChoiceDialog.btnParent.toolTipText"));
parentCamoButton.addActionListener(evt -> {
setUseDefault(true);
okButtonActionPerformed(evt);
});
parentCamoButton.setEnabled(getChooser().canHaveIndividualCamouflage());

final JPanel buttonPanel = new JPanel(new WrapLayout(FlowLayout.RIGHT));
buttonPanel.setName("buttonPanel");
buttonPanel.add(parentCamoButton);
buttonPanel.add(refreshButton);
buttonPanel.add(UIUtil.scaledHorizontalSpacer(30));
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);

container.add(modifierScrollPane);
container.add(buttonPanel);

return container;
}
//endregion Initialization

Expand All @@ -90,7 +160,13 @@ protected CamoChooser getChooser() {

@Override
public Camouflage getSelectedItem() {
return isUseDefault() ? new Camouflage() : ((Camouflage) super.getSelectedItem()).clone();
Camouflage result = new Camouflage();
if (!isUseDefault() && (super.getSelectedItem() != null)) {
result = ((Camouflage) super.getSelectedItem()).clone();
}
result.setScale(scaleSlider.getValue());
result.setRotationAngle(rotationSlider.getValue());
return result;
}

@Override
Expand All @@ -102,4 +178,22 @@ protected void finalizeInitialization() throws Exception {
private void adaptToGUIScale() {
UIUtil.adjustDialog(this, UIUtil.FONT_SCALE1);
}
}

@Override
public void setVisible(boolean b) {
if ((originalCamo != null) && b) {
rotationSlider.setValue(originalCamo.getRotationAngle());
scaleSlider.setValue(originalCamo.getScale());
}
if (b) {
getChooser().getImageList().addListSelectionListener(e -> updatePreview());
}
super.setVisible(b);
}

private void updatePreview() {
if (getSelectedItem() != null) {
entityImage.updateDisplayedEntity(entity, getSelectedItem());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import megamek.client.ui.renderers.AbstractIconRenderer;
import megamek.common.annotations.Nullable;
import megamek.common.icons.AbstractIcon;
import megamek.common.icons.Camouflage;
import megamek.common.util.fileUtils.AbstractDirectory;
import org.apache.logging.log4j.LogManager;

Expand Down Expand Up @@ -422,7 +423,9 @@ protected void setSelection(final @Nullable AbstractIcon icon) {
// Select the root if the selection could not be found
if (found) {
getTreeCategories().setSelectionPath(new TreePath(currentNode.getPath()));
getImageList().setSelectedValue(icon, true);
// Since camos in the chooser are all free of rotation and scaling, must remove these to find the item
Camouflage cleanedCamo = new Camouflage(icon.getCategory(), icon.getFilename());
getImageList().setSelectedValue(cleanedCamo, true);
} else {
getTreeCategories().setSelectionPath(new TreePath(root.getPath()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class DialogButton extends JButton {
private static final long serialVersionUID = 952919304556828345L;

/** The minimum width this button will have at GUI scale == 1 */
private final static int BUTTON_MIN_WIDTH = 120;
private final static int BUTTON_MIN_WIDTH = 95;

public DialogButton(String text) {
super(text);
Expand Down
4 changes: 4 additions & 0 deletions megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ public void focusLost(FocusEvent e) {
}
Player player = getSelectedClient().getLocalPlayer();
CamoChooserDialog ccd = new CamoChooserDialog(clientgui.getFrame(), player.getCamouflage());
List<Entity> playerEntities = game().getPlayerEntities(player, false);
if (!playerEntities.isEmpty()) {
ccd.setDisplayedEntity(CollectionUtil.anyOneElement(playerEntities));
}

// If the dialog was canceled or nothing selected, do nothing
if (!ccd.showDialog().isConfirmed()) {
Expand Down
4 changes: 3 additions & 1 deletion megamek/src/megamek/client/ui/swing/lobby/LobbyActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ public void individualCamo(Collection<Entity> entities) {
// Display the CamoChooser and await the result
// The dialog is preset to a random entity's settings
Entity entity = CollectionUtil.anyOneElement(entities);
CamoChooserDialog ccd = new CamoChooserDialog(frame(), entity.getOwner().getCamouflage());
boolean hasIndividualCamo = entities.stream().anyMatch(e -> !e.getCamouflage().hasDefaultCategory());
CamoChooserDialog ccd = new CamoChooserDialog(frame(), entity.getCamouflageOrElseOwners(), hasIndividualCamo);
ccd.setDisplayedEntity(entity);
if (ccd.showDialog().isCancelled()) {
return;
}
Expand Down
Loading