initial commit

This commit is contained in:
MitchWeaver
2016-07-26 18:37:18 -05:00
parent 084779ae15
commit a1bfbee043
247 changed files with 5756 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
package sjgs.__example_games;
import static sjgs.graphics.Colors.blue;
import static sjgs.graphics.Colors.orange;
import static sjgs.graphics.Colors.red;
import static sjgs.graphics.Colors.white;
import static sjgs.utils.Utils.rand;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import sjgs.core.Engine;
import sjgs.core.input.Mouse;
import sjgs.graphics.ui.InventorySystem;
import sjgs.graphics.ui.InventorySystemSlot;
/** Welcome to a demonstration of the @InventorySystem class in Mitch's Game Engine! */
class __InventoryExample extends Engine {
public __InventoryExample(final int WIDTH, final int HEIGHT, final String title) {
super(WIDTH, HEIGHT, title);
}
// Create our engine!
private static __InventoryExample engine;
// Create our inventory!
private static ExampleInventory inventory;
// Create the window for our engine, and initialize our engine itself while we're here.
public static void main(final String[] args) {
engine = new __InventoryExample(800, 600, "Inventory System Demonstration");
}
// Create our mouse input, which will also add itself to our engine that we're passing in.
@Override
protected void init() {
new MouseInput(this);
// Then we initialize our inventory
inventory = new ExampleInventory(getWidth() * 0.1f, getHeight() * 0.1f, getWidth() * 0.8f, getHeight() * 0.8f);
}
// In our main tick method, we make sure to tick our new inventory
@Override
protected void tick() { inventory.tick(); }
// Like wise in our main render method
@Override
protected void render(final Graphics2D g2d) { inventory.render(g2d); }
// Lets create an interface full of itemId's for our inventory --- Note that '0' is reserved for empty slots
private static interface items { static final int CIRCLE = 1, SQUARE = 2, TRIANGLE = 3; }
// INVENTORY SLOT CLASS!
private static class ExampleInventorySlot extends InventorySystemSlot implements items {
public ExampleInventorySlot(final float x, final float y, final float w, final float h, final int slotNumber) { super(x, y, w, h, slotNumber); }
@Override
public void init() {}
// Here we set the center of the slot to our mouse if we have clicked on it
@Override
public void tick() {
if(mouseClicked) bounds.setLocation(Mouse.getX() - bounds.getHalfWidth(), Mouse.getY() - bounds.getHalfHeight());
}
// Render our slots!
@Override
public void render(final Graphics2D g2d) {
switch(itemId) {
case EMPTY: break;
case CIRCLE: g2d.setColor(orange); g2d.fillOval((int)bounds.getX(), (int)bounds.getY(), (int)bounds.getWidth(), (int)bounds.getHeight()); break;
case SQUARE: g2d.setColor(red); bounds.drawFilled(g2d); break;
case TRIANGLE: g2d.setColor(blue); bounds.toTriangle().draw(g2d); break;
}
// Here I draw the starting locations of the slots for clarity during the demonstration
g2d.setColor(Color.white);
g2d.drawRect((int)getStartingX(), (int)getStartingY(), (int)getWidth(), (int)getHeight());
// Here is where you would draw quantities, but for this demonstration all quantities are zero
if(quantity > 1)
g2d.drawString(""+quantity, getX(), getY() + getHalfHeight());
}
// Python methods in the superclass require this "baseReset".
// More advanced inventory systems may require you to write your own reset methods however.
@Override
public void reset() { baseReset(); }
}
// INVENTORY CLASS!
private static class ExampleInventory extends InventorySystem implements items {
public ExampleInventory(final float x, final float y, final float width, final float height) { super(x, y, width, height); }
// Here we create all our inventory slots!
@Override
public void init() {
final int w = 50, h = 50;
for(int i = 1; i < 6; i++) slots.add(new ExampleInventorySlot(engine.getWidth() * 0.3f, engine.getHeight()*0.1f + i*h, w, h, i - 1));
for(int i = 1; i < 6; i++) slots.add(new ExampleInventorySlot(engine.getWidth() * 0.6f, engine.getHeight()*0.1f + i*h, w, h, i - 1));
// And fill them with random items
for(int i = 0; i < 10; i++) addItem(rand.nextInt(4));
}
// Tick all our slots
@Override
public void tick() { for(final InventorySystemSlot i : slots) i.tick(); }
// Render all our slots, and draw the main inventory's bounds
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(white);
getBounds().draw(g2d);
for(final InventorySystemSlot i : slots) i.render(g2d);
/* pro tip: running through again here like so will put the held item always above the others */
for(final InventorySystemSlot i : slots) if(i.mouseClicked) { i.render(g2d); break; }
}
@Override
public void destroy() {}
// Here you can write whatever logic you want to happen on clicks, however for now we use the base, standard click method.
@Override
public void onLeftClick() { baseOnLeftClick(); }
@Override
public boolean isStackable(final int itemId) { return false; }
@Override
public int getStackableAmount(final int itemId) { return 0; }
@Override
public void swapSlots(final InventorySystemSlot i, final InventorySystemSlot e) {
baseSwapSlots(i, e);
}
}
// Basic mouse input class so we have input
private static class MouseInput extends Mouse {
public MouseInput(final Engine engine) { super(engine); }
@Override
public void mouseMoved(final MouseEvent e) {
setLocation(e.getX(), e.getY());
}
@Override
public void mouseClicked(final MouseEvent e) {
setClickLocation(e.getX(), e.getY());
if(e.getButton() == 1) inventory.onLeftClick();
}
@Override
public void mouseWheelMoved(final MouseWheelEvent arg0) {}
@Override
public void mousePressed(final MouseEvent e) {}
@Override
public void mouseReleased(final MouseEvent e) {}
}
// And that's it! Very easy, simple implementation of an inventory class :)
}

View File

@@ -0,0 +1,151 @@
package sjgs.__example_games;
import static sjgs.core.input.Keyboard.A;
import static sjgs.core.input.Keyboard.D;
import static sjgs.core.input.Keyboard.Q;
import static sjgs.core.input.Keyboard.SPACE;
import static sjgs.graphics.Colors.blue;
import static sjgs.graphics.Colors.red;
import java.awt.Color;
import java.awt.Graphics2D;
import sjgs.base_objects.BaseTile;
import sjgs.base_objects.PlayerBase;
import sjgs.core.Camera;
import sjgs.core.Engine;
import sjgs.core.Handler;
import sjgs.core.input.Keyboard;
import sjgs.enums.Facing;
import sjgs.graphics.lighting.Light;
import sjgs.graphics.lighting.LightsRenderer;
import sjgs.graphics.lighting.RadialLight;
/** Weclome to the @Light demonstration from Mitch's Game Engine! */
class __LightingDemonstration extends Engine {
public __LightingDemonstration(final int WIDTH, final int HEIGHT, final String title) {
super(WIDTH, HEIGHT, title);
}
// as normal we create our engine, camera, and players
public static __LightingDemonstration engine;
public static Camera camera;
public static Player player;
// create our window, initializing our engine...
public static void main(final String[] args) {
engine = new __LightingDemonstration(1280, 720, "Lighting Demonstration");
}
// initialize our player and camera, and here we create some lights, (see later)
@Override
protected void init() {
player = new Player(getWidth()/2, getHeight()/2, 32, 64);
camera = new Camera(this);
for(int i = 0; i < 100; i++) {
if(i % 10 == 0) new StaticLight(i*32, 575, 30, 1);
new Block(i*32, 600, 32, 32);
}
}
// again, as usual we need to tick our camera, keyinput, and handler
// ... but here we're also going to tick all our lights!
@Override
protected void tick() {
camera.tick(player.getCenter(), getScaleFactor());
KeyInput.tick(player);
Handler.tick(camera, getScaleFactor());
for(final Light i : Handler.lights) i.tick();
}
// Render the handler, plus a nice white background. We also need to renderer the light manager
@Override
protected void render(final Graphics2D g2d) {
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
Handler.render(g2d, camera, getScaleFactor());
LightsRenderer.render(g2d, camera, engine);
}
// nice little light class, implementing its own tick method to move themselves
private static class StaticLight extends RadialLight {
public StaticLight(final float x, final float y, final float radius, final float intensity) {
super(x, y, radius, intensity);
}
@Override
public void tick() { setX(getX() + 1); }
}
// basic player class
private static class Player extends PlayerBase {
// ... but this time with its own light!
private final RadialLight light;
public Player(final float x, final float y, final float w, final float h) {
super(x, y, w, h);
light = new RadialLight(getCenterX(), getCenterY(), getWidth() * 3, 1);
}
@Override
protected void init() { }
@Override
public void tick() {
applyPhysics().discard();
// make sure to set your light to the new location each game advancement!
light.setLocation(getCenterX(), getCenterY());
manageJumping();
}
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(blue);
getFullBounds().drawFilled(g2d);
}
@Override
protected void destroy() {}
}
// basic blocks so we don't fall through the floor
private static class Block extends BaseTile {
public Block(final int x, final int y, final int w, final int h) {
super(x, y, w, h, 0, 0);
}
@Override
public void init() {
dontApplyGravity();
}
@Override
public void tick() { applyPhysics().discard(); }
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(red);
getBounds().draw(g2d);
}
@Override
public void destroy() {}
}
// basic key input class
private static class KeyInput implements Keyboard {
final static float walkingVelocity = 3f;
static void tick(final PlayerBase player) {
if(D()) { player.setVelX(walkingVelocity); player.setFacing(Facing.RIGHT); }
else if(A()) { player.setVelX(-walkingVelocity); player.setFacing(Facing.LEFT); }
if(SPACE()) if(!player.getJumping()) { player.setJumping(true); player.setVelY(-15f); }
if(Q()) engine.exit();
}
}
// AND THAT IS IT! Lighting can be such a complicated and frustrating subject,
// However with Mitch's Game Engine I strived to make the process as simple as possible
// Now, all you do is remember to call your light manager, set the lights to their new positions,
// and you're done! Have fun!
}

View File

@@ -0,0 +1,279 @@
package sjgs.__example_games;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.Serializable;
import sjgs.base_objects.BaseTile;
import sjgs.base_objects.Bullet;
import sjgs.base_objects.HardObject;
import sjgs.base_objects.PlayerBase;
import sjgs.core.DeveloperConsole;
import sjgs.core.Engine;
import sjgs.core.Handler;
import sjgs.core.input.Keyboard;
import sjgs.core.input.Mouse;
import sjgs.enums.Facing;
import sjgs.enums.Type;
import sjgs.graphics.Colors;
import sjgs.utils.Utils;
import sjgs.utils.io.SaveFile;
import sjgs.utils.tools.Timer;
//---------------------------------------------------------------------------------------------------//
class __PhysicsDemonstration extends Engine {
public __PhysicsDemonstration(final int WIDTH, final int HEIGHT, final String title) { super(WIDTH, HEIGHT, title); }
public static void main(final String[] args) {
engine = new __PhysicsDemonstration(1280, 720, "Physics Demonstration");
}
public static __PhysicsDemonstration engine;
public static ExamplePlayer player;
@Override
protected void init() {
// disableFpsCap();
setDoubleTickRate();
new ExampleDevConsole(this);
new ExampleMouseInput(this);
generateWorld();
}
private void generateWorld() {
clearHandler();
final String[] map = {
"t............................................t",
"t............................................t",
"t............................................t",
"t........................................rtttt",
"ttttttttttl.............................rttttt",
"tttttttttttl....p......................rtttttt",
"ttttttttttttl.........................rttttttt",
"tttttttttttttttttttttttttttttttttttttttttttttt",
};
final int level_width = map[0].length();
final int TILE_SIZE = 40;
for(int row = 1; row < map.length+1; row++)
for(int col = 1; col < level_width+1; col++)
switch(map[row-1].charAt(col-1)) {
case 't': new ExampleTile(TILE_SIZE*col, TILE_SIZE*row, TILE_SIZE, TILE_SIZE, 0, 0); break;
case 'l': new ExampleTile(TILE_SIZE*col, TILE_SIZE*row, TILE_SIZE, TILE_SIZE, 100, 0); break;
case 'r': new ExampleTile(TILE_SIZE*col, TILE_SIZE*row, TILE_SIZE, TILE_SIZE, 0, 100 ); break;
case 'p': player = new ExamplePlayer(TILE_SIZE*col, TILE_SIZE*row, TILE_SIZE, TILE_SIZE*1.5f); break;
}
}
@Override
protected void tick() {
try {
if(DeveloperConsole.CONSOLE_OPEN) return;
ExampleKeyInput.tick(player);
camera.tick(player.getCenter(), getScaleFactor());
Handler.tick(camera, getScaleFactor());
} catch (final NullPointerException npe) { }
}
@Override
protected void render(Graphics2D g2d) {
Handler.render(g2d, camera, getScaleFactor());
g2d = createG2D();
g2d.setColor(Colors.white);
final float x = getWidth()*0.025f;
final float y = getHeight();
g2d.drawString("Controls: ", x, y*0.85f);
g2d.drawString("----------------------------", x, y*0.861f);
g2d.drawString("Reset World = 'R'", x, y*0.875f);
g2d.drawString("Shoot = 'F' or left click", x, y*0.895f);
g2d.drawString("Spawn Tiles = 'E'", x, y*0.915f);
g2d.drawString("Jump = 'Space'", x, y*0.935f);
g2d.drawString("Zoom = 'Scroll Wheel'", x, y*0.955f);
g2d.drawString("Console = '~'", x, y*0.9775f);
}
private static class ExamplePlayer extends PlayerBase {
public ExamplePlayer(final float x, final float y, final float WIDTH, final float HEIGHT) { super(x, y, WIDTH, HEIGHT); }
@Override
public void init() { }
@Override
public void tick() {
applyPhysics().discard();
manageJumping();
}
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(Colors.blue);
getFullBounds().drawFilled(g2d);
g2d.setColor(Colors.red);
getBounds().draw(g2d);
}
@Override
public void destroy() { removeFromHandler(); }
}
private static class ExampleTile extends BaseTile {
public ExampleTile(final int x, final int y, final int width, final int height, final int startingHeight, final int endingHeight) {
super(x, y, width, height, startingHeight, endingHeight);
}
@Override
public void init() { }
@Override
public void tick() { }
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(Colors.green);
drawTestBoundaries(g2d);
}
@Override
public void destroy() { Handler.stationary_hard_objects.remove(this); }
}
private static class ExampleObject extends HardObject {
private final Timer timer;
private final Color color;
public ExampleObject(final float x, final float y, final float width, final float height, final boolean mobile) {
super(x, y, width, height, mobile);
color = new Color(Utils.nextInt(256), Utils.nextInt(256), Utils.nextInt(256));
timer = new Timer(500);
}
@Override
public void init() {
getBounds().setVelX(Utils.nextFloat() * Utils.nextInt(12) + 1);
getBounds().setVelY(Utils.nextFloat() * Utils.nextInt(15) + 1);
if(Utils.COIN_FLIP()) getBounds().invertVelX();
if(Utils.COIN_FLIP()) getBounds().invertVelY();
}
@Override
public void tick() {
applyPhysics(Type.PLAYER, Type.HARD_OBJECT, Type.BULLET).discard();
if(timer.tick()) destroy();
}
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(color);
getBounds().draw(g2d);
}
@Override
public void destroy() { removeFromHandler(); }
}
private static class ExampleBullet extends Bullet {
private final Color color;
public ExampleBullet(final float x, final float y, final float width, final float height, final float velX, final float velY, final int elasticity, final int health_timer, final Color color) {
super(x, y, width, height, velX, velY, elasticity, health_timer);
this.color = color;
}
@Override
public void init() { }
@Override
public void tick() {
applyPhysics().discard();
if(timer.tick()) destroy();
}
@Override
public void render(final Graphics2D g2d) {
g2d.setColor(color);
getBounds().draw(g2d);
}
@Override
public void destroy() { removeFromHandler(); }
}
private static class ExampleDevConsole extends DeveloperConsole {
public ExampleDevConsole(final Engine engine) { super(engine); }
private static ExampleSaveFile saveFile;
@Override
protected void commands(final String action, final String item, final String value) {
switch(action) {
case "spawn": spawn(); break;
case "reset": engine.generateWorld(); break;
case "fps": System.out.println("FPS: " + engine.FPS +", " + "TPS: " + engine.TPS); break;
case "save": saveFile = new ExampleSaveFile(engine); break;
case "load": saveFile.load(engine); break;
}
}
private void spawn() {
final int range = 10;
for(int i = 0 ; i < 1000; i++) {
final float velX = Utils.COIN_FLIP() ? -Utils.nextInt(range) : Utils.nextInt(range);
final float velY = Utils.COIN_FLIP() ? -Utils.nextInt(range) : Utils.nextInt(range);
final float x = Utils.COIN_FLIP() ? player.getCenter().x - Utils.nextInt(700) : player.getCenter().x + Utils.nextInt(700);
final float y = player.getCenter().y - Utils.nextInt(500) - 10;
new ExampleBullet( x, y, 15 + Utils.nextInt(40), 15 + Utils.nextInt(40),
velX, velY, Utils.nextInt(100), 1000, new Color(Utils.nextInt(256), Utils.nextInt(256), Utils.nextInt(256))
);
}
}
private static class ExampleSaveFile extends SaveFile implements Serializable {
public ExampleSaveFile(final Engine engine) { super(engine); }
@Override
public void load(final Engine engine) {
loadEngineAndHandler(engine);
player = (ExamplePlayer)Handler.players.peek();
}
}
}
private static class ExampleKeyInput implements Keyboard {
static final float walkingVelocity = 3.5f;
static void tick(final PlayerBase player) {
if(DeveloperConsole.CONSOLE_OPEN) return;
if(Keyboard.D()) { player.setVelX(walkingVelocity); player.setFacing(Facing.RIGHT); }
else if(Keyboard.A()) { player.setVelX(-walkingVelocity); player.setFacing(Facing.LEFT); }
if(Keyboard.SPACE()) if(!player.getJumping()) { player.setJumping(true); player.setVelY(-15f); }
if(Keyboard.E()) new ExampleObject(player.getCenter().x, player.getCenter().y, 20, 20, true);
if(Keyboard.R()) engine.generateWorld();
if(Keyboard.F()) {
final float velX = player.facingLeft() ? -15 : 15;
new ExampleBullet(player.getCenter().x, player.getCenter().y, 40, 40, velX, 0, 75, 225, Colors.orange);
}
if(Keyboard.Q()) engine.exit();
}
}
private static class ExampleMouseInput extends Mouse {
public ExampleMouseInput(final Engine engine) { super(engine); }
@Override
public void mouseWheelMoved(final MouseWheelEvent e) {
engine.setScaleFactor(engine.getScaleFactor() + -e.getPreciseWheelRotation() / 5f);
engine.setScaleFactor(Utils.clamp(engine.getScaleFactor(), 0.1f, 15f));
}
@Override
public void mousePressed(final MouseEvent e) {
if(e.getButton() == 1) {
final float velX = player.facingLeft() ? -50 : 50;
new ExampleBullet(player.getCenter().x, player.getCenter().y, 40, 40, velX, 0, 35, 650, Colors.cyan);
}
}
@Override
public void mouseMoved(final MouseEvent e) { }
@Override
public void mouseClicked(final MouseEvent e) { }
@Override
public void mouseReleased(final MouseEvent e) { }
}
}

View File

@@ -0,0 +1,67 @@
package sjgs.base_objects;
import static sjgs.utils.Utils.brensenham;
import static sjgs.utils.Utils.exit;
import static sjgs.utils.Utils.isPercent;
import static sjgs.utils.Utils.print;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.utils.data_structures.vectors.SimplePoint;
public abstract class BaseTile extends HardObject implements Serializable {
private final SimplePoint[] slopePoints;
/** @param sh: The starting point at the left of the tile */
/** @param eh: The ending point at the right of the tile */
/** Both are measured from the floor upwards */
private final int sh, eh;
public BaseTile(final int x, final int y, final int w, final int h, final float startingHeightPercentage, final float endingHeightPercentage) {
super(x, y, w, h, false);
if(!isPercent(startingHeightPercentage / 100f) || !isPercent(endingHeightPercentage / 100f)) { print("Error: Tile angle height % out of range"); exit(); }
sh = (int)(getHeight() * (startingHeightPercentage / 100f)); eh = (int)(getHeight() * (endingHeightPercentage / 100f));
if(!(sh == 0 && eh == 0)) {
final int shX = (int)getX();
final int shY = (int)(getY() + getHeight() - sh);
final int ehX = (int)(getX() + w);
final int ehY = (int)(getY() + getHeight() - eh);
slopePoints = brensenham(shX, shY, ehX, ehY);
} else slopePoints = null;
}
@Override
public abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
public abstract void destroy();
public boolean angled() { return slopePoints != null; }
public SimplePoint[] getSlopePoints() { return slopePoints; }
protected void drawTestBoundaries(final Graphics2D g2d) {
if(!angled()) getBounds().draw(g2d);
else {
// BOTTOM LINE
g2d.drawLine((int)getX(), (int)(getY()+getHeight()), (int)(getX()+getWidth()), (int)getY()+(int)getHeight());
// BOTTOM TO SH LINE
g2d.drawLine((int)getX(), (int)(getY()+getHeight()), (int)getX(), (int)(getY()+getHeight()) - sh);
// SH TO EH LINE
g2d.drawLine((int)getX(), (int)(getY()+getHeight() - sh), (int)(getX() + getWidth()), (int)(getY()+getHeight()) - eh);
// BOTTOM RIGHT TO EH LINE
g2d.drawLine((int)(getX() + getWidth()), (int)(getY()+getHeight()), (int)(getX() + getWidth()), (int)(getY()+getHeight()) - eh);
}
}
}

View File

@@ -0,0 +1,43 @@
package sjgs.base_objects;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.core.Handler;
import sjgs.enums.Type;
import sjgs.utils.tools.Timer;
public abstract class Bullet extends GameObject implements Serializable {
public static final float MAX_ELASTICITY = 100, NO_ELASTICITY = 0;
protected final Timer timer;
/** @elasticity: A percent value, (e.g. from 0.01 to 1.00), that is a multiplier for the amount
* of energy retained after a collision. */
protected float elasticity;
public Bullet(final float x, final float y, final float w, final float h, final float velX, final float velY, final int elasticity, final int health_timer) {
super(x, y, w, h, Type.BULLET);
setVelX(velX);
setVelY(velY);
this.elasticity = elasticity / 100f;
timer = new Timer(health_timer);
Handler.bullets.add(this);
}
@Override
protected abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
protected abstract void destroy();
@Override
protected void removeFromHandler() { Handler.bullets.remove(this); }
public float getElasticity() { return elasticity; }
public void setElasticity(final float elasticity) { this.elasticity = elasticity; }
}

View File

@@ -0,0 +1,126 @@
package sjgs.base_objects;
import static sjgs.utils.pyutils.PyUtils.java2py;
import java.awt.Graphics2D;
import java.io.Serializable;
import org.python.core.PyFunction;
import org.python.core.PyObject;
import sjgs.enums.Facing;
import sjgs.enums.Type;
import sjgs.physics.Physics;
import sjgs.physics.structs.BoundingBox;
import sjgs.physics.structs.CollisionResponse;
import sjgs.utils.data_structures.shapes.Line;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
import sjgs.utils.pyutils.PyUtils;
public abstract class GameObject implements Serializable {
private final BoundingBox bounds;
private final Physics physics;
public final PyObject self;
public GameObject(final float x, final float y, final float w, final float h, final Type type) {
bounds = new BoundingBox(x, y, w, h, type);
physics = new Physics();
init();
self = java2py(this);
}
protected abstract void init();
public abstract void tick();
public abstract void render(Graphics2D g2d);
protected abstract void destroy();
protected abstract void removeFromHandler();
protected CollisionResponse applyPhysics() { return applyPhysics(Physics.getDefaultGravity(), Physics.getDefaultFriction(), Type.HARD_OBJECT); }
protected CollisionResponse applyPhysics(final Type ...args) { return applyPhysics(Physics.getDefaultGravity(), Physics.getDefaultFriction(), args); }
protected CollisionResponse applyPhysics(final float SPECIFIED_GRAVITY, final float SPECIFIED_FRICTION, final Type ...args) {
physics.updatePosition(this);
physics.applyFriction(this, SPECIFIED_FRICTION);
final CollisionResponse response = physics.collision(this, args);
physics.applyGravity(this, SPECIFIED_GRAVITY);
physics.calcGroundedAndOnSlope(this, response.collided_hard_objects);
return response;
}
public boolean falling() { return getFalling(); }
public boolean getFalling() { return !getOnSlope() && !getGrounded() && getVelY() > 0; }
public PyObject getSelf() { return self; }
public boolean movingRight() { return getVelX() > 0; }
public boolean movingLeft() { return getVelX() < 0; }
public boolean movingDown() { return getVelY() > 0; }
public boolean movingUp() { return getVelY() < 0; }
public Physics getPhysics() { return physics; } // NOTE you should really never need to do this!
public boolean isMoving() { return getVelX() != 0 || getVelY() != 0; }
public void updatePosition() { physics.updatePosition(this); }
public CollisionResponse getCollidedObjects() { return physics.getCollidedObjects(this); }
public boolean useCustomGravity() { return bounds.useCustomGravity(); }
public void dontUseCustomGravity() { bounds.dontUseCustomGravity(); }
public void setCustomGravity(final float gravity) { bounds.setCustomGravity(gravity); }
public float getCustomGravity() { return getCustomGravity(); }
public boolean stopped() { return bounds.stopped(); }
public float getVelX() { return bounds.getVelX(); }
public float getVelY() { return bounds.getVelY(); }
public void setVelX(final float velX) { bounds.setVelX(velX); }
public void setVelY(final float velY) { bounds.setVelY(velY); }
public void setVelocity(final float velX, final float velY) { bounds.setVelocity(velX, velY); }
public float getArea() { return bounds.getArea(); }
public BoundingBox getBounds() { return bounds; }
public Rectangle getFullBounds() { return bounds.getFullBounds(); }
public Rectangle getLeftBounds() { return bounds.getLeftBounds(); }
public Rectangle getRightBounds() { return bounds.getRightBounds(); }
public Rectangle getTopBounds() { return bounds.getTopBounds(); }
public Rectangle getBottomBounds() { return bounds.getBottomBounds(); }
public float getWidth() { return bounds.getFullBounds().getWidth(); }
public float getHeight() { return bounds.getFullBounds().getHeight(); }
public float getHalfWidth() { return getWidth() * 0.5f; }
public float getHalfHeight() { return getHeight() * 0.5f; }
public void setWidth(final float w) { bounds.setWidth(w); }
public void setHeight(final float h) { bounds.setHeight(h); }
public void setSize(final float w, final float h) { bounds.setSize(w, h); }
public boolean getGrounded() { return bounds.getGrounded(); }
public boolean getOnSlope() { return bounds.getOnSlope(); }
public void setGrounded(final boolean b) { bounds.setGrounded(b); }
public void setOnSlope(final boolean b) { bounds.setOnSlope(b); }
public void dontApplyGravity() { bounds.setDontApplyGravity(true); }
public boolean getDontApplyGravity() { return bounds.getDontApplyGravity(); }
public void setDontApplyGravity(final boolean b) { bounds.setDontApplyGravity(b); }
public void toggleDontApplyGravity() { bounds.toggleDontApplyGravity(); }
public void dontApplyFriction() { bounds.setDontApplyFriction(true); }
public boolean getDontApplyFriction() { return bounds.getDontApplyFriction(); }
public void setDontApplyFriction(final boolean b) { bounds.setDontApplyFriction(b); }
public void toggleDontApplyFriction() { bounds.toggleDontApplyFriction(); }
public Point2f getLocation() { return bounds.getLocation(); }
public void setLocation(final float x, final float y) { bounds.setLocation(x, y); }
public void setLocation(final Point2f p) { bounds.setLocation(p.x, p.y); }
public void setLocation(final SimplePoint p) { bounds.setLocation(p.x, p.y); }
public void setX(final float x) { bounds.setX(x); }
public void setY(final float y) { bounds.setY(y); }
public float getX() { return bounds.getFullBounds().getX(); }
public float getY() { return bounds.getFullBounds().getY(); }
public float getCenterX() { return getX() + getHalfWidth(); }
public float getCenterY() { return getY() + getHalfHeight(); }
public Point2f getCenter() { return new Point2f(getX() + getHalfWidth(), getY() + getHalfHeight()); }
public void setCenter(final Point2f p) { setLocation(p.x - getWidth() / 2, p.y - getHeight() / 2); }
public void setCenter(final float x, final float y) { setLocation(x - getWidth() / 2, y - getHeight() / 2); }
public Facing getFacing() { return bounds.getFacing(); }
public boolean facingLeft() { return bounds.facingLeft(); }
public boolean facingRight() { return bounds.facingRight(); }
public void setFacing(final Facing facing) { bounds.setFacing(facing); }
public Type getType() { return bounds.getType(); }
public PyFunction createPyFunction(String funcName) { return PyUtils.createPyFunction(funcName); }
public Point2f getTopRight() { return getBounds().getTopRight(); }
public Point2f getTopLeft() { return getBounds().getTopLeft(); }
public Point2f getBottomRight() { return getBounds().getBottomRight(); }
public Point2f getBottomLeft() { return getBounds().getBottomLeft(); }
public Line getTop() { return getBounds().getTop(); }
public Line getBottom() { return getBounds().getBottom(); }
public Line getLeft() { return getBounds().getLeft(); }
public Line getRight() { return getBounds().getRight(); }
}

