/*
* $Id$
*
* Copyright (c) 2000-2012 by Rodney Kinney, Brent Easton
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.build.module;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.properties.MutablePropertiesContainer;
import VASSAL.build.module.properties.MutableProperty;
import VASSAL.build.module.properties.MutableProperty.Impl;
import VASSAL.command.Command;
import VASSAL.command.NullCommand;
import VASSAL.configure.AutoConfigurer;
import VASSAL.configure.Configurer;
import VASSAL.configure.ConfigurerFactory;
import VASSAL.configure.ConfigurerWindow;
import VASSAL.configure.IconConfigurer;
import VASSAL.configure.PlayerIdFormattedStringConfigurer;
import VASSAL.configure.VisibilityCondition;
import VASSAL.i18n.Resources;
import VASSAL.i18n.TranslatableConfigurerFactory;
import VASSAL.tools.FormattedString;
import VASSAL.tools.KeyStrokeListener;
import VASSAL.tools.LaunchButton;
import VASSAL.tools.NamedKeyStroke;
/**
* This component places a button into the controls window toolbar.
* Pressing the button generates random numbers and displays the
* result in the Chatter */
public class DiceButton extends AbstractConfigurable {
protected java.util.Random ran;
protected int nSides = 6, nDice = 2, plus = 0, addToTotal=0;
protected boolean reportTotal = false;
protected boolean promptAlways = false;
protected boolean sortDice = false;
protected FormattedString reportFormat = new FormattedString("** $" + REPORT_NAME + "$ = $" + RESULT + "$ *** <$" + GlobalOptions.PLAYER_NAME + "$>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
protected LaunchButton launch;
protected String tooltip = ""; //$NON-NLS-1$
protected MutableProperty.Impl property = new Impl("",this);
public static final String DEPRECATED_NAME = "label"; //$NON-NLS-1$
public static final String BUTTON_TEXT = "text"; //$NON-NLS-1$
public static final String TOOLTIP = "tooltip"; //$NON-NLS-1$
public static final String NAME = "name"; //$NON-NLS-1$
public static final String ICON = "icon"; //$NON-NLS-1$
public static final String N_DICE = "nDice"; //$NON-NLS-1$
public static final String N_SIDES = "nSides"; //$NON-NLS-1$
public static final String PLUS = "plus"; //$NON-NLS-1$
public static final String ADD_TO_TOTAL = "addToTotal"; //$NON-NLS-1$
public static final String HOTKEY = "hotkey"; //$NON-NLS-1$
public static final String REPORT_TOTAL = "reportTotal"; //$NON-NLS-1$
public static final String PROMPT_ALWAYS = "prompt"; //$NON-NLS-1$
public static final String REPORT_FORMAT = "reportFormat"; //$NON-NLS-1$
public static final String SORT_DICE_RESULTS = "sortDice";
/** Variable name for reporting format */
public static final String RESULT = "result"; //$NON-NLS-1$
public static final String REPORT_NAME = "name"; //$NON-NLS-1$
public DiceButton() {
initLaunchButton();
}
protected void initLaunchButton() {
final ActionListener rollAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (promptAlways) {
final DiceButton delegate = new DiceButton() {
protected void initLaunchButton() {
launch = new LaunchButton(null,BUTTON_TEXT,HOTKEY,null);
}
};
final List<String> keepAttributes =
Arrays.asList(new String[]{N_DICE, N_SIDES, PLUS, ADD_TO_TOTAL});
for (String key : keepAttributes) {
delegate.setAttribute(key, getAttributeValueString(key));
}
final AutoConfigurer ac = new AutoConfigurer(delegate);
final ConfigurerWindow w = new ConfigurerWindow(ac, true);
for (String key : getAttributeNames()) {
if (!keepAttributes.contains(key)) {
final Component controls = ac.getConfigurer(key).getControls();
controls.getParent().remove(controls);
}
}
w.pack();
w.setLocationRelativeTo(launch.getTopLevelAncestor());
w.setVisible(true);
for (String key : keepAttributes) {
setAttribute(key, delegate.getAttributeValueString(key));
}
if (! w.isCancelled()) {
DR();
}
}
else {
DR();
}
}
};
launch = new LaunchButton(null, TOOLTIP, BUTTON_TEXT, HOTKEY, ICON, rollAction);
setAttribute(NAME, "2d6");
setAttribute(BUTTON_TEXT, "2d6");
launch.setAttribute(TOOLTIP, "2d6");
}
public static String getConfigureTypeName() {
return Resources.getString("Editor.DiceButton.component_type"); //$NON-NLS-1$
}
/**
* The text reported before the results of the roll
* @deprecated
*/
@Deprecated protected String getReportPrefix() {
return " *** " + getConfigureName() + " = "; //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* The text reported after the results of the roll;
* @deprecated
*/
@Deprecated protected String getReportSuffix() {
return " *** <" //$NON-NLS-1$
+ GameModule.getGameModule().getChatter().getHandle() + ">"; //$NON-NLS-1$
}
/**
* Forwards the result of the roll to the {@link Chatter#send}
* method of the {@link Chatter} of the {@link GameModule}. Format is
* prefix+[comma-separated roll list]+suffix */
protected void DR() {
StringBuilder val = new StringBuilder();
int total = addToTotal;
int[] dice = null; // stays null if no sorting
if (!reportTotal && nDice > 1 && sortDice) {
dice = new int[nDice];
}
for (int i = 0; i < nDice; ++i) {
final int roll = ran.nextInt(nSides) + 1 + plus;
if (dice != null) {
dice[i] = roll;
}
else if (reportTotal) {
total += roll;
}
else { // do not sort
val.append(roll);
if (i < nDice - 1)
val.append(","); //$NON-NLS-1$
}
}
if (reportTotal) {
val.append(total);
}
else if (dice != null) {
Arrays.sort(dice);
for (int i = 0; i < nDice; ++i) {
val.append(dice[i]);
if (i < nDice - 1) {
val.append(","); //$NON-NLS-1$
}
}
}
String report = formatResult(val.toString());
Command c = report.length() == 0 ? new NullCommand() : new Chatter.DisplayText(GameModule.getGameModule().getChatter(),report);
c.execute();
c.append(property.setPropertyValue(val.toString()));
GameModule.getGameModule().sendAndLog(c);
}
/**
* Use the configured FormattedString to format the result of a roll
* @param result
* @return
*/
protected String formatResult(String result) {
reportFormat.setProperty(REPORT_NAME, getLocalizedConfigureName());
reportFormat.setProperty(RESULT, result);
reportFormat.setProperty(N_DICE, Integer.toString(nDice));
reportFormat.setProperty(N_SIDES, Integer.toString(nSides));
reportFormat.setProperty(PLUS, Integer.toString(plus));
reportFormat.setProperty(ADD_TO_TOTAL, Integer.toString(addToTotal));
String text = reportFormat.getLocalizedText();
String report = text;
if (text.length() > 0) {
report = text.startsWith("*") ? "*" + text : "* " + text; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
return report;
}
public String[] getAttributeNames() {
return new String[] {
NAME,
BUTTON_TEXT,
TOOLTIP,
ICON,
N_DICE,
N_SIDES,
PLUS,
ADD_TO_TOTAL,
REPORT_TOTAL,
HOTKEY,
PROMPT_ALWAYS,
REPORT_FORMAT,
SORT_DICE_RESULTS,
};
}
public String[] getAttributeDescriptions() {
return new String[]{
Resources.getString(Resources.NAME_LABEL),
Resources.getString(Resources.BUTTON_TEXT),
Resources.getString(Resources.TOOLTIP_TEXT),
Resources.getString(Resources.BUTTON_ICON),
Resources.getString("Dice.number_of_dice"), //$NON-NLS-1$
Resources.getString("Dice.number_of_sides"), //$NON-NLS-1$
Resources.getString("Dice.add_to_each_side"), //$NON-NLS-1$
Resources.getString("Dice.add_to_total"), //$NON-NLS-1$
Resources.getString("Editor.DiceButton.report_total"), //$NON-NLS-1$
Resources.getString(Resources.HOTKEY_LABEL),
Resources.getString("Editor.DiceButton.prompt_value"), //$NON-NLS-1$
Resources.getString("Editor.report_format"), //$NON-NLS-1$
Resources.getString("Editor.DiceButton.sort_results") //$NON-NLS-1$
};
}
public static class IconConfig implements ConfigurerFactory {
public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
return new IconConfigurer(key, name, "/images/die.gif"); //$NON-NLS-1$
}
}
public static class ReportFormatConfig implements TranslatableConfigurerFactory {
public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
return new PlayerIdFormattedStringConfigurer(key, name, new String[]{REPORT_NAME, RESULT, N_DICE, N_SIDES, PLUS, ADD_TO_TOTAL});
}
}
public Class<?>[] getAttributeTypes() {
return new Class<?>[]{
String.class,
String.class,
String.class,
IconConfig.class,
Integer.class,
Integer.class,
Integer.class,
Integer.class,
Boolean.class,
NamedKeyStroke.class,
Boolean.class,
ReportFormatConfig.class,
Boolean.class,
};
}
private final VisibilityCondition cond = new VisibilityCondition() {
public boolean shouldBeVisible() {
return !promptAlways;
}
};
private final VisibilityCondition canSort = new VisibilityCondition() {
public boolean shouldBeVisible() {
return !reportTotal;
}
};
public VisibilityCondition getAttributeVisibility(String name) {
if (N_DICE.equals(name)
|| N_SIDES.equals(name)
|| PLUS.equals(name)
|| ADD_TO_TOTAL.equals(name)) {
return cond;
}
else if (SORT_DICE_RESULTS.equals(name)) {
return canSort;
}
else {
return null;
}
}
/**
* Expects to be added to a GameModule. Adds the button to the
* control window's toolbar and registers itself as a {@link
* KeyStrokeListener} */
public void addTo(Buildable parent) {
ran = GameModule.getGameModule().getRNG();
GameModule.getGameModule().getToolBar().add(getComponent());
property.setPropertyValue("1"); // Initialize with a numeric value //$NON-NLS-1$
property.addTo((MutablePropertiesContainer)parent);
}
/**
* The component to be added to the control window toolbar
*/
protected java.awt.Component getComponent() {
return launch;
}
public void setAttribute(String key, Object o) {
if (DEPRECATED_NAME.equals(key)) { // Backward compatibility. Before v1.3, name and button text were combined into one attribute
setAttribute(NAME, o);
setAttribute(BUTTON_TEXT, o);
}
else if (NAME.equals(key)) {
setConfigureName((String) o);
property.setPropertyName(getConfigureName()+"_result"); //$NON-NLS-1$
launch.setToolTipText((String) o);
}
else if (N_DICE.equals(key)) {
if (o instanceof Integer) {
nDice = ((Integer) o).intValue();
}
else if (o instanceof String) {
nDice = Integer.parseInt((String) o);
}
}
else if (N_SIDES.equals(key)) {
if (o instanceof Integer) {
nSides = ((Integer) o).intValue();
}
else if (o instanceof String) {
nSides = Integer.parseInt((String) o);
}
}
else if (PLUS.equals(key)) {
if (o instanceof Integer) {
plus = ((Integer) o).intValue();
}
else if (o instanceof String) {
plus = Integer.parseInt((String) o);
}
}
else if (ADD_TO_TOTAL.equals(key)) {
if (o instanceof Integer) {
addToTotal = ((Integer) o).intValue();
}
else if (o instanceof String) {
addToTotal = Integer.parseInt((String) o);
}
}
else if (REPORT_TOTAL.equals(key)) {
if (o instanceof Boolean) {
reportTotal = ((Boolean) o).booleanValue();
}
else if (o instanceof String) {
reportTotal = "true".equals(o); //$NON-NLS-1$
}
}
else if (PROMPT_ALWAYS.equals(key)) {
if (o instanceof Boolean) {
promptAlways = ((Boolean) o).booleanValue();
}
else if (o instanceof String) {
promptAlways = "true".equals(o); //$NON-NLS-1$
}
}
else if (REPORT_FORMAT.equals(key)) {
reportFormat.setFormat((String) o);
}
else if (TOOLTIP.equals(key)) {
tooltip = (String) o;
launch.setAttribute(key, o);
}
else if (SORT_DICE_RESULTS.equals(key)) {
if (o instanceof Boolean) {
sortDice = ((Boolean) o).booleanValue();
}
else if (o instanceof String) {
sortDice = "true".equals(o); //$NON-NLS-1$
}
}
else {
launch.setAttribute(key, o);
}
}
public String getAttributeValueString(String key) {
if (NAME.equals(key)) {
return getConfigureName();
}
else if (N_DICE.equals(key)) {
return String.valueOf(nDice);
}
else if (N_SIDES.equals(key)) {
return String.valueOf(nSides);
}
else if (PLUS.equals(key)) {
return String.valueOf(plus);
}
else if (ADD_TO_TOTAL.equals(key)) {
return String.valueOf(addToTotal);
}
else if (REPORT_TOTAL.equals(key)) {
return String.valueOf(reportTotal);
}
else if (PROMPT_ALWAYS.equals(key)) {
return String.valueOf(promptAlways);
}
else if (REPORT_FORMAT.equals(key)) {
return reportFormat.getFormat();
}
else if (TOOLTIP.equals(key)) {
return tooltip.length() == 0 ? launch.getAttributeValueString(BUTTON_TEXT) : tooltip;
}
else if (SORT_DICE_RESULTS.equals(key)) {
return String.valueOf(sortDice);
}
else {
return launch.getAttributeValueString(key);
}
}
public Class<?>[] getAllowableConfigureComponents() {
return new Class[0];
}
public void removeFrom(Buildable b) {
GameModule.getGameModule().getToolBar().remove(getComponent());
GameModule.getGameModule().getToolBar().revalidate();
}
public HelpFile getHelpFile() {
return HelpFile.getReferenceManualPage("GameModule.htm","DiceButton"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Implement PropertyNameSource - Expose roll result property
*/
public List<String> getPropertyNames() {
final ArrayList<String> l = new ArrayList<String>();
l.add(getConfigureName()+"_result");
return l;
}
}