ProjectZombie/src/projectzombie/entity/Entity.java

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;
}
}