package com.flansmod.common.driveables.mechas; import java.util.ArrayList; import io.netty.buffer.ByteBuf; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.BlockPos; import net.minecraft.util.DamageSource; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; import net.minecraft.world.WorldSettings.GameType; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import com.flansmod.client.debug.EntityDebugVector; import com.flansmod.client.gui.GuiDriveableController; import com.flansmod.client.model.GunAnimations; import com.flansmod.common.FlansMod; import com.flansmod.common.RotatedAxes; import com.flansmod.common.driveables.DriveableData; import com.flansmod.common.driveables.DriveablePart; import com.flansmod.common.driveables.DriveableType; import com.flansmod.common.driveables.EntityDriveable; import com.flansmod.common.driveables.EntitySeat; import com.flansmod.common.driveables.EnumDriveablePart; import com.flansmod.common.guns.BulletType; import com.flansmod.common.guns.EnumFireMode; import com.flansmod.common.guns.GunType; import com.flansmod.common.guns.InventoryHelper; import com.flansmod.common.guns.ItemBullet; import com.flansmod.common.guns.ItemGun; import com.flansmod.common.guns.ItemShootable; import com.flansmod.common.guns.ShootableType; import com.flansmod.common.network.PacketDriveableDamage; import com.flansmod.common.network.PacketDriveableGUI; import com.flansmod.common.network.PacketDriveableKey; import com.flansmod.common.network.PacketMechaControl; import com.flansmod.common.network.PacketPlaySound; import com.flansmod.common.teams.TeamsManager; import com.flansmod.common.tools.ItemTool; import com.flansmod.common.vector.Vector3f; import com.flansmod.common.vector.Vector3i; public class EntityMecha extends EntityDriveable { private int ticksSinceUsed; public int toggleTimer = 0; protected float moveX = 0; protected float moveZ = 0; public RotatedAxes legAxes; public float prevLegsYaw = 0F; private int jumpDelay = 0; public MechaInventory inventory; public float legSwing = 0; /** Used for shooting guns */ public float shootDelayLeft = 0, shootDelayRight = 0; /** Used for gun sounds */ public int soundDelayLeft = 0, soundDelayRight = 0; /** The coords of the blocks being destroyed */ public Vector3i breakingBlock = null; /** Progress made towards breaking each block */ public float breakingProgress = 0F; /** Timer for the RocketPack Sound */ private float rocketTimer = 0F; private int diamondTimer = 0; /** Gun animations */ public GunAnimations leftAnimations = new GunAnimations(), rightAnimations = new GunAnimations(); boolean couldNotFindFuel; public EntityPlayer placer; public float yOffset; public EntityMecha(World world) { super(world); setSize(2F, 3F); stepHeight = 3; legAxes = new RotatedAxes(); inventory = new MechaInventory(this); } public EntityMecha(World world, double x, double y, double z, MechaType type, DriveableData data, NBTTagCompound tags) { super(world, type, data); legAxes = new RotatedAxes(); setSize(2F, 3F); stepHeight = 3; setPosition(x, y, z); initType(type, false); inventory = new MechaInventory(this, tags); } public EntityMecha(World world, double x, double y, double z, EntityPlayer placer, MechaType type, DriveableData data, NBTTagCompound tags) { this(world, x, y, z, type, data, tags); rotateYaw(placer.rotationYaw + 90F); legAxes.rotateGlobalYaw(placer.rotationYaw + 90F); prevLegsYaw = legAxes.getYaw(); this.placer = placer; } @Override protected void initType(DriveableType type, boolean clientSide) { super.initType(type, clientSide); setSize(((MechaType)type).width, ((MechaType)type).height); stepHeight = ((MechaType)type).stepHeight; } @Override protected void writeEntityToNBT(NBTTagCompound tag) { super.writeEntityToNBT(tag); tag.setFloat("LegsYaw", legAxes.getYaw()); tag.setTag("Inventory", inventory.writeToNBT(new NBTTagCompound())); } @Override protected void readEntityFromNBT(NBTTagCompound tag) { super.readEntityFromNBT(tag); legAxes.setAngles(tag.getFloat("LegsYaw"), 0, 0); inventory.readFromNBT(tag.getCompoundTag("Inventory")); } @Override public void writeSpawnData(ByteBuf data) { super.writeSpawnData(data); ByteBufUtils.writeTag(data, inventory.writeToNBT(new NBTTagCompound())); } @Override public void readSpawnData(ByteBuf data) { super.readSpawnData(data); legAxes.rotateGlobalYaw(axes.getYaw()); prevLegsYaw = legAxes.getYaw(); inventory.readFromNBT(ByteBufUtils.readTag(data)); } @Override public double getYOffset() { return yOffset; } @Override public void onMouseMoved(int deltaX, int deltaY) { } @Override public boolean interactFirst(EntityPlayer entityplayer) { if(isDead) return false; if(worldObj.isRemote) return false; //If they are using a repair tool, don't put them in ItemStack currentItem = entityplayer.getCurrentEquippedItem(); if(currentItem != null && currentItem.getItem() instanceof ItemTool && ((ItemTool)currentItem.getItem()).type.healDriveables) return true; MechaType type = getMechaType(); //Check each seat in order to see if the player can sit in it for(int i = 0; i <= type.numPassengers; i++) { if(seats[i].interactFirst(entityplayer)) return true; } return false; } public MechaType getMechaType() { return MechaType.getMecha(driveableType); } @Override public boolean pressKey(int key, EntityPlayer player) { MechaType type = getMechaType(); DriveableData data = getDriveableData(); //send keys which require server side updates to the server if(worldObj.isRemote && (key == 6 || key == 8 || key == 9)) { FlansMod.getPacketHandler().sendToServer(new PacketDriveableKey(key)); return true; } switch(key) { case 0 : //Forwards (these movement cases are redundant, as Mechas need to stop when the key is released) { return true; } case 1 : //Backwards { return true; } case 2 : //Left { return true; } case 3 : //Right { return true; } case 4 : //Jump { boolean canThrustCreatively = seats != null && seats[0] != null && seats[0].riddenByEntity instanceof EntityPlayer && ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode; if(onGround && (jumpDelay == 0) && (canThrustCreatively || data.fuelInTank > data.engine.fuelConsumption) && isPartIntact(EnumDriveablePart.hips)) { jumpDelay = 20; motionY += type.jumpVelocity; if(!canThrustCreatively) data.fuelInTank -= data.engine.fuelConsumption; } return true; } case 5 : //Down : Do nothing { return true; } case 6 : //Exit : Get out { seats[0].riddenByEntity.mountEntity(null); return true; } case 7 : //Inventory { FlansMod.getPacketHandler().sendToServer(new PacketDriveableGUI(4)); ((EntityPlayer)seats[0].riddenByEntity).openGui(FlansMod.INSTANCE, 10, worldObj, chunkCoordX, chunkCoordY, chunkCoordZ); return true; } case 8 : //UseR { return true; //useItem(false); } case 9 : //UseL { return true; //useItem(true); } case 10 : //Change control mode : Do nothing { return true; } case 11 : //Roll left : Do nothing { return true; } case 12 : //Roll right : Do nothing { return true; } case 13 : //Gear : Do nothing { return true; } case 14 : //Door : Do nothing { return true; } case 15 : //??? { return true; } case 16 : //??? { return true; } case 17 : //??? { return true; } } return false; } protected boolean creative() { return !(seats[0].riddenByEntity instanceof EntityPlayer) || ((EntityPlayer) seats[0].riddenByEntity).capabilities.isCreativeMode; } protected boolean useItem(boolean left) { if(left? isPartIntact(EnumDriveablePart.leftArm) : isPartIntact(EnumDriveablePart.rightArm)) { ItemStack heldStack = left ? inventory.getStackInSlot(EnumMechaSlotType.leftTool) : inventory.getStackInSlot(EnumMechaSlotType.rightTool); if(heldStack == null) return false; Item heldItem = heldStack.getItem(); MechaType mechaType = getMechaType(); if(heldItem instanceof ItemMechaAddon) { MechaItemType toolType = ((ItemMechaAddon)heldItem).type; float reach = toolType.reach * mechaType.reach; Vector3f lookOrigin = new Vector3f((float)mechaType.seats[0].x / 16F, (float)mechaType.seats[0].y / 16F + seats[0].riddenByEntity.getMountedYOffset(), (float)mechaType.seats[0].z / 16F); lookOrigin = axes.findLocalVectorGlobally(lookOrigin); Vector3f.add(lookOrigin, new Vector3f(posX, posY, posZ), lookOrigin); Vector3f lookVector = axes.findLocalVectorGlobally(seats[0].looking.findLocalVectorGlobally(new Vector3f(reach, 0F, 0F))); worldObj.spawnEntityInWorld(new EntityDebugVector(worldObj, lookOrigin, lookVector, 20)); Vector3f lookTarget = Vector3f.add(lookVector, lookOrigin, null); MovingObjectPosition hit = worldObj.rayTraceBlocks(lookOrigin.toVec3(), lookTarget.toVec3()); //MovingObjectPosition hit = ((EntityLivingBase)seats[0].riddenByEntity).rayTrace(reach, 1F); if(hit != null && hit.typeOfHit == MovingObjectType.BLOCK) { BlockPos pos = hit.getBlockPos(); if(breakingBlock == null || breakingBlock.x != pos.getX() || breakingBlock.y != pos.getY() || breakingBlock.z != pos.getZ()) breakingProgress = 0F; breakingBlock = new Vector3i(pos.getX(), pos.getY(), pos.getZ()); } } else if(heldItem instanceof ItemGun) { ItemGun gunItem = (ItemGun)heldItem; GunType gunType = gunItem.GetType(); //Get the correct shoot delay float delay = left ? shootDelayLeft : shootDelayRight; //If we can shoot if(delay <= 0) { //Go through the bullet stacks in the gun and see if any of them are not null int bulletID = 0; ItemStack bulletStack = null; for(; bulletID < gunType.numAmmoItemsInGun; bulletID++) { ItemStack checkingStack = gunItem.getBulletItemStack(heldStack, bulletID); if(checkingStack != null && checkingStack.getItem() != null && checkingStack.getItemDamage() < checkingStack.getMaxDamage()) { bulletStack = checkingStack; break; } } //If no bullet stack was found, reload if(bulletStack == null) { gunItem.Reload(heldStack, worldObj, this, driveableData, left, true, true, (infiniteAmmo() || creative())); } //A bullet stack was found, so try shooting with it else if(bulletStack.getItem() instanceof ItemBullet) { //Shoot shoot(heldStack, gunType, bulletStack, creative(), left); //Apply animations to 3D modelled guns //TODO : Move to client side and sync if(worldObj.isRemote) { int pumpDelay = gunType.model == null ? 0 : gunType.model.pumpDelay; int pumpTime = gunType.model == null ? 1 : gunType.model.pumpTime; if(left) { leftAnimations.doShoot(pumpDelay, pumpTime); } else { rightAnimations.doShoot(pumpDelay, pumpTime); } } //Damage the bullet item bulletStack.setItemDamage(bulletStack.getItemDamage() + 1); //Update the stack in the gun gunItem.setBulletItemStack(heldStack, bulletStack, bulletID); } } } } return true; } private void shoot(ItemStack stack, GunType gunType, ItemStack bulletStack, boolean creative, boolean left) { MechaType mechaType = getMechaType(); ShootableType bulletType = ((ItemShootable)bulletStack.getItem()).type; RotatedAxes a = new RotatedAxes(); Vector3f armVector = new Vector3f(mechaType.armLength, 0F, 0F); Vector3f gunVector = new Vector3f(mechaType.armLength + 1.2F * mechaType.heldItemScale, 0.5F * mechaType.heldItemScale, 0F); Vector3f armOrigin = left ? mechaType.leftArmOrigin : mechaType.rightArmOrigin; a.rotateGlobalYaw(axes.getYaw()); armOrigin = a.findLocalVectorGlobally(armOrigin); a.rotateLocalPitch(-seats[0].looking.getPitch()); gunVector = a.findLocalVectorGlobally(gunVector); armVector = a.findLocalVectorGlobally(armVector); Vector3f bulletOrigin = Vector3f.add(armOrigin, gunVector, null); bulletOrigin = Vector3f.add(new Vector3f(posX, posY, posZ), bulletOrigin, null); if(!worldObj.isRemote) for (int k = 0; k < gunType.numBullets * bulletType.numBullets; k++) { // TODO: Do mechas properly. No hacks float speed = gunType.getBulletSpeed(stack); if(speed <= 0.0f) speed = 5.0f; worldObj.spawnEntityInWorld(((ItemShootable)bulletStack.getItem()).getEntity(worldObj, bulletOrigin, armVector, (EntityLivingBase)(seats[0].riddenByEntity), gunType.getSpread(stack) / 2F, gunType.getDamage(stack), speed, mechaType)); } if(left) shootDelayLeft = gunType.mode == EnumFireMode.SEMIAUTO ? Math.max(gunType.GetShootDelay(stack), 5) : gunType.GetShootDelay(stack); else shootDelayRight = gunType.mode == EnumFireMode.SEMIAUTO ? Math.max(gunType.GetShootDelay(stack), 5) : gunType.GetShootDelay(stack); if(bulletType.dropItemOnShoot != null && !creative) ItemGun.dropItem(worldObj, this, bulletType.dropItemOnShoot); // Play a sound if the previous sound has finished if((left ? soundDelayLeft : soundDelayRight) <= 0 && gunType.shootSound != null) { PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, gunType.shootSound, gunType.distortSound); if(left) soundDelayLeft = gunType.shootSoundLength; else soundDelayRight = gunType.shootSoundLength; } } @Override public void fall(float f, float l) { attackEntityFrom(DamageSource.fall, f); } @Override public boolean attackEntityFrom(DamageSource damagesource, float i) { if(worldObj.isRemote || isDead) return true; MechaType type = getMechaType(); if(damagesource.getDamageType().equals("fall")) { boolean takeFallDamage = type.takeFallDamage && !stopFallDamage(); boolean damageBlocksFromFalling = type.damageBlocksFromFalling || breakBlocksUponFalling(); byte wouldBeNegativeDamage; if(((i * type.fallDamageMultiplier * vulnerability())-2)<0){wouldBeNegativeDamage=0;} else {wouldBeNegativeDamage=1;}; float damageToInflict = takeFallDamage ? i * ((type.fallDamageMultiplier * vulnerability())) * wouldBeNegativeDamage : 0; float blockDamageFromFalling = damageBlocksFromFalling ? i * (type.blockDamageFromFalling) / 10F : 0; driveableData.parts.get(EnumDriveablePart.hips).attack(damageToInflict, false); checkParts(); FlansMod.getPacketHandler().sendToAllAround(new PacketDriveableDamage(this), posX, posY, posZ, FlansMod.driveableUpdateRange, dimension); if(blockDamageFromFalling > 1) { worldObj.createExplosion(this, posX, posY, posZ, blockDamageFromFalling, TeamsManager.explosions); } } else if(damagesource.damageType.equals("player") && damagesource.getEntity().onGround && (seats[0] == null || seats[0].riddenByEntity == null)) { ItemStack mechaStack = new ItemStack(type.item, 1, driveableData.paintjobID); NBTTagCompound tags = new NBTTagCompound(); mechaStack.setTagCompound(tags); driveableData.writeToNBT(tags); inventory.writeToNBT(tags); entityDropItem(mechaStack, 0.5F); setDead(); } else { driveableData.parts.get(EnumDriveablePart.core).attack(i * vulnerability(), damagesource.isFireDamage()); } return true; } @Override public void onUpdate() { super.onUpdate(); //Decrement delay variables if(jumpDelay > 0) jumpDelay--; if(shootDelayLeft > 0) shootDelayLeft--; if(shootDelayRight > 0) shootDelayRight--; if(soundDelayLeft > 0) soundDelayLeft--; if(soundDelayRight > 0) soundDelayRight--; //If the player left the driver's seat, stop digging / whatever if(!worldObj.isRemote && (seats[0] == null || seats[0].riddenByEntity == null)) rightMouseHeld = leftMouseHeld = false; //Update gun animations leftAnimations.update(); rightAnimations.update(); //Get Mecha Type MechaType type = this.getMechaType(); DriveableData data = getDriveableData(); if (type == null) { FlansMod.log("Mecha type null. Not ticking mecha"); return; } prevLegsYaw = legAxes.getYaw(); //Autorepair. Like a Boss. if(toggleTimer == 0 && autoRepair()) { for(EnumDriveablePart part: EnumDriveablePart.values()) { DriveablePart thisPart = data.parts.get(part); boolean hasCreativePlayer = seats != null && seats[0] != null && seats[0].riddenByEntity instanceof EntityPlayer && ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode; if(thisPart != null && thisPart.health != 0 && thisPart.health < thisPart.maxHealth && (hasCreativePlayer || data.fuelInTank >= 10F)) { thisPart.health += 1; if(!hasCreativePlayer) data.fuelInTank -= 10F; } } toggleTimer = 20; } if(diamondDetect() != null && diamondTimer == 0 && worldObj.isRemote && seats[0] != null && seats[0].riddenByEntity instanceof EntityPlayer && FlansMod.proxy.isThePlayer((EntityPlayer)seats[0].riddenByEntity)) { float sqDistance = 901; for(float i = -30; i <= 30; i++) { for(float j = -30; j <= 30; j++) { for(float k = -30; k <= 30; k++) { int x = MathHelper.floor_double(i + posX); int y = MathHelper.floor_double(j + posY); int z = MathHelper.floor_double(k + posZ); if(i * i + j * j + k * k < sqDistance && worldObj.getBlockState(new BlockPos(x, y, z)).getBlock() == (Blocks.diamond_ore)) { sqDistance = i * i + j * j + k * k; } } } } if(sqDistance < 901) { PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, diamondDetect().detectSound, false); diamondTimer = 1 + 2 * MathHelper.floor_float(MathHelper.sqrt_float(sqDistance)); } } if(diamondTimer > 0) --diamondTimer; //TODO better implement this if(isPartIntact(EnumDriveablePart.hips)) { setSize(type.width, type.height); yOffset = type.yOffset; } else { setSize(type.width, type.height - type.chassisHeight); yOffset = type.yOffset - type.chassisHeight; } //Work out of this is client side and the player is driving boolean thePlayerIsDrivingThis = worldObj.isRemote && seats[0] != null && seats[0].riddenByEntity instanceof EntityPlayer && FlansMod.proxy.isThePlayer((EntityPlayer)seats[0].riddenByEntity); boolean driverIsLiving = seats[0] != null && seats[0].riddenByEntity instanceof EntityLivingBase; //Despawning ticksSinceUsed++; if(!worldObj.isRemote && seats[0].riddenByEntity != null) ticksSinceUsed = 0; if(!worldObj.isRemote && TeamsManager.mechaLove > 0 && ticksSinceUsed > TeamsManager.mechaLove * 20) { setDead(); } //Timer, for general use (only current use is for Auto Repair) if(toggleTimer > 0) toggleTimer--; //Player is not driving this. Update its position from server update packets if(worldObj.isRemote && !thePlayerIsDrivingThis) { //The driveable is currently moving towards its server position. Continue doing so. if (serverPositionTransitionTicker > 0) { double x = posX + (serverPosX - posX) / serverPositionTransitionTicker; double y = posY + (serverPosY - posY) / serverPositionTransitionTicker; double z = posZ + (serverPosZ - posZ) / serverPositionTransitionTicker; double dYaw = MathHelper.wrapAngleTo180_double(serverYaw - axes.getYaw()); double dPitch = MathHelper.wrapAngleTo180_double(serverPitch - axes.getPitch()); double dRoll = MathHelper.wrapAngleTo180_double(serverRoll - axes.getRoll()); rotationYaw = (float)(axes.getYaw() + dYaw / serverPositionTransitionTicker); rotationPitch = (float)(axes.getPitch() + dPitch / serverPositionTransitionTicker); float rotationRoll = (float)(axes.getRoll() + dRoll / serverPositionTransitionTicker); --serverPositionTransitionTicker; setPosition(x, y, z); setRotation(rotationYaw, rotationPitch, rotationRoll); //return; } //If the driveable is at its server position and does not have the next update, it should just simulate itself as a server side driveable would, so continue } //Movement if(seats[0] != null) { //if(seats[0].riddenByEntity == null) //{ // axes.rotateGlobalYaw(2F); //} if(seats[0].riddenByEntity instanceof EntityLivingBase && !(seats[0].riddenByEntity instanceof EntityPlayer)) axes.setAngles(((EntityLivingBase)seats[0].riddenByEntity).renderYawOffset + 90F, 0F, 0F); else { //Function to limit Head Movement Left/Right if(type.limitHeadTurn) { float axesLegs = legAxes.getYaw(); float axesBody = axes.getYaw(); double dYaw = axesBody - axesLegs; if(dYaw > 180) axesBody -= 360F; if(dYaw < -180) axesBody += 360F; if(axesLegs + type.limitHeadTurnValue < axesBody) axes.setAngles(axesLegs + type.limitHeadTurnValue, 0F, 0F); if(axesLegs - type.limitHeadTurnValue > axesBody) axes.setAngles(axesLegs - type.limitHeadTurnValue, 0F, 0F); } float yaw = seats[0].looking.getYaw() - seats[0].prevLooking.getYaw(); axes.rotateGlobalYaw(yaw); seats[0].looking.rotateGlobalYaw(-yaw); seats[0].playerLooking.rotateGlobalYaw(-yaw); } } moveX = 0; moveZ = 0; float jetPack = jetPackPower(); if(!onGround && thePlayerIsDrivingThis && Minecraft.getMinecraft().currentScreen instanceof GuiDriveableController && FlansMod.proxy.isKeyDown(4) && shouldFly() && (((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode || data.fuelInTank >= (10F*jetPack))) { motionY *= 0.95; motionY += (0.07*jetPack); fallDistance = 0; if(!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= (10F*jetPack); if(rocketTimer <= 0 && rocketPack().soundEffect != null) { PacketPlaySound.sendSoundPacket(posX, posY, posZ, FlansMod.soundRange, dimension, rocketPack().soundEffect, false); rocketTimer = rocketPack().soundTime; } } else if(isInWater() && shouldFloat()) { motionY *= 0.89; motionY += 0.1; } if(rocketTimer != 0) rocketTimer --; Vector3f actualMotion = new Vector3f(0F, motionY - (16F / 400F), 0F); if(driverIsLiving) { EntityLivingBase entity = (EntityLivingBase)seats[0].riddenByEntity; boolean driverIsCreative = entity instanceof EntityPlayer && ((EntityPlayer)entity).capabilities.isCreativeMode; if(thePlayerIsDrivingThis && Minecraft.getMinecraft().currentScreen instanceof GuiDriveableController) { if(FlansMod.proxy.isKeyDown(0)) moveX = 1; if(FlansMod.proxy.isKeyDown(1)) moveX = -1; if(FlansMod.proxy.isKeyDown(2)) moveZ = -1; if(FlansMod.proxy.isKeyDown(3)) moveZ = 1; } else if(seats[0].riddenByEntity instanceof EntityLiving && !(seats[0].riddenByEntity instanceof EntityPlayer)) { moveZ = 1; /* EntityLiving ent = (EntityLiving)seats[0].riddenByEntity; //System.out.println(ent.moveForward); Vec3 target = Vec3.createVectorHelper(0D, 0D, 0D); if(ent.getNavigator().getPath() != null) target = ent.getNavigator().getPath().getPosition(ent); moveX = (float) target.xCoord; moveZ = (float) target.zCoord; */ } Vector3f intent = new Vector3f(moveX, 0, moveZ); if(Math.abs(intent.lengthSquared()) > 0.1) { intent.normalise(); ++legSwing; intent = axes.findLocalVectorGlobally(intent); Vector3f intentOnLegAxes = legAxes.findGlobalVectorLocally(intent); float intentAngle = (float)Math.atan2(intent.z, intent.x) * 180F / 3.14159265F; float angleBetween = intentAngle - legAxes.getYaw(); if(angleBetween > 180F) angleBetween -= 360F; if(angleBetween < -180F) angleBetween += 360F; float signBetween = Math.signum(angleBetween); angleBetween = Math.abs(angleBetween); if(angleBetween > 0.1) { legAxes.rotateGlobalYaw(Math.min(angleBetween, type.rotateSpeed)*signBetween); } intent.scale((type.moveSpeed * data.engine.engineSpeed * speedMultiplier())*(4.3F/20F)); boolean canThrustCreatively = seats != null && seats[0] != null && seats[0].riddenByEntity instanceof EntityPlayer && ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode; if((canThrustCreatively || data.fuelInTank > data.engine.fuelConsumption) && isPartIntact(EnumDriveablePart.hips)) { if(!onGround && shouldFly() && (canThrustCreatively || data.fuelInTank > 10F*jetPack + data.engine.fuelConsumption)) { intent.scale(jetPack); if(!canThrustCreatively) data.fuelInTank -= 10F*jetPack; } //Move! Vector3f.add(actualMotion, intent, actualMotion); //If we can't thrust creatively, we must thrust using fuel. Nom. if(!canThrustCreatively) data.fuelInTank -= data.engine.fuelConsumption; } } //Block breaking if(!worldObj.isRemote) { //Use left and right items on the server side if(leftMouseHeld) useItem(true); if(rightMouseHeld) useItem(false); //Check the left block being mined if(breakingBlock != null) { //Get block and material IBlockState state = worldObj.getBlockState(new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z)); Block blockHit = state.getBlock(); Material material = blockHit.getMaterial(); //Get the itemstacks in each hand ItemStack leftStack = inventory.getStackInSlot(EnumMechaSlotType.leftTool); ItemStack rightStack = inventory.getStackInSlot(EnumMechaSlotType.rightTool); //Work out if we are actually breaking blocks boolean leftStackIsTool = leftStack != null && leftStack.getItem() instanceof ItemMechaAddon; boolean rightStackIsTool = rightStack != null && rightStack.getItem() instanceof ItemMechaAddon; boolean breakingBlocks = (leftMouseHeld && leftStackIsTool) || (rightMouseHeld && rightStackIsTool); //If we are not breaking blocks, reset everything if(blockHit == null || !breakingBlocks) { //if(worldObj.isRemote) // Minecraft.getMinecraft().renderGlobal.destroyBlockPartially(getEntityId(), breakingBlock.x, breakingBlock.y, breakingBlock.z, -1); breakingBlock = null; } else { //Get the block hardness float blockHardness = blockHit.getBlockHardness(worldObj, new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z)); //Calculate the mine speed float mineSpeed = 1F; boolean atLeastOneEffectiveTool = false; if(leftStackIsTool) { MechaItemType leftType = ((ItemMechaAddon)leftStack.getItem()).type; if(leftType.function.effectiveAgainst(material) && leftType.toolHardness > blockHardness) { mineSpeed *= leftType.speed; atLeastOneEffectiveTool = true; } } if(rightStackIsTool) { MechaItemType rightType = ((ItemMechaAddon)rightStack.getItem()).type; if(rightType.function.effectiveAgainst(material) && rightType.toolHardness > blockHardness) { mineSpeed *= rightType.speed; atLeastOneEffectiveTool = true; } } //If this block is immortal, do not break it if(blockHardness < -0.01F) mineSpeed = 0F; //If this block's hardness is zero-ish, then the tool's power is OVER 9000!!!! else if(Math.abs(blockHardness) < 0.01F) mineSpeed = 9001F; else { mineSpeed /= blockHit.getBlockHardness(worldObj, new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z)); } //Add block digging overlay //if(worldObj.isRemote) // Minecraft.getMinecraft().renderGlobal.destroyBlockPartially(getEntityId(), breakingBlock.x, breakingBlock.y, breakingBlock.z, (int)(breakingProgress * 10)); breakingProgress += 0.1F * mineSpeed; if(breakingProgress >= 1F) { boolean cancelled = false; if(entity instanceof EntityPlayerMP) { int eventOutcome = ForgeHooks.onBlockBreakEvent(worldObj, ((EntityPlayerMP)entity).capabilities.isCreativeMode ? GameType.CREATIVE : ((EntityPlayerMP)entity).capabilities.allowEdit ? GameType.SURVIVAL : GameType.ADVENTURE, (EntityPlayerMP)entity, new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z)); cancelled = eventOutcome == -1; } if(!cancelled) { //blockHit.dropBlockAsItem(worldObj, breakingBlock.x, breakingBlock.y, breakingBlock.z, worldObj.getBlockMetadata(breakingBlock.x, breakingBlock.y, breakingBlock.z), 1); //FlansMod.proxy.playBlockBreakSound(breakingBlock.x, breakingBlock.y, breakingBlock.z, worldObj.getBlockId(breakingBlock.x, breakingBlock.y, breakingBlock.z)); //worldObj.setBlockToAir(breakingBlock.x, breakingBlock.y, breakingBlock.z); boolean vacuumItems = vacuumItems(); if(vacuumItems) { for(ItemStack stack : blockHit.getDrops(worldObj, new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z), state, 0)) { //Check for iron regarding refining boolean fuelCheck = (data.fuelInTank >= 5F || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && refineIron() && stack.getItem() instanceof ItemBlock && ((ItemBlock)stack.getItem()).block == Blocks.iron_ore) { stack = (new ItemStack(Items.iron_ingot, 1, 0)); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 5F; } //Check for waste to be compacted fuelCheck = (data.fuelInTank >= 0.1F || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && wasteCompact() && stack.getItem() instanceof ItemBlock && (((ItemBlock)stack.getItem()).block == Blocks.cobblestone || ((ItemBlock)stack.getItem()).block == Blocks.dirt || ((ItemBlock)stack.getItem()).block == Blocks.sand)) { stack.stackSize = 0; if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 0.1F; } //Check for item multipliers fuelCheck = (data.fuelInTank >= 3F*diamondMultiplier() || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && stack.getItem() == Items.diamond) { float multiplier = diamondMultiplier(); stack.stackSize *= MathHelper.floor_float(multiplier) + (rand.nextFloat() < tailFloat(multiplier) ? 1 : 0); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 3F*diamondMultiplier(); } fuelCheck = (data.fuelInTank >= 2F*redstoneMultiplier() || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && stack.getItem() == Items.redstone) { float multiplier = redstoneMultiplier(); stack.stackSize *= MathHelper.floor_float(multiplier) + (rand.nextFloat() < tailFloat(multiplier) ? 1 : 0); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 2F*redstoneMultiplier(); } fuelCheck = (data.fuelInTank >= 2F*coalMultiplier() || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && stack.getItem() == Items.coal) { float multiplier = coalMultiplier(); stack.stackSize *= MathHelper.floor_float(multiplier) + (rand.nextFloat() < tailFloat(multiplier) ? 1 : 0); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 2F*coalMultiplier(); } fuelCheck = (data.fuelInTank >= 2F*emeraldMultiplier() || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); if(fuelCheck && stack.getItem() == Items.emerald) { float multiplier = emeraldMultiplier(); stack.stackSize *= MathHelper.floor_float(multiplier) + (rand.nextFloat() < tailFloat(multiplier) ? 1 : 0); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 2F*emeraldMultiplier(); } fuelCheck = (data.fuelInTank >= 2F*ironMultiplier() || ((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode); //check for refineIron OTHERWISE NICE DUPE. think about it and you will get why if(fuelCheck && (stack.getItem() == Items.iron_ingot) && refineIron()) { float multiplier = ironMultiplier(); stack.stackSize *= MathHelper.floor_float(multiplier) + (rand.nextFloat() < tailFloat(multiplier) ? 1 : 0); if (!((EntityPlayer)seats[0].riddenByEntity).capabilities.isCreativeMode) data.fuelInTank -= 2F*ironMultiplier(); } //Check for auto coal consumption if(autoCoal() && (stack.getItem() == Items.coal) && (data.fuelInTank + 250F < type.fuelTankSize)) { data.fuelInTank = Math.min(data.fuelInTank + 1000F, type.fuelTankSize); couldNotFindFuel = false; stack.stackSize = 0; } //Add the itemstack to mecha inventory if(!InventoryHelper.addItemStackToInventory(driveableData, stack, driverIsCreative) && !worldObj.isRemote && worldObj.getGameRules().getBoolean("doTileDrops")) { worldObj.spawnEntityInWorld(new EntityItem(worldObj, breakingBlock.x + 0.5F, breakingBlock.y + 0.5F, breakingBlock.z + 0.5F, stack)); } } } //Destroy block worldObj.destroyBlock(new BlockPos(breakingBlock.x, breakingBlock.y, breakingBlock.z), atLeastOneEffectiveTool && !vacuumItems); } } } } } } else moveAI(actualMotion); motionY = actualMotion.y; moveEntity(actualMotion.x, actualMotion.y, actualMotion.z); //FlansMod.log("" + fallDistance); setPosition(posX, posY, posZ); //Calculate movement on the client and then send position, rotation etc to the server if(thePlayerIsDrivingThis) { FlansMod.getPacketHandler().sendToServer(new PacketMechaControl(this)); serverPosX = posX; serverPosY = posY; serverPosZ = posZ; serverYaw = axes.getYaw(); } //If this is the server, send position updates to everyone, having received them from the driver if(!worldObj.isRemote && ticksExisted % 5 == 0) { FlansMod.getPacketHandler().sendToAllAround(new PacketMechaControl(this), posX, posY, posZ, FlansMod.driveableUpdateRange, dimension); } for(EntitySeat seat : seats) { if(seat != null) seat.updatePosition(); } if(!driverIsLiving || thePlayerIsDrivingThis) legSwing = legSwing / type.legSwingLimit; } protected void moveAI(Vector3f actualMotion) { } private float tailFloat(float f) { return f - MathHelper.floor_float(f); } /** This is a series of iterators which check all upgrades * for various triggers and multipliers */ /** Stop fall damage? */ public boolean stopFallDamage() { for(MechaItemType type : getUpgradeTypes()) { if(type.stopMechaFallDamage) return true; } return false; } /** Force fall to break blocks? */ public boolean breakBlocksUponFalling() { for(MechaItemType type : getUpgradeTypes()) { if(type.forceBlockFallDamage) return true; } return false; } /** Vacuum items? */ public boolean vacuumItems() { for(MechaItemType type : getUpgradeTypes()) { if(type.vacuumItems) return true; } return false; } /** Refine iron? */ public boolean refineIron() { for(MechaItemType type : getUpgradeTypes()) { if(type.refineIron) return true; } return false; } /** Detect Diamonds? */ public MechaItemType diamondDetect() { for(MechaItemType type : getUpgradeTypes()) { if(type.diamondDetect) return type; } return null; } /** Compact Waste? */ public Boolean wasteCompact() { for(MechaItemType type : getUpgradeTypes()) { if(type.wasteCompact) return true; } return false; } /** Diamond yield multiplier */ public float diamondMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.fortuneDiamond; } return multiplier; } /** Movement speed multiplier */ public float speedMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.speedMultiplier; } return multiplier; } /** Coal yield multiplier */ public float coalMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.fortuneCoal; } return multiplier; } /** Redstone yield multiplier */ public float redstoneMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.fortuneRedstone; } return multiplier; } /** Vulnerability */ public float vulnerability() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= (1 - type.damageResistance); } return multiplier; } /** Emerald yield multiplier */ public float emeraldMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.fortuneEmerald; } return multiplier; } /** Iron yield multiplier */ public float ironMultiplier() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.fortuneIron; } return multiplier; } /** Light Level */ public int lightLevel() { int level = 0; for(MechaItemType type : getUpgradeTypes()) { level = Math.max(level, type.lightLevel); } return level; } /** Force Darkness */ public boolean forceDark() { for(MechaItemType type : getUpgradeTypes()) { if(type.forceDark) return true; } return false; } /** Convert coal to fuel? */ public boolean autoCoal() { for(MechaItemType type : getUpgradeTypes()) { if(type.autoCoal) return true; } return false; } /** Automatically repair damage? */ public boolean autoRepair() { for(MechaItemType type : getUpgradeTypes()) { if(type.autoRepair) return true; } return false; } /** Float in water? */ public boolean shouldFloat() { for(MechaItemType type : getUpgradeTypes()) { if(type.floater) return true; } return false; } /** Have infinite ammo? */ public boolean infiniteAmmo() { for(MechaItemType type : getUpgradeTypes()) { if(type.infiniteAmmo) return true; } return false; } /** Have a Rocket Pack? */ public MechaItemType rocketPack() { for(MechaItemType type : getUpgradeTypes()) { if(type.rocketPack) return type; } return null; } public boolean shouldFly() { return rocketPack() != null; } /** Jetpack multiplier */ public float jetPackPower() { float multiplier = 1F; for(MechaItemType type : getUpgradeTypes()) { multiplier *= type.rocketPower; } return multiplier; } public ArrayList<MechaItemType> getUpgradeTypes() { ArrayList<MechaItemType> types = new ArrayList<MechaItemType>(); for(ItemStack stack : inventory.stacks.values()) { if(stack != null && stack.getItem() instanceof ItemMechaAddon) { types.add(((ItemMechaAddon)stack.getItem()).type); } } return types; } @SideOnly(Side.CLIENT) @Override public boolean showInventory(int seat) { return seat != 0; } @Override protected void dropItemsOnPartDeath(Vector3f midpoint, DriveablePart part) { if(part.type == EnumDriveablePart.core) { for(int i = 0; i < inventory.getSizeInventory(); i++) { if(inventory.getStackInSlot(i) != null) worldObj.spawnEntityInWorld(new EntityItem(worldObj, posX + midpoint.x, posY + midpoint.y, posZ + midpoint.z, inventory.getStackInSlot(i))); } } } @Override public boolean hasMouseControlMode() { return false; } @Override public String getBombInventoryName() { return ""; } @Override public String getMissileInventoryName() { return ""; } @Override @SideOnly(Side.CLIENT) public EntityLivingBase getCamera() { return null; } }