376 lines
9.1 KiB
Java
Executable File
376 lines
9.1 KiB
Java
Executable File
package projectzombie.entity;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.Random;
|
|
|
|
import bdf.classes.IBdfClassManager;
|
|
import bdf.types.BdfNamedList;
|
|
import bdf.types.BdfObject;
|
|
import gl_engine.MathHelpers;
|
|
import gl_engine.vec.Vec2d;
|
|
import gl_engine.vec.Vec2i;
|
|
import gl_engine.vec.Vec3d;
|
|
import projectzombie.Main;
|
|
import projectzombie.init.Entities;
|
|
import projectzombie.model.IModel;
|
|
import projectzombie.tiles.Tile;
|
|
import projectzombie.util.math.TileState;
|
|
import projectzombie.world.chunk.Chunk;
|
|
import projectzombie.world.layer.Layer;
|
|
|
|
public abstract class Entity implements IBdfClassManager
|
|
{
|
|
public double hitbox = 1;
|
|
public boolean isSolid = false;
|
|
public Chunk chunk;
|
|
private TileState tile_front;
|
|
private TileState tile_back;
|
|
protected static final Random rand = new Random();
|
|
public boolean emitsLight = false;
|
|
private boolean isDead = false;
|
|
protected boolean hasGravity = true;
|
|
|
|
private Vec3d pos;
|
|
private Vec3d velocity;
|
|
|
|
public abstract IModel getModel();
|
|
|
|
public boolean isDead() {
|
|
return isDead;
|
|
}
|
|
|
|
public Vec3d getPos() {
|
|
return pos;
|
|
}
|
|
|
|
public void setPos(Vec3d pos) {
|
|
this.pos = pos;
|
|
}
|
|
|
|
public Vec3d getVelocity() {
|
|
return velocity;
|
|
}
|
|
|
|
public void setVelocity(Vec3d velocity) {
|
|
this.velocity = velocity;
|
|
}
|
|
|
|
protected double getTilesLightDissipation() {
|
|
if(chunk == null) return 0;
|
|
TileState tsf = chunk.getFrontTile(pos.xz().toInt());
|
|
TileState tsb = chunk.getBackTile(pos.xz().toInt());
|
|
return MathHelpers.biggest(
|
|
tsf.tile.getLightDissipation(tsf),
|
|
tsb.tile.getLightDissipation(tsb));
|
|
}
|
|
|
|
protected double getLightWithHeight(double light) {
|
|
double height = Math.abs(getVelocity().y) + 1;
|
|
return light - (this.getTilesLightDissipation() * height);
|
|
}
|
|
|
|
public int getID() {
|
|
for(int i=0;i<Entities.ENTITIES.size();i++) {
|
|
Class<? extends Entity> ec = Entities.ENTITIES.get(i);
|
|
if(ec == this.getClass()) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static Entity loadEntity(BdfObject bdf)
|
|
{
|
|
try
|
|
{
|
|
// Load the entity id
|
|
BdfNamedList nl = bdf.getNamedList();
|
|
int id = nl.get("id").getInteger();
|
|
|
|
// Send back null if the id is out of range
|
|
if(id < 0 || id >= Entities.ENTITIES.size()) {
|
|
System.out.println("Warning: Invalid ID detected: " + id);
|
|
return null;
|
|
}
|
|
|
|
// Get the class and the constructor
|
|
Class<? extends Entity> ecl = Entities.ENTITIES.get(id);
|
|
Constructor<? extends Entity> econ = ecl.getConstructor(BdfObject.class);
|
|
|
|
// Send back the new entity
|
|
return econ.newInstance(bdf);
|
|
}
|
|
|
|
catch(
|
|
NoSuchMethodException |
|
|
SecurityException |
|
|
InstantiationException |
|
|
IllegalAccessException |
|
|
IllegalArgumentException |
|
|
InvocationTargetException e)
|
|
{
|
|
e.printStackTrace();
|
|
|
|
// Send null if there was an issue
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public Entity(Vec3d pos, Vec3d velocity)
|
|
{
|
|
// Store the specified values
|
|
this.velocity = velocity.copy();
|
|
this.pos = pos.copy();
|
|
}
|
|
|
|
protected Entity() {
|
|
}
|
|
|
|
@Override
|
|
public void BdfClassLoad(BdfObject bdf) {
|
|
BdfNamedList nl = bdf.getNamedList();
|
|
pos = new Vec3d(nl.get("pos"));
|
|
velocity = new Vec3d(nl.get("velocity"));
|
|
hitbox = nl.get("hitbox").getDouble();
|
|
isSolid = nl.get("isSolid").getBoolean();
|
|
emitsLight = nl.get("emitsLight").getBoolean();
|
|
hasGravity = nl.get("hasGravity").getBoolean();
|
|
}
|
|
|
|
@Override
|
|
public void BdfClassSave(BdfObject bdf)
|
|
{
|
|
// Get the ID and return if its invalid (entity can't be saved)
|
|
int id = getID();
|
|
if(id == -1) return;
|
|
|
|
BdfNamedList nl = bdf.getNamedList();
|
|
nl.set("id", bdf.newObject().setInteger(getID()));
|
|
|
|
BdfObject pos_bdf = bdf.newObject();
|
|
pos.BdfClassSave(pos_bdf);
|
|
|
|
BdfObject velocity_bdf = bdf.newObject();
|
|
velocity.BdfClassSave(velocity_bdf);
|
|
|
|
nl.set("pos", pos_bdf);
|
|
nl.set("velocity", velocity_bdf);
|
|
nl.set("hitbox", bdf.newObject().setDouble(hitbox));
|
|
nl.set("isSolid", bdf.newObject().setBoolean(isSolid));
|
|
nl.set("emitsLight", bdf.newObject().setBoolean(emitsLight));
|
|
nl.set("hasGravity", bdf.newObject().setBoolean(hasGravity));
|
|
}
|
|
|
|
public void tick(Chunk chunk, Layer layer)
|
|
{
|
|
//speed = 1;
|
|
//angle = MathHelpers.mod(angle, 360);
|
|
if(chunk == null) chunk = layer.getChunk(pos.xz());
|
|
this.chunk = chunk;
|
|
|
|
Vec2i tpos = pos.xz().toInt();
|
|
boolean pos_is_legal = moveIsLegal(pos.xz());
|
|
|
|
tile_back = chunk.getBackTile(tpos);
|
|
tile_front = chunk.getFrontTile(tpos);
|
|
|
|
if(velocity.x != 0 || velocity.y != 0 || velocity.z != 0)
|
|
{
|
|
Vec3d new_pos = pos.add(velocity);
|
|
|
|
double slipperiness = Math.max(
|
|
tile_back.tile.getSlipperiness(tile_back),
|
|
tile_front.tile.getSlipperiness(tile_front));
|
|
|
|
if(isSolid) {
|
|
|
|
if(moveIsLegal(new Vec2d(new_pos.x, pos.z)) || !pos_is_legal) {
|
|
pos.x = new_pos.x;
|
|
} else {
|
|
velocity.x *= -0.25;
|
|
}
|
|
|
|
if(moveIsLegal(new Vec2d(pos.x, new_pos.z)) || !pos_is_legal) {
|
|
pos.z = new_pos.z;
|
|
} else {
|
|
velocity.z *= -0.25;
|
|
}
|
|
|
|
} else {
|
|
pos.x = new_pos.x;
|
|
pos.z = new_pos.z;
|
|
}
|
|
|
|
if(new_pos.y > 0) {
|
|
pos.y = new_pos.y;
|
|
} else if(pos.y >= 0) {
|
|
velocity.y *= -0.25;
|
|
velocity.x *= slipperiness;
|
|
velocity.z *= slipperiness;
|
|
pos.y = 0;
|
|
}
|
|
}
|
|
|
|
if(hasGravity) {
|
|
velocity.y -= MathHelpers.FallSpeed;
|
|
}
|
|
|
|
if(isSolid)
|
|
{
|
|
moveAwayFromSolidEntities(layer);
|
|
|
|
if(pos.y <= 0) {
|
|
tile_back.tile.onWalkedOn(chunk, layer, tpos, this, tile_back);
|
|
tile_front.tile.onWalkedOn(chunk, layer, tpos, this, tile_front);
|
|
}
|
|
|
|
// Move the player out of a solid area
|
|
if(!pos_is_legal)
|
|
{
|
|
Vec2i collision_pos = pos.xz().toInt();
|
|
Vec2d direction = pos.xz().subtract(collision_pos.toDouble()).subtract(0.5);
|
|
|
|
// Do this to prevent division by zero, just move in an arbitrary direction
|
|
if(direction.x == 0 && direction.y == 0) {
|
|
push(new Vec3d(0.1, 0, 0));
|
|
}
|
|
|
|
else {
|
|
push(direction.multiply(0.1).xny());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void moveAwayFromSolidEntities(Layer layer)
|
|
{
|
|
if(isSolid)
|
|
{
|
|
for(Entity e : layer.getNearbyEntities(pos.xz(), hitbox))
|
|
{
|
|
if(e.isSolid && e != this)
|
|
{
|
|
double distance = pos.distance(e.pos);
|
|
double angle = MathHelpers.atan2(e.pos.z - pos.z, e.pos.x - pos.x);
|
|
|
|
Vec3d vec_push = MathHelpers.moveTowards2(distance == 0 ? 1 : (0.5 / distance), angle).xny();
|
|
|
|
push(vec_push.multiply(-0.125));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void push(Vec3d vec)
|
|
{
|
|
if(pos.y <= 0)
|
|
{
|
|
Layer layer = Main.world.getLayer();
|
|
|
|
Vec2i tpos = pos.xz().toInt();
|
|
tile_back = layer.getBackTile(tpos);
|
|
tile_front = layer.getFrontTile(tpos);
|
|
|
|
double slipperiness = Math.max(
|
|
tile_back.tile.getSlipperiness(tile_back),
|
|
tile_front.tile.getSlipperiness(tile_front));
|
|
|
|
double resistance = Math.max(
|
|
tile_back.tile.getResistance(tile_back),
|
|
tile_front.tile.getResistance(tile_front));
|
|
|
|
velocity = velocity.add(vec.multiply((1 - slipperiness) * (1 - resistance)));
|
|
}
|
|
|
|
else
|
|
{
|
|
velocity = velocity.add(vec);
|
|
}
|
|
}
|
|
|
|
public void kill() {
|
|
chunk.killEntity(this);
|
|
isDead = true;
|
|
}
|
|
|
|
public void activateTile(Vec2i tpos)
|
|
{
|
|
// Get the tile position and the layer
|
|
Layer layer = Main.world.getLayer();
|
|
TileState tile_front = layer.getFrontTile(tpos);
|
|
TileState tile_back = layer.getBackTile(tpos);
|
|
|
|
// Activate both tiles
|
|
tile_front.tile.onActivated(layer, tpos, this, tile_front);
|
|
tile_back.tile.onActivated(layer, tpos, this, tile_back);
|
|
}
|
|
|
|
public void activateSteppedOnTile()
|
|
{
|
|
// Get the tile position and the layer
|
|
Layer layer = Main.world.getLayer();
|
|
Vec2i tpos = new Vec2i(MathHelpers.floor(pos.x)+0, MathHelpers.floor(pos.z)+0);
|
|
|
|
// Activate both tiles
|
|
tile_front.tile.onWalkedOn(chunk, layer, tpos, this, tile_front);
|
|
tile_back.tile.onWalkedOn(chunk, layer, tpos, this, tile_back);
|
|
}
|
|
|
|
private boolean moveIsLegal(Vec2d pos)
|
|
{
|
|
Vec2i t_pos = new Vec2i(MathHelpers.floor(pos.x)+0, MathHelpers.floor(pos.y)+0);
|
|
Chunk chunk = Main.world.getLayer().getChunk(pos);
|
|
tile_back = chunk.getBackTile(t_pos);
|
|
tile_front = chunk.getFrontTile(t_pos);
|
|
|
|
// Is this entity solid
|
|
if(isSolid)
|
|
{
|
|
// Check the tile the player is standing on
|
|
Vec2i tpos = new Vec2i(MathHelpers.floor(pos.x)+0, MathHelpers.floor(pos.y)+0);
|
|
|
|
{
|
|
// Get the tile
|
|
Tile t = tile_back.tile;
|
|
|
|
// Check the tiles hitbox if the tile is solid
|
|
if(t.tileSolid)
|
|
{
|
|
// Is the entity in the tiles hitbox
|
|
Vec2d check = pos.subtract(new Vec2d(tpos.x + 0.5, tpos.y + 0.5));
|
|
if(Math.max(Math.abs(check.x), Math.abs(check.y)) <= t.tileHitbox)
|
|
{
|
|
// Send back false
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// Get the front tile
|
|
Tile t = tile_front.tile;
|
|
|
|
// Check the tiles hitbox if the tile is solid
|
|
if(t.tileSolid)
|
|
{
|
|
// Is the entity in the tiles hitbox
|
|
Vec2d check = pos.subtract(new Vec2d(tpos.x + 0.5, tpos.y + 0.5));
|
|
if(Math.max(Math.abs(check.x), Math.abs(check.y)) <= t.tileHitbox)
|
|
{
|
|
// Send back false
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send back true if everything passes
|
|
return true;
|
|
}
|
|
|
|
public double getLightLevel() {
|
|
return 0;
|
|
}
|
|
}
|