View File

@@ -0,0 +1,35 @@
package sjgs.base_objects;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.core.Handler;
import sjgs.enums.Type;
public abstract class HardObject extends GameObject implements Serializable {
public final boolean mobile;
public HardObject(final float x, final float y, final float w, final float h, final boolean mobile) {
super(x, y, w, h, Type.HARD_OBJECT);
this.mobile = mobile;
if(mobile) Handler.mobile_hard_objects.add(this);
else Handler.stationary_hard_objects.insert(this);
}
@Override
protected abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
protected abstract void destroy();
@Override
protected void removeFromHandler() {
if(mobile) Handler.mobile_hard_objects.remove(this);
else Handler.stationary_hard_objects.remove(this);
}
}

44
src/sjgs/base_objects/Mob.java Executable file
View File

@@ -0,0 +1,44 @@
package sjgs.base_objects;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.core.Handler;
import sjgs.enums.Type;
public abstract class Mob extends GameObject implements Serializable {
protected float health, damage;
protected boolean attacking;
public Mob(final float x, final float y, final float w, final float h) {
super(x, y, w, h, Type.MOB);
Handler.mobs.add(this);
}
@Override
protected abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
protected abstract void destroy();
protected boolean checkIfAlive() { if(health <= 0) destroy(); return health <= 0; }
@Override
protected void removeFromHandler() { Handler.mobs.remove(this); }
public void setAttacking(final boolean attacking) { this.attacking = attacking; }
public boolean getAttacking() { return attacking; }
public void decrementHealth(final float decrement) { setHealth(getHalfHeight() - decrement); }
public void incrementHealth(final float increment) { setHealth(getHealth() + increment); }
public void setHealth(final float health) { this.health = health; }
public float getHealth() { return health; }
public void setDamage(final float damage) { this.damage = damage; }
public float getDamage() { return damage; }
}

View File

@@ -0,0 +1,51 @@
package sjgs.base_objects;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.core.Handler;
import sjgs.enums.Type;
public abstract class PlayerBase extends GameObject implements Serializable {
protected float health;
protected boolean jumping, attacking, lookingUp, lookingDown;
public PlayerBase(final float x, final float y, final float w, final float h) {
super(x, y, w, h, Type.PLAYER);
Handler.addPlayer(this);
}
@Override
protected abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
protected abstract void destroy();
/** @manageJumping: Sets player to not be able to jump if the player is falling while not
* grounded, or has already jumped. Make sure you set the player's jumping variable to
* be true when he/she jumps in your keyinput class. */
protected void manageJumping() {
if(jumping && getBounds().getGrounded() || getBounds().getOnSlope()) jumping = false;
}
@Override
protected void removeFromHandler() { Handler.removePlayer(this); }
public void setLookingUp(boolean b) { this.lookingUp = b; }
public boolean getLookingUp() { return lookingUp; }
public void setLookingDown(boolean b) { this.lookingDown = b; }
public boolean getLookingDown() { return lookingDown; }
public void setJumping(final boolean jumping) { this.jumping = jumping; }
public boolean getJumping() { return jumping; }
public void setAttacking(final boolean attacking) { this.attacking = attacking; }
public boolean getAttacking() { return attacking; }
public void decrementHealth(final float decrement) { setHealth(getHalfHeight() - decrement); }
public void incrementHealth(final float increment) { setHealth(getHealth() + increment); }
public void setHealth(final float health) { this.health = health; }
public float getHealth() { return health; }
}

View File

@@ -0,0 +1,35 @@
package sjgs.base_objects;
import java.awt.Graphics2D;
import java.io.Serializable;
import sjgs.core.Handler;
import sjgs.enums.Type;
public abstract class SoftObject extends GameObject implements Serializable {
public final boolean mobile;
public SoftObject(final float x, final float y, final float w, final float h, final boolean mobile) {
super(x, y, w, h, Type.SOFT_OBJECT);
this.mobile = mobile;
if(mobile) Handler.mobile_soft_objects.add(this);
else Handler.stationary_soft_objects.insert(this);
}
@Override
protected abstract void init();
@Override
public abstract void tick();
@Override
public abstract void render(Graphics2D g2d);
@Override
protected abstract void destroy();
@Override
protected void removeFromHandler() {
if(mobile) Handler.mobile_soft_objects.remove(this);
else Handler.stationary_soft_objects.remove(this);
}
}

View File

@@ -0,0 +1,43 @@
# mob will travel around a given rectangle -- think like in early stages of metroid
# territory === given rectangle
def mob_travel_around_rectangle_ai(mob, territory, speed, clockWise):
fullBounds = mob.getFullBounds()
rightBounds = mob.getRightBounds()
leftBounds = mob.getLeftBounds()
topBounds = mob.getTopBounds()
bottomBounds = mob.getBottomBounds()
top = territory.getTop()
right = territory.getRight()
left = territory.getLeft()
bottom = territory.getBottom()
if clockWise:
if fullBounds.intersects(right) and not fullBounds.intersects(bottom):
mob.setVelocity(0, speed)
mob.setX(territory.pos.x + territory.getWidth())
mob.setY(restrict(mob.getY(), territory.getY() - 2))
elif fullBounds.intersects(top):
mob.setVelocity(speed, 0)
mob.setY(territory.pos.y - mob.getHeight())
mob.setX(restrict(mob.getX(), territory.getX() - 1))
elif fullBounds.intersects(left) and not fullBounds.intersects(top) and not topBounds.intersects(bottom):
mob.setVelocity(0, -speed)
mob.setX(territory.pos.x - mob.getHeight())
elif fullBounds.intersects(bottom):
mob.setVelocity(-speed, 0)
mob.setY(territory.pos.y + 2 + mob.getHeight())
else:
if fullBounds.intersects(right) and not fullBounds.intersects(top):
mob.setVelocity(0, -speed)
mob.setX(territory.pos.x + territory.getWidth())
mob.setY(restrict(mob.getY(), territory.getY() - 2))
elif fullBounds.intersects(top) and not fullBounds.intersects(left):
mob.setVelocity(-speed, 0)
mob.setY(territory.pos.y - mob.getHeight())
elif fullBounds.intersects(bottom) and not fullBounds.intersects(right):
mob.setVelocity(speed, 0)
mob.setY(territory.pos.y + mob.getHeight() + 2)
elif fullBounds.intersects(left):
mob.setVelocity(0, speed)
mob.setX(territory.pos.x - mob.getHeight())

126
src/sjgs/core/Camera.java Executable file
View File

@@ -0,0 +1,126 @@
package sjgs.core;
import static sjgs.utils.Utils.brensenham;
import static sjgs.utils.Utils.cinch;
import static sjgs.utils.Utils.converge;
import static sjgs.utils.Utils.cos;
import static sjgs.utils.Utils.sin;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
public class Camera { //TODO: camera acceleartion / delay and shake
private final Rectangle bounds;
private SimplePoint[] shakePoints;
private float shakeSpeed;
private int shakeIndex;
private float acceleration, speedX, speedY, maxSpeed;
private float minX, minY, maxX, maxY;
public Camera(final Engine engine) {
bounds = new Rectangle(0, 0, engine.getWidth(), engine.getHeight());
}
public void tick(final Point2f target, final double scaleFactor) {
final float w = (float)(getWidth() / scaleFactor);
final float h = (float)(getHeight() / scaleFactor);
final float hw = w * 0.5f;
final float hh = h * 0.5f;
float proposedX;
float proposedY;
final float tX = -target.x + hw;
final float tY = -target.y + hh;
// IF WE'RE NOT SHAKING:
if(shakePoints == null) {
// IF THERE'S NO ACCELERATION
if(acceleration <= 0) {
proposedX = tX;
proposedY = tY;
// ELSE IF THERE *IS* ACCELERATION
} else {
proposedX = converge(getX(), tX, speedX);
proposedY = converge(getY(), tY, speedY);
if(getX() != tX) speedX = cinch(speedX + acceleration, maxSpeed);
else speedX = 0;
if(getY() != tY) speedY = cinch(speedY + acceleration, maxSpeed);
else speedY = 0;
}
// ELSE IF WE *ARE* SHAKING
} else {
if(shakeIndex >= shakePoints.length - 1) shakePoints = null;
proposedX = shakePoints[shakeIndex].x;
proposedY = shakePoints[shakeIndex].y;
shakeIndex++;
}
// IF THERE ARE BOUNDARIES SET
if(!(minX == 0 && minY == 0 && maxX == 0 && maxY == 0)) {
if(-proposedX < minX) setX(minX);
else if(-proposedX + w > maxX) setX(-(maxX - w));
else setX(proposedX);
if(-proposedY < minY) setY(minY);
else if(-proposedY + h > maxY) setY(-(maxY - h));
else setY(proposedY);
// ELSE IF THERE *ISNT* BOUNDARIES SET
} else {
setX(proposedX);
setY(proposedY);
}
}
public void changeSettings(final float screenWidth, final float screenHeight) { bounds.setSize(screenWidth, screenHeight); }
public float getX() { return bounds.getX(); }
public float getY() { return bounds.getY(); }
public float getCenterX() { return bounds.getCenterX(); }
public float getCenterY() { return bounds.getCenterY(); }
public float getWidth() { return bounds.getWidth(); }
public float getHeight() { return bounds.getHeight(); }
public float getHalfWidth() { return bounds.getHalfWidth(); }
public float getHalfHeight() { return bounds.getHalfHeight(); }
public void setX(final float x) { bounds.setX(x); }
public void setY(final float y) { bounds.setY(y); }
public Point2f getPos() { return bounds.pos; }
public Point2f getCenter() { return bounds.getCenter(); }
public void setScreenWidth(final float screenWidth) { bounds.setWidth(screenWidth); }
public void setScreenHeight(final float screenHeight) { bounds.setHeight(screenHeight); }
public void disableAcceleration() { acceleration = maxSpeed = -1; }
public void setAcceleration(final float acceleration, final float maxSpeed) { this.acceleration = acceleration; this.maxSpeed = maxSpeed; }
public void setLimitsWithRect(final float x, final float y, final float width, final float height) {
minX = x;
minY = y;
maxX = x + width;
maxY = y + height;
}
public void setLimits(final float minX, final float minY, final float maxX, final float maxY) {
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
/** @shake: Moves the center of the camera to a point away from the camera center based on
* the given distance and angle in radians. The transition is governed by the attack
* and fall durations. */
public void shake(final float distance, final float theta, final float shakeSpeed) {
shakePoints = brensenham(getPos(), new Point2f(getX() + distance * cos(theta), getY() + distance * sin(theta)));
shakeIndex = 0;
this.shakeSpeed = shakeSpeed;
}
}

View File

@@ -0,0 +1,157 @@
package sjgs.core;
import static sjgs.graphics.Colors.black;
import static sjgs.graphics.Colors.pastelRed;
import static sjgs.graphics.Colors.white;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.InputStream;
import java.util.LinkedList;
public abstract class DeveloperConsole extends KeyAdapter {
// Remember to add this to your engine.frame!
private static StringBuilder sb;
private static LinkedList<String> history;
private static int index;
public static boolean CONSOLE_OPEN;
private static Font ponderosa_20;
private static Font gregorian48Bold;
public DeveloperConsole(final Engine engine) {
sb = new StringBuilder();
history = new LinkedList<String>();
try { InputStream is1 = DeveloperConsole.class.getResourceAsStream("/gregorian.ttf");
InputStream is2 = DeveloperConsole.class.getResourceAsStream("/ponderosa.ttf");
ponderosa_20 = Font.createFont(Font.TRUETYPE_FONT, is2).deriveFont(Font.BOLD, 10.0f);
gregorian48Bold = Font.createFont(Font.TRUETYPE_FONT, is1).deriveFont(Font.BOLD, 48f);
is1.close(); is1 = null; is2.close(); is2 = null;
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(gregorian48Bold);
ge.registerFont(ponderosa_20);
} catch (final Exception e) { e.printStackTrace(); }
engine.frame.addKeyListener(this);
}
protected abstract void commands(String action, String item, String value);
private synchronized void execute(final String command) {
if(command.length() <= 0) { exit(); return; }
String action, item, value;
// GET THE ACTION, IF THE ACTION IS A ONE-WORD PHRASE, ACTION = THE WHOLE COMMAND
try { action = command.substring(0, command.indexOf(" ")); } catch (final StringIndexOutOfBoundsException e) { action = command.toString(); }
// GET THE ITEM, IF THERE IS NO AMOUNT, ITEM = JUST THE ITEM, IF THERE IS NO ITEM, ITEM = ""
try { final String tempItem = command.substring(command.indexOf(" ") + 1); item = tempItem.substring(0, tempItem.indexOf(" ")); } catch (final StringIndexOutOfBoundsException e) {
try { item = command.substring(command.indexOf(" ") + 1); } catch (final StringIndexOutOfBoundsException ee) { item = ""; }
}
// GET THE VALUE ----
try { final String temp = command.substring(command.indexOf(" ") + 1) ;
value = temp.substring(temp.indexOf(" ") + 1);
} catch (final StringIndexOutOfBoundsException ee) { value = ""; }
item.replace(" ", ""); action.replace(" ", ""); value.replace(" ", "");
//------------------------------------------------//
baseCommands(action, item, value);
commands(action, item, value);
//------------------------------------------------//
exit();
}
private static void baseCommands(final String action, @SuppressWarnings("unused") final String item, @SuppressWarnings("unused") final String value) {
switch(action) {
case "quit": exit(); break;
case "exit": exit(); break;
}
}
@Override
public void keyPressed(final KeyEvent e) {
if(e.getKeyChar() == '~') { if(!CONSOLE_OPEN) open(); else exit(); return; }
else if(!CONSOLE_OPEN) return;
else if(e.getKeyCode() == KeyEvent.VK_ESCAPE) { exit(); return; }
else if(e.getKeyCode() == KeyEvent.VK_UP) {
if(history.size() <= 0) return;
final String temp = sb.toString();
sb = new StringBuilder();
try { sb.append(history.get(index--)); } catch (final Exception ex) {
index++;
sb.append(temp);
}
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
if(history.size() <= 0) return;
final String temp = sb.toString();
sb = new StringBuilder();
try { sb.append(history.get(++index)); } catch (final Exception ex) {
index--;
sb.append(temp);
}
}
else if(e.getKeyCode() == KeyEvent.VK_ENTER) {
history.add(sb.toString());
execute(sb.toString());
sb = new StringBuilder();
return;
}
else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { if(sb.length() > 0) sb.deleteCharAt(sb.length()-1); return; }
else if(sb.length() < 67) {
switch(e.getKeyCode()) {
case KeyEvent.VK_BACK_SPACE: return;
case KeyEvent.VK_TAB: return;
case KeyEvent.VK_SHIFT: return;
case KeyEvent.VK_UP: return;
case KeyEvent.VK_DOWN: return;
case KeyEvent.VK_LEFT: return;
case KeyEvent.VK_RIGHT: return;
case KeyEvent.VK_CONTROL: return;
case KeyEvent.VK_WINDOWS: return;
case KeyEvent.VK_UNDEFINED: return;
case KeyEvent.VK_NUM_LOCK: return;
default: sb.append(e.getKeyChar());
}
}
}
public static void render(final Graphics g, final int width, final int height) {
if(!CONSOLE_OPEN) return;
g.setColor(black);
g.fillRect(width/2 - width/4, height/2 - height/4, width/2, height/20);
g.setColor(white);
g.setFont(ponderosa_20);
g.drawString(sb.toString(), width/2 - width/4 + 10, height/2 - height/4 + 20);
g.setColor(pastelRed);
g.setFont(gregorian48Bold);
g.drawString("sjgs console:", width/2 - width/4 + 2, height/2 - height/4 - 10);
}
private static synchronized void open() {
sb = new StringBuilder();
CONSOLE_OPEN = true;
}
private static synchronized void exit() {
index = history.size() - 1;
CONSOLE_OPEN = false;
}
}

226
src/sjgs/core/Engine.java Executable file
View File

@@ -0,0 +1,226 @@
package sjgs.core;
import static sjgs.graphics.Colors.black;
import static sjgs.graphics.Colors.white;
import static sjgs.utils.Utils.error;
import static sjgs.utils.pyutils.PyUtils.java2py;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.io.Serializable;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.python.core.PyObject;
import sjgs.core.input.__Keyboard;
import sjgs.core.jython.Jython;
import sjgs.physics.Physics;
import sjgs.utils.Utils;
import sjgs.utils.multithreading.Runner;
import sjgs.utils.multithreading.ThreadPool;
public abstract class Engine extends Canvas implements Runnable, Serializable {
private static final String engine_version = "SJGS v0.0.12";
/** @TICK_INTERVAL && @FPS_CAP: default no arg constructor gives 60 tps / fps */
private double TICK_INTERVAL;
private double FRAME_RATE, FPS_CAP;
private double scaleFactor = 1d;
private boolean running;
protected int frames, ticks, FPS, TPS;
// NOTE THE DEFAULTS!
private boolean multithreadedGameUpdating = false, multithreadedRendering = true, variableSleepRate = false;
public final ThreadPool pool;
public final JFrame frame;
public final JPanel panel;
public final Camera camera;
public final PyObject self;
public Engine(final int WIDTH, final int HEIGHT, final String title) {
pool = new ThreadPool();
frame = new JFrame();
panel = new JPanel();
new Runner(() -> {
new Runner(() -> { new Physics().init(); }).run();
new Runner(() -> { new Handler().init(); }).run();
setFPS_CAP(60.0d);
TICK_INTERVAL = Utils.second / 60.0d;
error(getVersion() + "\n" + Utils.OS);
toggleDrawFPS();
}).run();
new Jython().__init__();
createWindow(WIDTH, HEIGHT, title);
camera = new Camera(this);
init();
self = java2py(this);
pool.runTask(this);
}
private void createWindow(final int WIDTH, final int HEIGHT, final String title) {
new Runner(() -> {
setFocusTraversalKeysEnabled(false); /* NOTE YOU NEED THIS FOR TAB KEY TO WORK! */
frame.setTitle(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}).run();
frame.add(panel); frame.add(this); frame.pack();
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
new __Keyboard().init(panel);
}
//----------------------------------------------------------------//
protected abstract void init();
protected abstract void tick();
protected abstract void render(Graphics2D g2d);
//----------------------------------------------------------------//
private boolean drawFPS, rendering;
private BufferStrategy bs; private Graphics g; private Graphics2D g2d;
public void engine_render() {
begin();
drawBaseLayer();
scale();
// ================================ //
render(g2d);
// ================================ //
renderDevConsole();
renderFPS();
end();
}
private void end() {
g2d.dispose(); g.dispose();
g2d = null; g = null;
bs.show();
rendering = false;
}
private void renderFPS() { if(drawFPS) drawFPS(); }
private void renderDevConsole() {
final Graphics2D consoleRenderer = createG2D();
DeveloperConsole.render(consoleRenderer, getWidth(), getHeight());
}
private void begin() {
if (bs == null) createBufferStrategy(3);
bs = getBufferStrategy();
if (bs.getDrawGraphics() != null) g = bs.getDrawGraphics();
if(g != null) g2d = (Graphics2D) g;
}
private void drawBaseLayer() {
g2d.setColor(black);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
private void drawFPS() {
final Graphics2D fps = createG2D();
fps.setColor(white);
fps.drawString("FPS: " + FPS + " " + "TPS: " + TPS, 50, 50);
}
public final void scale() { g2d.scale(scaleFactor, scaleFactor); }
/** @method createG2D: Creates a new g2d object, useful for when you need to reset
* your current g2d object, but can't destroy it. */
public final Graphics2D createG2D() {
if (bs.getDrawGraphics() != null) g = bs.getDrawGraphics();
return g != null ? (Graphics2D) g : null;
}
@Override
public final void run() {
long now, tickThen, frameThen, fpsUpdateTimer;
now = tickThen = frameThen = System.nanoTime();
fpsUpdateTimer = System.currentTimeMillis();
final int SECOND = 1000;
double deltaR, deltaT;
int sleepTime;
final Runner ticker = new Runner(() -> { tick(); ticks++; });
final Runner renderer = new Runner(() -> { engine_render(); frames++; });
frame.setVisible(true);
setVisible(true);
running = true;
// GAME LOOP
while (running) {
// GET TIME
now = System.nanoTime();
// TICK
if(now - tickThen > TICK_INTERVAL) {
if(multithreadedGameUpdating) pool.runTask( ticker );
else { tick(); ticks++; }
tickThen = now;
}
// RUN RENDERER IN THREADPOOL
if(!rendering && now - frameThen > FRAME_RATE) {
if(multithreadedRendering) pool.runTask(renderer);
else { engine_render(); frames++; }
frameThen = now;
rendering = true;
}
// UPDATE FRAMES PER SECOND
if (System.currentTimeMillis() - fpsUpdateTimer > SECOND) {
FPS = frames; TPS = ticks; frames = ticks = 0;
fpsUpdateTimer += SECOND;
}
if(variableSleepRate) {
// FIND WHICH TIME IS SHORTER:
// ---- The time until next game update,
// ---- or the time until next render
// Take whichever is shorter, and sleep that length
now = System.nanoTime();
deltaR = FRAME_RATE - (now - frameThen);
deltaT = TICK_INTERVAL - (now - tickThen);
sleepTime = (int) (deltaR < deltaT ? deltaR : deltaT) / 1_000_000 - 1;
if (sleepTime > 0) try { Thread.sleep(sleepTime); } catch (final InterruptedException e) { e.printStackTrace(); }
}
}
}
protected int getScreenWidth() { return Utils.SCREEN_WIDTH; }
protected int getScreenHeight() { return Utils.SCREEN_HEIGHT; }
protected void clearHandler() { Handler.clearAll(); }
public void disableFpsCap() { setFPS_CAP(10_000); }
public double getScaleFactor() { return scaleFactor; }
public void setScaleFactor(final double scaleFactor) { this.scaleFactor = scaleFactor; }
public static final String getVersion() { return engine_version; }
public final int getFPS() { return FPS; }
public final int getTPS() { return TPS; }
public final double getTickRate() { return TICK_INTERVAL; }
public final void setTickRate(final double TICK_INTERVAL) { this.TICK_INTERVAL = TICK_INTERVAL; }
public final double getFPS_CAP() { return FPS_CAP; }
public final void setFPS_CAP(final double FPS_CAP) { this.FPS_CAP = FPS_CAP; FRAME_RATE = Utils.second / FPS_CAP; }
public final void setDoubleTickRate() { TICK_INTERVAL = Utils.second / 120d; new Physics().setDoubleTickRate(); }
public void exit() { System.gc(); running = false; System.exit(0); }
public final void toggleDrawFPS() { drawFPS = !drawFPS; }
public final boolean getDrawFps() { return drawFPS; }
public final void setDrawFps(final boolean drawFPS) { this.drawFPS = drawFPS; }
public final void enableVariableSleepRate() { variableSleepRate = true; }
public final void enableMultithreadedGameUpdating() { multithreadedGameUpdating = true; }
public final void enableMultithreadedRendering() { multithreadedRendering = true; }
public final void disableVariableSleepRate() { variableSleepRate = false; }
public final void disableMultithreadedGameUpdating() { multithreadedGameUpdating = false; }
public final void disableMultithreadedRendering() { multithreadedRendering = false; }
}

98
src/sjgs/core/Handler.java Executable file
View File

@@ -0,0 +1,98 @@
package sjgs.core;
import java.awt.Graphics2D;
import sjgs.base_objects.GameObject;
import sjgs.base_objects.PlayerBase;
import sjgs.graphics.lighting.Light;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.gaming.QuadTree;
public final class Handler {
public static QuadTree stationary_hard_objects, stationary_soft_objects;
public static Stack<GameObject> players, mobs, bullets, mobile_hard_objects, mobile_soft_objects;
public static Stack<Light> lights;
private static int TREE_OFFSET, TREE_BOUNDS;
void init() {
TREE_OFFSET = 32;
TREE_BOUNDS = 5000;
stationary_hard_objects = new QuadTree(-TREE_BOUNDS, -TREE_BOUNDS, TREE_BOUNDS, TREE_BOUNDS);
stationary_soft_objects = new QuadTree(-TREE_BOUNDS, -TREE_BOUNDS, TREE_BOUNDS, TREE_BOUNDS);
mobile_hard_objects = new Stack<GameObject>();
mobile_soft_objects = new Stack<GameObject>();
mobs = new Stack<GameObject>();
bullets = new Stack<GameObject>();
players = new Stack<GameObject>();
lights = new Stack<Light>();
}
/** @param scaleFactor: should be one if not using any scaling */
public static void tick(final Camera camera, final double scaleFactor) {
final float minX = -camera.getX() - TREE_OFFSET;
final float minY = -camera.getY() - TREE_OFFSET;
final float maxX = (float)(-camera.getX() + camera.getWidth() / scaleFactor + TREE_OFFSET);
final float maxY = (float)(-camera.getY() + camera.getHeight() / scaleFactor + TREE_OFFSET);
try { for(final GameObject g : stationary_hard_objects.search(minX, minY, maxX, maxY)) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : stationary_soft_objects.search(minX, minY, maxX, maxY)) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : mobile_hard_objects) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : mobile_soft_objects) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : bullets) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : mobs) g.tick(); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject player : players) try { player.tick(); } catch(final Exception e) { e.printStackTrace(); } } catch (final Exception e) { e.printStackTrace(); }
}
public static void render(final Graphics2D g2d, final Camera camera, final double scaleFactor) {
g2d.translate(camera.getX(), camera.getY());
final float minX = -camera.getX() - TREE_OFFSET;
final float minY = -camera.getY() - TREE_OFFSET;
final float maxX = (float)(-camera.getX() + camera.getWidth() / scaleFactor + TREE_OFFSET);
final float maxY = (float)(-camera.getY() + camera.getHeight() / scaleFactor + TREE_OFFSET);
try { for(final GameObject g : stationary_hard_objects.search(minX, minY, maxX, maxY)) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); } } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject g : stationary_soft_objects.search(minX, minY, maxX, maxY)) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); } } catch (final Exception e) { e.printStackTrace(); }
for(final GameObject g : mobile_hard_objects) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); }
for(final GameObject g : mobile_soft_objects) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); }
for(final GameObject g : mobs) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); }
for(final GameObject g : bullets) try { g.render(g2d); } catch (final Exception e) { e.printStackTrace(); }
try { for(final GameObject player : players) try { player.render(g2d); } catch (final Exception e) { e.printStackTrace(); } } catch (final Exception e) { e.printStackTrace(); }
g2d.translate(-camera.getX(), -camera.getY());
// g2d.scale(-scaleFactor, -scaleFactor);
}
public static void setBounds(final int _TREE_BOUNDS) {
TREE_BOUNDS = _TREE_BOUNDS;
final Stack<GameObject> tempHards = stationary_hard_objects.toStack();
final Stack<GameObject> tempSofts = stationary_soft_objects.toStack();
stationary_hard_objects.clear(); stationary_soft_objects.clear();
stationary_hard_objects = new QuadTree(-TREE_BOUNDS, -TREE_BOUNDS, TREE_BOUNDS, TREE_BOUNDS);
stationary_soft_objects = new QuadTree(-TREE_BOUNDS, -TREE_BOUNDS, TREE_BOUNDS, TREE_BOUNDS);
for(final GameObject h : tempHards) stationary_hard_objects.insert(h);
for(final GameObject s : tempSofts) stationary_soft_objects.insert(s);
}
public static void setTreeOffset(final int _TREE_OFFSET) { TREE_OFFSET = _TREE_OFFSET; }
public static int getTreeOffset() { return TREE_OFFSET; }
public static int getTreeBounds() { return TREE_BOUNDS; }
public static void addPlayer(final PlayerBase PLAYER) { players.push(PLAYER); }
public static void removePlayer(final PlayerBase PLAYER) { players.remove(PLAYER); }
public static void clearPlayers() { if(players != null) players.clear(); }
public static void clearBullets() { if(bullets != null) bullets.clear(); }
public static void clearMobs() { if(mobs != null) mobs.clear(); }
public static void clearSoftObjects() { if(stationary_soft_objects != null) stationary_soft_objects.clear(); if(mobile_soft_objects != null) mobile_soft_objects.clear(); }
public static void clearHardObjects() { if(stationary_hard_objects != null) stationary_hard_objects.clear(); if(mobile_hard_objects != null) mobile_hard_objects.clear(); }
public static void clearAll() {
clearPlayers();
clearBullets();
clearMobs();
clearSoftObjects();
clearHardObjects();
}
}

