<commit_before>/*
 * $Id$
 *
 * Copyright (c) 2005 by <NAME>, <NAME>, <NAME>
 *
 * 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.counters;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import VASSAL.build.module.Map;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.map.MapShader;
import VASSAL.build.module.map.boardPicker.Board;
import VASSAL.build.module.map.boardPicker.board.GeometricGrid;
import VASSAL.build.module.map.boardPicker.board.MapGrid;
import VASSAL.command.ChangeTracker;
import VASSAL.command.Command;
import VASSAL.configure.BooleanConfigurer;
import VASSAL.configure.ChooseComponentDialog;
import VASSAL.configure.ColorConfigurer;
import VASSAL.configure.HotKeyConfigurer;
import VASSAL.configure.IntConfigurer;
import VASSAL.configure.StringConfigurer;
import VASSAL.i18n.PieceI18nData;
import VASSAL.i18n.TranslatablePiece;
import VASSAL.tools.SequenceEncoder;

/**
 * @author <NAME> <EMAIL>
 *
 * Displays a transparency surrounding the GamePiece which represents the Area of Effect of the GamePiece
 */
public class AreaOfEffect extends Decorator implements TranslatablePiece, MapShader.ShadedPiece {
  public static final String ID = "AreaOfEffect;";
  protected static final Color defaultTransparencyColor = Color.GRAY;
  protected static final float defaultTransparencyLevel = 0.3F;
  protected static final int defaultRadius = 1;

  protected Color transparencyColor;
  protected float transparencyLevel;
  protected int radius;
  protected boolean alwaysActive;
  protected boolean active;
  protected String activateCommand;
  protected KeyStroke activateKey;
  protected KeyCommand[] commands;
  protected String mapShaderName;
  protected MapShader shader;
  protected KeyCommand keyCommand;
  protected boolean fixedRadius = true;
  protected String radiusMarker = "";
  protected String description = "";

  public AreaOfEffect() {
    this(ID + ColorConfigurer.colorToString(defaultTransparencyColor), null);
  }

  public AreaOfEffect(String type, GamePiece inner) {
    mySetType(type);
    setInner(inner);
  }

  public String getDescription() {
    String d = "Area Of Effect";
    if (description.length() > 0) {
      d += " - " + description;
    }
    return d;
  }

  public String myGetType() {
    SequenceEncoder se = new SequenceEncoder(';');
    se.append(transparencyColor);
    se.append((int) (transparencyLevel * 100));
    se.append(radius);
    se.append(alwaysActive);
    se.append(activateCommand);
    se.append(activateKey);
    se.append(mapShaderName == null ? "" : mapShaderName);
    se.append(fixedRadius);
    se.append(radiusMarker);
    se.append(description);

    return ID + se.getValue();
  }

  public void mySetType(String type) {
    SequenceEncoder.Decoder st = new SequenceEncoder.Decoder(type, ';');
    st.nextToken();    // Discard ID
    transparencyColor = st.nextColor(defaultTransparencyColor);
    transparencyLevel = st.nextInt((int) (defaultTransparencyLevel * 100)) / 100.0F;
    radius = st.nextInt(defaultRadius);
    alwaysActive = st.nextBoolean(true);
    activateCommand = st.nextToken("Show Area");
    activateKey = st.nextKeyStroke(null);
    keyCommand = new KeyCommand(activateCommand, activateKey, Decorator.getOutermost(this), this);
    mapShaderName = st.nextToken("");
    if (mapShaderName.length() == 0) {
      mapShaderName = null;
    }
    fixedRadius = st.nextBoolean(true);
    radiusMarker = st.nextToken("");
    description = st.nextToken("");
    shader = null;
    commands = null;
  }

  // State does not change during the game
  public String myGetState() {
    return alwaysActive ? "" : String.valueOf(active);
  }

  // State does not change during the game
  public void mySetState(String newState) {
    if (!alwaysActive) {
      active = "true".equals(newState);
    }
  }

  public Rectangle boundingBox() {
    // TODO: Need the context of the parent Component, because the transparency is only drawn
    // on a Map.View object.  Because this context is not known, the bounding box returned by
    // this method does not encompass the bounds of the transparency.  The result of this is
    // that portions of the transparency will not be drawn after scrolling the Map window.
    return piece.boundingBox();
  }

  public Shape getShape() {
    return piece.getShape();
  }

  public String getName() {
    return piece.getName();
  }

