package com.pointcliki.particles;
import java.util.ArrayList;
import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import org.newdawn.slick.particles.ConfigurableEmitter;
import org.newdawn.slick.particles.Particle;
import org.newdawn.slick.particles.ParticleEmitter;
import org.newdawn.slick.particles.ParticleSystem;
import org.newdawn.slick.util.FastTrig;
import com.pointcliki.core.ISavable;
import com.pointcliki.random.RandomVariable;
/**
* @author Hugheth
* @since 3
*/
public class StandardEmitter implements ParticleEmitter, ISavable {
/** The spawn interval range property - how often spawn happens */
public RandomVariable spawnInterval = new RandomVariable(100, 100);
/** The spawn count property - how many particles are spawned each time */
public RandomVariable spawnCount = new RandomVariable(5, 5);
/** The initial life of the new pixels */
public RandomVariable initialLife = new RandomVariable(1000, 1000);
/** The initial size of the new pixels */
public RandomVariable initialSize = new RandomVariable(10, 10);
/** The offset from the x position */
public RandomVariable xOffset = new RandomVariable(0, 0);
/** The offset from the y position */
public RandomVariable yOffset = new RandomVariable(0, 0);
/** The spread of the particles */
public RandomVariable spread = new RandomVariable(360);
/** The angular offset */
public RandomVariable angularOffset = new RandomVariable(0);
/** The initial distance of the particles */
public RandomVariable initialDistance = new RandomVariable(0, 0);
/** The speed particles fly out */
public RandomVariable speed = new RandomVariable(50, 50);
/** The growth factor on the particles */
public RandomVariable growthFactor = new RandomVariable(0);
/** The factor of gravity to apply */
public RandomVariable gravityFactor = new RandomVariable(0);
/** The factor of wind to apply */
public RandomVariable windFactor = new RandomVariable(0);
/** The time duration of the effect */
public RandomVariable duration = new RandomVariable(1000, 1000);
/**
* The color range
*
* @see ColorRecord
*/
public ArrayList colors = new ArrayList();
/** The starting alpha value */
public RandomVariable startAlpha = new RandomVariable(255, 255);
/** The ending alpha value */
public RandomVariable endAlpha = new RandomVariable(0, 0);
/** Whiskas - Interpolated value for alpha */
public RandomVariable alpha;
/** Whiskas - Interpolated value for size */
public RandomVariable size;
/** Whiskas - Interpolated value for velocity */
public RandomVariable velocity;
/** Whiskas - Interpolated value for y axis scaling */
public RandomVariable scaleY;
/** The number of particles that will be emitted */
public RandomVariable emitCount = new RandomVariable(1000, 1000);
/** The points indicate */
public int usePoints = Particle.INHERIT_POINTS;
/** True if the quads should be orieted based on velocity */
public boolean useOriented = false;
/**
* True if the additivie blending mode should be used for particles owned by
* this emitter
*/
public boolean useAdditive = false;
/** The name attribute */
public String name;
/** The image being used for the particles */
public Image image;
/** True if the emitter is enabled */
private boolean enabled = true;
/** The x coordinate of the position of this emitter */
private float x;
/** The y coordinate of the position of this emitter */
private float y;
/** The time in milliseconds til the next spawn */
private int nextSpawn = 0;
/** The timeout counting down to spawn */
private int timeout;
/** The number of particles in use by this emitter */
private int particleCount;
/** The system this emitter is being updated to */
private ParticleSystem engine;
/** The number of particles that are left ot emit */
private int leftToEmit;
/** True if we're wrapping up */
private boolean wrapUp = false;
/** True if the system has completed due to a wrap up */
private boolean completed = false;
/**
* Create a new emitter configurable externally
* @param name
* The name of emitter
*/
public StandardEmitter(String name) {
this.name = name;
leftToEmit = (int) emitCount.value();
timeout = (int) (duration.value());
colors.add(new ColorRecord(0, Color.white));
colors.add(new ColorRecord(1, Color.red));
/*
ArrayList<Vector2f> curve = new ArrayList<Vector2f>();
curve.add(new Vector2f(0.0f, 0.0f));
curve.add(new Vector2f(1.0f, 255.0f));
alpha = new RandomVariable(curve, 0, 255);
curve = new ArrayList<Vector2f>();
curve.add(new Vector2f(0.0f, 0.0f));
curve.add(new Vector2f(1.0f, 255.0f));
size = new RandomVariable(curve, 0, 255);
curve = new ArrayList<Vector2f>();
curve.add(new Vector2f(0.0f, 0.0f));
curve.add(new Vector2f(1.0f, 1.0f));
velocity = new RandomVariabler(curve, 0, 1);
curve = new ArrayList();
curve.add(new Vector2f(0.0f, 0.0f));
curve.add(new Vector2f(1.0f, 1.0f));
scaleY = new LinearInterpolator(curve, 0, 1);
*/
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "[" + name + "]";
}
/**
* Set the position of this particle source
*
* @param x
* The x coodinate of that this emitter should spawn at
* @param y
* The y coodinate of that this emitter should spawn at
*/
public void setPosition(float x, float y) {
this.x = x;
this.y = y;
}
/**
* Get the base x coordiante for spawning particles
*
* @return The x coordinate for spawning particles
*/
public float getX() {
return x;
}
/**
* Get the base y coordiante for spawning particles
*
* @return The y coordinate for spawning particles
*/
public float getY() {
return y;
}
/**
* @see org.newdawn.slick.particles.ParticleEmitter#isEnabled()
*/
public boolean isEnabled() {
return enabled;
}
/**
* @see org.newdawn.slick.particles.ParticleEmitter#setEnabled(boolean)
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* @see org.newdawn.slick.particles.ParticleEmitter#update(org.newdawn.slick.particles.ParticleSystem,
* int)
*/
public void update(ParticleSystem system, int delta) {
this.engine = system;
if ((wrapUp) ||
(duration.value() != -1 && (timeout < 0)) ||
((emitCount.value() != -1 && (leftToEmit <= 0)))) {
if (particleCount == 0) {
completed = true;
}
}
particleCount = 0;
if (wrapUp) {
return;
}
if (duration.value() != -1) {
if (timeout < 0) {
return;
}
timeout -= delta;
}
if (emitCount.value() != -1) {
if (leftToEmit <= 0) {
return;
}
}
nextSpawn -= delta;
if (nextSpawn < 0) {
nextSpawn = (int) spawnInterval.value();
int count = (int) spawnCount.value();
for (int i = 0; i < count; i++) {
Particle p = system.getNewParticle(this, initialLife.value());
p.setSize(initialSize.value());
p.setPosition(x + xOffset.value(), y + yOffset.value());
p.setVelocity(0, 0, 0);
float dist = initialDistance.value();
float power = speed.value();
if ((dist != 0) || (power != 0)) {
float s = spread.value();
float ang = (s + angularOffset.value() - (spread.value() / 2)) - 90;
float xa = (float) FastTrig.cos(Math.toRadians(ang)) * dist;
float ya = (float) FastTrig.sin(Math.toRadians(ang)) * dist;
p.adjustPosition(xa, ya);
float xv = (float) FastTrig.cos(Math.toRadians(ang));
float yv = (float) FastTrig.sin(Math.toRadians(ang));
p.setVelocity(xv, yv, power * 0.001f);
}
if (image != null) {
p.setImage(image);
}
ColorRecord start = (ColorRecord) colors.get(0);
p.setColor(start.col.r, start.col.g, start.col.b, startAlpha.value() / 255.0f);
p.setUsePoint(usePoints);
p.setOriented(useOriented);
if (emitCount.value() != -1) {
leftToEmit--;
if (leftToEmit <= 0) {
break;
}
}
}
}
}
/**
* @see org.newdawn.slick.particles.ParticleEmitter#updateParticle(org.newdawn.slick.particles.Particle,
* int)
*/
public void updateParticle(Particle particle, int delta) {
particleCount++;
particle.adjustVelocity(windFactor.value() * 0.00005f * delta, gravityFactor.value() * 0.00005f * delta);
float offset = particle.getLife() / particle.getOriginalLife();
float inv = 1 - offset;
float colOffset = 0;
float colInv = 1;
Color startColor = null;
Color endColor = null;
for (int i = 0; i < colors.size() - 1; i++) {
ColorRecord rec1 = (ColorRecord) colors.get(i);
ColorRecord rec2 = (ColorRecord) colors.get(i + 1);
if ((inv >= rec1.pos) && (inv <= rec2.pos)) {
startColor = rec1.col;
endColor = rec2.col;
float step = rec2.pos - rec1.pos;
colOffset = inv - rec1.pos;
colOffset /= step;
colOffset = 1 - colOffset;
colInv = 1 - colOffset;
}
}
/*
if (startColor != null) {
float r = (startColor.r * colOffset) + (endColor.r * colInv);
float g = (startColor.g * colOffset) + (endColor.g * colInv);
float b = (startColor.b * colOffset) + (endColor.b * colInv);
float a;
if (alpha.isActive()) {
a = alpha.getValue(inv) / 255.0f;
} else {
a = ((startAlpha.value() / 255.0f) * offset)
+ ((endAlpha.value() / 255.0f) * inv);
}
particle.setColor(r, g, b, a);
}
if (size.isActive()) {
float s = size.getValue(inv);
particle.setSize(s);
} else {
particle.adjustSize(delta * growthFactor.value() * 0.001f);
}
if (velocity.isActive()) {
particle.setSpeed(velocity.getValue(inv));
}
if (scaleY.isActive()) {
particle.setScaleY(scaleY.getValue(inv));
}
*/
}
/**
* Check if this emitter has completed it's cycle
*
* @return True if the emitter has completed it's cycle
*/
public boolean completed() {
if (engine == null) {
return false;
}
if (duration.value() != -1) {
if (timeout > 0) {
return false;
}
return completed;
}
if (emitCount.value() != -1) {
if (leftToEmit > 0) {
return false;
}
return completed;
}
if (wrapUp) {
return completed;
}
return false;
}
/**
* Cause the emitter to replay it's circle
*/
public void replay() {
reset();
nextSpawn = 0;
leftToEmit = (int) emitCount.value();
timeout = (int) (duration.value());
}
/**
* Release all the particles held by this emitter
*/
public void reset() {
completed = false;
if (engine != null) {
engine.releaseAll(this);
}
}
/**
* Check if the replay has died out - used by the editor
*/
public void replayCheck() {
if (completed()) {
if (engine != null) {
if (engine.getParticleCount() == 0) {
replay();
}
}
}
}
/**
* Create a duplicate of this emitter.
* The duplicate should be added to a ParticleSystem to be used.
* @return a copy if no IOException occurred, null otherwise
*/
public ConfigurableEmitter duplicate() {
return null;
}
/**
* A single element in the colour range of this emitter
*
* @author kevin
*/
public class ColorRecord {
/** The position in the life cycle */
public float pos;
/** The color at this position */
public Color col;
/**
* Create a new record
*
* @param pos
* The position in the life cycle (0 = start, 1 = end)
* @param col
* The color applied at this position
*/
public ColorRecord(float pos, Color col) {
this.pos = pos;
this.col = col;
}
}
/**
* Add a point in the colour cycle
*
* @param pos
* The position in the life cycle (0 = start, 1 = end)
* @param col
* The color applied at this position
*/
public void addColorPoint(float pos, Color col) {
colors.add(new ColorRecord(pos, col));
}
public boolean useAdditive() {
return useAdditive;
}
public boolean isOriented() {
return this.useOriented;
}
public boolean usePoints(ParticleSystem system) {
return (this.usePoints == Particle.INHERIT_POINTS) && (system.usePoints()) ||
(this.usePoints == Particle.USE_POINTS);
}
public Image getImage() {
return image;
}
public void wrapUp() {
wrapUp = true;
}
public void resetState() {
replay();
}
@Override
public ISavable snapshot() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return null;
}
}