// 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.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.infinity.datatype.Flag;
import org.infinity.datatype.SectionCount;
import org.infinity.datatype.SectionOffset;
import org.infinity.gui.layeritem.AbstractLayerItem;
import org.infinity.gui.layeritem.AnimatedLayerItem;
import org.infinity.gui.layeritem.IconLayerItem;
import org.infinity.resource.StructEntry;
import org.infinity.resource.are.Animation;
import org.infinity.resource.are.AreResource;
/**
* Manages background animation layer objects.
*/
public class LayerAnimation extends BasicLayer<LayerObjectAnimation>
{
private static final String AvailableFmt = "Background animations: %1$d";
private boolean realEnabled, realPlaying, forcedInterpolation, isAnimActiveIgnored;
private int frameState;
private Object interpolationType;
private double frameRate;
public LayerAnimation(AreResource are, AreaViewer viewer)
{
super(are, ViewerConstants.LayerType.ANIMATION, viewer);
realEnabled = realPlaying = false;
frameState = ViewerConstants.FRAME_AUTO;
forcedInterpolation = false;
interpolationType = ViewerConstants.TYPE_NEAREST_NEIGHBOR;
loadLayer(false);
}
@Override
public int loadLayer(boolean forced)
{
if (forced || !isInitialized()) {
close();
List<LayerObjectAnimation> list = getLayerObjects();
if (hasAre()) {
AreResource are = getAre();
SectionOffset so = (SectionOffset)are.getAttribute(AreResource.ARE_OFFSET_ANIMATIONS);
SectionCount sc = (SectionCount)are.getAttribute(AreResource.ARE_NUM_ANIMATIONS);
if (so != null && sc != null) {
int ofs = so.getValue();
int count = sc.getValue();
List<StructEntry> listStruct = getStructures(ofs, count, Animation.class);
for (int i = 0, size = listStruct.size(); i < size; i++) {
LayerObjectAnimation obj = new LayerObjectAnimation(are, (Animation)listStruct.get(i));
setListeners(obj);
list.add(obj);
}
setInitialized(true);
}
}
// sorting entries (animations not flagged as "draw as background" come first)
Collections.sort(list, new Comparator<LayerObjectAnimation>() {
@Override
public int compare(LayerObjectAnimation o1, LayerObjectAnimation o2) {
boolean isBackground1, isBackground2;
try {
isBackground1 = ((Flag)((Animation)o1.getViewable()).getAttribute(Animation.ARE_ANIMATION_APPEARANCE)).isFlagSet(8);
isBackground2 = ((Flag)((Animation)o2.getViewable()).getAttribute(Animation.ARE_ANIMATION_APPEARANCE)).isFlagSet(8);
} catch (Exception e) {
isBackground1 = false;
isBackground2 = false;
}
if (!isBackground1 && isBackground2) {
return -1;
} else if (isBackground1 && !isBackground2) {
return 1;
} else {
return 0;
}
}
});
return list.size();
}
return 0;
}
@Override
public String getAvailability()
{
int cnt = getLayerObjectCount();
return String.format(AvailableFmt, cnt);
}
/**
* Sets the visibility state of all items in the layer. Takes enabled states of the different
* item types into account.
*/
public void setLayerVisible(boolean visible)
{
setVisibilityState(visible);
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
boolean state = isLayerVisible() && (!isScheduleEnabled() || (isScheduleEnabled() && isScheduled(i)));
LayerObjectAnimation obj = list.get(i);
IconLayerItem iconItem = (IconLayerItem)obj.getLayerItem(ViewerConstants.ANIM_ITEM_ICON);
if (iconItem != null) {
iconItem.setVisible(state && !realEnabled);
}
AnimatedLayerItem animItem = (AnimatedLayerItem)obj.getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (animItem != null) {
animItem.setVisible(state && realEnabled);
if (isRealAnimationEnabled() && isRealAnimationPlaying()) {
animItem.play();
} else {
animItem.stop();
}
}
}
}
}
/**
* Returns the currently active interpolation type for real animations.
* @return Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR, ViewerConstants.TYPE_NEAREST_BILINEAR
* or ViewerConstants.TYPE_BICUBIC.
*/
public Object getRealAnimationInterpolation()
{
return interpolationType;
}
/**
* Sets the interpolation type for real animations
* @param interpolationType Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR,
* ViewerConstants.TYPE_NEAREST_BILINEAR or ViewerConstants.TYPE_BICUBIC.
*/
public void setRealAnimationInterpolation(Object interpolationType)
{
if (interpolationType != this.interpolationType) {
this.interpolationType = interpolationType;
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
AnimatedLayerItem item = (AnimatedLayerItem)list.get(i).getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (item != null) {
item.setInterpolationType(this.interpolationType);
}
}
}
}
}
/**
* Returns whether to force the specified interpolation type or use the best one available, depending
* on the current zoom factor.
*/
public boolean isRealAnimationForcedInterpolation()
{
return forcedInterpolation;
}
/**
* Specify whether to force the specified interpolation type or use the best one available, depending
* on the current zoom factor.
*/
public void setRealAnimationForcedInterpolation(boolean forced)
{
if (forced != forcedInterpolation) {
forcedInterpolation = forced;
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
AnimatedLayerItem item = (AnimatedLayerItem)list.get(i).getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (item != null) {
item.setForcedInterpolation(forcedInterpolation);
}
}
}
}
}
/**
* Returns whether real animation items or iconic animation items are enabled.
* @return If {@code true}, real animation items are enabled.
* If {@code false}, iconic animation items are enabled.
*/
public boolean isRealAnimationEnabled()
{
return realEnabled;
}
/**
* Specify whether iconic animation type or real animation type is enabled.
* @param enable If {@code true}, real animation items will be shown.
* If {@code false}, iconic animation items will be shown.
*/
public void setRealAnimationEnabled(boolean enable)
{
if (enable != realEnabled) {
realEnabled = enable;
if (isLayerVisible()) {
setLayerVisible(isLayerVisible());
}
}
}
/**
* Returns whether real animation items are enabled and animated.
*/
public boolean isRealAnimationPlaying()
{
return realEnabled && realPlaying;
}
/**
* Specify whether real animation should be animated. Setting to {@code true} will enable
* real animations automatically.
*/
public void setRealAnimationPlaying(boolean play)
{
if (play != realPlaying) {
realPlaying = play;
if (realPlaying && !realEnabled) {
realEnabled = true;
}
if (isLayerVisible()) {
setLayerVisible(isLayerVisible());
}
}
}
/**
* Returns the current frame visibility.
* @return One of ViewerConstants.FRAME_NEVER, ViewerConstants.FRAME_AUTO or ViewerConstants.FRAME_ALWAYS.
*/
public int getRealAnimationFrameState()
{
return frameState;
}
/**
* Specify the frame visibility for real animations
* @param state One of ViewerConstants.FRAME_NEVER, ViewerConstants.FRAME_AUTO or ViewerConstants.FRAME_ALWAYS.
*/
public void setRealAnimationFrameState(int state)
{
switch (state) {
case ViewerConstants.FRAME_NEVER:
case ViewerConstants.FRAME_AUTO:
case ViewerConstants.FRAME_ALWAYS:
{
frameState = state;
updateFrameState();
break;
}
}
}
/**
* Returns the frame rate used for playing back background animations.
* @return Frame rate in frames/second.
*/
public double getRealAnimationFrameRate()
{
return frameRate;
}
/**
* Specify a new frame rate for real animations.
* @param frameRate Frame rate in frames/second.
*/
public void setRealAnimationFrameRate(double frameRate)
{
frameRate = Math.min(Math.max(frameRate, 1.0), 30.0);
if (frameRate != this.frameRate) {
this.frameRate = frameRate;
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
AnimatedLayerItem item = (AnimatedLayerItem)list.get(i).getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (item != null) {
item.setFrameRate(this.frameRate);
}
}
}
}
}
/**
* Returns whether the current activation states of real animations are ignored
* (i.e. treated as always active).
*/
public boolean isRealAnimationActiveIgnored()
{
return isAnimActiveIgnored;
}
/**
* Sets whether the activation state of real animations are ignored (i.e. treated as always active).
* @param set
*/
public void setRealAnimationActiveIgnored(boolean set)
{
isAnimActiveIgnored = set;
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
AnimatedLayerItem item = (AnimatedLayerItem)list.get(i).getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (item != null) {
if (item.getAnimation() instanceof BackgroundAnimationProvider) {
((BackgroundAnimationProvider)item.getAnimation()).setActiveIgnored(isAnimActiveIgnored);
}
}
}
}
}
private void updateFrameState()
{
List<LayerObjectAnimation> list = getLayerObjects();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
AnimatedLayerItem item = (AnimatedLayerItem)list.get(i).getLayerItem(ViewerConstants.ANIM_ITEM_REAL);
if (item != null) {
switch (frameState) {
case ViewerConstants.FRAME_NEVER:
item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false);
item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, false);
break;
case ViewerConstants.FRAME_AUTO:
item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false);
item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true);
break;
case ViewerConstants.FRAME_ALWAYS:
item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, true);
item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true);
break;
}
}
}
}
}
}