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 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 ecl = Entities.ENTITIES.get(id); Constructor 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; } }