  public void draw(Graphics g, int x, int y, Component obs, double zoom) {
    if ((alwaysActive || active) && mapShaderName == null) {
// The transparency is only draw on a Map.View component.  Only the GamePiece is drawn within
// other windows (Counter Palette, etc.)
      if (obs instanceof Map.View) {
        Graphics2D g2d = (Graphics2D) g;

        Color oldColor = g2d.getColor();
        g2d.setColor(transparencyColor);

        Composite oldComposite = g2d.getComposite();
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparencyLevel));

        Area a = getArea();

        if (zoom != 1.0) {
          a = new Area(AffineTransform.getScaleInstance(zoom,zoom).createTransformedShape(a));
        }
        g2d.fill(a);

        g2d.setColor(oldColor);
        g2d.setComposite(oldComposite);<commit_msg>Bug fix:  NullPointer in AreaOfEffect<commit_after>/*
 * $Id$
 *
 * Copyright (c) 2005 by <NAME>, <NAME>, <NAME>
 *
 * 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.counters;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import VASSAL.build.module.Map;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.map.MapShader;
import VASSAL.build.module.map.boardPicker.Board;
import VASSAL.build.module.map.boardPicker.board.GeometricGrid;
import VASSAL.build.module.map.boardPicker.board.MapGrid;
import VASSAL.command.ChangeTracker;
import VASSAL.command.Command;
import VASSAL.configure.BooleanConfigurer;
import VASSAL.configure.ChooseComponentDialog;
import VASSAL.configure.ColorConfigurer;
import VASSAL.configure.HotKeyConfigurer;
import VASSAL.configure.IntConfigurer;
import VASSAL.configure.StringConfigurer;
import VASSAL.i18n.PieceI18nData;
import VASSAL.i18n.TranslatablePiece;
import VASSAL.tools.SequenceEncoder;

/**
 * @author <NAME> <EMAIL>
 *
 * Displays a transparency surrounding the GamePiece which represents the Area of Effect of the GamePiece
 */
public class AreaOfEffect extends Decorator implements TranslatablePiece, MapShader.ShadedPiece {
  public static final String ID = "AreaOfEffect;";
  protected static final Color defaultTransparencyColor = Color.GRAY;
  protected static final float defaultTransparencyLevel = 0.3F;
  protected static final int defaultRadius = 1;

  protected Color transparencyColor;
  protected float transparencyLevel;
  protected int radius;
  protected boolean alwaysActive;
  protected boolean active;
  protected String activateCommand;
  protected KeyStroke activateKey;
  protected KeyCommand[] commands;
  protected String mapShaderName;
  protected MapShader shader;
  protected KeyCommand keyCommand;
  protected boolean fixedRadius = true;
  protected String radiusMarker = "";
  protected String description = "";

  public AreaOfEffect() {
    this(ID + ColorConfigurer.colorToString(defaultTransparencyColor), null);
  }

  public AreaOfEffect(String type, GamePiece inner) {
    mySetType(type);
    setInner(inner);
  }

  public String getDescription() {
    String d = "Area Of Effect";
    if (description.length() > 0) {
      d += " - " + description;
    }
    return d;
  }

  public String myGetType() {
    SequenceEncoder se = new SequenceEncoder(';');
    se.append(transparencyColor);
    se.append((int) (transparencyLevel * 100));
    se.append(radius);
    se.append(alwaysActive);
    se.append(activateCommand);
    se.append(activateKey);
    se.append(mapShaderName == null ? "" : mapShaderName);
    se.append(fixedRadius);
    se.append(radiusMarker);
    se.append(description);

    return ID + se.getValue();
  }

  public void mySetType(String type) {
    SequenceEncoder.Decoder st = new SequenceEncoder.Decoder(type, ';');
    st.nextToken();    // Discard ID
    transparencyColor = st.nextColor(defaultTransparencyColor);
    transparencyLevel = st.nextInt((int) (defaultTransparencyLevel * 100)) / 100.0F;
    radius = st.nextInt(defaultRadius);
    alwaysActive = st.nextBoolean(true);
    activateCommand = st.nextToken("Show Area");
    activateKey = st.nextKeyStroke(null);
    keyCommand = new KeyCommand(activateCommand, activateKey, Decorator.getOutermost(this), this);
    mapShaderName = st.nextToken("");
    if (mapShaderName.length() == 0) {
      mapShaderName = null;
    }
    fixedRadius = st.nextBoolean(true);
    radiusMarker = st.nextToken("");
    description = st.nextToken("");
    shader = null;
    commands = null;
  }

  // State does not change during the game
  public String myGetState() {
    return alwaysActive ? "" : String.valueOf(active);
  }

  // State does not change during the game
  public void mySetState(String newState) {
    if (!alwaysActive) {
      active = "true".equals(newState);
    }
  }

  public Rectangle boundingBox() {
    // TODO: Need the context of the parent Component, because the transparency is only drawn
    // on a Map.View object.  Because this context is not known, the bounding box returned by
    // this method does not encompass the bounds of the transparency.  The result of this is
    // that portions of the transparency will not be drawn after scrolling the Map window.
    return piece.boundingBox();
  }

  public Shape getShape() {
    return piece.getShape();
  }

  public String getName() {
    return piece.getName();
  }

  public void draw(Graphics g, int x, int y, Component obs, double zoom) {
    if ((alwaysActive || active) && mapShaderName == null) {
// The transparency is only draw on a Map.View component.  Only the GamePiece is drawn within
// other windows (Counter Palette, etc.)
      if (obs instanceof Map.View && getMap() != null) {
        Graphics2D g2d = (Graphics2D) g;

        Color oldColor = g2d.getColor();
        g2d.setColor(transparencyColor);

        Composite oldComposite = g2d.getComposite();
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparencyLevel));

        Area a = getArea();

        if (zoom != 1.0) {
          a = new Area(AffineTransform.getScaleInstance(zoom,zoom).createTransformedShape(a));
        }
        g2d.fill(a);

        g2d.setColor(oldColor);
        g2d.setComposite(oldComposite);
{'fraction_non_alphanumeric': 0.09836685316800242, 'fraction_numerical': 0.005897474671102374, 'mean_word_length': 4.025455927051672, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6121025', 'n_tokens_mistral': 3913, 'n_tokens_neox': 3793, 'n_words': 1111}