Unicopia/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java
2019-04-04 13:00:42 +02:00

435 lines
14 KiB
Java

package com.minelittlepony.unicopia.player;
import java.util.Random;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.init.UParticles;
import com.minelittlepony.unicopia.init.USounds;
import com.minelittlepony.unicopia.mixin.MixinEntity;
import com.minelittlepony.unicopia.particle.Particles;
import com.minelittlepony.unicopia.spell.IMagicEffect;
import com.minelittlepony.unicopia.util.serialisation.InbtSerialisable;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.DamageSource;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
class PlayerGravityDelegate implements IUpdatable, IGravity, InbtSerialisable, IFlyingPredicate, IPlayerHeightPredicate {
private final IPlayer player;
private static final float MAXIMUM_FLIGHT_EXPERIENCE = 1500;
public int ticksNextLevel = 0;
public float flightExperience = 0;
public boolean isFlying = false;
public boolean isRainbooming = false;
private double lastTickPosX = 0;
private double lastTickPosZ = 0;
private float gravity = 0;
public PlayerGravityDelegate(IPlayer player) {
this.player = player;
}
@Override
public boolean checkCanFly(IPlayer player) {
if (player.getOwner().capabilities.isCreativeMode) {
return true;
}
if (player.hasEffect()) {
IMagicEffect effect = player.getEffect();
if (!effect.getDead() && effect instanceof IFlyingPredicate) {
return ((IFlyingPredicate)effect).checkCanFly(player);
}
}
return player.getPlayerSpecies().canFly();
}
protected boolean isRainboom(IPlayer player) {
return Math.sqrt(getHorizontalMotion(player.getOwner())) > 0.4F;
}
@Override
public float getTargetEyeHeight(IPlayer player) {
if (player.hasEffect()) {
IMagicEffect effect = player.getEffect();
if (!effect.getDead() && effect instanceof IPlayerHeightPredicate) {
float val = ((IPlayerHeightPredicate)effect).getTargetEyeHeight(player);
if (val > 0) {
return val;
}
}
}
if (isFlying && isRainboom(player)) {
return 0.5F;
}
return player.getOwner().getDefaultEyeHeight();
}
@Override
public float getTargetBodyHeight(IPlayer player) {
if (player.hasEffect()) {
IMagicEffect effect = player.getEffect();
if (!effect.getDead() && effect instanceof IPlayerHeightPredicate) {
float val = ((IPlayerHeightPredicate)effect).getTargetBodyHeight(player);
if (val > 0) {
return val;
}
}
}
// Player height is reset at this point, so we can use it as our baseline.
if (isFlying && isRainboom(player)) {
return player.getOwner().height / 2;
}
return player.getOwner().height;
}
@Override
public void setGraviationConstant(float constant) {
gravity = constant;
}
@Override
public float getGravitationConstant() {
return gravity;
}
@Override
public boolean isExperienceCritical() {
return isRainbooming || flightExperience > MAXIMUM_FLIGHT_EXPERIENCE * 0.8;
}
@Override
public void onUpdate() {
EntityPlayer entity = player.getOwner();
if (isExperienceCritical() && player.isRemote()) {
Random rnd = player.getWorld().rand;
for (int i = 0; i < 360 + getHorizontalMotion(entity); i += 10) {
Vec3d pos = player.getOriginVector().add(
rnd.nextGaussian() * entity.width,
rnd.nextGaussian() * entity.height/2,
rnd.nextGaussian() * entity.width
);
Vec3d vel = new Vec3d(entity.motionX, entity.motionY, entity.motionZ);
Particles.instance().spawnParticle(UParticles.UNICORN_MAGIC, true, pos, vel);
}
}
entity.capabilities.allowFlying = checkCanFly(player);
if (!entity.capabilities.isCreativeMode) {
entity.capabilities.isFlying |= entity.capabilities.allowFlying && isFlying && !entity.onGround && !entity.isWet();
}
isFlying = entity.capabilities.isFlying && !entity.capabilities.isCreativeMode;
if (gravity != 0) {
if (!entity.capabilities.isFlying) {
entity.motionY += 0.08;
entity.motionY -= gravity;
}
entity.onGround = !entity.world.isAirBlock(new BlockPos(entity.posX, entity.posY + entity.height + 0.5F, entity.posZ));
if (entity.onGround) {
entity.capabilities.isFlying = isFlying = false;
}
}
float bodyHeight = getTargetBodyHeight(player);
float eyeHeight = getTargetEyeHeight(player);
if (gravity < 0) {
eyeHeight = bodyHeight - eyeHeight;
}
if (entity.ticksExisted > 10) {
bodyHeight = player.getInterpolator().interpolate("standingHeight", bodyHeight, 10);
eyeHeight = player.getInterpolator().interpolate("eyeHeight", eyeHeight, 10);
}
MixinEntity.setSize(entity, entity.width, bodyHeight);
entity.eyeHeight = eyeHeight;
if (gravity < 0) {
if (entity.isSneaking()) {
entity.eyeHeight += 0.2F;
}
}
if (!entity.capabilities.isCreativeMode && !entity.isElytraFlying()) {
if (isFlying && !entity.isRiding()) {
if (!isRainbooming && getHorizontalMotion(entity) > 0.2 && flightExperience < MAXIMUM_FLIGHT_EXPERIENCE) {
flightExperience++;
}
entity.fallDistance = 0;
if (player.getPlayerSpecies() != Race.CHANGELING && entity.world.rand.nextInt(100) == 0) {
float exhaustion = (0.3F * ticksNextLevel) / 70;
if (entity.isSprinting()) {
exhaustion *= 3.11F;
}
exhaustion *= (1 - flightExperience / MAXIMUM_FLIGHT_EXPERIENCE);
entity.addExhaustion(exhaustion);
}
if (ticksNextLevel++ >= MAXIMUM_FLIGHT_EXPERIENCE) {
ticksNextLevel = 0;
entity.addExperience(1);
addFlightExperience(entity, 1);
entity.playSound(SoundEvents.ENTITY_GUARDIAN_FLOP, 1, 1);
}
moveFlying(entity);
if (isExperienceCritical()) {
if (player.getEnergy() <= 0.25F) {
player.addEnergy(2);
}
if (isRainbooming || (entity.isSneaking() && isRainboom(player))) {
float forward = 0.5F * flightExperience / MAXIMUM_FLIGHT_EXPERIENCE;
entity.motionX += - forward * MathHelper.sin(entity.rotationYaw * 0.017453292F);
entity.motionZ += forward * MathHelper.cos(entity.rotationYaw * 0.017453292F);
entity.motionY += 0.3;
if (!isRainbooming || entity.world.rand.nextInt(5) == 0) {
entity.playSound(SoundEvents.ENTITY_LIGHTNING_THUNDER, 1, 1);
}
if (flightExperience > 0) {
flightExperience -= 13;
isRainbooming = true;
} else {
isRainbooming = false;
}
}
}
if (ticksNextLevel > 0 && ticksNextLevel % 30 == 0) {
entity.playSound(getWingSound(), 0.5F, 1);
}
} else {
if (ticksNextLevel != 0) {
entity.playSound(getWingSound(), 0.4F, 1);
}
ticksNextLevel = 0;
if (isExperienceCritical()) {
addFlightExperience(entity, -0.39991342F);
} else {
addFlightExperience(entity, -0.019991342F);
}
if (flightExperience < 0.02) {
isRainbooming = false;
}
}
}
lastTickPosX = entity.posX;
lastTickPosZ = entity.posZ;
}
public SoundEvent getWingSound() {
return player.getPlayerSpecies() == Race.CHANGELING ? USounds.INSECT : USounds.WING_FLAP;
}
protected void moveFlying(EntityPlayer player) {
float forward = 0.000015F * flightExperience * (float)Math.sqrt(getHorizontalMotion(player));
int factor = gravity < 0 ? -1 : 1;
boolean sneak = !player.isSneaking();
// vertical drop due to gravity
if (sneak) {
player.motionY -= (0.005F - getHorizontalMotion(player) / 100) * factor;
} else {
forward += 0.005F;
player.motionY -= 0.0001F * factor;
}
player.motionX += - forward * MathHelper.sin(player.rotationYaw * 0.017453292F);
player.motionZ += forward * MathHelper.cos(player.rotationYaw * 0.017453292F);
if (player.world.isRainingAt(player.getPosition())) {
float glance = 360 * player.world.rand.nextFloat();
forward = 0.015F * player.world.rand.nextFloat() * player.world.getRainStrength(1);
if (player.world.rand.nextInt(30) == 0) {
forward *= 10;
}
if (player.world.rand.nextInt(30) == 0) {
forward *= 10;
}
if (player.world.rand.nextInt(40) == 0) {
forward *= 100;
}
if (player.world.isThundering() && player.world.rand.nextInt(60) == 0) {
player.motionY += forward * 3;
}
if (forward >= 1) {
player.world.playSound(null, player.getPosition(), USounds.WIND_RUSH, SoundCategory.AMBIENT, 3, 1);
}
if (forward > 4) {
forward = 4;
}
//player.knockBack(player, forward, 1, 1);
player.motionX += - forward * MathHelper.sin((player.rotationYaw + glance) * 0.017453292F);
player.motionZ += forward * MathHelper.cos((player.rotationYaw + glance) * 0.017453292F);
}
}
public void landHard(EntityPlayer player, float distance, float damageMultiplier) {
if (distance <= 0) {
return;
}
PotionEffect potioneffect = player.getActivePotionEffect(MobEffects.JUMP_BOOST);
float potion = potioneffect != null ? potioneffect.getAmplifier() + 1 : 0;
int i = MathHelper.ceil((distance - 8.0F - potion) * damageMultiplier);
if (i > 0) {
int j = MathHelper.floor(player.posX);
int k = MathHelper.floor(player.posY - 0.20000000298023224D);
int l = MathHelper.floor(player.posZ);
BlockPos pos = new BlockPos(j, k, l);
IBlockState state = player.world.getBlockState(pos);
Block block = state.getBlock();
if (state.getMaterial() != Material.AIR) {
player.playSound(getFallSound(i), 1, 1);
player.attackEntityFrom(DamageSource.FALL, i);
SoundType soundtype = block.getSoundType(state, player.getEntityWorld(), pos, player);
player.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.5f, soundtype.getPitch() * 0.75f);
}
}
}
protected double getHorizontalMotion(Entity e) {
double motionX = e.posX - lastTickPosX;
double motionZ = e.posZ - lastTickPosZ;
return (motionX * motionX)
+ (motionZ * motionZ);
}
protected SoundEvent getFallSound(int distance) {
return distance > 4 ? SoundEvents.ENTITY_PLAYER_BIG_FALL : SoundEvents.ENTITY_PLAYER_SMALL_FALL;
}
private void addFlightExperience(EntityPlayer entity, float factor) {
float maximumGain = MAXIMUM_FLIGHT_EXPERIENCE - flightExperience;
float gainSteps = 20;
flightExperience = Math.max(0, flightExperience + factor * maximumGain / gainSteps);
}
public void updateFlightStat(EntityPlayer entity, boolean flying) {
if (!entity.capabilities.isCreativeMode) {
entity.capabilities.allowFlying = player.getPlayerSpecies().canFly();
if (entity.capabilities.allowFlying) {
entity.capabilities.isFlying |= flying;
isFlying = entity.capabilities.isFlying;
if (isFlying) {
ticksNextLevel = 0;
}
} else {
entity.capabilities.isFlying = false;
isFlying = false;
}
}
}
@Override
public void writeToNBT(NBTTagCompound compound) {
compound.setInteger("flightDuration", ticksNextLevel);
compound.setFloat("flightExperience", flightExperience);
compound.setBoolean("isFlying", isFlying);
compound.setBoolean("isRainbooming", isRainbooming);
if (gravity != 0) {
compound.setFloat("gravity", gravity);
}
}
@Override
public void readFromNBT(NBTTagCompound compound) {
ticksNextLevel = compound.getInteger("flightDuration");
flightExperience = compound.getFloat("flightExperience");
isFlying = compound.getBoolean("isFlying");
isRainbooming = compound.getBoolean("isRainbooming");
if (compound.hasKey("gravity")) {
gravity = compound.getFloat("gravity");
} else {
gravity = 0;
}
}
@Override
public boolean isFlying() {
return isFlying;
}
@Override
public float getFlightExperience() {
return flightExperience / MAXIMUM_FLIGHT_EXPERIENCE;
}
@Override
public float getFlightDuration() {
return ticksNextLevel / MAXIMUM_FLIGHT_EXPERIENCE;
}
}