/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.puppygames.applet.widgets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.puppygames.applet.Anchor;
import net.puppygames.applet.Game;
import net.puppygames.applet.Screen;
import org.lwjgl.util.Point;
import org.lwjgl.util.ReadablePoint;
import org.lwjgl.util.ReadableRectangle;
import org.lwjgl.util.Rectangle;
import org.w3c.dom.Element;
import com.shavenpuppy.jglib.resources.Feature;
import com.shavenpuppy.jglib.sprites.Appearance;
import com.shavenpuppy.jglib.sprites.Sprite;
import com.shavenpuppy.jglib.sprites.SpriteAllocator;
import com.shavenpuppy.jglib.util.FPMath;
import com.shavenpuppy.jglib.util.XMLUtil;
/**
* $Id: PowerDisplayFeature.java,v 1.5 2010/08/03 23:43:39 foo Exp $
* Displays coloured bars indicating the level of a powerup.
* @author $Author: foo $
* @version $Revision: 1.5 $
*/
public class PowerDisplayFeature extends Feature {
private static final long serialVersionUID = 1L;
/*
* Static stuff
*/
/** Bounds (used for determining mouse clicks) */
private Rectangle bounds = new Rectangle(0, 0, 0, 0);
/** Default gap between bars where not specified */
private int gap;
/** Location */
private Point location = new Point(0, 0);
/** Layer */
private int layer;
/** Bars */
private List<BarFeature> bars;
/** Which direction the bars go */
private int dx, dy;
/** Scale of bar Sprite image */
private float scale = 1.0f;
/** Anchors */
private ArrayList<Anchor> anchors;
/**
* Bars
*/
private static class BarFeature extends Feature {
private static final long serialVersionUID = 1L;
private String on, off;
private transient Appearance onResource, offResource;
private Screen screen;
private class BarInstance {
Sprite sprite;
PowerDisplay display;
void init(PowerDisplay display, SpriteAllocator screen) {
this.display = display;
sprite = screen.allocateSprite(BarFeature.this);
if (sprite == null) {
throw new RuntimeException("Not enough sprites to create power display");
}
sprite.setLayer(display.getFeature().layer);
if (offResource == null) {
sprite.setAppearance(onResource);
sprite.setVisible(false);
}
if (display.getFeature().scale != 1.0f) {
sprite.setScale(FPMath.fpValue(display.getFeature().scale));
}
}
void cleanup() {
if (sprite != null) {
sprite.deallocate();
sprite = null;
}
}
void setUsed(boolean used) {
if (used) {
sprite.setAppearance(onResource);
sprite.setVisible(display.isVisible());
} else if (offResource == null) {
sprite.setVisible(false);
} else {
sprite.setAppearance(offResource);
sprite.setVisible(display.isVisible());
}
}
void setLocation(float x, float y) {
sprite.setLocation(x, y);
}
}
/**
* C'tor
*/
private BarFeature() {
setAutoCreated();
setSubResource(true);
}
/**
* @param screen
* @return
*/
BarInstance init(PowerDisplay display, SpriteAllocator screen) {
BarInstance ret = new BarInstance();
ret.init(display, screen);
return ret;
}
}
/**
* C'tor
*/
public PowerDisplayFeature(String name) {
super(name);
setAutoCreated();
}
/**
* C'tor
*/
public PowerDisplayFeature() {
setAutoCreated();
}
@Override
public void load(Element element, Loader loader) throws Exception {
super.load(element, loader);
List<Element> barElements = XMLUtil.getChildren(element, "bar");
bars = new ArrayList<BarFeature>(barElements.size());
for (Element barElement : barElements) {
BarFeature bf = new BarFeature();
bf.load(barElement, loader);
bars.add(bf);
}
// Anchors
List<Element> anchorElements = XMLUtil.getChildren(element, "anchor");
if (anchorElements.size() > 0) {
anchors = new ArrayList<Anchor>(anchorElements.size());
for (Element anchorChild : anchorElements) {
Anchor anchor = (Anchor) loader.load(anchorChild);
anchors.add(anchor);
}
}
}
@Override
protected void doCreate() {
super.doCreate();
for (Iterator<BarFeature> i = bars.iterator(); i.hasNext(); ) {
BarFeature bar = i.next();
bar.create();
}
}
@Override
protected void doDestroy() {
super.doDestroy();
for (Iterator<BarFeature> i = bars.iterator(); i.hasNext(); ) {
BarFeature bar = i.next();
bar.destroy();
}
}
/**
* Create a power display instance
* @param screen
*/
public PowerDisplay spawn(final Screen screen) {
PowerDisplay ret = new PowerDisplay() {
/** My bounds */
Rectangle instance_bounds = new Rectangle(PowerDisplayFeature.this.bounds);
/** The bars */
BarFeature.BarInstance[] bar;
/** Location */
float x, y;
/** Used bars */
int used = -1;
/** Visibility */
boolean visible = true;
@Override
public PowerDisplayFeature getFeature() {
return PowerDisplayFeature.this;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setVisible(boolean visible) {
if (this.visible == visible) {
return;
}
this.visible = visible;
doSetUsed();
}
@Override
public void setUsed(int used) {
if (this.used == used) {
return;
}
this.used = used;
doSetUsed();
}
private void doSetUsed() {
for (int i = 0; i < Math.min(bar.length, used); i ++) {
bar[i].setUsed(true);
}
for (int i = Math.max(0, used); i < bar.length; i ++) {
bar[i].setUsed(false);
}
}
@Override
public ReadableRectangle getBounds() {
return new Rectangle((int) x, (int) y, 0, 0);
}
@Override
public void setBounds(int x, int y, int w, int h) {
setLocation(x, y);
}
@Override
public void setLocation(float x, float y) {
this.x = x;
this.y = y;
instance_bounds.setLocation((int) x, (int) y);
for (int i = 0; i < bar.length; i ++) {
bar[i].setLocation(x, y);
if (gap == 0) {
x += dx;
y += dy;
} else {
x += gap;
}
}
}
@Override
public ReadablePoint getBarLocation(int idx) {
if (idx >= bar.length || idx < 1) {
return null;
}
return new Point((int) bar[idx - 1].sprite.getX(), (int) bar[idx - 1].sprite.getY());
}
@Override
public void setAlpha(int alpha) {
for (int i = 0; i < bar.length; i ++) {
bar[i].sprite.setAlpha(alpha);
}
}
@Override
public Sprite getSprite(int idx) {
return bar[idx].sprite;
}
@Override
public int getBarAt(int xx, int yy) {
if (!instance_bounds.contains(xx, yy)) {
return -1;
}
return (getMax() * (xx - instance_bounds.getX())) / (instance_bounds.getWidth());
}
@Override
public void init(SpriteAllocator screen) {
bar = new BarFeature.BarInstance[bars.size()];
for (int i = 0; i < bar.length; i ++) {
bar[i] = bars.get(i).init(this, screen);
}
}
@Override
public void cleanup() {
if (bar == null) {
return;
}
for (int i = 0; i < bar.length; i ++) {
bar[i].cleanup();
}
bar = null;
}
@Override
public int getUsed() {
return used;
}
@Override
public int getMax() {
return bars.size();
}
@Override
public void onResized() {
// Apply anchors unless screen is "centred"
if (anchors != null) {
for (Iterator<Anchor> i = anchors.iterator(); i.hasNext(); ) {
Anchor anchor = i.next();
anchor.apply(this);
}
} else if (location != null) {
boolean centreX = screen.isCentred() || screen.isCentredX();
boolean centreY = screen.isCentred() || screen.isCentredY();
if (centreX || centreY) {
int newX = (centreX ? (Game.getWidth() - Game.getScale()) / 2 + location.getX() : location.getX());
int newY = (centreY ? (Game.getHeight() - Game.getScale()) / 2 + location.getY() : location.getY());
setLocation(newX, newY);
}
}
}
};
ret.init(screen);
ret.setLocation(location.getX(), location.getY());
ret.setUsed(0);
return ret;
}
/**
* Get the total bars
* @return int
*/
public int getTotalBars() {
return bars.size();
}
}