View File

@@ -0,0 +1,55 @@
package sjgs.core.input;
import java.util.HashSet;
public interface Keyboard {
static final HashSet<Integer> keysDown = __Keyboard.keysDown;
// --------------------- KEY CODES ---------------------------------- //
static final int W=0, A=1, S=2, D=3, SPACE=4, TAB=5, E=11, Q=12, ESCAPE=13, F=14,
MINUS=15, EQUALS = 16, CTRL = 17, PLUS=18, ONE=6, TWO=7, THREE=8,
FOUR=9, FIVE=10, SIX=19, SEVEN=20, EIGHT=21, NINE=22, ZERO=23, R=24,
Z=25, X=26, C=27, V=28, B=29, G=30, T=31, M=32, UP=33, DOWN=34, RIGHT=35, LEFT=36;
// -------------------- END KEY CODES ------------------------------- //
public static boolean W() { return keysDown.contains(W); }
public static boolean A() { return keysDown.contains(A); }
public static boolean S() { return keysDown.contains(S); }
public static boolean D() { return keysDown.contains(D); }
public static boolean SPACE() { return keysDown.contains(SPACE); }
public static boolean TAB() { return keysDown.contains(TAB); }
public static boolean E() { return keysDown.contains(E); }
public static boolean Q() { return keysDown.contains(Q); }
public static boolean ESCAPE() { return keysDown.contains(ESCAPE); }
public static boolean F() { return keysDown.contains(F); }
public static boolean MINUS() { return keysDown.contains(MINUS); }
public static boolean EQUALS() { return keysDown.contains(EQUALS); }
public static boolean CTRL() { return keysDown.contains(CTRL); }
public static boolean PLUS() { return keysDown.contains(PLUS); }
public static boolean ONE() { return keysDown.contains(ONE); }
public static boolean TWO() { return keysDown.contains(TWO); }
public static boolean THREE() { return keysDown.contains(THREE); }
public static boolean FOUR() { return keysDown.contains(FOUR); }
public static boolean FIVE() { return keysDown.contains(FIVE); }
public static boolean SIX() { return keysDown.contains(SIX); }
public static boolean SEVEN() { return keysDown.contains(SEVEN); }
public static boolean EIGHT() { return keysDown.contains(EIGHT); }
public static boolean NINE() { return keysDown.contains(NINE); }
public static boolean ZERO() { return keysDown.contains(ZERO); }
public static boolean R() { return keysDown.contains(R); }
public static boolean Z() { return keysDown.contains(Z); }
public static boolean X() { return keysDown.contains(X); }
public static boolean C() { return keysDown.contains(C); }
public static boolean V() { return keysDown.contains(V); }
public static boolean B() { return keysDown.contains(B); }
public static boolean G() { return keysDown.contains(G); }
public static boolean T() { return keysDown.contains(T); }
public static boolean M() { return keysDown.contains(M); }
public static boolean UP() { return keysDown.contains(UP); }
public static boolean DOWN() { return keysDown.contains(DOWN); }
public static boolean RIGHT() { return keysDown.contains(RIGHT); }
public static boolean LEFT() { return keysDown.contains(LEFT); }
}

56
src/sjgs/core/input/Mouse.java Executable file
View File

@@ -0,0 +1,56 @@
package sjgs.core.input;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import sjgs.core.Engine;
import sjgs.utils.data_structures.vectors.Point2f;
public abstract class Mouse implements MouseListener, MouseWheelListener, MouseMotionListener{
protected static final Point2f pos = new Point2f(0, 0);
protected static final Point2f clickPos = new Point2f(0, 0);
protected static final Point2f rightClickPos = new Point2f(0,0);
public Mouse(final Engine engine) {
engine.addMouseListener(this);
engine.addMouseWheelListener(this);
engine.addMouseMotionListener(this);
}
@Override
public abstract void mouseMoved(MouseEvent e);
@Override
public abstract void mouseWheelMoved(MouseWheelEvent arg0);
@Override
public abstract void mouseClicked(MouseEvent e);
@Override
public abstract void mousePressed(MouseEvent e);
@Override
public abstract void mouseReleased(MouseEvent e);
@Override
public void mouseDragged(final MouseEvent e) { pos.setLocation(e.getX(), e.getY()); }
@Override
public void mouseEntered(final MouseEvent e) { pos.setLocation(e.getX(), e.getY()); }
@Override
public void mouseExited(final MouseEvent e) { pos.setLocation(e.getX(), e.getY()); }
public static float getX() { return pos.x; }
public static float getY() { return pos.y; }
public static float getClickX() { return clickPos.x; }
public static float getClickY() { return clickPos.y; }
public static float getRightClickX() { return rightClickPos.x; }
public static float getRightClickY() { return rightClickPos.y; }
public void setLocation(final int x, final int y) { pos.setLocation(x, y); }
public void setClickLocation(final int x, final int y) { clickPos.setLocation(x, y); }
public void setRightClickLocation(final int x, final int y) { rightClickPos.setLocation(x, y); }
public static Point2f getClickPos() { return clickPos; }
public static Point2f getRightClickPos() { return rightClickPos; }
public static Point2f getMousePos() { return pos; }
}

View File

@@ -0,0 +1,383 @@
package sjgs.core.input;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public final class __Keyboard implements Keyboard {
// NOTE THIS IS ONLY TO BE USED FOR GAMES, TO BE USED IN REGULAR TYPING, A KEY ADAPTER WILL WORK MUCH BETTER
static HashSet<Integer> keysDown;
public void init(final JPanel panel) {
keysDown = new HashSet<Integer>();
initWASD(panel);
initArrowKeys(panel);
initSpace(panel);
initEscape(panel);
initNumbers(panel);
initQ(panel);
initE(panel);
initR(panel);
initF(panel);
initZ(panel);
initX(panel);
initC(panel);
initV(panel);
initB(panel);
initG(panel);
initT(panel);
initM(panel);
}
// *************** INITIALIZATION *************************************************************************************************** //
private static void initWASD(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
// ---- ADD WASD ------------------------------------ //
final Action walkUp = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(W)) keysDown.add(W); } };
final Action walkDown = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(S)) keysDown.add(S); } };
final Action walkLeft = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(A)) keysDown.add(A); } };
final Action walkRight = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(D)) keysDown.add(D); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("W"), "walkUp");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("A"), "walkLeft");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("S"), "walkDown");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("D"), "walkRight");
panel.getActionMap().put("walkUp", walkUp);
panel.getActionMap().put("walkDown", walkDown);
panel.getActionMap().put("walkLeft", walkLeft);
panel.getActionMap().put("walkRight", walkRight);
// --- END ADD WASD --------------------------------- //
// ---- REMOVE WASD ----------------------------------//
final Action stopWalkUp = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(W)) keysDown.remove(W); } };
final Action stopWalkDown = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(S)) keysDown.remove(S); } };
final Action stopWalkLeft = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(A)) keysDown.remove(A); } };
final Action stopWalkRight = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(D)) keysDown.remove(D); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released W"), "stopWalkUp");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released A"), "stopWalkLeft");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released S"), "stopWalkDown");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released D"), "stopWalkRight");
panel.getActionMap().put("stopWalkUp", stopWalkUp);
panel.getActionMap().put("stopWalkDown", stopWalkDown);
panel.getActionMap().put("stopWalkLeft", stopWalkLeft);
panel.getActionMap().put("stopWalkRight", stopWalkRight);
// -- END REMOVE WASD ----------------------------------//
}
private static void initQ(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addQ = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Q)) keysDown.add(Q); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("Q"), "addQ");
panel.getActionMap().put("addQ", addQ);
final Action removeQ = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Q)) keysDown.remove(Q); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released Q"), "removeQ");
panel.getActionMap().put("removeQ", removeQ);
}
private static void initE(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addE = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(E)) keysDown.add(E); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("E"), "addE");
panel.getActionMap().put("addE", addE);
final Action removeE = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(E)) keysDown.remove(E); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released E"), "removeE");
panel.getActionMap().put("removeE", removeE);
}
private static void initR(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addR = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(R)) keysDown.add(R); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("R"), "addR");
panel.getActionMap().put("addR", addR);
final Action removeR = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(R)) keysDown.remove(R); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released R"), "removeR");
panel.getActionMap().put("removeR", removeR);
}
private static void initF(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addF = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(F)) keysDown.add(F); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("F"), "addF");
panel.getActionMap().put("addF", addF);
final Action removeF = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(F)) keysDown.remove(F); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released F"), "removeF");
panel.getActionMap().put("removeF", removeF);
}
private static void initSpace(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action space = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(SPACE)) keysDown.add(SPACE); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("SPACE"), "space");
panel.getActionMap().put("space", space);
final Action releasedSpace = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(SPACE)) keysDown.remove(SPACE); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released SPACE"), "releasedSpace");
panel.getActionMap().put("releasedSpace", releasedSpace);
}
private static void initEscape(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action escape = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(ESCAPE)) keysDown.add(ESCAPE); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
panel.getActionMap().put("escape", escape);
final Action releasedEscape = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(ESCAPE)) keysDown.remove(ESCAPE); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released ESCAPE"), "releasedEscape");
panel.getActionMap().put("releasedEscape", releasedEscape);
}
private static void initZ(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addZ = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Z)) keysDown.add(Z); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("Z"), "addZ");
panel.getActionMap().put("addZ", addZ);
final Action removeZ = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Z)) keysDown.remove(Z); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released Z"), "removeZ");
panel.getActionMap().put("removeZ", removeZ);
}
private static void initX(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addX = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(X)) keysDown.add(X); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("X"), "addX");
panel.getActionMap().put("addX", addX);
final Action removeX = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(X)) keysDown.remove(X); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released X"), "removeX");
panel.getActionMap().put("removeX", removeX);
}
private static void initC(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addC = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(C)) keysDown.add(C); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("C"), "addC");
panel.getActionMap().put("addC", addC);
final Action removeC = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(C)) keysDown.remove(C); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released C"), "removeC");
panel.getActionMap().put("removeC", removeC);
}
private static void initV(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addV = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(V)) keysDown.add(V); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("V"), "addV");
panel.getActionMap().put("addV", addV);
final Action removeV = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(V)) keysDown.remove(V); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released V"), "removeV");
panel.getActionMap().put("removeV", removeV);
}
private static void initB(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addB = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(B)) keysDown.add(B); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("B"), "addB");
panel.getActionMap().put("addB", addB);
final Action removeB = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(B)) keysDown.remove(B); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released B"), "removeB");
panel.getActionMap().put("removeB", removeB);
}
private static void initG(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addG = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(G)) keysDown.add(G); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("G"), "addG");
panel.getActionMap().put("addG", addG);
final Action removeG = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(G)) keysDown.remove(G); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released G"), "removeG");
panel.getActionMap().put("removeG", removeG);
}
private static void initT(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(T)) keysDown.add(T); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("T"), "addT");
panel.getActionMap().put("addT", addT);
final Action removeT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(T)) keysDown.remove(T); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released T"), "removeT");
panel.getActionMap().put("removeT", removeT);
}
private static void initM(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
final Action addM = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(M)) keysDown.add(M); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("M"), "addM");
panel.getActionMap().put("addM", addM);
final Action removeM = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(M)) keysDown.remove(M); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released M"), "removeM");
panel.getActionMap().put("removeM", removeM);
}
private static void initNumbers(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
// -------------------------------- ADDS --------------------------------------- //
final Action add1 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.ONE)) keysDown.add(Keyboard.ONE); } };
final Action add2 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.TWO)) keysDown.add(Keyboard.TWO); } };
final Action add3 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.THREE)) keysDown.add(Keyboard.THREE); } };
final Action add4 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.FOUR)) keysDown.add(Keyboard.FOUR); } };
final Action add5 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.FIVE)) keysDown.add(Keyboard.FIVE); } };
final Action add6 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.SIX)) keysDown.add(Keyboard.SIX); } };
final Action add7 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.SEVEN)) keysDown.add(Keyboard.SEVEN); } };
final Action add8 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.EIGHT)) keysDown.add(Keyboard.EIGHT); } };
final Action add9 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.NINE)) keysDown.add(Keyboard.NINE); } };
final Action add0 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(Keyboard.ZERO)) keysDown.add(Keyboard.ZERO); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("1"), "add1");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("2"), "add2");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("3"), "add3");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("4"), "add4");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("5"), "add5");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("6"), "add6");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("7"), "add7");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("8"), "add8");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("9"), "add9");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("0"), "add0");
panel.getActionMap().put("add1", add1);
panel.getActionMap().put("add2", add2);
panel.getActionMap().put("add3", add3);
panel.getActionMap().put("add4", add4);
panel.getActionMap().put("add5", add5);
panel.getActionMap().put("add6", add6);
panel.getActionMap().put("add7", add7);
panel.getActionMap().put("add8", add8);
panel.getActionMap().put("add9", add9);
panel.getActionMap().put("add0", add0);
// ------------------------------ RELEASES ------------------------------------- //
final Action release1 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.ONE)) keysDown.remove(Keyboard.ONE); } };
final Action release2 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.TWO)) keysDown.remove(Keyboard.TWO); } };
final Action release3 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.THREE)) keysDown.remove(Keyboard.THREE); } };
final Action release4 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.FOUR)) keysDown.remove(Keyboard.FOUR); } };
final Action release5 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.FIVE)) keysDown.remove(Keyboard.FIVE); } };
final Action release6 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.SIX)) keysDown.remove(Keyboard.SIX); } };
final Action release7 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.SEVEN)) keysDown.remove(Keyboard.SEVEN); } };
final Action release8 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.EIGHT)) keysDown.remove(Keyboard.EIGHT); } };
final Action release9 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.NINE)) keysDown.remove(Keyboard.NINE); } };
final Action release0 = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(Keyboard.ZERO)) keysDown.remove(Keyboard.ZERO); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 1"), "release1");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 2"), "release2");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 3"), "release3");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 4"), "release4");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 5"), "release5");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 6"), "release6");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 7"), "release7");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 8"), "release8");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 9"), "release9");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 0"), "release0");
panel.getActionMap().put("release1", release1);
panel.getActionMap().put("release2", release2);
panel.getActionMap().put("release3", release3);
panel.getActionMap().put("release4", release4);
panel.getActionMap().put("release5", release5);
panel.getActionMap().put("release6", release6);
panel.getActionMap().put("release7", release7);
panel.getActionMap().put("release8", release8);
panel.getActionMap().put("release9", release9);
panel.getActionMap().put("release0", release0);
}
private static void initArrowKeys(final JPanel panel) {
final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
// ---- ADD ARROW KEYS ------------------------------------ //
final Action ADD_UP = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(UP)) keysDown.add(UP); } };
final Action ADD_DOWN = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(DOWN)) keysDown.add(DOWN); } };
final Action ADD_LEFT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(LEFT)) keysDown.add(LEFT); } };
final Action ADD_RIGHT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (!keysDown.contains(RIGHT)) keysDown.add(RIGHT); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("UP"), "UP");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("LEFT"), "LEFT");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("DOWN"), "DOWN");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("RIGHT"), "RIGHT");
panel.getActionMap().put("UP", ADD_UP);
panel.getActionMap().put("LEFT", ADD_LEFT);
panel.getActionMap().put("DOWN", ADD_DOWN);
panel.getActionMap().put("RIGHT", ADD_RIGHT);
// --- END ADD ARROW KEYS --------------------------------- //
// ---- REMOVE ARROW KEYS ----------------------------------//
final Action REMOVE_UP = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(UP)) keysDown.remove(UP); } };
final Action REMOVE_DOWN = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(DOWN)) keysDown.remove(DOWN); } };
final Action REMOVE_LEFT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(LEFT)) keysDown.remove(LEFT); } };
final Action REMOVE_RIGHT = new AbstractAction() { @Override
public void actionPerformed(final ActionEvent e) { if (keysDown.contains(RIGHT)) keysDown.remove(RIGHT); } };
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released UP"), "RELEASEUP");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released LEFT"), "RELEASELEFT");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released DOWN"), "RELEASEDOWN");
panel.getInputMap(when).put(KeyStroke.getKeyStroke("released RIGHT"), "RELEASERIGHT");
panel.getActionMap().put("RELEASEUP", REMOVE_UP);
panel.getActionMap().put("RELEASEDOWN", REMOVE_DOWN);
panel.getActionMap().put("RELEASELEFT", REMOVE_LEFT);
panel.getActionMap().put("RELEASERIGHT", REMOVE_RIGHT);
// -- END REMOVE ARROW KEYS ----------------------------------//
}
// ********************************************************************************************************************************** //
}

View File

@@ -0,0 +1,37 @@
package sjgs.core.jython;
import static sjgs.utils.Utils.readTextFileAsString;
import org.python.util.PythonInterpreter;
import sjgs.graphics.ui.InventorySystem;
import sjgs.utils.pyutils.PyUtils;
public final class Jython {
public static final PythonInterpreter pi = new PythonInterpreter();
public void __init__() {
//final long then = System.nanoTime();
initialize();
imports();
loadMethodsIntoInterpretor();
createPyFuncs();
//pi.exec("print " + "'Jython / Python initialized --- Time taken: " + Utils.df.format((System.nanoTime() - then) / Utils.second) + " seconds'");
}
// INITIALIZES INTERP WITH SYSTEM INFORMATION
private static void initialize() { PyUtils.initializePySystem(); }
// IMPORTS ALL NECESSARY JAVA CLASSES FOR INTERP
private static void imports() { pi.exec(readTextFileAsString("/sjgs/core/jython/engine_imports.py")); }
// RUNS THE MODULES THROUGH THE INTERP TO LOAD THE NAMES OF FUNCS
private static void loadMethodsIntoInterpretor() {
pi.exec(readTextFileAsString("/sjgs/graphics/ui/__engine_inventory.py"));
pi.exec(readTextFileAsString("/sjgs/base_objects/mob_ai/mob_travel_around_rectangle.py"));
}
private static void createPyFuncs() {
InventorySystem.__engine_init_pyfuncs();
}
}

View File

@@ -0,0 +1,46 @@
### Java standard library imports
from java.lang.Math import *
from java.lang import Integer, Float, Double
### engine imports
from sjgs.core import Engine as Engine
from sjgs.core import Camera as Camera
from sjgs.core import Handler as Handler
import sjgs.core.input.Mouse as Mouse
import sjgs.enums.Facing as Facing
import sjgs.enums.TickRate as TickRate
import sjgs.enums.Type as Type
from sjgs.graphics.Colors import *
from sjgs.graphics import Colors
from sjgs.base_objects import *
import sjgs.sound.SoundPlayer as SoundPlayer
import sjgs.physics.Physics as Physics
import sjgs.physics.structs.BoundingBox as BoundingBox
import sjgs.physics.structs.CollisionResponse as CollisionResponse
from sjgs.utils.Utils import *
import sjgs.utils.Utils
import sjgs.utils.tools.Timer as Timer
import sjgs.utils.encryption.StrongCaesarCipher
import sjgs.utils.data_structures.Stack as Stack
from sjgs.utils.data_structures.shapes import *
from sjgs.utils.data_structures.vectors import *
from sjgs.utils.data_structures.gaming import *
import sjgs.utils.data_structures.shapes.Rectangle as Rectangle
import sjgs.utils.data_structures.vectors.Point2f as Point2f
import sjgs.utils.data_structures.vectors.SimplePoint as SimplePoint
import sjgs.utils.data_structures.shapes.Line as Line
from sjgs.graphics.ui.InventorySystemSlot import EMPTY
import sjgs.graphics.ui.InventorySystem as InventorySystem
import sjgs.graphics.ui.InventorySystemSlot as InventorySystemSlot

7
src/sjgs/enums/Facing.java Executable file
View File

@@ -0,0 +1,7 @@
package sjgs.enums;
public enum Facing {
LEFT, RIGHT, ABOVE, BELOW, NONE
}

7
src/sjgs/enums/TickRate.java Executable file
View File

@@ -0,0 +1,7 @@
package sjgs.enums;
public enum TickRate {
_60, _120
}

7
src/sjgs/enums/Type.java Executable file
View File

@@ -0,0 +1,7 @@
package sjgs.enums;
public enum Type {
BULLET, HARD_OBJECT, MOB, PLAYER, SOFT_OBJECT, NONE
}

100
src/sjgs/graphics/Animation.java Executable file
View File

