Added old sources; initial commit.

This commit is contained in:
Ondřej Hruška
2014-05-24 20:28:49 +02:00
parent 7f50a261eb
commit 3d7bda9331
661 changed files with 114251 additions and 0 deletions
+151
View File
@@ -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);
}
}
+334
View File
@@ -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();
}
}
+684
View File
@@ -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);
}
}
+159
View File
@@ -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);
}
}
+291
View File
@@ -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));
}
}
+279
View File
@@ -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();
}
+75
View File
@@ -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;
}
}
+261
View File
@@ -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);
}
}
}
+144
View File
@@ -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;
}
}
+143
View File
@@ -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;
}
}
+57
View File
@@ -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;
}
+41
View File
@@ -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();
}
+208
View File
@@ -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;
}
}
+811
View File
@@ -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;
}
}
+91
View File
@@ -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);
}
}
+95
View File
@@ -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);
}
}
+174
View File
@@ -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();
}
+28
View File
@@ -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;
}
}
+28
View File
@@ -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;
}
}
+29
View File
@@ -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;
}
}
+28
View File
@@ -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;
}
}
+28
View File
@@ -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 "";
}
}
+132
View File
@@ -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 + "}";
}
}
+172
View File
@@ -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 + "}";
}
}
+213
View File
@@ -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 + "}";
}
}
+256
View File
@@ -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 + "}";
}
}
+295
View File
@@ -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 + "}";
}
}
+336
View File
@@ -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 + "}";
}
}
+377
View File
@@ -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 + "}";
}
}
+54
View File
@@ -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;
}
}
+130
View File
@@ -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;
}
}
+182
View File
@@ -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;
}
}
+997
View File
@@ -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;
}
}
}
+104
View File
@@ -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;
}
}
+51
View File
@@ -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 = { &quot;one&quot;, 1, &quot;two&quot;, 4, &quot;three&quot;, 9, &quot;four&quot;, 16 };
* Map&lt;String, Integer&gt; args = new VarargsParser&lt;String, Integer&gt;().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;
}
}
+556
View File
@@ -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;
}
}
+92
View File
@@ -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;
}
+41
View File
@@ -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);
}
}
+127
View File
@@ -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);
}
}
+130
View File
@@ -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();
}
}
+33
View File
@@ -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;
}
}
+123
View File
@@ -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();
}
}
+13
View File
@@ -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 {
}
+12
View File
@@ -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 {
}
+29
View File
@@ -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;
}
}
+393
View File
@@ -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;
}
}
+24
View File
@@ -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;
}
+273
View File
@@ -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));
}
}
}
+132
View File
@@ -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;
}
}
+26
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
package net.sector.entities;
public enum EFormation {
SNAKE, SWARM, SHAPE, NONE
}
+664
View File
@@ -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();
}
}
+14
View File
@@ -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();
}
+55
View File
@@ -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