package zmaster587.advancedRocketry.stations;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import zmaster587.advancedRocketry.AdvancedRocketry;
import zmaster587.advancedRocketry.api.Configuration;
import zmaster587.advancedRocketry.api.StatsRocket;
import zmaster587.advancedRocketry.api.dimension.IDimensionProperties;
import zmaster587.advancedRocketry.api.stations.ISpaceObject;
import zmaster587.advancedRocketry.api.stations.IStorageChunk;
import zmaster587.advancedRocketry.dimension.DimensionProperties;
import zmaster587.advancedRocketry.network.PacketStationUpdate;
import zmaster587.advancedRocketry.network.PacketStationUpdate.Type;
import zmaster587.advancedRocketry.tile.station.TileDockingPort;
import zmaster587.advancedRocketry.util.StorageChunk;
import zmaster587.libVulpes.block.BlockFullyRotatable;
import zmaster587.libVulpes.network.PacketHandler;
import zmaster587.libVulpes.util.BlockPosition;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.ForgeDirection;
public class SpaceObject implements ISpaceObject {
private int posX, posY;
private boolean created;
private int altitude;
private float orbitalDistance;
private int destinationDimId;
private int fuelAmount;
private final int MAX_FUEL = 1000;
private BlockPosition spawnLocation;
private List<BlockPosition> spawnLocations;
private List<BlockPosition> warpCoreLocation;
private HashMap<BlockPosition, String> dockingPoints;
private HashMap<BlockPosition,Boolean> occupiedLandingPads;
private long transitionEta;
private ForgeDirection direction;
private double rotation;
private double angularVelocity;
private long lastTimeModification = 0;
private DimensionProperties properties;
public boolean hasWarpCores = false;
public SpaceObject() {
properties = (DimensionProperties) zmaster587.advancedRocketry.dimension.DimensionManager.defaultSpaceDimensionProperties.clone();
spawnLocations = new LinkedList<BlockPosition>();
occupiedLandingPads = new HashMap<BlockPosition,Boolean>();
warpCoreLocation = new LinkedList<BlockPosition>();
dockingPoints = new HashMap<BlockPosition, String>();
transitionEta = -1;
destinationDimId = 0;
created = false;
}
public long getExpireTime() {
return Long.MAX_VALUE;
}
public void beginTransition(long time) {
if(time > 0)
transitionEta = time;
}
public long getTransitionTime() {
return transitionEta;
}
/**
* @return id of the space object (NOT the DIMID)
*/
@Override
public int getId() {
return properties.getId();
}
/**
* @return dimension properties of the object
*/
@Override
public DimensionProperties getProperties() {
return properties;
}
@SideOnly(Side.CLIENT)
public void setProperties(IDimensionProperties properties) {
this.properties = (DimensionProperties)properties;
}
/**
* @return the DIMID of the planet the object is currently orbiting, -1 if none
*/
@Override
public int getOrbitingPlanetId() {
return properties.getParentPlanet();
}
/**
* Sets the forward Facing direction of the object. Mostly used for warpships
* @param direction
*/
public void setForwardDirection(ForgeDirection direction) {
this.direction = direction;
}
/**
* Gets the forward facing direction of the ship. Direction is not garunteed to be set
* @return direction of the ship, or UNKNOWN if none exists
*/
public ForgeDirection getForwardDirection() {
if(direction == null)
return ForgeDirection.NORTH;
return direction;
}
/**
* @return the altitude above the parent DIM the object currently is
*/
public int getAltitude() {
return altitude;
}
/**
* @return rotation of the station in degrees
*/
public double getRotation() {
return (rotation + getDeltaRotation()*(getWorldTime() - lastTimeModification)) % (360D);
}
/**
* @param rotation rotation of the station in degrees
*/
public void setRotation(double rotation) {
this.rotation = rotation;
}
/**
* @return anglarVelocity of the station in degrees per tick
*/
public double getDeltaRotation() {
return angularVelocity;
}
/**
* @param rotation anglarVelocity of the station in degrees per tick
*/
public void setDeltaRotation(double rotation) {
this.rotation = getRotation();
this.lastTimeModification = getWorldTime();
this.angularVelocity = rotation;
}
public double getMaxRotationalAcceleration() {
return 0.00002D;
}
private long getWorldTime() {
return AdvancedRocketry.proxy.getWorldTimeUniversal(Configuration.spaceDimId);
}
/**
* @return the X postion on the graph the object is stored in {@link SpaceObjectManager}
*/
public int getPosX() {
return posX;
}
/**
* @return the Y postion on the graph the object is stored in {@link SpaceObjectManager}
*/
public int getPosY() {
return posY;
}
/**
* @return the spawn location of the object
*/
public BlockPosition getSpawnLocation() {
return spawnLocation;
}
public void addWarpCore(BlockPosition position) {
warpCoreLocation.add(position);
hasWarpCores = true;
}
public void removeWarpCore(BlockPosition position) {
warpCoreLocation.remove(position);
if(warpCoreLocation.isEmpty())
hasWarpCores = false;
}
public List<BlockPosition> getWarpCoreLocations() {
return warpCoreLocation;
}
public boolean hasUsableWarpCore() {
return hasWarpCores && properties.getParentPlanet() != SpaceObjectManager.WARPDIMID && getDestOrbitingBody() != getOrbitingPlanetId();
}
public int getFuelAmount() {
return fuelAmount;
}
public int getMaxFuelAmount() {
return MAX_FUEL;
}
public void setFuelAmount(int amt) {
fuelAmount = amt;
}
/**
* Adds the passed amount of fuel to the space station
* @param amt
* @return amount of fuel used
*/
public int addFuel(int amt) {
if(amt < 0)
return amt;
int oldFuelAmt = fuelAmount;
fuelAmount = Math.min(fuelAmount + amt, MAX_FUEL);
amt = fuelAmount - oldFuelAmt;
if(FMLCommonHandler.instance().getSide().isServer())
PacketHandler.sendToAll(new PacketStationUpdate(this, Type.FUEL_UPDATE));
return amt;
}
/**
* Used the amount of fuel passed
* @param amt
* @return amount of fuel consumed
*/
public int useFuel(int amt) {
if(amt > getFuelAmount())
return 0;
fuelAmount -= amt;
if(FMLCommonHandler.instance().getSide().isServer())
PacketHandler.sendToAll(new PacketStationUpdate(this, Type.FUEL_UPDATE));
return amt;
}
/**
* Adds a landing pad to the station
* @param x
* @param z
*/
public void addLandingPad(int x, int z) {
BlockPosition pos = new BlockPosition(x, 0, z);
if(!spawnLocations.contains(pos)) {
spawnLocations.add(pos);
occupiedLandingPads.put(pos, false);
}
}
/**
* Adds a docking location to the station
* @param x
* @param y
* @param z
*/
public void addDockingPosition(int x, int y, int z, String str) {
BlockPosition pos = new BlockPosition(x, y, z);
dockingPoints.put(pos, str);
}
/**
* Removes a docking location from the station
* @param x
* @param y
* @param z
*/
public void removeDockingPosition(int x, int y, int z) {
BlockPosition pos = new BlockPosition(x, y, z);
dockingPoints.remove(pos);
}
/**
* Removes an existing landing pad from the station
* @param x
* @param z
*/
public void removeLandingPad(int x, int z) {
BlockPosition pos = new BlockPosition(x, 0, z);
spawnLocations.remove(pos);
occupiedLandingPads.remove(pos);
}
/**
* @return next viable place to land
*/
public BlockPosition getNextLandingPad(boolean commit) {
for(BlockPosition pos : spawnLocations) {
if(!occupiedLandingPads.get(pos)) {
if(commit)
occupiedLandingPads.put(pos, true);
return pos;
}
}
return null;
}
/**
* @return true if there is an empty pad to land on
*/
public boolean hasFreeLandingPad() {
for(BlockPosition pos : spawnLocations) {
if(!occupiedLandingPads.get(pos)) {
return true;
}
}
return false;
}
/**
* @param x
* @param z
* @param full true if the pad is avalible to use
*/
public void setPadStatus(int x, int z, boolean full) {
BlockPosition pos = new BlockPosition(x, 0, z);
if(occupiedLandingPads.containsKey(pos))
occupiedLandingPads.put(pos, full);
}
/**
* @param id the space object id of this object (NOT DIMID)
*/
@Override
public void setId(int id) {
properties.setId(id);
}
/**
* Sets the coords of the space object on the graph
* @param posX
* @param posY
*/
@Override
public void setPos(int posX, int posY) {
this.posX = posX;
this.posY = posY;
}
/**
* Sets the spawn location for the space object
* @param x
* @param y
* @param z
*/
@Override
public void setSpawnLocation(int x, int y, int z) {
spawnLocation = new BlockPosition(x,y,z);
}
/**
* Sets the orbiting planet for the space object but does NOT register it with the planet
* @param id
*/
@Override
public void setOrbitingBody(int id) {
if(id == this.getOrbitingPlanetId())
return;
properties.setParentPlanet(zmaster587.advancedRocketry.dimension.DimensionManager.getInstance().getDimensionProperties(id), false);
if(id != SpaceObjectManager.WARPDIMID)
destinationDimId = id;
}
@Override
public void setDestOrbitingBody(int id) {
destinationDimId = id;
if(FMLCommonHandler.instance().getSide().isServer()) {
PacketHandler.sendToAll(new PacketStationUpdate(this, PacketStationUpdate.Type.DEST_ORBIT_UPDATE));
}
}
@Override
public int getDestOrbitingBody() {
return destinationDimId;
}
/**
* When the space stations are first created they are 'unpacked' from the storage chunk they reside in
* Can also be called when a module is shipped
* @param chunk
*/
@Override
public void onModuleUnpack(IStorageChunk chunk) {
if(DimensionManager.isDimensionRegistered(Configuration.spaceDimId) && DimensionManager.getWorld(Configuration.spaceDimId) == null)
DimensionManager.initDimension(Configuration.spaceDimId);
World worldObj = DimensionManager.getWorld(Configuration.spaceDimId);
//If this is the first module sent up
if(!created) {
chunk.pasteInWorld(worldObj, spawnLocation.x - chunk.getSizeX()/2, spawnLocation.y - chunk.getSizeY()/2, spawnLocation.z - chunk.getSizeZ()/2);
created = true;
}
else {
List<TileEntity> tiles = chunk.getTileEntityList();
List<String> targetIds = new LinkedList<String>();
List<TileEntity> myPoss = new LinkedList<TileEntity>();
BlockPosition pos = null;
TileDockingPort destTile = null;
TileDockingPort srcTile = null;
//Iterate though all docking ports on the module in the chunk being launched
for(TileEntity tile : tiles) {
if(tile instanceof TileDockingPort) {
targetIds.add(((TileDockingPort)tile).getTargetId());
myPoss.add(tile);
}
}
//Find the first docking port on the station that matches the id in the new chunk
for(Entry<BlockPosition, String> map : dockingPoints.entrySet()) {
if(targetIds.contains(map.getValue())) {
int loc = targetIds.indexOf(map.getValue());
pos = map.getKey();
TileEntity tile;
if((tile = worldObj.getTileEntity(pos.x, pos.y, pos.z)) instanceof TileDockingPort) {
destTile = (TileDockingPort)tile;
srcTile = (TileDockingPort) myPoss.get(loc);
break;
}
}
}
if(destTile != null) {
ForgeDirection stationFacing = BlockFullyRotatable.getFront(destTile.getBlockMetadata());
ForgeDirection moduleFacing = BlockFullyRotatable.getFront(srcTile.getBlockMetadata());
ForgeDirection cross = moduleFacing.getRotation(stationFacing);
if(cross == moduleFacing) {
if(moduleFacing == stationFacing) {
if(cross == ForgeDirection.DOWN || cross == ForgeDirection.UP) {
chunk.rotateBy(ForgeDirection.NORTH);
chunk.rotateBy(ForgeDirection.NORTH);
}
else {
chunk.rotateBy(ForgeDirection.UP);
chunk.rotateBy(ForgeDirection.UP);
}
}
}
else if(cross.getOpposite() != moduleFacing)
chunk.rotateBy(stationFacing.offsetY == 0 ? cross : cross.getOpposite());
int xCoord = (stationFacing.offsetX == 0 ? -srcTile.xCoord : srcTile.xCoord*stationFacing.offsetX) + stationFacing.offsetX + destTile.xCoord;
int yCoord = (stationFacing.offsetY == 0 ? -srcTile.yCoord : srcTile.yCoord*stationFacing.offsetY) + stationFacing.offsetY + destTile.yCoord;
int zCoord = (stationFacing.offsetZ == 0 ? -srcTile.zCoord : srcTile.zCoord*stationFacing.offsetZ) + stationFacing.offsetZ + destTile.zCoord;
chunk.pasteInWorld(worldObj, xCoord, yCoord, zCoord);
worldObj.setBlockToAir(destTile.xCoord + stationFacing.offsetX, destTile.yCoord + stationFacing.offsetY, destTile.zCoord + stationFacing.offsetZ);
worldObj.setBlockToAir(destTile.xCoord, destTile.yCoord, destTile.zCoord);
}
}
}
@Override
public void writeToNbt(NBTTagCompound nbt) {
properties.writeToNBT(nbt);
nbt.setBoolean("created", created);
nbt.setInteger("id", getId());
nbt.setInteger("posX", posX);
nbt.setInteger("posY", posY);
nbt.setInteger("alitude", altitude);
nbt.setInteger("spawnX", spawnLocation.x);
nbt.setInteger("spawnY", spawnLocation.y);
nbt.setInteger("spawnZ", spawnLocation.z);
nbt.setInteger("destinationDimId", destinationDimId);
nbt.setInteger("fuel", fuelAmount);
nbt.setDouble("rotation", rotation);
nbt.setDouble("deltaRotation", angularVelocity);
if(direction != null)
nbt.setInteger("direction", direction.ordinal());
if(transitionEta > -1)
nbt.setLong("transitionEta", transitionEta);
NBTTagList list = new NBTTagList();
for(BlockPosition pos : this.spawnLocations) {
NBTTagCompound tag = new NBTTagCompound();
tag.setBoolean("occupied", occupiedLandingPads.get(pos));
tag.setIntArray("pos", new int[] {pos.x, pos.z});
list.appendTag(tag);
}
nbt.setTag("spawnPositions", list);
list = new NBTTagList();
for(BlockPosition pos : this.warpCoreLocation) {
NBTTagCompound tag = new NBTTagCompound();
tag.setIntArray("pos", new int[] {pos.x, pos.y, pos.z});
list.appendTag(tag);
}
nbt.setTag("warpCorePositions", list);
list = new NBTTagList();
for(Entry<BlockPosition, String> obj : this.dockingPoints.entrySet()) {
NBTTagCompound tag = new NBTTagCompound();
BlockPosition pos = obj.getKey();
String str = obj.getValue();
tag.setIntArray("pos", new int[] {pos.x, pos.y, pos.z});
tag.setString("id", str);
list.appendTag(tag);
}
nbt.setTag("dockingPositons", list);
}
@Override
public void readFromNbt(NBTTagCompound nbt) {
properties.readFromNBT(nbt);
if((int)orbitalDistance != properties.getParentOrbitalDistance())
orbitalDistance = properties.getParentOrbitalDistance();
created = nbt.getBoolean("created");
destinationDimId = nbt.getInteger("destinationDimId");
posX = nbt.getInteger("posX");
posY = nbt.getInteger("posY");
altitude = nbt.getInteger("altitude");
fuelAmount = nbt.getInteger("fuel");
spawnLocation = new BlockPosition(nbt.getInteger("spawnX"), nbt.getInteger("spawnY"), nbt.getInteger("spawnZ"));
properties.setId(nbt.getInteger("id"));
rotation = nbt.getDouble("rotation");
angularVelocity = nbt.getDouble("deltaRotation");
if(nbt.hasKey("direction"))
direction = ForgeDirection.getOrientation(nbt.getInteger("direction"));
if(nbt.hasKey("transitionEta"))
transitionEta = nbt.getLong("transitionEta");
NBTTagList list = nbt.getTagList("spawnPositions", NBT.TAG_COMPOUND);
spawnLocations.clear();
occupiedLandingPads.clear();
for(int i = 0; i < list.tagCount(); i++) {
NBTTagCompound tag = list.getCompoundTagAt(i);
int[] posInt = tag.getIntArray("pos");
BlockPosition pos = new BlockPosition(posInt[0], 0, posInt[1]);
spawnLocations.add(pos);
occupiedLandingPads.put(pos, tag.getBoolean("occupied"));
}
list = nbt.getTagList("warpCorePositions", NBT.TAG_COMPOUND);
warpCoreLocation.clear();
hasWarpCores = false;
for(int i = 0; i < list.tagCount(); i++) {
NBTTagCompound tag = list.getCompoundTagAt(i);
int[] posInt = tag.getIntArray("pos");
BlockPosition pos = new BlockPosition(posInt[0], posInt[1], posInt[2]);
warpCoreLocation.add(pos);
hasWarpCores = true;
}
list = nbt.getTagList("dockingPositons", NBT.TAG_COMPOUND);
dockingPoints.clear();
for(int i = 0; i < list.tagCount(); i++) {
NBTTagCompound tag = list.getCompoundTagAt(i);
int[] posInt = tag.getIntArray("pos");
BlockPosition pos = new BlockPosition(posInt[0], posInt[1], posInt[2]);
String str = tag.getString("id");
dockingPoints.put(pos, str);
}
}
/**
* True if the spawn location for this space object is not the default one assigned to it
* @return
*/
@Override
public boolean hasCustomSpawnLocation() {
return false;
}
@Override
public float getOrbitalDistance() {
return orbitalDistance;
}
@Override
public void setOrbitalDistance(float finalVel) {
if((int)orbitalDistance != properties.getParentOrbitalDistance())
properties.setParentOrbitalDistance((int)orbitalDistance);
orbitalDistance = finalVel;
}
}