@@ -0,0 +1,100 @@
package sjgs.graphics;
import static sjgs.utils.Utils.invertImage;
import static sjgs.utils.Utils.reverseImage;
import static sjgs.utils.Utils.rotateImageLeft;
import static sjgs.utils.Utils.rotateImageRight;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import sjgs.physics.structs.BoundingBox;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
public class Animation {
/** @param speed : the number of ticks needed to advance each frame
* @param timer : the index of the current tick amount
* @param currentFrame : the index of the currently displaying image */
private int speed;
public int currentFrame, timer;
private final BufferedImage[] images;
public Animation(final int speed, final BufferedImage... args) {
this.speed = speed; images = new BufferedImage[args.length];
for (int i = 0; i < args.length; i++) images[i] = args[i];
}
public Animation(final int speed, final Stack<BufferedImage> args) {
this.speed = speed; images = new BufferedImage[args.size()];
for (int i = 0; i < args.size(); i++) images[i] = args.get(i);
}
public Animation(final int speed, final Image... args) {
this.speed = speed; images = new BufferedImage[args.length];
for (int i = 0; i < args.length; i++) images[i] = Utils.imageToBufferedImage(args[i]);
}
public void runAnimation() { if (++timer > speed) nextFrame(); }
public void playAnimationOnce() {
if (currentFrame != images.length - 1 && ++timer > speed) nextFrame();
}
public void nextFrame() {
if (++currentFrame >= images.length) restartAnimation();
else timer = 0;
}
public void restartAnimation() { currentFrame = timer = 0; }
public void drawAnimation(final Graphics2D g2d, final Point2f p){ g2d.drawImage(images[currentFrame], (int) p.x, (int) p.y, null); }
public void drawAnimation(final Graphics2D g2d, final SimplePoint p){ g2d.drawImage(images[currentFrame], p.x, p.y, null); }
public void drawAnimation(final Graphics2D g2d, final Point p){ g2d.drawImage(images[currentFrame], p.x, p.y, null); }
public void drawAnimation(final Graphics2D g2d, final double x, final double y) { g2d.drawImage(images[currentFrame], (int) x, (int) y, null); }
public void drawAnimation(final Graphics2D g2d, final float x, final float y) { g2d.drawImage(images[currentFrame], (int) x, (int) y, null); }
public void drawAnimation(final Graphics2D g2d, final int x, final int y) { g2d.drawImage(images[currentFrame], x, y, null); }
public void drawAnimation(final Graphics2D g2d, final Rectangle r) { g2d.drawImage(images[currentFrame], (int)r.getPos().x, (int)r.getPos().y, null); }
public void drawAnimation(final Graphics2D g2d, final BoundingBox b) { g2d.drawImage(images[currentFrame], (int)b.getX(), (int)b.getY(), null); }
public BufferedImage getLastFrame() { return images[images.length - 1]; }
public int size() { return images.length; }
public BufferedImage[] getImages() { return images; }
public void setSpeed(final int speed) { this.speed = speed; }
@Override
public Animation clone() { return new Animation(speed, images); }
public Animation getReverse() {
final BufferedImage[] sprites = new BufferedImage[images.length];
for(int i = 0; i < images.length; i++) sprites[i] = reverseImage(images[i]);
return new Animation(speed, sprites);
}
public Animation getInverted() {
final BufferedImage[] sprites = new BufferedImage[images.length];
for(int i = 0; i < images.length; i++) sprites[i] = invertImage(images[i]);
return new Animation(speed, sprites);
}
public Animation getRotatedRight() {
final BufferedImage[] sprites = new BufferedImage[images.length];
for(int i = 0; i < images.length; i++) sprites[i] = rotateImageRight(images[i]);
return new Animation(speed, sprites);
}
public Animation getRotatedLeft() {
final BufferedImage[] sprites = new BufferedImage[images.length];
for(int i = 0; i < images.length; i++) sprites[i] = rotateImageLeft(images[i]);
return new Animation(speed, sprites);
}
}

24
src/sjgs/graphics/Colors.java Executable file
View File

@@ -0,0 +1,24 @@
package sjgs.graphics;
import java.awt.Color;
import sjgs.utils.Utils;
public class Colors {
public static final Color red = Color.red,
green = Color.green,
black = Color.black,
white = Color.white,
blue = Color.blue,
orange = Color.orange,
purple = Color.magenta,
pink = Color.pink,
yellow = Color.yellow,
gray = Color.gray,
darkGray = Color.darkGray,
cyan = Color.cyan;
public static final Color pastelRed = new Color(255, 80, 50),
voidColor = Utils.voidColor;
}

View File

@@ -0,0 +1,55 @@
package sjgs.graphics.backgrounds;
import static sjgs.utils.Utils.exit;
import static sjgs.utils.Utils.print;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import sjgs.core.Camera;
import sjgs.utils.data_structures.vectors.SimplePoint;
public class Background {
private final BufferedImage image;
private final float scrollX, scrollY;
private final SimplePoint start;
private final boolean loop;
// NOTE a background that shouldn't move with that player, that is to be completely static, should have a scrollPercentage of 0
public Background(final BufferedImage image, final float x, final float y, final float scrollPercentageX, final float scrollPercentageY, final boolean shouldLoop) {
this.image = image;
if(scrollPercentageX > 100 || scrollPercentageX < 0 || scrollPercentageY > 100 || scrollPercentageY < 0) { print("Incorrect scroll %"); exit(); }
scrollX = scrollPercentageX / 100f;
scrollY = scrollPercentageY / 100f;
start = new SimplePoint((int)x, (int)y);
loop = shouldLoop;
}
public void render(final Graphics2D g2d, final Camera camera, @SuppressWarnings("unused") final double scaleFactor) {
final int x = start.x + (int)(camera.getX() * scrollX);
final int y = start.y + (int)(camera.getY() * scrollY);
g2d.drawImage(image, x, y, null);
// NOTE THIS IS CURRENTLY BROKEN:
// int screenCovered = x;
// start with nothing drawn, at the camera X
// continue to redraw the background until the full screen is covered
// do {
// // set clip if it would go over
// if(screenCovered + image.getWidth() > x + camera.getWidth() / scaleFactor) {
// final int lengthNeeded = (int) ((x + camera.getWidth() / scaleFactor) - screenCovered);
// final Graphics2D copy = (Graphics2D) g2d.create();
// copy.setClip(screenCovered, y, lengthNeeded, image.getHeight());
// copy.drawImage(image, screenCovered, y, null);
// } else g2d.drawImage(image, screenCovered, y, null);
//
//
// screenCovered += image.getWidth();
// } while(loop && screenCovered < x + camera.getWidth() / scaleFactor);
}
}

View File

@@ -0,0 +1,17 @@
package sjgs.graphics.backgrounds;
import java.awt.Graphics2D;
import sjgs.core.Camera;
public class ParallaxBackground {
private final Background[] backgrounds;
// sort these later so no need to worry about order
public ParallaxBackground(final Background ...args) { backgrounds = args; }
public void render(final Graphics2D g2d, final Camera camera, final double scaleFactor) {
for(final Background b : backgrounds) b.render(g2d, camera, scaleFactor);
}
}

View File

@@ -0,0 +1,32 @@
package sjgs.graphics.lighting;
import java.awt.Graphics2D;
import sjgs.core.Camera;
import sjgs.core.Engine;
import sjgs.utils.data_structures.vectors.Point2f;
public abstract class Light {
protected final float intensity;
protected final Point2f center;
public Light(final float x, final float y, final float intensity) {
this.intensity = intensity;
center = new Point2f(x, y);
}
abstract void update(Graphics2D lightsRenderer, Camera camera, Engine engine);
public void tick() { }
public void destroy() { }
public float getX() { return center.x; }
public float getY() { return center.y; }
public Point2f getCenter() { return center; }
public float getIntensity() { return intensity; }
public void setX(final float x) { center.setX(x); }
public void setY(final float y) { center.setY(y); }
public void setLocation(final float x, final float y) { setX(x); setY(y); }
}

View File

@@ -0,0 +1,31 @@
package sjgs.graphics.lighting;
import static sjgs.graphics.Colors.black;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ConcurrentModificationException;
import sjgs.core.Camera;
import sjgs.core.Engine;
import sjgs.core.Handler;
public class LightsRenderer {
public static void render(final Graphics2D g2d, final Camera camera, final Engine engine){
final BufferedImage lightmap = new BufferedImage((int) (engine.getWidth() * 1.1), (int) (engine.getHeight() * 1.1), BufferedImage.TYPE_INT_ARGB);
final Graphics2D lightsRenderer = (Graphics2D) lightmap.getGraphics();
lightsRenderer.setColor(black);
lightsRenderer.fillRect(0, 0, lightmap.getWidth(), lightmap.getHeight());
lightsRenderer.setComposite(AlphaComposite.DstOut);
try{ for(final Light light : Handler.lights) light.update(lightsRenderer, camera, engine); }
catch (final ConcurrentModificationException cme){ /* happens when adding / removing lights, ignore */ }
lightsRenderer.dispose();
g2d.drawImage(lightmap, 0, 0, null);
}
}

View File

@@ -0,0 +1,44 @@
package sjgs.graphics.lighting;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;
import sjgs.core.Camera;
import sjgs.core.Engine;
import sjgs.core.Handler;
public class RadialLight extends Light {
private final BufferedImage lightImage;
protected final float radius;
public RadialLight(final float x, final float y, final float radius, final float intensity) {
super(x, y, intensity);
this.radius = radius;
/******** DRAW THE LIGHT IMAGE **********************************************************/
final Color[] color = { new Color(0, 0, 0, intensity), new Color(0, 0, 0, 0) };
lightImage = new BufferedImage((int)(radius*2), (int)(radius*2), BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = (Graphics2D) lightImage.getGraphics();
final float[] distance = { 0f, 1f };
g2d.setPaint(new RadialGradientPaint(new Point((int)radius, (int)radius), radius, distance, color));
g2d.fillOval(0, 0, (int)(radius*2), (int)(radius*2));
g2d.dispose();
/***************************************************************************************/
Handler.lights.add(this);
}
@Override
public void update(final Graphics2D lightsRenderer, final Camera camera, final Engine engine){
lightsRenderer.drawImage(lightImage.getScaledInstance((int)(lightImage.getWidth() * engine.getScaleFactor()),
(int)(lightImage.getHeight() * engine.getScaleFactor()), Image.SCALE_FAST),
(int)((center.x + camera.getX()) * engine.getScaleFactor()) - (int)(radius * engine.getScaleFactor()),
(int)((center.y + camera.getY()) * engine.getScaleFactor()) - (int)(radius * engine.getScaleFactor()), null);
}
public float getRadius() { return radius; }
}

View File

@@ -0,0 +1,86 @@
package sjgs.graphics.ui;
import static sjgs.core.jython.Jython.pi;
import static sjgs.utils.pyutils.PyUtils.One;
import static sjgs.utils.pyutils.PyUtils.True;
import static sjgs.utils.pyutils.PyUtils.int2py;
import static sjgs.utils.pyutils.PyUtils.java2py;
import java.awt.Graphics2D;
import org.python.core.PyFunction;
import org.python.core.PyObject;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Rectangle;
public abstract class InventorySystem {
protected final Rectangle INVENTORY_BOUNDS;
protected Stack<InventorySystemSlot> slots;
protected final PyObject self;
public InventorySystem(final float x, final float y, final float width, final float height) {
INVENTORY_BOUNDS = new Rectangle(x, y, width, height);
slots = new Stack<InventorySystemSlot>();
self = java2py(this);
init();
}
public abstract void init();
public abstract void tick();
public abstract void render(Graphics2D g2d);
public abstract void destroy();
public abstract void onLeftClick(); // NOTE: you must put 'baseOnLeftClick()' in this method!
public abstract void swapSlots(final InventorySystemSlot i, final InventorySystemSlot e);
public abstract boolean isStackable(int itemId);
public abstract int getStackableAmount(int itemId);
public void addItem(final int itemId, final int quantity) { addItem.__call__(int2py(itemId), int2py(quantity), self); }
public void addItem(final int itemId) { addItem.__call__(int2py(itemId), One, self); }
public void removeItemWithSlotNumber(final int itemId, final int quantity, final int slotNumber) {
removeItemWithSlotNumber.__call__(int2py(itemId), int2py(quantity), int2py(slotNumber), self);
}
public void removeItem(final int itemId, final int quantity) { removeItem.__call__(int2py(itemId), int2py(quantity), self); }
public void removeItem(final int itemId) { removeItem.__call__(int2py(itemId), One, self); }
public void resetPositions() { for(final InventorySystemSlot i : slots) if(!i.mouseClicked) i.resetPosition(); }
// swaps the values from this slot to the other slot and vice versa
public void baseSwapSlots(final InventorySystemSlot i, final InventorySystemSlot e) {
final int tempId = i.itemId;
final int tempQ = i.quantity;
i.itemId = e.itemId;
i.quantity = e.quantity;
e.itemId = tempId;
e.quantity = tempQ;
e.mouseClicked = false;
if(i.isEmpty()) i.mouseClicked = false;
resetPositions();
}
// NOTE YOU MUST USE THIS!
public void baseOnLeftClick() { baseOnLeftClick.__call__(self); }
public void clearInventory() { for (final InventorySystemSlot i : slots) i.reset(); }
public void resetMouseClicked() { for (final InventorySystemSlot i : slots) i.mouseClicked = false; }
public boolean isFull(final int itemId, final int quantity) { return isFull.__call__(int2py(itemId), int2py(quantity), self) == True; }
public boolean contains(final int itemId, final int quantity) { return contains.__call__(int2py(itemId), int2py(quantity), self) == True; }
public Stack<InventorySystemSlot> getSlots() { return slots; }
public float getWidth() { return INVENTORY_BOUNDS.getWidth(); }
public float getHeight() { return INVENTORY_BOUNDS.getHeight(); }
public float getX() { return INVENTORY_BOUNDS.getX(); }
public float getY() { return INVENTORY_BOUNDS.getY(); }
public Rectangle getBounds() { return INVENTORY_BOUNDS; }
//=======================================================================================================//
private static PyFunction isFull, contains, baseOnLeftClick, addItem, removeItem, removeItemWithSlotNumber;
public static void __engine_init_pyfuncs() {
isFull = pi.get("__engine_inventory_is_full", PyFunction.class);
contains = pi.get("__engine_inventory_contains", PyFunction.class);
baseOnLeftClick = pi.get("__engine_onLeftClick", PyFunction.class);
addItem = pi.get("__engine_add_item", PyFunction.class);
removeItem = pi.get("__engine_remove_item", PyFunction.class);
removeItemWithSlotNumber = pi.get("__engine_remove_item_with_slot_number", PyFunction.class);
}
}

View File

@@ -0,0 +1,55 @@
package sjgs.graphics.ui;
import java.awt.Graphics2D;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
public abstract class InventorySystemSlot {
public static final int EMPTY = 0;
public final Point2f startingPoint;
public final Rectangle bounds;
public boolean mouseClicked;
public int itemId, quantity, slotNumber;
public InventorySystemSlot(final float x, final float y, final float w, final float h, final int slotNumber) {
bounds = new Rectangle(x, y, w, h);
this.slotNumber = slotNumber;
startingPoint = new Point2f(x, y);
init();
}
public abstract void init();
public abstract void tick();
public abstract void render(Graphics2D g2d);
public abstract void reset();
protected void baseReset() {
itemId = quantity = 0;
bounds.setX(startingPoint.x);
bounds.setY(startingPoint.y);
}
public void resetPosition() {
bounds.setX(startingPoint.x);
bounds.setY(startingPoint.y);
}
public void setStartingPoint(final Point2f p) { startingPoint.setLocation(p.getX(), p.getY()); }
public void setStartingPoint(final float x, final float y) { startingPoint.setLocation(x, y); }
public float getStartingX() { return startingPoint.getX(); }
public float getStartingY() { return startingPoint.getY(); }
public float getX() { return bounds.getX(); }
public float getY() { return bounds.getY(); }
public float getWidth() { return bounds.getWidth(); }
public float getHeight() { return bounds.getHeight(); }
public float getHalfHeight() { return getHeight() * 0.5f; }
public float getHalfWidth() { return getWidth() * 0.5f; }
public boolean isEmpty() { return itemId == 0 && quantity == 0; }
public void setLocation(final float x, final float y) { bounds.setLocation(x, y); }
}

15
src/sjgs/graphics/ui/Menu.java Executable file
View File

@@ -0,0 +1,15 @@
package sjgs.graphics.ui;
import java.awt.Graphics2D;
import sjgs.utils.data_structures.Stack;
public abstract class Menu {
public Stack<MenuButton> buttons;
public abstract void init();
public abstract void tick();
public abstract void render(Graphics2D g2d);
public abstract void destroy();
}

View File

@@ -0,0 +1,40 @@
package sjgs.graphics.ui;
import java.awt.Graphics2D;
import sjgs.core.input.Mouse;
import sjgs.graphics.Animation;
import sjgs.utils.data_structures.shapes.Rectangle;
/** @class: USAGE - Create a Menu class to hold a stack of your menu buttons, along
* with any other functions you deem to need. Then, in your also created MouseInput class,
* (which should be extending Mouse...), loop through all your menu buttons on each click.
*
* Render your list of menu buttons in your Engine's render method. */
public abstract class MenuButton {
private final Rectangle bounds;
public final Animation animation;
public MenuButton(final float x, final float y, final float w, final float h, final Animation animation) {
bounds = new Rectangle(x, y, w, h);
this.animation = animation;
}
public MenuButton(final float x, final float y, final float w, final float h) {
bounds = new Rectangle(x, y, w, h);
animation = null;
}
public abstract void tick();
public abstract void render(Graphics2D g2d);
public abstract void onClick();
public abstract void onHover();
public float getX() { return bounds.getX(); }
public float getY() { return bounds.getY(); }
public float getWidth() { return bounds.getWidth(); }
public float getHeight() { return bounds.getHeight(); }
public Rectangle getBounds() { return bounds; }
public boolean containsMouse() { return bounds.contains(Mouse.getX(), Mouse.getY()); }
}

View File

@@ -0,0 +1,100 @@
def __engine_onLeftClick(inventory):
# SLOT SWAPPING!
# @param i: The slot you are currently holding
# @param e: The slot you are trying to click on
for i in inventory.getSlots():
# FILTER UNTIL I == THE SLOT YOU'RE HOLDING
if not i.mouseClicked: continue
for e in inventory.getSlots():
# IF THE SECOND SLOT CONTAINS THE MOUSE POINTER, SWAP!
if i != e and e.bounds.contains(Mouse.getClickX(), Mouse.getClickY()):
inventory.swap(i, e)
return # NOTE: YOU MUST RETURN HERE OR IT WONT WORK!
# GRAPHICAL SLOT MOVING!
for i in inventory.getSlots():
if i.isEmpty(): continue
# SET MOUSELCICKED TO TRUE
if not i.mouseClicked and i.bounds.contains(Mouse.getClickX(), Mouse.getClickY()):
inventory.resetMouseClicked() # ONLY WANT 1 ITEM TO BE MOUSE CLICKED AT A TIME
i.mouseClicked = True
break
# IF HOLDING AN ITEM, AND CLICK OUTSIDE OF THE INVENTORY BOUNDS --- DROP
elif i.mouseClicked and not inventory.getBounds().contains(Mouse.getClickX(), Mouse.getClickY()):
i.mouseClicked = False
break
# ELSE IF YOU JUST CLICKED SOMEWHERE IN THE INVEN WITHOUT BEING ON A SLOT LIKE AN IDIOT
else: i.mouseClicked = False
# FIXES BUGS -- ALWAYS RUN THIS
inventory.resetPositions()
def __engine_add_item(itemId, quantity, inventory):
# DONT ADD ITEM IF IT IS THE NULL PLACEHOLDER ITEM
if itemId == EMPTY: return
# CANT ADD ITEM IF THE INVEN IS FULL
if __engine_inventory_is_full(itemId, quantity, inventory): return
# IF THE ITEM IS STACKABLE, RUN THIS LOOP
if inventory.isStackable(itemId):
for i in inventory.getSlots():
if i.itemId == itemId and (i.quantity + quantity) <= inventory.getStackableAmount(itemId):
i.mouseClicked = False
i.quantity += quantity
inventory.resetPositions()
return
# IF THE ITEM IS NOT STACKABLE, OR FAILED THE ABOVE LOOP, RUN THIS LOOP
for i in inventory.getSlots():
if(i.isEmpty()):
i.mouseClicked = False
i.itemId = itemId
i.quantity = quantity
inventory.resetPositions()
return
def __engine_remove_item(itemId, quantity, inventory):
for slot in inventory.getSlots():
if slot.itemId == itemId:
temp = slot.quantity
slot.quantity -= quantity
quantity -= temp
slot.mouseClicked = False
if slot.quantity <= 0: slot.reset()
# if the amount of things to remove has been satisfied
if quantity <= 0: break
inventory.resetPositions()
def __engine_remove_item_with_slot_number(itemId, quantity, slotNumber):
for i in inventory.getSlots():
if i.slotNumber == slotNumber:
if i.itemId == itemId:
i.quantity -= quantity
i.mouseClicked = False
if i.quantity <= 0: slot.reset()
break
inventory.resetPositions()
def __engine_inventory_is_full(itemId, quantity, inventory):
if inventory.isStackable(itemId): # IF ITEM IS STACKABLE, RUN THIS LOOP
for slot in inventory.getSlots():
# CHECK IF THE (QUANTITY + THE ADDED QUANTITY) IS <= THE MAX QUANTITY
WOULD_BE_QUANTITY = slot.quantity + quantity
if slot.itemId == itemId and WOULD_BE_QUANTITY <= inventory.getStackableAmount(itemId): return False
# IF IT IS NOT STACKABLE, OR THE ITEM FAILED THE FIRST LOOP, RUN THIS LOOP
for slot in inventory.getSlots():
if slot.itemId == 0: return False
# IF NONE OF THE ABOVE CONDITIONS COULD BE MET, THE INVENTORY MUST BE FULL
return True
def __engine_inventory_contains(itemId, quantity, inventory):
count = 0
for slot in inventory.getSlots():
if slot.itemId == itemId: count += slot.quantity
return count >= quantity

52
src/sjgs/physics/Physics.java Executable file
View File

@@ -0,0 +1,52 @@
package sjgs.physics;
import sjgs.base_objects.GameObject;
import sjgs.base_objects.HardObject;
import sjgs.enums.TickRate;
import sjgs.enums.Type;
import sjgs.physics.structs.CollisionResponse;
import sjgs.utils.data_structures.Stack;
public final class Physics {
public void updatePosition(final GameObject obj) { __UpdatePosition.updatePosition(obj); }
public void applyFriction(final GameObject obj, final float friction_decrement) { __Friction.applyFriction(obj, friction_decrement); }
public CollisionResponse collision(final GameObject obj, final Type ...args) { return __Collision.collision(obj, args); }
public void applyGravity(final GameObject obj, final float gravity) { __Gravity.applyGravity(obj, gravity); }
public void calcGroundedAndOnSlope(final GameObject obj, final Stack<HardObject> collided_hard_objects) { __Calculators.calcGroundedAndOnSlope(obj, collided_hard_objects); }
public CollisionResponse getCollidedObjects(final GameObject obj) { return __Collision.getCollidedObjects(obj); }
public static void init(final float user_supplied_default_gravity, final float user_supplied_terminal_velocity, final float user_supplied_default_friction) {
__Gravity.setDefaultGravity(user_supplied_default_gravity);
__Gravity.setTerminalVelocity(user_supplied_terminal_velocity);
__Friction.setDefaultFriction(user_supplied_default_friction);
}
public void init() {
final float default_gravity = 0.75f, default_terminal_velocity = 10f, default_friction = 0.1f;
__Gravity.setDefaultGravity(default_gravity);
__Gravity.setTerminalVelocity(default_terminal_velocity);
__Friction.setDefaultFriction(default_friction);
TPS = TickRate._60;
}
private static TickRate TPS;
static TickRate getTickRate() { return TPS; }
public void setDoubleTickRate() { TPS = TickRate._120; }
public static void setUpHillFrictionMultiplier(final float f) { __Friction.setOnSlopeFrictionMultiplier(f); }
public static void disableGravity() { __Gravity.disableGravity(); }
public static void enableGravity() { __Gravity.enableGravity(); }
public static void toggleGravity() { __Gravity.toggleGravity(); }
public static void disableFriction() { __Friction.disableFriction(); }
public static void enableFriction() { __Friction.enableFriction(); }
public static void toggleFriction() { __Friction.toggleFriction(); }
public static float getTerminalVelocity() { return __Gravity.getTerminalVelocity(); }
public static float getDefaultGravity() { return __Gravity.getDefaultGravity(); }
public static float getDefaultFriction() { return __Friction.getDefaultFriction(); }
}

View File

@@ -0,0 +1,42 @@
package sjgs.physics;
import static sjgs.utils.Utils.calcMostIntersecting;
import sjgs.base_objects.Bullet;
import sjgs.base_objects.GameObject;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.shapes.Rectangle;
class __BulletPhysics {
static void calcBulletCollision(final Bullet obj, final GameObject g) {
final float velX = obj.getVelX();
final float velY = obj.getVelY();
final float ep = obj.getElasticity();
switch(Utils.intDirection(g.getCenter(), obj.getCenter())) {
case 1: obj.setVelY(-velY * ep); obj.setY(g.getY() - obj.getHeight()); break;
case 3: obj.setVelX(-velX * ep); obj.setX(g.getX() + g.getWidth() - 1); break; // NOTE THE - 1 is VERY IMPORTANT!
case 5: obj.setVelY(-velY * ep); obj.setY(g.getY() + g.getHeight()); break;
case 7: obj.setVelX(-velX * ep); obj.setX(g.getX() - obj.getWidth()); break;
case 2: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getBottomBounds(), obj.getLeftBounds());
if(r == obj.getBottomBounds()) { obj.setVelY(-velY * ep); obj.setY(g.getY() - obj.getHeight()); }
else if(r == obj.getLeftBounds()) { obj.setVelX(-velX * ep); obj.setX(g.getX() + g.getWidth()); }
break; }
case 4: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getTopBounds(), obj.getLeftBounds());
if(r == obj.getTopBounds()) { obj.setVelY(-velY * ep); obj.setY(g.getY() + g.getHeight()); }
else if(r == obj.getLeftBounds()) { obj.setVelX(-velX * ep); obj.setX(g.getX() + g.getWidth()); }
break; }
case 6: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getTopBounds(), obj.getRightBounds());
if(r == obj.getTopBounds()) { obj.setVelY(-velY * ep); obj.setY(g.getY() + g.getHeight()); }
else if(r == obj.getRightBounds()) { obj.setVelX(-velX * ep); obj.setX(g.getX() - obj.getWidth()); }
break; }
case 8: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getBottomBounds(), obj.getRightBounds());
if(r == obj.getBottomBounds()) { obj.setVelY(-velY * ep); obj.setY(g.getY() - obj.getHeight()); }
else if(r == obj.getRightBounds()) { obj.setVelX(-velX * ep); obj.setX(g.getX() - obj.getWidth()); }
break; }
}
}
}

View File

