// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.resource.are.viewer;
import java.awt.Color;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import org.infinity.datatype.DecNumber;
import org.infinity.datatype.Flag;
import org.infinity.datatype.TextString;
import org.infinity.gui.layeritem.AbstractLayerItem;
import org.infinity.gui.layeritem.IconLayerItem;
import org.infinity.gui.layeritem.ShapedLayerItem;
import org.infinity.icon.Icons;
import org.infinity.resource.Viewable;
import org.infinity.resource.are.Ambient;
import org.infinity.resource.are.AreResource;
import org.infinity.resource.are.viewer.icon.ViewerIcons;
/**
* Handles specific layer type: ARE/Ambient Sound and Ambient Sound Range
* Note: Ambient returns two layer items: 0=icon, 1=range (if available)
*/
public class LayerObjectAmbient extends LayerObject
{
private static final Image[] IconGlobal = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_AMBIENT_G_1),
Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_AMBIENT_G_2)};
private static final Image[] IconLocal = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_AMBIENT_L_1),
Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_AMBIENT_L_2)};
private static final Point Center = new Point(16, 16);
final Color[] ColorRange = {new Color(0xA0000080, true), new Color(0xA0000080, true),
new Color(0x00204080, true), new Color(0x004060C0, true)};
private final Ambient ambient;
private final Point location = new Point();
private IconLayerItem itemIcon; // for sound icon
private ShapedLayerItem itemShape; // for sound range
private int radiusLocal, volume;
private Flag scheduleFlags;
public LayerObjectAmbient(AreResource parent, Ambient ambient)
{
super(ViewerConstants.RESOURCE_ARE, "Sound", Ambient.class, parent);
this.ambient = ambient;
init();
}
@Override
public Viewable getViewable()
{
return ambient;
}
@Override
public Viewable[] getViewables()
{
if (isLocal()) {
return new Viewable[]{ambient, ambient};
} else {
return new Viewable[]{ambient};
}
}
@Override
public AbstractLayerItem getLayerItem()
{
return itemIcon;
}
/**
* Returns the layer item of specified type.
* @param type The type of the item to return (either {@code ViewerConstants.AMBIENT_ITEM_ICON} or
* {@code ViewerConstants.AMBIENT_ITEM_RANGE}).
* @return The layer item of specified type.
*/
@Override
public AbstractLayerItem getLayerItem(int type)
{
if (type == ViewerConstants.AMBIENT_ITEM_RANGE && isLocal()) {
return itemShape;
} else if (type == ViewerConstants.AMBIENT_ITEM_ICON) {
return itemIcon;
} else {
return null;
}
}
@Override
public AbstractLayerItem[] getLayerItems()
{
if (isLocal()) {
return new AbstractLayerItem[]{itemIcon, itemShape};
} else {
return new AbstractLayerItem[]{itemIcon};
}
}
@Override
public void reload()
{
init();
}
@Override
public void update(double zoomFactor)
{
int x = (int)(location.x*zoomFactor + (zoomFactor / 2.0));
int y = (int)(location.y*zoomFactor + (zoomFactor / 2.0));
if (itemIcon != null) {
itemIcon.setItemLocation(x, y);
}
if (isLocal()) {
Shape circle = createShape(zoomFactor);
Rectangle rect = circle.getBounds();
itemShape.setItemLocation(x, y);
itemShape.setCenterPosition(new Point(rect.width / 2, rect.height / 2));
itemShape.setShape(circle);
}
}
@Override
public Point getMapLocation()
{
return location;
}
@Override
public Point[] getMapLocations()
{
return new Point[]{location, location};
}
/**
* Returns whether the ambient sound uses a local sound radius.
*/
public boolean isLocal()
{
return (itemShape != null);
}
/**
* Returns the local radius of the ambient sound (if any).
*/
public int getRadius()
{
return radiusLocal;
}
/**
* Returns the volume of the ambient sound.
*/
public int getVolume()
{
return volume;
}
@Override
public boolean isScheduled(int schedule)
{
if (schedule >= ViewerConstants.TIME_0 && schedule <= ViewerConstants.TIME_23) {
return (scheduleFlags.isFlagSet(schedule));
} else {
return false;
}
}
private void init()
{
if (ambient != null) {
String msg = "";
Image[] icon = IconGlobal;
Shape circle = null;
Color[] color = new Color[ColorRange.length];
try {
location.x = ((DecNumber)ambient.getAttribute(Ambient.ARE_AMBIENT_ORIGIN_X)).getValue();
location.y = ((DecNumber)ambient.getAttribute(Ambient.ARE_AMBIENT_ORIGIN_Y)).getValue();
radiusLocal = ((DecNumber)ambient.getAttribute(Ambient.ARE_AMBIENT_RADIUS)).getValue();
volume = ((DecNumber)ambient.getAttribute(Ambient.ARE_AMBIENT_VOLUME)).getValue();
if (((Flag)ambient.getAttribute(Ambient.ARE_AMBIENT_FLAGS)).isFlagSet(2)) {
icon = IconGlobal;
radiusLocal = 0;
} else {
icon = IconLocal;
}
scheduleFlags = ((Flag)ambient.getAttribute(Ambient.ARE_AMBIENT_ACTIVE_AT));
msg = ((TextString)ambient.getAttribute(Ambient.ARE_AMBIENT_NAME)).toString();
if (icon == IconLocal) {
circle = createShape(1.0);
double minAlpha = 0.0, maxAlpha = 64.0;
double alphaF = minAlpha + Math.sqrt((double)volume) / 10.0 * (maxAlpha - minAlpha);
int alpha = (int)alphaF & 0xff;
color[0] = ColorRange[0];
color[1] = ColorRange[1];
color[2] = new Color(ColorRange[2].getRGB() | (alpha << 24), true);
color[3] = new Color(ColorRange[3].getRGB() | (alpha << 24), true);
}
} catch (Exception e) {
e.printStackTrace();
}
// Using cached icons
String keyIcon = String.format("%1$s%2$s", SharedResourceCache.createKey(icon[0]),
SharedResourceCache.createKey(icon[1]));
if (SharedResourceCache.contains(SharedResourceCache.Type.ICON, keyIcon)) {
icon = ((ResourceIcon)SharedResourceCache.get(SharedResourceCache.Type.ICON, keyIcon)).getData();
SharedResourceCache.add(SharedResourceCache.Type.ICON, keyIcon);
} else {
SharedResourceCache.add(SharedResourceCache.Type.ICON, keyIcon, new ResourceIcon(keyIcon, icon));
}
// creating sound item
itemIcon = new IconLayerItem(location, ambient, msg, icon[0], Center);
itemIcon.setName(getCategory());
itemIcon.setToolTipText(msg);
itemIcon.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icon[1]);
itemIcon.setVisible(isVisible());
// creating sound range item
if (icon == IconLocal) {
itemShape = new ShapedLayerItem(location, ambient, msg, circle, new Point(radiusLocal, radiusLocal));
itemShape.setName(getCategory());
itemShape.setStrokeColor(AbstractLayerItem.ItemState.NORMAL, color[0]);
itemShape.setStrokeColor(AbstractLayerItem.ItemState.HIGHLIGHTED, color[1]);
itemShape.setFillColor(AbstractLayerItem.ItemState.NORMAL, color[2]);
itemShape.setFillColor(AbstractLayerItem.ItemState.HIGHLIGHTED, color[3]);
itemShape.setStrokeWidth(AbstractLayerItem.ItemState.NORMAL, 2);
itemShape.setStrokeWidth(AbstractLayerItem.ItemState.HIGHLIGHTED, 2);
itemShape.setStroked(true);
itemShape.setFilled(true);
itemShape.setVisible(isVisible());
}
}
}
private Shape createShape(double zoomFactor)
{
if (ambient != null && itemShape != null && radiusLocal > 0) {
float diameter = (float)(radiusLocal*zoomFactor + (zoomFactor / 2.0)) * 2.0f;
return new Ellipse2D.Float(0.0f, 0.0f, diameter, diameter);
}
return null;
}
}