package zmaster587.advancedRocketry.stations;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import zmaster587.advancedRocketry.AdvancedRocketry;
import zmaster587.advancedRocketry.api.AdvancedRocketryAPI;
import zmaster587.advancedRocketry.api.Configuration;
import zmaster587.advancedRocketry.api.ISpaceObjectManager;
import zmaster587.advancedRocketry.api.stations.ISpaceObject;
import zmaster587.advancedRocketry.dimension.DimensionProperties;
import zmaster587.advancedRocketry.network.PacketSpaceStationInfo;
import zmaster587.advancedRocketry.network.PacketStationUpdate;
import zmaster587.libVulpes.network.PacketHandler;
import zmaster587.libVulpes.util.BlockPosition;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.PlayerTickEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ChatComponentText;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.util.Constants.NBT;
public class SpaceObjectManager implements ISpaceObjectManager {
private int nextId = 1;
public static final int WARPDIMID = Integer.MIN_VALUE;
private long nextStationTransitionTick = -1;
//station ids to object
HashMap<Integer,ISpaceObject> stationLocations;
//Map of planet IDs to station Ids
HashMap<Integer, List<ISpaceObject>> spaceStationOrbitMap;
HashMap<Integer, Long> temporaryDimensions; //Stores a list of temporary dimensions to time they vanish
HashMap<Integer, Integer> temporaryDimensionPlayerNumber;
HashMap<String, Class> nameToClass;
HashMap<Class, String> classToString;
private final static SpaceObjectManager spaceObjectManager = new SpaceObjectManager();
private SpaceObjectManager() {
stationLocations = new HashMap<Integer,ISpaceObject>();
spaceStationOrbitMap = new HashMap<Integer, List<ISpaceObject>>();
nameToClass = new HashMap<String, Class>();
classToString = new HashMap<Class, String>();
temporaryDimensions = new HashMap<Integer, Long>();
AdvancedRocketryAPI.spaceObjectManager = this;
}
/**
* The {@link SpaceObjectManager} is used for tasks such as managing space stations and orbiting worlds
* @return the {@link SpaceObjectManager} registered with the DimensionManager
*/
public final static SpaceObjectManager getSpaceManager() {
return spaceObjectManager;
}
/**
* @param id
* @return {@link SpaceObject} object registered to this spaceObject id, or null if doesn't exist
*/
public ISpaceObject getSpaceStation(int id) {
return stationLocations.get(id);
}
public Collection<ISpaceObject> getSpaceObjects() {
return stationLocations.values();
}
/**
* @return the next valid space object id and increments the value for the next one
*/
public int getNextStationId() {
for(int i = 1; i < Integer.MAX_VALUE; i++)
if(!stationLocations.containsKey(i))
return i;
return Integer.MAX_VALUE;
}
/**
* Registers the spaceobject class with this manager, this must be done or the object cannot be saved!
* @param str key with which to register the spaceObject type
* @param clazz class of space object to register
*/
public void registerSpaceObjectType(String str, Class<? extends Object> clazz) {
nameToClass.put(str, clazz);
classToString.put(clazz, str);
}
/**
* Attempts to get a registered SpaceObject
* @param id string identifier of the spaceobject
* @return a new instance of the spaceobject or null if not registered
*/
public ISpaceObject getNewSpaceObjectFromIdentifier(String id) {
Class clazz = nameToClass.get(id);
try {
return (ISpaceObject)clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public String getItentifierFromClass(Class<? extends ISpaceObject> clazz) {
return classToString.get(clazz);
}
/**
* Gets the object at the location of passed Block x and z
* @return Space object occupying the block coords of null if none
*/
public ISpaceObject getSpaceStationFromBlockCoords(int x, int z) {
x = (int) Math.round((x)/(2f*Configuration.stationSize));
z = (int) Math.round((z)/(2f*Configuration.stationSize));
int radius = Math.max(Math.abs(x), Math.abs(z));
int index = (int) Math.pow((2*radius-1),2) + x + radius;
if(Math.abs(z) != radius) {
index = (int) Math.pow((2*radius-1),2) + z + radius + (4*radius + 2) - 1;
if(x > 0)
index += 2*radius-1;
}
else if(z > 0)
index += 2*radius+1;
return getSpaceStation(index);
}
/**
* Registers a space object with this manager, the class must have been registered prior to this with registerSpaceObjectType!
* @param object
* @param dimId
* @param stationId
*/
public void registerSpaceObject(ISpaceObject object, int dimId, int stationId) {
object.setId(stationId);
stationLocations.put(stationId, object);
/*Calculate the location of a space station along a square spiral
* here the top and bottom(including the corner locations) are filled first then the left and right last
*
* Example shown below:
*9 A B C D
* 1 2 3
* 7 0 8
* 4 5 6
*E F.....
*/
int radius = (int) Math.floor(Math.ceil(Math.sqrt(stationId+1))/2);
int ringIndex = (int) (stationId-Math.pow((radius*2) - 1,2));
int x,z;
if(ringIndex < (radius*2 + 1)*2) {
x = ringIndex % (radius*2 + 1) - radius;
if(ringIndex < (radius*2 + 1))
z = -radius;
else
z = radius;
}
else {
int newIndex = ringIndex - (radius*2 + 1)*2;
z = newIndex % ((radius-1)*2 + 1) - (radius - 1);
if(newIndex < ((radius-1)*2 + 1))
x = -radius;
else
x = radius;
}
object.setPos(2*x, 2*z);
if(!object.hasCustomSpawnLocation())
object.setSpawnLocation(2*Configuration.stationSize*x + Configuration.stationSize/2, 128, 2*Configuration.stationSize*z + Configuration.stationSize/2);
object.setOrbitingBody(dimId);
moveStationToBody(object, dimId, false);
}
/**
* Registers a dimension that is set to expire at a given an expiration time
* @param object object to register
* @param dimId dimid to orbit around
* @param expireTime time at which to expire the dimension
*/
public void registerTemporarySpaceObject(ISpaceObject object, int dimId, long expireTime) {
int nextDimId = getNextStationId();
temporaryDimensions.put(nextDimId, expireTime);
temporaryDimensionPlayerNumber.put(dimId, 0);
registerSpaceObject(object, nextDimId);
}
/**
* Registers a space station and updates clients
* @param object
* @param dimId dimension to place it in orbit around, -1 for undefined
*/
public void registerSpaceObject(ISpaceObject object, int dimId) {
registerSpaceObject(object, dimId, getNextStationId());
PacketHandler.sendToAll(new PacketSpaceStationInfo(object.getId(), object));
}
public void unregisterSpaceObject(int id) {
temporaryDimensions.remove(id);
temporaryDimensionPlayerNumber.remove(id);
spaceStationOrbitMap.remove(id);
stationLocations.remove(id);
PacketHandler.sendToAll(new PacketSpaceStationInfo(id, null));
}
/**
* registers a dimension with the given station ID
* Used on client to create stations on packet recieve from server
* FOR INTERNAL USE ONLY
* @param object
* @param dimId dimension to place it in orbit around, -1 for undefined
*/
@SideOnly(Side.CLIENT)
public void registerSpaceObjectClient(ISpaceObject object, int dimId, int stationId) {
registerSpaceObject(object, dimId, stationId);
}
/**
*
* @param planetId id of the planet to get stations around
* @return list of spaceObjects around the planet
*/
public List<ISpaceObject> getSpaceStationsOrbitingPlanet(int planetId) {
return spaceStationOrbitMap.get(planetId);
}
/**
* Event designed to teleport a player to the spawn point for the station if he'she falls out of the world in space
* TODO: prevent inf loop if nowhere to fall!
*/
@SubscribeEvent
public void onPlayerTick(PlayerTickEvent event) {
if(event.player.worldObj.provider.dimensionId == Configuration.spaceDimId) {
if(event.player.posY < 0 && !event.player.worldObj.isRemote) {
ISpaceObject object = getSpaceStationFromBlockCoords((int)event.player.posX, (int)event.player.posZ);
if(object != null) {
BlockPosition loc = object.getSpawnLocation();
event.player.fallDistance=0;
event.player.motionY = 0;
event.player.setPositionAndUpdate(loc.x, loc.y, loc.z);
event.player.addChatComponentMessage(new ChatComponentText("You wake up finding yourself back on the station"));
}
}
int result = Math.abs(2*(((int)event.player.posZ + Configuration.stationSize/2) % (2*Configuration.stationSize) )/Configuration.stationSize);
if(result == 0 || result == 3) {
event.player.motionZ = -event.player.motionZ;
if(result == 0) {
event.player.setPosition(event.player.posX, event.player.posY, event.player.posZ + (event.player.posZ < 0 ? Math.abs(event.player.posZ % 16) : (16 - event.player.posZ % 16)));
}
else
event.player.setPosition(event.player.posX, event.player.posY, event.player.posZ - (event.player.posZ < 0 ? 16 - Math.abs(event.player.posZ % 16) : (event.player.posZ % 16)));
}
//double posX = event.player.posX < 0 ? -event.player.posX - Configuration.stationSize : event.player.posX;
result = Math.abs(2*(((int)event.player.posX + Configuration.stationSize/2) % (2*Configuration.stationSize) )/Configuration.stationSize);
if(event.player.posX < -Configuration.stationSize/2)
if(result == 3)
result = 0;
else if(result == 0)
result = 3;
if(result == 0 || result == 3) {
event.player.motionX = -event.player.motionX;
if(result == 0) {
event.player.setPosition(event.player.posX + (event.player.posX < 0 ? Math.abs(event.player.posX % 16) : (16 - event.player.posX % 16)), event.player.posY, event.player.posZ);
}
else
event.player.setPosition(event.player.posX - (event.player.posX < 0 ? 16 - Math.abs(event.player.posX % 16) : (event.player.posX % 16)), event.player.posY, event.player.posZ);
}
}
}
@SubscribeEvent
public void onServerTick(TickEvent.ServerTickEvent event) {
if(DimensionManager.getWorld(Configuration.spaceDimId) == null)
return;
long worldTime = DimensionManager.getWorld(Configuration.spaceDimId).getTotalWorldTime();
//Assuming server
//If no dim undergoing transition then nextTransitionTick = -1
if((nextStationTransitionTick != -1 && worldTime >= nextStationTransitionTick && spaceStationOrbitMap.get(WARPDIMID) != null) || (nextStationTransitionTick == -1 && spaceStationOrbitMap.get(WARPDIMID) != null && !spaceStationOrbitMap.get(WARPDIMID).isEmpty())) {
long newNextTransitionTick = -1;
for(ISpaceObject obj : spaceStationOrbitMap.get(WARPDIMID)) {
if(obj.getTransitionTime() <= worldTime) {
moveStationToBody(obj, obj.getDestOrbitingBody());
spaceStationOrbitMap.get(WARPDIMID).remove(obj);
}
else if(newNextTransitionTick == -1 || obj.getTransitionTime() < newNextTransitionTick)
newNextTransitionTick = obj.getTransitionTime();
}
nextStationTransitionTick = newNextTransitionTick;
}
}
public void onServerStopped() {
stationLocations.clear();
spaceStationOrbitMap.clear();
temporaryDimensions.clear();
nextStationTransitionTick = -1;
}
/*@SubscribeEvent
public void onPlayerTransition(PlayerEvent.PlayerChangedDimensionEvent event) {
if(event.toDim == Configuration.spaceDimId && getSpaceStationFromBlockCoords((int)event.player.posX, (int)event.player.posZ) != null &&
temporaryDimensions.containsKey(getSpaceStationFromBlockCoords((int)event.player.posX, (int)event.player.posZ))) {
int stationId = getSpaceStationFromBlockCoords((int)event.player.posX, (int)event.player.posZ).getId();
temporaryDimensionPlayerNumber.put(stationId, temporaryDimensionPlayerNumber.get(stationId)+1);
}
if(event.fromDim != Configuration.spaceDimId)
return;
ISpaceObject spaceObj = getSpaceStationFromBlockCoords((int)event.player.posX, (int)event.player.posZ);
Long expireTime = spaceObj.getExpireTime();
int numplayers;
temporaryDimensionPlayerNumber.put(spaceObj.getId(), (numplayers = temporaryDimensionPlayerNumber.get(spaceObj.getId())-1));
if(expireTime == null)
return;
long worldTime = DimensionManager.getWorld(event.toDim).getTotalWorldTime();
if(expireTime >= worldTime && numplayers == 0) {
//expired and delete
unregisterSpaceObject(spaceObj.getId());
}
}*/
public void moveStationToBody(ISpaceObject station, int dimId) {
moveStationToBody(station, dimId, true);
}
/**
* Changes the orbiting body of the space object
* @param station
* @param dimId
*/
public void moveStationToBody(ISpaceObject station, int dimId, boolean update) {
//Remove station from the planet it's in orbit around before moving it!
if(spaceStationOrbitMap.get(station.getOrbitingPlanetId()) != null) {
spaceStationOrbitMap.get(station.getOrbitingPlanetId()).remove(station);
}
if(spaceStationOrbitMap.get(dimId) == null)
spaceStationOrbitMap.put(dimId,new LinkedList<ISpaceObject>());
if(!spaceStationOrbitMap.get(dimId).contains(station))
spaceStationOrbitMap.get(dimId).add(station);
station.setOrbitingBody(dimId);
if(update) {
//if(FMLCommonHandler.instance().getSide().isServer()) {
PacketHandler.sendToAll(new PacketStationUpdate(station, PacketStationUpdate.Type.ORBIT_UPDATE));
//}
AdvancedRocketry.proxy.fireFogBurst(station);
}
}
/**
* Changes the orbiting body of the space object
* @param station
* @param dimId
* @param timeDelta time in ticks to fully make the jump
*/
public void moveStationToBody(ISpaceObject station, int dimId, int timeDelta) {
//Remove station from the planet it's in orbit around before moving it!
if(station.getOrbitingPlanetId() != WARPDIMID && spaceStationOrbitMap.get(station.getOrbitingPlanetId()) != null) {
spaceStationOrbitMap.get(station.getOrbitingPlanetId()).remove(station);
}
if(spaceStationOrbitMap.get(WARPDIMID) == null)
spaceStationOrbitMap.put(WARPDIMID,new LinkedList<ISpaceObject>());
if(!spaceStationOrbitMap.get(WARPDIMID).contains(station))
spaceStationOrbitMap.get(WARPDIMID).add(station);
station.setOrbitingBody(WARPDIMID);
//if(FMLCommonHandler.instance().getSide().isServer()) {
PacketHandler.sendToAll(new PacketStationUpdate(station, PacketStationUpdate.Type.ORBIT_UPDATE));
//}
AdvancedRocketry.proxy.fireFogBurst(station);
((DimensionProperties)station.getProperties()).setAtmosphereDensityDirect(0);
nextStationTransitionTick = (int)(Configuration.travelTimeMultiplier*timeDelta) + DimensionManager.getWorld(Configuration.spaceDimId).getTotalWorldTime();
station.beginTransition(nextStationTransitionTick);
}
public void writeToNBT(NBTTagCompound nbt) {
Iterator<ISpaceObject> iterator = stationLocations.values().iterator();
NBTTagList nbtList = new NBTTagList();
while(iterator.hasNext()) {
ISpaceObject object = iterator.next();
NBTTagCompound nbtTag = new NBTTagCompound();
object.writeToNbt(nbtTag);
nbtTag.setString("type", classToString.get(object.getClass()));
if(temporaryDimensions.containsKey(object.getId())) {
nbtTag.setLong("expireTime", temporaryDimensions.get(object.getId()));
nbtTag.setInteger("numPlayers", temporaryDimensionPlayerNumber.get(object.getId()));
}
nbtList.appendTag(nbtTag);
}
nbt.setTag("spaceContents", nbtList);
nbt.setInteger("nextInt", nextId);
nbt.setLong("nextStationTransitionTick", nextStationTransitionTick);
}
public void readFromNBT(NBTTagCompound nbt) {
NBTTagList list = nbt.getTagList("spaceContents", NBT.TAG_COMPOUND);
nextId = nbt.getInteger("nextInt");
nextStationTransitionTick = nbt.getLong("nextStationTransitionTick");
for(int i = 0; i < list.tagCount(); i++) {
NBTTagCompound tag = list.getCompoundTagAt(i);
try {
ISpaceObject object = (ISpaceObject)nameToClass.get(tag.getString("type")).newInstance();
object.readFromNbt(tag);
if(tag.hasKey("expireTime")) {
long expireTime = tag.getLong("expireTime");
int numPlayers = tag.getInteger("numPlayers");
if (DimensionManager.getWorld(Configuration.spaceDimId).getTotalWorldTime() >= expireTime && numPlayers == 0)
continue;
temporaryDimensions.put(object.getId(), expireTime);
temporaryDimensionPlayerNumber.put(object.getId(), numPlayers);
}
registerSpaceObject(object, object.getOrbitingPlanetId(), object.getId() );
} catch (Exception e) {
System.out.println(tag.getString("type"));
e.printStackTrace();
}
}
}
}