@@ -0,0 +1,38 @@
package sjgs.physics;
import static sjgs.utils.Utils.cinch;
import sjgs.base_objects.BaseTile;
import sjgs.base_objects.GameObject;
import sjgs.base_objects.HardObject;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.vectors.SimplePoint;
class __Calculators {
static void calcGroundedAndOnSlope(final GameObject obj, final Stack<HardObject> collided_hard_objects) {
obj.setGrounded(false); obj.setOnSlope(false);
for (final HardObject ho : collided_hard_objects) {
if (ho.getFullBounds().intersects(obj.getBottomBounds())) {
obj.setGrounded(true);
if (ho instanceof BaseTile) {
final BaseTile t = (BaseTile) ho;
if(t.angled()) {
obj.setOnSlope(true);
for(final SimplePoint p : t.getSlopePoints())
if(obj.getFullBounds().contains(p))
obj.setY(p.y - obj.getHeight());
}
}
}
}
if(obj.getGrounded() && !obj.getOnSlope()) obj.setVelY(cinch(obj.getVelY(), 0));
}
// static float calcMomentum(BoundingBox bounds) {
// return (bounds.getV + bounds.getVelY()) / sqrtOf2;
// }
}

View File

@@ -0,0 +1,76 @@
package sjgs.physics;
import sjgs.base_objects.GameObject;
import sjgs.core.Handler;
import sjgs.enums.Type;
import sjgs.physics.structs.CollisionResponse;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Rectangle;
class __Collision {
/** @class Collision:
*
* GOALS:
* 1: Find all objects that are near the given bounds by searching through the
* Handler's quad trees.
* 2: Find all objects that actually intersect with said bounds, after narrowing down the search list, (1).
* 3: Provide default collision methods for use with this collision response, that can be used by the
* user if he or she chooses. If not, the user can implement his or her own collision methods, using
* the collection of objects that has been found to be collided.
*/
/** @param bounds: the bounding box being passed in from the object
* @param args: The different kinds of objects that hard collision SHOULD be
* ran against. */
public static CollisionResponse collision(final GameObject obj, final Type ...args) {
final CollisionResponse all_collided_objects = getCollidedObjects(obj);
final Stack<Type> types = new Stack<Type>();
for(final Type i : args) types.push(i);
if(types.size() > 0) {
if(types.contains(Type.HARD_OBJECT)) __GeneralCollision.default_hard_collision_against_hard_objects(obj, all_collided_objects.collided_hard_objects);
if(types.contains(Type.MOB)) __GeneralCollision.default_hard_collision_against_mobs(obj, all_collided_objects.collided_mobs);
if(types.contains(Type.SOFT_OBJECT)) __GeneralCollision.default_hard_collision_against_soft_objects(obj, all_collided_objects.collided_soft_objects);
if(types.contains(Type.BULLET)) __GeneralCollision.default_hard_collision_against_bullets(obj, all_collided_objects.collided_bullets);
if(types.contains(Type.PLAYER)) __GeneralCollision.default_hard_collision_against_players(obj, all_collided_objects.collided_players);
} else __GeneralCollision.default_hard_collision_against_hard_objects(obj, all_collided_objects.collided_hard_objects);
return all_collided_objects;
}
/** @getCollidedObjects: searches through the quad trees to find all objects around the bound's area.
* Then creates a collision response containing stacks of all objects that do
* indeed intersect with the bounding box. */
static CollisionResponse getCollidedObjects(final GameObject obj) {
/** @param OFFSET_MULTIPLIER: small mult here helps the quad tree search, fixes rare bugs */
final float OFFSET_MULTIPLIER = 1.25f;
/** @param OFFSET: Restricting the tree offset here as to prevent very small objects from not getting their collided objects */
final float OFFSET = Utils.restrict(Utils.biggest(obj.getWidth(), obj.getHeight()) * OFFSET_MULTIPLIER, 5f);
final float x = obj.getCenterX();
final float y = obj.getCenterY();
final Stack<GameObject> hard_objects = new Stack<>();
final Stack<GameObject> soft_objects = new Stack<>();
final Stack<GameObject> bullets = new Stack<>();
final Stack<GameObject> mobs = new Stack<>();
final Rectangle r = obj.getFullBounds();
for(final GameObject g : Handler.mobile_hard_objects) if(r.intersects(g.getFullBounds())) hard_objects.push(g);
hard_objects.combine(Handler.stationary_hard_objects.search(x + -OFFSET, y + -OFFSET, x + OFFSET, y + OFFSET));
for(final GameObject g : Handler.mobile_soft_objects) if(r.intersects(g.getFullBounds())) soft_objects.push(g);
soft_objects.combine(Handler.stationary_soft_objects.search(x + -OFFSET, y + -OFFSET, x + OFFSET, y + OFFSET));
for(final GameObject g : Handler.bullets) if(r.intersects(g.getFullBounds())) bullets.push(g);
for(final GameObject g : Handler.mobs) if(r.intersects(g.getFullBounds())) mobs.push(g);
return new CollisionResponse(obj, hard_objects, mobs, bullets, soft_objects);
}
}

View File

@@ -0,0 +1,54 @@
package sjgs.physics;
import static sjgs.utils.Utils.abs;
import sjgs.base_objects.GameObject;
class __Friction {
private static boolean apply_friction = true;
static float onSlopeFrictionMultiplier = 2f;
private static float DEFAULT_FRICTION;
static void applyFriction(final GameObject obj) { applyFriction(obj, DEFAULT_FRICTION); }
public static void applyFriction(final GameObject obj, final float friction_decrement) {
if(!apply_friction || obj.getDontApplyFriction()) return;
if(obj.getGrounded()) runFriction(obj, friction_decrement);
// run it again if going uphill, 3x friction
if(obj.getOnSlope() && obj.getGrounded()) {
for(int i = 0 ; i < onSlopeFrictionMultiplier; i++)
runFriction(obj, friction_decrement);
}
}
private static void runFriction(final GameObject obj, float friction_decrement) {
switch(Physics.getTickRate()) {
case _60: break;
case _120: friction_decrement *= 0.5f; break;
}
if(obj.getVelX() != 0 && abs(obj.getVelX()) > friction_decrement)
obj.setVelX(obj.getVelX() > 0 ? obj.getVelX() - friction_decrement : obj.getVelX() + friction_decrement);
else obj.setVelX(0);
if(obj.getVelY() != 0 && abs(obj.getVelY()) > friction_decrement)
obj.setVelY(obj.getVelY() > 0 ? obj.getVelY() - friction_decrement : obj.getVelY() + friction_decrement);
else obj.setVelY(0);
}
static float getDefaultFriction() { return DEFAULT_FRICTION; }
static void setDefaultFriction(final float f) { DEFAULT_FRICTION = f; }
static void setOnSlopeFrictionMultiplier(final float f) { onSlopeFrictionMultiplier = f; }
static void disableFriction() { apply_friction = false; }
static void enableFriction() { apply_friction = true; }
static void toggleFriction() { apply_friction = !apply_friction; }
static boolean getDontApplyFriction() { return !apply_friction; }
static void setDontApplyFriction(final boolean b) { apply_friction = !b; }
}

View File

@@ -0,0 +1,96 @@
package sjgs.physics;
import static sjgs.utils.Utils.calcMostIntersecting;
import static sjgs.utils.Utils.cinch;
import static sjgs.utils.Utils.restrict;
import sjgs.base_objects.BaseTile;
import sjgs.base_objects.Bullet;
import sjgs.base_objects.GameObject;
import sjgs.base_objects.HardObject;
import sjgs.base_objects.Mob;
import sjgs.base_objects.PlayerBase;
import sjgs.base_objects.SoftObject;
import sjgs.enums.Type;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.SimplePoint;
class __GeneralCollision {
private static void calcDefaultHardCollision(final GameObject obj, final GameObject g) {
if(obj.getType() == Type.BULLET) { __BulletPhysics.calcBulletCollision((Bullet)obj, g); return; }
final float velX = obj.getVelX();
final float velY = obj.getVelY();
switch(Utils.intDirection(g.getCenter(), obj.getCenter())) {
case 1: obj.setVelY(cinch(velY, 0)); obj.setY(g.getY() - obj.getHeight()); break;
case 3: obj.setVelX(restrict(velX, 0)); obj.setX(g.getX() + g.getWidth() - 1); break; // NOTE THE - 1 is VERY IMPORTANT!
case 5: obj.setVelY(restrict(velY, 0)); obj.setY(g.getY() + g.getHeight()); break;
case 7: obj.setVelX(cinch(velX, 0)); obj.setX(g.getX() - obj.getWidth()); break;
case 2: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getBottomBounds(), obj.getLeftBounds());
if(r == obj.getBottomBounds()) { obj.setVelY(cinch(velY, 0)); obj.setY(g.getY() - obj.getHeight()); }
else if(r == obj.getLeftBounds()) { obj.setVelX(restrict(velX, 0)); obj.setX(g.getX() + g.getWidth()); }
break; }
case 4: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getTopBounds(), obj.getLeftBounds());
if(r == obj.getTopBounds()) { obj.setVelY(restrict(velY, 0)); obj.setY(g.getY() + g.getHeight()); }
else if(r == obj.getLeftBounds()) { obj.setVelX(restrict(velX, 0)); obj.setX(g.getX() + g.getWidth()); }
break; }
case 6: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getTopBounds(), obj.getRightBounds());
if(r == obj.getTopBounds()) { obj.setVelY(restrict(velY, 0)); obj.setY(g.getY() + g.getHeight()); }
else if(r == obj.getRightBounds()) { obj.setVelX(cinch(velX, 0)); obj.setX(g.getX() - obj.getWidth()); }
break; }
case 8: { final Rectangle r = calcMostIntersecting(g.getFullBounds(), obj.getBottomBounds(), obj.getRightBounds());
if(r == obj.getBottomBounds()) { obj.setVelY(cinch(velY, 0)); obj.setY(g.getY() - obj.getHeight()); }
else if(r == obj.getRightBounds()) { obj.setVelX(cinch(velX, 0)); obj.setX(g.getX() - obj.getWidth()); }
break; }
}
}
//--------------------------------- TILE COLLISION --------------------------------------------------------//
static void default_hard_collision_against_hard_objects(final GameObject obj, final Stack<HardObject> collided_hard_objects) {
final Stack<Rectangle> rects = new Stack<>();
for(final HardObject ho : collided_hard_objects) {
if(ho instanceof BaseTile) {
final BaseTile t = (BaseTile)ho;
if(t.angled()) {
for(final SimplePoint p : t.getSlopePoints())
if(obj.getFullBounds().contains(p))
obj.setY(p.y - obj.getHeight());
} else rects.add(ho.getFullBounds());
}
}
final Rectangle mostIntersected = calcMostIntersecting(obj.getFullBounds(), rects);
for(final HardObject ho : collided_hard_objects) if(ho.getFullBounds() == mostIntersected) calcDefaultHardCollision(obj, ho);
}
// --------------------------------- MOB COLLISION ------------------------------------------------------- //
static void default_hard_collision_against_mobs(final GameObject obj, final Stack<Mob> collided_mobs) {
for(final Mob m : collided_mobs) { if(m.getBounds() == obj.getBounds()) continue;
calcDefaultHardCollision(obj, m);
}
}
// -------------------------------- SCENERY COLLISION ---------------------------------------------------- //
static void default_hard_collision_against_soft_objects(final GameObject obj, final Stack<SoftObject> collided_mobs) {
for(final SoftObject s : collided_mobs) { if(s.getBounds() == obj.getBounds()) continue;
calcDefaultHardCollision(obj, s);
}
}
// -------------------------------- BULLET COLLISION ----------------------------------------------------- //
static void default_hard_collision_against_bullets(final GameObject obj, final Stack<Bullet> collided_bullets) {
for(final Bullet b : collided_bullets) { if(b.getBounds() == obj.getBounds()) continue;
calcDefaultHardCollision(obj, b);
}
}
// -------------------------------- PLAYER COLLISION ----------------------------------------------------- //
static void default_hard_collision_against_players(final GameObject obj, final Stack<PlayerBase> collided_players) {
for(final PlayerBase p : collided_players) { if(p.getBounds() == obj.getBounds()) continue;
calcDefaultHardCollision(obj, p);
}
}
}

33
src/sjgs/physics/__Gravity.java Executable file
View File

@@ -0,0 +1,33 @@
package sjgs.physics;
import static sjgs.utils.Utils.cinch;
import sjgs.base_objects.GameObject;
class __Gravity {
private static boolean GRAVITY_DISABLED;
private static float DEFAULT_GRAVITY, TERMINAL_VELOCITY;
static void applyGravity(final GameObject obj, float gravity) {
if(GRAVITY_DISABLED || obj.getDontApplyGravity()) return;
if(obj.useCustomGravity()) gravity = obj.getCustomGravity();
switch(Physics.getTickRate()) {
case _60: break;
case _120: gravity *= 0.5f; break;
}
obj.setVelY(cinch(obj.getVelY() + gravity, TERMINAL_VELOCITY));
}
static float getTerminalVelocity() { return TERMINAL_VELOCITY; }
static float getDefaultGravity() { return DEFAULT_GRAVITY; }
static void setDefaultGravity(final float g) { DEFAULT_GRAVITY = g; }
static void setTerminalVelocity(final float t) { TERMINAL_VELOCITY = t; }
static void disableGravity() { GRAVITY_DISABLED = true; }
static void enableGravity() { GRAVITY_DISABLED = false; }
static void toggleGravity() { GRAVITY_DISABLED = !GRAVITY_DISABLED; }
}

View File

@@ -0,0 +1,29 @@
package sjgs.physics;
import static sjgs.utils.Utils.sqrtOf2;
import sjgs.base_objects.GameObject;
class __UpdatePosition {
private static int step = 1;
static void updatePosition(final GameObject obj) {
float tempVelX = obj.getVelX();
float tempVelY = obj.getVelY();
// ADJUST FOR SMOOTH, EVEN DIAGONAL MOVEMENT, (fast pythagorous method)
if(obj.getVelX() == obj.getVelY() && obj.getVelX() != 0 && obj.getVelY() != 0) {
tempVelX /= sqrtOf2;
tempVelY /= sqrtOf2;
}
switch(Physics.getTickRate()) {
case _60: obj.setLocation(obj.getX() + tempVelX, obj.getY() + tempVelY);
break;
case _120: obj.setLocation(obj.getX() + tempVelX*0.5f, obj.getY() + tempVelY*0.5f);
break;
}
}
}

View File

