Added old sources; initial commit.
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package com.porcupine.color;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* HSV color
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class HSV {
|
||||
|
||||
/** H */
|
||||
public double h;
|
||||
/** S */
|
||||
public double s;
|
||||
/** V */
|
||||
public double v;
|
||||
|
||||
/**
|
||||
* Create black color 0,0,0
|
||||
*/
|
||||
public HSV() {}
|
||||
|
||||
/**
|
||||
* Color from HSV 0-1
|
||||
*
|
||||
* @param h
|
||||
* @param s
|
||||
* @param v
|
||||
*/
|
||||
public HSV(Number h, Number s, Number v) {
|
||||
this.h = h.doubleValue();
|
||||
this.s = s.doubleValue();
|
||||
this.v = v.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return hue 0-1
|
||||
*/
|
||||
public double h() {
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return saturation 0-1
|
||||
*/
|
||||
public double s() {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return value/brightness 0-1
|
||||
*/
|
||||
public double v() {
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color to other color
|
||||
*
|
||||
* @param copied copied color
|
||||
* @return this
|
||||
*/
|
||||
public HSV setTo(HSV copied) {
|
||||
|
||||
h = copied.h;
|
||||
s = copied.s;
|
||||
v = copied.v;
|
||||
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to H,S,V 0-1
|
||||
*
|
||||
* @param h hue
|
||||
* @param s saturation
|
||||
* @param v value
|
||||
* @return this
|
||||
*/
|
||||
public HSV setTo(Number h, Number s, Number v) {
|
||||
this.h = h.doubleValue();
|
||||
this.s = s.doubleValue();
|
||||
this.v = v.doubleValue();
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix numbers out of range 0-1
|
||||
*/
|
||||
public void norm() {
|
||||
h = Calc.clampd(h, 0, 1);
|
||||
s = Calc.clampd(s, 0, 1);
|
||||
v = Calc.clampd(v, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to RGB
|
||||
*
|
||||
* @return RGB representation
|
||||
*/
|
||||
public RGB toRGB() {
|
||||
|
||||
int rgb = Color.HSBtoRGB((float) h, (float) s, (float) v);
|
||||
|
||||
return RGB.fromHex(rgb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make from RGB
|
||||
*
|
||||
* @param color RGB
|
||||
* @return HSV
|
||||
*/
|
||||
public static HSV fromRGB(RGB color) {
|
||||
return color.toHSV();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HSV[" + h + ";" + s + ";" + v + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof HSV)) return false;
|
||||
return ((HSV) obj).h == h && ((HSV) obj).s == s && ((HSV) obj).v == v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Double.valueOf(h).hashCode() ^ Double.valueOf(s).hashCode() ^ Double.valueOf(v).hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public HSV copy() {
|
||||
return new HSV().setTo(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
package com.porcupine.color;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* RGB color
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class RGB {
|
||||
|
||||
/** White */
|
||||
public static final RGB WHITE = new RGB(1, 1, 1);
|
||||
/** Black */
|
||||
public static final RGB BLACK = new RGB(0, 0, 0);
|
||||
/** Red */
|
||||
public static final RGB RED = new RGB(1, 0, 0);
|
||||
/** Lime green */
|
||||
public static final RGB GREEN = new RGB(0, 1, 0);
|
||||
/** Blue */
|
||||
public static final RGB BLUE = new RGB(0, 0, 1);
|
||||
/** Yellow */
|
||||
public static final RGB YELLOW = new RGB(1, 1, 0);
|
||||
/** Purple */
|
||||
public static final RGB PURPLE = new RGB(1, 0, 1);
|
||||
/** Cyan */
|
||||
public static final RGB CYAN = new RGB(0, 1, 1);
|
||||
/** orange */
|
||||
public static final RGB ORANGE = new RGB(1, 0.6, 0);
|
||||
|
||||
/** R */
|
||||
public double r;
|
||||
/** G */
|
||||
public double g;
|
||||
/** B */
|
||||
public double b;
|
||||
/** ALPHA */
|
||||
public double a = 1;
|
||||
|
||||
/**
|
||||
* Create black color 0,0,0
|
||||
*/
|
||||
public RGB() {}
|
||||
|
||||
/**
|
||||
* Get copy with custom alpha
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return copy w/ alpha
|
||||
*/
|
||||
public RGB setAlpha(double alpha) {
|
||||
return copy().setAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* set alpha IP
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return this
|
||||
*/
|
||||
public RGB setAlpha_ip(double alpha) {
|
||||
a = alpha;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copy.
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public RGB copy() {
|
||||
return new RGB(r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copy with alpha multiplied by custom value
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return copy w/ alpha
|
||||
*/
|
||||
public RGB mulAlpha(double alpha) {
|
||||
return copy().mulAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply alpha by given number
|
||||
*
|
||||
* @param alpha alpha multiplier
|
||||
* @return this
|
||||
*/
|
||||
public RGB mulAlpha_ip(double alpha) {
|
||||
a *= alpha;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Color from RGB 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
*/
|
||||
public RGB(Number r, Number g, Number b) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Color from RGB 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @param a alpha
|
||||
*/
|
||||
public RGB(Number r, Number g, Number b, Number a) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = a.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex integer
|
||||
*/
|
||||
public RGB(int hex) {
|
||||
setTo(RGB.fromHex(hex));
|
||||
norm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex integer
|
||||
* @param alpha alpha color
|
||||
*/
|
||||
public RGB(int hex, double alpha) {
|
||||
setTo(RGB.fromHex(hex));
|
||||
a = alpha;
|
||||
norm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Color from other RGB and alpha channel
|
||||
*
|
||||
* @param color other RGB color
|
||||
* @param alpha new alpha channel
|
||||
*/
|
||||
public RGB(RGB color, double alpha) {
|
||||
setTo(color);
|
||||
setAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return red channel 0-1
|
||||
*/
|
||||
public double r() {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return green channel 0-1
|
||||
*/
|
||||
public double g() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return blue channel 0-1
|
||||
*/
|
||||
public double b() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return alpha 0-1
|
||||
*/
|
||||
public double a() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color to other color
|
||||
*
|
||||
* @param copied copied color
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(RGB copied) {
|
||||
|
||||
r = copied.r;
|
||||
g = copied.g;
|
||||
b = copied.b;
|
||||
a = copied.a;
|
||||
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to represent hex color
|
||||
*
|
||||
* @param hex hex integer RRGGBB
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(int hex) {
|
||||
setTo(RGB.fromHex(hex));
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to R,G,B 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @param a alpha
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(Number r, Number g, Number b, Number a) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = a.doubleValue();
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to R,G,B 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(Number r, Number g, Number b) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = 1;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix numbers out of range 0-1
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public RGB norm() {
|
||||
r = Calc.clampd(r, 0, 1);
|
||||
g = Calc.clampd(g, 0, 1);
|
||||
b = Calc.clampd(b, 0, 1);
|
||||
a = Calc.clampd(a, 0, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hex value 0xRRGGBB
|
||||
*
|
||||
* @return hex value RRGGBB
|
||||
*/
|
||||
public int getHex() {
|
||||
int ri = (int) Math.round(r * 255);
|
||||
int gi = (int) Math.round(g * 255);
|
||||
int bi = (int) Math.round(b * 255);
|
||||
return (ri << 16) | (gi << 8) | bi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to HSV
|
||||
*
|
||||
* @return HSV representation
|
||||
*/
|
||||
public HSV toHSV() {
|
||||
float[] hsv = { 0, 0, 0 };
|
||||
Color.RGBtoHSB((int) (r * 255), (int) (g * 255), (int) (b * 255), hsv);
|
||||
return new HSV(hsv[0], hsv[1], hsv[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex RRGGBB
|
||||
* @return the new color
|
||||
*/
|
||||
public static RGB fromHex(int hex) {
|
||||
int bi = hex & 0xff;
|
||||
int gi = (hex >> 8) & 0xff;
|
||||
int ri = (hex >> 16) & 0xff;
|
||||
return new RGB(ri / 255D, gi / 255D, bi / 255D);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make from HSV
|
||||
*
|
||||
* @param color HSV color
|
||||
* @return RGB
|
||||
*/
|
||||
public static RGB fromHSV(HSV color) {
|
||||
return color.toRGB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RGB[" + r + ";" + g + ";" + b + ";" + a + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof RGB)) return false;
|
||||
return ((RGB) obj).r == r && ((RGB) obj).g == g && ((RGB) obj).b == b && ((RGB) obj).a == a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Double.valueOf(r).hashCode() ^ Double.valueOf(g).hashCode() ^ Double.valueOf(b).hashCode() ^ Double.valueOf(a).hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
package com.porcupine.coord;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Coordinate class, object with three or two double coordinates.<br>
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Coord {
|
||||
/** Zero Coord */
|
||||
public static final Coord ZERO = new Coord(0, 0);
|
||||
/** RNG */
|
||||
protected static Random rand = new Random();
|
||||
/** X coordinate */
|
||||
public double x = 0;
|
||||
|
||||
/** Y coordinate */
|
||||
public double y = 0;
|
||||
|
||||
/** Z coordinate */
|
||||
public double z = 0;
|
||||
|
||||
/**
|
||||
* Create zero coord
|
||||
*/
|
||||
public Coord() {}
|
||||
|
||||
/**
|
||||
* Create 2D coord
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
*/
|
||||
public Coord(Number x, Number y) {
|
||||
setTo(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create 3D coord
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
*/
|
||||
public Coord(Number x, Number y, Number z) {
|
||||
setTo(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create coord as a copy of another
|
||||
*
|
||||
* @param copied copied coord
|
||||
*/
|
||||
public Coord(Coord copied) {
|
||||
this.x = copied.x;
|
||||
this.y = copied.y;
|
||||
this.z = copied.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert X and Y coordinates of this coord to a new CoordI.
|
||||
*
|
||||
* @return the new CoordI
|
||||
*/
|
||||
public CoordI toCoordI() {
|
||||
return new CoordI((int) Math.round(x), (int) Math.round(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random coord (gaussian)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Coord random(double max) {
|
||||
return new Coord(Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2), Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2),
|
||||
Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random coord (min-max)
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return new coord
|
||||
*/
|
||||
public static Coord random(double min, double max) {
|
||||
return new Coord((rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)), (rand.nextBoolean() ? -1 : 1)
|
||||
* (min + rand.nextDouble() * (max - min)), (rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)));
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord random_offset_ip(double max) {
|
||||
return add(random(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord random_offset_ip(double min, double max) {
|
||||
add(random(min, max));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return offset coord
|
||||
*/
|
||||
public Coord random_offset(double max) {
|
||||
Coord r = random(1);
|
||||
Vec v = new Vec(r);
|
||||
v.norm_ip(0.00001 + rand.nextDouble() * max);
|
||||
return copy().add_ip(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return offset coord
|
||||
*/
|
||||
public Coord random_offset(double min, double max) {
|
||||
return copy().add_ip(random(min, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public double x() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public double y() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public double z() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public double xd() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public double yd() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public double zd() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public float xf() {
|
||||
return (float) x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public float yf() {
|
||||
return (float) y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public float zf() {
|
||||
return (float) z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public int xi() {
|
||||
return (int) Math.round(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public int yi() {
|
||||
return (int) Math.round(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public int zi() {
|
||||
return (int) Math.round(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3D coordinates to
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Number x, Number y, Number z) {
|
||||
this.x = x.doubleValue();
|
||||
this.y = y.doubleValue();
|
||||
this.z = z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2D coordinates to
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Number x, Number y) {
|
||||
setTo(x, y, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set coordinates to match other coord
|
||||
*
|
||||
* @param copied coord whose coordinates are used
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Coord copied) {
|
||||
setTo(copied.x, copied.y, copied.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set X coordinate in place
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setX_ip(Number x) {
|
||||
this.x = x.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Y coordinate in place
|
||||
*
|
||||
* @param y y coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setY_ip(Number y) {
|
||||
this.y = y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Z coordinate in place
|
||||
*
|
||||
* @param z z coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setZ_ip(Number z) {
|
||||
this.z = z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set X coordinate in a copy
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setX(Number x) {
|
||||
return copy().setX_ip(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Y coordinate in a copy
|
||||
*
|
||||
* @param y y coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setY(Number y) {
|
||||
return copy().setY_ip(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Z coordinate in a copy
|
||||
*
|
||||
* @param z z coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setZ(Number z) {
|
||||
return copy().setZ_ip(z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by 3D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Number x, Number y, Number z) {
|
||||
return copy().sub_ip(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by 2D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Number x, Number y) {
|
||||
return copy().sub_ip(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by vector
|
||||
*
|
||||
* @param vec offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Coord vec) {
|
||||
return copy().sub_ip(vec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by 3D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Number x, Number y, Number z) {
|
||||
this.x -= x.doubleValue();
|
||||
this.y -= y.doubleValue();
|
||||
this.z -= z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by 2D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Number x, Number y) {
|
||||
this.x -= x.doubleValue();
|
||||
this.y -= y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by vector in place
|
||||
*
|
||||
* @param vec offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Coord vec) {
|
||||
this.x -= vec.x;
|
||||
this.y -= vec.y;
|
||||
this.z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy offset by 3D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Number x, Number y, Number z) {
|
||||
return copy().add_ip(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy offset by 2D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Number x, Number y) {
|
||||
return copy().add_ip(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy offset by vector
|
||||
*
|
||||
* @param vec offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Coord vec) {
|
||||
return copy().add_ip(vec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by 3D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Number x, Number y, Number z) {
|
||||
this.x += x.doubleValue();
|
||||
this.y += y.doubleValue();
|
||||
this.z += z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by 2D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Number x, Number y) {
|
||||
this.x += x.doubleValue();
|
||||
this.y += y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset by vector in place
|
||||
*
|
||||
* @param vec offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Coord vec) {
|
||||
this.x += vec.x;
|
||||
this.y += vec.y;
|
||||
this.z += vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return copy of this vector
|
||||
*/
|
||||
public Coord copy() {
|
||||
return new Coord(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get distance to other point
|
||||
*
|
||||
* @param point other point
|
||||
* @return distance in units
|
||||
*/
|
||||
public double distTo(Coord point) {
|
||||
return Math.sqrt((point.x - x) * (point.x - x) + (point.y - y) * (point.y - y) + (point.z - z) * (point.z - z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create vector from this point to other point
|
||||
*
|
||||
* @param point second point
|
||||
* @return vector
|
||||
*/
|
||||
public Vec vecTo(Coord point) {
|
||||
return (Vec) (new Vec(point)).add(new Vec(this).neg());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distance to other point
|
||||
*
|
||||
* @param a point a
|
||||
* @param b point b
|
||||
* @return distance in units
|
||||
*/
|
||||
public static double dist(Coord a, Coord b) {
|
||||
return a.distTo(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get middle of line to other point
|
||||
*
|
||||
* @param other other point
|
||||
* @return middle
|
||||
*/
|
||||
public Coord midTo(Coord other) {
|
||||
return add(vecTo(other).scale(0.5));
|
||||
}
|
||||
|
||||
private Coord last;
|
||||
private Vec offs;
|
||||
|
||||
/**
|
||||
* Store current value as LAST
|
||||
*/
|
||||
public void pushLast() {
|
||||
if (last == null) last = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
last.setTo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply coordinates change for delta time
|
||||
*/
|
||||
public void update() {
|
||||
if (last == null) last = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
offs = last.vecTo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coordinate at delta time since LAST
|
||||
*
|
||||
* @param delta delta time 0-1
|
||||
* @return delta pos
|
||||
*/
|
||||
public Coord getDelta(double delta) {
|
||||
if (last == null) last = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
return new Coord(this.add(offs.scale(delta)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[ " + x + " ; " + y + " ; " + z + " ]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!obj.getClass().isAssignableFrom(Vec.class)) return false;
|
||||
Vec other = (Vec) obj;
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Double.valueOf(x).hashCode() ^ Double.valueOf(y).hashCode() ^ Double.valueOf(z).hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply by number
|
||||
*
|
||||
* @param d number
|
||||
* @return multiplied copy
|
||||
*/
|
||||
public Coord mul(double d) {
|
||||
return copy().mul_ip(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply by number in place
|
||||
*
|
||||
* @param d multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Coord mul_ip(double d) {
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply coords by number
|
||||
*
|
||||
* @param xd x multiplier
|
||||
* @param yd y multiplier
|
||||
* @param zd z multiplier
|
||||
* @return multiplied copy
|
||||
*/
|
||||
public Coord mul(double xd, double yd, double zd) {
|
||||
return copy().mul_ip(xd, yd, zd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply coords by number in place
|
||||
*
|
||||
* @param xd x multiplier
|
||||
* @param yd y multiplier
|
||||
* @param zd z multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Coord mul_ip(double xd, double yd, double zd) {
|
||||
x *= xd;
|
||||
y *= yd;
|
||||
z *= zd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide by number in place
|
||||
*
|
||||
* @param d number to divide by
|
||||
* @return this
|
||||
*/
|
||||
public Coord div_ip(double d) {
|
||||
x /= d;
|
||||
y /= d;
|
||||
z /= d;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copy divided by number
|
||||
*
|
||||
* @param d number to divide by
|
||||
* @return divided copy
|
||||
*/
|
||||
public Coord div(double d) {
|
||||
return copy().div_ip(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Coord round_ip() {
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
z = Math.round(z);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy with rounded coords
|
||||
*
|
||||
* @return rounded copy
|
||||
*/
|
||||
public Coord round() {
|
||||
return copy().round_ip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this rectangle in inside a rectangular zone
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @return is inside
|
||||
*/
|
||||
public boolean isInRect(Coord min, Coord max) {
|
||||
return (x >= min.x && x <= max.x) && (y >= min.y && y <= max.y) && (z >= min.z && z <= max.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.porcupine.coord;
|
||||
|
||||
|
||||
/**
|
||||
* Simple integer coordinate class<br>
|
||||
* Unlike Coord, this is suitable for using in array indices etc.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class CoordI {
|
||||
|
||||
/** X coordinate */
|
||||
public int x = 0;
|
||||
/** Y coordinate */
|
||||
public int y = 0;
|
||||
|
||||
/**
|
||||
* Integer 2D Coord
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
*/
|
||||
public CoordI(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create CoordI as copy of other
|
||||
*
|
||||
* @param other coord to copy
|
||||
*/
|
||||
public CoordI(CoordI other) {
|
||||
setTo(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public CoordI copy() {
|
||||
return new CoordI(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set coords to
|
||||
*
|
||||
* @param x x coord to set
|
||||
* @param y y coord to set
|
||||
*/
|
||||
public void setTo(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to coords from other coord
|
||||
*
|
||||
* @param other source coord
|
||||
*/
|
||||
public void setTo(CoordI other) {
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to double Coord
|
||||
*
|
||||
* @return coord with X and y from this CoordI
|
||||
*/
|
||||
public Coord toCoord() {
|
||||
return new Coord(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[ " + x + " ; " + y + " ]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (obj instanceof CoordI) return ((CoordI) obj).x == x && ((CoordI) obj).y == y;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return x ^ y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add other coordI coordinates in place
|
||||
*
|
||||
* @param move coordI to add
|
||||
* @return this
|
||||
*/
|
||||
public CoordI add_ip(CoordI move) {
|
||||
x += move.x;
|
||||
y += move.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract other coordI coordinates in place
|
||||
*
|
||||
* @param move coordI to subtract
|
||||
* @return this
|
||||
*/
|
||||
public CoordI sub_ip(CoordI move) {
|
||||
x -= move.x;
|
||||
y -= move.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Middle of this and other coordinate, rounded to CoordI - integers
|
||||
*
|
||||
* @param other other coordI
|
||||
* @return middle CoordI
|
||||
*/
|
||||
public CoordI midTo(CoordI other) {
|
||||
return new CoordI((x + other.x) / 2, (y + other.y) / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract x,y in a copy
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return copy subtracted
|
||||
*/
|
||||
public CoordI sub(int x, int y) {
|
||||
return copy().sub_ip(new CoordI(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract other coordI coordinates in a copy
|
||||
*
|
||||
* @param other coordI to subtract
|
||||
* @return copy subtracted
|
||||
*/
|
||||
public CoordI sub(CoordI other) {
|
||||
return copy().sub_ip(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add other coordI coordinates in a copy
|
||||
*
|
||||
* @param other coordI to add
|
||||
* @return copy modified
|
||||
*/
|
||||
public CoordI add(CoordI other) {
|
||||
return copy().add_ip(other);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package com.porcupine.coord;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Rectangle determined by two coordinates - min and max.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Rect {
|
||||
|
||||
/** Lowest coordinates xy */
|
||||
protected Coord min = new Coord();
|
||||
/** Highest coordinates xy */
|
||||
protected Coord max = new Coord();
|
||||
|
||||
/**
|
||||
* New Rect
|
||||
*/
|
||||
public Rect() {
|
||||
this(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* New Rect
|
||||
*
|
||||
* @param x1 lower x
|
||||
* @param y1 lower y
|
||||
* @param x2 upper x
|
||||
* @param y2 upper y
|
||||
*/
|
||||
public Rect(double x1, double y1, double x2, double y2) {
|
||||
setTo(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/**
|
||||
* New rect of two coords
|
||||
*
|
||||
* @param c1 coord 1
|
||||
* @param c2 coord 2
|
||||
*/
|
||||
public Rect(Coord c1, Coord c2) {
|
||||
this(c1.x, c1.y, c2.x, c2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* New rect as a copy of other rect
|
||||
*
|
||||
* @param r other rect
|
||||
*/
|
||||
public Rect(Rect r) {
|
||||
this(r.min.x, r.min.y, r.max.x, r.max.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public Rect copy() {
|
||||
return new Rect(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset in place (add)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return this
|
||||
*/
|
||||
public Rect add_ip(Vec move) {
|
||||
min.add_ip(move);
|
||||
max.add_ip(move);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offset copy (add)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect add(Vec move) {
|
||||
return copy().add_ip(move);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset in place (subtract)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return this
|
||||
*/
|
||||
public Rect sub_ip(Vec move) {
|
||||
min.sub_ip(move);
|
||||
max.sub_ip(move);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offset copy (subtract)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect sub(Vec move) {
|
||||
return copy().sub_ip(move);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return lowest coordinates xy
|
||||
*/
|
||||
public Coord getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return highjest coordinates xy
|
||||
*/
|
||||
public Coord getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if point is inside this rectangle
|
||||
*
|
||||
* @param point point to test
|
||||
* @return is inside
|
||||
*/
|
||||
public boolean isInside(Coord point) {
|
||||
return Calc.inRange(point.x, min.x, max.x) && Calc.inRange(point.y, min.y, max.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size (width, height) as (x,y)
|
||||
*
|
||||
* @return coord of width,height
|
||||
*/
|
||||
public Coord getSize() {
|
||||
return new Coord(Math.abs(min.x - max.x), Math.abs(min.y - max.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rect center
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenter() {
|
||||
return min.midTo(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get center of the lower edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterDown() {
|
||||
return new Coord((max.x + min.x) / 2, min.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get center of the left edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterLeft() {
|
||||
return new Coord(min.x, (max.y + min.y) / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get center of the top edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterTop() {
|
||||
return new Coord((max.x + min.x) / 2, max.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get center of the right edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterRight() {
|
||||
return new Coord(max.x, (max.y + min.y) / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return lower x
|
||||
*/
|
||||
public double x1() {
|
||||
return min.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return lower y
|
||||
*/
|
||||
public double y1() {
|
||||
return min.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return upper x
|
||||
*/
|
||||
public double x2() {
|
||||
return max.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return upper y
|
||||
*/
|
||||
public double y2() {
|
||||
return max.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to other rect's coordinates
|
||||
*
|
||||
* @param r other rect
|
||||
*/
|
||||
public void setTo(Rect r) {
|
||||
min.setTo(r.min);
|
||||
max.setTo(r.max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to coordinates
|
||||
*
|
||||
* @param x1 lower x
|
||||
* @param y1 lower y
|
||||
* @param x2 upper x
|
||||
* @param y2 upper y
|
||||
*/
|
||||
public void setTo(double x1, double y1, double x2, double y2) {
|
||||
min.x = Calc.min(x1, x2);
|
||||
min.y = Calc.min(y1, y2);
|
||||
max.x = Calc.max(x1, x2);
|
||||
max.y = Calc.max(y1, y2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "rect{ " + min + " - " + max + " }";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add X and Y to all coordinates in place
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return this
|
||||
*/
|
||||
public Rect add_ip(double x, double y) {
|
||||
return add_ip(new Vec(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract X and Y from all coordinates in place
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Rect sub_ip(double x, double y) {
|
||||
return sub_ip(new Vec(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add X and Y to all coordinates in a copy
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return copy changed
|
||||
*/
|
||||
public Rect add(double x, double y) {
|
||||
return add(new Vec(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract X and Y from all coordinates in a copy
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return copy changed
|
||||
*/
|
||||
public Rect sub(double x, double y) {
|
||||
return sub(new Vec(x, y));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package com.porcupine.coord;
|
||||
|
||||
|
||||
/**
|
||||
* Vector in 2D/3D space.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Vec extends Coord {
|
||||
|
||||
/** Zero vector */
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Vec ZERO = new Vec(0, 0, 0);
|
||||
|
||||
/**
|
||||
* Create zero vector
|
||||
*/
|
||||
public Vec() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create 2D vector
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
*/
|
||||
public Vec(Number x, Number y) {
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create 3D vector
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
*/
|
||||
public Vec(Number x, Number y, Number z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create vector as a copy of another
|
||||
*
|
||||
* @param copied copied vector
|
||||
*/
|
||||
public Vec(Coord copied) {
|
||||
super(copied);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1)
|
||||
*
|
||||
* @return negated coordinate
|
||||
*/
|
||||
public Vec neg() {
|
||||
return copy().neg_ip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1), in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Vec neg_ip() {
|
||||
scale_ip(-1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply all coordinates by factor; scalar multiplication
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Vec scale(double factor) {
|
||||
return copy().scale_ip(factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply all coordinates by factor, in place
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Vec scale_ip(double factor) {
|
||||
return (Vec) mul_ip(factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Vec cross(Vec vec) {
|
||||
return copy().cross_ip(vec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication; in place
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return this
|
||||
*/
|
||||
public Vec cross_ip(Vec vec) {
|
||||
setTo(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dot product
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return dot product
|
||||
*/
|
||||
public double dot(Vec vec) {
|
||||
return x * vec.x + y * vec.y + z * vec.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vector size
|
||||
*
|
||||
* @return vector size in units
|
||||
*/
|
||||
public double size() {
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale vector to given size
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Vec norm(double size) {
|
||||
return copy().norm_ip(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale vector to given size, in place
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Vec norm_ip(double size) {
|
||||
if (size() == 0) {
|
||||
z = -1;
|
||||
}
|
||||
if (size == 0) {
|
||||
setTo(0, 0, 0);
|
||||
return this;
|
||||
}
|
||||
double k = size / size();
|
||||
scale_ip(k);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// STATIC
|
||||
|
||||
/**
|
||||
* Get vector size
|
||||
*
|
||||
* @param vec vector to get size of
|
||||
* @return size in units
|
||||
*/
|
||||
public static double size(Vec vec) {
|
||||
return vec.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dot product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return dot product
|
||||
*/
|
||||
public static double dot(Vec a, Vec b) {
|
||||
return a.dot(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cross product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return cross product
|
||||
*/
|
||||
public static Vec cross(Vec a, Vec b) {
|
||||
return a.cross(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale vector
|
||||
*
|
||||
* @param a vector
|
||||
* @param scale
|
||||
* @return scaled copy
|
||||
*/
|
||||
public static Vec scale(Vec a, double scale) {
|
||||
return a.scale(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec copy() {
|
||||
return new Vec(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random coord (gaussian)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Vec random(double max) {
|
||||
return new Vec(Coord.random(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random coord (min-max)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Vec random(double min, double max) {
|
||||
return new Vec(Coord.random(min, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return this
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset_ip(double max) {
|
||||
return (Vec) super.random_offset_ip(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return this
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset_ip(double min, double max) {
|
||||
return (Vec) super.random_offset_ip(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return offset coord
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset(double max) {
|
||||
return (Vec) super.random_offset(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return offset coord
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset(double min, double max) {
|
||||
return (Vec) super.random_offset(min, max);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractIonList<T> extends ArrayList<T> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IOException {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
|
||||
if (b == IonMarks.ENTRY) {
|
||||
T value = (T) Ion.readObject(in);
|
||||
add(value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in AbstractIonList: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IOException {
|
||||
for (T entry : this) {
|
||||
if (entry instanceof IonizableOptional && !((IonizableOptional) entry).ionShouldSave()) continue;
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
Ion.writeObject(out, entry);
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in) {}
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out) {}
|
||||
|
||||
|
||||
@Override
|
||||
public abstract byte ionMark();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <V>
|
||||
*/
|
||||
public abstract class AbstractIonMap<V> extends LinkedHashMap<String, V> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(String key, V value) {
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IOException {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
if (b == IonMarks.ENTRY) {
|
||||
String key = StreamUtils.readStringBytes(in);
|
||||
V value = (V) Ion.readObject(in);
|
||||
put(key, value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IOException {
|
||||
for (java.util.Map.Entry<String, V> entry : entrySet()) {
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
StreamUtils.writeStringBytes(out, entry.getKey());
|
||||
Ion.writeObject(out, entry.getValue());
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in) {}
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out) {}
|
||||
|
||||
@Override
|
||||
public byte ionMark() {
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Universal data storage system
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Ion {
|
||||
|
||||
/** Ionizables<Mark, Class> */
|
||||
private static Map<Byte, Class<?>> customIonizables = new HashMap<Byte, Class<?>>();
|
||||
|
||||
// register default ionizables
|
||||
static {
|
||||
registerIonizable(IonMarks.MAP, IonMap.class);
|
||||
registerIonizable(IonMarks.LIST, IonList.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new Ionizable for direct reconstructing.
|
||||
*
|
||||
* @param mark byte mark to be used, see {@link IonMarks} for reference.
|
||||
* @param objClass class of the registered Ionizable
|
||||
*/
|
||||
public static void registerIonizable(byte mark, Class<?> objClass) {
|
||||
if (customIonizables.containsKey(mark)) {
|
||||
throw new RuntimeException("IonMark " + mark + " is already used @ " + objClass.getSimpleName());
|
||||
}
|
||||
customIonizables.put(mark, objClass);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file path
|
||||
* @return the loaded object
|
||||
*/
|
||||
public static Object fromFile(String file) {
|
||||
return fromFile(new File(file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file
|
||||
* @return the loaded object
|
||||
*/
|
||||
public static Object fromFile(File file) {
|
||||
try {
|
||||
InputStream in = new FileInputStream(file);
|
||||
|
||||
Object obj = fromStream(in);
|
||||
|
||||
in.close();
|
||||
return obj;
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("Could not find ION file " + file);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Ion object from stream.
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
*/
|
||||
public static Object fromStream(InputStream in) {
|
||||
try {
|
||||
return readObject(in);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
*/
|
||||
public static void toFile(String path, Object obj) {
|
||||
toFile(new File(path), obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
*/
|
||||
public static void toFile(File path, Object obj) {
|
||||
try {
|
||||
String f = path.toString();
|
||||
File dir = new File(f.substring(0, f.lastIndexOf(File.separator)));
|
||||
|
||||
dir.mkdirs();
|
||||
|
||||
OutputStream out = new FileOutputStream(path);
|
||||
|
||||
toStream(out, obj);
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Ion object to output stream.
|
||||
*
|
||||
* @param out output stream *
|
||||
* @param obj object to store
|
||||
*/
|
||||
public static void toStream(OutputStream out, Object obj) {
|
||||
try {
|
||||
writeObject(out, obj);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read single ionizable or primitive object from input stream
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
*/
|
||||
public static Object readObject(InputStream in) {
|
||||
try {
|
||||
int bi = in.read();
|
||||
if (bi == -1) throw new RuntimeException("Unexpected end of stream.");
|
||||
byte b = (byte) bi;
|
||||
if (customIonizables.containsKey(b)) {
|
||||
Ionizable ion = ((Ionizable) customIonizables.get(b).newInstance());
|
||||
ion.ionRead(in);
|
||||
return ion;
|
||||
}
|
||||
|
||||
switch (b) {
|
||||
case IonMarks.BOOLEAN:
|
||||
return StreamUtils.readBoolean(in);
|
||||
case IonMarks.BYTE:
|
||||
return StreamUtils.readByte(in);
|
||||
case IonMarks.CHAR:
|
||||
return StreamUtils.readChar(in);
|
||||
case IonMarks.SHORT:
|
||||
return StreamUtils.readShort(in);
|
||||
case IonMarks.INT:
|
||||
return StreamUtils.readInt(in);
|
||||
case IonMarks.LONG:
|
||||
return StreamUtils.readLong(in);
|
||||
case IonMarks.FLOAT:
|
||||
return StreamUtils.readFloat(in);
|
||||
case IonMarks.DOUBLE:
|
||||
return StreamUtils.readDouble(in);
|
||||
case IonMarks.STRING:
|
||||
String s = StreamUtils.readString(in);
|
||||
return s;
|
||||
default:
|
||||
throw new RuntimeException("Invalid Ion mark " + Integer.toHexString(bi));
|
||||
}
|
||||
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write single ionizable or primitive object to output stream
|
||||
*
|
||||
* @param out output stream
|
||||
* @param obj stored object
|
||||
*/
|
||||
public static void writeObject(OutputStream out, Object obj) {
|
||||
try {
|
||||
if (obj instanceof Ionizable) {
|
||||
out.write(((Ionizable) obj).ionMark());
|
||||
((Ionizable) obj).ionWrite(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Boolean) {
|
||||
out.write(IonMarks.BOOLEAN);
|
||||
StreamUtils.writeBoolean(out, (Boolean) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Byte) {
|
||||
out.write(IonMarks.BYTE);
|
||||
StreamUtils.writeByte(out, (Byte) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Character) {
|
||||
out.write(IonMarks.CHAR);
|
||||
StreamUtils.writeChar(out, (Character) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Short) {
|
||||
out.write(IonMarks.SHORT);
|
||||
StreamUtils.writeShort(out, (Short) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Integer) {
|
||||
out.write(IonMarks.INT);
|
||||
StreamUtils.writeInt(out, (Integer) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Long) {
|
||||
out.write(IonMarks.LONG);
|
||||
StreamUtils.writeLong(out, (Long) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Float) {
|
||||
out.write(IonMarks.FLOAT);
|
||||
StreamUtils.writeFloat(out, (Float) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Double) {
|
||||
out.write(IonMarks.DOUBLE);
|
||||
StreamUtils.writeDouble(out, (Double) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
out.write(IonMarks.STRING);
|
||||
StreamUtils.writeString(out, (String) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException(Calc.className(obj) + " can't be stored to Ion storage.");
|
||||
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class IonList extends ArrayList<Object> implements Ionizable {
|
||||
|
||||
public boolean getBoolean(int index) {
|
||||
return (Boolean) get(index);
|
||||
}
|
||||
|
||||
public boolean getBool(int index) {
|
||||
return (Boolean) get(index);
|
||||
}
|
||||
|
||||
public byte getByte(int index) {
|
||||
return (Byte) get(index);
|
||||
}
|
||||
|
||||
public char getChar(int index) {
|
||||
return (Character) get(index);
|
||||
}
|
||||
|
||||
public char getCharacter(int index) {
|
||||
return (Character) get(index);
|
||||
}
|
||||
|
||||
public short getShort(int index) {
|
||||
return (Short) get(index);
|
||||
}
|
||||
|
||||
public int getInteger(int index) {
|
||||
return (Integer) get(index);
|
||||
}
|
||||
|
||||
public int getInt(int index) {
|
||||
return (Integer) get(index);
|
||||
}
|
||||
|
||||
public long getLong(int index) {
|
||||
return (Long) get(index);
|
||||
}
|
||||
|
||||
public float getFloat(int index) {
|
||||
return (Float) get(index);
|
||||
}
|
||||
|
||||
public double getDouble(int index) {
|
||||
return (Double) get(index);
|
||||
}
|
||||
|
||||
public String getString(int index) {
|
||||
return (String) get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
return super.get(index);
|
||||
}
|
||||
|
||||
public void addBoolean(boolean num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addBool(boolean num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addByte(int num) {
|
||||
add((byte) num);
|
||||
}
|
||||
|
||||
public void addChar(char num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addShort(int num) {
|
||||
add((short) num);
|
||||
}
|
||||
|
||||
public void addInteger(int num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addInt(int num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addLong(long num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addFloat(double num) {
|
||||
add((float) num);
|
||||
}
|
||||
|
||||
public void addDouble(double num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
public void addString(String num) {
|
||||
add(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IOException {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
if (b == IonMarks.ENTRY) {
|
||||
Object value = Ion.readObject(in);
|
||||
add(value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in IonList: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IOException {
|
||||
for (Object entry : this) {
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
Ion.writeObject(out, entry);
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte ionMark() {
|
||||
return IonMarks.LIST;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class IonMap extends LinkedHashMap<String, Object> implements Ionizable {
|
||||
|
||||
public boolean getBoolean(String key) {
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
public boolean getBool(String key) {
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return (Byte) get(key);
|
||||
}
|
||||
|
||||
public char getChar(String key) {
|
||||
return (Character) get(key);
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return (Short) get(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return (Integer) get(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return (Long) get(key);
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return (Float) get(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return (Double) get(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return (String) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(Object arg0) {
|
||||
return super.get(arg0);
|
||||
}
|
||||
|
||||
public void putBoolean(String key, boolean num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putBool(String key, boolean num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putByte(String key, int num) {
|
||||
put(key, (byte) num);
|
||||
}
|
||||
|
||||
public void putChar(String key, char num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putCharacter(String key, char num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putShort(String key, int num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putInt(String key, int num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putInteger(String key, int num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putLong(String key, long num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putFloat(String key, double num) {
|
||||
put(key, (float) num);
|
||||
}
|
||||
|
||||
public void putDouble(String key, double num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
public void putString(String key, String num) {
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IOException {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
if (b == IonMarks.ENTRY) {
|
||||
String key = StreamUtils.readStringBytes(in);
|
||||
Object value = Ion.readObject(in);
|
||||
put(key, value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IOException {
|
||||
for (Entry<String, Object> entry : entrySet()) {
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
StreamUtils.writeStringBytes(out, entry.getKey());
|
||||
Ion.writeObject(out, entry.getValue());
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte ionMark() {
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Byte marks used to structure data in Ion files.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonMarks {
|
||||
|
||||
/** Null value */
|
||||
public static final byte NULL = 0;
|
||||
|
||||
/** Boolean value */
|
||||
public static final byte BOOLEAN = 1;
|
||||
|
||||
/** Byte value */
|
||||
public static final byte BYTE = 2;
|
||||
|
||||
/** Character value */
|
||||
public static final byte CHAR = 3;
|
||||
|
||||
/** Short value */
|
||||
public static final byte SHORT = 4;
|
||||
|
||||
/** Integer value */
|
||||
public static final byte INT = 5;
|
||||
|
||||
/** Long value */
|
||||
public static final byte LONG = 6;
|
||||
|
||||
/** Float value */
|
||||
public static final byte FLOAT = 7;
|
||||
|
||||
/** Double value */
|
||||
public static final byte DOUBLE = 8;
|
||||
|
||||
/** String value */
|
||||
public static final byte STRING = 9;
|
||||
|
||||
/** List value (begin) - contains entries, ends with END */
|
||||
public static final byte LIST = 10;
|
||||
|
||||
/** Map value (begin) - contains entries, ends with END */
|
||||
public static final byte MAP = 11;
|
||||
|
||||
/**
|
||||
* List / Map entry<br>
|
||||
* In list directly followed by entry value. In map followed by (string) key
|
||||
* and the entry value.
|
||||
*/
|
||||
public static final byte ENTRY = 12;
|
||||
|
||||
/** End of List / Map */
|
||||
public static final byte END = 13;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Object that can be saved to and loaded from Ion file.<br>
|
||||
* All classes implementing Ionizable must be registered to {@link Ion} using
|
||||
* Ion.registerIonizable(obj.class).
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface Ionizable {
|
||||
/**
|
||||
* Load data from the input stream. Mark has already been read, begin
|
||||
* reading right after it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @throws IOException at IO error
|
||||
*/
|
||||
public void ionRead(InputStream in) throws IOException;
|
||||
|
||||
/**
|
||||
* Store data to output stream. mark has already been written, begin right
|
||||
* after it.
|
||||
*
|
||||
* @param out Output stream
|
||||
* @throws IOException at IO error
|
||||
*/
|
||||
public void ionWrite(OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Get Ion mark byte.
|
||||
*
|
||||
* @return Ion mark byte.
|
||||
*/
|
||||
public byte ionMark();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Optional ionizable
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface IonizableOptional extends Ionizable {
|
||||
/**
|
||||
* Get if this ionizable should be saved to a list
|
||||
*
|
||||
* @return should save
|
||||
*/
|
||||
public boolean ionShouldSave();
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.porcupine.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities to store and load objects to streams.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
|
||||
@SuppressWarnings("javadoc")
|
||||
public class StreamUtils {
|
||||
|
||||
private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8);
|
||||
private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8);
|
||||
private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8);
|
||||
private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8);
|
||||
private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8);
|
||||
private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8);
|
||||
|
||||
private static byte[] ai = new byte[Integer.SIZE / 8];
|
||||
private static byte[] ad = new byte[Double.SIZE / 8];
|
||||
private static byte[] af = new byte[Float.SIZE / 8];
|
||||
private static byte[] ac = new byte[Character.SIZE / 8];
|
||||
private static byte[] al = new byte[Long.SIZE / 8];
|
||||
private static byte[] as = new byte[Short.SIZE / 8];
|
||||
|
||||
// CONVERSIONS
|
||||
|
||||
private static byte[] convBool(boolean bool) {
|
||||
return new byte[] { (byte) (bool ? 1 : 0) };
|
||||
}
|
||||
|
||||
private static byte[] convByte(byte num) {
|
||||
return new byte[] { num };
|
||||
}
|
||||
|
||||
private static byte[] convChar(char num) {
|
||||
bc.clear();
|
||||
bc.putChar(num);
|
||||
return bc.array();
|
||||
}
|
||||
|
||||
private static byte[] convShort(short num) {
|
||||
bs.clear();
|
||||
bs.putShort(num);
|
||||
return bs.array();
|
||||
}
|
||||
|
||||
private static byte[] convInt(int num) {
|
||||
bi.clear();
|
||||
bi.putInt(num);
|
||||
return bi.array();
|
||||
}
|
||||
|
||||
private static byte[] convLong(long num) {
|
||||
bl.clear();
|
||||
bl.putLong(num);
|
||||
return bl.array();
|
||||
}
|
||||
|
||||
private static byte[] convFloat(float num) {
|
||||
bf.clear();
|
||||
bf.putFloat(num);
|
||||
return bf.array();
|
||||
}
|
||||
|
||||
private static byte[] convDouble(double num) {
|
||||
bd.clear();
|
||||
bd.putDouble(num);
|
||||
return bd.array();
|
||||
}
|
||||
|
||||
private static byte[] convString(String str) {
|
||||
char[] chars = str.toCharArray();
|
||||
|
||||
ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8));
|
||||
for (char c : chars) {
|
||||
bstr.putChar(c);
|
||||
}
|
||||
|
||||
bstr.putChar((char) 0);
|
||||
|
||||
return bstr.array();
|
||||
}
|
||||
|
||||
private static byte[] convString_b(String str) {
|
||||
char[] chars = str.toCharArray();
|
||||
ByteBuffer bstr = ByteBuffer.allocate((Byte.SIZE / 8) * chars.length + 1);
|
||||
for (char c : chars) {
|
||||
bstr.put((byte) c);
|
||||
}
|
||||
bstr.put((byte) 0);
|
||||
|
||||
return bstr.array();
|
||||
}
|
||||
|
||||
public static void writeBoolean(OutputStream out, boolean num) throws IOException {
|
||||
out.write(convBool(num));
|
||||
}
|
||||
|
||||
public static void writeByte(OutputStream out, byte num) throws IOException {
|
||||
out.write(convByte(num));
|
||||
}
|
||||
|
||||
public static void writeChar(OutputStream out, char num) throws IOException {
|
||||
out.write(convChar(num));
|
||||
}
|
||||
|
||||
public static void writeShort(OutputStream out, short num) throws IOException {
|
||||
out.write(convShort(num));
|
||||
}
|
||||
|
||||
public static void writeInt(OutputStream out, int num) throws IOException {
|
||||
out.write(convInt(num));
|
||||
}
|
||||
|
||||
public static void writeLong(OutputStream out, long num) throws IOException {
|
||||
out.write(convLong(num));
|
||||
}
|
||||
|
||||
public static void writeFloat(OutputStream out, float num) throws IOException {
|
||||
out.write(convFloat(num));
|
||||
}
|
||||
|
||||
public static void writeDouble(OutputStream out, double num) throws IOException {
|
||||
out.write(convDouble(num));
|
||||
}
|
||||
|
||||
public static void writeString(OutputStream out, String str) throws IOException {
|
||||
out.write(convString(str));
|
||||
}
|
||||
|
||||
public static void writeStringBytes(OutputStream out, String str) throws IOException {
|
||||
out.write(convString_b(str));
|
||||
}
|
||||
|
||||
|
||||
// READING
|
||||
|
||||
public static boolean readBoolean(InputStream in) throws IOException {
|
||||
return in.read() > 0;
|
||||
}
|
||||
|
||||
public static byte readByte(InputStream in) throws IOException {
|
||||
return (byte) in.read();
|
||||
}
|
||||
|
||||
public static char readChar(InputStream in) throws IOException {
|
||||
in.read(ac, 0, ac.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ac);
|
||||
return buf.getChar();
|
||||
}
|
||||
|
||||
public static short readShort(InputStream in) throws IOException {
|
||||
in.read(as, 0, as.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(as);
|
||||
return buf.getShort();
|
||||
}
|
||||
|
||||
public static long readLong(InputStream in) throws IOException {
|
||||
in.read(al, 0, al.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(al);
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
public static int readInt(InputStream in) throws IOException {
|
||||
in.read(ai, 0, ai.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ai);
|
||||
return buf.getInt();
|
||||
}
|
||||
|
||||
public static float readFloat(InputStream in) throws IOException {
|
||||
in.read(af, 0, af.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(af);
|
||||
return buf.getFloat();
|
||||
}
|
||||
|
||||
public static double readDouble(InputStream in) throws IOException {
|
||||
in.read(ad, 0, ad.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ad);
|
||||
return buf.getDouble();
|
||||
}
|
||||
|
||||
public static String readString(InputStream in) throws IOException {
|
||||
String s = "";
|
||||
char c;
|
||||
while ((c = readChar(in)) > 0) {
|
||||
s += c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static String readStringBytes(InputStream in) throws IOException {
|
||||
String s = "";
|
||||
byte b;
|
||||
while ((b = readByte(in)) > 0) {
|
||||
s += (char) b;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,811 @@
|
||||
package com.porcupine.math;
|
||||
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Math helper
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Calc {
|
||||
|
||||
/** Square root of two */
|
||||
public static final double SQ2 = 1.41421356237;
|
||||
|
||||
public static double fixNan(double toFix, double replace) {
|
||||
if(Double.isNaN(toFix)) return replace;
|
||||
return toFix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distance from 2D line to 2D point [X,Y]
|
||||
*
|
||||
* @param lineDirVec line directional vector
|
||||
* @param linePoint point of line
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDist(Vec lineDirVec, Coord linePoint, Coord point) {
|
||||
// line point L[lx,ly]
|
||||
double lx = linePoint.x;
|
||||
double ly = linePoint.y;
|
||||
|
||||
// line equation ax+by+c=0
|
||||
double a = -lineDirVec.y;
|
||||
double b = lineDirVec.x;
|
||||
double c = -a * lx - b * ly;
|
||||
|
||||
// checked point P[x,y]
|
||||
double x = point.x;
|
||||
double y = point.y;
|
||||
|
||||
// distance
|
||||
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distance from 2D line to 2D point [X,Z]
|
||||
*
|
||||
* @param lineDirVec line directional vector
|
||||
* @param linePoint point of line
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDistXZ(Vec lineDirVec, Coord linePoint, Coord point) {
|
||||
return linePointDist(new Vec(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get longest side of a right-angled triangle
|
||||
*
|
||||
* @param a side a (opposite)
|
||||
* @param b side b (adjacent)
|
||||
* @return longest side (hypotenuse)
|
||||
*/
|
||||
public static double pythC(double a, double b) {
|
||||
return Math.sqrt(square(a) + square(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get adjacent side of a right-angled triangle
|
||||
*
|
||||
* @param a side a (opposite)
|
||||
* @param c side c (hypotenuse)
|
||||
* @return side b (adjacent)
|
||||
*/
|
||||
public static double pythB(double a, double c) {
|
||||
return Math.sqrt(square(c) - square(a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get opposite side of a right-angled triangle
|
||||
*
|
||||
* @param b side b (adjacent)
|
||||
* @param c side c (hypotenuse)
|
||||
* @return side a (opposite)
|
||||
*/
|
||||
public static double pythA(double b, double c) {
|
||||
return Math.sqrt(square(c) - square(b));
|
||||
}
|
||||
|
||||
private static class Angles {
|
||||
|
||||
public static double delta(double alpha, double beta, double a360) {
|
||||
|
||||
while (Math.abs(alpha - beta) > a360 / 2D) {
|
||||
alpha = norm(alpha + a360 / 2D, a360);
|
||||
beta = norm(beta + a360 / 2D, a360);
|
||||
}
|
||||
|
||||
return beta - alpha;
|
||||
}
|
||||
|
||||
public static double norm(double angle, double a360) {
|
||||
while (angle < 0)
|
||||
angle += a360;
|
||||
while (angle > a360)
|
||||
angle -= a360;
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calc subclass with buffer utils.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Buffers {
|
||||
|
||||
/**
|
||||
* Create java.nio.FloatBuffer of given floats, and flip it.
|
||||
*
|
||||
* @param obj floats or float array
|
||||
* @return float buffer
|
||||
*/
|
||||
public static FloatBuffer fBuff(float... obj) {
|
||||
return (FloatBuffer) BufferUtils.createFloatBuffer(obj.length).put(obj).flip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill java.nio.FloatBuffer with floats or float array
|
||||
*
|
||||
* @param buff
|
||||
* @param obj
|
||||
*/
|
||||
public static void fillBuff(FloatBuffer buff, float... obj) {
|
||||
buff.put(obj);
|
||||
buff.flip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new java.nio.FloatBuffer of given length
|
||||
*
|
||||
* @param count elements
|
||||
* @return the new java.nio.FloatBuffer
|
||||
*/
|
||||
public static FloatBuffer mkBuff(int count) {
|
||||
return BufferUtils.createFloatBuffer(count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle calculations for degrees.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Deg {
|
||||
/** 180° in degrees */
|
||||
public static final double a180 = 180;
|
||||
/** 270° in degrees */
|
||||
public static final double a270 = 270;
|
||||
/** 360° in degrees */
|
||||
public static final double a360 = 360;
|
||||
/** 45° in degrees */
|
||||
public static final double a45 = 45;
|
||||
/** 90° in degrees */
|
||||
public static final double a90 = 90;
|
||||
|
||||
/**
|
||||
* Subtract two angles alpha - beta
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return (alpha - beta) in degrees
|
||||
*/
|
||||
public static double delta(double alpha, double beta) {
|
||||
return Angles.delta(alpha, beta, a360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Difference of two angles (absolute value of delta)
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return difference in radians
|
||||
*/
|
||||
public static double diff(double alpha, double beta) {
|
||||
return Math.abs(Angles.delta(alpha, beta, a360));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosinus in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return cosinus
|
||||
*/
|
||||
public static double cos(double deg) {
|
||||
return Math.cos(toRad(deg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sinus in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return sinus
|
||||
*/
|
||||
public static double sin(double deg) {
|
||||
return Math.sin(toRad(deg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tangents in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return tangents
|
||||
*/
|
||||
public static double tan(double deg) {
|
||||
return Math.tan(toRad(deg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle normalized to 0-360 range
|
||||
*
|
||||
* @param angle angle to normalize
|
||||
* @return normalized angle
|
||||
*/
|
||||
public static double norm(double angle) {
|
||||
return Angles.norm(angle, a360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to radians
|
||||
*
|
||||
* @param deg degrees
|
||||
* @return radians
|
||||
*/
|
||||
public static double toRad(double deg) {
|
||||
return Math.toRadians(deg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round angle to 0,45,90,135...
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @param x rounding increment (45 - round to 0,45,90...)
|
||||
* @return rounded
|
||||
*/
|
||||
public static int roundX(double deg, double x) {
|
||||
double half = x / 2d;
|
||||
deg += half;
|
||||
deg = norm(deg);
|
||||
int times = (int) Math.floor(deg / x);
|
||||
double a = times * x;
|
||||
if (a == 360) a = 0;
|
||||
return (int) Math.round(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round angle to 0,45,90,135...
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round45(double deg) {
|
||||
return roundX(deg, 45);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round angle to 0,90,180,270
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round90(double deg) {
|
||||
return roundX(deg, 90);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round angle to 0,15,30,45,60,75,90...
|
||||
*
|
||||
* @param deg angle in deg to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round15(double deg) {
|
||||
return roundX(deg, 15);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle calculations for radians.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Rad {
|
||||
/** 180° in radians */
|
||||
public static final double a180 = Math.PI;
|
||||
/** 270° in radians */
|
||||
public static final double a270 = Math.PI * 1.5D;
|
||||
/** 360° in radians */
|
||||
public static final double a360 = Math.PI * 2D;
|
||||
/** 45° in radians */
|
||||
public static final double a45 = Math.PI / 4D;
|
||||
/** 90° in radians */
|
||||
public static final double a90 = Math.PI / 2D;
|
||||
|
||||
/**
|
||||
* Subtract two angles alpha - beta
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return (alpha - beta) in radians
|
||||
*/
|
||||
public static double delta(double alpha, double beta) {
|
||||
return Angles.delta(alpha, beta, a360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Difference of two angles (absolute value of delta)
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return difference in radians
|
||||
*/
|
||||
public static double diff(double alpha, double beta) {
|
||||
return Math.abs(Angles.delta(alpha, beta, a360));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cos
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return cos
|
||||
*/
|
||||
public static double cos(double rad) {
|
||||
return Math.cos(rad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sin
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return sin
|
||||
*/
|
||||
public static double sin(double rad) {
|
||||
return Math.sin(rad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tan
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return tan
|
||||
*/
|
||||
public static double tan(double rad) {
|
||||
return Math.tan(rad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle normalized to 0-2*PI range
|
||||
*
|
||||
* @param angle angle to normalize
|
||||
* @return normalized angle
|
||||
*/
|
||||
public static double norm(double angle) {
|
||||
return Angles.norm(angle, a360);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to degrees
|
||||
*
|
||||
* @param rad radians
|
||||
* @return degrees
|
||||
*/
|
||||
public static double toDeg(double rad) {
|
||||
return Math.toDegrees(rad);
|
||||
}
|
||||
}
|
||||
|
||||
private static Random rand = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* Get volume of a sphere
|
||||
*
|
||||
* @param radius sphere radius
|
||||
* @return volume in cubic units
|
||||
*/
|
||||
public static double sphereGetVolume(double radius) {
|
||||
return (4D / 3D) * Math.PI * cube(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radius of a sphere
|
||||
*
|
||||
* @param volume sphere volume
|
||||
* @return radius in units
|
||||
*/
|
||||
public static double sphereGetRadius(double volume) {
|
||||
return Math.cbrt((3D * volume) / (4 * Math.PI));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get surface of a circle
|
||||
*
|
||||
* @param radius circle radius
|
||||
* @return volume in square units
|
||||
*/
|
||||
public static double circleGetSurface(double radius) {
|
||||
return Math.PI * square(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get radius of a circle
|
||||
*
|
||||
* @param surface circle volume
|
||||
* @return radius in units
|
||||
*/
|
||||
public static double circleGetRadius(double surface) {
|
||||
return Math.sqrt(surface / Math.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if objects are equal (for equals function)
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return are equal
|
||||
*/
|
||||
public static boolean areObjectsEqual(Object a, Object b) {
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private clamping helper.
|
||||
*
|
||||
* @param number number to be clamped
|
||||
* @param min min value
|
||||
* @param max max value
|
||||
* @return clamped double
|
||||
*/
|
||||
private static double clamp_double(Number number, Number min, Number max) {
|
||||
double n = number.doubleValue();
|
||||
double mind = min.doubleValue();
|
||||
double maxd = max.doubleValue();
|
||||
if (n > maxd) n = maxd;
|
||||
if (n < mind) n = mind;
|
||||
if(Double.isNaN(number.doubleValue())) n = mind;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private clamping helper.
|
||||
*
|
||||
* @param number number to be clamped
|
||||
* @param min min value
|
||||
* @return clamped double
|
||||
*/
|
||||
private static double clamp_double(Number number, Number min) {
|
||||
double n = number.doubleValue();
|
||||
double mind = min.doubleValue();
|
||||
if (n < mind) n = mind;
|
||||
if(Double.isNaN(number.doubleValue())) n = mind;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Number min, Number max) {
|
||||
return clamp_double(number, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Number min, Number max) {
|
||||
return (float) clamp_double(number, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Number min, Number max) {
|
||||
return (int) Math.round(clamp_double(number, min, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Range range) {
|
||||
return (int) Math.round(clamp_double(number, range.getMin(), range.getMax()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Range range) {
|
||||
return clamp_double(number, range.getMin(), range.getMax());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Range range) {
|
||||
return (float) clamp_double(number, range.getMin(), range.getMax());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Number min) {
|
||||
return clamp_double(number, min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Number min) {
|
||||
return (float) clamp_double(number, min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Number min) {
|
||||
return (int) Math.round(clamp_double(number, min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class simple name
|
||||
*
|
||||
* @param obj object
|
||||
* @return simple name
|
||||
*/
|
||||
public static String className(Object obj) {
|
||||
if (obj == null) return "NULL";
|
||||
return obj.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cube a double
|
||||
*
|
||||
* @param a squared double
|
||||
* @return square
|
||||
*/
|
||||
public static double cube(double a) {
|
||||
return a * a * a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert double to string, remove the mess at the end.
|
||||
*
|
||||
* @param d double
|
||||
* @return string
|
||||
*/
|
||||
public static String doubleToString(double d) {
|
||||
String s = Double.toString(d);
|
||||
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
|
||||
s = s.replaceAll("0+$", "");
|
||||
s = s.replaceAll("\\.$", "");
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert float to string, remove the mess at the end.
|
||||
*
|
||||
* @param f float
|
||||
* @return string
|
||||
*/
|
||||
public static String floatToString(float f) {
|
||||
String s = Float.toString(f);
|
||||
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
|
||||
s = s.replaceAll("0+$", "");
|
||||
s = s.replaceAll("\\.$", "");
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if number is in range
|
||||
*
|
||||
* @param number checked
|
||||
* @param left lower end
|
||||
* @param right upper end
|
||||
* @return is in range
|
||||
*/
|
||||
public static boolean inRange(double number, double left, double right) {
|
||||
return number >= left && number <= right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last number
|
||||
* @param now new number
|
||||
* @param dtime delta time
|
||||
* @return current number to render
|
||||
*/
|
||||
public static double interpolate(double last, double now, double dtime) {
|
||||
return last + (now - last) * dtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get angle [degrees] from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last angle
|
||||
* @param now new angle
|
||||
* @param delta delta time
|
||||
* @return current angle to render
|
||||
*/
|
||||
public static double interpolateDeg(double last, double now, double delta) {
|
||||
return Deg.norm(last + Deg.delta(now, last) * delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static double max(double... numbers) {
|
||||
double highest = numbers[0];
|
||||
for (double num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static float max(float... numbers) {
|
||||
float highest = numbers[0];
|
||||
for (float num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static int max(int... numbers) {
|
||||
int highest = numbers[0];
|
||||
for (int num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static double min(double... numbers) {
|
||||
double lowest = numbers[0];
|
||||
for (double num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static float min(float... numbers) {
|
||||
float lowest = numbers[0];
|
||||
for (float num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static int min(int... numbers) {
|
||||
int lowest = numbers[0];
|
||||
for (int num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split comma separated list of integers.
|
||||
*
|
||||
* @param list String containing the list.
|
||||
* @return array of integers or null.
|
||||
*/
|
||||
public static List<Integer> parseIntList(String list) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = list.split(",");
|
||||
|
||||
ArrayList<Integer> intList = new ArrayList<Integer>();
|
||||
|
||||
for (String part : parts) {
|
||||
try {
|
||||
intList.add(Integer.parseInt(part));
|
||||
} catch (NumberFormatException e) {}
|
||||
}
|
||||
|
||||
return intList;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick random element from a given list.
|
||||
*
|
||||
* @param list list of choices
|
||||
* @return picked element
|
||||
*/
|
||||
public static Object pick(List<?> list) {
|
||||
if (list.size() == 0) return null;
|
||||
return list.get(rand.nextInt(list.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Square a double
|
||||
*
|
||||
* @param a squared double
|
||||
* @return square
|
||||
*/
|
||||
public static double square(double a) {
|
||||
return a * a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signum.
|
||||
*
|
||||
* @param number
|
||||
* @return sign, -1,0,1
|
||||
*/
|
||||
public static int sgn(double number) {
|
||||
return number > 0 ? 1 : number < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.porcupine.math;
|
||||
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Polar coordinate
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Polar {
|
||||
/** angle in radians */
|
||||
public double angle = 0;
|
||||
/** distance in units */
|
||||
public double distance = 0;
|
||||
|
||||
/**
|
||||
* @param angle angle in radians
|
||||
* @param distance distance from origin
|
||||
*/
|
||||
public Polar(double angle, double distance) {
|
||||
this.angle = angle;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coord
|
||||
*
|
||||
* @param coord coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoord(Coord coord) {
|
||||
return new Polar(Math.atan2(coord.y, coord.x), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoord(double x, double y) {
|
||||
return Polar.fromCoord(new Coord(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param z z coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(double x, double z) {
|
||||
return Polar.fromCoordXZ(new Coord(x, 0, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoord() {
|
||||
return new Coord(distance * Math.cos(angle), distance * Math.sin(angle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get X,0,Y coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoordXZ() {
|
||||
return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Polar(theta=" + angle + ", r=" + distance + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build polar from X,Z instead of X,Y
|
||||
*
|
||||
* @param coord cpprd with X,Z
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(Coord coord) {
|
||||
return fromCoord(coord.x, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.porcupine.math;
|
||||
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.math.Calc.Deg;
|
||||
import com.porcupine.math.Calc.Rad;
|
||||
|
||||
|
||||
/**
|
||||
* Polar coordinate in degrees
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class PolarDeg {
|
||||
/** angle in degrees */
|
||||
public double angle = 0;
|
||||
/** distance in units */
|
||||
public double distance = 0;
|
||||
|
||||
/**
|
||||
* Polar coordinate in degrees
|
||||
*
|
||||
* @param angle angle in degrees
|
||||
* @param distance distance from origin
|
||||
*/
|
||||
public PolarDeg(double angle, double distance) {
|
||||
this.angle = angle;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coord
|
||||
*
|
||||
* @param coord coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoord(Coord coord) {
|
||||
return new PolarDeg(Rad.toDeg(Math.atan2(coord.y, coord.x)), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoord(double x, double y) {
|
||||
return PolarDeg.fromCoord(new Coord(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param z y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoordXZ(double x, double z) {
|
||||
return PolarDeg.fromCoordXZ(new Coord(x, 0, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoord() {
|
||||
return new Coord(distance * Math.cos(Deg.toRad(angle)), distance * Math.sin(Deg.toRad(angle)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get X,0,Y coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoordXZ() {
|
||||
return new Coord(distance * Math.cos(Deg.toRad(angle)), 0, distance * Math.sin(Deg.toRad(angle)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Polar(theta=" + angle + ", r=" + distance + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build polar from X,Z instead of X,Y
|
||||
*
|
||||
* @param coord cpprd with X,Z
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoordXZ(Coord coord) {
|
||||
return fromCoord(coord.x, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.porcupine.math;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
/**
|
||||
* Numeric range, able to generate random numbers and give min/max values.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Range {
|
||||
private double min = 0;
|
||||
private double max = 1;
|
||||
|
||||
private static Random rand = new Random();
|
||||
|
||||
/**
|
||||
* Implicit range constructor 0-1
|
||||
*/
|
||||
public Range() {}
|
||||
|
||||
|
||||
/**
|
||||
* Create new range
|
||||
*
|
||||
* @param min min number
|
||||
* @param max max number
|
||||
*/
|
||||
public Range(double min, double max) {
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new range
|
||||
*
|
||||
* @param minmax min = max number
|
||||
*/
|
||||
public Range(double minmax) {
|
||||
this.min = minmax;
|
||||
this.max = minmax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random integer from range
|
||||
*
|
||||
* @return random int
|
||||
*/
|
||||
public int randInt() {
|
||||
return (int) (Math.round(min) + rand.nextInt((int) (Math.round(max) - Math.round(min)) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random double from this range
|
||||
*
|
||||
* @return random double
|
||||
*/
|
||||
public double randDouble() {
|
||||
return min + rand.nextDouble() * (max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get min
|
||||
*
|
||||
* @return min number
|
||||
*/
|
||||
public double getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max
|
||||
*
|
||||
* @return max number
|
||||
*/
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get min
|
||||
*
|
||||
* @return min number
|
||||
*/
|
||||
public int getMinI() {
|
||||
return (int) min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max
|
||||
*
|
||||
* @return max number
|
||||
*/
|
||||
public int getMaxI() {
|
||||
return (int) max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set min
|
||||
*
|
||||
* @param min min value
|
||||
*/
|
||||
public void setMin(double min) {
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max
|
||||
*
|
||||
* @param max max value
|
||||
*/
|
||||
public void setMax(double max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Range(" + min + ";" + max + ")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get identical copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public Range copy() {
|
||||
return new Range(min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to value of other range
|
||||
*
|
||||
* @param other copied range
|
||||
*/
|
||||
public void setTo(Range other) {
|
||||
if (other == null) return;
|
||||
min = other.min;
|
||||
max = other.max;
|
||||
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to min-max values
|
||||
*
|
||||
* @param min min value
|
||||
* @param max max value
|
||||
*/
|
||||
public void setTo(double min, double max) {
|
||||
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable object
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <T> type
|
||||
*/
|
||||
public abstract class AbstractMutable<T> {
|
||||
/** The wrapped value */
|
||||
public T o = getDefault();
|
||||
|
||||
/**
|
||||
* Implicint constructor
|
||||
*/
|
||||
public AbstractMutable() {}
|
||||
|
||||
/**
|
||||
* new mutable object
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public AbstractMutable(T o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped value
|
||||
*
|
||||
* @return value
|
||||
*/
|
||||
public T get() {
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value
|
||||
*
|
||||
* @param o new value to set
|
||||
*/
|
||||
public void set(T o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default value
|
||||
*
|
||||
* @return default value
|
||||
*/
|
||||
protected abstract T getDefault();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable boolean
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class MBoolean extends AbstractMutable<Boolean> {
|
||||
/**
|
||||
* Mutable boolean
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public MBoolean(Boolean o) {
|
||||
super(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imp.c.
|
||||
*/
|
||||
public MBoolean() {}
|
||||
|
||||
@Override
|
||||
protected Boolean getDefault() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable double
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class MDouble extends AbstractMutable<Double> {
|
||||
/**
|
||||
* Mutable double
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public MDouble(Double o) {
|
||||
super(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imp.c.
|
||||
*/
|
||||
public MDouble() {}
|
||||
|
||||
@Override
|
||||
protected Double getDefault() {
|
||||
return 0d;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable float
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class MFloat extends AbstractMutable<Float> {
|
||||
/**
|
||||
* Mutable float
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public MFloat(Float o) {
|
||||
super(o);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Imp.c.
|
||||
*/
|
||||
public MFloat() {}
|
||||
|
||||
@Override
|
||||
protected Float getDefault() {
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable integer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class MInt extends AbstractMutable<Integer> {
|
||||
/**
|
||||
* Mutable int
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public MInt(Integer o) {
|
||||
super(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imp.c.
|
||||
*/
|
||||
public MInt() {}
|
||||
|
||||
@Override
|
||||
protected Integer getDefault() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.porcupine.mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable string
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class MString extends AbstractMutable<String> {
|
||||
/**
|
||||
* Mutable string
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public MString(String o) {
|
||||
super(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imp.c.
|
||||
*/
|
||||
public MString() {}
|
||||
|
||||
@Override
|
||||
protected String getDefault() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 2 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
*/
|
||||
public class Struct2<T1, T2> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* Make structure of 2 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
*/
|
||||
public Struct2(T1 objA, T2 objB) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct2<?, ?> t = (Struct2<?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 3 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
*/
|
||||
public class Struct3<T1, T2, T3> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* Make structure of 3 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
*/
|
||||
public Struct3(T1 objA, T2 objB, T3 objC) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct3<?, ?, ?> t = (Struct3<?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 4 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
* @param <T4> 4th object class
|
||||
*/
|
||||
public class Struct4<T1, T2, T3, T4> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* 4th object
|
||||
*/
|
||||
public T4 d;
|
||||
|
||||
/**
|
||||
* Make structure of 4 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
* @param objD 4th object
|
||||
*/
|
||||
public Struct4(T1 objA, T2 objB, T3 objC, T4 objD) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
d = objD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 get4() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void setD(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void set4(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct4<?, ?, ?, ?> t = (Struct4<?, ?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c) && Calc.areObjectsEqual(d, t.d);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
hash += (d == null ? 0 : d.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "," + d + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 5 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
* @param <T4> 4th object class
|
||||
* @param <T5> 5th object class
|
||||
*/
|
||||
public class Struct5<T1, T2, T3, T4, T5> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* 4th object
|
||||
*/
|
||||
public T4 d;
|
||||
|
||||
/**
|
||||
* 5th object
|
||||
*/
|
||||
public T5 e;
|
||||
|
||||
/**
|
||||
* Make structure of 4 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
* @param objD 4th object
|
||||
* @param objE 5th object
|
||||
*/
|
||||
public Struct5(T1 objA, T2 objB, T3 objC, T4 objD, T5 objE) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
d = objD;
|
||||
e = objE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 get4() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 get5() {
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void setD(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void setE(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void set4(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void set5(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct5<?, ?, ?, ?, ?> t = (Struct5<?, ?, ?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c) && Calc.areObjectsEqual(d, t.d)
|
||||
&& Calc.areObjectsEqual(e, t.e);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
hash += (d == null ? 0 : d.hashCode());
|
||||
hash += (e == null ? 0 : e.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "," + d + "," + e + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 6 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
* @param <T4> 4th object class
|
||||
* @param <T5> 5th object class
|
||||
* @param <T6> 6th object class
|
||||
*/
|
||||
public class Struct6<T1, T2, T3, T4, T5, T6> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* 4th object
|
||||
*/
|
||||
public T4 d;
|
||||
|
||||
/**
|
||||
* 5th object
|
||||
*/
|
||||
public T5 e;
|
||||
|
||||
/**
|
||||
* 6th object
|
||||
*/
|
||||
public T6 f;
|
||||
|
||||
/**
|
||||
* Make structure of 4 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
* @param objD 4th object
|
||||
* @param objE 5th object
|
||||
* @param objF 6th object
|
||||
*/
|
||||
public Struct6(T1 objA, T2 objB, T3 objC, T4 objD, T5 objE, T6 objF) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
d = objD;
|
||||
e = objE;
|
||||
f = objF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 getF() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 get4() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 get5() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 get6() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void setD(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void setE(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setF(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void set4(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void set5(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set6(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct6<?, ?, ?, ?, ?, ?> t = (Struct6<?, ?, ?, ?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c) && Calc.areObjectsEqual(d, t.d)
|
||||
&& Calc.areObjectsEqual(e, t.e) && Calc.areObjectsEqual(f, t.f);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
hash += (d == null ? 0 : d.hashCode());
|
||||
hash += (e == null ? 0 : e.hashCode());
|
||||
hash += (f == null ? 0 : f.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "," + d + "," + e + "," + f + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 7 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
* @param <T4> 4th object class
|
||||
* @param <T5> 5th object class
|
||||
* @param <T6> 6th object class
|
||||
* @param <T7> 7th object class
|
||||
*/
|
||||
public class Struct7<T1, T2, T3, T4, T5, T6, T7> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* 4th object
|
||||
*/
|
||||
public T4 d;
|
||||
|
||||
/**
|
||||
* 5th object
|
||||
*/
|
||||
public T5 e;
|
||||
|
||||
/**
|
||||
* 6th object
|
||||
*/
|
||||
public T6 f;
|
||||
|
||||
/**
|
||||
* 7th object
|
||||
*/
|
||||
public T7 g;
|
||||
|
||||
/**
|
||||
* Make structure of 4 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
* @param objD 4th object
|
||||
* @param objE 5th object
|
||||
* @param objF 6th object
|
||||
* @param objG 7th object
|
||||
*/
|
||||
public Struct7(T1 objA, T2 objB, T3 objC, T4 objD, T5 objE, T6 objF, T7 objG) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
d = objD;
|
||||
e = objE;
|
||||
f = objF;
|
||||
g = objG;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 getF() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 7th object
|
||||
*/
|
||||
public T7 getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 get4() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 get5() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 get6() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 7th object
|
||||
*/
|
||||
public T7 get7() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void setD(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void setE(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setF(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 7th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setG(T7 obj) {
|
||||
g = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void set4(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void set5(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set6(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 7th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set7(T7 obj) {
|
||||
g = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct7<?, ?, ?, ?, ?, ?, ?> t = (Struct7<?, ?, ?, ?, ?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c) && Calc.areObjectsEqual(d, t.d)
|
||||
&& Calc.areObjectsEqual(e, t.e) && Calc.areObjectsEqual(f, t.f) && Calc.areObjectsEqual(g, t.g);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
hash += (d == null ? 0 : d.hashCode());
|
||||
hash += (e == null ? 0 : e.hashCode());
|
||||
hash += (f == null ? 0 : f.hashCode());
|
||||
hash += (g == null ? 0 : g.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "," + d + "," + e + "," + f + "," + g + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
package com.porcupine.struct;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 7 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
* @param <T4> 4th object class
|
||||
* @param <T5> 5th object class
|
||||
* @param <T6> 6th object class
|
||||
* @param <T7> 7th object class
|
||||
* @param <T8> 8th object class
|
||||
*/
|
||||
public class Struct8<T1, T2, T3, T4, T5, T6, T7, T8> {
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 a;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 b;
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 c;
|
||||
|
||||
/**
|
||||
* 4th object
|
||||
*/
|
||||
public T4 d;
|
||||
|
||||
/**
|
||||
* 5th object
|
||||
*/
|
||||
public T5 e;
|
||||
|
||||
/**
|
||||
* 6th object
|
||||
*/
|
||||
public T6 f;
|
||||
|
||||
/**
|
||||
* 7th object
|
||||
*/
|
||||
public T7 g;
|
||||
|
||||
/**
|
||||
* 8th object
|
||||
*/
|
||||
public T8 h;
|
||||
|
||||
/**
|
||||
* Make structure of 4 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
* @param objD 4th object
|
||||
* @param objE 5th object
|
||||
* @param objF 6th object
|
||||
* @param objG 7th object
|
||||
* @param objH 8th object
|
||||
*/
|
||||
public Struct8(T1 objA, T2 objB, T3 objC, T4 objD, T5 objE, T6 objF, T7 objG, T8 objH) {
|
||||
a = objA;
|
||||
b = objB;
|
||||
c = objC;
|
||||
d = objD;
|
||||
e = objE;
|
||||
f = objF;
|
||||
g = objG;
|
||||
h = objH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 getF() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 7th object
|
||||
*/
|
||||
public T7 getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 8th object
|
||||
*/
|
||||
public T8 getH() {
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 get1() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 get2() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 get3() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 4th object
|
||||
*/
|
||||
public T4 get4() {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 5th object
|
||||
*/
|
||||
public T5 get5() {
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 6th object
|
||||
*/
|
||||
public T6 get6() {
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 7th object
|
||||
*/
|
||||
public T7 get7() {
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 8th object
|
||||
*/
|
||||
public T8 get8() {
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setA(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void setB(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void setC(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void setD(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void setE(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setF(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 7th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setG(T7 obj) {
|
||||
g = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 8th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void setH(T8 obj) {
|
||||
h = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void set1(T1 obj) {
|
||||
a = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 2nd object
|
||||
*
|
||||
* @param obj 2nd object
|
||||
*/
|
||||
public void set2(T2 obj) {
|
||||
b = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 3rd object
|
||||
*
|
||||
* @param obj 3rd object
|
||||
*/
|
||||
public void set3(T3 obj) {
|
||||
c = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 4th object
|
||||
*
|
||||
* @param obj 4th object
|
||||
*/
|
||||
public void set4(T4 obj) {
|
||||
d = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 5th object
|
||||
*
|
||||
* @param obj 5th object
|
||||
*/
|
||||
public void set5(T5 obj) {
|
||||
e = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 6th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set6(T6 obj) {
|
||||
f = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 7th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set7(T7 obj) {
|
||||
g = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 8th object
|
||||
*
|
||||
* @param obj 6th object
|
||||
*/
|
||||
public void set8(T8 obj) {
|
||||
h = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Struct8<?, ?, ?, ?, ?, ?, ?, ?> t = (Struct8<?, ?, ?, ?, ?, ?, ?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(a, t.a) && Calc.areObjectsEqual(b, t.b) && Calc.areObjectsEqual(c, t.c) && Calc.areObjectsEqual(d, t.d)
|
||||
&& Calc.areObjectsEqual(e, t.e) && Calc.areObjectsEqual(f, t.f) && Calc.areObjectsEqual(g, t.g) && Calc.areObjectsEqual(h, t.h);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (a == null ? 0 : a.hashCode());
|
||||
hash += (b == null ? 0 : b.hashCode());
|
||||
hash += (c == null ? 0 : c.hashCode());
|
||||
hash += (d == null ? 0 : d.hashCode());
|
||||
hash += (e == null ? 0 : e.hashCode());
|
||||
hash += (f == null ? 0 : f.hashCode());
|
||||
hash += (g == null ? 0 : g.hashCode());
|
||||
hash += (h == null ? 0 : h.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "STRUCT {" + a + "," + b + "," + c + "," + d + "," + e + "," + f + "," + g + "," + h + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.porcupine.time;
|
||||
|
||||
|
||||
/**
|
||||
* Class for counting FPS in games.<br>
|
||||
* This class can be used also as a simple frequency meter - output is in Hz.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FpsMeter {
|
||||
|
||||
private long frames = 0;
|
||||
private long drops = 0;
|
||||
private long lastTimeMillis = System.currentTimeMillis();
|
||||
private long lastSecFPS = 0;
|
||||
private long lastSecDrop = 0;
|
||||
|
||||
/**
|
||||
* @return current second's FPS
|
||||
*/
|
||||
public long getFPS() {
|
||||
return lastSecFPS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that frame was rendered
|
||||
*/
|
||||
public void frame() {
|
||||
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
|
||||
lastSecFPS = frames;
|
||||
lastSecDrop = drops;
|
||||
frames = 0;
|
||||
drops = 0;
|
||||
lastTimeMillis = System.currentTimeMillis();
|
||||
}
|
||||
frames++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that some frames have been dropped
|
||||
*
|
||||
* @param dropped dropped frames
|
||||
*/
|
||||
public void drop(int dropped) {
|
||||
drops += dropped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current second's dropped frames
|
||||
*/
|
||||
public long getDropped() {
|
||||
return lastSecDrop;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.porcupine.time;
|
||||
|
||||
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Precise game timer
|
||||
*/
|
||||
public class Timer {
|
||||
|
||||
/**
|
||||
* Ticks elapsed since last updateTimer().<br>
|
||||
* Used to count how many frames to skip.
|
||||
*/
|
||||
public int ticksMissed;
|
||||
|
||||
|
||||
/** Speed multiplier. */
|
||||
public float timerSpeedMultiplier;
|
||||
|
||||
|
||||
/**
|
||||
* How much of the next tick has already elapsed.
|
||||
*/
|
||||
public float renderDeltaTime;
|
||||
|
||||
|
||||
/**
|
||||
* How many update ticks are to be run since last time
|
||||
*
|
||||
* @return update ticks needed
|
||||
*/
|
||||
public int getTicksMissed() {
|
||||
return ticksMissed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get render delta time, 0-1
|
||||
*
|
||||
* @return delta time
|
||||
*/
|
||||
public double getDeltaTime() {
|
||||
return renderDeltaTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return speed multiplier
|
||||
*/
|
||||
public double getSpeed() {
|
||||
return timerSpeedMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set speed multiplier
|
||||
*
|
||||
* @param speed new speed multiplier
|
||||
*/
|
||||
public void setSpeed(double speed) {
|
||||
timerSpeedMultiplier = (float) speed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private float ticksPerSecond;
|
||||
private double lastUpdateSecs;
|
||||
private long lastUpdateMillis;
|
||||
private long lastSyncMillis;
|
||||
private long syncCounter;
|
||||
private double timeSyncAdjustment;
|
||||
|
||||
|
||||
/**
|
||||
* init timer
|
||||
*
|
||||
* @param ticksPerSecond logic update ticks per second
|
||||
*/
|
||||
public Timer(float ticksPerSecond) {
|
||||
timerSpeedMultiplier = 1.0F;
|
||||
renderDeltaTime = 0.0F;
|
||||
timeSyncAdjustment = 1.0D;
|
||||
this.ticksPerSecond = ticksPerSecond;
|
||||
|
||||
lastUpdateMillis = System.currentTimeMillis();
|
||||
lastSyncMillis = System.nanoTime() / 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all fields of the Timer using the current time
|
||||
*/
|
||||
public void update() {
|
||||
long l = System.currentTimeMillis();
|
||||
long deltaMillis = l - lastUpdateMillis;
|
||||
long millis = System.nanoTime() / 1000000;
|
||||
double secs = millis / 1000D;
|
||||
|
||||
if (deltaMillis > 1000L) {
|
||||
lastUpdateSecs = secs;
|
||||
} else if (deltaMillis < 0L) {
|
||||
lastUpdateSecs = secs;
|
||||
} else {
|
||||
syncCounter += deltaMillis;
|
||||
|
||||
if (syncCounter > 1000L) {
|
||||
long d1 = millis - lastSyncMillis;
|
||||
double d2 = (double) syncCounter / (double) d1;
|
||||
timeSyncAdjustment += (d2 - timeSyncAdjustment) * 0.2D;
|
||||
lastSyncMillis = millis;
|
||||
syncCounter = 0L;
|
||||
}
|
||||
|
||||
if (syncCounter < 0L) {
|
||||
lastSyncMillis = millis;
|
||||
}
|
||||
}
|
||||
|
||||
lastUpdateMillis = l;
|
||||
|
||||
double delta = (secs - lastUpdateSecs) * timeSyncAdjustment;
|
||||
lastUpdateSecs = secs;
|
||||
|
||||
delta = Calc.clampd(delta, 0, 1);
|
||||
|
||||
renderDeltaTime += delta * timerSpeedMultiplier * ticksPerSecond;
|
||||
ticksMissed = (int) renderDeltaTime;
|
||||
renderDeltaTime -= ticksMissed;
|
||||
|
||||
ticksMissed = Calc.clampi(ticksMissed, 0, 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.porcupine.util;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
|
||||
/**
|
||||
* File filter for certain suffixes
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FileSuffixFilter implements FileFilter {
|
||||
|
||||
/** Array of allowed suffixes */
|
||||
private String[] suffixes = null;
|
||||
|
||||
/**
|
||||
* Suffix filter
|
||||
*
|
||||
* @param suffixes var-args allowed suffixes, case insensitive
|
||||
*/
|
||||
public FileSuffixFilter(String... suffixes) {
|
||||
this.suffixes = suffixes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
//System.out.println(pathname);
|
||||
for (String suffix : suffixes) {
|
||||
return pathname.isFile() && pathname.getName().toLowerCase().trim().endsWith(suffix.toLowerCase().trim());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.porcupine.util;
|
||||
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sector.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for filesystem
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private enum EnumOS {
|
||||
linux, solaris, windows, macos, unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* get working directory path ending with slash
|
||||
*
|
||||
* @param dirname name of the directory, dot will be added automatically
|
||||
* @return File path to the folder
|
||||
*/
|
||||
public static File getAppDir(String dirname) {
|
||||
String s = System.getProperty("user.home", ".");
|
||||
File file;
|
||||
|
||||
switch (getOs()) {
|
||||
case linux:
|
||||
case solaris:
|
||||
file = new File(s, "." + dirname + '/');
|
||||
break;
|
||||
|
||||
case windows:
|
||||
String s1 = System.getenv("APPDATA");
|
||||
|
||||
if (s1 != null) {
|
||||
file = new File(s1, "." + dirname + '/');
|
||||
} else {
|
||||
file = new File(s, "." + dirname + '/');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case macos:
|
||||
file = new File(s, "Library/Application Support/" + dirname);
|
||||
break;
|
||||
|
||||
default:
|
||||
file = new File(s, dirname + "/");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!file.exists() && !file.mkdirs()) {
|
||||
throw new RuntimeException((new StringBuilder()).append("The working directory could not be created: ").append(file).toString());
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
private static EnumOS getOs() {
|
||||
String s = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (s.contains("win")) {
|
||||
return EnumOS.windows;
|
||||
}
|
||||
|
||||
if (s.contains("mac")) {
|
||||
return EnumOS.macos;
|
||||
}
|
||||
|
||||
if (s.contains("solaris")) {
|
||||
return EnumOS.solaris;
|
||||
}
|
||||
|
||||
if (s.contains("sunos")) {
|
||||
return EnumOS.solaris;
|
||||
}
|
||||
|
||||
if (s.contains("linux")) {
|
||||
return EnumOS.linux;
|
||||
}
|
||||
|
||||
if (s.contains("unix")) {
|
||||
return EnumOS.linux;
|
||||
} else {
|
||||
return EnumOS.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @param filter file filter
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listFolder(File dir, FileFilter filter) {
|
||||
try {
|
||||
dir.mkdir();
|
||||
} catch (RuntimeException e) {
|
||||
Log.e("Error creating folder " + dir, e);
|
||||
}
|
||||
|
||||
List<File> list = new ArrayList<File>();
|
||||
|
||||
try {
|
||||
for (File f : dir.listFiles(filter)) {
|
||||
list.add(f);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Error listing folder " + dir, e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listFolder(File dir) {
|
||||
return listFolder(dir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param file file
|
||||
* @return filename without extension
|
||||
*/
|
||||
public static String removeExtension(File file) {
|
||||
return removeExtension(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param filename
|
||||
* @return filename without extension
|
||||
*/
|
||||
public static String removeExtension(String filename) {
|
||||
String[] parts = filename.split("[.]");
|
||||
String out = "";
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
out += parts[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read entire file to a string.
|
||||
*
|
||||
* @param file file
|
||||
* @return file contents
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String fileToString(File file) throws IOException {
|
||||
String result = null;
|
||||
DataInputStream in = null;
|
||||
|
||||
byte[] buffer = new byte[(int) file.length()];
|
||||
in = new DataInputStream(new FileInputStream(file));
|
||||
in.readFully(buffer);
|
||||
result = new String(buffer);
|
||||
in.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,997 @@
|
||||
package com.porcupine.util;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
|
||||
/**
|
||||
* Property manager with advanced formatting and value checking.<br>
|
||||
* Methods starting with put are for filling. Most of the others are shortcuts
|
||||
* to getters.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class PropertyManager {
|
||||
/**
|
||||
* Properties stored in file, alphabetically sorted.<br>
|
||||
* Property file is much cleaner than the normal java.util.Properties,
|
||||
* newlines can be inserted to separate categories, and individual keys can
|
||||
* have their own inline comments.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
*/
|
||||
private static class PC_SortedProperties extends Properties {
|
||||
|
||||
/** A table of hex digits */
|
||||
private static final char[] hexDigit_custom = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
/**
|
||||
* this is here because the original method is private.
|
||||
*
|
||||
* @param nibble
|
||||
* @return hex char.
|
||||
*/
|
||||
private static char toHex_custom(int nibble) {
|
||||
return hexDigit_custom[(nibble & 0xF)];
|
||||
}
|
||||
|
||||
private static void writeComments_custom(BufferedWriter bw, String comm) throws IOException {
|
||||
|
||||
String comments = comm.replace("\n\n", "\n \n");
|
||||
|
||||
int len = comments.length();
|
||||
int current = 0;
|
||||
int last = 0;
|
||||
char[] uu = new char[6];
|
||||
uu[0] = '\\';
|
||||
uu[1] = 'u';
|
||||
while (current < len) {
|
||||
char c = comments.charAt(current);
|
||||
if (c > '\u00ff' || c == '\n' || c == '\r') {
|
||||
if (last != current) {
|
||||
bw.write("# " + comments.substring(last, current));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (c > '\u00ff') {
|
||||
uu[2] = toHex_custom((c >> 12) & 0xf);
|
||||
uu[3] = toHex_custom((c >> 8) & 0xf);
|
||||
uu[4] = toHex_custom((c >> 4) & 0xf);
|
||||
uu[5] = toHex_custom(c & 0xf);
|
||||
bw.write(new String(uu));
|
||||
} else {
|
||||
bw.newLine();
|
||||
if (c == '\r' && current != len - 1 && comments.charAt(current + 1) == '\n') {
|
||||
current++;
|
||||
}
|
||||
// if (current == len - 1 || (comments.charAt(current + 1) != '#' && comments.charAt(current + 1) != '!'))
|
||||
// bw.write("#");
|
||||
}
|
||||
last = current + 1;
|
||||
}
|
||||
current++;
|
||||
}
|
||||
if (last != current) {
|
||||
bw.write("# " + comments.substring(last, current));
|
||||
}
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
/** Option: put empty line before each comment. */
|
||||
public boolean cfgEmptyLineBeforeComment = true;
|
||||
/**
|
||||
* Option: Separate sections by newline<br>
|
||||
* Section = string before first dot in key.
|
||||
*/
|
||||
public boolean cfgSeparateSectionsByEmptyLine = true;
|
||||
|
||||
private boolean firstEntry = true;
|
||||
|
||||
private Hashtable<String, String> keyComments = new Hashtable<String, String>();
|
||||
|
||||
private String lastSectionBeginning = "";
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public synchronized Enumeration keys() {
|
||||
Enumeration keysEnum = super.keys();
|
||||
Vector keyList = new Vector();
|
||||
while (keysEnum.hasMoreElements()) {
|
||||
keyList.add(keysEnum.nextElement());
|
||||
}
|
||||
Collections.sort(keyList);
|
||||
return keyList.elements();
|
||||
}
|
||||
|
||||
private String saveConvert_custom(String theString, boolean escapeSpace, boolean escapeUnicode) {
|
||||
|
||||
int len = theString.length();
|
||||
int bufLen = len * 2;
|
||||
if (bufLen < 0) {
|
||||
bufLen = Integer.MAX_VALUE;
|
||||
}
|
||||
StringBuffer outBuffer = new StringBuffer(bufLen);
|
||||
|
||||
for (int x = 0; x < len; x++) {
|
||||
char aChar = theString.charAt(x);
|
||||
// Handle common case first, selecting largest block that
|
||||
// avoids the specials below
|
||||
if ((aChar > 61) && (aChar < 127)) {
|
||||
if (aChar == '\\') {
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('\\');
|
||||
continue;
|
||||
}
|
||||
outBuffer.append(aChar);
|
||||
continue;
|
||||
}
|
||||
switch (aChar) {
|
||||
case ' ':
|
||||
if (x == 0 || escapeSpace) {
|
||||
outBuffer.append('\\');
|
||||
}
|
||||
outBuffer.append(' ');
|
||||
break;
|
||||
case '\t':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('t');
|
||||
break;
|
||||
case '\n':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('n');
|
||||
break;
|
||||
case '\r':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('r');
|
||||
break;
|
||||
case '\f':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('f');
|
||||
break;
|
||||
case '=': // Fall through
|
||||
case ':': // Fall through
|
||||
case '#': // Fall through
|
||||
case '!':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append(aChar);
|
||||
break;
|
||||
default:
|
||||
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('u');
|
||||
outBuffer.append(toHex_custom((aChar >> 12) & 0xF));
|
||||
outBuffer.append(toHex_custom((aChar >> 8) & 0xF));
|
||||
outBuffer.append(toHex_custom((aChar >> 4) & 0xF));
|
||||
outBuffer.append(toHex_custom(aChar & 0xF));
|
||||
} else {
|
||||
outBuffer.append(aChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional comment to a key
|
||||
*
|
||||
* @param key key for comment
|
||||
* @param comment the comment
|
||||
*/
|
||||
public void setKeyComment(String key, String comment) {
|
||||
keyComments.put(key, comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(OutputStream out, String comments) throws IOException {
|
||||
store_custom(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")), comments, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void store_custom(BufferedWriter bw, String comments, boolean escUnicode) throws IOException {
|
||||
if (comments != null) {
|
||||
writeComments_custom(bw, comments);
|
||||
}
|
||||
synchronized (this) {
|
||||
for (Enumeration e = keys(); e.hasMoreElements();) {
|
||||
|
||||
boolean wasNewLine = false;
|
||||
|
||||
String key = (String) e.nextElement();
|
||||
String val = (String) get(key);
|
||||
key = saveConvert_custom(key, true, escUnicode);
|
||||
val = saveConvert_custom(val, false, escUnicode);
|
||||
|
||||
if (cfgSeparateSectionsByEmptyLine && !lastSectionBeginning.equals(key.split("[.]")[0])) {
|
||||
|
||||
if (!firstEntry) {
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
wasNewLine = true;
|
||||
lastSectionBeginning = key.split("[.]")[0];
|
||||
}
|
||||
|
||||
if (keyComments.containsKey(key)) {
|
||||
String cm = keyComments.get(key);
|
||||
cm = cm.replace("\r", "\n");
|
||||
cm = cm.replace("\r\n", "\n");
|
||||
cm = cm.replace("\n\n", "\n \n");
|
||||
|
||||
String[] cmlines = cm.split("\n");
|
||||
|
||||
if (!wasNewLine && !firstEntry && cfgEmptyLineBeforeComment) {
|
||||
bw.newLine();
|
||||
}
|
||||
for (String cmline : cmlines) {
|
||||
bw.write("# " + cmline);
|
||||
bw.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
bw.write(key + " = " + val);
|
||||
bw.newLine();
|
||||
|
||||
firstEntry = false;
|
||||
}
|
||||
}
|
||||
bw.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class which loads Properties from UTF-8 file (Properties use
|
||||
* "ISO-8859-1" by default)
|
||||
*
|
||||
* @author Itay Maman
|
||||
*/
|
||||
private static class PropertiesLoader {
|
||||
private static String escapifyStr(String str) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int len = str.length();
|
||||
for (int x = 0; x < len; x++) {
|
||||
char ch = str.charAt(x);
|
||||
if (ch <= 0x007e) {
|
||||
result.append(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.append('\\');
|
||||
result.append('u');
|
||||
result.append(hexDigit(ch, 12));
|
||||
result.append(hexDigit(ch, 8));
|
||||
result.append(hexDigit(ch, 4));
|
||||
result.append(hexDigit(ch, 0));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static char hexDigit(char ch, int offset) {
|
||||
int val = (ch >> offset) & 0xF;
|
||||
if (val <= 9) {
|
||||
return (char) ('0' + val);
|
||||
}
|
||||
|
||||
return (char) ('A' + val - 10);
|
||||
}
|
||||
|
||||
public static PC_SortedProperties loadProperties(PC_SortedProperties props, InputStream is) throws IOException {
|
||||
return loadProperties(props, is, "utf-8");
|
||||
}
|
||||
|
||||
|
||||
public static PC_SortedProperties loadProperties(PC_SortedProperties props, InputStream is, String encoding) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InputStreamReader isr = new InputStreamReader(is, encoding);
|
||||
while (true) {
|
||||
int temp = isr.read();
|
||||
if (temp < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char c = (char) temp;
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
String read = sb.toString();
|
||||
|
||||
String inputString = escapifyStr(read);
|
||||
byte[] bs = inputString.getBytes("ISO-8859-1");
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
|
||||
|
||||
PC_SortedProperties ps = props;
|
||||
ps.load(bais);
|
||||
return ps;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property entry in Property manager.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
*/
|
||||
private class Property {
|
||||
public boolean bool = false;
|
||||
public String entryComment;
|
||||
public boolean defbool = false;
|
||||
public int defnum = -1;
|
||||
|
||||
public String defstr = "";
|
||||
public String name;
|
||||
public int num = -1;
|
||||
public String str = "";
|
||||
public PropertyType type;
|
||||
|
||||
/**
|
||||
* Property
|
||||
*
|
||||
* @param key key
|
||||
* @param default_value default value
|
||||
* @param entry_type type
|
||||
* @param entry_comment entry comment
|
||||
*/
|
||||
public Property(String key, boolean default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defbool = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Property entry
|
||||
*
|
||||
* @param key property key
|
||||
* @param default_value default value
|
||||
* @param entry_type property type from enum
|
||||
* @param entry_comment property comment or null
|
||||
*/
|
||||
public Property(String key, int default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defnum = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Property
|
||||
*
|
||||
* @param key key
|
||||
* @param default_value default value
|
||||
* @param entry_type type
|
||||
* @param entry_comment entry comment
|
||||
*/
|
||||
public Property(String key, String default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defstr = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boolean
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean getBoolean() {
|
||||
return bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number
|
||||
*
|
||||
* @return the number
|
||||
*/
|
||||
public int getInteger() {
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String getString() {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* is this key pressed?
|
||||
*
|
||||
* @return pressed state
|
||||
*/
|
||||
public boolean isKeyDown() {
|
||||
return type == PropertyType.KEY && Keyboard.isKeyDown(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this entry valid?
|
||||
*
|
||||
* @return is valid
|
||||
*/
|
||||
public boolean isValid() {
|
||||
if (type == PropertyType.KEY) {
|
||||
return Keyboard.getKeyName(num) != null;
|
||||
}
|
||||
if (type == PropertyType.STRING) {
|
||||
return str != null;
|
||||
}
|
||||
if (type == PropertyType.BOOLEAN || type == PropertyType.INT) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load property value from a file
|
||||
*
|
||||
* @param string the string loaded
|
||||
* @return this entry
|
||||
*/
|
||||
public boolean parse(String string) {
|
||||
if (type == PropertyType.INT) {
|
||||
if (string == null) {
|
||||
//Log.finest("* Numeric property \"" + name + "\" not set, setting to default \"" + defnum + "\"");
|
||||
num = defnum;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
num = Integer.parseInt(string.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
//Log.warning("Numeric property \"" + name + "\" has invalid value \"" + string + "\". Falling back to default \"" + defnum + "\"");
|
||||
num = defnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == PropertyType.KEY) {
|
||||
if (string == null) {
|
||||
//Log.finest("* Key property \"" + name + "\" not set, setting to default \"" + Keyboard.getKeyName(defnum) + "\"");
|
||||
num = defnum;
|
||||
return false;
|
||||
}
|
||||
num = Keyboard.getKeyIndex(string);
|
||||
if (num == Keyboard.KEY_NONE) {
|
||||
//Log.warning("Key property \"" + name + "\" has invalid value \"" + string + "\". Falling back to default \""
|
||||
// + Keyboard.getKeyName(defnum) + "\"");
|
||||
num = defnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == PropertyType.STRING) {
|
||||
if (string == null) {
|
||||
//Log.finest("* String property \"" + name + "\" not set, setting to default \"" + defstr + "\"");
|
||||
str = defstr;
|
||||
return false;
|
||||
}
|
||||
this.str = string;
|
||||
}
|
||||
|
||||
if (type == PropertyType.BOOLEAN) {
|
||||
if (string == null) {
|
||||
//Log.finest("* Boolean property \"" + name + "\" not set, setting to default \"" + defbool + "\"");
|
||||
bool = defbool;
|
||||
return false;
|
||||
}
|
||||
String string2 = string.toLowerCase();
|
||||
bool = string2.equals("yes") || string2.equals("true") || string2.equals("on") || string2.equals("enabled")
|
||||
|| string2.equals("enable");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare the contents for insertion into Properties
|
||||
*
|
||||
* @return the string prepared, or null if type is invalid
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!isValid()) {
|
||||
if (type == PropertyType.INT) {
|
||||
num = defnum;
|
||||
}
|
||||
}
|
||||
if (type == PropertyType.INT) {
|
||||
return Integer.toString(num);
|
||||
}
|
||||
if (type == PropertyType.STRING) {
|
||||
return str;
|
||||
}
|
||||
if (type == PropertyType.KEY) {
|
||||
return Keyboard.getKeyName(num) == null ? "none" : Keyboard.getKeyName(num);
|
||||
}
|
||||
if (type == PropertyType.BOOLEAN) {
|
||||
return bool ? "True" : "False";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this entry is not valid, change it to the dafault value.
|
||||
*/
|
||||
public void validate() {
|
||||
if (!isValid()) {
|
||||
if (type == PropertyType.KEY) {
|
||||
//Log.warning("Key property \"" + name + "\" has invalid value (unknown key name). Falling back to default value \""
|
||||
// + Keyboard.getKeyName(defnum) + "\"");
|
||||
num = defnum;
|
||||
}
|
||||
if (type == PropertyType.STRING) {
|
||||
//Log.warning("String property \"" + name + "\" has invalid value (NULL). Falling back to default value \"" + defstr + "\"");
|
||||
str = defstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property type enum.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
*/
|
||||
private enum PropertyType {
|
||||
BOOLEAN, INT, KEY, STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Option to put newline before inline comments
|
||||
*/
|
||||
private boolean cfgNewlineBeforeComments = true;
|
||||
/** Disable entry validation */
|
||||
private boolean cfgNoValidate = true;
|
||||
|
||||
/**
|
||||
* Option to put newline between sections.<br>
|
||||
* Sections are detected by text before first dot in identifier.
|
||||
*/
|
||||
private boolean cfgSeparateSections = true;
|
||||
|
||||
/**
|
||||
* Disables enter-leave logging.
|
||||
*/
|
||||
private boolean cfgSilent = false;
|
||||
|
||||
private String comment = "";
|
||||
|
||||
private TreeMap<String, Property> entries;
|
||||
|
||||
|
||||
private String filename;
|
||||
|
||||
private TreeMap<String, String> keyRename;
|
||||
|
||||
private PC_SortedProperties pr = new PC_SortedProperties();
|
||||
|
||||
private TreeMap<String, String> setValues;
|
||||
|
||||
/**
|
||||
* Create property manager from file path and an initial comment.
|
||||
*
|
||||
* @param filename file with the props
|
||||
* @param comment the initial comment. Use \n in it if you want.
|
||||
*/
|
||||
public PropertyManager(String filename, String comment) {
|
||||
this.filename = filename;
|
||||
this.entries = new TreeMap<String, Property>();
|
||||
this.setValues = new TreeMap<String, String>();
|
||||
this.keyRename = new TreeMap<String, String>();
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load, fix and write to file.
|
||||
*/
|
||||
public void apply() {
|
||||
|
||||
if (!cfgSilent) {
|
||||
//Log.finest("Loading configuration from file \"" + filename + "\"");
|
||||
}
|
||||
|
||||
boolean needsSave = false;
|
||||
try {
|
||||
|
||||
new File((new File(filename)).getParent()).mkdirs();
|
||||
|
||||
pr = PropertiesLoader.loadProperties(pr, new FileInputStream(filename));
|
||||
|
||||
} catch (IOException e) {
|
||||
needsSave = true;
|
||||
pr = new PC_SortedProperties();
|
||||
}
|
||||
|
||||
pr.cfgSeparateSectionsByEmptyLine = cfgSeparateSections;
|
||||
pr.cfgEmptyLineBeforeComment = cfgNewlineBeforeComments;
|
||||
|
||||
ArrayList<String> keyList = new ArrayList<String>();
|
||||
|
||||
// rename keys
|
||||
for (Entry<String, String> entry : keyRename.entrySet()) {
|
||||
if (pr.getProperty(entry.getKey()) == null) {
|
||||
continue;
|
||||
}
|
||||
pr.setProperty(entry.getValue(), pr.getProperty(entry.getKey()));
|
||||
pr.remove(entry.getKey());
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
// set the override values into the freshly loaded properties file
|
||||
for (Entry<String, String> entry : setValues.entrySet()) {
|
||||
pr.setProperty(entry.getKey(), entry.getValue());
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
|
||||
// validate entries one by one, replace with default when needed
|
||||
for (Property entry : entries.values()) {
|
||||
|
||||
keyList.add(entry.name);
|
||||
|
||||
String propOrig = pr.getProperty(entry.name);
|
||||
if (!entry.parse(propOrig)) needsSave = true;
|
||||
if (!cfgNoValidate) {
|
||||
entry.validate();
|
||||
}
|
||||
|
||||
if (entry.entryComment != null) {
|
||||
pr.setKeyComment(entry.name, entry.entryComment);
|
||||
}
|
||||
|
||||
if (propOrig == null || !entry.toString().equals(propOrig)) {
|
||||
|
||||
pr.setProperty(entry.name, entry.toString());
|
||||
|
||||
needsSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
// removed unused props
|
||||
for (String propname : pr.keySet().toArray(new String[pr.size()])) {
|
||||
|
||||
if (!keyList.contains(propname)) {
|
||||
pr.remove(propname);
|
||||
//Log.finest("* Removing unused property \"" + propname + "\" from config file " + filename);
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// save if needed
|
||||
if (needsSave) {
|
||||
try {
|
||||
//Log.finest("* Saving modified property file " + filename);
|
||||
pr.store(new FileOutputStream(filename), comment);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfgSilent) {
|
||||
//Log.finest("Configuration loaded.");
|
||||
}
|
||||
|
||||
setValues.clear();
|
||||
keyRename.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @return the boolean found, or false
|
||||
*/
|
||||
public Boolean bool(String n) {
|
||||
return getBoolean(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newlineBeforeComments put newline before comments
|
||||
*/
|
||||
public void cfgNewlineBeforeComments(boolean newlineBeforeComments) {
|
||||
this.cfgNewlineBeforeComments = newlineBeforeComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param separateSections do separate sections by newline
|
||||
*/
|
||||
public void cfgSeparateSections(boolean separateSections) {
|
||||
this.cfgSeparateSections = separateSections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param silent the cfgSilent to set
|
||||
*/
|
||||
public void cfgSilent(boolean silent) {
|
||||
this.cfgSilent = silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validate enable validation
|
||||
*/
|
||||
public void enableValidation(boolean validate) {
|
||||
this.cfgNoValidate = !validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @return the boolean found, or false
|
||||
*/
|
||||
public Boolean flag(String n) {
|
||||
return getBoolean(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property entry (rarely used)
|
||||
*
|
||||
* @param n key
|
||||
* @return the entry
|
||||
*/
|
||||
private Property get(String n) {
|
||||
try {
|
||||
return entries.get(n);
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @return the boolean found, or false
|
||||
*/
|
||||
public Boolean getBoolean(String n) {
|
||||
try {
|
||||
return entries.get(n).getBoolean();
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer getInt(String n) {
|
||||
return getInteger(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer getInteger(String n) {
|
||||
try {
|
||||
return get(n).getInteger();
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer getNum(String n) {
|
||||
return getInteger(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string property
|
||||
*
|
||||
* @param n key
|
||||
* @return the string found, or null
|
||||
*/
|
||||
public String getString(String n) {
|
||||
try {
|
||||
return get(n).getString();
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer integer(String n) {
|
||||
try {
|
||||
return get(n).getInteger();
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the key pressed? (works only for properties of type KEY)
|
||||
*
|
||||
* @param n key of the key property
|
||||
* @return is pressed
|
||||
*/
|
||||
public Boolean isKeyDown(String n) {
|
||||
try {
|
||||
return entries.get(n).isKeyDown();
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer num(String n) {
|
||||
return getInteger(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putBoolean(String n, boolean d) {
|
||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, null));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putBoolean(String n, boolean d, String comment) {
|
||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putInteger(String n, int d) {
|
||||
entries.put(n, new Property(n, d, PropertyType.INT, null));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putInteger(String n, int d, String comment) {
|
||||
entries.put(n, new Property(n, d, PropertyType.INT, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putKey(String n, int d) {
|
||||
entries.put(n, new Property(n, d, PropertyType.KEY, null));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putKey(String n, int d, String comment) {
|
||||
entries.put(n, new Property(n, d, PropertyType.KEY, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putString(String n, String d) {
|
||||
entries.put(n, new Property(n, d, PropertyType.STRING, null));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putString(String n, String d, String comment) {
|
||||
entries.put(n, new Property(n, d, PropertyType.STRING, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Rename key before doing "apply"; value is preserved
|
||||
*
|
||||
* @param oldKey old key
|
||||
* @param newKey new key
|
||||
*/
|
||||
public void renameKey(String oldKey, String newKey) {
|
||||
keyRename.put(oldKey, newKey);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set value saved to certain key; use to save runtime-changed configuration
|
||||
* values.
|
||||
*
|
||||
* @param key key
|
||||
* @param value the saved value
|
||||
*/
|
||||
public void setValue(String key, Object value) {
|
||||
setValues.put(key, value.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string property
|
||||
*
|
||||
* @param n key
|
||||
* @return the string found, or null
|
||||
*/
|
||||
public String str(String n) {
|
||||
try {
|
||||
return get(n).getString();
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get string property
|
||||
*
|
||||
* @param n key
|
||||
* @return the string found, or null
|
||||
*/
|
||||
public String string(String n) {
|
||||
try {
|
||||
return get(n).getString();
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.porcupine.util;
|
||||
|
||||
|
||||
/**
|
||||
* General purpose string utilities
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* Get if string is in array
|
||||
*
|
||||
* @param needle checked string
|
||||
* @param case_sensitive case sensitive comparision
|
||||
* @param haystack array of possible values
|
||||
* @return is in array
|
||||
*/
|
||||
public static boolean isInArray(String needle, boolean case_sensitive, String... haystack) {
|
||||
if (case_sensitive) {
|
||||
for (String s : haystack) {
|
||||
if (needle.equals(s)) return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (String s : haystack) {
|
||||
if (needle.equalsIgnoreCase(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeat a string
|
||||
*
|
||||
* @param repeated string
|
||||
* @param count
|
||||
* @return output
|
||||
*/
|
||||
public static String repeat(String repeated, int count) {
|
||||
String s = "";
|
||||
for (int i = 0; i < count; i++)
|
||||
s += repeated;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert string to a same-length sequence of # marks
|
||||
*
|
||||
* @param password password
|
||||
* @return encoded
|
||||
*/
|
||||
public static String passwordify(String password) {
|
||||
return passwordify(password, "#");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert string to a same-length sequence of chars
|
||||
*
|
||||
* @param password password
|
||||
* @param replacing character used in output
|
||||
* @return encoded
|
||||
*/
|
||||
public static String passwordify(String password, String replacing) {
|
||||
return repeat(replacing, password.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ordinal version of numbers (1 = 1st, 5 = 5th etc.)
|
||||
*
|
||||
* @param number number
|
||||
* @return ordinal, string
|
||||
*/
|
||||
public static String numberToOrdinal(int number) {
|
||||
if (number % 100 < 4 || number % 100 > 13) {
|
||||
if (number % 10 == 1) return number + "st";
|
||||
if (number % 10 == 2) return number + "nd";
|
||||
if (number % 10 == 3) return number + "rd";
|
||||
}
|
||||
return number + "th";
|
||||
}
|
||||
|
||||
/**
|
||||
* Format number with thousands separated by a dot.
|
||||
*
|
||||
* @param number number
|
||||
* @return string 12.004.225
|
||||
*/
|
||||
public static String formatInt(long number) {
|
||||
String num = number + "";
|
||||
String out = "";
|
||||
String dot = ".";
|
||||
int cnt = 1;
|
||||
for (int i = num.length() - 1; i >= 0; i--) {
|
||||
out = num.charAt(i) + out;
|
||||
if (cnt % 3 == 0 && i > 0) out = dot + out;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.porcupine.util;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Varargs parser<br>
|
||||
* Converts an array of repeated "key, value" pairs to a LinkedHashMap.<br>
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* Object[] array = { "one", 1, "two", 4, "three", 9, "four", 16 };
|
||||
* Map<String, Integer> args = new VarargsParser<String, Integer>().parse(array);
|
||||
* </pre>
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <K> Type for Map keys
|
||||
* @param <V> Type for Map values
|
||||
*/
|
||||
public class VarargsParser<K, V> {
|
||||
/**
|
||||
* Parse array of vararg key, value pairs to a LinkedHashMap.
|
||||
*
|
||||
* @param args varargs
|
||||
* @return LinkedHashMap
|
||||
* @throws ClassCastException in case of incompatible type in the array
|
||||
* @throws IllegalArgumentException in case of invalid array length (odd)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public LinkedHashMap<K, V> parse(Object... args) throws ClassCastException, IllegalArgumentException {
|
||||
LinkedHashMap<K, V> attrs = new LinkedHashMap<K, V>();
|
||||
|
||||
if (args.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Odd number of elements in varargs map!");
|
||||
}
|
||||
|
||||
K key = null;
|
||||
for (Object o : args) {
|
||||
if (key == null) {
|
||||
if (o == null) throw new RuntimeException("Key cannot be NULL in varargs map.");
|
||||
key = (K) o;
|
||||
} else {
|
||||
attrs.put(key, (V) o);
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileLock;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import net.sector.gui.screens.Screen;
|
||||
import net.sector.gui.screens.ScreenMenuMain;
|
||||
import net.sector.gui.screens.ScreenSplash;
|
||||
import net.sector.input.Keys;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.threads.ThreadSaveScreenshot;
|
||||
import net.sector.util.Log;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.math.Calc;
|
||||
import com.porcupine.time.FpsMeter;
|
||||
import com.porcupine.time.Timer;
|
||||
import com.porcupine.util.FileUtils;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SECTOR main class
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class App {
|
||||
|
||||
/** Flag indicating that network threads failed to load. */
|
||||
public static boolean offlineMode = false;
|
||||
|
||||
/** instance */
|
||||
public static App inst;
|
||||
|
||||
private static DisplayMode windowDisplayMode = null;
|
||||
|
||||
/** current screen */
|
||||
public static Screen screen = null;
|
||||
|
||||
private static boolean lockInstance() {
|
||||
final File lockFile = new File(Utils.getGameFolder(), ".lock");
|
||||
try {
|
||||
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
|
||||
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
|
||||
if (fileLock != null) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
fileLock.release();
|
||||
randomAccessFile.close();
|
||||
lockFile.delete();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to remove lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to create and/or lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is if FS
|
||||
*
|
||||
* @return is in fs
|
||||
*/
|
||||
public static boolean isFullscreen() {
|
||||
return Display.isFullscreen();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
inst = new App();
|
||||
try {
|
||||
inst.start();
|
||||
} catch (Throwable t) {
|
||||
showCrashReport(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show crash report dialog with error stack trace.
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
public static void showCrashReport(Throwable error) {
|
||||
|
||||
|
||||
Log.e(error);
|
||||
|
||||
try {
|
||||
inst.deinit();
|
||||
} catch (Throwable t) {}
|
||||
|
||||
JFrame f = new JFrame("SECTOR has crashed!");
|
||||
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
f.getContentPane().setLayout(new BoxLayout(f.getContentPane(), BoxLayout.Y_AXIS));
|
||||
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
error.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
|
||||
String errorLogAsString = "Not found.";
|
||||
String wholeLogAsString = "Not found.";
|
||||
|
||||
try {
|
||||
wholeLogAsString = FileUtils.fileToString(Utils.getGameSubfolder(Constants.FILE_LOG));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
errorLogAsString = FileUtils.fileToString(Utils.getGameSubfolder(Constants.FILE_LOG_E));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
String txt = "";
|
||||
txt = "";
|
||||
txt += "SECTOR HAS CRASHED!\n";
|
||||
txt += "\n";
|
||||
txt += "Please report it to MightyPork:\n";
|
||||
txt += "\tE-mail: ondra@ondrovo.com\n";
|
||||
txt += "\tTwitter: #MightyPork (post log via pastebin.com)\n";
|
||||
txt += "\n";
|
||||
txt += "\n";
|
||||
txt += "Version: " + Constants.VERSION_NAME + "\n";
|
||||
txt += "\n";
|
||||
txt += "\n";
|
||||
txt += "### STACK TRACE ###\n";
|
||||
txt += "\n";
|
||||
txt += exceptionAsString + "\n";
|
||||
txt += "\n";
|
||||
txt += "\n";
|
||||
txt += "### ERROR LOG ###\n";
|
||||
txt += "\n";
|
||||
txt += errorLogAsString + "\n";
|
||||
txt += "\n";
|
||||
txt += "\n";
|
||||
txt += "### FULL LOG ###\n";
|
||||
txt += "\n";
|
||||
txt += wholeLogAsString + "\n";
|
||||
|
||||
|
||||
// Create Scrolling Text Area in Swing
|
||||
JTextArea ta = new JTextArea(txt, 20, 70);
|
||||
ta.setFont(new Font("Courier", 0, 16));
|
||||
ta.setMargin(new Insets(10, 10, 10, 10));
|
||||
ta.setEditable(false);
|
||||
ta.setLineWrap(false);
|
||||
JScrollPane sbrText = new JScrollPane(ta);
|
||||
sbrText.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10), BorderFactory.createEtchedBorder()));
|
||||
sbrText.setWheelScrollingEnabled(true);
|
||||
sbrText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
sbrText.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
|
||||
// Create Quit Button
|
||||
JButton btnQuit = new JButton("Quit");
|
||||
btnQuit.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
btnQuit.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(btnQuit);
|
||||
|
||||
|
||||
f.getContentPane().add(sbrText);
|
||||
f.getContentPane().add(buttonPane);
|
||||
|
||||
// Close when the close button is clicked
|
||||
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
|
||||
|
||||
//Display Frame
|
||||
f.pack(); // Adjusts frame to size of components
|
||||
|
||||
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
f.setLocation((dim.width - f.getWidth()) / 2, (dim.height - f.getHeight()) / 2);
|
||||
|
||||
f.setVisible(true);
|
||||
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void deinit() {
|
||||
Display.destroy();
|
||||
Mouse.destroy();
|
||||
Keyboard.destroy();
|
||||
Sounds.soundManager.clear();
|
||||
AL.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit to OS
|
||||
*/
|
||||
public void exit() {
|
||||
deinit();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current screen
|
||||
*
|
||||
* @return screen
|
||||
*/
|
||||
public Screen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get screen size
|
||||
*
|
||||
* @return size
|
||||
*/
|
||||
public Coord getSize() {
|
||||
return new Coord(Display.getWidth(), Display.getHeight());
|
||||
}
|
||||
|
||||
// INIT
|
||||
|
||||
private void init() throws LWJGLException {
|
||||
|
||||
GameConfig.initLoad();
|
||||
|
||||
Log.enable(GameConfig.logEnabled);
|
||||
Log.setPrintToStdout(GameConfig.logStdOut);
|
||||
|
||||
Log.i("Game version: " + Constants.VERSION_NAME);
|
||||
|
||||
// init display
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(Constants.WINDOW_SIZE_X, Constants.WINDOW_SIZE_Y));
|
||||
Display.setResizable(GameConfig.enableResize);
|
||||
Display.setVSyncEnabled(GameConfig.enableVsync);
|
||||
Display.setTitle(Constants.TITLEBAR);
|
||||
|
||||
int samples = GameConfig.antialiasing;
|
||||
while (true) {
|
||||
try {
|
||||
Display.create(new PixelFormat().withSamples(samples).withAlphaBits(4));
|
||||
Log.i("Created display with " + samples + "x multisampling.");
|
||||
break;
|
||||
} catch (LWJGLException e) {
|
||||
Log.w("Failed to create display with " + samples + "x multisampling, trying " + samples / 2 + "x.");
|
||||
if (samples >= 2) {
|
||||
samples /= 2;
|
||||
} else if (samples == 1) {
|
||||
samples = 0;
|
||||
} else if (samples == 0) {
|
||||
Log.e("Could not create display.", e);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sounds.soundManager.setMaxSources(256);
|
||||
Sounds.soundManager.init();
|
||||
Sounds.setListener(Constants.LISTENER_POS);
|
||||
applySoundConfig();
|
||||
Mouse.create();
|
||||
Keyboard.create();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
Keys.init();
|
||||
|
||||
LoadingManager.loadForSplash();
|
||||
|
||||
StaticInitializer.initOnStartup();
|
||||
|
||||
|
||||
//Display.update();
|
||||
if (GameConfig.startInFullscreen) {
|
||||
switchFullscreen();
|
||||
Display.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply sounds configuration (from config)
|
||||
*/
|
||||
public void applySoundConfig() {
|
||||
Sounds.soundManager.setSoundVolume(Calc.clampf(GameConfig.audioVolumeSound / 100f, 0, 1));
|
||||
Sounds.soundManager.setMusicVolume(Calc.clampf(GameConfig.audioVolumeMusic / 100f, 0, 1));
|
||||
}
|
||||
|
||||
private void start() throws LWJGLException {
|
||||
|
||||
if (!lockInstance()) {
|
||||
System.out.println("No more than 1 instance of Sector can be running at a time.");
|
||||
|
||||
JOptionPane.showMessageDialog(null, "SECTOR is already running.", "Instance error", JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.enable(true);
|
||||
Log.setPrintToStdout(true);
|
||||
|
||||
init();
|
||||
mainLoop();
|
||||
deinit();
|
||||
}
|
||||
|
||||
// INIT END
|
||||
|
||||
|
||||
// UPDATE LOOP
|
||||
|
||||
/** fps meter */
|
||||
public FpsMeter fpsMeter;
|
||||
|
||||
/** timer */
|
||||
public Timer timer;
|
||||
|
||||
private long timerAfterResize;
|
||||
|
||||
private void mainLoop() {
|
||||
screen = new ScreenSplash();
|
||||
|
||||
screen.init();
|
||||
|
||||
timer = new Timer(Constants.FPS_UPDATE);
|
||||
fpsMeter = new FpsMeter();
|
||||
|
||||
while (!Display.isCloseRequested()) {
|
||||
glLoadIdentity();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
int j = timer.ticksMissed;
|
||||
|
||||
if (j > 1) fpsMeter.drop(j - 1);
|
||||
timer.update();
|
||||
|
||||
for (int i = 0; i < j; i++) {
|
||||
screen.update();
|
||||
}
|
||||
|
||||
fpsMeter.frame();
|
||||
float delta = timer.renderDeltaTime;
|
||||
if (!screen.deltaEnabled()) delta = 0;
|
||||
screen.render(delta);
|
||||
Display.update();
|
||||
|
||||
// boolean fs2 = Display.isFullscreen();
|
||||
// if (fs != fs2) {
|
||||
// screen.onFullscreenChange();
|
||||
// fs = fs2;
|
||||
// }
|
||||
|
||||
if (Keys.justPressed(Keyboard.KEY_F11)) {
|
||||
Log.f2("F11, toggle fullscreen.");
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
Keys.destroyChangeState(Keyboard.KEY_F11);
|
||||
}
|
||||
|
||||
if (Keys.justPressed(Keyboard.KEY_F2)) {
|
||||
Log.f2("F2, taking screenshot.");
|
||||
takeScreenshot();
|
||||
Keys.destroyChangeState(Keyboard.KEY_F2);
|
||||
}
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
|
||||
Log.f2("Ctrl+Q, force quit.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_Q);
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_M)) {
|
||||
Log.f2("Ctrl+M, go to main menu.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_M);
|
||||
screen.rootPanel.onClose();
|
||||
screen.rootPanel.onBlur();
|
||||
replaceScreen(new ScreenMenuMain());
|
||||
}
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
|
||||
Log.f2("Ctrl+F, switch fullscreen.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_F);
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
}
|
||||
}
|
||||
|
||||
if (Display.wasResized()) {
|
||||
screen.onWindowResize();
|
||||
timerAfterResize = 0;
|
||||
} else {
|
||||
timerAfterResize++;
|
||||
if (timerAfterResize > Constants.FPS_UPDATE * 0.3) {
|
||||
timerAfterResize = 0;
|
||||
int x = Display.getX();
|
||||
int y = Display.getY();
|
||||
|
||||
int w = Display.getWidth();
|
||||
int h = Display.getHeight();
|
||||
if (w % 2 != 0 || h % 2 != 0) {
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(w - w % 2, h - h % 2));
|
||||
screen.onWindowResize();
|
||||
Display.setLocation(x, y);
|
||||
} catch (LWJGLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Display.sync(Constants.FPS_RENDER);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Your graphics card driver does not support fullscreen properly.", t);
|
||||
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
} catch (LWJGLException e) {
|
||||
Log.e("Error going back from corrupted fullscreen.");
|
||||
showCrashReport(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SuperContext.saveUserList(); // just to make sure nothing gets lost.
|
||||
}
|
||||
|
||||
// UPDATE LOOP END
|
||||
|
||||
|
||||
private void takeScreenshot() {
|
||||
Sounds.shutter.playEffect(1, 1f, false);
|
||||
glReadBuffer(GL_FRONT);
|
||||
int width = Display.getDisplayMode().getWidth();
|
||||
int height = Display.getDisplayMode().getHeight();
|
||||
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
new ThreadSaveScreenshot(buffer, width, height, bpp).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace screen
|
||||
*
|
||||
* @param newScreen new screen
|
||||
*/
|
||||
public void replaceScreen(Screen newScreen) {
|
||||
screen = newScreen;
|
||||
screen.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace screen, don't init it
|
||||
*
|
||||
* @param newScreen new screen
|
||||
*/
|
||||
public void replaceScreenNoInit(Screen newScreen) {
|
||||
screen = newScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle FS if possible
|
||||
*/
|
||||
public void switchFullscreen() {
|
||||
try {
|
||||
if (!Display.isFullscreen()) {
|
||||
Log.f3("Entering fullscreen.");
|
||||
// save window resize
|
||||
windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
|
||||
|
||||
Display.setDisplayMode(Display.getDesktopDisplayMode());
|
||||
Display.setFullscreen(true);
|
||||
Display.update();
|
||||
//
|
||||
//
|
||||
// DisplayMode mode = Display.getDesktopDisplayMode(); //findDisplayMode(WIDTH, HEIGHT);
|
||||
// Display.setDisplayModeAndFullscreen(mode);
|
||||
} else {
|
||||
Log.f3("Leaving fullscreen.");
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed to toggle fullscreen mode.", t);
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
} catch (Throwable t1) {
|
||||
showCrashReport(t1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 1 for window, 2 for fullscreen.
|
||||
*
|
||||
* @return 1 or 2
|
||||
*/
|
||||
public static int fs2() {
|
||||
return isFullscreen() ? 2 : 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Sector constants
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Constants {
|
||||
|
||||
// STRINGS
|
||||
public static final int VERSION_NUMBER = 18;
|
||||
public static final String VERSION_NAME = "1.8";
|
||||
public static final String TITLEBAR = "SECTOR " + VERSION_NAME + " - Game by MightyPork - www.ondrovo.com/sector";
|
||||
|
||||
// FILES+DIRS
|
||||
public static final String APP_DIR = "sector";
|
||||
|
||||
public static final String DIR_LEVELS_SHARED = "levels/shared";
|
||||
public static final String DIR_LEVELS_LOCAL = "levels/local";
|
||||
|
||||
public static final String DIR_LASTSHIP_LOCAL = "last_ship/local";
|
||||
public static final String DIR_LASTSHIP_SHARED = "last_ship/shared";
|
||||
public static final String DIR_LASTSHIP_INTERNAL = "last_ship/internal";
|
||||
|
||||
public static final String DIR_HIGHSCORE_LOCAL = "highscore/local";
|
||||
public static final String DIR_HIGHSCORE_SHARED = "highscore/shared";
|
||||
public static final String DIR_HIGHSCORE_INTERNAL = "highscore/internal";
|
||||
|
||||
public static final String SUFFIX_SHIP = "ship";
|
||||
public static final String DIR_SHIPS = "ships";
|
||||
|
||||
public static final String DIR_SCREENSHOTS = "screenshots";
|
||||
|
||||
public static final String FILE_CONFIG = "users.ion";
|
||||
public static final String FILE_LOG = "Sector.log";
|
||||
public static final String FILE_LOG_E = "Sector_errors.log";
|
||||
|
||||
// NETWORK
|
||||
public static final String WEB_URL = "http://www.ondrovo.com/sector/download";
|
||||
public static final String SERVER_URL = "http://www.ondrovo.com/sector/api/server.php";
|
||||
|
||||
// LIGHT
|
||||
public static final float LIGHT_AMBIENT = 0.3F;
|
||||
public static final float LIGHT_SPECULAR = 0.3F;
|
||||
public static final float LIGHT_DIFFUSE = 0.3F;
|
||||
public static final Coord LIGHT_POS = new Coord(-3, 5, 5);
|
||||
public static final float LIGHT_ATTR = 1;
|
||||
|
||||
public static final float SCENE_MAT_AMBIENT = 0.15F;
|
||||
public static final float SCENE_MAT_SPECULAR = 0.4F;
|
||||
public static final float SCENE_MAT_DIFFUSE = 0.3F;
|
||||
|
||||
// CAMERA & SCENE
|
||||
public static final Coord CAM_POS = new Coord(0, 3.3, 5);
|
||||
public static final Coord CAM_LOOKAT = new Coord(0, 2.3, 0);
|
||||
public static final float CAM_ANGLE = 45;
|
||||
public static final double CAM_NEAR = 0.1;
|
||||
public static final double FOG_START = 80;
|
||||
public static final double CAM_FAR = 100;
|
||||
|
||||
// AUDIO
|
||||
public static final Coord LISTENER_POS = new Coord(0, 5, 0);
|
||||
|
||||
// TIMING
|
||||
public static final int FPS_UPDATE = 55;
|
||||
public static final double SPEED_MUL = 100D / FPS_UPDATE;
|
||||
|
||||
public static final int FPS_RENDER = 200; // max
|
||||
|
||||
// LOGGING GROUPS
|
||||
public static final boolean LOG_DRIVERS = false;
|
||||
public static final boolean LOG_FONTS = false;
|
||||
public static final boolean LOG_MODELS = false;
|
||||
public static final boolean LOG_TEXTURES = false;
|
||||
public static final boolean LOG_SOUNDS = false;
|
||||
public static final boolean LOG_XML_LOADING = false;
|
||||
public static final boolean LOG_COUNTRIES = false;
|
||||
public static final boolean LOG_ZONES = false;
|
||||
|
||||
// INITIAL WINDOW SIZE (later loaded from config file)
|
||||
public static final int WINDOW_SIZE_X = 800;
|
||||
public static final int WINDOW_SIZE_Y = 600;
|
||||
|
||||
public static final int PARTICLE_COUNT_LIMIT = 3000;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import net.sector.level.highscore.HighscoreEntry;
|
||||
import net.sector.level.highscore.HighscoreTable;
|
||||
import net.sector.network.ProfileList;
|
||||
import net.sector.network.UserProfile;
|
||||
|
||||
import com.porcupine.ion.Ion;
|
||||
|
||||
|
||||
/**
|
||||
* Class adding ION marks for custom ionizable objects
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class CustomIonMarks {
|
||||
|
||||
// ION
|
||||
public static final byte HIGHSCORE_TABLE = 20;
|
||||
public static final byte HIGHSCORE_ENTRY = 21;
|
||||
public static final byte USER_PROFILE_LIST = 22;
|
||||
public static final byte USER_PROFILE = 23;
|
||||
|
||||
// public static final byte LEVEL_LIST = 24;
|
||||
// public static final byte LEVEL_CONTAINER = 25;
|
||||
|
||||
/**
|
||||
* Register ion marks
|
||||
*/
|
||||
public static void init() {
|
||||
Ion.registerIonizable(HIGHSCORE_ENTRY, HighscoreEntry.class);
|
||||
Ion.registerIonizable(HIGHSCORE_TABLE, HighscoreTable.class);
|
||||
Ion.registerIonizable(USER_PROFILE_LIST, ProfileList.class);
|
||||
Ion.registerIonizable(USER_PROFILE, UserProfile.class);
|
||||
// Ion.registerIonizable(LEVEL_LIST, NetLevelList.class);
|
||||
// Ion.registerIonizable(LEVEL_CONTAINER, NetLevelContainer.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import com.porcupine.util.PropertyManager;
|
||||
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class GameConfig {
|
||||
|
||||
public static final String pk_win_resize = "window.resizable";
|
||||
public static final String pk_splash = "window.splash";
|
||||
public static final String pk_win_fs = "window.start.fullscreen";
|
||||
|
||||
public static final String pk_antialiasing = "graphics.antialiasing";
|
||||
public static final String pk_vsync = "graphics.vsync";
|
||||
|
||||
public static final String pk_in_mouse = "input.mouse.sensitivity";
|
||||
|
||||
public static final String pk_sound_volume = "audio.volume.sound";
|
||||
public static final String pk_music_volume = "audio.volume.music";
|
||||
|
||||
public static final String pk_log = "log.enable";
|
||||
public static final String pk_log_stdout = "log.toStdOut";
|
||||
|
||||
|
||||
public static final String pk_key_shield = "key.shield";
|
||||
|
||||
public static final String pk_update_notifications = "feature.update_alerts";
|
||||
|
||||
|
||||
|
||||
public static boolean enableUpdateAlerts;
|
||||
public static boolean startInFullscreen;
|
||||
public static boolean enableVsync;
|
||||
public static boolean enableSplash;
|
||||
public static boolean enableResize;
|
||||
public static int mouseSensitivity;
|
||||
public static int audioVolumeSound;
|
||||
public static int audioVolumeMusic;
|
||||
public static int antialiasing;
|
||||
public static boolean logEnabled;
|
||||
public static boolean logStdOut;
|
||||
|
||||
public static int keyShield = Keyboard.KEY_LCONTROL;
|
||||
|
||||
public static boolean colliderWireframe = false;
|
||||
private static PropertyManager p;
|
||||
|
||||
/**
|
||||
* Init property manager and load from file.
|
||||
*/
|
||||
public static void initLoad() {
|
||||
p = new PropertyManager(Utils.getGameFolder() + "/config.ini", "Main SECTOR's configuration file.");
|
||||
|
||||
p.cfgSeparateSections(true);
|
||||
p.cfgNewlineBeforeComments(false);
|
||||
|
||||
p.putBoolean(pk_win_fs, false, "Start in fullscreen.");
|
||||
p.putBoolean(pk_update_notifications, true, "Show update notifications.");
|
||||
p.putInteger(pk_antialiasing, 4, "Antialiasing 0, 2, 4, 8; Depends of graphic card.");
|
||||
p.putBoolean(pk_splash, true, "Enable splash animation");
|
||||
p.putBoolean(pk_vsync, true, "Enable vsync");
|
||||
p.putBoolean(pk_win_resize, true, "Make window resizable");
|
||||
|
||||
p.putInteger(pk_in_mouse, 1000, "Mouse sensitivity for ship movement. 1000 is the default sensitivity.");
|
||||
|
||||
p.putInteger(pk_sound_volume, 100, "Sound volume, 0-100.");
|
||||
p.putInteger(pk_music_volume, 100, "Music volume, 0-100.");
|
||||
|
||||
p.putBoolean(pk_log, true, "Enable logging.");
|
||||
p.putBoolean(pk_log_stdout, false, "Print log messages also to stdout.");
|
||||
|
||||
p.putKey(pk_key_shield, Keyboard.KEY_LCONTROL);
|
||||
|
||||
saveLoad();
|
||||
useLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property value (call saveLoad and useLoaded afterwards)
|
||||
*
|
||||
* @param key key to set
|
||||
* @param newValue new value assigned
|
||||
*/
|
||||
public static void setNewProp(String key, Object newValue) {
|
||||
p.setValue(key, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes and load from file.
|
||||
*/
|
||||
public static void saveLoad() {
|
||||
p.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store loaded config to fields
|
||||
*/
|
||||
public static void useLoaded() {
|
||||
startInFullscreen = p.getBoolean(pk_win_fs);
|
||||
enableSplash = p.getBoolean(pk_splash);
|
||||
enableVsync = p.getBoolean(pk_vsync);
|
||||
enableResize = p.getBoolean(pk_win_resize);
|
||||
|
||||
mouseSensitivity = p.getInteger(pk_in_mouse);
|
||||
|
||||
antialiasing = p.getInteger(pk_antialiasing);
|
||||
audioVolumeSound = p.getInteger(pk_sound_volume);
|
||||
audioVolumeMusic = p.getInteger(pk_music_volume);
|
||||
|
||||
logEnabled = p.getBoolean(pk_log);
|
||||
logStdOut = p.getBoolean(pk_log_stdout);
|
||||
|
||||
keyShield = p.getInt(pk_key_shield);
|
||||
|
||||
enableUpdateAlerts = p.getBoolean(pk_update_notifications);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.fonts.Fonts;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.Textures;
|
||||
import net.sector.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class responsible for resource loading.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class LoadingManager {
|
||||
|
||||
private static final int groups = 4;
|
||||
|
||||
private static int lastloaded = -1;
|
||||
private static long beginTime;
|
||||
|
||||
private static void timerStart() {
|
||||
beginTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static double timerGet() {
|
||||
return (System.currentTimeMillis() - beginTime) / 1000D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load resources needed to animate splash
|
||||
*/
|
||||
public static void loadForSplash() {
|
||||
Log.f1("Loading resources needed for Splash screen.");
|
||||
|
||||
timerStart();
|
||||
Fonts.loadForSplash();
|
||||
Log.i("LOADING: Fonts for Splash loaded in " + timerGet() + "s");
|
||||
|
||||
timerStart();
|
||||
Sounds.loadForSplash();
|
||||
Log.i("LOADING: Sounds for Splash loaded in " + timerGet() + "s");
|
||||
|
||||
timerStart();
|
||||
Textures.loadForSplash();
|
||||
Log.i("LOADING: Textures for Splash loaded in " + timerGet() + "s");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info text for resource group (eg. Loading sounds...)
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
public static String getSplashInfo() {
|
||||
switch (lastloaded + 1) {
|
||||
case 0:
|
||||
return "Loading fonts...";
|
||||
case 1:
|
||||
return "Loading textures...";
|
||||
case 2:
|
||||
return "Loading models...";
|
||||
case 3:
|
||||
return "Loading sounds...";
|
||||
}
|
||||
return "Loading...";
|
||||
}
|
||||
|
||||
/**
|
||||
* Load next resource group
|
||||
*/
|
||||
public static void loadGroup() {
|
||||
|
||||
switch (lastloaded + 1) {
|
||||
case 0:
|
||||
timerStart();
|
||||
Fonts.load();
|
||||
Log.i("LOADING: Fonts loaded in " + timerGet() + "s");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
timerStart();
|
||||
Textures.load();
|
||||
Log.i("LOADING: Textures loaded in " + timerGet() + "s");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
timerStart();
|
||||
|
||||
// something may change while loading models
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
glPushMatrix();
|
||||
|
||||
Models.load();
|
||||
|
||||
glPopMatrix();
|
||||
glPopAttrib();
|
||||
|
||||
Log.i("LOADING: Models loaded in " + timerGet() + "s");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
timerStart();
|
||||
Sounds.load();
|
||||
Log.i("LOADING: Sounds loaded in " + timerGet() + "s");
|
||||
break;
|
||||
}
|
||||
|
||||
lastloaded++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has more resource groups to load
|
||||
*
|
||||
* @return has more
|
||||
*/
|
||||
public static boolean hasMoreGroups() {
|
||||
return lastloaded < groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after all resources have been loaded.
|
||||
*/
|
||||
public static void onResourcesLoaded() {
|
||||
Log.i("LOADING: All resources loaded.");
|
||||
|
||||
StaticInitializer.initPostLoad();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.IDamageable;
|
||||
|
||||
|
||||
public class NullDamageSource implements IDamageable {
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {}
|
||||
|
||||
@Override
|
||||
public boolean isDead() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package net.sector;
|
||||
|
||||
|
||||
import javax.naming.TimeLimitExceededException;
|
||||
|
||||
import net.sector.gui.widgets.ColorScheme;
|
||||
import net.sector.level.drivers.FunctorRegistry;
|
||||
import net.sector.level.sequence.LevelNodeRegistry;
|
||||
import net.sector.level.ship.DiscoveryRegistry;
|
||||
import net.sector.level.ship.PieceRegistry;
|
||||
import net.sector.level.spawners.EntityRegistry;
|
||||
import net.sector.network.CountryList;
|
||||
import net.sector.threads.*;
|
||||
import net.sector.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Initialization utility, initializing all the static stuff that is needed
|
||||
* before starting main loop.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class StaticInitializer {
|
||||
|
||||
/**
|
||||
* Init static things and start threads.<br>
|
||||
* This is called on startup, even before the splash screen.
|
||||
*/
|
||||
public static void initOnStartup() {
|
||||
|
||||
CustomIonMarks.init();
|
||||
|
||||
DiscoveryRegistry.init();
|
||||
FunctorRegistry.init();
|
||||
LevelNodeRegistry.init();
|
||||
|
||||
CountryList.init();
|
||||
ColorScheme.init();
|
||||
EntityRegistry.init();
|
||||
|
||||
// load user profiles
|
||||
new ThreadLoadAndActivateProfiles().start();
|
||||
|
||||
// check latest version.
|
||||
new ThreadCheckLatestVersion().start();
|
||||
|
||||
// download new levels.
|
||||
new ThreadDownloadNewLevels().start();
|
||||
|
||||
// load local and internal levels
|
||||
new ThreadLoadOfflineLevels().start();
|
||||
|
||||
// load drivers.
|
||||
new ThreadLoadBasicDrivers().start();
|
||||
|
||||
}
|
||||
|
||||
private static void logThreadStatus() {
|
||||
Log.f2("\nLOADING THREADS:");
|
||||
Log.f2("\tThreadLoadBasicDrivers: " + ThreadLoadBasicDrivers.status);
|
||||
Log.f2("\tThreadLoadOfflineLevels: " + ThreadLoadOfflineLevels.status);
|
||||
Log.f2("\tThreadDownloadNewLevels: " + ThreadDownloadNewLevels.status);
|
||||
Log.f2("\tThreadCheckLatestVersion: " + ThreadCheckLatestVersion.status);
|
||||
Log.f2("\tThreadLoadAndActivateProfiles: " + ThreadLoadAndActivateProfiles.status);
|
||||
Log.f2("\n\n");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all.
|
||||
*/
|
||||
public static void initPostLoad() {
|
||||
|
||||
// initialize piece and discovery registry
|
||||
// put here, so that ThreadUnpackLevels can build ship bundles.
|
||||
PieceRegistry.init();
|
||||
|
||||
Log.f1("Waiting for loading threads to finish...");
|
||||
|
||||
logThreadStatus();
|
||||
|
||||
long beginTime = System.currentTimeMillis();
|
||||
|
||||
// wait for threads.
|
||||
while (true) {
|
||||
if (System.currentTimeMillis() - beginTime > 8000) {
|
||||
Log.w("Loading time limit exceeded.");
|
||||
logThreadStatus();
|
||||
|
||||
if (ThreadLoadBasicDrivers.status == EThreadStatus.WORKING || ThreadLoadOfflineLevels.status == EThreadStatus.WORKING) {
|
||||
Log.w("Cannot continue, aborting startup.");
|
||||
App.showCrashReport(new TimeLimitExceededException("Resource loading thread(s) timed out."));
|
||||
} else {
|
||||
// network problem..
|
||||
Log.w("Could not connect to server, entering offline mode.");
|
||||
App.offlineMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ThreadLoadBasicDrivers.status == EThreadStatus.WORKING) continue;
|
||||
if (ThreadLoadOfflineLevels.status == EThreadStatus.WORKING) continue;
|
||||
if (ThreadDownloadNewLevels.status == EThreadStatus.WORKING) continue;
|
||||
if (ThreadCheckLatestVersion.status == EThreadStatus.WORKING) continue;
|
||||
if (ThreadLoadAndActivateProfiles.status == EThreadStatus.WORKING) continue;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (ThreadDownloadNewLevels.status == EThreadStatus.FAILURE
|
||||
|| ThreadCheckLatestVersion.status == EThreadStatus.FAILURE
|
||||
|| ThreadLoadAndActivateProfiles.status == EThreadStatus.FAILURE) {
|
||||
|
||||
Log.w("Could not connect to server, entering offline mode.");
|
||||
App.offlineMode = true;
|
||||
|
||||
}
|
||||
|
||||
Log.f1("Unpacking level containers...");
|
||||
new ThreadUnpackLevels().start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.sector.annotations;
|
||||
|
||||
|
||||
/**
|
||||
* Describes internal method or field which should not be accessed from outside.<br>
|
||||
* Used where private is not possible, but public does not mean available for
|
||||
* anyone.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public @interface Internal {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.sector.annotations;
|
||||
|
||||
|
||||
/**
|
||||
* Annotation for apparently unused methods, to indicate that they can be safely
|
||||
* removed when preparing the game for final release
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public @interface Unused {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.sector.collision;
|
||||
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Collider object, used to hold information about object positions, rotations
|
||||
* and to detect their collisions.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class Collider {
|
||||
/** Central point */
|
||||
public Coord pos = new Coord();
|
||||
|
||||
/**
|
||||
* Check if collides with other collider
|
||||
*
|
||||
* @param other other collider
|
||||
* @return collides
|
||||
*/
|
||||
public abstract boolean collidesWith(Collider other);
|
||||
|
||||
/**
|
||||
* Render debug sphere
|
||||
*/
|
||||
public abstract void render();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package net.sector.collision;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.level.ship.modules.ShipBody;
|
||||
import net.sector.level.ship.modules.pieces.Piece;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Player ship collider (made up of pieces)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ColliderPlayerShip extends ColliderSphere {
|
||||
|
||||
/** Ship entity */
|
||||
public Entity entity;
|
||||
/** Entity scene */
|
||||
public Scene scene;
|
||||
|
||||
/**
|
||||
* Player ship collider
|
||||
*
|
||||
* @param center central coord
|
||||
* @param shipBody ship body.
|
||||
*/
|
||||
public ColliderPlayerShip(Coord center, ShipBody shipBody) {
|
||||
super(center, Calc.pythC(shipBody.sizeX * ShipBody.pieceDist, shipBody.sizeX * ShipBody.pieceDist) / 2d);
|
||||
body = shipBody;
|
||||
body.setCollider(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Player ship collider
|
||||
*
|
||||
* @param center central coord
|
||||
* @param width body width [x]
|
||||
* @param height body height [z]
|
||||
*/
|
||||
public ColliderPlayerShip(Coord center, int width, int height) {
|
||||
super(center, 0);
|
||||
body = new ShipBody(width, height);
|
||||
this.radius = Calc.pythC(width * ShipBody.pieceDist, height * ShipBody.pieceDist) / 2d;
|
||||
body.setCollider(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook called when the ship was added to scene
|
||||
*
|
||||
* @param entity ship entity
|
||||
*/
|
||||
public void onAddedToScene(Entity entity) {
|
||||
this.entity = entity;
|
||||
this.scene = entity.scene;
|
||||
body.onReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rotation around Y in delta time
|
||||
*
|
||||
* @param delta delta time
|
||||
* @return Y rot.
|
||||
*/
|
||||
public double getRotY(double delta) {
|
||||
return entity.rotAngle.delta(delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rotation around Z in delta time
|
||||
*
|
||||
* @param delta delta time
|
||||
* @return Z rot.
|
||||
*/
|
||||
public double getRotZ(double delta) {
|
||||
return Calc.clampd((-entity.getMotion().x) * 150, -25, 25);
|
||||
}
|
||||
|
||||
/** Ship body */
|
||||
public ShipBody body;
|
||||
/** Colliders collided during the last impact */
|
||||
public ArrayList<Piece> lastCollided = null;
|
||||
/** Last impact was caused by shield. */
|
||||
public boolean collidingShield = false;
|
||||
|
||||
@Override
|
||||
public boolean collidesWith(Collider other) {
|
||||
if (other instanceof ColliderSphere) {
|
||||
ColliderSphere otherSphere = (ColliderSphere) other;
|
||||
if (pos.distTo(otherSphere.pos) < radius + otherSphere.radius) {
|
||||
// collides with the outer sphere
|
||||
if (body.isShieldRunning()) {
|
||||
lastCollided = null;
|
||||
collidingShield = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
collidingShield = false;
|
||||
|
||||
ArrayList<Piece> colliding = body.getCollidingPieces(other);
|
||||
if (colliding.size() == 0) colliding = null;
|
||||
if (colliding != null) {
|
||||
lastCollided = colliding;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
lastCollided = null;
|
||||
return false;
|
||||
}
|
||||
throw new RuntimeException("Collision test not implemented for " + Calc.className(this) + " and " + Calc.className(other));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package net.sector.collision;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import org.lwjgl.util.glu.Sphere;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Simple spheric collider
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ColliderSphere extends Collider {
|
||||
|
||||
/** sphere radius */
|
||||
public double radius = 1.0;
|
||||
|
||||
/**
|
||||
* Sphere collider
|
||||
*
|
||||
* @param center central point
|
||||
* @param radius sphere radius
|
||||
*/
|
||||
public ColliderSphere(Coord center, double radius) {
|
||||
pos.setTo(center);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collidesWith(Collider other) {
|
||||
if (other instanceof ColliderSphere) {
|
||||
ColliderSphere otherSphere = (ColliderSphere) other;
|
||||
return pos.distTo(otherSphere.pos) < radius + otherSphere.radius;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Collision test not implemented for " + Calc.className(this) + " and " + Calc.className(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
glPushMatrix();
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
glLoadIdentity();
|
||||
glTranslated(pos.x, pos.y, -pos.z);
|
||||
glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
Sphere sp = new Sphere();
|
||||
sp.draw((float) this.radius, 6, 6);
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glPopAttrib();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coord
|
||||
*
|
||||
* @return pos coord
|
||||
*/
|
||||
public Coord getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.sector.collision;
|
||||
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Sphere collider, never colliding
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ColliderSphereFake extends ColliderSphere {
|
||||
|
||||
/**
|
||||
* Sphere collider
|
||||
*
|
||||
* @param center center
|
||||
* @param radius sphere radius
|
||||
*/
|
||||
public ColliderSphereFake(Coord center, double radius) {
|
||||
super(center, radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collidesWith(Collider other) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
package net.sector.collision;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.GameConfig;
|
||||
import net.sector.effects.ParticleManager;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.player.EntityPlayerShip;
|
||||
import net.sector.entities.shots.EntityShotBase;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
import com.porcupine.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* 2D collider map with Z-axis zones
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Scene {
|
||||
|
||||
// number of zones
|
||||
private static final int ZONES = 18;
|
||||
// size of one zone in GL units
|
||||
private static final double ZONE_WIDTH = 6;
|
||||
|
||||
/** collision zones */
|
||||
protected ColliderZone[] zones = new ColliderZone[ZONES];
|
||||
|
||||
/** set of all entities in this map */
|
||||
public ArrayList<Entity> allEntities = new ArrayList<Entity>();
|
||||
|
||||
/** set of all entities to add when we can */
|
||||
public ArrayList<Entity> toAdd = new ArrayList<Entity>();
|
||||
|
||||
/** Particle manager */
|
||||
public ParticleManager particles = new ParticleManager();
|
||||
|
||||
/** Current player ship instance */
|
||||
public EntityPlayerShip playerShip = null;
|
||||
private long lastt;
|
||||
|
||||
/**
|
||||
* Add effect to particle manager
|
||||
*
|
||||
* @param particle particle added
|
||||
*/
|
||||
public void addEffect(Particle particle) {
|
||||
particles.add(particle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make new collider map
|
||||
*/
|
||||
public Scene() {
|
||||
for (int i = 0; i < ZONES; i++) {
|
||||
zones[i] = new ColliderZone(i * ZONE_WIDTH, (i + 1) * ZONE_WIDTH, (i == 0 ? -1 : i == ZONES - 1 ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zone of entities within this collider.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
private class ColliderZone extends HashSet<Entity> {
|
||||
|
||||
public double zFrom = 0;
|
||||
public double zTo = 10;
|
||||
|
||||
/**
|
||||
* Collider zone
|
||||
*
|
||||
* @param zFrom starting z index
|
||||
* @param zTo ending z index
|
||||
* @param position position. -1 first, 0 middle, 1 last;
|
||||
*/
|
||||
public ColliderZone(double zFrom, double zTo, int position) {
|
||||
this.zFrom = zFrom;
|
||||
this.zTo = zTo;
|
||||
if (position == -1) this.zFrom = -20;
|
||||
if (position == 1) this.zTo = ZONE_WIDTH * (ZONES + 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure all entities are in the proper zones.
|
||||
*/
|
||||
public void arrangeEntities() {
|
||||
|
||||
// move entities that no longer belong to this zone to other zones
|
||||
// remove dead entities
|
||||
Iterator<Entity> i = this.iterator();
|
||||
while (i.hasNext()) {
|
||||
Entity e = i.next();
|
||||
|
||||
if (e.isDead()) {
|
||||
allEntities.remove(e);
|
||||
i.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all entities for collisions
|
||||
*/
|
||||
public void collideAndReact() {
|
||||
|
||||
Iterator<Entity> i = this.iterator();
|
||||
loop1:
|
||||
while (i.hasNext()) {
|
||||
|
||||
Entity entity1 = i.next();
|
||||
if (entity1.isDead()) {
|
||||
allEntities.remove(entity1);
|
||||
i.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity1.isDead()) continue loop1;
|
||||
|
||||
Iterator<Entity> i2 = this.iterator();
|
||||
loop2:
|
||||
while (i2.hasNext()) {
|
||||
Entity entity2 = i2.next();
|
||||
if (entity2.isDead()) continue loop2;
|
||||
|
||||
if (entity1 != entity2) {
|
||||
if (entity1.collidesWith(entity2) && entity2.collidesWith(entity1)) {
|
||||
if (entity1.collidePriority > entity2.collidePriority) {
|
||||
entity1.react(entity2);
|
||||
} else {
|
||||
entity2.react(entity1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new entity to the correct zones and to global list
|
||||
*
|
||||
* @param added added entity
|
||||
*/
|
||||
public void add(Entity added) {
|
||||
if (added.getType() == EEntity.PLAYER) playerShip = (EntityPlayerShip) added;
|
||||
toAdd.add(added);
|
||||
}
|
||||
|
||||
private void addAllWaiting() {
|
||||
for (Entity added : toAdd) {
|
||||
added.setScene(this);
|
||||
allEntities.add(added);
|
||||
added.onAddedToScene();
|
||||
for (ColliderZone zone : zones) {
|
||||
if (added.belongsToZone(zone.zFrom, zone.zTo)) {
|
||||
zone.add(added);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toAdd.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove entity from all lists and zones
|
||||
*
|
||||
* @param removed removed entity
|
||||
*/
|
||||
public void remove(Entity removed) {
|
||||
//remove(removed, false);
|
||||
removed.setDead();
|
||||
}
|
||||
|
||||
|
||||
// List<Entity> removeList = new ArrayList<Entity>();
|
||||
//
|
||||
// public void remove(Entity removed, boolean now) {
|
||||
// if(now) {
|
||||
// if(removeList==null||removeList.isEmpty()) return;
|
||||
// for(Entity nowRemoved:removeList) {
|
||||
// allEntities.remove(nowRemoved);
|
||||
// for (ColliderZone zone : zones) {
|
||||
// zone.remove(nowRemoved);
|
||||
// }
|
||||
// }
|
||||
// removeList.clear();
|
||||
// }else {
|
||||
// removeList.add(removed);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render all contained entities
|
||||
*
|
||||
* @param delta
|
||||
*/
|
||||
public void render(float delta) {
|
||||
Collections.sort(allEntities);
|
||||
|
||||
Models.renderBegin();
|
||||
|
||||
for (Entity entity : allEntities) {
|
||||
if (entity.isDead()) continue;
|
||||
if (!Utils.canSkipRendering(entity.getPos())) {
|
||||
|
||||
entity.render(delta);
|
||||
|
||||
if (GameConfig.colliderWireframe) {
|
||||
entity.collider.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Models.renderEnd();
|
||||
particles.render(delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global movement vector
|
||||
*
|
||||
* @return global movement vector
|
||||
*/
|
||||
public Vec getGlobalMovement() {
|
||||
return globalMovement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global movement vector
|
||||
*
|
||||
* @param newMovement new global movement
|
||||
*/
|
||||
public void setGlobalMovement(Vec newMovement) {
|
||||
globalMovement.setTo(newMovement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all contained entities
|
||||
*/
|
||||
public void update() {
|
||||
|
||||
particles.moveAllParticles(globalMovement.scale(Constants.SPEED_MUL));
|
||||
|
||||
particles.update();
|
||||
|
||||
// add entities waiting to be added
|
||||
addAllWaiting();
|
||||
|
||||
for (Entity entity : allEntities) {
|
||||
if (entity.isDead()) continue;
|
||||
|
||||
entity.update();
|
||||
|
||||
// assign to zone.
|
||||
for (ColliderZone zone : zones) {
|
||||
if(!zone.contains(entity)) {
|
||||
if (entity.belongsToZone(zone.zFrom, zone.zTo)) {
|
||||
zone.add(entity);
|
||||
}
|
||||
}else {
|
||||
if (!entity.belongsToZone(zone.zFrom, zone.zTo)) {
|
||||
zone.remove(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity instanceof EntityPlayerShip) continue;
|
||||
|
||||
if (entity.getPos().x < -60) {
|
||||
if (entity instanceof EntityShotBase) {
|
||||
entity.setDead();
|
||||
} else {
|
||||
entity.getMotion().x *= -1;
|
||||
}
|
||||
}
|
||||
if (entity.getPos().x > 60) {
|
||||
if (entity instanceof EntityShotBase) {
|
||||
entity.setDead();
|
||||
} else {
|
||||
entity.getMotion().x *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.getPos().z < -15) entity.setDead();
|
||||
if (entity.getPos().z > 160) entity.setDead();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// remove dead and other entities
|
||||
for (ColliderZone zone : zones) {
|
||||
zone.arrangeEntities();
|
||||
}
|
||||
|
||||
for (ColliderZone zone : zones) {
|
||||
// check for collisions, do reactions if needed
|
||||
zone.collideAndReact();
|
||||
}
|
||||
|
||||
if(Constants.LOG_ZONES) {
|
||||
long newt;
|
||||
if((newt = System.currentTimeMillis())-lastt > 1000) {
|
||||
lastt = newt;
|
||||
System.out.println("\n### ZONE MAP");
|
||||
for(ColliderZone zone: zones) {
|
||||
System.out.println("Zone[ "+(int)zone.zFrom+" , "+(int)zone.zTo+" ] = "+StringUtils.repeat("(#)", zone.size()));
|
||||
}
|
||||
|
||||
System.out.println("### ZONE MAP\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get set of entities in given range. Their center points may be outside
|
||||
* the range, only their colliders are checked.
|
||||
*
|
||||
* @param center central point
|
||||
* @param range radius of collision sphere to get entities from
|
||||
* @return set of the entities
|
||||
*/
|
||||
public Set<Entity> getEntitiesInRange(Coord center, double range) {
|
||||
Set<Entity> buffer = new HashSet<Entity>();
|
||||
Collider rangeCol = new ColliderSphere(center, range);
|
||||
|
||||
for (Entity entity : allEntities) {
|
||||
if (entity.isDead()) continue;
|
||||
if (entity.collider.collidesWith(rangeCol)) {
|
||||
buffer.add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entities in line of sight (can be hit by weapon)
|
||||
*
|
||||
* @param origin observer pos
|
||||
* @param direction look direction
|
||||
* @param maxDistanceFromLine max distance to side from direct line
|
||||
* @param lengthOfSight max distance from observer
|
||||
* @return set of matching entities
|
||||
*/
|
||||
public Set<Entity> getEntitiesInLineOfSight(Coord origin, Vec direction, double maxDistanceFromLine, double lengthOfSight) {
|
||||
Set<Entity> buffer = new HashSet<Entity>();
|
||||
|
||||
for (Entity entity : allEntities) {
|
||||
if (entity.isDead()) continue;
|
||||
|
||||
// too far?
|
||||
if (entity.getPos().distTo(origin) > lengthOfSight) continue;
|
||||
|
||||
double dist = Calc.linePointDistXZ(direction, origin, entity.getPos());
|
||||
dist -= entity.collider.radius;
|
||||
if (dist < 0) dist = 0;
|
||||
|
||||
if (dist <= maxDistanceFromLine) buffer.add(entity);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Global movement vector */
|
||||
private Vec globalMovement = new Vec(0, 0, -0.1);
|
||||
|
||||
/**
|
||||
* Get player ship instance (if any)
|
||||
*
|
||||
* @return player ship
|
||||
*/
|
||||
public EntityPlayerShip getPlayerShip() {
|
||||
return playerShip;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.sector.effects;
|
||||
|
||||
|
||||
/**
|
||||
* Enum particle types
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public enum EParticle {
|
||||
/** fire */
|
||||
FIRE,
|
||||
/** smoke */
|
||||
SMOKE,
|
||||
/** shard */
|
||||
SHARD,
|
||||
/** star */
|
||||
STAR,
|
||||
/** binary */
|
||||
BINARY,
|
||||
/** EMP hit */
|
||||
EMP,
|
||||
/** Special spawned when orb is collected */
|
||||
ORB;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package net.sector.effects;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.particles.*;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Effects helper
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Effects {
|
||||
/** RNG */
|
||||
private static Random rand = new Random();
|
||||
|
||||
/**
|
||||
* Add fire from jet engine (rocket)
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center engine pos
|
||||
* @param motion engine motion
|
||||
* @param repeatCount repeat count
|
||||
*/
|
||||
public static void addEngineFire(ParticleManager manager, Coord center, Vec motion, int repeatCount) {
|
||||
addEngineFire(manager, center, motion, repeatCount, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fire from jet engine (rocket)
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center engine pos
|
||||
* @param motion engine motion
|
||||
* @param repeatCount repeat count
|
||||
* @param type 0 red, 1 blue
|
||||
*/
|
||||
public static void addEngineFire(ParticleManager manager, Coord center, Vec motion, int repeatCount, int type) {
|
||||
addEngineFire(manager, center, motion, repeatCount, type, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fire from jet engine (rocket)
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center engine pos
|
||||
* @param motion engine motion
|
||||
* @param repeatCount repeat count
|
||||
* @param type 0 red, 1 blue
|
||||
* @param size particle max size
|
||||
*/
|
||||
public static void addEngineFire(ParticleManager manager, Coord center, Vec motion, int repeatCount, int type, double size) {
|
||||
Coord pos;
|
||||
Vec pmotion;
|
||||
double rotSpeed;
|
||||
|
||||
for (int i = 0; i < repeatCount; i++) {
|
||||
pos = center.random_offset(0.02);
|
||||
pmotion = motion.copy();
|
||||
rotSpeed = 0;
|
||||
ParticleFire p;
|
||||
manager.add(p = (ParticleFire) new ParticleFire(pos, pmotion, rotSpeed, (0.2 + rand.nextDouble() * 0.2) * size, false)
|
||||
.setGlobalMovement(false));
|
||||
p.maxAge = (int) (Constants.FPS_UPDATE * 0.3 * size);
|
||||
p.setType(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add orb effect
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param motionSource velocity of explosion source
|
||||
* @param radius burst radius (3D units)
|
||||
* @param particles number of particles
|
||||
* @param color particle average color (0 blue, 1 red, 2 green)
|
||||
* @param globalMovement should the particles move with "asteroid shift"?
|
||||
*/
|
||||
public static void addOrbBurst(ParticleManager manager, Coord center, Vec motionSource, double radius, int particles, int color,
|
||||
boolean globalMovement, boolean gaussian) {
|
||||
|
||||
if (!Utils.canCoordBeSeen(center, 20)) return;
|
||||
|
||||
Coord pos;
|
||||
Vec motion;
|
||||
|
||||
for (int i = 0; i < particles; i++) {
|
||||
|
||||
pos = center.random_offset(gaussian ? Math.abs(Calc.clampd(rand.nextGaussian() * radius, 0, radius * 1.2)) : radius);
|
||||
motion = (Vec) motionSource.mul(0.5).add(Vec.random(-0.005, 0.005));
|
||||
|
||||
manager.add(new ParticleOrb(pos, motion, 0.2 + (0.2 + rand.nextDouble()) * 0.3, color).setGlobalMovement(globalMovement));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fire burst
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param motionSource velocity of explosion source
|
||||
* @param radius burst radius (3D units)
|
||||
* @param particles number of particles
|
||||
* @param globalMovement should the particles move with "asteroid shift"?
|
||||
*/
|
||||
public static void addFireBurst(ParticleManager manager, Coord center, Vec motionSource, double radius, int particles, boolean globalMovement,
|
||||
boolean gaussian) {
|
||||
|
||||
if (!Utils.canCoordBeSeen(center, 20)) return;
|
||||
|
||||
Coord pos;
|
||||
Vec motion;
|
||||
|
||||
for (int i = 0; i < particles; i++) {
|
||||
|
||||
pos = center.random_offset(gaussian ? Math.abs(Calc.clampd(rand.nextGaussian() * radius, 0, radius * 1.2)) : radius);
|
||||
motion = (Vec) motionSource.mul(0.8).add(Vec.random(-0.005, 0.005));
|
||||
double rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
|
||||
manager.add(new ParticleFire(pos, motion, rotSpeed, true).setGlobalMovement(globalMovement));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add explosion
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param motionSource velocity of explosion source
|
||||
* @param strength explosion strength
|
||||
* @param globalMovement should the particles move with "asteroid shift"?
|
||||
* @param sound make sound
|
||||
*/
|
||||
public static void addEMPExplosion(ParticleManager manager, Coord center, Vec motionSource, double strength, boolean globalMovement, boolean sound) {
|
||||
|
||||
if (!Utils.canCoordBeSeen(center, 20)) return;
|
||||
|
||||
Coord pos;
|
||||
Vec motion;
|
||||
|
||||
for (int i = 0; i < strength * 5; i++) {
|
||||
pos = center.random_offset(0.02 * strength);
|
||||
motion = (Vec) motionSource.mul(0.5).add(Vec.random(-0.005, 0.005));
|
||||
|
||||
manager.add(new ParticleEMP(pos, motion, 0.3 + (0.2 + rand.nextDouble()) * 0.3 * strength).setGlobalMovement(globalMovement));
|
||||
}
|
||||
|
||||
if (sound && center.distTo(new Coord(0, 0, 0)) < 80 /*25*/) {
|
||||
float pitch = 0.3f + rand.nextFloat() * 0.6f;
|
||||
float gain = Calc.clampf((float) strength * 0.12f, 0, 1.5f) * 0.3f;
|
||||
Sounds.explosion().playEffectLinearZ(pitch, gain, 60, false, center);
|
||||
Sounds.shot_emp_hit.playEffectLinearZ(pitch, gain, 60, false, center);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add explosion
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param motionSource velocity of explosion source
|
||||
* @param strength explosion strength
|
||||
* @param shards add shards
|
||||
* @param globalMovement should the particles move with "asteroid shift"?
|
||||
*/
|
||||
public static void addExplosion(ParticleManager manager, Coord center, Vec motionSource, double strength, boolean shards, boolean globalMovement) {
|
||||
addExplosion(manager, center, motionSource, strength, shards, globalMovement, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add explosion
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param motionSource velocity of explosion source
|
||||
* @param strength explosion strength
|
||||
* @param shards add shards
|
||||
* @param globalMovement should the particles move with "asteroid shift"?
|
||||
* @param sound enable sounds
|
||||
*/
|
||||
public static void addExplosion(ParticleManager manager, Coord center, Vec motionSource, double strength, boolean shards, boolean globalMovement,
|
||||
boolean sound) {
|
||||
|
||||
if (!Utils.canCoordBeSeen(center, 20)) return;
|
||||
|
||||
Coord pos;
|
||||
Vec motion;
|
||||
double rotSpeed;
|
||||
|
||||
for (int i = 0; i < strength * 5; i++) {
|
||||
pos = center.random_offset(0.01 * strength);
|
||||
motion = (Vec) motionSource.mul(0.5).add(Vec.random(-0.005, 0.005));
|
||||
rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
|
||||
manager.add(new ParticleFire(pos, motion, rotSpeed, 0.6 + (0.7 + rand.nextDouble()) * 0.6 * strength).setGlobalMovement(globalMovement));
|
||||
|
||||
if (i < strength * 3 && shards) {
|
||||
pos = center.random_offset(0.04);
|
||||
|
||||
motion = (Vec) motionSource.mul(0.5).add(Vec.random(-0.02, 0.02));
|
||||
rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
manager.add(new ParticleSmoke(pos, motion, rotSpeed).setGlobalMovement(globalMovement));
|
||||
}
|
||||
|
||||
if (i < strength * 2 && shards) {
|
||||
pos = center.random_offset(0.04);
|
||||
motion = (Vec) motionSource.mul(0.5).add(Vec.random(-0.02, 0.02));
|
||||
rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
manager.add(new ParticleShard(pos, motion, rotSpeed).setGlobalMovement(globalMovement));
|
||||
}
|
||||
}
|
||||
|
||||
if (sound && center.distTo(new Coord(0, 0, 0)) < 80 /*25*/) {
|
||||
float pitch = 0.6f + rand.nextFloat() * 1.7f;
|
||||
float gain = Calc.clampf((float) strength * 0.09f, 0, 1.5f) * 0.3f;
|
||||
Sounds.explosion().playEffectLinearZ(pitch, gain, 60, false, center);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// /**
|
||||
// * Add stars
|
||||
// *
|
||||
// * @param manager particle manager
|
||||
// * @param center stars center
|
||||
// * @param count stars count
|
||||
// */
|
||||
// public static void addStar(ParticleManager manager, Coord center, int count) {
|
||||
// Coord pos;
|
||||
// Vec motion;
|
||||
// double rotSpeed;
|
||||
//
|
||||
// for (int i = 0; i < count; i++) {
|
||||
// pos = center.random_offset(0.3);
|
||||
// motion = Vec.random(-0.005, 0.005);
|
||||
// rotSpeed = -5 + rand.nextDouble() * 10;
|
||||
//
|
||||
// manager.add(new ParticleStar(pos, motion, rotSpeed, 0.3 + (0.1 + rand.nextDouble()) * 0.8));
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Add binary particles for splash
|
||||
*
|
||||
* @param manager particle manager
|
||||
* @param center center
|
||||
* @param count binaries count
|
||||
*/
|
||||
public static void addBinaries(ParticleManager manager, Coord center, int count) {
|
||||
Coord pos;
|
||||
Vec motion;
|
||||
double rotSpeed;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
pos = center.random_offset(0.3);
|
||||
motion = Vec.random(-0.005, 0.005);
|
||||
rotSpeed = -3 + rand.nextDouble() * 6;
|
||||
|
||||
manager.add(new ParticleBinary(pos, motion, rotSpeed, 0.1 + (rand.nextDouble()) * 0.35));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package net.sector.effects;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.renderers.*;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Particle manager, container and animator of particles.<br>
|
||||
* It extends HashSet, so you can simply use add(particle) to add new effect.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleManager extends ArrayList<Particle> {
|
||||
|
||||
@Override
|
||||
public boolean add(Particle e) {
|
||||
if (size() > Constants.PARTICLE_COUNT_LIMIT) return false;
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
/** particle renderers */
|
||||
public static ParticleRenderer[] renderers = {
|
||||
new ParticleSmokeRenderer(),
|
||||
new ParticleShardRenderer(),
|
||||
new ParticleFireRenderer(),
|
||||
new ParticleStarRenderer(),
|
||||
new ParticleBinaryRenderer(),
|
||||
new ParticleEMPRenderer(),
|
||||
new ParticleOrbRenderer()
|
||||
};
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
* Update all particles in this manager
|
||||
*/
|
||||
public void update() {
|
||||
Iterator<Particle> i = this.iterator();
|
||||
while (i.hasNext()) {
|
||||
Particle p = i.next();
|
||||
p.update();
|
||||
if (p.isDead) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
Collections.sort(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all particles in this manager
|
||||
*
|
||||
* @param delta delta time
|
||||
*/
|
||||
public void render(double delta) {
|
||||
glPushMatrix();
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_FOG);
|
||||
glDepthMask(false);
|
||||
|
||||
glLoadIdentity();
|
||||
|
||||
for (Particle p : this) {
|
||||
|
||||
if (Utils.canSkipRendering(p.pos)) continue;
|
||||
|
||||
EParticle pt = p.getType();
|
||||
|
||||
|
||||
for (ParticleRenderer pr : renderers) {
|
||||
|
||||
if (pr.getType() == pt) {
|
||||
|
||||
pr.prepareRender();
|
||||
pr.renderParticle(p, delta);
|
||||
pr.finishRender();
|
||||
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(true);
|
||||
// for (ParticleRenderer pr : renderers) {
|
||||
//
|
||||
// pr.prepareRender();
|
||||
//
|
||||
// ParticleType pt = pr.getType();
|
||||
//
|
||||
// for (Particle p : this) {
|
||||
//
|
||||
// if (p.getType() == pt) {
|
||||
// pr.renderParticle(p);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pr.finishRender();
|
||||
//
|
||||
// }
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_FOG);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
//glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* move all particles (same as move all entities)
|
||||
*
|
||||
* @param motion
|
||||
*/
|
||||
public void moveAllParticles(Vec motion) {
|
||||
for (Particle p : this) {
|
||||
if (p.hasGlobalMovement()) p.pos.add_ip(motion).update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.util.DeltaDoubleDeg;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Particle pseudo-entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class Particle implements Comparable<Particle> {
|
||||
|
||||
private boolean hasGlobalMovement = false;
|
||||
|
||||
/**
|
||||
* Set whether particle should move together with asteroids (global
|
||||
* movement)
|
||||
*
|
||||
* @param state
|
||||
* @return this
|
||||
*/
|
||||
public Particle setGlobalMovement(boolean state) {
|
||||
hasGlobalMovement = state;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should move with asteroids? (global movement)
|
||||
*
|
||||
* @return has global movement
|
||||
*/
|
||||
public boolean hasGlobalMovement() {
|
||||
return hasGlobalMovement;
|
||||
}
|
||||
|
||||
/** RNG */
|
||||
public static Random rand = new Random();
|
||||
|
||||
/** particle quad size when rendered */
|
||||
public double size = 1;
|
||||
|
||||
/** size of a new particle. */
|
||||
public double sizeOrig = 1;
|
||||
|
||||
/** position in 3D space */
|
||||
public Coord pos = new Coord(0, 0, 0);
|
||||
|
||||
/** Motion, added each tick to position */
|
||||
public Vec motion = new Vec(0, 0, 0);
|
||||
|
||||
/** Angle (deg) of Z-axis rotation */
|
||||
public DeltaDoubleDeg rotAngle = new DeltaDoubleDeg(0);
|
||||
|
||||
/** Rotation speed, added each tick to rotAngle */
|
||||
public double rotSpeed = 2;
|
||||
|
||||
/** Particle age */
|
||||
public long age = 0;
|
||||
|
||||
/** Max particle age */
|
||||
public long maxAge = 100;
|
||||
|
||||
/** Flag that this particle should be removed from manager next tick. */
|
||||
public boolean isDead = false;
|
||||
|
||||
/** Color multiplier for the particle rendering */
|
||||
public RGB renderColor = new RGB(1, 1, 1);
|
||||
|
||||
/** Particle alpha 0-1 */
|
||||
public double renderAlpha = 1.0;
|
||||
|
||||
/**
|
||||
* @return particle ID, from ParticleType.
|
||||
*/
|
||||
public abstract EParticle getType();
|
||||
|
||||
/**
|
||||
* Set particle dead → will be removed from manager next update tick.
|
||||
*/
|
||||
public void setDead() {
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
*/
|
||||
public Particle(Coord pos, Vec motion) {
|
||||
this.pos.setTo(pos);
|
||||
this.motion.setTo(motion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the particle
|
||||
*/
|
||||
public final void update() {
|
||||
if (isDead) return;
|
||||
|
||||
pos.pushLast();
|
||||
rotAngle.pushLast();
|
||||
|
||||
pos.add_ip(motion.mul(Constants.SPEED_MUL));
|
||||
rotAngle.add(rotSpeed * Constants.SPEED_MUL);
|
||||
|
||||
age++;
|
||||
if (age >= maxAge) {
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
|
||||
onUpdate();
|
||||
|
||||
pos.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each update tick. You can check age and set isDead here, do some
|
||||
* additional animation etc.
|
||||
*/
|
||||
public abstract void onUpdate();
|
||||
|
||||
@Override
|
||||
public int compareTo(Particle o) {
|
||||
if (this == o) return 0;
|
||||
return Double.compare(new Double(o.pos.z), new Double(pos.z));
|
||||
// if(o.pos.z > pos.z) return -1;
|
||||
// if(o.pos.z < pos.z) return 1;
|
||||
// return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* "Binary" particle (animated green 0s and 1s in splash screen)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleBinary extends Particle {
|
||||
|
||||
private Vec origMotion = null;
|
||||
private boolean slow = false;
|
||||
|
||||
/** Type (0,1) */
|
||||
public int type = 0;
|
||||
|
||||
/**
|
||||
* Binary particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleBinary(Coord pos, Vec motion, double rotSpeed, boolean slowDown) {
|
||||
super(pos, motion);
|
||||
origMotion = motion.copy();
|
||||
slow = slowDown;
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = rotSpeed;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.4 + rand.nextDouble() * 1.5));
|
||||
this.size = this.sizeOrig = 0.6 + rand.nextDouble();
|
||||
this.renderColor.setTo(new RGB(rand.nextDouble() * 0.3, 0.7 + rand.nextDouble() * 0.3, rand.nextDouble() * 0.3));
|
||||
this.renderAlpha = 1;
|
||||
type = rand.nextInt(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
*/
|
||||
public ParticleBinary(Coord pos, Vec motion, double rotSpeed, double scale) {
|
||||
this(pos, motion, rotSpeed, true);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleBinary(Coord pos, Vec motion, double rotSpeed, double scale, boolean slowDown) {
|
||||
this(pos, motion, rotSpeed, slowDown);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.BINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
renderAlpha = Calc.square(1F - (float) age / (float) maxAge);
|
||||
|
||||
//size = Calc.square(1F - Calc.clampd( ((float) age / ((float) maxAge)) , 0, 1))*sizeOrig;
|
||||
|
||||
if (slow) {
|
||||
motion.setTo(origMotion.scale(Calc.square(1 - (float) age / (float) maxAge)));
|
||||
//if(size < sizeOrig*0.2) setDead();
|
||||
} else {
|
||||
motion.scale_ip(0.90);
|
||||
//if(size < 0.2) setDead();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.HSV;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* "EMP" particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleEMP extends Particle {
|
||||
|
||||
private Vec origMotion = null;
|
||||
private boolean slow = false;
|
||||
|
||||
/** Type (0-5) */
|
||||
public int type = 0;
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleEMP(Coord pos, Vec motion, boolean slowDown) {
|
||||
super(pos, motion);
|
||||
origMotion = motion.copy();
|
||||
slow = slowDown;
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.2 + rand.nextDouble() * 1));
|
||||
this.size = this.sizeOrig = 0.6 + rand.nextDouble();
|
||||
this.renderColor.setTo(new HSV(0.7 - 0.2 + rand.nextDouble() * 0.4, 0.8 - 0.2 + rand.nextDouble() * 0.4, 1).toRGB());
|
||||
this.renderAlpha = 1;
|
||||
type = rand.nextInt(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param scale render size 0.001-2
|
||||
*/
|
||||
public ParticleEMP(Coord pos, Vec motion, double scale) {
|
||||
this(pos, motion, true);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param scale render size 0.001-2
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleEMP(Coord pos, Vec motion, double scale, boolean slowDown) {
|
||||
this(pos, motion, slowDown);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.EMP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
size = Calc.square(1F - ((float) age / (float) maxAge)) * sizeOrig;
|
||||
|
||||
if (slow) {
|
||||
motion.setTo(origMotion.scale(Calc.square(1 - (float) age / (float) maxAge)));
|
||||
if (size < sizeOrig * 0.2) setDead();
|
||||
} else {
|
||||
motion.scale_ip(0.90);
|
||||
if (size < 0.2) setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fire / explode particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleFire extends Particle {
|
||||
|
||||
private Vec origMotion = null;
|
||||
private boolean slow = false;
|
||||
public int type = 0;
|
||||
|
||||
public ParticleFire setType(int type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleFire(Coord pos, Vec motion, double rotSpeed, boolean slowDown) {
|
||||
super(pos, motion);
|
||||
origMotion = motion.copy();
|
||||
slow = slowDown;
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = rotSpeed;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.2 + rand.nextDouble() * 1));
|
||||
this.size = this.sizeOrig = 0.6 + rand.nextDouble();
|
||||
this.renderColor.setTo(new RGB(1, 1, 1));
|
||||
this.renderAlpha = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
*/
|
||||
public ParticleFire(Coord pos, Vec motion, double rotSpeed, double scale) {
|
||||
this(pos, motion, rotSpeed, true);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleFire(Coord pos, Vec motion, double rotSpeed, double scale, boolean slowDown) {
|
||||
this(pos, motion, rotSpeed, slowDown);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.FIRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
size = Calc.square(1F - ((float) age / (float) maxAge)) * sizeOrig;
|
||||
|
||||
if (slow) {
|
||||
motion.setTo(origMotion.scale(Calc.square(1 - (float) age / (float) maxAge)));
|
||||
if (size < sizeOrig * 0.2) setDead();
|
||||
} else {
|
||||
motion.scale_ip(0.90);
|
||||
if (size < 0.2) setDead();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.HSV;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* "EMP" particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleOrb extends Particle {
|
||||
|
||||
private Vec origMotion = null;
|
||||
private boolean slow = false;
|
||||
|
||||
/** Type (0-5) */
|
||||
public int type = 0;
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleOrb(Coord pos, Vec motion, boolean slowDown, int color) {
|
||||
super(pos, motion);
|
||||
origMotion = motion.copy();
|
||||
slow = slowDown;
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = -10 + rand.nextDouble() * 20;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.2 + rand.nextDouble() * 1));
|
||||
this.size = this.sizeOrig = 0.6 + rand.nextDouble();
|
||||
double h = 0;
|
||||
|
||||
if (color == 0) h = 0.5 + rand.nextDouble() * 0.3;
|
||||
if (color == 1) h = rand.nextBoolean() ? rand.nextDouble() * 0.1 : 1 - rand.nextDouble() * 0.1;
|
||||
if (color == 2) h = 0.2 + rand.nextDouble() * 0.3;
|
||||
|
||||
this.renderColor.setTo(new HSV(h, 0.6 + rand.nextDouble() * 0.4, 1).toRGB());
|
||||
this.renderAlpha = 1;
|
||||
type = rand.nextInt(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param scale render size 0.001-2
|
||||
*/
|
||||
public ParticleOrb(Coord pos, Vec motion, double scale, int color) {
|
||||
this(pos, motion, true, color);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* EMP particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param scale render size 0.001-2
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleOrb(Coord pos, Vec motion, double scale, boolean slowDown, int color) {
|
||||
this(pos, motion, slowDown, color);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.ORB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
size = Calc.square(1F - ((float) age / (float) maxAge)) * sizeOrig;
|
||||
|
||||
if (slow) {
|
||||
motion.setTo(origMotion.scale(Calc.square(1 - (float) age / (float) maxAge)));
|
||||
if (size < sizeOrig * 0.2) setDead();
|
||||
} else {
|
||||
motion.scale_ip(0.90);
|
||||
if (size < 0.2) setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Shard (explosion) particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleShard extends Particle {
|
||||
|
||||
/** shard texture type */
|
||||
public int type = 0;
|
||||
|
||||
/**
|
||||
* Shard particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
*/
|
||||
public ParticleShard(Coord pos, Vec motion, double rotSpeed) {
|
||||
super(pos, motion);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = rotSpeed;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.6 + rand.nextDouble() * 1));
|
||||
this.size = this.sizeOrig = rand.nextDouble() * 0.5;
|
||||
this.renderColor.setTo(new RGB(1, 1, 1));
|
||||
this.renderAlpha = 1;
|
||||
type = rand.nextInt(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.SHARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
renderAlpha = Calc.square(1F - (float) age / (float) maxAge);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Smoke particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleSmoke extends Particle {
|
||||
|
||||
/** Texture type */
|
||||
public int type = 0;
|
||||
|
||||
/**
|
||||
* Smoke particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
*/
|
||||
public ParticleSmoke(Coord pos, Vec motion, double rotSpeed) {
|
||||
super(pos, motion);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = rotSpeed;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.6 + rand.nextDouble() * 1));
|
||||
this.size = this.sizeOrig = rand.nextDouble() * 0.5;
|
||||
this.renderColor.setTo(new RGB(1, 1, 1));
|
||||
this.renderAlpha = 1;
|
||||
type = rand.nextInt(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.SMOKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
renderAlpha = 0.5 * Calc.square(1F - (float) age / (float) maxAge);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package net.sector.effects.particles;
|
||||
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.color.HSV;
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Experimental (= ugly) star particle
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleStar extends Particle {
|
||||
|
||||
private Vec origMotion = null;
|
||||
private boolean slow = false;
|
||||
|
||||
private HSV hsv = new HSV(0, 1, 1);
|
||||
|
||||
/**
|
||||
* Star particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleStar(Coord pos, Vec motion, double rotSpeed, boolean slowDown) {
|
||||
super(pos, motion);
|
||||
origMotion = motion.copy();
|
||||
slow = slowDown;
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = rotSpeed;
|
||||
this.maxAge = (long) (Constants.FPS_UPDATE * (0.4 + rand.nextDouble() * 1.5));
|
||||
this.size = this.sizeOrig = 0.6 + rand.nextDouble();
|
||||
this.renderColor.setTo(new RGB(1, 1, 1));
|
||||
this.renderAlpha = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Star particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
*/
|
||||
public ParticleStar(Coord pos, Vec motion, double rotSpeed, double scale) {
|
||||
this(pos, motion, rotSpeed, true);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Star particle
|
||||
*
|
||||
* @param pos position
|
||||
* @param motion motion
|
||||
* @param rotSpeed rotation speed
|
||||
* @param scale render size 0.001-2
|
||||
* @param slowDown can slow down gradually
|
||||
*/
|
||||
public ParticleStar(Coord pos, Vec motion, double rotSpeed, double scale, boolean slowDown) {
|
||||
this(pos, motion, rotSpeed, slowDown);
|
||||
this.size = this.sizeOrig = Calc.clampd(scale, 0.001, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.STAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
renderAlpha = Calc.square(1F - (float) age / (float) maxAge);
|
||||
|
||||
this.renderColor.setTo(hsv.toRGB());
|
||||
hsv.h -= 0.005;
|
||||
if (hsv.h < 0) hsv.h = 1 - hsv.h;
|
||||
|
||||
size = Calc.square(1F - Calc.clampd((age / ((float) maxAge + 1)), 0, 1)) * sizeOrig;
|
||||
|
||||
if (slow) {
|
||||
motion.setTo(origMotion.scale(Calc.square(1 - (float) age / (float) maxAge)));
|
||||
//if(size < sizeOrig*0.2) setDead();
|
||||
} else {
|
||||
motion.scale_ip(0.90);
|
||||
//if(size < 0.2) setDead();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleBinary;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Binary particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleBinaryRenderer extends ParticleRendererPlain {
|
||||
/**
|
||||
* Binary particle renderer
|
||||
*/
|
||||
public ParticleBinaryRenderer() {
|
||||
super(new Coord(0, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
texCoord.x = 1 + ((ParticleBinary) part).type;
|
||||
super.renderParticle(part, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.BINARY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleEMP;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Fire particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleEMPRenderer extends ParticleRendererBlend {
|
||||
/**
|
||||
* Fire particle renderer
|
||||
*/
|
||||
public ParticleEMPRenderer() {
|
||||
super(new Coord(1, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
texCoord.x = 1 + ((ParticleEMP) part).type % 2;
|
||||
texCoord.y = 1 + ((ParticleEMP) part).type / 2;
|
||||
super.renderParticle(part, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.EMP;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleFire;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Fire particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleFireRenderer extends ParticleRendererBlend {
|
||||
/**
|
||||
* Fire particle renderer
|
||||
*/
|
||||
public ParticleFireRenderer() {
|
||||
super(new Coord(0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
switch (((ParticleFire) part).type) {
|
||||
case 0:
|
||||
texCoord.setTo(0, 0);
|
||||
break;
|
||||
case 1:
|
||||
texCoord.setTo(4, 1);
|
||||
}
|
||||
super.renderParticle(part, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.FIRE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleOrb;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* orb particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleOrbRenderer extends ParticleRendererBlend {
|
||||
/**
|
||||
* Fire particle renderer
|
||||
*/
|
||||
public ParticleOrbRenderer() {
|
||||
super(new Coord(1, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
texCoord.x = 1 + ((ParticleOrb) part).type % 2;
|
||||
texCoord.y = 1 + ((ParticleOrb) part).type / 2;
|
||||
super.renderParticle(part, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.ORB;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Particle renderer.<br>
|
||||
* Only one instance of this renderer is made, and is later held in
|
||||
* ParticleManager.renderers
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class ParticleRenderer {
|
||||
|
||||
/**
|
||||
* coordinates 0-7,0-7 in texture, allowing having multiple particles in
|
||||
* single file
|
||||
*/
|
||||
public Coord texCoord;
|
||||
|
||||
/**
|
||||
* New particle renderer
|
||||
*
|
||||
* @param textureCoords new float[]{left, top, right, bottom} coordinates in
|
||||
* texture, 0-1
|
||||
*/
|
||||
public ParticleRenderer(Coord textureCoords) {
|
||||
this.texCoord = textureCoords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the particle. You should load identity, translate and rotate and
|
||||
* do the rendering using texture.bind().<br>
|
||||
* Don't forget to add MINUS sign before z axis in translation.
|
||||
*
|
||||
* @param part the particle
|
||||
* @param delta delta time
|
||||
*/
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
|
||||
double left = (texCoord.x) * 0.125;
|
||||
double top = (texCoord.y) * 0.125;
|
||||
double right = (texCoord.x + 1) * 0.125;
|
||||
double bottom = (texCoord.y + 1) * 0.125;
|
||||
|
||||
Coord pos = part.pos.getDelta(delta);
|
||||
double scale = part.size * 1.4142414 * 0.5;
|
||||
|
||||
double sx1 = Math.cos(Math.toRadians(part.rotAngle.delta(delta))) * scale;
|
||||
double sy1 = Math.sin(Math.toRadians(part.rotAngle.delta(delta))) * scale;
|
||||
double sx2 = -sy1;
|
||||
double sy2 = sx1;
|
||||
|
||||
glColor4d(part.renderColor.r, part.renderColor.g, part.renderColor.b, part.renderAlpha);
|
||||
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(pos.x + sx1, pos.y + sy1, -pos.z);
|
||||
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(pos.x + sx2, pos.y + sy2, -pos.z);
|
||||
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(pos.x - sx1, pos.y - sy1, -pos.z);
|
||||
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(pos.x - sx2, pos.y - sy2, -pos.z);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for rendering
|
||||
*/
|
||||
public abstract void prepareRender();
|
||||
|
||||
/**
|
||||
* Finish the rendering
|
||||
*/
|
||||
public abstract void finishRender();
|
||||
|
||||
/**
|
||||
* Get particle type this renderer can render
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public abstract EParticle getType();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.textures.TextureManager;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Particle renderer BLEND (black image with colors).
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class ParticleRendererBlend extends ParticleRenderer {
|
||||
|
||||
/**
|
||||
* Blend renderer
|
||||
*
|
||||
* @param textureCoords texture coords for texture
|
||||
*/
|
||||
public ParticleRendererBlend(Coord textureCoords) {
|
||||
super(textureCoords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareRender() {
|
||||
glPushMatrix();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
//glBlendFunc(GL_ONE_MINUS_DST_ALPHA,GL_DST_ALPHA);
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
TextureManager.bind("particles_blend");
|
||||
glBegin(GL_QUADS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishRender() {
|
||||
|
||||
glEnd();
|
||||
TextureManager.unbind();
|
||||
glDisable(GL_BLEND);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.textures.TextureManager;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Particle renderer PLAIN.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class ParticleRendererPlain extends ParticleRenderer {
|
||||
|
||||
/**
|
||||
* Plain renderer
|
||||
*
|
||||
* @param textureCoords texture coords
|
||||
*/
|
||||
public ParticleRendererPlain(Coord textureCoords) {
|
||||
super(textureCoords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareRender() {
|
||||
glPushMatrix();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
TextureManager.bind("particles_plain");
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishRender() {
|
||||
|
||||
glEnd();
|
||||
TextureManager.unbind();
|
||||
glDisable(GL_BLEND);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleShard;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Shard particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleShardRenderer extends ParticleRendererPlain {
|
||||
/**
|
||||
* Shard particle renderer
|
||||
*/
|
||||
public ParticleShardRenderer() {
|
||||
super(new Coord(0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
texCoord.x = 2 + ((ParticleShard) part).type;
|
||||
//glEnable(GL_LIGHTING);
|
||||
super.renderParticle(part, delta);
|
||||
//glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.SHARD;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
import net.sector.effects.particles.Particle;
|
||||
import net.sector.effects.particles.ParticleSmoke;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Smoke particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleSmokeRenderer extends ParticleRendererPlain {
|
||||
/**
|
||||
* Smoke particle renderer
|
||||
*/
|
||||
public ParticleSmokeRenderer() {
|
||||
super(new Coord(0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Particle part, double delta) {
|
||||
texCoord.x = ((ParticleSmoke) part).type;
|
||||
|
||||
super.renderParticle(part, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.SMOKE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.sector.effects.renderers;
|
||||
|
||||
|
||||
import net.sector.effects.EParticle;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Star particle renderer
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ParticleStarRenderer extends ParticleRendererPlain {
|
||||
/**
|
||||
* Star particle renderer
|
||||
*/
|
||||
public ParticleStarRenderer() {
|
||||
super(new Coord(0, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EParticle getType() {
|
||||
return EParticle.STAR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
/**
|
||||
* Entity type
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public enum EEntity {
|
||||
/** Shot, bullet, rocket etc. by aliens */
|
||||
SHOT_BAD,
|
||||
/** Player shot */
|
||||
SHOT_GOOD,
|
||||
/** Player ship */
|
||||
PLAYER,
|
||||
/** Enemy ship / space craft */
|
||||
ENEMY,
|
||||
/** Natural = rocks etc. */
|
||||
NATURAL,
|
||||
/** Power up */
|
||||
BONUS,
|
||||
/** MINE */
|
||||
MINE,
|
||||
/** NONE (fake damage source) */
|
||||
NONE;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
public enum EFormation {
|
||||
SNAKE, SWARM, SHAPE, NONE
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.NullDamageSource;
|
||||
import net.sector.collision.Collider;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.collision.Scene;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.orbs.EntityOrbArtifact;
|
||||
import net.sector.entities.player.EntityPlayerShip;
|
||||
import net.sector.util.DeltaDoubleDeg;
|
||||
import net.sector.util.Log;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Entity object: something that moves, reacts to collisions and does other cool
|
||||
* stuff.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class Entity implements IPhysEntity, Comparable<Entity> {
|
||||
|
||||
public static final IDamageable NO_SOURCE = new NullDamageSource();
|
||||
|
||||
private int artifacts = 0;
|
||||
|
||||
/** Delay before two following explosions. */
|
||||
protected static final long ExplodeCooldown = Constants.FPS_UPDATE / 3;
|
||||
|
||||
/** Counts down until next explosion can be added == 0 */
|
||||
public long explodeCooldown = 0;
|
||||
|
||||
public double healthMul = 1;
|
||||
|
||||
/** Global movement disabled */
|
||||
private boolean globalMovement = true;
|
||||
|
||||
/** Collide priority, entity with higher number handles collision. */
|
||||
public int collidePriority = 0;
|
||||
|
||||
/** Speed limit */
|
||||
public double MAX_SPEED = 0.3;
|
||||
|
||||
/** points added to counter when killed by player */
|
||||
public int scoreValue = 0;
|
||||
|
||||
/** health used to add damage */
|
||||
public double health = 1;
|
||||
|
||||
/** time left before death (in update ticks) */
|
||||
public int lifetime = Constants.FPS_UPDATE * 5;
|
||||
|
||||
/** flag that entity is dead */
|
||||
protected boolean isDead = false;
|
||||
|
||||
/** Motion - number of units to move per update tick */
|
||||
public Vec motion = new Vec();
|
||||
|
||||
/** rotation vector for glRotate - the axis */
|
||||
public Vec rotDir = new Vec();
|
||||
|
||||
/** Y rot angle */
|
||||
public DeltaDoubleDeg rotAngle = new DeltaDoubleDeg(0);
|
||||
|
||||
/** Entity mass, to be used in reaction calculations */
|
||||
public double mass = 1.0;
|
||||
|
||||
/** the scene */
|
||||
public Scene scene;
|
||||
|
||||
/** Primary entity collider */
|
||||
public ColliderSphere collider;
|
||||
|
||||
public double effectEmpTicks;
|
||||
public double effectFireTicks;
|
||||
public IDamageable fireSource = NO_SOURCE;
|
||||
|
||||
/** RNG */
|
||||
public static Random rand = new Random();
|
||||
|
||||
/**
|
||||
* Get entity type
|
||||
*
|
||||
* @return entity type
|
||||
*/
|
||||
@Override
|
||||
public abstract EEntity getType();
|
||||
|
||||
/**
|
||||
* Add artifact to this entity, dropped on death.
|
||||
*
|
||||
* @param artifacts artifacts
|
||||
*/
|
||||
public final void addArtifacts(int artifacts) {
|
||||
this.artifacts += artifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if entity has an artifact.
|
||||
*
|
||||
* @return has artifact.
|
||||
*/
|
||||
public final int getArtifacts() {
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove artifact, if any, from this entity.
|
||||
*/
|
||||
public final void removeArtifacts() {
|
||||
artifacts = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global movement
|
||||
*
|
||||
* @param flag global movement enabled; False if entity is player ship /
|
||||
* player's shot / boss
|
||||
* @return this
|
||||
*/
|
||||
public final Entity setGlobalMovement(boolean flag) {
|
||||
globalMovement = flag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if has global movement
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasGlobalMovement() {
|
||||
return globalMovement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Coord getPos() {
|
||||
return collider.pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Vec getMotion() {
|
||||
return motion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double getMass() {
|
||||
return mass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDead() {
|
||||
return isDead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double getSpeed() {
|
||||
return motion.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setMotion(Vec newMotion) {
|
||||
motion.setTo(newMotion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set entity dead
|
||||
*/
|
||||
@Override
|
||||
public final void setDead() {
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign scene to entity. Called when entity is added to scene.
|
||||
*
|
||||
* @param scene the scene assigned
|
||||
*/
|
||||
@Override
|
||||
public final void setScene(Scene scene) {
|
||||
this.scene = scene;
|
||||
onAddedToScene();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this entity collides with other entity
|
||||
*
|
||||
* @param other the other entity
|
||||
* @return does collide
|
||||
*/
|
||||
public boolean collidesWith(Entity other) {
|
||||
ColliderSphere c = getColliderFor(other.collider);
|
||||
if (c == null) return false;
|
||||
return c.collidesWith(other.collider);
|
||||
}
|
||||
|
||||
/**
|
||||
* React to collision with other entity (eg. bullet hitting asteroid)
|
||||
*
|
||||
* @param hitBy
|
||||
*/
|
||||
public final void react(Entity hitBy) {
|
||||
onImpact(hitBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called right after entity was added to scene.
|
||||
*/
|
||||
public void onAddedToScene() {}
|
||||
|
||||
/**
|
||||
* Handle collision and do reaction here.
|
||||
*
|
||||
* @param hitBy
|
||||
*/
|
||||
public abstract void onImpact(Entity hitBy);
|
||||
|
||||
/**
|
||||
* Default reaction on impact.
|
||||
*
|
||||
* @param hitBy
|
||||
*/
|
||||
public final void defaultOnImpact(Entity hitBy) {
|
||||
try {
|
||||
if (hitBy.isDead()) return;
|
||||
|
||||
ColliderSphere mycol = getColliderFor(hitBy.collider);
|
||||
ColliderSphere ocol = hitBy.getColliderFor(this.collider);
|
||||
if (mycol == null || ocol == null) return;
|
||||
|
||||
Vec move = getPos().vecTo(hitBy.getPos());
|
||||
|
||||
Coord midpoint = getPos().add(move.norm(mycol.radius));
|
||||
|
||||
if (hitBy.getType() == EEntity.PLAYER && !((EntityPlayerShip) hitBy).body.isShieldRunning()) {
|
||||
|
||||
//move.scale_ip(0.2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
move.norm_ip((mycol.radius + ocol.radius) - mycol.getPos().distTo(ocol.getPos()));
|
||||
|
||||
hitBy.getPos().add_ip(move.scale(0.3));
|
||||
getPos().add_ip(move.scale(0.3).neg());
|
||||
|
||||
//this.motion.offset_ip(move.neg().scale(40));
|
||||
|
||||
Vec added = move.scale(1 / hitBy.mass);
|
||||
|
||||
|
||||
hitBy.getMotion().add_ip(added);
|
||||
getMotion().add_ip(move.neg().scale(1 / mass));
|
||||
|
||||
hitBy.getMotion().add_ip(getMotion().scale((1 / hitBy.mass) * 0.1));
|
||||
getMotion().add_ip(hitBy.getMotion().scale((1 / mass) * 0.1));
|
||||
|
||||
double damageGot = hitBy.mass * hitBy.getSpeed();
|
||||
if (!Double.isNaN(damageGot)) addDamage(hitBy, damageGot);
|
||||
double damageGiven = mass * getSpeed();
|
||||
if (!Double.isNaN(damageGiven)) hitBy.addDamage(this, damageGiven);
|
||||
|
||||
|
||||
if (!isDead) {
|
||||
explode(midpoint, 0.01, false);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode, if not cooled down yet, do nothing.
|
||||
*
|
||||
* @param pos position of explosion
|
||||
* @param strength strength of explosion
|
||||
* @param shards has shards
|
||||
*/
|
||||
public final void explode(Coord pos, double strength, boolean shards) {
|
||||
if (!Utils.canSkipRendering(pos) && explodeCooldown == 0) {
|
||||
Effects.addExplosion(scene.particles, pos, getMotion(), strength, shards, hasGlobalMovement());
|
||||
explodeCooldown = ExplodeCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode, ignore explodeCooldown
|
||||
*
|
||||
* @param pos position of explosion
|
||||
* @param strength strength of explosion
|
||||
* @param shards has shards
|
||||
*/
|
||||
public final void explodeForce(Coord pos, double strength, boolean shards) {
|
||||
if (!Utils.canSkipRendering(pos)) {
|
||||
Effects.addExplosion(scene.particles, pos, getMotion(), strength, shards, hasGlobalMovement());
|
||||
explodeCooldown += ExplodeCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allowVerticalMovement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Coord posBackup = null;
|
||||
private Vec motionBackup = null;
|
||||
|
||||
/**
|
||||
* Update entity position and other things. Called each update tick.
|
||||
*/
|
||||
public final void update() {
|
||||
|
||||
if (posBackup == null) posBackup = getPos().copy();
|
||||
if (motionBackup == null) motionBackup = getMotion().copy();
|
||||
|
||||
if (!allowVerticalMovement()) {
|
||||
getPos().setY_ip(0);
|
||||
getMotion().setY_ip(0);
|
||||
}
|
||||
|
||||
|
||||
if (effectEmpTicks > 0) effectEmpTicks -= 1 * Constants.SPEED_MUL;
|
||||
if (effectFireTicks > 0) effectFireTicks -= 1 * Constants.SPEED_MUL;
|
||||
|
||||
|
||||
getPos().pushLast();
|
||||
rotAngle.pushLast();
|
||||
|
||||
fixNans();
|
||||
|
||||
if (explodeCooldown > 0) explodeCooldown--;
|
||||
getPos().add_ip(motion.mul(Constants.SPEED_MUL));
|
||||
|
||||
if (hasGlobalMovement()) {
|
||||
// if(!(this instanceof EntityAsteroid))System.out.println("Global movement of: "+getClass().getSimpleName());
|
||||
getPos().add_ip(scene.getGlobalMovement().mul(Constants.SPEED_MUL));
|
||||
}
|
||||
|
||||
if (lifetime > 0) {
|
||||
lifetime--;
|
||||
if (lifetime == 0) {
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpParalyzed()) {
|
||||
if (rand.nextInt(5) == 0)
|
||||
Effects.addEMPExplosion(scene.particles, getPos(), getMotion(), 1.6 * collider.radius, hasGlobalMovement(), false);
|
||||
}
|
||||
|
||||
if (isOnFire()) {
|
||||
if (rand.nextInt(3) == 0) {
|
||||
Effects.addFireBurst(scene.particles, getPos(), getMotion(), collider.radius * 0.9, 6, hasGlobalMovement(), false);
|
||||
}
|
||||
|
||||
addDamage(fireSource, 0.09 * Constants.SPEED_MUL * getFireSensitivity());
|
||||
}
|
||||
|
||||
if (!isDead) onUpdate();
|
||||
|
||||
double sp = getSpeed();
|
||||
if (sp > MAX_SPEED) getMotion().norm_ip(MAX_SPEED);
|
||||
|
||||
fixNans();
|
||||
|
||||
getPos().update();
|
||||
|
||||
}
|
||||
|
||||
private void fixNans() {
|
||||
boolean correction = false;
|
||||
Coord pos = getPos().copy();
|
||||
|
||||
if (Double.isNaN(pos.x) || Double.isNaN(pos.y) || Double.isNaN(pos.z)) {
|
||||
correction = true;
|
||||
|
||||
getPos().x = Calc.fixNan(pos.x, posBackup.x);
|
||||
getPos().y = Calc.fixNan(pos.y, posBackup.y);
|
||||
getPos().z = Calc.fixNan(pos.z, posBackup.z);
|
||||
|
||||
getPos().pushLast();
|
||||
getPos().update();
|
||||
}
|
||||
|
||||
if (correction) {
|
||||
Log.f3("\n!!! Correction: position of " + Calc.className(this) + ":\n" + pos + " -> " + getPos());
|
||||
}
|
||||
|
||||
|
||||
|
||||
correction = false;
|
||||
|
||||
Vec motion = getMotion().copy();
|
||||
|
||||
if (Double.isNaN(motion.x) || Double.isNaN(motion.y) || Double.isNaN(motion.z)) {
|
||||
correction = true;
|
||||
|
||||
getMotion().x = Calc.fixNan(motion.x, motionBackup.x);
|
||||
getMotion().y = Calc.fixNan(motion.y, motionBackup.y);
|
||||
getMotion().z = Calc.fixNan(motion.z, motionBackup.z);
|
||||
|
||||
getMotion().pushLast();
|
||||
getMotion().update();
|
||||
}
|
||||
|
||||
if (correction) {
|
||||
Log.f3("\n!!! Correction: motion of " + Calc.className(this) + ":\n" + motion + " -> " + getMotion());
|
||||
}
|
||||
|
||||
posBackup.setTo(getPos());
|
||||
motionBackup.setTo(getMotion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if is EMP paralyzed (unable to move,shootm etc)
|
||||
*
|
||||
* @return is emp paralyzed
|
||||
*/
|
||||
public final boolean isEmpParalyzed() {
|
||||
return effectEmpTicks > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if is on fire
|
||||
*
|
||||
* @return is on fire
|
||||
*/
|
||||
public final boolean isOnFire() {
|
||||
return effectFireTicks > 0;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Get if this entity is electronic and affected by EMP missiles
|
||||
// *
|
||||
// * @return is EMP sensitive
|
||||
// */
|
||||
// public abstract boolean isEmpSensitive();
|
||||
|
||||
/**
|
||||
* Get EMP sensitivity (1 is normal, 0 is EMP-protected)
|
||||
*
|
||||
* @return EMP sensitivity
|
||||
*/
|
||||
public abstract double getEmpSensitivity();
|
||||
|
||||
/**
|
||||
* Get fire sensitivity (1 is full, 0 is fire-protected)
|
||||
*
|
||||
* @return fire sensitivity
|
||||
*/
|
||||
public abstract double getFireSensitivity();
|
||||
|
||||
/**
|
||||
* Get fire flammability (1 is full, 0 is fire-protected) - how much fire
|
||||
* can be added by a fireball.
|
||||
*
|
||||
* @return fire sensitivity
|
||||
*/
|
||||
public abstract double getFireFlammability();
|
||||
|
||||
/**
|
||||
* Add EMP ticks
|
||||
*
|
||||
* @param ticks
|
||||
*/
|
||||
public final void addEmp(double ticks) {
|
||||
effectEmpTicks += ticks * getEmpSensitivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fire ticks
|
||||
*
|
||||
* @param ticks
|
||||
*/
|
||||
public final void addFire(IDamageable source, double ticks) {
|
||||
effectFireTicks += ticks * getFireFlammability();
|
||||
fireSource = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each update tick, for position update and AI.
|
||||
*/
|
||||
public abstract void onUpdate();
|
||||
|
||||
/**
|
||||
* Called when entity dies - for explosion effects etc.
|
||||
*/
|
||||
public abstract void onDeath();
|
||||
|
||||
/**
|
||||
* Render this entity
|
||||
*
|
||||
* @param delta
|
||||
*/
|
||||
public abstract void render(double delta);
|
||||
|
||||
/** Entity which gave last damage (for scoring) */
|
||||
protected IDamageable lastDamageSource = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (isDead()) return;
|
||||
lastDamageSource = source;
|
||||
health -= points / healthMul;
|
||||
if (health <= 0) {
|
||||
setDead();
|
||||
health = 0;
|
||||
|
||||
if (artifacts > 0) {
|
||||
spawnArtifact(artifacts);
|
||||
removeArtifacts();
|
||||
}
|
||||
|
||||
onDeath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do spawn artifact at ship pos (on death)
|
||||
*/
|
||||
public void spawnArtifact(int points) {
|
||||
scene.add(new EntityOrbArtifact(getPos(), points));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this entity belongs to a zone in Scene - zone map.<br>
|
||||
* Typically done by calculating lowest and highest Z coordinate of the
|
||||
* entity and comparing them to the boundaries.
|
||||
*
|
||||
* @param zFrom start z
|
||||
* @param zTo end z
|
||||
* @return belongs to zone (at least partially)
|
||||
*/
|
||||
public boolean belongsToZone(double zFrom, double zTo) {
|
||||
return Calc.inRange(collider.pos.z, zFrom - collider.radius - 0.5, zTo + collider.radius + 0.5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPos(Coord pos) {
|
||||
collider.pos.setTo(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxSpeed(double maxSpeed) {
|
||||
MAX_SPEED = maxSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scene getScene() {
|
||||
return scene;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadius() {
|
||||
return collider.radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColliderSphere getColliderFor(Collider hitBy) {
|
||||
return collider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get score value when killed by player
|
||||
*
|
||||
* @return score points
|
||||
*/
|
||||
public int getScoreValue() {
|
||||
return scoreValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set entity score value
|
||||
*
|
||||
* @param scoreValue score value
|
||||
*/
|
||||
public void setScoreValue(int scoreValue) {
|
||||
this.scoreValue = scoreValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get health remaining
|
||||
*
|
||||
* @return points of health remaining
|
||||
*/
|
||||
@Override
|
||||
public double getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get health max
|
||||
*
|
||||
* @return points of health remaining
|
||||
*/
|
||||
@Override
|
||||
public abstract double getHealthMax();
|
||||
|
||||
/**
|
||||
* Set health - if used to count damage.<br>
|
||||
* Modular ships don't use this.
|
||||
*
|
||||
* @param health
|
||||
*/
|
||||
public void setHealth(double health) {
|
||||
this.health = health;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get remaining life in update ticks
|
||||
*
|
||||
* @return lifetime life time remaining
|
||||
*/
|
||||
public int getLifetime() {
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial life time
|
||||
*
|
||||
* @param lifetime ticks of life
|
||||
*/
|
||||
public void setLifetime(int lifetime) {
|
||||
this.lifetime = lifetime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getRotDir() {
|
||||
return rotDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeltaDoubleDeg getRotAngle() {
|
||||
return rotAngle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare by Z position, sorting for particle rendering.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Entity o) {
|
||||
if (this == o) return 0;
|
||||
return Double.valueOf(getPos().z).compareTo(o.getPos().z);
|
||||
}
|
||||
|
||||
/**
|
||||
* heal this entity
|
||||
*
|
||||
* @param add health points to add
|
||||
*/
|
||||
public void addHealth(double add) {
|
||||
this.health = Calc.clampd(this.health + add, 0, getHealthMax());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.level.drivers.INavigated;
|
||||
import net.sector.level.drivers.Navigator;
|
||||
import net.sector.level.drivers.TaskList;
|
||||
import net.sector.models.PhysModel;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Polar;
|
||||
import com.porcupine.math.PolarDeg;
|
||||
|
||||
|
||||
/**
|
||||
* Navigable entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class EntityNavigable extends Entity implements INavigated {
|
||||
|
||||
/** Velocity multiplier */
|
||||
public double speedMul1 = 1;
|
||||
|
||||
/** Stable speed multiplier, set when spawning entity */
|
||||
public double speedMulStable = 1;
|
||||
|
||||
/** Ship driver */
|
||||
public Navigator nav = new Navigator(this);
|
||||
|
||||
/** scale (relative, 1 default) */
|
||||
public double scale = 1;
|
||||
|
||||
/** render scale (how much is the model enlarged) */
|
||||
public double scaleRender = 1;
|
||||
|
||||
/** Phys model */
|
||||
public PhysModel model = null;
|
||||
|
||||
/** Desired motion speed */
|
||||
public double fullMoveSpeed = 0.04;
|
||||
|
||||
private Entity[] fleet;
|
||||
|
||||
private Entity cachedLeader = null;
|
||||
|
||||
private EFormation formation = EFormation.NONE;
|
||||
|
||||
private Entity targetEntity = null;
|
||||
|
||||
@Override
|
||||
public void spawnArtifact(int num) {
|
||||
// if in formation, only the last one drops artifact.
|
||||
|
||||
if (formation == EFormation.SHAPE) {
|
||||
for (Entity e : fleet) {
|
||||
if (e == this) continue;
|
||||
if (e.isDead()) continue;
|
||||
return;
|
||||
}
|
||||
|
||||
super.spawnArtifact(num);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (formation != EFormation.NONE) {
|
||||
if (formationIsLeader() && formationIsTail()) {
|
||||
super.spawnArtifact(num);
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.spawnArtifact(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getTargetEntity() {
|
||||
return targetEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTargetEntity(Entity targetEntity) {
|
||||
this.targetEntity = targetEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFormation(Entity[] fleet, EFormation formation) {
|
||||
this.fleet = fleet;
|
||||
this.formation = formation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getFormationLeader() {
|
||||
if (formation == EFormation.NONE) return null;
|
||||
if (cachedLeader == null || cachedLeader.isDead() || cachedLeader.getPos().z < -1) {
|
||||
cachedLeader = null;
|
||||
if (formation == EFormation.SNAKE) cachedLeader = formationSnakeGetLeader();
|
||||
if (formation == EFormation.SWARM) cachedLeader = formationSwarmGetLeader();
|
||||
}
|
||||
|
||||
return cachedLeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean formationIsTail() {
|
||||
if (formation == EFormation.NONE) return false;
|
||||
if (formation == EFormation.SNAKE) {
|
||||
for (int i = 0; i < fleet.length; i++) {
|
||||
if (fleet[i] == null || fleet[i].isDead()) continue;
|
||||
return fleet[i] == this;
|
||||
}
|
||||
}
|
||||
if (formation == EFormation.SWARM) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean formationIsLeader() {
|
||||
return getFormationLeader() == null;
|
||||
}
|
||||
|
||||
private Entity formationSnakeGetLeader() {
|
||||
boolean finding = false;
|
||||
int leader = -1;
|
||||
for (int i = 0; i < fleet.length; i++) {
|
||||
Entity e = fleet[i];
|
||||
if (e == null) continue;
|
||||
if (e == this) {
|
||||
finding = true;
|
||||
continue;
|
||||
}
|
||||
if (e.isDead()) continue;
|
||||
|
||||
if (finding) {
|
||||
leader = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leader != -1) {
|
||||
return fleet[leader];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Entity formationSwarmGetLeader() {
|
||||
int leader = -1;
|
||||
|
||||
for (int i = fleet.length - 1; i >= 0; i--) {
|
||||
Entity e = fleet[i];
|
||||
if (e == null) continue;
|
||||
if (e.isDead()) continue;
|
||||
leader = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (leader != -1) {
|
||||
return fleet[leader];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean formationContains(Entity drone) {
|
||||
if (fleet != null) {
|
||||
for (Entity e : fleet)
|
||||
if (e == drone) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param model
|
||||
* @param desiredSpeed
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
protected EntityNavigable(PhysModel model, double desiredSpeed, double scale, Coord pos) {
|
||||
this.fullMoveSpeed = desiredSpeed;
|
||||
this.collidePriority = 1;
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
collider = new ColliderSphere(pos, model.colliderRadius * scale);
|
||||
|
||||
this.motion.setTo(0, 0, -desiredSpeed);
|
||||
|
||||
this.lifetime = -1;
|
||||
|
||||
this.MAX_SPEED = 0.3;
|
||||
|
||||
// rotateable around Y axis
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
// rotate towards the player
|
||||
this.rotAngle.set(270);
|
||||
|
||||
setModel(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the model (alter entity)
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
public void setModel(PhysModel model) {
|
||||
this.model = model;
|
||||
|
||||
this.scaleRender = model.renderScale * scale;
|
||||
|
||||
this.mass = model.getMass(scale);
|
||||
this.collider.radius = model.colliderRadius * scale;
|
||||
|
||||
this.health = model.getHealth(scale);
|
||||
|
||||
this.scoreValue = (int) (model.getScore(scale));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust strength, radius, score and mass for scale
|
||||
*
|
||||
* @param scale relative scale (1 = normal)
|
||||
* @return this
|
||||
*/
|
||||
public EntityNavigable adjustForScale(double scale) {
|
||||
this.scale = scale;
|
||||
this.scaleRender = model.renderScale * scale;
|
||||
|
||||
this.mass = model.getMass(scale);
|
||||
collider.radius = model.colliderRadius * scale;
|
||||
|
||||
this.health = model.getHealth(scale);
|
||||
|
||||
this.scoreValue = (int) (model.getScore(scale));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get direction based on entity rotation
|
||||
*
|
||||
* @return direction vector
|
||||
*/
|
||||
public Vec getRotateAimVector() {
|
||||
double deg = getRotAngle().get();
|
||||
PolarDeg pl = new PolarDeg(deg + 90, 1);
|
||||
return new Vec(pl.toCoordXZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get direction based on entity rotation with random deviation
|
||||
*
|
||||
* @param deviationDeg max angular deviation (degrees)
|
||||
* @return direction vector
|
||||
*/
|
||||
public Vec getRotateAimVector(double deviationDeg) {
|
||||
double deg = getRotAngle().get();
|
||||
PolarDeg pl = new PolarDeg(deg + 90 - deviationDeg + rand.nextDouble() * deviationDeg * 2, 1);
|
||||
return new Vec(pl.toCoordXZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get direction based on entity rotation with degrees deviation
|
||||
*
|
||||
* @param addAngle angle added
|
||||
* @return direction vector
|
||||
*/
|
||||
public Vec getRotateAimVectorPlusDeg(double addAngle) {
|
||||
double deg = getRotAngle().get();
|
||||
PolarDeg pl = new PolarDeg(deg + addAngle, 1);
|
||||
return new Vec(pl.toCoordXZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vector from this entity to player
|
||||
*
|
||||
* @return vector to player
|
||||
*/
|
||||
public Vec getVectorToPlayer() {
|
||||
return getPos().vecTo(getScene().getPlayerShip().getPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vector from this entity to player
|
||||
*
|
||||
* @param deviationDeg max angular deviation (degrees)
|
||||
* @return vector to player
|
||||
*/
|
||||
public Vec getVectorToPlayer(double deviationDeg) {
|
||||
Vec v = getVectorToPlayer();
|
||||
PolarDeg p = PolarDeg.fromCoord(v.x, v.z);
|
||||
PolarDeg p2 = new PolarDeg(p.angle - deviationDeg + rand.nextDouble() * deviationDeg * 2, p.distance);
|
||||
return new Vec(p2.toCoordXZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vector to closest asteroid, or null if no target found.
|
||||
*
|
||||
* @return vector to player
|
||||
*/
|
||||
public Vec getVectorToClosestAsteroid() {
|
||||
Set<Entity> ents = getScene().getEntitiesInRange(getPos(), 6);
|
||||
double shortest = 100;
|
||||
Coord shortestPos = null;
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e.getType() == EEntity.NATURAL) {
|
||||
double d;
|
||||
|
||||
if ((d = getPos().vecTo(e.getPos()).size()) < shortest) {
|
||||
shortest = d;
|
||||
shortestPos = e.getPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shortestPos != null) {
|
||||
return getPos().vecTo(shortestPos);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* MUST BE OVERRIDEN AND USED
|
||||
*
|
||||
* @param scale scale 1 = normal
|
||||
* @param pos coord center
|
||||
*/
|
||||
//public EntityNavigable(double scale, Coord pos) {}
|
||||
|
||||
/**
|
||||
* Constructor without model.<br>
|
||||
* !!! You must define things like score, health, lifetime and mass
|
||||
* yourself.
|
||||
*
|
||||
* @param desiredSpeed desired motion speed for AI
|
||||
* @param pos position
|
||||
* @param radius collider radius
|
||||
*/
|
||||
public EntityNavigable(double desiredSpeed, Coord pos, double radius) {
|
||||
this.fullMoveSpeed = desiredSpeed;
|
||||
this.collidePriority = 1;
|
||||
|
||||
collider = new ColliderSphere(pos, radius * scale);
|
||||
|
||||
this.motion.setTo(0, 0, -desiredSpeed);
|
||||
|
||||
this.lifetime = -1;
|
||||
|
||||
// rotateable around Y axis
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
// rotate towards the player
|
||||
this.rotAngle.set(270);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor without model.<br>
|
||||
* !!! You must define things like score, collider, rotDir, rotAngle,
|
||||
* collider priority, health, lifetime and mass yourself.
|
||||
*
|
||||
* @param pos position
|
||||
*/
|
||||
public EntityNavigable(Coord pos) {
|
||||
this.fullMoveSpeed = 0.001;
|
||||
this.collidePriority = 1;
|
||||
|
||||
collider = new ColliderSphere(pos, 0.5);
|
||||
|
||||
this.motion.setTo(0, 0, -fullMoveSpeed);
|
||||
|
||||
this.lifetime = -1;
|
||||
|
||||
// rotateable around Y axis
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
// rotate towards the player
|
||||
this.rotAngle.set(270);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDriver(TaskList driver) {
|
||||
this.nav.setDriver(driver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Navigator getNavigator() {
|
||||
return nav;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDesiredSpeed() {
|
||||
return fullMoveSpeed * speedMul1 * speedMulStable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDesiredSpeed(double speed) {
|
||||
fullMoveSpeed = speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShipLevel(int level) {}
|
||||
|
||||
@Override
|
||||
public abstract void shootOnce(int gunIndex);
|
||||
|
||||
@Override
|
||||
public abstract Vec getGunShotDir(int gunIndex);
|
||||
|
||||
/**
|
||||
* Get data for shot - position and direction.
|
||||
*
|
||||
* @param x relative X coordinate (affected by scale factor)
|
||||
* @param z relative Z coordinate (affected by scale factor)
|
||||
* @return struct of (real position, motion vector)
|
||||
*/
|
||||
protected Coord getShotPos(double x, double z) {
|
||||
Vec zplus = motion.norm(z * scale);
|
||||
Coord mtn = motion.norm(x * scale);
|
||||
Polar pr = Polar.fromCoord(mtn.x, mtn.z);
|
||||
pr.angle += Math.PI / 2;
|
||||
|
||||
Vec ro = new Vec(pr.toCoord());
|
||||
|
||||
Coord pos = getPos().add(ro.x, 0, ro.z).add(zplus);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.ENEMY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void onImpact(Entity hitBy);
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireFlammability() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.85;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
if (nav != null && !isDead) nav.onUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void onDeath();
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
if (isDead) return;
|
||||
if (model != null) {
|
||||
glLoadIdentity();
|
||||
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glScaled(scaleRender, scaleRender, scaleRender);
|
||||
|
||||
model.render();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract double getHealthMax();
|
||||
|
||||
@Override
|
||||
public double getHealthPercent() {
|
||||
return Math.round((getHealth() / getHealthMax()) * 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGlobalMovement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ship variant - used for level building.
|
||||
*
|
||||
* @param variant variant number
|
||||
*/
|
||||
public void setShipVariant(int variant) {}
|
||||
|
||||
|
||||
public double getSpeedMultiplier() {
|
||||
return speedMulStable;
|
||||
}
|
||||
|
||||
public void setSpeedMultiplier(double speedMul) {
|
||||
this.speedMulStable = speedMul;
|
||||
}
|
||||
|
||||
public void setStableSpeedMultiplier(double speed) {
|
||||
this.speedMul1 = speed;
|
||||
}
|
||||
|
||||
public double getStableSpeedMultiplier() {
|
||||
return speedMul1;
|
||||
}
|
||||
|
||||
public double getSpeedMultiplierTotal() {
|
||||
return getSpeedMultiplier() * getStableSpeedMultiplier();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
public interface IDamageable {
|
||||
public void addDamage(IDamageable source, double points);
|
||||
|
||||
public boolean isDead();
|
||||
|
||||
public double getHealth();
|
||||
|
||||
public EEntity getType();
|
||||
|
||||
double getHealthMax();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
import net.sector.collision.Collider;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.collision.Scene;
|
||||
import net.sector.util.DeltaDoubleDeg;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Entity interface for physics calculations
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface IPhysEntity extends IDamageable {
|
||||
public Coord getPos();
|
||||
|
||||
public Vec getMotion();
|
||||
|
||||
public void setMotion(Vec motion);
|
||||
|
||||
public void setPos(Coord pos);
|
||||
|
||||
public void setMaxSpeed(double maxSpeed);
|
||||
|
||||
public double getSpeed();
|
||||
|
||||
public void setDead();
|
||||
|
||||
@Override
|
||||
public boolean isDead();
|
||||
|
||||
@Override
|
||||
public double getHealth();
|
||||
|
||||
public double getMass();
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double damage);
|
||||
|
||||
public Scene getScene();
|
||||
|
||||
public void setScene(Scene scene);
|
||||
|
||||
public double getRadius();
|
||||
|
||||
public Vec getRotDir();
|
||||
|
||||
public DeltaDoubleDeg getRotAngle();
|
||||
|
||||
public ColliderSphere getColliderFor(Collider hitBy);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.sector.entities;
|
||||
|
||||
|
||||
/**
|
||||
* This be an interface for player's ship, which counts score.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface IScoreCounter {
|
||||
/**
|
||||
* Add score to counter.
|
||||
*
|
||||
* @param points points to add.
|
||||
*/
|
||||
public void addScore(int points);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.orbs.EntityOrbShield;
|
||||
import net.sector.entities.player.EntityPlayerShip;
|
||||
import net.sector.entities.shots.EntityShotBase;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Mine entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityMine extends EntityNavigable {
|
||||
|
||||
private double scale = 1;
|
||||
private double healthMax;
|
||||
|
||||
private static PhysModel shipModel = Models.spaceMine;
|
||||
|
||||
private static final double mMoveSpeed = 0.00001;
|
||||
|
||||
@Override
|
||||
public boolean hasGlobalMovement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship
|
||||
*
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
public EntityMine(double scale, Coord pos) {
|
||||
super(shipModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.collidePriority = 2000;
|
||||
this.lifetime = -1;
|
||||
this.MAX_SPEED = 0.2 * (0.3 / mass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship, scale=1
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public EntityMine(Coord pos) {
|
||||
super(shipModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.collidePriority = 2000;
|
||||
this.lifetime = -1;
|
||||
this.MAX_SPEED = 0.2 * (0.3 / mass);
|
||||
}
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("mine"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return healthMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
if (hitBy == null) return;
|
||||
|
||||
boolean dead = false;
|
||||
if (hitBy.getType() == EEntity.SHOT_BAD || hitBy.getType() == EEntity.SHOT_GOOD) {
|
||||
if (rand.nextInt(2) == 0) {
|
||||
dead = true;
|
||||
if (((EntityShotBase) hitBy).scoreCounter != null) {
|
||||
((EntityShotBase) hitBy).scoreCounter.addScore(scoreValue);
|
||||
}
|
||||
|
||||
boom(hitBy);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
dead = true;
|
||||
boom(hitBy);
|
||||
}
|
||||
|
||||
if (!hitBy.isDead()) defaultOnImpact(hitBy);
|
||||
|
||||
if (dead) setDead();
|
||||
}
|
||||
|
||||
public void boom(Entity hitBy) {
|
||||
explodeForce(getPos(), 30 * scale, true);
|
||||
|
||||
double range = 6;
|
||||
double distMultiplier = 7; // to make it fade away faster
|
||||
|
||||
double damage = 50 * scale;
|
||||
|
||||
if (hitBy != null) {
|
||||
if (!(hitBy instanceof EntityPlayerShip)) {
|
||||
hitBy.addDamage(this, damage);
|
||||
} else {
|
||||
((EntityPlayerShip) hitBy).piecesAddDamageSquare(collider, damage, distMultiplier, range);
|
||||
}
|
||||
}
|
||||
|
||||
Set<Entity> ents = getScene().getEntitiesInRange(getPos(), range);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e.isDead()) continue;
|
||||
if (e instanceof EntityMine) continue;
|
||||
if (e == hitBy) continue;
|
||||
|
||||
double dist = e.getPos().distTo(getPos()) - e.getRadius() - getRadius();
|
||||
|
||||
dist *= distMultiplier;
|
||||
|
||||
if (dist < 1) dist = 1;
|
||||
|
||||
if (e.getType() == EEntity.PLAYER) {
|
||||
((EntityPlayerShip) e).piecesAddDamageSquare(collider, damage, distMultiplier, range);
|
||||
}
|
||||
|
||||
e.addDamage(this, damage / (dist * dist));
|
||||
e.getMotion().add_ip(getPos().vecTo(e.getPos()).norm(0.1 / (dist * dist)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
if (lastDamageSource.getType() == EEntity.SHOT_GOOD && rand.nextInt(4) == 0) {
|
||||
if (scene.playerShip.body.shieldSystem.getLoadRatio() < 1) {
|
||||
scene.add(new EntityOrbShield(getPos(), 400 * (0.6 + rand.nextDouble() * 0.7)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.MINE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.orbs.EntityOrbShield;
|
||||
import net.sector.entities.shots.EntityLaser2;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy ship entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipBird extends EntityNavigable {
|
||||
|
||||
private static PhysModel shipModel = Models.enemyBird;
|
||||
|
||||
private static final double mMoveSpeed = 0.11;
|
||||
|
||||
/**
|
||||
* Enemy ship
|
||||
*
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipBird(double scale, Coord pos) {
|
||||
super(shipModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship, scale=1
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipBird(Coord pos) {
|
||||
super(shipModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("bird"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
|
||||
if (!Utils.canSkipRendering(getPos())) {
|
||||
Vec back = getRotateAimVector().neg();
|
||||
Coord pos = getShotPos(0, -0.45);
|
||||
Effects.addEngineFire(getScene().particles, pos, back.norm(0.025), 2, 0, scale);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
|
||||
|
||||
Coord pos = getShotPos(0, 1);
|
||||
|
||||
Vec motion = getGunShotDir(gunIndex);
|
||||
|
||||
if (collider.pos.z > 0) {
|
||||
scene.add(new EntityLaser2(pos, motion, this, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getVectorToPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass, true);
|
||||
|
||||
if (lastDamageSource.getType() == EEntity.SHOT_GOOD && rand.nextInt(4) == 0) {
|
||||
if (scene.playerShip.body.shieldSystem.getLoadRatio() < 1) {
|
||||
scene.add(new EntityOrbShield(getPos(), 500 * (0.6 + rand.nextDouble() * 0.7)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityPlasma;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy burger entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipBurger extends EntityNavigable {
|
||||
private static PhysModel burgerModel = Models.enemyBurger;
|
||||
|
||||
|
||||
private static final double mMoveSpeed = 0.075;
|
||||
|
||||
/**
|
||||
* Enemy Burger
|
||||
*
|
||||
* @param scale scale (1)
|
||||
* @param pos center pos
|
||||
*/
|
||||
public EntityShipBurger(double scale, Coord pos) {
|
||||
super(burgerModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy Burger scale=1
|
||||
*
|
||||
* @param pos center pos
|
||||
*/
|
||||
public EntityShipBurger(Coord pos) {
|
||||
super(burgerModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("burger_zone"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
Vec dir = getGunShotDir(gunIndex);
|
||||
if (dir == null) return;
|
||||
double slevel = 1 + 0.3 * scale;
|
||||
EntityPlasma shot = new EntityPlasma(getPos(), dir, this, gunIndex == 100 ? 3 : slevel);
|
||||
shot.setColor(new RGB(1, 0.6, 0.6, 1)).setScale(0.1 + 0.05 * scale);
|
||||
//shot.getMotion().add_ip(getMotion());
|
||||
scene.add(shot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
// 0 to player
|
||||
// 1 in direction of rotation
|
||||
// other in dir of motion
|
||||
if (gunIndex == 0) return getVectorToPlayer();
|
||||
if (gunIndex == 1) return getRotateAimVector();
|
||||
if (gunIndex == 100) return getVectorToClosestAsteroid();
|
||||
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityPlasma;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy burger entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipBurgerKing extends EntityNavigable {
|
||||
private static PhysModel burgerModel = Models.enemyBurgerKing;
|
||||
|
||||
|
||||
private static final double mMoveSpeed = 0.08;
|
||||
|
||||
/**
|
||||
* Enemy Burger
|
||||
*
|
||||
* @param scale scale (1)
|
||||
* @param pos center pos
|
||||
*/
|
||||
public EntityShipBurgerKing(double scale, Coord pos) {
|
||||
super(burgerModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy Burger scale=1
|
||||
*
|
||||
* @param pos center pos
|
||||
*/
|
||||
public EntityShipBurgerKing(Coord pos) {
|
||||
super(burgerModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("shark"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0.05;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
Vec dir = getGunShotDir(gunIndex);
|
||||
if (dir == null) return;
|
||||
double slevel = 4 + 0.3 * scale;
|
||||
EntityPlasma shot = new EntityPlasma(getPos(), dir, this, gunIndex == 100 ? 3 : slevel);
|
||||
shot.setColor(new RGB(1, 0.6, 0.6, 1)).setScale(0.1 + 0.05 * scale);
|
||||
//shot.getMotion().add_ip(getMotion());
|
||||
scene.add(shot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
// 0 to player
|
||||
// 1 in direction of rotation
|
||||
// other in dir of motion
|
||||
if (gunIndex == 0) return getVectorToPlayer(15);
|
||||
if (gunIndex == 1) return getRotateAimVector();
|
||||
if (gunIndex == 100) return getVectorToClosestAsteroid();
|
||||
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityPlasma;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy cube entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipCube extends EntityNavigable {
|
||||
|
||||
private int type = 0;
|
||||
|
||||
private static final double mMoveSpeed = 0.07;
|
||||
|
||||
/**
|
||||
* Enemy Cube
|
||||
*
|
||||
* @param scale scale (1)
|
||||
* @param pos center pos
|
||||
* @param texture texture index 0-4
|
||||
*/
|
||||
public EntityShipCube(double scale, Coord pos, int texture) {
|
||||
super(Models.enemyCube[Calc.clampi(texture, 0, Models.enemyCube.length - 1)], mMoveSpeed, scale, pos);
|
||||
type = Calc.clampi(texture, 0, Models.enemyCube.length - 1);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy Cube scale=1
|
||||
*
|
||||
* @param pos center pos
|
||||
* @param texture texture index 0-4
|
||||
*/
|
||||
public EntityShipCube(Coord pos, int texture) {
|
||||
super(Models.enemyCube[Calc.clampi(texture, 0, Models.enemyCube.length - 1)], mMoveSpeed, 1, pos);
|
||||
type = Calc.clampi(texture, 0, Models.enemyCube.length - 1);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enemy Cube scale=1
|
||||
*
|
||||
* @param pos center pos
|
||||
* @param texture texture index 0-4
|
||||
*/
|
||||
public EntityShipCube(Coord pos) {
|
||||
this(pos, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShipVariant(int variant) {
|
||||
type = Calc.clampi(variant, 0, Models.enemyCube.length - 1);
|
||||
setModel(Models.enemyCube[Calc.clampi(type, 0, Models.enemyCube.length - 1)]);
|
||||
}
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("cube_snake"));
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
|
||||
Coord pos;
|
||||
Vec dir = getGunShotDir(gunIndex);
|
||||
if (dir == null) return;
|
||||
|
||||
pos = getPos();
|
||||
|
||||
|
||||
EntityPlasma shot = new EntityPlasma(pos, dir, this, gunIndex == 100 ? 3 : 1 + 0.3 * scale);
|
||||
shot.setColor(colors[type]).setScale(0.1 + 0.05 * scale);
|
||||
scene.add(shot);
|
||||
}
|
||||
|
||||
RGB[] colors = { new RGB(0.0, 1.0, 0.0), new RGB(1.0, 0.0, 0.0), new RGB(0.2, 0.5, 1.0), new RGB(0.6, 0.0, 1.0), new RGB(1.0, 1.0, 0.0), };
|
||||
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
if (gunIndex == 0) return getVectorToPlayer();
|
||||
if (gunIndex == 100) return getVectorToClosestAsteroid();
|
||||
return getRotateAimVector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityLaser;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy ship entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipFalcon extends EntityNavigable {
|
||||
|
||||
private static PhysModel shipModel = Models.enemyFalcon;
|
||||
|
||||
private static final double mMoveSpeed = 0.15;
|
||||
|
||||
/**
|
||||
* Enemy ship
|
||||
*
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipFalcon(double scale, Coord pos) {
|
||||
super(shipModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0.3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship, scale=1
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipFalcon(Coord pos) {
|
||||
super(shipModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("falcon"));
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
|
||||
Coord left = getShotPos(-2, 2);
|
||||
Coord right = getShotPos(2, 2);
|
||||
|
||||
Vec motion = getGunShotDir(gunIndex);
|
||||
RGB red = RGB.PURPLE;
|
||||
|
||||
if (collider.pos.z > 0) {
|
||||
scene.add(new EntityLaser(left, motion, this, red, 5));
|
||||
scene.add(new EntityLaser(right, motion, this, red, 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getRotateAimVector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass*2, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityLaser;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy ship entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipFighter extends EntityNavigable {
|
||||
|
||||
private static PhysModel shipModel = Models.enemyFighter;
|
||||
private int level = 1;
|
||||
|
||||
private static final double mMoveSpeed = 0.1;
|
||||
|
||||
/**
|
||||
* Enemy ship
|
||||
*
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipFighter(double scale, Coord pos) {
|
||||
super(shipModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship, scale=1
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipFighter(Coord pos) {
|
||||
super(shipModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("fighter"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
|
||||
if (!Utils.canSkipRendering(getPos())) {
|
||||
Vec back = getRotateAimVector().neg();
|
||||
Coord pos = getShotPos(-0.23, -0.55);
|
||||
Effects.addEngineFire(getScene().particles, pos, back.norm(0.025), 2, 0, scale);
|
||||
pos = getShotPos(0.23, -0.55);
|
||||
Effects.addEngineFire(getScene().particles, pos, back.norm(0.025), 2, 0, scale);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
|
||||
Coord left = getShotPos(-0.3, 2);
|
||||
Coord right = getShotPos(0.3, 2);
|
||||
|
||||
Vec motion = getGunShotDir(gunIndex);
|
||||
RGB red = RGB.RED;
|
||||
|
||||
if (collider.pos.z > 0) {
|
||||
scene.add(new EntityLaser(left, motion, this, red, level));
|
||||
scene.add(new EntityLaser(right, motion, this, red, level));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getRotateAimVector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package net.sector.entities.enemies;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.shots.EntityLaser2;
|
||||
import net.sector.entities.shots.EntityPlasma;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Enemy ship entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityShipShark extends EntityNavigable {
|
||||
|
||||
private static PhysModel shipModel = Models.enemyShark;
|
||||
|
||||
private static final double mMoveSpeed = 0.14;
|
||||
|
||||
/**
|
||||
* Enemy ship
|
||||
*
|
||||
* @param scale
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipShark(double scale, Coord pos) {
|
||||
super(shipModel, mMoveSpeed, scale, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enemy ship, scale=1
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public EntityShipShark(Coord pos) {
|
||||
super(shipModel, mMoveSpeed, 1, pos);
|
||||
setDefaultDriver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (!isEmpParalyzed()) {
|
||||
if (source.getType() == EEntity.ENEMY) return;
|
||||
if (source.getType() == EEntity.NATURAL) return;
|
||||
if (source.getType() == EEntity.SHOT_BAD) return;
|
||||
if (source.getType() == EEntity.MINE) return;
|
||||
}
|
||||
super.addDamage(source, points);
|
||||
}
|
||||
|
||||
|
||||
private void setDefaultDriver() {
|
||||
setDriver(SuperContext.basicDrivers.getDriver("shark"));
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setShipLevel(int level) {
|
||||
// this.level = level;
|
||||
// super.adjustForScale(1 + level * 0.5);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {
|
||||
if (Utils.canSkipRendering(getPos())) return;
|
||||
|
||||
Coord left = getShotPos(-2, 1);
|
||||
Coord right = getShotPos(2, 1);
|
||||
|
||||
Vec motion = getGunShotDir(gunIndex);
|
||||
|
||||
if (collider.pos.z > 0) {
|
||||
if (gunIndex == 0) {
|
||||
scene.add(new EntityPlasma(left, motion, this, 3 + 0.3 * scale).setGlobalMovement(false));
|
||||
scene.add(new EntityPlasma(right, motion, this, 3 + 0.3 * scale).setGlobalMovement(false));
|
||||
}
|
||||
if (gunIndex == 1) {
|
||||
scene.add(new EntityLaser2(left, motion, this, 3).setGlobalMovement(false));
|
||||
scene.add(new EntityLaser2(right, motion, this, 3).setGlobalMovement(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
if (gunIndex == 0) return getRotateAimVector(15);
|
||||
if (gunIndex == 1) return getRotateAimVector(10);
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), mass * 6, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return model.getHealth(scale);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package net.sector.entities.natural;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.orbs.EntityOrbShield;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Asteroid entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityAsteroid extends EntityNatural {
|
||||
|
||||
|
||||
private int texture = 0;
|
||||
/** Rotation speed */
|
||||
protected double rotSpeed = 0;
|
||||
|
||||
private PhysModel rock;
|
||||
|
||||
private double renderScale = 0;
|
||||
private double scale = 1;
|
||||
private double healthMax;
|
||||
|
||||
/**
|
||||
* Asteroid entity
|
||||
*
|
||||
* @param scale asteroid scale
|
||||
* @param pos asteroid center position
|
||||
* @param motion asteroid motion
|
||||
* @param texture asteroid texture index
|
||||
*/
|
||||
public EntityAsteroid(double scale, Coord pos, Vec motion, int texture) {
|
||||
|
||||
this.texture = texture;
|
||||
this.rotDir.setTo(-1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
this.rotSpeed = -5 + rand.nextDouble() * 10;
|
||||
|
||||
this.collidePriority = 0;
|
||||
|
||||
this.scale = scale;
|
||||
rock = Models.pickAsteroidOfType(texture);
|
||||
|
||||
this.renderScale = rock.renderScale * scale;
|
||||
|
||||
this.healthMax = this.health = rock.health * scale;
|
||||
this.lifetime = -1;
|
||||
this.mass = rock.getMass(scale);
|
||||
collider = new ColliderSphere(pos, rock.colliderRadius * scale);
|
||||
this.motion.setTo(motion);
|
||||
this.scoreValue = (int) Math.round(rock.getScore(scale));
|
||||
|
||||
this.MAX_SPEED = 0.2 * (0.3 / mass);
|
||||
|
||||
setGlobalMovement(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return healthMax;
|
||||
}
|
||||
|
||||
//private static Object3D model = new Object3D("res/models/asteroid03.obj", true);
|
||||
/**
|
||||
* Asteroid entity
|
||||
*
|
||||
* @param scale asteroid scale
|
||||
* @param pos asteroid position
|
||||
* @param motion asteroid motion
|
||||
*/
|
||||
public EntityAsteroid(double scale, Coord pos, Vec motion) {
|
||||
this(scale, pos, motion, rand.nextInt(Models.rockTypes.length));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
this.rotSpeed = Calc.clampd(this.rotSpeed + rand.nextGaussian(), -5, 5);
|
||||
|
||||
if (hitBy instanceof EntityAsteroid) {
|
||||
((EntityAsteroid) hitBy).rotSpeed = Calc.clampd(((EntityAsteroid) hitBy).rotSpeed + rand.nextGaussian(), -5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
rotAngle.add(rotSpeed * Constants.SPEED_MUL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glPushMatrix();
|
||||
|
||||
glLoadIdentity();
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glScaled(renderScale, renderScale, renderScale);
|
||||
rock.model.render();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean belongsToZone(double zFrom, double zTo) {
|
||||
return Calc.inRange(collider.pos.z, zFrom - collider.radius, zTo + collider.radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), getRadius() * 14, true);
|
||||
|
||||
//explodeForce(getPos(), mass / 2, true);
|
||||
|
||||
if (scale > 0.4) {
|
||||
int pieces = 2 + rand.nextInt(7);
|
||||
|
||||
double vol = Calc.sphereGetVolume(scale) * 0.4;
|
||||
|
||||
double approxPerPart = vol / pieces;
|
||||
|
||||
double[] volumes = new double[pieces];
|
||||
|
||||
for (int i = 0; i < pieces; i++) {
|
||||
double v = 0.05 + ((rand.nextDouble() + rand.nextDouble()) / 2) * approxPerPart;
|
||||
if (v > vol) v = vol;
|
||||
vol -= v;
|
||||
if (v < 0) v = Calc.sphereGetVolume(0.05 + rand.nextDouble() * 0.1);
|
||||
volumes[i] = v;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < pieces; i++) {
|
||||
double newScale = Calc.sphereGetRadius(volumes[i]);
|
||||
|
||||
if (newScale < 0.05) newScale = 0.05;
|
||||
|
||||
Coord apos = this.getPos();
|
||||
|
||||
double r = getRadius() * 0.6;
|
||||
|
||||
apos.add_ip(-r + rand.nextDouble() * (r) * 2, 0, -r + rand.nextDouble() * (r) * 2);
|
||||
|
||||
Vec amotion = getPos().vecTo(apos).norm(0.05 + rand.nextDouble() * 0.1);
|
||||
Entity e;
|
||||
scene.add(e = new EntityAsteroid(newScale, apos, amotion, texture));
|
||||
e.health *= 0.1;
|
||||
e.scoreValue *= 0.6;
|
||||
e.healthMul = healthMul * 0.8;
|
||||
|
||||
// tiny rocks will eventually disappear.
|
||||
if (newScale < 0.15) e.lifetime = (int) (Constants.FPS_UPDATE * (0.3 + rand.nextDouble() * 5));
|
||||
}
|
||||
}
|
||||
|
||||
if (lastDamageSource.getType() == EEntity.SHOT_GOOD && rand.nextInt(3) == 0 && scale > 0.5) {
|
||||
if (scene.playerShip.body.shieldSystem.getLoadRatio() < 1) {
|
||||
scene.add(new EntityOrbShield(getPos(), scale * 600 * (0.6 + rand.nextDouble() * 0.7)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.NATURAL;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.sector.entities.natural;
|
||||
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
|
||||
|
||||
public abstract class EntityNatural extends Entity {
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.NATURAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void onImpact(Entity hitBy);
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireFlammability() {
|
||||
return 0.3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0.08;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void onUpdate();
|
||||
|
||||
@Override
|
||||
public abstract void onDeath();
|
||||
|
||||
@Override
|
||||
public abstract void render(double delta);
|
||||
|
||||
@Override
|
||||
public abstract double getHealthMax();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package net.sector.entities.orbs;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphereFake;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.player.EntityPlayerShip;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.level.ship.modules.pieces.Piece;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.wavefront.loader.RenderModel;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
import net.sector.util.Log;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
public class EntityOrbArtifact extends EntityNavigable {
|
||||
|
||||
private double scale = 0.25;
|
||||
// protected double rotSpeed = 2;
|
||||
|
||||
private static RenderModel model = Models.orbArtifact;
|
||||
private double scaleRender = 0.5;
|
||||
|
||||
private Coord target;
|
||||
|
||||
private int discoveryPoints;
|
||||
|
||||
|
||||
public EntityOrbArtifact(Coord pos, int artifacts) {
|
||||
super(pos);
|
||||
this.discoveryPoints = artifacts;
|
||||
|
||||
setDriver(SuperContext.basicDrivers.getDriver("powerup_artifact"));
|
||||
|
||||
fullMoveSpeed = 0.06;
|
||||
|
||||
this.target = new Coord(-1 + rand.nextDouble() * 2, 0, -1);
|
||||
|
||||
this.scoreValue = 0;
|
||||
this.health = 1000;
|
||||
this.mass = 0.6;
|
||||
this.collidePriority = 2003;
|
||||
|
||||
this.lifetime = Constants.FPS_UPDATE * 5000;
|
||||
this.motion.setTo(0, 0, -0.003);
|
||||
|
||||
this.rotDir.setTo(-1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
//this.rotSpeed = -3 + rand.nextDouble() * 6;
|
||||
|
||||
this.collider = new ColliderSphereFake(pos, scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
|
||||
motion.norm_ip(fullMoveSpeed);
|
||||
addEffect();
|
||||
|
||||
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 1);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == scene.playerShip) {
|
||||
EntityPlayerShip sh = (EntityPlayerShip) e;
|
||||
Log.f3("Adding artifact to player.");
|
||||
if (discoveryPoints > 0) {
|
||||
sh.cursor.addArtifact(discoveryPoints);
|
||||
discoveryPoints = 0;
|
||||
}
|
||||
|
||||
|
||||
sh.body.energySystem.fill();
|
||||
sh.body.shieldSystem.fill();
|
||||
|
||||
for (Piece p : sh.body.allPieces) {
|
||||
if (!p.isDead) {
|
||||
p.addHealth(p.getHealthMax() / 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sounds.powerup2.playEffect(1f, 0.5f, false);
|
||||
Effects.addOrbBurst(scene.particles, getPos(), getMotion(), scaleRender * 1.5, 200, 1, false, true);
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
Sounds.appear.playEffect(1f, 0.2f, false);
|
||||
}
|
||||
|
||||
public void addEffect() {
|
||||
Effects.addOrbBurst(scene.particles, getPos(), getMotion(), scaleRender * 1.5, 20, 1, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glLoadIdentity();
|
||||
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glScaled(scaleRender, scaleRender, scaleRender);
|
||||
|
||||
model.render();
|
||||
TextureManager.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
// dont add damage
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.BONUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
System.out.println("Artifact died. That sucks.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean belongsToZone(double zFrom, double zTo) {
|
||||
return Calc.inRange(collider.pos.z, zFrom - 2 * collider.radius, zTo + 2 * collider.radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireFlammability() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShipLevel(int level) {}
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthPercent() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package net.sector.entities.orbs;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphereFake;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.EntityNavigable;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.player.EntityPlayerShip;
|
||||
import net.sector.level.SuperContext;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.wavefront.loader.RenderModel;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
public class EntityOrbShield extends EntityNavigable {
|
||||
|
||||
private double scale = 0.25;
|
||||
// protected double rotSpeed = 2;
|
||||
|
||||
private static RenderModel model = Models.orbShield;
|
||||
private double scaleRender = 0.25;
|
||||
|
||||
private Coord target;
|
||||
|
||||
private double shieldPoints = 100;
|
||||
|
||||
|
||||
public EntityOrbShield(Coord pos, double shieldPoints) {
|
||||
super(pos);
|
||||
|
||||
shieldPoints = Calc.clampd(shieldPoints, 0, 650);
|
||||
|
||||
setDriver(SuperContext.basicDrivers.getDriver("powerup_shield"));
|
||||
|
||||
fullMoveSpeed = 0.06;
|
||||
|
||||
this.target = new Coord(-1 + rand.nextDouble() * 2, 0, -1);
|
||||
|
||||
this.scaleRender *= (shieldPoints / 700);
|
||||
|
||||
this.scoreValue = 0;
|
||||
this.health = 1000;
|
||||
this.mass = 0.6;
|
||||
this.collidePriority = 2002;
|
||||
|
||||
this.lifetime = Constants.FPS_UPDATE * 500;
|
||||
this.motion.setTo(0, 0, -0.003);
|
||||
|
||||
this.rotDir.setTo(-1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2, -1 + rand.nextDouble() * 2);
|
||||
this.rotAngle.set(rand.nextDouble() * 360);
|
||||
//this.rotSpeed = -3 + rand.nextDouble() * 6;
|
||||
|
||||
this.collider = new ColliderSphereFake(pos, scale);
|
||||
|
||||
this.shieldPoints = shieldPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
super.onUpdate();
|
||||
|
||||
motion.norm_ip(fullMoveSpeed);
|
||||
addEffect();
|
||||
|
||||
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 1);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == scene.playerShip) {
|
||||
EntityPlayerShip sh = (EntityPlayerShip) e;
|
||||
sh.body.shieldSystem.addShieldPoints(shieldPoints);
|
||||
Sounds.powerup1.playEffect(1f, 0.4f, false);
|
||||
Effects.addOrbBurst(scene.particles, getPos(), getMotion(), 0.4, 60, 0, false, true);
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {}
|
||||
|
||||
public void addEffect() {
|
||||
Effects.addOrbBurst(scene.particles, getPos(), getMotion(), scaleRender, 4, 0, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glLoadIdentity();
|
||||
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glScaled(scaleRender, scaleRender, scaleRender);
|
||||
|
||||
model.render();
|
||||
TextureManager.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.BONUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {
|
||||
defaultOnImpact(hitBy);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
System.out.println("Bonus died.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean belongsToZone(double zFrom, double zTo) {
|
||||
return Calc.inRange(collider.pos.z, zFrom - 2 * collider.radius, zTo + 2 * collider.radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireFlammability() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShipLevel(int level) {}
|
||||
|
||||
@Override
|
||||
public void shootOnce(int gunIndex) {}
|
||||
|
||||
@Override
|
||||
public Vec getGunShotDir(int gunIndex) {
|
||||
return getMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthPercent() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGlobalMovement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
package net.sector.entities.player;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.Collider;
|
||||
import net.sector.collision.ColliderPlayerShip;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.entities.IDamageable;
|
||||
import net.sector.entities.IPhysEntity;
|
||||
import net.sector.entities.IScoreCounter;
|
||||
import net.sector.input.IInputHandler;
|
||||
import net.sector.level.GameCursor;
|
||||
import net.sector.level.ship.ShipBundle;
|
||||
import net.sector.level.ship.modules.ShipBody;
|
||||
import net.sector.level.ship.modules.pieces.Piece;
|
||||
import net.sector.sounds.Sounds;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Player ship entity
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EntityPlayerShip extends Entity implements IScoreCounter, IInputHandler {
|
||||
|
||||
public static final int MAXROT = 85;
|
||||
|
||||
|
||||
/** Ship body piece store + shield + energy system */
|
||||
public ShipBody body;
|
||||
|
||||
public GameCursor cursor;
|
||||
|
||||
/** Collider as player ship collider */
|
||||
private ColliderPlayerShip colliderBody;
|
||||
|
||||
/** Flag indicating that ship should rotate back to Z- direction gradually. */
|
||||
public boolean rotationCenterRequested = false;
|
||||
|
||||
/** Degrees player wants to add to ship rotation - added gradually. */
|
||||
public int angleInc = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Create ship bundle from current state (damaged ship body, new scores
|
||||
* etc.)
|
||||
*
|
||||
* @return new context
|
||||
*/
|
||||
public ShipBundle createNewShipBundle() {
|
||||
return new ShipBundle(body.toTable(), body.shieldSystem.level, body.energySystem.level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Player ship<br>
|
||||
*
|
||||
* @param pos center in 3D space
|
||||
* @param cursor game cursor
|
||||
*/
|
||||
public EntityPlayerShip(Coord pos, GameCursor cursor) {
|
||||
|
||||
this.cursor = cursor;
|
||||
|
||||
this.collidePriority = -1;
|
||||
|
||||
int X = cursor.shipBundle.ship[0].length, Z = cursor.shipBundle.ship.length;
|
||||
|
||||
collider = colliderBody = new ColliderPlayerShip(pos, X, Z);
|
||||
body = colliderBody.body;
|
||||
|
||||
|
||||
body.setCenterCoord(X / 2, Z / 2);
|
||||
|
||||
double massSum = 0;
|
||||
|
||||
for (int z = 0; z < Z; z++) {
|
||||
for (int x = 0; x < X; x++) {
|
||||
if (cursor.shipBundle.ship[z][x] != null) {
|
||||
Piece p;
|
||||
body.setPiece(x, z, p = cursor.shipBundle.ship[z][x].toPiece());
|
||||
massSum += p.getPieceMass();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mass = 5 * massSum;
|
||||
|
||||
body.energyLevel = cursor.shipBundle.energyLevel;
|
||||
body.shieldLevel = cursor.shipBundle.shieldLevel;
|
||||
|
||||
setHealth(100000);
|
||||
|
||||
this.motion.setTo(0, 0, 0);
|
||||
|
||||
this.lifetime = -1;
|
||||
this.MAX_SPEED = 0.3;
|
||||
this.rotDir.setTo(0, 1, 0);
|
||||
setGlobalMovement(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColliderSphere getColliderFor(Collider hitBy) {
|
||||
if (colliderBody.collidesWith(hitBy)) {
|
||||
if (colliderBody.lastCollided == null) {
|
||||
if (colliderBody.collidingShield) {
|
||||
return colliderBody;
|
||||
}
|
||||
} else {
|
||||
return colliderBody.lastCollided.get(0).pieceCollider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
colliderBody.onAddedToScene(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add damage to pieces, weakening with square of distance
|
||||
*
|
||||
* @param source source of damage
|
||||
* @param damage damage at full strength
|
||||
* @param distMultiplier distance multiplier
|
||||
* @param range max distance of destruction
|
||||
*/
|
||||
public void piecesAddDamageSquare(ColliderSphere source, double damage, double distMultiplier, double range) {
|
||||
if (body.shieldSystem.forceFieldActive) damage *= 0.3;
|
||||
for (Piece p : body.allPieces) {
|
||||
if (p.isDead) continue;
|
||||
|
||||
double dist = p.getPieceCollider().getPos().distTo(source.getPos());
|
||||
dist -= source.radius;
|
||||
dist -= p.getPieceCollider().radius;
|
||||
|
||||
if (dist < 0) dist = 0;
|
||||
|
||||
if (dist > range) continue;
|
||||
|
||||
dist *= distMultiplier;
|
||||
|
||||
if (dist < 1) dist = 1;
|
||||
|
||||
p.addDamage((damage) / (dist * dist));
|
||||
}
|
||||
body.checkIntegrity();
|
||||
|
||||
if (body.isDead) {
|
||||
setDead();
|
||||
onDeath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDamage(IDamageable source, double points) {
|
||||
if (Double.isNaN(points)) return;
|
||||
ArrayList<Piece> p = colliderBody.lastCollided;
|
||||
if (p != null) {
|
||||
for (Piece pp : p) {
|
||||
pp.addDamage(points / p.size());
|
||||
}
|
||||
|
||||
if (body.isDead) {
|
||||
setDead();
|
||||
onDeath();
|
||||
}
|
||||
|
||||
} else if (colliderBody.collidingShield) {
|
||||
if (source != this && (!source.isDead() || source.getType() == EEntity.SHOT_BAD)) {
|
||||
Sounds.shield_hit.playEffect(0.6f + rand.nextFloat() * 0.6f, 0.12f, false, getPos());
|
||||
|
||||
double energy = body.shieldSystem.shieldEnergy;
|
||||
|
||||
double neededToKill = source.getHealth();
|
||||
|
||||
double killCost = neededToKill * 100; // - body.shieldSystem.level * 10);
|
||||
|
||||
if (energy >= killCost) {
|
||||
body.shieldSystem.shieldEnergy -= killCost;
|
||||
source.addDamage(this, neededToKill);
|
||||
} else {
|
||||
double consumed = energy;
|
||||
|
||||
source.addDamage(this, neededToKill * (consumed / killCost) * 0.5);
|
||||
}
|
||||
|
||||
IPhysEntity hit = (IPhysEntity) source;
|
||||
|
||||
Vec move = getPos().vecTo(hit.getPos());
|
||||
|
||||
Coord midpoint = getPos().add(move.norm(collider.radius));
|
||||
|
||||
if (!hit.isDead()) {
|
||||
explodeForce(midpoint, 0.02, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (body.isDead) {
|
||||
setDead();
|
||||
onDeath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImpact(Entity hitBy) {}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
body.update();
|
||||
|
||||
if (angleInc != 0) {
|
||||
double inc = Math.min(1, Math.abs(angleInc)) * Constants.SPEED_MUL;
|
||||
if (Math.abs(rotAngle.d) > MAXROT) {
|
||||
rotAngle.d = Calc.clampd(rotAngle.d, -MAXROT, MAXROT);
|
||||
angleInc = 0;
|
||||
rotAngle.pushLast();
|
||||
} else {
|
||||
rotAngle.d += Calc.sgn(angleInc) * inc;
|
||||
angleInc -= Calc.sgn(angleInc) * inc;
|
||||
}
|
||||
}
|
||||
|
||||
if (rotationCenterRequested) {
|
||||
if (Math.abs(rotAngle.d) > 2) {
|
||||
rotAngle.d += Calc.sgn(0 - rotAngle.d) * 1 * Constants.SPEED_MUL;
|
||||
} else {
|
||||
rotAngle.d = 0;
|
||||
rotationCenterRequested = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
body.render(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean belongsToZone(double zFrom, double zTo) {
|
||||
return Calc.inRange(collider.pos.z, zFrom - collider.radius, zTo + collider.radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
explodeForce(getPos(), 20, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EEntity getType() {
|
||||
return EEntity.PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScore(int points) {
|
||||
cursor.addScore(points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseMove(Coord pos, Vec move, int wheelDelta) {
|
||||
body.onMouseMove(pos, move, wheelDelta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseButton(int button, boolean down, int wheelDelta, Coord pos, Coord deltaPos) {
|
||||
body.onMouseButton(button, down, wheelDelta, pos, deltaPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKey(int key, char c, boolean down) {
|
||||
body.onKey(key, c, down);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStaticInputs() {
|
||||
body.handleStaticInputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get acceleration for GUI, based on number of engines and their level.
|
||||
*
|
||||
* @return acceleration
|
||||
*/
|
||||
public double getAcceleration() {
|
||||
return 0.05 * body.countEnginesSq() / (mass / 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deceleration for GUI, based on number of engines and their level.
|
||||
*
|
||||
* @return deceleration
|
||||
*/
|
||||
public double getDecelerate() {
|
||||
return 0.015 * (1 + body.countEnginesSq() / (mass / 16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEmpSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireFlammability() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFireSensitivity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealthMax() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
import net.sector.util.RenderUtils;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
public class EntityEMP extends EntityShotBase {
|
||||
|
||||
protected RGB color = new RGB(0.75, 0.5, 1);
|
||||
|
||||
private static final double SPEED = 0.3;
|
||||
|
||||
protected static int renderlist = -1;
|
||||
|
||||
private double scale = 0.25;
|
||||
|
||||
private Entity target;
|
||||
|
||||
private Vec origDirection = null;
|
||||
|
||||
private int level;
|
||||
|
||||
static {
|
||||
renderlist = glGenLists(1);
|
||||
|
||||
glNewList(renderlist, GL_COMPILE);
|
||||
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
//glDisable(GL_FOG);
|
||||
glDepthMask(false);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
TextureManager.bind("particles_blend");
|
||||
|
||||
Coord texCoord = new Coord(0, 1);
|
||||
|
||||
double left = (texCoord.x) * 0.125;
|
||||
double top = (texCoord.y) * 0.125;
|
||||
double right = (texCoord.x + 1) * 0.125;
|
||||
double bottom = (texCoord.y + 1) * 0.125;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
double sh = 1;
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(-sh, +sh, 0);
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(+sh, +sh, 0);
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(+sh, -sh, 0);
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(-sh, -sh, 0);
|
||||
glEnd();
|
||||
|
||||
TextureManager.unbind();
|
||||
|
||||
|
||||
glDepthMask(true);
|
||||
|
||||
glPopAttrib();
|
||||
|
||||
glEndList();
|
||||
|
||||
}
|
||||
|
||||
public EntityEMP(Coord pos, Vec speed, Entity origin, int level) {
|
||||
super(pos, speed, origin, SPEED);
|
||||
|
||||
this.shotDamage = 0;
|
||||
this.collidePriority = 1004;
|
||||
this.mass = 0.002;
|
||||
this.scoreValue = 0;
|
||||
this.health = 0.17 * level * level;
|
||||
this.lifetime = Constants.FPS_UPDATE * 5;
|
||||
this.scale = Calc.clampd(0.06 * level, 0.1, 0.4);
|
||||
this.collider = new ColliderSphere(pos, scale);
|
||||
|
||||
origDirection = speed.copy();
|
||||
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
private static long lastSoundTime = 0;
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
if (System.currentTimeMillis() - lastSoundTime > 50) {
|
||||
Sounds.shot_emp.playEffect(1f, 1.5f, false, getPos().setY(Constants.LISTENER_POS.y - 3));
|
||||
lastSoundTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public EntityEMP setColor(RGB clr) {
|
||||
color = clr;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
|
||||
motion.add_ip(origDirection.norm(0.05));
|
||||
motion.norm_ip(shotSpeed);
|
||||
|
||||
// magnetic
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 6);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e.getType() == EEntity.ENEMY) {
|
||||
double dist = e.getPos().distTo(getPos()) - e.getRadius();
|
||||
if (dist < 0) dist = 0.00001;
|
||||
double move = 0.4 / dist;
|
||||
move = Calc.clampd(move, 0, 0.2);
|
||||
motion.add_ip(((Vec) getPos().vecTo(e.getPos()).setY(0)).norm(move));
|
||||
motion.norm_ip(shotSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
motion.norm_ip(shotSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glScaled(scale, scale, scale);
|
||||
|
||||
RenderUtils.setColor(color);
|
||||
glCallList(renderlist);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath() {
|
||||
onHitTarget(null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onHitTarget(Entity target) {
|
||||
Effects.addEMPExplosion(scene.particles, getPos(), getMotion(), Calc.clampi(4 * level, 4, 12), hasGlobalMovement(), true);
|
||||
|
||||
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 3 + 2 * level);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e.getType() == EEntity.ENEMY) {
|
||||
e.addEmp(level * 250);
|
||||
|
||||
Effects.addEMPExplosion(scene.particles, e.getPos(), e.getMotion(), Calc.clampi(4 * level, 4, 12), e.hasGlobalMovement(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.sounds.Sounds;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.PolarDeg;
|
||||
|
||||
|
||||
public class EntityFireball extends EntityShotBase {
|
||||
|
||||
private static final double SPEED = 0.35;
|
||||
|
||||
private int level = 1;
|
||||
|
||||
public EntityFireball(Coord pos, Vec speed, Entity origin, int techLevel) {
|
||||
super(pos, speed, origin, SPEED);
|
||||
this.collidePriority = 1001;
|
||||
this.mass = 0.3;
|
||||
this.lifetime = Constants.FPS_UPDATE * 7;
|
||||
this.collider = new ColliderSphere(pos, 0.30);
|
||||
this.health = 0.4 * techLevel * techLevel;
|
||||
|
||||
this.shotDamage = 0.1 * Math.pow(techLevel, 1.2);
|
||||
this.level = techLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
PolarDeg polar = PolarDeg.fromCoordXZ(motion);
|
||||
polar.angle += -0.2 + rand.nextDouble() * 0.4;
|
||||
motion.setTo(polar.toCoordXZ());
|
||||
motion.norm_ip(shotSpeed);
|
||||
addEffect();
|
||||
}
|
||||
|
||||
private static long lastSoundTime = 0;
|
||||
private static long lastExplTime = 0;
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
if (System.currentTimeMillis() - lastSoundTime > 100) {
|
||||
Sounds.shot_fireball.playEffect(1f, 0.2f, false, getPos().setY(Constants.LISTENER_POS.y - 2));
|
||||
lastSoundTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public void addEffect() {
|
||||
if (rand.nextInt(6) == 0) {//+0.03*(level)
|
||||
Effects.addExplosion(scene.particles, getPos(), getMotion(), 0.02 + 0.03 * (level), false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {}
|
||||
|
||||
@Override
|
||||
public void onHitTarget(Entity target) {
|
||||
if (System.currentTimeMillis() - lastExplTime > 150) {
|
||||
Effects.addExplosion(scene.particles, collider.pos, target.getMotion(), 6 * level, true, target.hasGlobalMovement());
|
||||
lastExplTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 0.8 * level);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e != target) e.addDamage(this, shotDamage);
|
||||
e.addFire(origin, level * Constants.FPS_UPDATE * 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
import com.porcupine.math.Calc.Rad;
|
||||
import com.porcupine.math.Polar;
|
||||
|
||||
|
||||
public class EntityLaser extends EntityShotBase {
|
||||
|
||||
private static final double SPEED = 0.5;
|
||||
|
||||
private static int renderlist = -1;
|
||||
private double scale = 0.1;
|
||||
private RGB color = new RGB(0, 1, 0);
|
||||
|
||||
static {
|
||||
renderlist = glGenLists(1);
|
||||
|
||||
glNewList(renderlist, GL_COMPILE);
|
||||
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
//glDisable(GL_FOG);
|
||||
glDepthMask(false);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
TextureManager.bind("particles_blend");
|
||||
|
||||
Coord texCoord = new Coord(3, 1);
|
||||
|
||||
double left = (texCoord.x) * 0.125;
|
||||
double top = (texCoord.y) * 0.125;
|
||||
double right = (texCoord.x + 1) * 0.125;
|
||||
double bottom = (texCoord.y + 1) * 0.125;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
double sh = 0.05;
|
||||
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(-sh, 0, -1);
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(+sh, 0, -1);
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(+sh, 0, 0);
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(-sh, 0, 0);
|
||||
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(0, -sh, -0.9);
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(0, +sh, -0.9);
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(0, +sh, -0.1);
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(0, -sh, -0.1);
|
||||
glEnd();
|
||||
|
||||
TextureManager.unbind();
|
||||
|
||||
glDepthMask(true);
|
||||
glPopAttrib();
|
||||
|
||||
glEndList();
|
||||
|
||||
}
|
||||
|
||||
public EntityLaser(Coord pos, Vec speed, Entity origin) {
|
||||
super(pos, speed, origin, SPEED);
|
||||
this.shotDamage = 0.8;
|
||||
this.collidePriority = 1000;
|
||||
this.mass = 0.01;
|
||||
this.lifetime = Constants.FPS_UPDATE * 6;
|
||||
this.collider = new ColliderSphere(pos, scale);
|
||||
this.health = 0.005;
|
||||
}
|
||||
|
||||
public EntityLaser(Coord pos, Vec speed, Entity origin, RGB color) {
|
||||
this(pos, speed, origin);
|
||||
this.color.setTo(color);
|
||||
}
|
||||
|
||||
public EntityLaser(Coord pos, Vec speed, Entity origin, RGB color, int level) {
|
||||
this(pos, speed, origin, color);
|
||||
this.shotDamage = 0.8 * level;
|
||||
this.shotDamage = Calc.clampd(this.shotDamage, 1.3);
|
||||
this.health = 0.1 * level * level;
|
||||
}
|
||||
|
||||
private static long lastSoundTime = 0;
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
if (System.currentTimeMillis() - lastSoundTime > 50) {
|
||||
Sounds.shot_laser.playEffect(1f, 0.4f, false, getPos().setY(Constants.LISTENER_POS.y - 3));
|
||||
lastSoundTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Entity setDamage(double dmg) {
|
||||
this.shotDamage = dmg;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
//collider.pos.add_ip(motion);
|
||||
|
||||
motion.norm_ip(shotSpeed);
|
||||
|
||||
Polar p = Polar.fromCoord(motion.x, motion.z);
|
||||
rotAngle.set(-90 + Rad.toDeg(p.angle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glColor4d(color.r, color.g, color.b, 1);
|
||||
|
||||
glCallList(renderlist);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
|
||||
import com.porcupine.color.RGB;
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
import com.porcupine.math.Calc.Rad;
|
||||
import com.porcupine.math.Polar;
|
||||
|
||||
|
||||
public class EntityLaser2 extends EntityShotBase {
|
||||
|
||||
private static final double SPEED = 0.3;
|
||||
|
||||
private static int renderlist = -1;
|
||||
private double scale = 0.1;
|
||||
private static RGB defColor = new RGB(1, 0.6, 0);
|
||||
private RGB color = defColor;
|
||||
|
||||
static {
|
||||
renderlist = glGenLists(1);
|
||||
|
||||
glNewList(renderlist, GL_COMPILE);
|
||||
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDepthMask(false);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
TextureManager.bind("particles_blend");
|
||||
|
||||
Coord texCoord = new Coord(4, 2);
|
||||
|
||||
double left = (texCoord.x) * 0.125;
|
||||
double top = (texCoord.y) * 0.125;
|
||||
double right = (texCoord.x + 1) * 0.125;
|
||||
double bottom = (texCoord.y + 1) * 0.125;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
double sh = 0.15;
|
||||
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(-sh, 0, -1);
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(+sh, 0, -1);
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(+sh, 0, 0);
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(-sh, 0, 0);
|
||||
|
||||
sh = 0.07;
|
||||
glTexCoord2d(left, top);
|
||||
glVertex3d(0, -sh, -0.8);
|
||||
glTexCoord2d(right, top);
|
||||
glVertex3d(0, +sh, -0.8);
|
||||
glTexCoord2d(right, bottom);
|
||||
glVertex3d(0, +sh, -0.2);
|
||||
glTexCoord2d(left, bottom);
|
||||
glVertex3d(0, -sh, -0.2);
|
||||
glEnd();
|
||||
|
||||
TextureManager.unbind();
|
||||
|
||||
glDepthMask(true);
|
||||
glPopAttrib();
|
||||
|
||||
glEndList();
|
||||
|
||||
}
|
||||
|
||||
public EntityLaser2(Coord pos, Vec speed, Entity origin) {
|
||||
super(pos, speed, origin, SPEED);
|
||||
this.shotDamage = 2;
|
||||
this.collidePriority = 1000;
|
||||
this.mass = 0.01;
|
||||
this.lifetime = Constants.FPS_UPDATE * 6;
|
||||
this.collider = new ColliderSphere(pos, scale);
|
||||
this.health = 0.005;
|
||||
}
|
||||
|
||||
public EntityLaser2(Coord pos, Vec speed, Entity origin, RGB color) {
|
||||
this(pos, speed, origin);
|
||||
this.color.setTo(color);
|
||||
}
|
||||
|
||||
public EntityLaser2(Coord pos, Vec speed, Entity origin, RGB color, int level) {
|
||||
this(pos, speed, origin, color);
|
||||
this.shotDamage = 2 * level;
|
||||
this.shotDamage = Calc.clampd(this.shotDamage, 1.5);
|
||||
this.health = 0.1 * level * level;
|
||||
}
|
||||
|
||||
public EntityLaser2(Coord pos, Vec speed, Entity origin, int level) {
|
||||
this(pos, speed, origin, defColor, level);
|
||||
}
|
||||
|
||||
private static long lastSoundTime = 0;
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
if (System.currentTimeMillis() - lastSoundTime > 50) {
|
||||
Sounds.shot_laser_acid.playEffect(1f, 0.4f, false, getPos().setY(Constants.LISTENER_POS.y - 3));
|
||||
lastSoundTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Entity setDamage(double dmg) {
|
||||
this.shotDamage = dmg;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
//collider.pos.add_ip(motion);
|
||||
|
||||
motion.norm_ip(shotSpeed);
|
||||
|
||||
Polar p = Polar.fromCoord(motion.x, motion.z);
|
||||
rotAngle.set(-90 + Rad.toDeg(p.angle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glColor4d(color.r, color.g, color.b, 1);
|
||||
|
||||
glCallList(renderlist);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.Constants;
|
||||
import net.sector.collision.ColliderSphere;
|
||||
import net.sector.effects.Effects;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.models.PhysModel;
|
||||
import net.sector.sounds.Sounds;
|
||||
import net.sector.textures.TextureManager;
|
||||
import net.sector.util.DeltaDoubleDeg;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
public class EntityMissileDirect extends EntityShotBase {
|
||||
|
||||
private static final double SPEED = 0.4;
|
||||
|
||||
private double scale = 0.25;
|
||||
protected DeltaDoubleDeg rot = new DeltaDoubleDeg(0);
|
||||
protected double rotSpeed = 2;
|
||||
|
||||
protected static PhysModel model = Models.rocketThin;
|
||||
protected double scaleRender = model.renderScale;
|
||||
|
||||
private int level;
|
||||
|
||||
|
||||
public EntityMissileDirect(Coord pos, Vec speed, Entity origin, int techLevel) {
|
||||
super(pos, speed, origin, SPEED);
|
||||
this.collidePriority = 1001;
|
||||
this.mass = 0.3;
|
||||
this.lifetime = Constants.FPS_UPDATE * 7;
|
||||
this.collider = new ColliderSphere(pos, scale);
|
||||
this.health = 0.4 * techLevel * techLevel;
|
||||
|
||||
this.shotDamage = 4 * Math.pow(techLevel, 1.5);
|
||||
this.level = techLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
motion.norm_ip(shotSpeed);
|
||||
rot.pushLast();
|
||||
rot.add(rotSpeed);
|
||||
//collider.pos.add_ip(motion);
|
||||
addEffect();
|
||||
|
||||
}
|
||||
|
||||
private static long lastSoundTime = 0;
|
||||
|
||||
@Override
|
||||
public void onAddedToScene() {
|
||||
if (System.currentTimeMillis() - lastSoundTime > 50) {
|
||||
Sounds.rocket.playEffect(1f, 0.4f, false, getPos().setY(Constants.LISTENER_POS.y - 2));
|
||||
lastSoundTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public void addEffect() {
|
||||
Coord firePos = getPos().add(getMotion().neg().norm(collider.radius));
|
||||
motion.norm_ip(shotSpeed);
|
||||
|
||||
Effects.addEngineFire(scene.particles, firePos, getMotion(), 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(double delta) {
|
||||
glLoadIdentity();
|
||||
|
||||
Coord p = getPos().getDelta(delta);
|
||||
glTranslated(p.x, p.y, -p.z);
|
||||
glRotated(rotAngle.delta(delta), rotDir.x, rotDir.y, rotDir.z);
|
||||
glRotated(rot.delta(delta), 0, 0, 1);
|
||||
glScaled(scaleRender, scaleRender, scaleRender);
|
||||
|
||||
model.render();
|
||||
TextureManager.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHitTarget(Entity target) {
|
||||
Effects.addExplosion(scene.particles, collider.pos, target.getMotion(), 10 * level, true, target.hasGlobalMovement());
|
||||
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), 3);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e != target) {
|
||||
e.addDamage(this, shotDamage);
|
||||
e.getMotion().add_ip(motion.scale(Calc.clampd(mass, 0, 1)));
|
||||
if (e.isDead() && scoreCounter != null) scoreCounter.addScore(e.scoreValue);
|
||||
}
|
||||
|
||||
e.addFire(origin, level * Constants.FPS_UPDATE * 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.sector.entities.shots;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.sector.entities.EEntity;
|
||||
import net.sector.entities.Entity;
|
||||
import net.sector.models.Models;
|
||||
import net.sector.util.Utils;
|
||||
|
||||
import com.porcupine.coord.Coord;
|
||||
import com.porcupine.coord.Vec;
|
||||
import com.porcupine.math.Calc;
|
||||
|
||||
|
||||
public class EntityMissileGuided extends EntityMissileDirect {
|
||||
|
||||
|
||||
public EntityMissileGuided(Coord pos, Vec speed, Entity origin, int techLevel) {
|
||||
super(pos, speed, origin, techLevel);
|
||||
this.model = Models.rocketFat;
|
||||
this.scaleRender = model.renderScale;
|
||||
this.shotDamage = 6 * Math.pow(techLevel, 1.5);
|
||||
this.health = 0.4 * techLevel * techLevel;
|
||||
}
|
||||
|
||||
private Entity target = null;
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
rot.pushLast();
|
||||
rot.add(rotSpeed);
|
||||
|
||||
if (target == null || target.isDead()) {
|
||||
Set<Entity> entities = scene.getEntitiesInRange(getPos(), 60);
|
||||
double mini = 180;
|
||||
double mind = 1000;
|
||||
for (Entity entity : entities) {
|
||||
if (entity.getType() != EEntity.ENEMY) continue;
|
||||
if (entity == this) continue;
|
||||
double i = 0;
|
||||
double d = entity.getPos().distTo(getPos());
|
||||
i = Utils.observerAngleToCoord(getPos(), entity.getPos(), motion);
|
||||
if (i < 70 && i < mini && d < mind) {
|
||||
target = entity;
|
||||
mini = i;
|
||||
mind = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
Vec direction = collider.pos.vecTo(target.getPos());
|
||||
direction.norm_ip(shotSpeed);
|
||||
double dist = target.getPos().distTo(getPos()) - target.getRadius();
|
||||
if (dist < 0) dist = 0.00001;
|
||||
double spd = 0.2 / dist;
|
||||
if (spd < 0.05) spd = 0.05;
|
||||
motion.add_ip(direction.norm(spd));
|
||||
motion.norm_ip(shotSpeed);
|
||||
|
||||
// //getPos().add_ip(motion);
|
||||
// Vec toTg = getPos().vecTo(target.getPos()).norm(0.06);
|
||||
// motion.add_ip(toTg);
|
||||
}
|
||||
|
||||
// avoid obstacles
|
||||
Set<Entity> ents = scene.getEntitiesInRange(getPos(), collider.radius + 3);
|
||||
if (!ents.isEmpty()) {
|
||||
for (Entity e : ents) {
|
||||
if (e == this) continue;
|
||||
if (e.getType() == EEntity.NATURAL) {
|
||||
double dist = e.getPos().distTo(getPos()) - e.getRadius();
|
||||
if (dist < 0) dist = 0.00001;
|
||||
double move = 0.05 / dist;
|
||||
move = Calc.clampd(move, 0.001, 0.2);
|
||||
motion.add_ip(((Vec) e.getPos().vecTo(getPos()).setY(0)).norm(move));
|
||||
motion.norm_ip(shotSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
motion.norm_ip(shotSpeed);
|
||||
addEffect();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user