package openblocks.common.entity;
import com.google.common.collect.MapMaker;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import java.util.Calendar;
import java.util.Map;
import java.util.Random;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraft.world.gen.NoiseGeneratorPerlin;
import openblocks.Config;
import openblocks.common.IVarioController;
import openblocks.common.Vario;
import openblocks.common.item.ItemHangGlider;
import openmods.Log;
import openmods.OpenMods;
public class EntityHangGlider extends Entity implements IEntityAdditionalSpawnData {
public static final int THERMAL_HEIGTH_MIN = 70;
public static final int THERMAL_HEIGTH_OPT = 110;
public static final int THERMAL_HEIGTH_MAX = 136;
public static final int THERMAL_STRONG_BONUS_HEIGTH = 100;
public static final double VSPEED_NORMAL = -0.052;
public static final double VSPEED_FAST = -0.176;
public static final double VSPEED_MIN = -0.32;
public static final double VSPEED_MAX = 0.4;
private static final int TICKS_PER_VARIO_UPDATE = 4;
public static final int FREQ_MIN = 300;
public static final int FREQ_AVG = 600;
public static final int FREQ_MAX = 2000;
public static final int BEEP_RATE_AVG = 4;
public static final int BEEP_RATE_MAX = 24;
private static final int PROPERTY_DEPLOYED = 17;
private static Map<EntityPlayer, EntityHangGlider> gliderMap = new MapMaker().weakKeys().weakValues().makeMap();
private IVarioController varioControl = IVarioController.NULL;
public static boolean isEntityHoldingGlider(Entity player) {
EntityHangGlider glider = gliderMap.get(player);
return glider != null;
}
public static boolean isGliderDeployed(Entity player) {
EntityHangGlider glider = gliderMap.get(player);
return glider != null && glider.isDeployed();
}
private static boolean isGliderValid(EntityPlayer player, EntityHangGlider glider) {
if (player == null || player.isDead || glider == null || glider.isDead) return false;
ItemStack held = player.getHeldItem();
if (held == null || !(held.getItem() instanceof ItemHangGlider)) return false;
if (player.worldObj.provider.dimensionId != glider.worldObj.provider.dimensionId) return false;
return true;
}
@SideOnly(Side.CLIENT)
public static void updateGliders(World worldObj) {
for (Map.Entry<EntityPlayer, EntityHangGlider> e : gliderMap.entrySet()) {
EntityPlayer player = e.getKey();
EntityHangGlider glider = e.getValue();
if (isGliderValid(player, glider)) glider.fixPositions(player, player instanceof EntityPlayerSP);
else glider.setDead();
}
}
private EntityPlayer player;
private NoiseGeneratorPerlin noiseGen;
private int ticksSinceLastVarioUpdate = 0;
private double verticalMotionSinceLastVarioUpdate = 0;
private double lastMotionY = 0;
public EntityHangGlider(World world) {
super(world);
this.noiseGen = new NoiseGeneratorPerlin(new Random(world.getCurrentDate().get(Calendar.DAY_OF_YEAR)), 2);
}
public EntityHangGlider(World world, EntityPlayer player) {
this(world);
this.player = player;
}
@Override
public void readSpawnData(ByteBuf data) {
int playerId = data.readInt();
Entity e = worldObj.getEntityByID(playerId);
if (e instanceof EntityPlayer) {
player = (EntityPlayer)e;
gliderMap.put(player, this);
if (OpenMods.proxy.isClientPlayer(player))
varioControl = Vario.instance.acquire();
} else {
setDead();
}
}
@Override
public void writeSpawnData(ByteBuf data) {
if (player == null) {
Log.warn("Got glider without player id (%s)", this);
data.writeInt(-42);
} else {
data.writeInt(player.getEntityId());
}
}
@Override
protected void entityInit() {
this.dataWatcher.addObject(PROPERTY_DEPLOYED, (byte)1);
}
public boolean isDeployed() {
return this.dataWatcher.getWatchableObjectByte(PROPERTY_DEPLOYED) == 1;
}
@Override
public void onUpdate() {
if (!isGliderValid(player, this)) {
setDead();
}
if (isDead) {
gliderMap.remove(player);
return;
}
varioControl.keepAlive();
boolean isDeployed = !player.onGround && !player.isInWater() && !player.isPlayerSleeping();
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(PROPERTY_DEPLOYED, (byte)(isDeployed? 1 : 0));
fixPositions(player, false);
}
if (isDeployed) {
if (player.motionY < lastMotionY) {
final double noise = Config.hanggliderEnableThermal? getNoise() : 0;
final double vspeed = (noise >= 0? VSPEED_MAX : -VSPEED_MIN);
final double horizontalSpeed;
final double verticalSpeed;
if (player.isSneaking()) {
horizontalSpeed = 0.1;
verticalSpeed = Math.max((VSPEED_FAST + noise * vspeed), VSPEED_MIN);
} else {
horizontalSpeed = 0.03;
verticalSpeed = Math.max((VSPEED_NORMAL + noise * vspeed), VSPEED_MIN);
}
player.motionY = verticalSpeed;
motionY = verticalSpeed;
lastMotionY = verticalSpeed;
if (varioControl.isValid()) {
ticksSinceLastVarioUpdate++;
verticalMotionSinceLastVarioUpdate += verticalSpeed; // * 1 tick, for unit freaks
if (ticksSinceLastVarioUpdate > TICKS_PER_VARIO_UPDATE) {
updateVario(verticalMotionSinceLastVarioUpdate / TICKS_PER_VARIO_UPDATE);
ticksSinceLastVarioUpdate = 0;
verticalMotionSinceLastVarioUpdate = 0;
}
}
double x = Math.cos(Math.toRadians(player.rotationYawHead + 90)) * horizontalSpeed;
double z = Math.sin(Math.toRadians(player.rotationYawHead + 90)) * horizontalSpeed;
player.motionX += x;
player.motionZ += z;
player.fallDistance = 0f; // Don't like getting hurt :( -- Mikee, probably
}
} else {
if (varioControl.isValid()) {
varioControl.setFrequencies(0, 0);
ticksSinceLastVarioUpdate = 0;
verticalMotionSinceLastVarioUpdate = 0;
}
}
}
private void updateVario(double vspeed) {
if (vspeed <= 0) {
vspeed = Math.max(VSPEED_MIN, vspeed);
double freq = (vspeed - VSPEED_MIN) / Math.abs(VSPEED_MIN) * (FREQ_AVG - FREQ_MIN) + FREQ_MIN;
varioControl.setFrequencies(freq, 0);
} else {
vspeed = Math.min(VSPEED_MAX, vspeed);
double freq = vspeed / Math.abs(VSPEED_MAX) * (FREQ_MAX - FREQ_AVG) + FREQ_AVG;
double beepfreq = vspeed / Math.abs(VSPEED_MAX) * (BEEP_RATE_MAX - BEEP_RATE_AVG) + BEEP_RATE_AVG;
varioControl.setFrequencies(freq, beepfreq);
}
}
public EntityPlayer getPlayer() {
return player;
}
public double getNoise() {
double noise = noiseGen.func_151601_a((float)player.posX / 20f, (float)player.posZ / 20f) / 4d;
final boolean strong = (noise > 0.7? true : false);
final int bonus = (strong? THERMAL_STRONG_BONUS_HEIGTH : 0);
final int biomeRain = worldObj.getBiomeGenForCoords((int)player.posX, (int)player.posZ).getIntRainfall();
noise *= Math.min((Math.max((player.posY - THERMAL_HEIGTH_MIN), 0d) / (THERMAL_HEIGTH_OPT - THERMAL_HEIGTH_MIN)), 1d);
noise *= Math.min((Math.max((THERMAL_HEIGTH_MAX + bonus - player.posY), 0d) / (THERMAL_HEIGTH_MAX - THERMAL_HEIGTH_OPT + bonus / 4)), 1d);
int worldTime = (int)(worldObj.getWorldTime() % 24000);
noise *= Math.min((worldTime / 1000d), 1);
noise *= Math.min((Math.max((12000 - worldTime), 0) / 1000d), 1);
if (player.dimension != 0)
noise = 0;
else if (worldObj.isRaining() && !strong)
noise = (biomeRain > 0? -0.5 : 0);
return noise;
}
@Override
public void setDead() {
super.setDead();
gliderMap.remove(player);
if (varioControl.isValid()) {
varioControl.kill();
varioControl.release();
ticksSinceLastVarioUpdate = 0;
verticalMotionSinceLastVarioUpdate = 0;
}
}
private void fixPositions(EntityPlayer thePlayer, boolean localPlayer) {
this.lastTickPosX = prevPosX = player.prevPosX;
this.lastTickPosY = prevPosY = player.prevPosY;
this.lastTickPosZ = prevPosZ = player.prevPosZ;
this.posX = player.posX;
this.posY = player.posY;
this.posZ = player.posZ;
setPosition(posX, posY, posZ);
this.prevRotationYaw = player.prevRenderYawOffset;
this.rotationYaw = player.renderYawOffset;
this.prevRotationPitch = player.prevRotationPitch;
this.rotationPitch = player.rotationPitch;
if (!localPlayer) {
this.posY += 1.2;
this.prevPosY += 1.2;
this.lastTickPosY += 1.2;
}
this.motionX = this.posX - this.prevPosX;
this.motionY = this.posY - this.prevPosY;
this.motionZ = this.posZ - this.prevPosZ;
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {}
@Override
public boolean writeToNBTOptional(NBTTagCompound p_70039_1_) {
return false;
}
}