@@ -0,0 +1,154 @@
package sjgs.physics.structs;
import java.awt.Graphics2D;
import sjgs.enums.Facing;
import sjgs.enums.Type;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.shapes.Line;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
import sjgs.utils.data_structures.vectors.Vector2f;
public class BoundingBox {
/** @type: The kind of object that this bounding box is attached to */
private final Type type;
private Facing facing = Facing.NONE; // note they always start out @ none
private final Rectangle fullBounds, leftBounds, rightBounds, topBounds, bottomBounds;
private float LONG, SMALL;
private final Vector2f velocity;
private float gravity;
private boolean useCustomGravity;
// ---------- booleans for the physics interpreter ----------- //
private boolean dontApplyGravity, grounded, onSlope, dontApplyFriction;
// ---------------------------------------------------------- //
public BoundingBox(final float x, final float y, final float WIDTH, final float HEIGHT, final Type type) {
this.type = type;
velocity = new Vector2f(0,0);
getLS(WIDTH, HEIGHT);
fullBounds = new Rectangle(0, 0, 0, 0);
leftBounds = new Rectangle(0, 0, 0, 0);
rightBounds = new Rectangle(0, 0, 0, 0);
topBounds = new Rectangle(0, 0, 0, 0);
bottomBounds = new Rectangle(0, 0, 0, 0);
setSize(WIDTH, HEIGHT);
setLocation(x, y);
}
public BoundingBox(final float x, final float y, final float WIDTH, final float HEIGHT, final float velX, final float velY, final Type type) {
this.type = type;
velocity = new Vector2f(velX, velY);
getLS(WIDTH, HEIGHT);
fullBounds = new Rectangle(0, 0, 0, 0);
leftBounds = new Rectangle(0, 0, 0, 0);
rightBounds = new Rectangle(0, 0, 0, 0);
topBounds = new Rectangle(0, 0, 0, 0);
bottomBounds = new Rectangle(0, 0, 0, 0);
setSize(WIDTH, HEIGHT);
setLocation(x, y);
}
@Override
public BoundingBox clone() {
final BoundingBox clone = new BoundingBox(getX(), getY(), getWidth(), getHeight(), getVelX(), getVelY(), type);
clone.onSlope = onSlope;
clone.grounded = grounded;
return clone;
}
@SuppressWarnings("unused")
private void getLS(final float w, final float h) {
switch(type) {
default: LONG = 1f/3f; SMALL = 1f/9f;
}
}
public void setLocation(final float x, final float y) {
fullBounds.setLocation(x, y);
leftBounds.setLocation(x, y + (getHeight() - leftBounds.height)/2);
rightBounds.setLocation(x+getWidth() - rightBounds.width, y + (getHeight() - leftBounds.height)/2);
topBounds.setLocation(x+(getWidth() - topBounds.width)/2, y);
bottomBounds.setLocation(x+(getWidth() - topBounds.width)/2, y+getHeight()-bottomBounds.height);
}
public void setSize(final float w, final float h) {
fullBounds.setSize(w, h);
leftBounds.setSize(Utils.restrict(w*SMALL, 1), Utils.restrict(h*LONG, 1));
rightBounds.setSize(Utils.restrict(w*SMALL, 1), Utils.restrict(h*LONG, 1));
topBounds.setSize(Utils.restrict(w*LONG, 1), Utils.restrict(h*SMALL, 1));
bottomBounds.setSize(Utils.restrict(w*LONG, 1), Utils.restrict(h*SMALL, 1));
}
public void draw(final Graphics2D g2d) {
fullBounds.draw(g2d);
leftBounds.draw(g2d);
rightBounds.draw(g2d);
topBounds.draw(g2d);
bottomBounds.draw(g2d);
}
public void drawFilled(final Graphics2D g2d) { g2d.fillRect((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight()); }
public void dontUseCustomGravity() { useCustomGravity = false; gravity = 0; }
public void setCustomGravity(final float gravity) { useCustomGravity = true; this.gravity = gravity; }
public float getCustomGravity() { return gravity; }
public boolean useCustomGravity() { return useCustomGravity; }
public boolean stopped() { return getVelX() == 0 && getVelY() == 0; }
public void invertVelX() { velocity.invertX(); }
public void invertVelY() { velocity.invertY(); }
public float getVelX() { return velocity.getX(); }
public float getVelY() { return velocity.getY(); }
public void setVelX(final float velX) { velocity.setX(velX); }
public void setVelY(final float velY) { velocity.setY(velY); }
public void setVelocity(final float velX, final float velY) { velocity.setXAndY(velX, velY); }
public float getArea() { return fullBounds.getArea(); }
public Point2f getLocation() { return new Point2f(getX(), getY()); }
public void setCenter(final float x, final float y) { setLocation(x - getHalfWidth(), y - getHalfHeight()); }
public void setCenter(final Point2f p) { setCenter(p.x, p.y); }
public void setCenter(final SimplePoint p) { setCenter(p.x, p.y); }
public float getHalfWidth() { return getWidth() * 0.5f; }
public float getHalfHeight() { return getHeight() * 0.5f; }
public boolean getDontApplyGravity() { return dontApplyGravity; }
public void setDontApplyGravity(final boolean b) { dontApplyGravity = b; }
public void toggleDontApplyGravity() { dontApplyGravity = !dontApplyGravity; }
public boolean getDontApplyFriction() { return dontApplyFriction; }
public void setDontApplyFriction(final boolean b) { dontApplyFriction = b; }
public void toggleDontApplyFriction() { dontApplyFriction = !dontApplyFriction; }
public boolean facingLeft() { return facing == Facing.LEFT; }
public boolean facingRight() { return facing == Facing.RIGHT; }
public Facing getFacing() { return facing; }
public void setFacing(final Facing facing) { this.facing = facing; }
public Rectangle getFullBounds() { return fullBounds; }
public Rectangle getLeftBounds() { return leftBounds; }
public Rectangle getRightBounds() { return rightBounds; }
public Rectangle getTopBounds() { return topBounds; }
public Rectangle getBottomBounds() { return bottomBounds; }
public boolean getGrounded() { return grounded; }
public void setGrounded(final boolean grounded) { this.grounded = grounded; }
public boolean getOnSlope() { return onSlope; }
public void setOnSlope(final boolean onSlope) { this.onSlope = onSlope; }
public Type getType() { return type; }
public Point2f getCenter() { return fullBounds.getCenter(); }
public Point2f getPosition() { return fullBounds.pos; }
public float getX() { return fullBounds.pos.x; }
public float getY() { return fullBounds.pos.y; }
public float getWidth() { return fullBounds.width; }
public float getHeight() { return fullBounds.height; }
public void setX(final float x) { setLocation(x, getY()); }
public void setY(final float y) { setLocation(getX(), y); }
public void setWidth(final float w) { setSize(w, getHeight()); }
public void setHeight(final float h) { setSize(getWidth(), h); }
public Point2f getTopRight() { return fullBounds.getTopRight(); }
public Point2f getTopLeft() { return fullBounds.getTopLeft(); }
public Point2f getBottomRight() { return fullBounds.getBottomRight(); }
public Point2f getBottomLeft() { return fullBounds.getBottomLeft(); }
public Line getTop() { return fullBounds.getTop(); }
public Line getBottom() { return fullBounds.getBottom(); }
public Line getLeft() { return fullBounds.getLeft(); }
public Line getRight() { return fullBounds.getRight(); }
}

View File

@@ -0,0 +1,116 @@
package sjgs.physics.structs;
import sjgs.base_objects.BaseTile;
import sjgs.base_objects.Bullet;
import sjgs.base_objects.GameObject;
import sjgs.base_objects.HardObject;
import sjgs.base_objects.Mob;
import sjgs.base_objects.PlayerBase;
import sjgs.base_objects.SoftObject;
import sjgs.core.Handler;
import sjgs.enums.Type;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Rectangle;
public final class CollisionResponse {
public Stack<HardObject> collided_hard_objects;
public Stack<SoftObject> collided_soft_objects;
public Stack<Mob> collided_mobs;
public Stack<Bullet> collided_bullets;
public Stack<PlayerBase> collided_players;
public CollisionResponse(final GameObject obj, Stack<GameObject> hard_objects, Stack<GameObject> mobs, Stack<GameObject> bullets, Stack<GameObject> soft_objects) {
try {
// CREATE STACKS
collided_hard_objects = new Stack<HardObject>();
collided_mobs = new Stack<Mob>();
collided_bullets = new Stack<Bullet>();
collided_soft_objects = new Stack<SoftObject>();
collided_players = new Stack<PlayerBase>();
Stack<HardObject> tempHardObjects = new Stack<HardObject>();
Stack<Mob> tempMobs = new Stack<Mob>();
Stack<Bullet> tempBullets = new Stack<Bullet>();
Stack<SoftObject> tempSoftObjects = new Stack<SoftObject>();
Stack<PlayerBase> tempPlayers = new Stack<PlayerBase>();
// CONVERT GENERICS TO USEABLE OBJECTS
for(final GameObject g : hard_objects) tempHardObjects.push((HardObject)g);
for(final GameObject g : mobs) tempMobs.push((Mob)g);
for(final GameObject g : bullets) tempBullets.push((Bullet)g);
for(final GameObject g : soft_objects) tempSoftObjects.push((SoftObject)g);
for(final GameObject g : Handler.players) tempPlayers.push((PlayerBase)g);
// REFINE SEARCHED STACKS FOR COLLISION
final Rectangle r = obj.getFullBounds();
// NOTE THE != BOUNDS CHECKS IF THE TWO ARE NOT THE SAME ENTITY
for(final HardObject i : tempHardObjects) if(i.getBounds() != obj.getBounds() && r.intersects(i.getFullBounds())) collided_hard_objects.push(i);
for(final Mob i : tempMobs) if(i.getBounds() != obj.getBounds() && r.intersects(i.getFullBounds())) collided_mobs.push(i);
for(final Bullet i : tempBullets) if(i.getBounds() != obj.getBounds() && r.intersects(i.getFullBounds())) collided_bullets.push(i);
for(final SoftObject i : tempSoftObjects) if(i.getBounds() != obj.getBounds() && r.intersects(i.getFullBounds())) collided_soft_objects.push(i);
for(final PlayerBase i : tempPlayers) if(i.getBounds() != obj.getBounds() && r.intersects(i.getFullBounds())) collided_players.push(i);
// NULL JUNK FOR MEMORY
hard_objects.clear(); mobs.clear(); bullets.clear(); soft_objects.clear();
hard_objects = mobs = bullets = soft_objects = null;
tempHardObjects.clear(); tempMobs.clear(); tempBullets.clear(); tempSoftObjects.clear(); tempPlayers.clear();
tempHardObjects = null; tempMobs = null; tempBullets = null; tempSoftObjects = null; tempPlayers = null;
} catch (final Exception e) {
/* nulls / class casts / concurrent mods...
* all kinds of shenanigans happen when saving / loading.
* Just catch them. */
e.printStackTrace();
}
}
/** @method isEmpty: return true if all the stacks pertaining to the args are null or empty.
* If no args were passed, assume all stacks are meant to be checked */
public boolean isEmpty(final Type ...args) {
final boolean hards = collided_hard_objects == null || collided_hard_objects.isEmpty();
final boolean softs = collided_soft_objects == null || collided_soft_objects.isEmpty();
final boolean mobs = collided_mobs == null || collided_mobs.isEmpty();
final boolean bullets = collided_bullets == null || collided_bullets.isEmpty();
final boolean players = collided_players == null || collided_players.isEmpty();
if(args.length == 0) return hards == softs == mobs == mobs == bullets == players;
final Stack<Boolean> bools = new Stack<Boolean>();
for(final Type t : args) {
if(t == Type.HARD_OBJECT) bools.push(hards);
else if(t == Type.SOFT_OBJECT) bools.push(softs);
else if(t == Type.MOB) bools.push(mobs);
else if(t == Type.BULLET) bools.push(bullets);
else if(t == Type.PLAYER) bools.push(players);
}
for(final Boolean b : bools) if(b != true) return false;
return true;
}
public void discard() {
collided_hard_objects.clear();
collided_mobs.clear();
collided_bullets.clear();
collided_soft_objects.clear();
collided_players.clear();
collided_hard_objects = null;
collided_mobs = null;
collided_bullets = null;
collided_soft_objects = null;
collided_players = null;
}
public Stack<BaseTile> getTiles() {
final Stack<BaseTile> tiles = new Stack<BaseTile>();
for(final HardObject o : collided_hard_objects) if(o instanceof BaseTile) tiles.push((BaseTile)o);
return tiles;
}
}

112
src/sjgs/sound/MusicPlayer.java Executable file
View File

@@ -0,0 +1,112 @@
package sjgs.sound;
import java.io.IOException;
import java.io.InputStream;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.FactoryRegistry;
import javazoom.jl.player.JavaSoundAudioDevice;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javazoom.jl.player.advanced.PlaybackListener;
// NOTE: This class needs the JLayer.jar in your main project in order to function!
public class MusicPlayer extends PlaybackListener {
private AdvancedPlayer player;
private SoundThread thread;
private JavaSoundAudioDevice device;
private InputStream stream;
private String filename;
private final float gain;
private final boolean loop;
private boolean paused;
public MusicPlayer(final String filename, final float gain, final boolean loop) {
this.filename = filename; this.gain = gain; this.loop = loop;
createAdvancedPlayer();
}
public MusicPlayer(final String filename, final boolean loop) {
this.filename = filename; gain = 0; this.loop = loop;
createAdvancedPlayer();
}
private void createAdvancedPlayer() {
try {
final JavaSoundAudioDevice device = (JavaSoundAudioDevice) FactoryRegistry.systemRegistry().createAudioDevice();
device.createSource();
device.setGain(gain);
stream = SoundPlayer.class.getResourceAsStream(filename);
player = new AdvancedPlayer(stream, device);
} catch (final Exception e) { e.printStackTrace(); }
player.setPlayBackListener(this);
}
public void play() {
thread = new SoundThread(this, loop, filename);
thread.start();
paused = false;
}
public void pause() {
thread.stop();
paused = true;
}
public void togglePause() {
if(paused) play(); else pause();
}
public void reset() { createAdvancedPlayer(); }
// REMEMBER TO CALL THIS FOR RESOURCE LEAKS!!!!
public void destroy() {
thread.stop();
thread = null;
// Not familiar with JLayer's API, but this seems to throw a null pointer occasionally?
try { device.flush(); device.close(); } catch (final NullPointerException npe) { }
try { stream.close(); } catch (final IOException e) { e.printStackTrace(); }
player.close();
player = null;
filename = null;
device = null;
stream = null;
}
private void runThread() {
try { player.play(); // will play from start if not paused before, else it will start @ pause position
createAdvancedPlayer();
} catch (final JavaLayerException e) {
e.printStackTrace();
}
}
public boolean paused() { return paused; }
public String getFilename() { return filename; }
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------//
private static class SoundThread extends Thread {
private final boolean loop;
private final MusicPlayer player;
public SoundThread(final MusicPlayer player, final boolean loop, final String filename) {
this.loop = loop;
this.player = player;
setName(filename);
}
@Override
public void run() {
do {
player.runThread();
} while(loop);
player.destroy();
}
}
}

42
src/sjgs/sound/SoundPlayer.java Executable file
View File

@@ -0,0 +1,42 @@
package sjgs.sound;
import java.io.InputStream;
import javazoom.jl.player.FactoryRegistry;
import javazoom.jl.player.JavaSoundAudioDevice;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javazoom.jl.player.advanced.PlaybackListener;
import sjgs.utils.multithreading.Runner;
// NOTE: This class needs the JLayer.jar in your main project in order to function!
public final class SoundPlayer extends PlaybackListener {
private static final SoundPlayer PLAYBACK_LISTENER = new SoundPlayer();
public static void play(final String filename) { play(filename, 0); }
/** @method play: Plays an mp3 audio file with given resource stream name.
* gain can be a value from -80 to 6. No argument assumes no gain */
public static void play(final String filename, final float gain) {
new Runner(() -> {
try {
JavaSoundAudioDevice device = (JavaSoundAudioDevice) FactoryRegistry.systemRegistry().createAudioDevice();
device.createSource();
device.setGain(gain);
InputStream stream = SoundPlayer.class.getResourceAsStream(filename);
AdvancedPlayer player = new AdvancedPlayer(stream, device);
player.setPlayBackListener(PLAYBACK_LISTENER);
player.play();
// --------- //
device.flush();
device.close();
player.close();
stream.close();
device = null;
stream = null;
player = null;
} catch (final Exception e) { e.printStackTrace(); }
}, "Sound Player: " + filename).start();
}
}

281
src/sjgs/utils/Utils.java Executable file
View File

@@ -0,0 +1,281 @@
package sjgs.utils;
import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.util.Random;
import javax.swing.JOptionPane;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Circle;
import sjgs.utils.data_structures.shapes.Line;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
/** @author: Mitch Weaver 2016 */
public final class Utils {
/********************** @COMMONLY_USED_VARIABLES ********************************/
public static final Random rand = new Random(); // No need to create more randoms for everything, can just always use this one
public static final String OS = "Running on: " + System.getProperty("os.name");
public static final float sqrtOf2 = sqrt(2); // used for a lot of things, saved as not to need to calc it
public static final double second = 1_000_000_000.0d; // 1 second in nanoseconds (1 billion)
public static final float PI = (float)Math.PI; // in float form for convenience
public static final float E = (float)Math.E; // in float form for convenience
public static final int _2147483647 = Integer.MAX_VALUE;
public static final float ulp = Math.ulp(0.0f); // smallest possible floating point number
public static final Color voidColor = __ImageManipulation.voidColor;
public static final String userHomeDir = __SerializationUtils.userHomeDir;
public static final String alphabet = "abcdefghijklmnopqrstuvwxyz";
public static final int SCREEN_WIDTH = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth();
public static final int SCREEN_HEIGHT = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight();
/*******************************************************************************/
/***************************** @DEGREES_AND_RADIANS *****************************/
public static final float radian = PI / 180f;
public static final float _45toRadians = toRadians(45), _90toRadians = _45toRadians*2, _180toRadians = _90toRadians*2;
public static final float toDegrees(final float theta) { return theta * 180 / PI; }
public static final double toDegrees(final double theta) { return theta * 180 / PI; }
public static final float toRadians(final float degrees) { return degrees * radian; }
public static final double toRadians(final double degrees) { return degrees * radian; }
public static final float sin(final float theta) { return (float)Math.sin(theta); }
public static final float cos(final float theta) { return (float)Math.cos(theta); }
public static final float tan(final float theta) { return (float)Math.tan(theta); }
public static final float atan(final float theta) { return (float)Math.atan(theta); }
public static final float atan2(final float y, final float x) { return (float)Math.atan2(y, x); } // NOTE: these are in reverse order, as is standard
public static final float getTheta(final float x, final float y) { return atan2(y, x); } // for those that can't remember its atan2, (lol)
/******************************************************************************/
/***************************** @DECIMAL_FORMATS ********************************/
public static final DecimalFormat dff = new DecimalFormat("0.##");
public static final DecimalFormat df = new DecimalFormat("0.#");
/******************************************************************************/
/******************************* @CLAMPS ************************************/
public static final int clamp(int input, final int floor, final int ceiling){ if(input < floor) input = floor; else if(input > ceiling) input = ceiling; return input; }
public static final float clamp(float input, final float floor, final float ceiling){ if(input < floor) input = floor; else if(input > ceiling) input = ceiling; return input; }
public static final double clamp(double input, final double floor, final double ceiling){ if(input < floor) input = floor; else if(input > ceiling) input = ceiling; return input; }
public static final int cinch(final int input, final int ceiling) { return input < ceiling ? input : ceiling; }
public static final float cinch(final float input, final float ceiling) { return input < ceiling ? input : ceiling; }
public static final double cinch(final double input, final double ceiling) { return input < ceiling ? input : ceiling; }
public static final int restrict(final int input, final int floor) { return input > floor ? input : floor; }
public static final float restrict(final float input, final float floor) { return input > floor ? input : floor; }
public static final double restrict(final double input, final double floor) { return input > floor ? input : floor; }
/** @method converge: Steps the input towards the target. If the input as excessed the target, clamp. */
public static final int converge(final int input, final int target, final int step) { return isNegative(target) ? restrict(input - step, target) : cinch(input + step, target); }
public static final float converge(final float input, final float target, final float step) { return isNegative(target) ? restrict(input - step, target) : cinch(input + step, target); }
public static final double converge(final double input, final double target, final double step) { return isNegative(target) ? restrict(input - step, target) : cinch(input + step, target); }
/**************************************************************************/
/************************* @POWERS *****************************************/
public static final int square(final int foo) { return foo*foo; }
public static final float square(final float foo) { return foo*foo; }
public static final double square(final double foo) { return foo*foo; }
public static final int cube(final int foo) { return foo*foo*foo; }
public static final float cube(final float foo) { return foo*foo*foo; }
public static final double cube(final double foo) { return foo*foo*foo; }
public static final int pow(int foo, final int bar) { for(int i = 0; i < bar; i++) foo*=foo; return foo; }
public static final float pow(float foo, final int bar) { for(int i = 0; i < bar; i++) foo*=foo; return foo; }
public static final double pow(double foo, final int bar) { for(int i = 0; i < bar; i++) foo*=foo; return foo; }
/**************************************************************************/
/********************** @TEXT_FILE_MANIPULATION *****************************/
public static final int getNumLinesFromTextFile(final String path) { return __TextFileManipulation.getNumLinesFromTextFile(path); }
public static final String[] makeStringArrayFromLinesOfTextFile(final String path) { return __TextFileManipulation.makeStringArrayFromLinesOfTextFile(path); }
public static final StringBuilder[] makeStrinBuilderArrayFromLinesOfTextFile(final String path) { return __TextFileManipulation.makeStringBuilderArrayFromLinesOfTextFile(path); }
public static final String readTextFileAsString(final String filepath){ return __TextFileManipulation.readTextFileAsString(filepath); }
public static final StringBuilder readTextFileAsStringBuilder(final String path){ return new StringBuilder(__TextFileManipulation.readTextFileAsString(path)); }
public static final char[] readTextFileAsCharArray(final String path) { return __TextFileManipulation.readTextFileAsString(path).toCharArray(); }
/****************************************************************************/
/********************** @IMAGE_MANIPULATION *********************************/
/** @method removeVoidColor ~ default no color arg is (255, 0, 255) for pink void color */
public static final BufferedImage reverseImage(final BufferedImage image) { return __ImageManipulation.reverseImage(image); }
public static final BufferedImage invertImage(final BufferedImage image) { return __ImageManipulation.invertImage(image); }
public static final BufferedImage rotateImageRight(final BufferedImage image) { return __ImageManipulation.rotateImageRight(image); }
public static final BufferedImage rotateImageLeft(final BufferedImage image) { return __ImageManipulation.rotateImageLeft(image); }
public static final BufferedImage imageToBufferedImage(final Image image) { return __ImageManipulation.imageToBufferedImage(image); }
public static final BufferedImage loadImage(final String path) { return __ImageManipulation.loadImage(path); }
/** @method grabSprite: Note col and rol start @ 0, this is for ease of use when using loops. */
public static final BufferedImage grabSprite(final BufferedImage image, final int col, final int row, final int width, final int height){ return __ImageManipulation.grabSprite(image, col, row, width, height); }
public static final BufferedImage grabSprite(final Image image, final int col, final int row, final int width, final int height){ return __ImageManipulation.grabSprite(imageToBufferedImage(image), col, row, width, height); }
public static final BufferedImage removeVoidColor(final BufferedImage image, final Color voidColor) { return __ImageManipulation.removeVoidColor(image, voidColor); }
public static final BufferedImage removeVoidColor(final BufferedImage image) { return removeVoidColor(image, voidColor); }
/** @method optimizeImage: Note all the above methods call this by default. */
public static final BufferedImage optimizeImage(final BufferedImage image) { return __ImageManipulation.optimizeImage(image); }
/**************************************************************************************/
/*************************** @MISC_GETTER_METHODS ***********************************/
public static final boolean isEven(final int foo) { return foo % 2 == 0; } public static final boolean isEven(final long foo) { return foo % 2 == 0; }
public static final boolean isEven(final short foo){ return foo % 2 == 0; } public static final boolean isEven(final byte foo) { return foo % 2 == 0; }
public static final boolean isOdd(final int foo) { return foo % 2 != 0; } public static final boolean isOdd(final long foo) { return foo % 2 != 0; }
public static final boolean isOdd(final short foo) { return foo % 2 != 0; } public static final boolean isOdd(final byte foo) { return foo % 2 != 0; }
public static final boolean isNegative(final int foo) { return foo < 0; } public static final boolean isPositive(final int foo) { return foo >= 0; }
public static final boolean isNegative(final float foo) { return foo < 0; } public static final boolean isPositive(final float foo) { return foo >= 0; }
public static final boolean isNegative(final double foo) { return foo < 0; } public static final boolean isPositive(final double foo) { return foo >= 0; }
public static final boolean bothNegative(final int foo, final int bar) { return isNegative(foo) && isNegative(bar); }
public static final boolean bothNegative(final float foo, final float bar) { return isNegative(foo) && isNegative(bar); }
public static final boolean bothNegative(final double foo, final double bar) { return isNegative(foo) && isNegative(bar); }
public static final boolean bothPositive(final int foo, final int bar) { return isPositive(foo) && isPositive(bar); }
public static final boolean bothPositive(final float foo, final float bar) { return isPositive(foo) && isPositive(bar); }
public static final boolean bothPositive(final double foo, final double bar) { return isPositive(foo) && isPositive(bar); }
public static final boolean isPercent(final float foo) { return foo >= 0 && foo <= 99.999f; }
public static final boolean isPercent(final double foo) { return foo >= 0 && foo <= 99.999f; }
/************************************************************************************/
/************************** @ROTATION ************************************************/
public static final Point rotatePoint(final Point p, final double theta) { return new Point((int)(p.x * Math.cos(theta) - p.y * Math.sin(theta)), (int)(p.x * Math.sin(theta) + p.y * Math.cos(theta)));}
public static final Point2f rotatePoint(final Point2f p, final double theta){ return new Point2f(p.x * Math.cos(theta) - p.y * Math.sin(theta), p.x * Math.sin(theta) + p.y * Math.cos(theta));}
public static final float rotateX(final int x, final int y, final double theta) { return (float)(x * Math.cos(theta) - y * Math.sin(theta)); }
public static final float rotateY(final int x, final int y, final double theta) { return (float)(x * Math.sin(theta) + y * Math.cos(theta)); }
public static final float rotateX(final float x, final float y, final double theta) { return (float)(x * Math.cos(theta) - y * Math.sin(theta)); }
public static final float rotateY(final float x, final float y, final double theta) { return (float)(x * Math.sin(theta) + y * Math.cos(theta)); }
public static final double rotateX(final double x, final double y, final double theta) { return x * Math.cos(theta) - y * Math.sin(theta); }
public static final double rotateY(final double x, final double y, final double theta) { return x * Math.sin(theta) + y * Math.cos(theta); }
/************************************************************************************/
/************************** @ABSOLUTE_VALUE ******************************************/
public static final int abs(final int value) { return value >= 0 ? value : value * -1; }
public static final float abs(final float value) { return value >= 0 ? value : value * -1f; }
public static final double abs(final double value) { return value >= 0 ? value : value * -1d; }
public static final long abs(final long value) { return value >= 0 ? value : value * -1; }
/************************************************************************************/
/*************************** @FAST_SQUARE_ROOT *************************************/
// evil floating point bit level hacking
// what the fuck?
public static final float sqrt(final float foo) { return (float)sqrt((double)foo); }
public static final double sqrt(final double foo) { final double sqrt = Double.longBitsToDouble( ( Double.doubleToLongBits( foo )-(1l<<52)>>1 ) + ( 1l<<61 ) ); return (sqrt + foo/sqrt)/2.0d; }
/**********************************************************************************/
/************************************** @PYTHAGORAS **********************************/
public static final float pythagoras(final int foo, final int bar){ return sqrt(square(foo) + square(bar)); }
public static final float pythagoras(final float foo, final float bar){ return sqrt(square(foo) + square(bar)); }
public static final double pythagoras(final double foo, final double bar){ return sqrt(square(foo) + square(bar)); }
/************************************************************************************/
/**************************************** @POINT_DISTANCE *************************************************************************/
public static final float distance(final Point2f foo, final Point2f bar){ return Utils.sqrt(Utils.square(foo.y - bar.y) + Utils.square(foo.x - bar.x)); }
public static final float distance(final Point foo, final Point bar){ return Utils.sqrt(Utils.square(foo.y - bar.y) + Utils.square(foo.x - bar.x)); }
public static final float distance(final float x1, final float y1, final float x2, final float y2){ return Utils.sqrt(Utils.square(y1 - y2) + Utils.square(x1 - x2)); }
public static final float distance(final int x1, final int y1, final int x2, final int y2){ return Utils.sqrt(Utils.square(y1 - y2) + Utils.square(x1 - x2)); }
/******************************************************************************************************************************************/
/************************************** @POINT_DIRECTION **********************************************************************/
/** @direction: returns the angle in radians that point is from another */
public static final float direction(final Point2f foo, final Point2f bar){ return foo.direction(bar); }
public static final int intDirection(final Point2f foo, final Point2f bar) {
double angle = toDegrees(atan2(bar.y - foo.y, bar.x - foo.x)); if(angle < 0) angle+=360;
if(angle > 247.5 && angle < 292.5) return 1; if(angle > 67.5 && angle < 112.5) return 5;
if(angle > 292.5 && angle < 337.5) return 2; if(angle > 112.5 && angle < 157.5) return 6;
if(angle > 337.5 || angle < 22.5) return 3; if(angle > 157.5 && angle < 202.5) return 7;
if(angle > 22.5 && angle < 67.5) return 4; if(angle > 202.5 && angle < 247.5) return 8; return 1;
}
/*****************************************************************************************************************************************/
/************************************** @MISC ***********************************************************/
public static final synchronized int getNumProcessors() { return Runtime.getRuntime().availableProcessors(); }
public static final float nextUlp(final float input) { return input + ulp; }
public static final double nextUlp(final double input) { return input + ulp; }
public static final <E> void print(final E e) { System.out.println(e); }
public static final <E> void error(final E e) { System.err.println(e); }
public static final <E> void print(final E[] arr) { for(final E e : arr) System.out.println(e); }
public static final <E> void dialog(final E e) { JOptionPane.showMessageDialog(null, e); }
public static final void exit() { System.exit(0); }
public static final void quit() { exit(); }
/********************************************************************************************************/
/************************************* @SERIALIZATION ****************************************************/
public static final synchronized void saveObject(final Object obj, final String path){ __SerializationUtils.saveObject(obj, path); }
public static final synchronized Object readObject(final String path){ return __SerializationUtils.readObject(path); }
/** NOTE: Unlike the other methods that load things as resource streams,
* @writeStringToFile must use a file writer, which doesnt use a stream.
* Do not put the '/' in front of your path! */
public static final synchronized void writeStringToFile(final String string, final String path) { __SerializationUtils.writeStringToFile(string, path); }
/*********************************************************************************************************/
/************************************ @IMAGE_RGB_MANIPULATION ********************************************/
public static final int unpackRed(final int pixel) { return pixel >> 16 & 0xff; }
public static final int unpackGreen(final int pixel) { return pixel >> 8 & 0xff; }
public static final int unpackBlue(final int pixel) { return pixel & 0xff; }
public static final boolean isTransparent(final int pixel) { return pixel == voidColor.getRGB() || pixel>>24 == 0x00; }
public static final int getTransparentPixel() { return 0x00; }
/********************************************************************************************************/
/************************* @RANDOM_CHANCES **************************************************************/
public static final boolean COIN_FLIP() { return rand.nextBoolean(); }
public static final boolean ONE_PERCENT() { return rand.nextInt(100) == 0; }
public static final boolean ONE_IN_A_THOUSAND() { return rand.nextInt(1_000) == 0; }
public static final boolean ONE_IN_A_MILLION() { return rand.nextInt(1_000_000) == 0; }
public static final boolean roll(final int bound) { return rand.nextInt(bound) == 0; }
public static final int nextInt(final int bound) { return rand.nextInt(bound); }
public static final float nextFloat() { return rand.nextFloat(); }
public static final double nextDouble() { return rand.nextDouble(); }
public static final int nextInt(final int floor, final int ceiling) { return restrict(nextInt(ceiling+1), floor); }
public static final byte nextByte() { return (byte)nextInt(-128, 128); }
/********************************************************************************************************/
/******************************** @MAX_AND_MIN ******************************************************/
public static final int max(final int ...args) { return __MixMaxUtils.biggest(args); }
public static final float max(final float ...args) { return __MixMaxUtils.biggest(args); }
public static final double max(final double ...args) { return __MixMaxUtils.biggest(args); }
public static final int biggest(final int ...args) { return __MixMaxUtils.biggest(args); }
public static final float biggest(final float ...args) { return __MixMaxUtils.biggest(args); }
public static final double biggest(final double ...args) { return __MixMaxUtils.biggest(args); }
public static final int min(final int ...args) { return __MixMaxUtils.smallest(args); }
public static final float min(final float ...args) { return __MixMaxUtils.smallest(args); }
public static final double min(final double ...args) { return __MixMaxUtils.smallest(args); }
public static final int smallest(final int ...args) { return __MixMaxUtils.smallest(args); }
public static final float smallest(final float ...args) { return __MixMaxUtils.smallest(args); }
public static final double smallest(final double ...args) { return __MixMaxUtils.smallest(args); }
/********************************************************************************************************/
/********************************** @ROUNDING ***********************************************************/
public static final float round(final float i) { return isPositive(i) ? truncate(i + 0.5f) : truncate(i - 0.5f); }
public static final double round(final double i) { return isPositive(i) ? truncate(i + 0.5f) : truncate(i - 0.5f); }
public static final float truncate(final float input) { return (int)input; }
public static final double truncate(final double input) { return (int)input; }
public static final float floor(final float input) { return truncate(input); }
public static final double floor(final double input) { return truncate(input); }
public static final float ceil(final float input) { return truncate(input + 0.5f); }
public static final double ceil(final double input) { return truncate(input + 0.5f); }
/********************************************************************************************************/
/****************************** @INTERSECTION_UTILS ******************************************************/
/** @calcMostIntersecting: Takes in a rectangle to check, and any number of other rectangles.
* Returns the rect from args that intersects with rect the most.
* If there is a tie, it will return a random rectangle from the answers. */
public static final Rectangle calcMostIntersecting(final Rectangle rect, final Rectangle ...args) { return __IntersectionUtils.calcMostIntersecting(rect, args); }
public static final Rectangle calcMostIntersecting(final Rectangle rect, final Stack<Rectangle> args) { return __IntersectionUtils.calcMostIntersecting(rect, args); }
/** @fudgeRectangleIntersection: returns if the rectangles collide given the tolerance amount */
public static final boolean fudgeRectIntersection(final Rectangle r1, final Rectangle r2, final float fudge_amount) { return __IntersectionUtils.fudgeRectIntersect(r1, r2, fudge_amount); }
public static final float calcArea(final float x1, final float y1, final float x2, final float y2) { return (x2 - x1) * (y2 - y1); }
public static final boolean intersects(final Point2f center, final float radius, final Rectangle rect) { return __IntersectionUtils.intersects(center, radius, rect); }
public static final boolean intersects(final Circle circle, final Rectangle rect) { return __IntersectionUtils.intersects(circle, rect); }
public static final boolean intersects(final Rectangle rect, final Circle circle) { return __IntersectionUtils.intersects(circle, rect); }
public static final boolean intersects(final Circle c1, final Circle c2){ return c1.intersects(c2); }
public static final boolean intersects(final Circle c1, final Point2f center2, final float radius2){ return c1.intersects(center2, radius2); }
public static final boolean intersects(final Point2f c1, final float r1, final Point2f c2, final float r2){ return c1.distance(c2) <= r1 + r2; }
public static final boolean intersects(final Line l1, final Line l2) { return l1.intersects(l2); }
public static final boolean intersects(final Point2f p1, final Point2f p2, final Point2f q1, final Point2f q2) { return new Line(p1, p2).intersects(new Line(q1, q2)); }
public static final boolean contains(final Rectangle r, final Point2f p) { return r.contains(p); }
public static final boolean contains(final Rectangle r, final SimplePoint p) { return r.contains(p); }
public static final boolean contains(final Point2f center, final float radius, final Point2f p){ return __IntersectionUtils.contains(center, radius, p); }
public static final boolean contains(final Point2f center, final float radius, final float x, final float y){ return __IntersectionUtils.contains(center, radius, x, y); }
/********************************************************************************************************/
/************************************* @ALGORITHMS ******************************************************/
public static final SimplePoint[] brensenham(final SimplePoint p1, final SimplePoint p2) { return __Algorithms.brensenham(p1, p2); }
public static final SimplePoint[] brensenham(final Point2f p1, final Point2f p2) { return __Algorithms.brensenham(p1, p2); }
public static final SimplePoint[] brensenham(final int x1, final int y1, final int x2, final int y2) { return __Algorithms.brensenham(x1, y1, x2, y2); }
public static int greatestCommonDivisor(final int p, final int q) { return q == 0 ? p : greatestCommonDivisor(q, p % q); }
/********************************************************************************************************/
}

View File

@@ -0,0 +1,67 @@
package sjgs.utils;
import static sjgs.utils.Utils.abs;
import java.util.ArrayList;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
class __Algorithms {
static SimplePoint[] brensenham(final SimplePoint p1, final SimplePoint p2) {
final ArrayList<SimplePoint> points = new ArrayList<SimplePoint>();
// BRENSENHAM the line across
final int dx = abs(p2.x-p1.x), sx = p1.x<p2.x ? 1 : -1;
final int dy = -abs(p2.y-p1.y), sy = p1.y<p2.y ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
while(true){
points.add(new SimplePoint(p1.x, p1.y));
if (p1.x==p2.x && p1.y==p2.y) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; p1.x += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; p1.y += sy; } /* e_xy+e_y < 0 */
}
return points.toArray(new SimplePoint[points.size()]);
}
static SimplePoint[] brensenham(final Point2f p1, final Point2f p2) {
final ArrayList<SimplePoint> points = new ArrayList<SimplePoint>();
// BRENSENHAM the line across
final int dx = (int)abs(p2.x-p1.x), sx = p1.x<p2.x ? 1 : -1;
final int dy = (int)-abs(p2.y-p1.y), sy = p1.y<p2.y ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
while(true){
points.add(new SimplePoint((int)p1.x, (int)p1.y));
if (p1.x==p2.x && p1.y==p2.y) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; p1.x += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; p1.y += sy; } /* e_xy+e_y < 0 */
}
return points.toArray(new SimplePoint[points.size()]);
}
static SimplePoint[] brensenham(int x1, int y1, final int x2, final int y2) {
final ArrayList<SimplePoint> points = new ArrayList<SimplePoint>();
// BRENSENHAM the line across
final int dx = abs(x2-x1), sx = x1<x2 ? 1 : -1;
final int dy = -abs(y2-y1), sy = y1<y2 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
while(true){
points.add(new SimplePoint(x1, y1));
if (x1==x2 && y1==y2) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x1 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y1 += sy; } /* e_xy+e_y < 0 */
}
return points.toArray(new SimplePoint[points.size()]);
}
}

