package com.flansmod.common.driveables; import net.minecraft.nbt.NBTTagCompound; import com.flansmod.common.guns.BulletType; import com.flansmod.common.guns.EntityBullet; import com.flansmod.common.guns.raytracing.FlansModRaytracer.DriveableHit; import com.flansmod.common.vector.Vector3f; public class DriveablePart { public EnumDriveablePart type; public CollisionBox box; public int maxHealth; public int health; public int fireTime; public boolean onFire; /** Keeps track of whether death code has been called or not */ public boolean dead; public DriveablePart(EnumDriveablePart e, CollisionBox b) { type = e; box = b; health = maxHealth = b == null ? 0 : b.health; } public void update(EntityDriveable driveable) { if(fireTime > 0) fireTime--; if(fireTime == 0) onFire = false; if(onFire) health--; if(health <= 0 && maxHealth > 0) { dead = true; } } public void writeToNBT(NBTTagCompound tags) { tags.setInteger(type.getShortName() + "_Health", health); tags.setBoolean(type.getShortName() + "_Fire", onFire); } public void readFromNBT(NBTTagCompound tags) { if(!tags.hasKey(type.getShortName() + "_Health")) { health = maxHealth; onFire = false; return; } health = tags.getInteger(type.getShortName() + "_Health"); onFire = tags.getBoolean(type.getShortName() + "_Fire"); } /** Called when a corner of this part hits the ground. * @return The amount of damage to do to the block */ public float smashIntoGround(EntityDriveable driveable, float damage) { //In these cases, there was no collision, so don't damage this or the block if(box == null || dead) return 0; if(!driveable.canHitPart(type)) return 0; //This part is immortal. Smash that block into oblivion if(maxHealth == 0) return damage; //As standard, take half damage and return the other half health -= (int)(damage / 2F); return damage / 2F; } /** Called by bullets that may have hit the plane * @return A bullet hit if it hit. Otherwise null */ public DriveableHit rayTrace(EntityDriveable driveable, Vector3f origin, Vector3f motion) { if(box == null || health <= 0 || dead) return null; if(!driveable.canHitPart(type)) return null; //Complicated. Will explain later. Someone remind me. /* boolean enteringX = coordIsEntering(origin.x, origin.x + motion.x, box.x / 16F, (box.x + box.w) / 16F); boolean enteringY = coordIsEntering(origin.y, origin.y + motion.y, box.y / 16F, (box.y + box.h) / 16F); boolean enteringZ = coordIsEntering(origin.z, origin.z + motion.z, box.z / 16F, (box.z + box.d) / 16F); boolean inX = coordIsIn(origin.x, origin.x + motion.x, box.x / 16F, (box.x + box.w) / 16F); boolean inY = coordIsIn(origin.y, origin.y + motion.y, box.y / 16F, (box.y + box.h) / 16F); boolean inZ = coordIsIn(origin.z, origin.z + motion.z, box.z / 16F, (box.z + box.d) / 16F); boolean hit = (enteringX && inY && inZ) || (inX && enteringY && inZ) || (inX && inY && enteringZ); */ //We now have an AABB starting at box(x, y, z) and with dimensions box(w, h, d) and our ray in the same coordinate system //We are looking for a point at which the ray enters the box, so we need only consider faces that the ray can see. Partition the space into 3 areas in each axis //X - axis and faces x = box.x and x = box.x + box.w if(motion.x != 0F) { if(origin.x < box.x) //Check face x = o.x { float intersectTime = (box.x - origin.x) / motion.x; float intersectY = origin.y + motion.y * intersectTime; float intersectZ = origin.z + motion.z * intersectTime; if(intersectY >= box.y && intersectY <= box.y + box.h && intersectZ >= box.z && intersectZ <= box.z + box.d) return new DriveableHit(driveable, type, intersectTime); } else if(origin.x > box.x + box.w) //Check face x = o.x + d.x { float intersectTime = (box.x + box.w - origin.x) / motion.x; float intersectY = origin.y + motion.y * intersectTime; float intersectZ = origin.z + motion.z * intersectTime; if(intersectY >= box.y && intersectY <= box.y + box.h && intersectZ >= box.z && intersectZ <= box.z + box.d) return new DriveableHit(driveable, type, intersectTime); } } //Z - axis and faces z = box.z and z = box.z + box.d if(motion.z != 0F) { if(origin.z < box.z) //Check face z = box.z { float intersectTime = (box.z - origin.z) / motion.z; float intersectX = origin.x + motion.x * intersectTime; float intersectY = origin.y + motion.y * intersectTime; if(intersectX >= box.x && intersectX <= box.x + box.w && intersectY >= box.y && intersectY <= box.y + box.h) return new DriveableHit(driveable, type, intersectTime); } else if(origin.z > box.z + box.d) //Check face z = box.z + box.d { float intersectTime = (box.z + box.d - origin.z) / motion.z; float intersectX = origin.x + motion.x * intersectTime; float intersectY = origin.y + motion.y * intersectTime; if(intersectX >= box.x && intersectX <= box.x + box.w && intersectY >= box.y && intersectY <= box.y + box.h) return new DriveableHit(driveable, type, intersectTime); } } //Y - axis and faces y = box.y and y = box.y + box.h if(motion.y != 0F) { if(origin.y < box.y) //Check face y = o.y { float intersectTime = (box.y - origin.y) / motion.y; float intersectX = origin.x + motion.x * intersectTime; float intersectZ = origin.z + motion.z * intersectTime; if(intersectX >= box.x && intersectX <= box.x + box.w && intersectZ >= box.z && intersectZ <= box.z + box.d) return new DriveableHit(driveable, type, intersectTime); } else if(origin.y > box.y + box.h) //Check face x = box.y + box.h { float intersectTime = (box.y + box.h - origin.y) / motion.y; float intersectX = origin.x + motion.x * intersectTime; float intersectZ = origin.z + motion.z * intersectTime; if(intersectX >= box.x && intersectX <= box.x + box.w && intersectZ >= box.z && intersectZ <= box.z + box.d) return new DriveableHit(driveable, type, intersectTime); } } return null; } /** Called when the bullet decided that it hit this driveable part */ public void hitByBullet(BulletType type, float damage) { //Perform damage code health -= damage * type.damageVsDriveable; if(type.setEntitiesOnFire) { fireTime = 20; onFire = true; } } /** Ray traces a single co-ordinate * But only returns true once upon entering the box, and not while in or exiting * @param start The start of the ray in this co-ordinate * @param end The end of the ray in this co-ordinate * @param min The start of the box in this co-ordinate * @param max The end of the box in this co-ordinate * @return true if the ray hits in this co-ordinate */ private boolean coordIsEntering(float start, float end, float min, float max) { //Check to see if ray entered from the left hand side if(start < min && end >= min) return true; //Check to see if ray entered from the right hand side if(start > max && end <= max) return true; return false; } /** Ray traces a single co-ordinate * Returns true if the ray is in the bounds at some point along its length * @param start The start of the ray in this co-ordinate * @param end The end of the ray in this co-ordinate * @param min The start of the box in this co-ordinate * @param max The end of the box in this co-ordinate * @return true if the ray hits in this co-ordinate */ private boolean coordIsIn(float start, float end, float min, float max) { //Check to see if the start point is in if(start >= min && start <= max) return true; //Check to see if the end point is in if(end >= min && end <= max) return true; //Check to see if the start and end points are either side of the interval if(start < min && end > max) return true; if(end < min && start > max) return true; return false; } public boolean attack(float damage, boolean fireDamage) { health -= damage; if(fireDamage) { fireTime = 20; onFire = true; } return health <= 0; } }