package micdoodle8.mods.galacticraft.api.prefab.entity;
import io.netty.buffer.ByteBuf;
import micdoodle8.mods.galacticraft.api.entity.IEntityNoisy;
import micdoodle8.mods.galacticraft.api.entity.ILandable;
import micdoodle8.mods.galacticraft.api.tile.IFuelDock;
import micdoodle8.mods.galacticraft.api.tile.ILandingPadAttachable;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3;
import micdoodle8.mods.galacticraft.api.world.IGalacticraftWorldProvider;
import micdoodle8.mods.galacticraft.api.world.IOrbitDimension;
import micdoodle8.mods.galacticraft.core.Constants;
import micdoodle8.mods.galacticraft.core.GCBlocks;
import micdoodle8.mods.galacticraft.core.GCFluids;
import micdoodle8.mods.galacticraft.core.GalacticraftCore;
import micdoodle8.mods.galacticraft.core.blocks.BlockLandingPadFull;
import micdoodle8.mods.galacticraft.core.client.sounds.SoundUpdaterRocket;
import micdoodle8.mods.galacticraft.core.entities.player.GCPlayerStats;
import micdoodle8.mods.galacticraft.core.event.EventLandingPadRemoval;
import micdoodle8.mods.galacticraft.core.inventory.IInventoryDefaults;
import micdoodle8.mods.galacticraft.core.network.PacketDynamic;
import micdoodle8.mods.galacticraft.core.tile.TileEntityLandingPad;
import micdoodle8.mods.galacticraft.core.util.*;
import net.minecraft.block.Block;
import net.minecraft.client.audio.ISound;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.ByteBufUtils;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* Do not include this prefab class in your released mod download.
*/
public abstract class EntityAutoRocket extends EntitySpaceshipBase implements ILandable, IInventoryDefaults, IEntityNoisy
{
public int destinationFrequency = -1;
public BlockPos targetVec;
public int targetDimension;
protected ItemStack[] cargoItems;
private IFuelDock landingPad;
public boolean landing;
public EnumAutoLaunch autoLaunchSetting;
public int autoLaunchCountdown;
private BlockVec3 activeLaunchController;
public String statusMessage;
public String statusColour;
public int statusMessageCooldown;
public int lastStatusMessageCooldown;
public boolean statusValid;
protected double lastMotionY;
protected double lastLastMotionY;
private boolean waitForPlayer;
protected ITickable rocketSoundUpdater;
private boolean rocketSoundToStop = false;
private static Class<?> controllerClass = null;
static
{
try
{
controllerClass = Class.forName("micdoodle8.mods.galacticraft.planets.mars.tile.TileEntityLaunchController");
} catch (ClassNotFoundException e)
{
GCLog.info("Galacticraft-Planets' LaunchController not present, rockets will not be launch controlled.");
}
}
public EntityAutoRocket(World world)
{
super(world);
if (world != null && world.isRemote)
{
GalacticraftCore.packetPipeline.sendToServer(new PacketDynamic(this));
}
}
public EntityAutoRocket(World world, double posX, double posY, double posZ)
{
this(world);
this.setSize(0.98F, 2F);
this.setPosition(posX, posY, posZ);
this.motionX = 0.0D;
this.motionY = 0.0D;
this.motionZ = 0.0D;
this.prevPosX = posX;
this.prevPosY = posY;
this.prevPosZ = posZ;
}
//Used for Cargo Rockets, client is asking the server what is the status
public boolean checkLaunchValidity()
{
this.statusMessageCooldown = 40;
if (this.hasValidFuel())
{
if (this.launchPhase == EnumLaunchPhase.UNIGNITED.ordinal() && !this.worldObj.isRemote)
{
if (!this.setFrequency())
{
this.destinationFrequency = -1;
this.statusMessage = StatCollector.translateToLocal("gui.message.frequency.name") + "#" + StatCollector.translateToLocal("gui.message.not_set.name");
this.statusColour = "\u00a7c";
return false;
}
else
{
this.statusMessage = StatCollector.translateToLocal("gui.message.success.name");
this.statusColour = "\u00a7a";
return true;
}
}
}
else
{
this.destinationFrequency = -1;
this.statusMessage = StatCollector.translateToLocal("gui.message.not_enough.name") + "#" + StatCollector.translateToLocal("gui.message.fuel.name");
this.statusColour = "\u00a7c";
return false;
}
this.destinationFrequency = -1;
return false;
}
public boolean setFrequency()
{
if (!GalacticraftCore.isPlanetsLoaded || controllerClass == null)
{
return false;
}
if (this.activeLaunchController != null)
{
TileEntity launchController = this.activeLaunchController.getTileEntity(this.worldObj);
if (controllerClass.isInstance(launchController))
{
try
{
Boolean b = (Boolean) controllerClass.getMethod("validFrequency").invoke(launchController);
if (b != null && b)
{
int controllerFrequency = controllerClass.getField("destFrequency").getInt(launchController);
boolean foundPad = this.setTarget(false, controllerFrequency);
if (foundPad)
{
this.destinationFrequency = controllerFrequency;
GCLog.debug("Rocket under launch control: going to target frequency " + controllerFrequency);
return true;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
this.destinationFrequency = -1;
return false;
}
protected boolean setTarget(boolean doSet, int destFreq)
{
//Server instance can sometimes be null on a single player game switched to LAN mode
if (FMLCommonHandler.instance().getMinecraftServerInstance() == null || FMLCommonHandler.instance().getMinecraftServerInstance().worldServers == null || !GalacticraftCore.isPlanetsLoaded || controllerClass == null)
{
return false;
}
WorldServer[] servers = FMLCommonHandler.instance().getMinecraftServerInstance().worldServers;
for (int i = 0; i < servers.length; i++)
{
WorldServer world = servers[i];
try
{
for (TileEntity tile : new ArrayList<TileEntity>(world.loadedTileEntityList))
{
if (!controllerClass.isInstance(tile))
continue;
tile = world.getTileEntity(tile.getPos());
if (!controllerClass.isInstance(tile))
continue;
int controllerFrequency = controllerClass.getField("frequency").getInt(tile);
if (destFreq == controllerFrequency)
{
boolean targetSet = false;
blockLoop:
for (int x = -2; x <= 2; x++)
{
for (int z = -2; z <= 2; z++)
{
BlockPos pos = new BlockPos(tile.getPos().add(x, 0, z));
Block block = world.getBlockState(pos).getBlock();
if (block instanceof BlockLandingPadFull)
{
if (doSet)
{
this.targetVec = pos;
}
targetSet = true;
break blockLoop;
}
}
}
if (doSet)
{
this.targetDimension = tile.getWorld().provider.getDimensionId();
}
if (!targetSet)
{
if (doSet)
{
this.targetVec = null;
}
return false;
}
else
{
return true;
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
return false;
}
@Override
public int getScaledFuelLevel(int scale)
{
if (this.getFuelTankCapacity() <= 0)
{
return 0;
}
return this.fuelTank.getFluidAmount() * scale / this.getFuelTankCapacity() / ConfigManagerCore.rocketFuelFactor;
}
@Override
public void onUpdate()
{
//Workaround for a weird bug (?) in vanilla 1.8.9 where - if client view distance is shorter
//than the server's chunk loading distance - chunks will unload on the client, but the
//entity will still be in the WorldClient.loadedEntityList. This results in an entity which
//is in the world, not dead and still updating on both server and client, but not in any chunk's
//chunk.entityLists. Also, it won't be reloaded into any chunk's entityLists when the chunk comes
//back into viewing range - not sure why, maybe because it's already in the World.loadedEntityList?
//Because it's now not in any chunk's entityLists, it cannot be iterated for rendering by RenderGlobal,
//so it's gone invisible!
//Tracing shows that Chunk.onChunkUnload() is called on the chunk clientside when the chunk goes
//out of the view distance. However, even after Chunk.onChunkUnload() - which should remove
//this entity from the WorldClient.loadedEntityList - this entity stays in the world loadedEntityList.
//That's why an onUpdate() tick is active for it, still!
//Weird, huh?
if (this.worldObj.isRemote && this.addedToChunk)
{
Chunk chunk = this.worldObj.getChunkFromChunkCoords(this.chunkCoordX, this.chunkCoordZ);
int cx = MathHelper.floor_double(this.posX) >> 4;
int cz = MathHelper.floor_double(this.posZ) >> 4;
if (chunk.isChunkLoaded && this.chunkCoordX == cx && this.chunkCoordZ == cz)
{
boolean thisfound = false;
ClassInheritanceMultiMap<Entity> mapEntities = chunk.getEntityLists()[this.chunkCoordY];
for (Entity ent : mapEntities)
{
if (ent == this)
{
thisfound = true;
break;
}
}
if (!thisfound)
{
chunk.addEntity(this);
}
}
}
if (this.landing && this.launchPhase == EnumLaunchPhase.LAUNCHED.ordinal() && this.hasValidFuel())
{
if (this.targetVec != null)
{
double yDiff = this.posY - this.getOnPadYOffset() - this.targetVec.getY();
this.motionY = Math.max(-2.0, (yDiff - 0.04) / -70.0);
//Some lateral motion in case not exactly on target (happens if rocket was moving laterally during launch)
double diff = this.posX - this.targetVec.getX() - 0.5D;
double motX, motZ;
if (diff > 0D)
{
motX = Math.max(-0.1, diff / -100.0D);
}
else if (diff < 0D)
{
motX = Math.min(0.1, diff / -100.0D);
}
else motX = 0D;
diff = this.posZ - this.targetVec.getZ() - 0.5D;
if (diff > 0D)
{
motZ = Math.max(-0.1, diff / -100.0D);
}
else if (diff < 0D)
{
motZ = Math.min(0.1, diff / -100.0D);
}
else motZ = 0D;
if (motZ != 0D || motX != 0D)
{
double angleYaw = Math.atan(motZ / motX);
double signed = motX < 0 ? 50D : -50D;
double anglePitch = Math.atan(Math.sqrt(motZ * motZ + motX * motX) / signed) * 100D;
this.rotationYaw = (float)angleYaw * Constants.RADIANS_TO_DEGREES;
this.rotationPitch = (float)anglePitch * Constants.RADIANS_TO_DEGREES;
}
else
this.rotationPitch = 0F;
if (yDiff > 1D && yDiff < 4D)
{
for (Object o : this.worldObj.getEntitiesInAABBexcluding(this, this.getEntityBoundingBox().offset(0D, -3D, 0D), EntitySpaceshipBase.rocketSelector))
{
if (o instanceof EntitySpaceshipBase)
{
((EntitySpaceshipBase)o).dropShipAsItem();
((EntitySpaceshipBase)o).setDead();
}
}
}
if (yDiff < 0.04)
{
int yMin = MathHelper.floor_double(this.getEntityBoundingBox().minY - this.getOnPadYOffset() - 0.45D) - 2;
int yMax = MathHelper.floor_double(this.getEntityBoundingBox().maxY) + 1;
int zMin = MathHelper.floor_double(this.posZ) - 1;
int zMax = MathHelper.floor_double(this.posZ) + 1;
for (int x = MathHelper.floor_double(this.posX) - 1; x <= MathHelper.floor_double(this.posX) + 1; x++)
{
for (int z = zMin; z <= zMax; z++)
{
//Doing y as the inner loop may help with cacheing of chunks
for (int y = yMin; y <= yMax; y++)
{
if (this.worldObj.getTileEntity(new BlockPos(x, y, z)) instanceof IFuelDock)
{
//Land the rocket on the pad found
this.rotationPitch = 0F;
this.failRocket();
}
}
}
}
}
}
}
super.onUpdate();
if (!this.worldObj.isRemote)
{
if (this.statusMessageCooldown > 0)
{
this.statusMessageCooldown--;
}
if (this.statusMessageCooldown == 0 && this.lastStatusMessageCooldown > 0 && this.statusValid)
{
this.autoLaunch();
}
if (this.autoLaunchCountdown > 0 && (!(this instanceof EntityTieredRocket) || this.riddenByEntity != null))
{
if (--this.autoLaunchCountdown <= 0)
{
this.autoLaunch();
}
}
if (this.autoLaunchSetting == EnumAutoLaunch.ROCKET_IS_FUELED && this.fuelTank.getFluidAmount() == this.fuelTank.getCapacity() && (!(this instanceof EntityTieredRocket) || this.riddenByEntity != null))
{
this.autoLaunch();
}
if (this.autoLaunchSetting == EnumAutoLaunch.INSTANT)
{
if (this.autoLaunchCountdown == 0 && (!(this instanceof EntityTieredRocket) || this.riddenByEntity != null))
{
this.autoLaunch();
}
}
if (this.autoLaunchSetting == EnumAutoLaunch.REDSTONE_SIGNAL)
{
if (this.ticks % 11 == 0 && this.activeLaunchController != null)
{
if (RedstoneUtil.isBlockReceivingRedstone(this.worldObj, this.activeLaunchController.toBlockPos()))
{
this.autoLaunch();
}
}
}
if (this.launchPhase == EnumLaunchPhase.LAUNCHED.ordinal())
{
this.setPad(null);
}
else
{
if (this.launchPhase == EnumLaunchPhase.UNIGNITED.ordinal() && this.landingPad != null && this.ticks % 17 == 0)
{
this.updateControllerSettings(this.landingPad);
}
}
this.lastStatusMessageCooldown = this.statusMessageCooldown;
}
if (this.launchPhase == EnumLaunchPhase.IGNITED.ordinal() || this.getLaunched())
{
if (this.rocketSoundUpdater != null)
{
this.rocketSoundUpdater.update();
this.rocketSoundToStop = true;
}
}
else
{
//Not ignited - either because not yet launched, or because it has landed
if (this.rocketSoundToStop)
this.stopRocketSound();
}
}
@Override
protected boolean shouldMoveClientSide()
{
return false;
}
//Server side only - this is a Launch Controlled ignition attempt
private void autoLaunch()
{
if (this.autoLaunchSetting != null)
{
if (this.activeLaunchController != null)
{
TileEntity tile = this.activeLaunchController.getTileEntity(this.worldObj);
if (controllerClass.isInstance(tile))
{
Boolean autoLaunchEnabled = null;
try
{
autoLaunchEnabled = controllerClass.getField("controlEnabled").getBoolean(tile);
} catch (Exception e) { }
if (autoLaunchEnabled != null && autoLaunchEnabled)
{
if (this.fuelTank.getFluidAmount() > this.fuelTank.getCapacity() * 2 / 5)
this.ignite();
else
this.failMessageInsufficientFuel();
}
else
{
this.failMessageLaunchController();
}
}
}
this.autoLaunchSetting = null;
return;
}
else
{
this.ignite();
}
}
public boolean igniteWithResult()
{
if (this.setFrequency())
{
super.ignite();
this.activeLaunchController = null;
return true;
}
else
{
if (this.isPlayerRocket())
{
super.ignite();
this.activeLaunchController = null;
return true;
}
this.activeLaunchController = null;
return false;
}
}
@Override
public void ignite()
{
this.igniteWithResult();
}
public abstract boolean isPlayerRocket();
@Override
public void landEntity(BlockPos pos)
{
TileEntity tile = this.worldObj.getTileEntity(pos);
if (tile instanceof IFuelDock)
{
IFuelDock dock = (IFuelDock) tile;
if (this.isDockValid(dock))
{
if (!this.worldObj.isRemote)
{
//Drop any existing rocket on the landing pad
if (dock.getDockedEntity() instanceof EntitySpaceshipBase && dock.getDockedEntity() != this)
{
((EntitySpaceshipBase)dock.getDockedEntity()).dropShipAsItem();
((EntitySpaceshipBase)dock.getDockedEntity()).setDead();
}
this.setPad(dock);
}
this.onRocketLand(pos);
}
}
}
public void updateControllerSettings(IFuelDock dock)
{
HashSet<ILandingPadAttachable> connectedTiles = dock.getConnectedTiles();
try
{
for (ILandingPadAttachable updatedTile : connectedTiles)
{
if (controllerClass.isInstance(updatedTile))
{
//This includes a check for whether it has enough energy to run (if it doesn't, then a launch would not go to the destination frequency and the rocket would be lost!)
Boolean autoLaunchEnabled = controllerClass.getField("controlEnabled").getBoolean(updatedTile);
this.activeLaunchController = new BlockVec3((TileEntity) updatedTile);
if (autoLaunchEnabled)
{
this.autoLaunchSetting = EnumAutoLaunch.values()[controllerClass.getField("launchDropdownSelection").getInt(updatedTile)];
switch (this.autoLaunchSetting)
{
case INSTANT:
//Small countdown to give player a moment to exit the Launch Controller GUI
if (this.autoLaunchCountdown <= 0 || this.autoLaunchCountdown > 12) this.autoLaunchCountdown = 12;
break;
//The other settings set time to count down AFTER player mounts rocket but BEFORE engine ignition
//TODO: if autoLaunchCountdown > 0 add some smoke (but not flame) particle effects or other pre-flight test feedback so the player knows something is happening
case TIME_10_SECONDS:
if (this.autoLaunchCountdown <= 0 || this.autoLaunchCountdown > 200)
this.autoLaunchCountdown = 200;
break;
case TIME_30_SECONDS:
if (this.autoLaunchCountdown <= 0 || this.autoLaunchCountdown > 600) this.autoLaunchCountdown = 600;
break;
case TIME_1_MINUTE:
if (this.autoLaunchCountdown <= 0 || this.autoLaunchCountdown > 1200) this.autoLaunchCountdown = 1200;
break;
default:
break;
}
}
else
{
//This LaunchController is out of power, disabled, invalid target or set not to launch
//No auto launch - but maybe another connectedTile will have some launch settings?
this.autoLaunchSetting = null;
this.autoLaunchCountdown = 0;
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
protected void onRocketLand(BlockPos pos)
{
this.setPositionAndRotation(pos.getX() + 0.5, pos.getY() + 0.4D + this.getOnPadYOffset(), pos.getZ() + 0.5, this.rotationYaw, 0.0F);
this.stopRocketSound();
}
public void stopRocketSound()
{
if (this.rocketSoundUpdater != null)
{
((SoundUpdaterRocket) this.rocketSoundUpdater).stopRocketSound();
}
this.rocketSoundToStop = false;
}
@Override
public void setDead()
{
super.setDead();
if (this.rocketSoundUpdater != null)
{
this.rocketSoundUpdater.update();
}
}
@Override
public void decodePacketdata(ByteBuf buffer)
{
super.decodePacketdata(buffer);
this.fuelTank.setFluid(new FluidStack(GCFluids.fluidFuel, buffer.readInt()));
this.landing = buffer.readBoolean();
this.destinationFrequency = buffer.readInt();
if (buffer.readBoolean())
{
this.targetVec = new BlockPos(buffer.readInt(), buffer.readInt(), buffer.readInt());
}
this.motionX = buffer.readDouble() / 8000.0D;
this.motionY = buffer.readDouble() / 8000.0D;
this.motionZ = buffer.readDouble() / 8000.0D;
this.lastMotionY = buffer.readDouble() / 8000.0D;
this.lastLastMotionY = buffer.readDouble() / 8000.0D;
if (this.cargoItems == null)
{
this.cargoItems = new ItemStack[this.getSizeInventory()];
}
this.setWaitForPlayer(buffer.readBoolean());
this.statusMessage = ByteBufUtils.readUTF8String(buffer);
this.statusMessage = this.statusMessage.equals("") ? null : this.statusMessage;
this.statusMessageCooldown = buffer.readInt();
this.lastStatusMessageCooldown = buffer.readInt();
this.statusValid = buffer.readBoolean();
//Update client with correct rider if needed
if (this.worldObj.isRemote)
{
int shouldBeMountedId = buffer.readInt();
if (this.riddenByEntity == null)
{
if (shouldBeMountedId > -1)
{
Entity e = FMLClientHandler.instance().getWorldClient().getEntityByID(shouldBeMountedId);
if (e != null)
{
if (e.dimension != this.dimension)
{
if (e instanceof EntityPlayer)
{
e = WorldUtil.forceRespawnClient(this.dimension, e.worldObj.getDifficulty().getDifficultyId(), e.worldObj.getWorldInfo().getTerrainType().getWorldTypeName(), ((EntityPlayerMP)e).theItemInWorldManager.getGameType().getID());
e.mountEntity(this);
}
}
else
e.mountEntity(this);
}
}
}
else if (this.riddenByEntity.getEntityId() != shouldBeMountedId)
{
if (shouldBeMountedId == -1)
{
this.riddenByEntity.mountEntity(null);
}
else
{
Entity e = FMLClientHandler.instance().getWorldClient().getEntityByID(shouldBeMountedId);
if (e != null)
{
if (e.dimension != this.dimension)
{
if (e instanceof EntityPlayer)
{
e = WorldUtil.forceRespawnClient(this.dimension, e.worldObj.getDifficulty().getDifficultyId(), e.worldObj.getWorldInfo().getTerrainType().getWorldTypeName(), ((EntityPlayerMP)e).theItemInWorldManager.getGameType().getID());
e.mountEntity(this);
}
}
else
e.mountEntity(this);
}
}
}
}
this.statusColour = ByteBufUtils.readUTF8String(buffer);
if (this.statusColour.equals("")) this.statusColour = null;
}
@Override
public void getNetworkedData(ArrayList<Object> list)
{
if (this.worldObj.isRemote)
{
return;
}
super.getNetworkedData(list);
list.add(this.fuelTank.getFluidAmount());
list.add(this.landing);
list.add(this.destinationFrequency);
list.add(this.targetVec != null);
if (this.targetVec != null)
{
list.add(this.targetVec.getX());
list.add(this.targetVec.getY());
list.add(this.targetVec.getZ());
}
list.add(this.motionX * 8000.0D);
list.add(this.motionY * 8000.0D);
list.add(this.motionZ * 8000.0D);
list.add(this.lastMotionY * 8000.0D);
list.add(this.lastLastMotionY * 8000.0D);
list.add(this.getWaitForPlayer());
list.add(this.statusMessage != null ? this.statusMessage : "");
list.add(this.statusMessageCooldown);
list.add(this.lastStatusMessageCooldown);
list.add(this.statusValid);
if (!this.worldObj.isRemote)
{
list.add(this.riddenByEntity == null ? -1 : this.riddenByEntity.getEntityId());
}
list.add(this.statusColour != null ? this.statusColour : "");
}
@Override
protected void failRocket()
{
if (this.shouldCancelExplosion())
{
//TODO: why looking around when already know the target?
//TODO: it would be good to land on an alternative neighbouring pad if there is already a rocket on the target pad
for (int i = -3; i <= 3; i++)
{
BlockPos pos = new BlockPos((int) Math.floor(this.posX), (int) Math.floor(this.posY + i), (int) Math.floor(this.posZ));
if (this.landing && this.targetVec != null && this.worldObj.getTileEntity(pos) instanceof IFuelDock && this.posY - this.targetVec.getY() < 5)
{
for (int x = MathHelper.floor_double(this.posX) - 1; x <= MathHelper.floor_double(this.posX) + 1; x++)
{
for (int y = MathHelper.floor_double(this.posY - 3.0D); y <= MathHelper.floor_double(this.posY) + 1; y++)
{
for (int z = MathHelper.floor_double(this.posZ) - 1; z <= MathHelper.floor_double(this.posZ) + 1; z++)
{
BlockPos pos1 = new BlockPos(x, y, z);
TileEntity tile = this.worldObj.getTileEntity(pos1);
if (tile instanceof IFuelDock)
{
this.landEntity(pos1);
return;
}
}
}
}
}
}
}
if (this.launchPhase == EnumLaunchPhase.LAUNCHED.ordinal())
{
super.failRocket();
}
}
protected boolean shouldCancelExplosion()
{
return this.hasValidFuel();
}
public boolean hasValidFuel()
{
return this.fuelTank.getFluidAmount() > 0;
}
public void cancelLaunch()
{
this.setLaunchPhase(EnumLaunchPhase.UNIGNITED);
this.timeUntilLaunch = 0;
if (!this.worldObj.isRemote && this.riddenByEntity instanceof EntityPlayerMP)
{
((EntityPlayerMP) this.riddenByEntity).addChatMessage(new ChatComponentText(GCCoreUtil.translate("gui.rocket.warning.nogyroscope")));
}
}
public void failMessageLaunchController()
{
if (!this.worldObj.isRemote && this.riddenByEntity instanceof EntityPlayerMP)
{
((EntityPlayerMP) this.riddenByEntity).addChatMessage(new ChatComponentText(GCCoreUtil.translate("gui.rocket.warning.launchcontroller")));
}
}
public void failMessageInsufficientFuel()
{
if (!this.worldObj.isRemote && this.riddenByEntity instanceof EntityPlayerMP)
{
((EntityPlayerMP) this.riddenByEntity).addChatMessage(new ChatComponentText(GCCoreUtil.translate("gui.rocket.warning.fuelinsufficient")));
}
}
@Override
public void onLaunch()
{
if (!(this.worldObj.provider.getDimensionId() == GalacticraftCore.planetOverworld.getDimensionID() || this.worldObj.provider instanceof IGalacticraftWorldProvider))
{
if (ConfigManagerCore.disableRocketLaunchAllNonGC)
{
this.cancelLaunch();
return;
}
//No rocket flight in the Nether, the End etc
for (int i = ConfigManagerCore.disableRocketLaunchDimensions.length - 1; i >= 0; i--)
{
if (ConfigManagerCore.disableRocketLaunchDimensions[i] == this.worldObj.provider.getDimensionId())
{
this.cancelLaunch();
return;
}
}
}
super.onLaunch();
if (!this.worldObj.isRemote)
{
GCPlayerStats stats = null;
if (this.riddenByEntity != null && this.riddenByEntity instanceof EntityPlayerMP)
{
stats = GCPlayerStats.get(this.riddenByEntity);
if (!(this.worldObj.provider instanceof IOrbitDimension))
{
stats.setCoordsTeleportedFromX(this.riddenByEntity.posX);
stats.setCoordsTeleportedFromZ(this.riddenByEntity.posZ);
}
}
int amountRemoved = 0;
PADSEARCH:
for (int x = MathHelper.floor_double(this.posX) - 1; x <= MathHelper.floor_double(this.posX) + 1; x++)
{
for (int y = MathHelper.floor_double(this.posY) - 3; y <= MathHelper.floor_double(this.posY) + 1; y++)
{
for (int z = MathHelper.floor_double(this.posZ) - 1; z <= MathHelper.floor_double(this.posZ) + 1; z++)
{
BlockPos pos = new BlockPos(x, y, z);
final Block block = this.worldObj.getBlockState(pos).getBlock();
if (block != null && block instanceof BlockLandingPadFull)
{
if (amountRemoved < 9)
{
EventLandingPadRemoval event = new EventLandingPadRemoval(this.worldObj, pos);
MinecraftForge.EVENT_BUS.post(event);
if (event.allow)
{
this.worldObj.setBlockToAir(pos);
amountRemoved = 9;
}
break PADSEARCH;
}
}
}
}
}
//Set the player's launchpad item for return on landing - or null if launchpads not removed
if (stats != null)
{
stats.setLaunchpadStack(amountRemoved == 9 ? new ItemStack(GCBlocks.landingPad, 9, 0) : null);
}
this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F);
}
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt)
{
super.writeEntityToNBT(nbt);
if (this.fuelTank.getFluid() != null)
{
nbt.setTag("fuelTank", this.fuelTank.writeToNBT(new NBTTagCompound()));
}
if (this.getSizeInventory() > 0)
{
final NBTTagList var2 = new NBTTagList();
for (int var3 = 0; var3 < this.cargoItems.length; ++var3)
{
if (this.cargoItems[var3] != null)
{
final NBTTagCompound var4 = new NBTTagCompound();
var4.setByte("Slot", (byte) var3);
this.cargoItems[var3].writeToNBT(var4);
var2.appendTag(var4);
}
}
nbt.setTag("Items", var2);
}
nbt.setBoolean("TargetValid", this.targetVec != null);
if (this.targetVec != null)
{
nbt.setDouble("targetTileX", this.targetVec.getX());
nbt.setDouble("targetTileY", this.targetVec.getY());
nbt.setDouble("targetTileZ", this.targetVec.getZ());
}
nbt.setBoolean("WaitingForPlayer", this.getWaitForPlayer());
nbt.setBoolean("Landing", this.landing);
nbt.setInteger("AutoLaunchSetting", this.autoLaunchSetting != null ? this.autoLaunchSetting.getIndex() : -1);
nbt.setInteger("TimeUntilAutoLaunch", this.autoLaunchCountdown);
nbt.setInteger("DestinationFrequency", this.destinationFrequency);
if (this.activeLaunchController != null) this.activeLaunchController.writeToNBT(nbt,"ALCat");
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbt)
{
super.readEntityFromNBT(nbt);
if (nbt.hasKey("fuelTank"))
{
this.fuelTank.readFromNBT(nbt.getCompoundTag("fuelTank"));
}
if (this.getSizeInventory() > 0)
{
final NBTTagList var2 = nbt.getTagList("Items", 10);
this.cargoItems = new ItemStack[this.getSizeInventory()];
for (int var3 = 0; var3 < var2.tagCount(); ++var3)
{
final NBTTagCompound var4 = var2.getCompoundTagAt(var3);
final int var5 = var4.getByte("Slot") & 255;
if (var5 < this.cargoItems.length)
{
this.cargoItems[var5] = ItemStack.loadItemStackFromNBT(var4);
}
}
}
if (nbt.getBoolean("TargetValid") && nbt.hasKey("targetTileX"))
{
this.targetVec = new BlockPos(MathHelper.floor_double(nbt.getDouble("targetTileX")), MathHelper.floor_double(nbt.getDouble("targetTileY")), MathHelper.floor_double(nbt.getDouble("targetTileZ")));
}
this.setWaitForPlayer(nbt.getBoolean("WaitingForPlayer"));
this.landing = nbt.getBoolean("Landing");
int autoLaunchValue = nbt.getInteger("AutoLaunchSetting");
this.autoLaunchSetting = autoLaunchValue == -1 ? null : EnumAutoLaunch.values()[autoLaunchValue];
this.autoLaunchCountdown = nbt.getInteger("TimeUntilAutoLaunch");
this.destinationFrequency = nbt.getInteger("DestinationFrequency");
this.activeLaunchController = BlockVec3.readFromNBT(nbt, "ALCat");
}
@Override
public int addFuel(FluidStack liquid, boolean doFill)
{
return FluidUtil.fillWithGCFuel(this.fuelTank, liquid, doFill);
}
@Override
public FluidStack removeFuel(int amount)
{
return this.fuelTank.drain(amount * ConfigManagerCore.rocketFuelFactor, true);
}
@Override
public void setPad(IFuelDock pad)
{
//Called either when a rocket lands or when one is placed
//Can also be called with null param when rocket leaves a pad
this.landingPad = pad;
if (pad != null)
{
pad.dockEntity(this);
//NOTE: setPad() is called also when a world or chunk is loaded - if the rocket is Ignited (from NBT save data) do not change those settings
if (!(this.launchPhase == EnumLaunchPhase.IGNITED.ordinal()))
{
this.setLaunchPhase(EnumLaunchPhase.UNIGNITED);
this.targetVec = null;
if (GalacticraftCore.isPlanetsLoaded)
{
this.updateControllerSettings(pad);
}
}
this.landing = false;
}
}
@Override
public IFuelDock getLandingPad()
{
return this.landingPad;
}
@Override
public int getMaxFuel()
{
return this.fuelTank.getCapacity();
}
@Override
public boolean isDockValid(IFuelDock dock)
{
return (dock instanceof TileEntityLandingPad);
}
@Override
public EnumCargoLoadingState addCargo(ItemStack stack, boolean doAdd)
{
if (this.getSizeInventory() <= 3)
{
if (this.autoLaunchSetting == EnumAutoLaunch.CARGO_IS_FULL)
{
this.autoLaunch();
}
return EnumCargoLoadingState.NOINVENTORY;
}
int count = 0;
for (count = 0; count < this.cargoItems.length - 2; count++)
{
ItemStack stackAt = this.cargoItems[count];
if (stackAt != null && stackAt.getItem() == stack.getItem() && stackAt.getItemDamage() == stack.getItemDamage() && stackAt.stackSize < stackAt.getMaxStackSize())
{
if (stackAt.stackSize + stack.stackSize <= stackAt.getMaxStackSize())
{
if (doAdd)
{
this.cargoItems[count].stackSize += stack.stackSize;
this.markDirty();
}
return EnumCargoLoadingState.SUCCESS;
}
else
{
//Part of the stack can fill this slot but there will be some left over
int origSize = stackAt.stackSize;
int surplus = origSize + stack.stackSize - stackAt.getMaxStackSize();
if (doAdd)
{
this.cargoItems[count].stackSize = stackAt.getMaxStackSize();
this.markDirty();
}
stack.stackSize = surplus;
if (this.addCargo(stack, doAdd) == EnumCargoLoadingState.SUCCESS)
{
return EnumCargoLoadingState.SUCCESS;
}
this.cargoItems[count].stackSize = origSize;
if (this.autoLaunchSetting == EnumAutoLaunch.CARGO_IS_FULL)
{
this.autoLaunch();
}
return EnumCargoLoadingState.FULL;
}
}
}
for (count = 0; count < this.cargoItems.length - 2; count++)
{
ItemStack stackAt = this.cargoItems[count];
if (stackAt == null)
{
if (doAdd)
{
this.cargoItems[count] = stack;
this.markDirty();
}
return EnumCargoLoadingState.SUCCESS;
}
}
if (this.autoLaunchSetting == EnumAutoLaunch.CARGO_IS_FULL)
{
this.autoLaunch();
}
return EnumCargoLoadingState.FULL;
}
@Override
public RemovalResult removeCargo(boolean doRemove)
{
for (int i = 0; i < this.cargoItems.length - 2; i++)
{
ItemStack stackAt = this.cargoItems[i];
if (stackAt != null)
{
ItemStack resultStack = stackAt.copy();
resultStack.stackSize = 1;
if (doRemove && --stackAt.stackSize <= 0)
{
this.cargoItems[i] = null;
}
if (doRemove)
{
this.markDirty();
}
return new RemovalResult(EnumCargoLoadingState.SUCCESS, resultStack);
}
}
if (this.autoLaunchSetting == EnumAutoLaunch.CARGO_IS_UNLOADED)
{
this.autoLaunch();
}
return new RemovalResult(EnumCargoLoadingState.EMPTY, null);
}
@Override
public ItemStack getStackInSlot(int par1)
{
if (this.cargoItems == null) return null;
return this.cargoItems[par1];
}
@Override
public ItemStack decrStackSize(int par1, int par2)
{
if (this.cargoItems[par1] != null)
{
ItemStack var3;
if (this.cargoItems[par1].stackSize <= par2)
{
var3 = this.cargoItems[par1];
this.cargoItems[par1] = null;
return var3;
}
else
{
var3 = this.cargoItems[par1].splitStack(par2);
if (this.cargoItems[par1].stackSize == 0)
{
this.cargoItems[par1] = null;
}
return var3;
}
}
else
{
return null;
}
}
@Override
public ItemStack removeStackFromSlot(int par1)
{
if (this.cargoItems[par1] != null)
{
final ItemStack var2 = this.cargoItems[par1];
this.cargoItems[par1] = null;
return var2;
}
else
{
return null;
}
}
@Override
public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
{
this.cargoItems[par1] = par2ItemStack;
if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}
}
@Override
public String getName()
{
return GCCoreUtil.translate("container.spaceship.name");
}
@Override
public int getInventoryStackLimit()
{
return 64;
}
@Override
public boolean hasCustomName()
{
return true;
}
@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer)
{
return !this.isDead && entityplayer.getDistanceSqToEntity(this) <= 64.0D;
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemstack)
{
return false;
}
@Override
public void markDirty()
{
}
@Override
public void onPadDestroyed()
{
if (!this.isDead && this.launchPhase != EnumLaunchPhase.LAUNCHED.ordinal())
{
this.dropShipAsItem();
this.setDead();
}
}
@Override
public List<ItemStack> getItemsDropped(List<ItemStack> droppedItemList)
{
if (this.cargoItems != null)
{
for (final ItemStack item : this.cargoItems)
{
if (item != null)
{
droppedItemList.add(item);
}
}
}
return droppedItemList;
}
public boolean getWaitForPlayer()
{
return this.waitForPlayer;
}
public void setWaitForPlayer(boolean waitForPlayer)
{
this.waitForPlayer = waitForPlayer;
}
public static enum EnumAutoLaunch
{
CARGO_IS_UNLOADED(0, "cargo_unloaded"),
CARGO_IS_FULL(1, "cargo_full"),
ROCKET_IS_FUELED(2, "fully_fueled"),
INSTANT(3, "instant"),
TIME_10_SECONDS(4, "ten_sec"),
TIME_30_SECONDS(5, "thirty_sec"),
TIME_1_MINUTE(6, "one_min"),
REDSTONE_SIGNAL(7, "redstone_sig");
private final int index;
private String title;
private EnumAutoLaunch(int index, String title)
{
this.index = index;
this.title = title;
}
public int getIndex()
{
return this.index;
}
public String getTitle()
{
return GCCoreUtil.translate("gui.message." + this.title + ".name");
}
}
@Override
@SideOnly(Side.CLIENT)
public ITickable getSoundUpdater()
{
return this.rocketSoundUpdater;
}
@Override
@SideOnly(Side.CLIENT)
public ISound setSoundUpdater(EntityPlayerSP player)
{
this.rocketSoundUpdater = new SoundUpdaterRocket(player, this);
return (ISound) this.rocketSoundUpdater;
}
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRender3d(double x, double y, double z)
{
double d0 = this.posX - x;
double d1 = this.posY - y;
double d2 = this.posZ - z;
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
return d3 < Constants.RENDERDISTANCE_LONG;
}
}