View File

@@ -0,0 +1,97 @@
package sjgs.utils;
import static sjgs.utils.Utils.dialog;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import javax.imageio.ImageIO;
class __ImageManipulation {
static BufferedImage optimizeImage(final BufferedImage image) {
// obtain the current system graphical settings
final GraphicsConfiguration gfx_config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
/* if image is already compatible and optimized for current system
* settings, simply return it */
if (image.getColorModel().equals(gfx_config.getColorModel())) return image;
// image is not optimized, so create a new image that is
final BufferedImage new_image = gfx_config.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
final Graphics2D g2d = (Graphics2D) new_image.getGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return new_image;
}
static final Color voidColor = new Color(255, 0, 255);
//----------------------------------------------------------------------------------------------------------------------------------------------//
static BufferedImage reverseImage(final BufferedImage image) { /* (flips image horizontally) */
final BufferedImage mirrorImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); /* note, you need ARGB */
for (int y = 0; y < image.getHeight(); y++)
for (int x = 0, reverseX = image.getWidth() - 1; x < image.getWidth(); x++, reverseX--)
mirrorImage.setRGB(reverseX, y, image.getRGB(x, y));
return optimizeImage(mirrorImage);
}
static BufferedImage rotateImageRight(final BufferedImage image) {
final BufferedImage rotatedImage = new BufferedImage(image.getHeight(), image.getWidth(), image.getType());
for(int i=0; i < image.getWidth(); i++ )
for(int j=0; j < image.getHeight(); j++ )
rotatedImage.setRGB( image.getHeight()-1-j, i, image.getRGB(i,j));
return optimizeImage(rotatedImage);
}
// I know this is really bad but the above was so much headache i said fuck it, it works.
static BufferedImage rotateImageLeft(final BufferedImage image) {
return optimizeImage(rotateImageRight(rotateImageRight(rotateImageRight(image))));
}
static BufferedImage invertImage(final BufferedImage image) { /* (flips image horizontally) */
return optimizeImage(rotateImageRight(rotateImageRight(image)));
}
//----------------------------------------------------------------------------------------------------------------------------------------------//
static BufferedImage imageToBufferedImage(Image image) {
final BufferedImage buffered = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
buffered.getGraphics().drawImage(image, 0, 0, null); image = null;
return optimizeImage(buffered);
}
static BufferedImage loadImage(final String path) { /* loads as resource to work in jars */
try { InputStream is = Utils.class.getResourceAsStream(path);
final BufferedImage image = ImageIO.read(is); is.close(); is = null; return optimizeImage(removeVoidColor(image, voidColor));
} catch (final Exception e) { e.printStackTrace(); dialog("Utils --- loadImage --- Failed + \n + Contact the developer."); }
return null;
}
static BufferedImage removeVoidColor(final BufferedImage image, final Color voidColor) {
for(int i = 0; i < image.getWidth(); i++)
for(int j = 0; j < image.getHeight(); j++){
final int RGB = image.getRGB(i, j);
final int red = convertRed(RGB);
final int green = convertGreen(RGB);
final int blue = convertBlue(RGB);
if(red == voidColor.getRed() && green == voidColor.getGreen() && blue == voidColor.getBlue())
image.setRGB(i, j, voidColor.getRGB()&0x00ffffff);
}
return optimizeImage(image);
}
static final BufferedImage grabSprite(final BufferedImage image, final int col, final int row, final int width, final int height) {
return optimizeImage(removeVoidColor(image.getSubimage((col+1) * width - width, (row+1) * height - height, width, height), voidColor));
}
/************************************ IMAGE RGB MANIPULATION ********************************************/
static final int convertRed(final int pixel){ return pixel >> 16 & 0xff; }
static final int convertGreen(final int pixel){ return pixel >> 8 & 0xff; }
static final int convertBlue(final int pixel){ return pixel & 0xff; }
/********************************************************************************************************/
}

View File

@@ -0,0 +1,59 @@
package sjgs.utils;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.shapes.Circle;
import sjgs.utils.data_structures.shapes.Rectangle;
import sjgs.utils.data_structures.vectors.Point2f;
class __IntersectionUtils {
static Rectangle calcMostIntersecting(final Rectangle rect, final Rectangle ...args) {
if(args.length == 0) return null; if(args.length == 1) return args[0];
final Stack<Rectangle> rects = new Stack<Rectangle>();
for(final Rectangle r : args) if(r != null && rect.intersects(r)) rects.push(r);
if(rects.size() == 0) return null;
final float[] areas = new float[rects.size()];
for(int i = 0; i < rects.size(); i++) areas[i] = rect.getIntersectingArea(rects.get(i));
final float largest_area = Utils.max(areas);
final Stack<Rectangle> answers = new Stack<Rectangle>();
for(int i = 0; i < areas.length; i++) if(areas[i] == largest_area) answers.push(rects.get(i));
if(answers.size() == 0) return null;
return answers.get(Utils.rand.nextInt(answers.size()));
}
static Rectangle calcMostIntersecting(final Rectangle rect, final Stack<Rectangle> args) {
if(args.size() == 0) return null; if(args.size() == 1) return args.getFirst();
final Stack<Rectangle> rects = new Stack<Rectangle>();
for(final Rectangle r : args) if(r != null && rect.intersects(r)) rects.push(r);
if(rects.size() == 0) return null;
final float[] areas = new float[rects.size()];
for(int i = 0; i < rects.size(); i++) areas[i] = rect.getIntersectingArea(rects.get(i));
final float largest_area = Utils.max(areas);
final Stack<Rectangle> answers = new Stack<Rectangle>();
for(int i = 0; i < areas.length; i++) if(areas[i] == largest_area) answers.push(rects.get(i));
if(answers.size() == 0) return null;
return answers.get(Utils.rand.nextInt(answers.size()));
}
static boolean fudgeRectIntersect(final Rectangle r1, final Rectangle r2, final float fudge_amount) {
return !(r1.pos.x + r1.width + fudge_amount < r2.pos.x || // if this is LEFT of r
r1.pos.x - fudge_amount > r2.pos.x + r2.width || // if this is RIGHT of r
r1.pos.y + r1.height + fudge_amount < r2.pos.y || // if this is BELOW r
r1.pos.y - fudge_amount > r2.pos.y + r2.height); // if this is ABOVE r
}
// CIRCLES AND RECTS
static boolean intersects(final Circle circle, final Rectangle rect) {
return intersects(circle.center, circle.radius, rect);
}
static boolean intersects(final Point2f center, final float radius, final Rectangle rect){
return Utils.square(center.x - Utils.clamp(center.x, rect.pos.x, rect.pos.x + rect.width)) +
Utils.square(center.y - Utils.clamp(center.y, rect.pos.y, rect.pos.y + rect.height))
< Utils.square(radius);
}
// Un-made circles and Point2fs
static boolean contains(final Point2f center, final float radius, final Point2f p){ return Utils.square(p.x - center.x) + Utils.square(p.y - center.y) < Utils.square(radius); }
static boolean contains(final Point2f center, final float radius, final float x, final float y){ return Utils.square(x - center.x) + Utils.square(y - center.y) < Utils.square(radius); }
}

View File

@@ -0,0 +1,66 @@
package sjgs.utils;
class __MixMaxUtils {
static final int biggest(final int ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.biggest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
int max = args[0];
for(int i = 1; i < args.length; i++) if(args[i] > max) max = args[i];
return max;
}
static final float biggest(final float ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.biggest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
float max = args[0];
for(int i = 1; i < args.length; i++) if(args[i] > max) max = args[i];
return max;
}
static final double biggest(final double ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.biggest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
double max = args[0];
for(int i = 1; i < args.length; i++) if(args[i] > max) max = args[i];
return max;
}
static final int smallest(final int ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.smallest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
int min = args[0];
for(int i = 1; i < args.length; i++) if(args[i] < min) min = args[i];
return min;
}
static final float smallest(final float ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.smallest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
float min = args[0];
for(int i = 1; i < args.length; i++) if(args[i] < min) min = args[i];
return min;
}
static final double smallest(final double ...args) {
if(args.length == 0) { System.err.println("ERROR: Utils.smallest --- args.length == 0"); System.exit(1); }
else if(args.length == 1) return args[0];
double min = args[0];
for(int i = 1; i < args.length; i++) if(args[i] < min) min = args[i];
return min;
}
static final int max(final int ...args) { return biggest(args); }
static final float max(final float ...args) { return biggest(args); }
static final double max(final double ...args) { return biggest(args); }
static final int min(final int ...args) { return smallest(args); }
static final float min(final float ...args) { return smallest(args); }
static final double min(final double ...args) { return smallest(args); }
}

View File

@@ -0,0 +1,35 @@
package sjgs.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
class __SerializationUtils {
static final String userHomeDir = System.getProperty("user.home");
static synchronized void saveObject(final Object obj, final String path){
try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(path)));
oos.writeObject(obj); oos.close(); oos = null;
} catch (final Exception e) { e.printStackTrace(); System.exit(1); }
}
static synchronized Object readObject(final String path){
try { InputStream is = __SerializationUtils.class.getResourceAsStream(path);
ObjectInputStream oos = new ObjectInputStream(is);
final Object object = oos.readObject();
oos.close(); oos = null;
is.close(); is = null;
return object;
} catch (final Exception e) { e.printStackTrace(); System.exit(1); return null; }
}
static synchronized void writeStringToFile(final String string, final String path) {
try (FileWriter fw = new FileWriter(new File(path))) {
fw.write(string);
} catch (final Exception e) { e.printStackTrace(); }
}
}

View File

@@ -0,0 +1,57 @@
package sjgs.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import javax.swing.JOptionPane;
class __TextFileManipulation {
static int getNumLinesFromTextFile(final String path) { /* loads as resource to work in jars */
int count = 0;
try { final InputStream stream = __TextFileManipulation.class.getResourceAsStream(path);
final Scanner scanner = new Scanner(stream);
while (scanner.hasNextLine()) { scanner.nextLine(); count++; }
stream.close(); scanner.close();
} catch (final Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Text Files Failed To Load --- Utils.java"); }
return count;
}
static String[] makeStringArrayFromLinesOfTextFile(final String path) { /* loads as resource to work in jars */
final String[] lines = new String[getNumLinesFromTextFile(path)];
try { final InputStream stream = __TextFileManipulation.class.getResourceAsStream(path);
final Scanner scanner = new Scanner(stream);
for (int i = 0; scanner.hasNextLine(); i++) lines[i] = scanner.nextLine();
stream.close(); scanner.close();
} catch (final Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Text Files Failed To Load --- Utils.java"); }
return lines;
}
static StringBuilder[] makeStringBuilderArrayFromLinesOfTextFile(final String path) { /* loads as resource to work in jars */
final StringBuilder[] lines = new StringBuilder[getNumLinesFromTextFile(path)];
try { final InputStream stream = __TextFileManipulation.class.getResourceAsStream(path);
final Scanner scanner = new Scanner(stream);
for (int i = 0; scanner.hasNextLine(); i++) lines[i] = new StringBuilder(scanner.nextLine());
stream.close(); scanner.close();
} catch (final Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, "Text Files Failed To Load --- Utils.java"); }
return lines;
}
static String readTextFileAsString(final String path){
InputStream stream = null;
// NOTICE: Depending on what was used for making the jar, you *may* or *may not* have a /src/
// super directory, or it may be placed alongside your class files with anything that
// wasn't a *.java file during compile time. Fear not, as this will catch both of these
// cases should they be an issue.
try { stream = __TextFileManipulation.class.getResourceAsStream(path); } catch (final Exception e) {
stream = __TextFileManipulation.class.getResourceAsStream("/src/" + path);
}
Scanner file = new Scanner(stream);
final StringBuilder sb = new StringBuilder();
while(file.hasNextLine()) sb.append(file.nextLine() + "\n"); // NOTE: Preserves new line characters!
try { stream.close(); file.close(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); }
if(sb.toString().length() == 0) System.err.println("PROBLEM: TEXT FILE APPEARS TO BE BLANK? -- Utils.class");
file.close(); file = null; stream = null;
return sb.toString();
}
}

View File

@@ -0,0 +1,131 @@
package sjgs.utils.data_structures;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import sjgs.utils.data_structures.interfaces.StackInterface;
/** @class: A very powerful SinglyLinkedList based data structure. */
public class Stack<E> implements StackInterface<E> {
private Node<E> top;
private int size;
public Stack(final E ...args) { combine(args); }
public Stack(final Stack<E> args) { combine(args); }
@Override
public int size() { return size; }
@Override
public E peek() { return !isEmpty() ? top.data : null; }
@Override
public E get(final int index){
final int target = size - index - 1;
if(isEmpty() || target < 0 || index < 0) throw new NoSuchElementException();
Node<E> current = top;
for(int i = 0; i < target; i++) current = current.prev;
return current.data;
}
@Override
public void push(final E e) { top = new Node<E>(top, e); size++; }
@Override
public E pop() {
if (isEmpty()) return null;
final E answer = top.data;
top = --size == 0 ? null : top.prev;
return answer;
}
@Override
public void remove(final E e) {
if(isEmpty()) throw new NoSuchElementException();
try {
Node<E> current = top;
int depth = size - 1;
while(current.data != e) { current = current.prev; --depth; }
if(get(depth) != null) remove(depth); else throw new NoSuchElementException();
} catch (final NullPointerException npe) { /* no element to remove? */ }
}
@Override
public E remove(final int index){
final int target = size - index - 1;
if(isEmpty() || target < 0 || index < 0) throw new NoSuchElementException();
final Stack<E> temp = new Stack<E>();
for(int i = 0; i < target; i++) temp.push(pop());
final E answer = pop();
while(!temp.isEmpty()) push(temp.pop());
return answer;
}
@Override
public Stack<E> clone() {
final Stack<E> clone = new Stack<E>();
for(final E e : this) clone.push(e);
return clone;
}
public Stack<Stack<E>> split() { return split(2); }
public Stack<Stack<E>> split(final int parts) {
if(size < parts) throw new NoSuchElementException();
// CREATE STACK OF STACKS
final Stack<Stack<E>> stacks = new Stack<Stack<E>>();
for(int i = 0; i < parts; i++) stacks.push(new Stack<E>());
// FILL A TEMP STACK WITH CURRENT STACKS GOODS
final Stack<E> temp = new Stack<E>();
for(int i = 0; i < size; i++) temp.push(get(i));
// FILL EACH STACK IN STACK OF STACKS WITH EQUAL PARTS
for(int i = 0; i < parts; i++)
for(int j = 0; j < size / parts; j++)
stacks.get(i).push(temp.pop());
// IF SIZE IS ODD, THIS WILL CATCH THE STRAGLER
while(!temp.isEmpty()) stacks.peek().push(temp.pop());
return stacks;
}
@Override
public Iterator<E> iterator() { return new StackIterator(); }
public class StackIterator implements Iterator<E>, Serializable {
private Node<E> current;
private E temp; /* avoid unnecessary mass object creation */
public StackIterator() { super(); current = top; }
@Override
public boolean hasNext() { return current != null; }
@Override
public E next() {
if (!hasNext()) throw new NoSuchElementException();
temp = current.data;
current = current.prev;
return temp;
}
}
@Override
public String toString() {
if(isEmpty()) return "STACK IS EMPTY";
Node<E> temp = top; final StringBuilder sb = new StringBuilder();
sb.append("(");
for(int i = 0; i < size; i++, temp = temp.prev){
sb.append(temp.data);
if(i != size - 1) sb.append(", ");
} return sb.toString() + ")";
}
// ----------------- NODE CLASS ------------------------------------------------- //
private static class Node<E> implements Serializable {
public final E data;
public final Node<E> prev;
public Node(final Node<E> prev, final E data) { this.prev = prev; this.data = data; }
}
// -------------- END NODE CLASS ------------------------------------------------- //
}

View File

@@ -0,0 +1,231 @@
package sjgs.utils.data_structures.gaming;
import static sjgs.utils.Utils.COIN_FLIP;
import static sjgs.utils.Utils.rand;
import java.io.Serializable;
import sjgs.base_objects.GameObject;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.Stack;
/** @class: NOTE: This data structure is ONLY to be used for PURELY STATIC objects.
* This implementation does NOT have the ability to continually update
* the objects inside it. It loses the reference to said objects.
* With this in mind, this will work great for tiles and scenery, however
* this will *NOT* work for things like bullet collision, etc. */
public final class QuadTree implements Serializable {
private final Node root;
private int size = 0;
public QuadTree(final float minX, final float minY, final float maxX, final float maxY) {
root = new Node(minX, minY, maxX - minX, maxY - minY, null);
}
// ----------------------------- PUBLIC METHODS ------------------------------------------------------------------ //
public void insert(final GameObject value) {
float x = value.getX(), y = value.getY();
while(contains(x, y)) {
x = COIN_FLIP() ? x + rand.nextInt(Utils._2147483647) * 0.000_000_000_1f : x - rand.nextInt(Utils._2147483647) * 0.000_000_000_1f;
y = COIN_FLIP() ? y + rand.nextInt(Utils._2147483647) * 0.000_000_000_1f : y - rand.nextInt(Utils._2147483647) * 0.000_000_000_1f;
}
if (x < root.getX() || y < root.getY() || x > root.getX() + root.getWidth() || y > root.getY() + root.getHeight()) Utils.print("Out of bounds : (" + x + ", " + y + ")");
else if (insert(root, new Data(x, y, value))) size++;
}
/** @method remove: returns the success rate */
public boolean remove(final GameObject g) { return remove(g.getX(), g.getY()) != null; }
public boolean isEmpty() { return root.getNodeType() == NodeType.EMPTY; }
public int size() { return size; }
public void clear() {
root.nw = root.ne = root.sw = root.se = null;
root.setNodeType(NodeType.EMPTY);
root.setData(null); size = 0;
}
public Stack<GameObject> search(final float xmin, final float ymin, final float xmax, final float ymax) {
final Stack<GameObject> stack = new Stack<GameObject>();
try { navigate(root,(node) -> { final Data pt = node.getData();
if (!(pt.getX() < xmin || pt.getX() > xmax || pt.getY() < ymin || pt.getY() > ymax)) stack.push(node.getData().getGameObject());
}, xmin, ymin, xmax, ymax); } catch (final Exception e) { }
return stack;
}
public Stack<GameObject> toStack() {
final Stack<GameObject> stack = new Stack<GameObject>();
traverse(root, (node) -> stack.push(node.getData().getGameObject()));
return stack;
}
@Override
public QuadTree clone() {
final Stack<GameObject> temp = toStack();
final QuadTree clone = new QuadTree(root.getX(), root.getY(), root.getWidth(), root.getHeight());
for(final GameObject g : temp) clone.insert(g);
return clone;
}
// -------------------------------- END PUBLIC METHODS ---------------------------------------------------------------- //
private GameObject remove(final float x, final float y) {
final Node node = find(root, x, y);
if (node != null) {
final GameObject value = node.getData().getGameObject();
node.setData(null);
node.setNodeType(NodeType.EMPTY);
balance(node);
size--;
return value;
} else return null;
}
private static void navigate(final Node node, final Func func, final float xmin, final float ymin, final float xmax, final float ymax) {
switch (node.getNodeType()) {
case LEAF: func.call(node); break;
case POINTER:
if (intersects(xmin, ymax, xmax, ymin, node.ne)) navigate(node.ne, func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.se)) navigate(node.se, func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.sw)) navigate(node.sw, func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.nw)) navigate(node.nw, func, xmin, ymin, xmax, ymax); break;
}
}
private GameObject get(final float x, final float y) {
final Node node = find(root, x, y);
return node != null ? node.getData().getGameObject() : null;
}
private boolean contains(final float x, final float y) { return get(x, y) != null; }
private static boolean intersects(final float left, final float bottom, final float right, final float top, final Node node) {
return !(node.getX() > right || node.getX() + node.getWidth() < left || node.getY() > bottom || node.getY() + node.getHeight() < top);
}
private static void traverse(final Node node, final Func func) {
switch (node.getNodeType()) {
case LEAF: func.call(node); break;
case POINTER: traverse(node.ne, func);
traverse(node.se, func);
traverse(node.sw, func);
traverse(node.nw, func); break;
}
}
private static Node find(final Node node, final float x, final float y) {
switch (node.getNodeType()) {
case LEAF: return node.getData().getX() == x && node.getData().getY() == y ? node : null;
case POINTER: return find(getQuadrantForData(node, x, y), x, y);
} return null;
}
private boolean insert(final Node parent, final Data data) {
switch (parent.getNodeType()) {
case EMPTY: setDataForNode(parent, data); return true;
case LEAF:
if (parent.getData().getX() == data.getX() && parent.getData().getY() == data.getY()) {
setDataForNode(parent, data);
return false;
} else {
split(parent);
return insert(parent, data);
}
default: return insert(getQuadrantForData(parent, data.getX(), data.getY()), data);
}
}
private void split(final Node node) {
final Data oldData = node.getData();
node.setData(null);
node.setNodeType(NodeType.POINTER);
final float x = node.getX();
final float y = node.getY();
final float hw = node.getWidth() / 2;
final float hh = node.getHeight() / 2;
node.nw = new Node(x, y, hw, hh, node);
node.ne = new Node(x + hw, y, hw, hh, node);
node.sw = new Node(x, y + hh, hw, hh, node);
node.se = new Node(x + hw, y + hh, hw, hh, node);
insert(node, oldData);
}
private static void balance(final Node node) {
if(node.getNodeType() == NodeType.POINTER) {
Node firstLeaf = null;
if (node.nw.getNodeType() != NodeType.EMPTY) firstLeaf = node.nw; // note here it starts @ nw, calc'ing CLOCKWISE
if (node.ne.getNodeType() != NodeType.EMPTY && firstLeaf != null) firstLeaf = node.ne;
else if (node.sw.getNodeType() != NodeType.EMPTY && firstLeaf != null) firstLeaf = node.sw;
else if (node.se.getNodeType() != NodeType.EMPTY && firstLeaf != null) firstLeaf = node.se;
else if (firstLeaf == null) {
node.setNodeType(NodeType.EMPTY);
node.nw = node.ne = node.sw = node.se = null;
}
else if (firstLeaf.getNodeType() != NodeType.POINTER) {
node.setNodeType(NodeType.LEAF);
node.nw = node.ne = node.sw = node.se = null;
node.setData(firstLeaf.getData());
}
}
if (node.getParent() != null) balance(node.getParent());
}
private static Node getQuadrantForData(final Node parent, final float x, final float y) {
final float mx = parent.getX() + parent.getWidth() / 2f;
final float my = parent.getY() + parent.getHeight() / 2f;
if (x < mx) return y < my ? parent.nw : parent.sw; else return y < my ? parent.ne : parent.se;
}
private static void setDataForNode(final Node node, final Data data) {
if (node.getNodeType() == NodeType.POINTER) Utils.print("Can not set data for node of type POINTER");
else { node.setNodeType(NodeType.LEAF); node.setData(data); }
}
private static interface Func { void call(Node node); }
private static enum NodeType { EMPTY, LEAF, POINTER }
private static class Node implements Serializable {
private final float x, y, w, h;
private final Node parent;
private Data data;
private NodeType nodetype = NodeType.EMPTY;
private Node nw, ne, sw, se;
public Node(final float x, final float y, final float w, final float h, final Node parent) {
this.x = x; this.y = y; this.w = w; this.h = h;
this.parent = parent;
}
private float getX() { return x; }
private float getY() { return y; }
private float getWidth() { return w; }
private float getHeight() { return h; }
private Node getParent() { return parent; }
private void setData(final Data data) { this.data = data; }
private Data getData() { return data; }
private void setNodeType(final NodeType nodetype) { this.nodetype = nodetype; }
private NodeType getNodeType() { return nodetype; }
}
private static class Data implements Serializable {
private final float x, y;
private final GameObject game_object;
public Data(final float x, final float y, final GameObject game_object) {
this.x = x; this.y = y; this.game_object = game_object;
}
private float getX() { return x; }
private float getY() { return y; }
private GameObject getGameObject() { return game_object; }
}
}

View File

@@ -0,0 +1,37 @@
package sjgs.utils.data_structures.interfaces;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public interface ListInterface<E> extends Iterable<E>, Serializable {
public abstract int size();
public abstract void add(E e);
public abstract void remove(E e);
public abstract E remove(int index);
public abstract E get(int index);
default void clear() { while(!isEmpty()) removeFirst(); }
default boolean contains(final E e){ for(final E i : this) if (i == e) return true; return false; }
default E getFirst(){ return !isEmpty() ? get(0) : null; }
default E removeFirst() { return !isEmpty() ? remove(0) : null; }
default boolean isEmpty() { return size() == 0; }
default E[] toArray(){
final E[] arr = (E[])new Object[size()];
for(int i = 0; i < size(); i++) arr[i] = get(i);
return arr;
}
default void insert(final E e) { add(e); }
default E first() { return get(0); }
default void writeToFile(final String path) {
try { final ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(path))); oos.writeObject(this); oos.close();
} catch (final Exception e) { e.printStackTrace(); System.exit(1); }
}
}

View File

@@ -0,0 +1,67 @@
package sjgs.utils.data_structures.interfaces;
import static sjgs.utils.Utils.nextInt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.Stack;
public interface StackInterface<E> extends Iterable<E>, Serializable {
public abstract int size();
public abstract void push(E e);
public abstract E pop();
public abstract E peek();
public abstract E get(int index);
public abstract void remove(E e);
public abstract E remove(int index);
@Override
public abstract String toString();
default E first() { return getFirst(); }
default E getFirst(){ return !isEmpty() ? get(0) : null; }
default E getLast() { return peek(); }
default boolean contains(final E e){ for(final E i : this) if (i == e) return true; return false; }
default E removeFirst() { return remove(0); }
default void clear(){ while(!isEmpty()) pop(); }
default void writeToFile(final String path) {
try { final ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(path))); oos.writeObject(this); oos.close();
} catch (final Exception e) { e.printStackTrace(); System.exit(1); }
}
default void combine(final Stack<E> stack) { for(final E e : stack) push(e); }
default void combine(final Stack<E> ...args) { for(final Stack<E> s : args) combine(s); }
default void combine(final E[] arr) { for (final E element : arr)
push(element); }
default void combine(final E[] ...args) { for(final E[] a : args) combine(a); }
default E[] toArray(){
final E[] arr = (E[])new Object[size()];
for(int i = 0; i < size(); i++) arr[i] = get(i);
return arr;
}
default void reverse(){
final Stack<E> temp = new Stack<E>();
while(!isEmpty()) temp.push(removeFirst());
while(!temp.isEmpty()) push(temp.pop());
}
default void shuffle() {
final Stack<E> temp = new Stack<E>();
while(!isEmpty()) temp.push(pop());
while(!temp.isEmpty()) push(temp.remove(nextInt(temp.size())));
}
default void print() { if(isEmpty()) { Utils.print("STACK IS EMPTY"); return; } for(final E e : this) Utils.print(e); }
default boolean isEmpty() { return size() == 0; }
default void insert(final E e) { push(e); }
default void add(final E e) { push(e); }
default E top() { return peek(); }
}

View File

@@ -0,0 +1,41 @@
package sjgs.utils.data_structures.shapes;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import sjgs.utils.Utils;
import sjgs.utils.data_structures.vectors.Point2f;
public class Circle {
public Point2f center;
public float radius;
public Circle(final Point2f center, final float radius){ this.center = center; this.radius = radius; }
public Rectangle squareBounds(){ return new Rectangle((int)(center.x - radius), (int)(center.y - radius), (int)radius, (int)radius); }
public void draw(final Graphics2D g2d){ g2d.drawOval((int)(center.x - radius), (int)(center.y - radius), (int)diameter(), (int)diameter()); }
public void drawFilled(final Graphics2D g2d){ g2d.fillOval((int)(center.x - radius), (int)(center.y - radius), (int)diameter(), (int)diameter()); }
public boolean contains(final Point2f p){ return Utils.square(p.x - center.x) + Utils.square(p.y - center.y) < Utils.square(radius); }
public boolean contains(final Point p){ return Utils.square(p.x - center.x) + Utils.square(p.y - center.y) < Utils.square(radius); }
public boolean contains(final int x, final int y){ return Utils.square(x - center.x) + Utils.square(y - center.y) < Utils.square(radius); }
public boolean contains(final float x, final float y){ return Utils.square(x - center.x) + Utils.square(y - center.y) < Utils.square(radius); }
public float getWidth(){ return radius*2; }
public float getHeight(){ return radius*2; }
public float diameter(){ return radius*2; }
@Override
public String toString(){ return "radius: " + Utils.df.format(radius) + ", " + "center: " + center; }
public boolean intersects(final Circle c) {
return center.distance(c.center) <= radius + c.radius;
}
public boolean intersects(final Point2f center2, final float r2) {
return center.distance(center2) <= radius + r2;
}
}

View File

@@ -0,0 +1,63 @@
package sjgs.utils.data_structures.shapes;
import static sjgs.utils.Utils.max;
import static sjgs.utils.Utils.min;
import java.awt.Graphics2D;
import sjgs.utils.data_structures.vectors.Point2f;
public class Line {
public final Point2f p1, p2;
public final float angle;
public Line(final Point2f p1, final Point2f p2) {
this.p1 = p1; this.p2 = p2;
angle = p1.direction(p2);
}
public Line(final float x1, final float y1, final float x2, final float y2) {
p1 = new Point2f(x1, y1); p2 = new Point2f(x2, y2);
angle = p1.direction(p2);
}
public void draw(final Graphics2D g2d) {
g2d.drawLine((int)p1.x, (int)p1.y, (int)p2.x, (int)p2.y);
}
public boolean intersects(final Line l) { return doIntersect(p1, p2, l.p1, l.p2); }
// ----------------- INTERSECTION HELPERS ------------------------------------------------------------------------------------ //
private boolean onSegment(final Point2f p, final Point2f q, final Point2f r) {
return q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) && q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y);
}
private int orientation(final Point2f p, final Point2f q, final Point2f r) {
final float val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
return val == 0 ? 0 : val > 0? 1: 2; // colinear
}
private boolean doIntersect(final Point2f p1, final Point2f q1, final Point2f p2, final Point2f q2)
{
// Find the four orientations needed for general and
// special cases
final int o1 = orientation(p1, q1, p2);
final int o2 = orientation(p1, q1, q2);
final int o3 = orientation(p2, q2, p1);
final int o4 = orientation(p2, q2, q1);
// Special Cases
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
return o1 != o2 && o3 != o4 ||
o1 == 0 && onSegment(p1, p2, q1) ||
// p1, q1 and p2 are colinear and q2 lies on segment p1q1
o2 == 0 && onSegment(p1, q2, q1) ||
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
o3 == 0 && onSegment(p2, p1, q2) ||
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
o4 == 0 && onSegment(p2, q1, q2);
}
// ---------------- END INTERSECTION HELPERS ------------------------------------------------------------------------------------ //
}

View File

@@ -0,0 +1,120 @@
package sjgs.utils.data_structures.shapes;
import static sjgs.utils.Utils.clamp;
import static sjgs.utils.Utils.max;
import static sjgs.utils.Utils.min;
import static sjgs.utils.Utils.square;
import java.awt.Graphics2D;
import java.awt.Point;
import sjgs.utils.data_structures.vectors.Point2f;
import sjgs.utils.data_structures.vectors.SimplePoint;
public class Rectangle {
public float width, height;
public Point2f pos;
public Rectangle(final float x, final float y, final float width, final float height) {
pos = new Point2f(x, y); this.width = width; this.height = height;
}
public Rectangle(final Point2f pos, final float width, final float height) {
this.pos = new Point2f(pos.x, pos.y); this.width = width; this.height = height;
}
public Rectangle(final Point2f tl, final Point2f tr, final Point2f bl, @SuppressWarnings("unused") final Point2f br) {
pos = tl;
width = tr.x - tl.x;
height = bl.y - tl.y;
}
public boolean intersectsWithCircle(final Circle c) {
return square(c.center.x - clamp(c.center.x, pos.x, pos.x + width)) + square(c.center.y - clamp(c.center.y, pos.y, pos.y + height)) < square(c.radius);
}
public boolean intersects(final Rectangle r) {
return !(pos.x + width < r.pos.x || // if this is LEFT of r
pos.x > r.pos.x + r.width || // if this is RIGHT of r
pos.y + height < r.pos.y || // if this is BELOW r
pos.y > r.pos.y + r.height); // if this is ABOVE r
}
public boolean intersects(final Line line) {
return line.intersects(getTop()) || line.intersects(getRight()) || line.intersects(getBottom()) || line.intersects(getLeft());
}
public float getIntersectingArea(final Rectangle r) {
if(!intersects(r)) return 0;
final float newWidth = min(pos.x + width, r.pos.x + r.width) - max(pos.x, r.pos.x);
final float newHeight = min(pos.y + height, r.pos.y + r.height) - max(pos.y, r.pos.y);
return newWidth * newHeight;
}
public boolean contains(final Point2f p) {
if(!(p.x > pos.x && p.x < pos.x + width)) return false; if(!(p.y > pos.y && p.y < pos.y + height)) return false; return true;
}
public boolean contains(final SimplePoint p) {
if(!(p.x > pos.x && p.x < pos.x + width)) return false; if(!(p.y > pos.y && p.y < pos.y + height)) return false; return true;
}
public boolean contains(final Point p) {
if(!(p.x > pos.x && p.x < pos.x + width)) return false; if(!(p.y > pos.y && p.y < pos.y + height)) return false; return true;
}
public boolean contains(final float x, final float y) {
if(!(x > pos.x && x < pos.x + width)) return false; if(!(y > pos.y && y < pos.y + height)) return false; return true;
}
public boolean contains(final int x, final int y) {
if(!(x > pos.x && x < pos.x + width)) return false; if(!(y > pos.y && y < pos.y + height)) return false; return true;
}
public boolean isSquare() { return width == height; }
public void draw(final Graphics2D g2d) { g2d.drawRect((int)pos.x, (int)pos.y, (int)width, (int)height); }
public void drawFilled(final Graphics2D g2d) { g2d.fillRect((int)pos.x, (int)pos.y, (int)width, (int)height); }
public Triangle toTriangle() {
return new Triangle(new Point2f(getX(), getY() + getHeight()),
new Point2f(getX() + getHalfWidth(), getY()),
new Point2f(getX() + getWidth(), getY() + getHeight()));
}
@Override
public Rectangle clone() { return new Rectangle(getX(), getY(), getWidth(), getHeight()); }
@Override
public String toString() { return "x: " + getX() + ", y: " + getY() + ", w: " + getWidth() + ", h: " + getHeight(); }
public void setLocation(final float x, final float y) { pos.setLocation(x, y); }
public void setSize(final float w, final float h) { width = w; height = h; }
public float getWidth() { return width; }
public void setWidth(final float width) { this.width = width; }
public float getHeight() { return height; }
public void setHeight(final float height) { this.height = height; }
public float getHalfWidth() { return width * 0.5f; }
public float getHalfHeight() { return height * 0.5f; }
public Point2f getPos() { return pos; }
public void setPos(final Point2f pos) { this.pos = pos; }
public Point2f getCenter() { return new Point2f(pos.x + getHalfWidth(), pos.y + getHalfHeight()); }
public float getCenterX() { return pos.x + width*0.5f; }
public float getCenterY() { return pos.y + height*0.5f; }
public float getArea() { return width * height; }
public float getX() { return pos.x; }
public float getY() { return pos.y; }
public void setX(final float x) { pos.x = x; }
public void setY(final float y) { pos.y = y; }
public Point2f getTopLeft() { return pos; }
public Point2f getTopRight() { return new Point2f(getX() + getWidth(), getY()); }
public Point2f getBottomRight() { return new Point2f(getX() + getWidth(), getY() + getHeight()); }
public Point2f getBottomLeft() { return new Point2f(getX(), getY() + getHeight()); }
public Line getTop() { return new Line(getTopLeft(), getTopRight()); }
public Line getBottom() { return new Line(getBottomLeft(), getBottomRight()); }
public Line getLeft() { return new Line(getBottomLeft(), getTopLeft()); }
public Line getRight() { return new Line(getTopRight(), getBottomRight()); }
}

View File

@@ -0,0 +1,24 @@
package sjgs.utils.data_structures.shapes;
import java.awt.Graphics2D;
import sjgs.utils.data_structures.vectors.Point2f;
public class Triangle {
/** points go from bottom left corner @ [0], clockwise */
public final Point2f[] points;
public Triangle(final Point2f a, final Point2f b, final Point2f c) {
points = new Point2f[3];
points[0] = new Point2f(a.getX(), a.getY());
points[1] = new Point2f(b.getX(), b.getY());
points[2] = new Point2f(c.getX(), c.getY());
}
public void draw(final Graphics2D g2d) {
g2d.drawLine((int)points[0].x, (int)points[0].y, (int)points[1].x, (int)points[1].y);
g2d.drawLine((int)points[1].x, (int)points[1].y, (int)points[2].x, (int)points[2].y);
g2d.drawLine((int)points[2].x, (int)points[2].y, (int)points[0].x, (int)points[0].y);
}
}

View File

@@ -0,0 +1,56 @@
package sjgs.utils.data_structures.vectors;
import static sjgs.utils.Utils.atan2;
import java.awt.Graphics2D;
import java.awt.Point;
import sjgs.utils.Utils;
public class Point2f {
public float x, y;
public Point2f(final float x, final float y) { this.x = x; this.y = y; }
public Point2f(final double x, final double y) { this.x = (float)x; this.y = (float)y;}
public Point2f(final int x, final int y) { this.x = x; this.y = y;}
public Point2f(final long x, final long y) { this.x = x; this.y = y;}
public Point2f() { x = 0f; y = 0f; }
public Point2f(final Point2f p) { x = p.x; y = p.y; }
public Point2f(final Point p) { x = p.x; y = p.y; }
public Point2f(final SimplePoint p) { x = p.x; y = p.y; }
public void setLocation(final float x, final float y){ this.x = x; this.y = y; }
public void rotate(final double theta) {
final float temp = Utils.rotateX(x, y, theta);
y = Utils.rotateY(x, y, theta);
x = temp;
}
public float distance(final Point2f p){ return Utils.sqrt(Utils.square(y - p.y) + Utils.square(x - p.x)); }
public float distance(final Point p){ return Utils.sqrt(Utils.square(y - p.y) + Utils.square(x - p.x)); }
public float distance(final SimplePoint p){ return Utils.sqrt(Utils.square(y - p.y) + Utils.square(x - p.x)); }
public int distance(final int x, final int y){ return (int)Utils.sqrt(Utils.square(this.y - y) + Utils.square(this.x - x)); }
public float distance(final float x, final float y){ return Utils.sqrt(Utils.square(this.y - y) + Utils.square(this.x - x)); }
public double distance(final double x, final double y){ return Utils.sqrt(Utils.square(this.y - y) + Utils.square(this.x - x)); }
public long distance(final long x, final long y){ return (long)Utils.sqrt(Utils.square(this.y - y) + Utils.square(this.x - x)); }
// NOTE: returns the angle in radians!
public final float direction(final Point2f p){ return atan2(p.y - y, p.x - x); }
public Point toPoint(){ return new Point((int)x, (int)y); }
public SimplePoint toSimplePoint() { return new SimplePoint((int)x, (int)y); }
public void draw(final Graphics2D g2d){ g2d.drawString(".", x - 2, y); } // need the -2, not sure why but this is correct!
@Override
public String toString(){ return "(" + Utils.df.format(x) + ", " + Utils.df.format(y) + ")"; }
@Override
public Point2f clone() { return new Point2f(this); }
public float getX() { return x; }
public float getY() { return y; }
public void setX(final float x) { this.x = x; }
public void setY(final float y) { this.y = y; }
}

View File

@@ -0,0 +1,15 @@
package sjgs.utils.data_structures.vectors;
public class SimplePoint {
public int x, y;
public SimplePoint(final int x, final int y) { this.x = x; this.y = y; }
public SimplePoint() {}
public void setLocation(final int x, final int y) { this.x = x; this.y = y; }
@Override
public SimplePoint clone() { return new SimplePoint(x, y); }
}

View File

@@ -0,0 +1,39 @@
package sjgs.utils.data_structures.vectors;
import static sjgs.utils.Utils.atan2;
import static sjgs.utils.Utils.cos;
import static sjgs.utils.Utils.sin;
public class Vector2f {
/** @class Vector2f is just a vector, essentially its a copy of Point2f.
* However, I feel the names and variables make this class more explicit,
* and makes it easier to use than just two Point2f's for pos and vel, for example. */
private float x, y;
public Vector2f(final float x, final float y) { this.x = x; this.y = y; }
public Vector2f(final float theta) { setXYtoTheta(theta); }
public void add(final Vector2f vect) { x += vect.x; y += vect.y; }
public void subtract(final Vector2f vect) { x -= vect.x; y -= vect.y; }
public float getX() { return x; }
public float getY() { return y; }
public void setX(final float x) { this.x = x; }
public void setY(final float y) { this.y = y; }
public void setXAndY(final float x, final float y) { this.x = x; this.y = y; }
@Override
public Vector2f clone() { return new Vector2f(x, y); }
@Override
public String toString() { return "v1: " + x + ", v2: " + y; }
public void invertY() { y = -y; }
public void invertX() { x = -x; }
public void invertXandY() { x = -x; y = -y; }
public float getTheta() { return atan2(y, x); }
public void setXYtoTheta(final float theta) { x = cos(theta); y = sin(theta); }
}

View File

@@ -0,0 +1,57 @@
package sjgs.utils.encryption;
import static sjgs.utils.Utils.alphabet;
import static sjgs.utils.Utils.nextByte;
import static sjgs.utils.Utils.nextInt;
import sjgs.utils.Utils;
public class CaesarCipher implements EncryptionInterface {
private final byte[] message;
private int key;
public CaesarCipher(final String message) {
this.message = message.getBytes();
}
public String encrypt(int key) {
if(key < 1 || key > 26) {
Utils.print("INVALID KEY!");
return "INVALID KEY!";
}
this.key = --key; translate(key);
return getMessage();
}
/** @encrypt: default no arg generates a random key */
@Override
public String encrypt() {
key = nextInt(25) + 1;
translate(key);
return getMessage();
}
@Override
public String decrypt() { translate(-key); return getMessage(); }
private String translate(final int k) {
for(int i = 0; i < message.length; i++)
if (Character.isLowerCase(message[i])) message[i] = rotate(message[i], k);
return new String(message);
}
private byte rotate(final byte c, final int key) {
for (int i = 0; i < 26; i++)
if (c == alphabet.charAt(i)) return (byte)alphabet.charAt((i + key + 26) % 26);
Utils.print("ERROR! We shouldn't be here... -- CaeserCipher.class"); return c;
}
/** @method shred: Randomizes current data */
@Override
public void shred() {
for(int i = 0; i < message.length; i++) message[i] = nextByte();
}
@Override
public String toString() { return new String(message); }
}

View File

@@ -0,0 +1,16 @@
package sjgs.utils.encryption;
import sjgs.utils.Utils;
public interface EncryptionInterface {
public abstract String decrypt();
public abstract String encrypt();
public abstract void shred();
@Override
public abstract String toString();
default String getMessage() { return toString(); }
default void print() { Utils.print(getMessage()); }
}

View File

@@ -0,0 +1,45 @@
package sjgs.utils.encryption;
/** @class: This is essentially a CaeserCipher for every character
* in the encrypted message. Much more secure and more
* truly "encrypted". Each letter in the message has
* its own randomly generated key */
public class StrongCaesarCipher implements EncryptionInterface {
private final CaesarCipher[] message;
public StrongCaesarCipher(final String message) {
this.message = new CaesarCipher[message.length()];
for(int i = 0; i < message.length(); i++)
this.message[i] = new CaesarCipher(""+message.charAt(i));
}
@Override
public String decrypt() {
for (final CaesarCipher element : message)
element.decrypt();
return getMessage();
}
@Override
public String encrypt() {
for (final CaesarCipher element : message)
element.encrypt();
return getMessage();
}
@Override
public void shred() { for (final CaesarCipher element : message)
element.shred(); }
@Override
public String getMessage() { return toString(); }
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (final CaesarCipher element : message)
sb.append(element.toString());
return sb.toString();
}
}

36
src/sjgs/utils/io/SaveFile.java Executable file
View File

@@ -0,0 +1,36 @@
package sjgs.utils.io;
import java.io.Serializable;
import sjgs.core.Engine;
public abstract class SaveFile extends __HandlerSaveState implements Serializable {
// ----------- engine variables -------------- //
private final double engineTickInterval, engineFpsCap;
private final boolean engineDrawFPS;
// ------------------------------------------- //
public SaveFile(final Engine engine) {
super();
engineTickInterval = engine.getTickRate();
engineFpsCap = engine.getFPS_CAP();
engineDrawFPS = engine.getDrawFps();
}
public abstract void load(Engine engine);
private void loadIntoEngine(final Engine engine) {
engine.setDrawFps(engineDrawFPS);
engine.setTickRate(engineTickInterval);
engine.setFPS_CAP(engineFpsCap);
}
// returns success status
protected boolean loadEngineAndHandler(final Engine engine) {
try { loadIntoEngine(engine);
loadIntoHandler();
return true;
} catch (final Exception e) { e.printStackTrace(); return false; }
}
}

View File

@@ -0,0 +1,51 @@
package sjgs.utils.io;
import java.io.Serializable;
import sjgs.base_objects.GameObject;
import sjgs.core.Handler;
import sjgs.utils.data_structures.Stack;
import sjgs.utils.data_structures.gaming.QuadTree;
/** @HandlerSaveState: This is just for ease of use. Isn't entirely necessary.
* Just zips up all the @Handler data to be passed around.
* Easy to use! Just two no arg calls, the @constructor and
* then @method loadIntoHandler() */
class __HandlerSaveState implements Serializable {
private final QuadTree stationary_hard_objects, stationary_soft_objects;
private final Stack<GameObject> players, mobs, bullets, mobile_hard_objects, mobile_soft_objects;
private final int TREE_OFFSET, TREE_BOUNDS;
public __HandlerSaveState() {
stationary_hard_objects = Handler.stationary_hard_objects.clone();
stationary_soft_objects = Handler.stationary_soft_objects.clone();
players = Handler.players.clone();
mobs = Handler.mobs.clone();
bullets = Handler.bullets.clone();
mobile_hard_objects = Handler.mobile_hard_objects.clone();
mobile_soft_objects = Handler.mobile_soft_objects.clone();
TREE_OFFSET = Handler.getTreeOffset();
TREE_BOUNDS = Handler.getTreeBounds();
}
protected boolean loadIntoHandler() {
try { Handler.clearAll();
Handler.stationary_hard_objects = stationary_hard_objects;
Handler.stationary_soft_objects = stationary_soft_objects;
Handler.players = players;
Handler.mobs = mobs;
Handler.bullets = bullets;
Handler.mobile_hard_objects = mobile_hard_objects;
Handler.mobile_soft_objects = mobile_soft_objects;
Handler.setBounds(TREE_BOUNDS);
Handler.setTreeOffset(TREE_OFFSET);
return true;
} catch (final Exception e) { e.printStackTrace(); return false; }
}
}

View File

@@ -0,0 +1,3 @@
package sjgs.utils.multithreading;
public interface Executable { void execute(); }

View File

@@ -0,0 +1,21 @@
package sjgs.utils.multithreading;
public class Runner extends Thread {
private final Executable e;
public Runner(final Executable e) {
super();
this.e = e;
}
public Runner(final Executable e, final String name) {
super();
this.e = e;
setName(name);
}
@Override
public void run() { try { e.execute(); } catch (final Exception e) { e.printStackTrace(); } }
}

View File

@@ -0,0 +1,47 @@
package sjgs.utils.multithreading;
import java.util.ArrayList;
import sjgs.utils.Utils;
public class ThreadPool extends ThreadGroup {
private final ArrayList<Runnable> taskQueue;
public ThreadPool() {
super("ThreadPool");
taskQueue = new ArrayList<Runnable>();
setDaemon(true);
for (int i = 0; i < Utils.getNumProcessors(); i++) new PooledThread(this).start();
}
private synchronized Runnable getTask() {
while (taskQueue.isEmpty()) try { wait(); } catch (final InterruptedException e) { e.printStackTrace(); }
return taskQueue.remove(0);
}
public synchronized void runTask(final Runnable task) {
taskQueue.add(task);
notify();
}
private static class PooledThread extends Thread {
private final ThreadPool pool;
public PooledThread(final ThreadPool pool) {
super(pool, "PooledThread");
this.pool = pool;
}
@Override
public void run() {
while (!isInterrupted()) {
final Runnable task = pool.getTask();
try { task.run(); } catch (final Exception e) { e.printStackTrace(); }
}
}
}
}

View File

@@ -0,0 +1,38 @@
package sjgs.utils.pyutils;
import static sjgs.core.jython.Jython.pi;
import java.io.InputStream;
import org.python.core.Py;
import org.python.core.PyBoolean;
import org.python.core.PyFloat;
import org.python.core.PyFunction;
import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.core.PySystemState;
import sjgs.core.jython.Jython;
import sjgs.utils.Utils;
public class PyUtils {
public static final PyBoolean True = Py.True, False = Py.False;
public static final PyInteger One = Py.One, Zero = Py.Zero;
public static final synchronized void initializePySystem() { PySystemState.initialize(System.getProperties(), System.getProperties()); }
public static final void execPyScript(final String filename) {
InputStream in = Utils.class.getResourceAsStream(filename);
Jython.pi.execfile(filename);
try { in.close(); in = null; } catch (final Exception e) { e.printStackTrace(); }
}
public static PyFunction createPyFunction(String funcName) { return pi.get(funcName, PyFunction.class); }
public static PyObject java2py(final Object o) { return Py.java2py(o); }
public static PyInteger int2py(final int i) { return new PyInteger(i); }
public static PyFloat float2py(final float f) { return new PyFloat(f); }
public static PyBoolean bool2py(final boolean b) { return b ? True : False; }
public static <T> Object tojava(final PyObject o, final Class<T> c) { return Py.tojava(o, c); }
}

26
src/sjgs/utils/tools/Timer.java Executable file
View File

@@ -0,0 +1,26 @@
package sjgs.utils.tools;
public class Timer {
// NOTE THESE VARIABLES ARE BASED ON HAVING A 60 TPS TICK METHOD!
public static final int SECOND = 60, MINUTE = SECOND*60;
private final int duration;
private int timeRemaining;
private boolean paused;
public Timer(final int duration) { this.duration = timeRemaining = duration; }
public boolean tick() { return paused ? false : --timeRemaining <= 0; }
public void reset() { timeRemaining = duration; }
public void pause() { paused = true; }
public void resume() { paused = false; }
public void toggle() { paused = !paused; }
public void destroy() { timeRemaining = 0; paused = false; }
}

View File

@@ -0,0 +1,7 @@
package sjgs.world_generation;
public interface Action {
public abstract void run(int x, int y);
}

View File

@@ -0,0 +1,52 @@
package sjgs.world_generation;
import static sjgs.graphics.Colors.voidColor;
import static sjgs.utils.Utils.loadImage;
import static sjgs.utils.Utils.print;
import static sjgs.utils.Utils.unpackBlue;
import static sjgs.utils.Utils.unpackGreen;
import static sjgs.utils.Utils.unpackRed;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.HashMap;
public class TileMap {
private final HashMap<Color, Action> table;
private final BufferedImage map;
private final int TILE_SIZE;
public TileMap(final BufferedImage image, final int TILE_SIZE) {
map = image;
this.TILE_SIZE = TILE_SIZE;
table = new HashMap<Color, Action>();
}
/** @constructor: can also take a param as a string to load the image for you! */
public TileMap(final String image_path, final int TILE_SIZE) {
map = loadImage(image_path);
this.TILE_SIZE = TILE_SIZE;
table = new HashMap<Color, Action>();
}
public void setColorAction(final Color color, final Action action) { table.put(color, action); }
public void generateWorld() {
try {
for (int i = 0; i < map.getHeight(); i++)
for (int j = 0; j < map.getWidth(); j++) {
final int pixel = map.getRGB(i, j);
final Color color = new Color(unpackRed(pixel), unpackGreen(pixel), unpackBlue(pixel));
// grab the Action from the HashMap, make the i, j be the x,y of the Action
if(color != voidColor) table.get(color).run(i, j);
}
} catch (final ArrayIndexOutOfBoundsException e) {
print("The image must be square to be used as a TileMap." + "\n" +
"Hint: Use the void color to fill the rest of the square.");
}
}
}