/* * This file is part of Matter Overdrive * Copyright (c) 2015., Simeon Radivoev, All rights reserved. * * Matter Overdrive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Matter Overdrive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>. */ package matteroverdrive.entity.player; import cofh.api.energy.IEnergyContainerItem; import cofh.api.energy.IEnergyStorage; import com.google.common.collect.Multimap; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import matteroverdrive.MatterOverdrive; import matteroverdrive.Reference; import matteroverdrive.api.android.IAndroid; import matteroverdrive.api.android.IBionicStat; import matteroverdrive.api.events.MOEventAndroid; import matteroverdrive.api.events.weapon.MOEventEnergyWeapon; import matteroverdrive.api.inventory.IBionicPart; import matteroverdrive.data.Inventory; import matteroverdrive.data.MinimapEntityInfo; import matteroverdrive.data.inventory.BionicSlot; import matteroverdrive.data.inventory.EnergySlot; import matteroverdrive.gui.GuiAndroidHud; import matteroverdrive.handler.ConfigurationHandler; import matteroverdrive.handler.KeyHandler; import matteroverdrive.init.MatterOverdriveItems; import matteroverdrive.network.packet.client.PacketAndroidTransformation; import matteroverdrive.network.packet.client.PacketSendMinimapInfo; import matteroverdrive.network.packet.client.PacketSyncAndroid; import matteroverdrive.network.packet.server.PacketAndroidChangeAbility; import matteroverdrive.proxy.ClientProxy; import matteroverdrive.util.MOLog; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.IAttribute; import net.minecraft.entity.ai.attributes.IAttributeInstance; 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.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.living.LivingFallEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import org.apache.logging.log4j.Level; import java.util.*; import java.util.stream.Collectors; /** * Created by Simeon on 5/26/2015. */ public class AndroidPlayer implements IEnergyStorage, IAndroid { public static int RECHARGE_AMOUNT_ON_RESPAWN = 64000; public static boolean HURT_GLITCHING = true; public final static int BUILTIN_ENERGY_TRANSFER = 1024; public final static int TRANSFORM_TIME = 20 * 34; public final static String EFFECT_KEY_TURNING = "Turning"; public final static int ENERGY_WATCHER_DEFAULT = 29; private static int energyWatchID; public final static int ENERGY_FOOD_MULTIPLY = 256; public final static int ENERGY_PER_JUMP = 512; public final static float FALL_NEGATE = 0.5f; public final static int MINIMAP_SEND_TIMEOUT = 20*2; public static boolean TRANSFORMATION_DEATH = true; public static boolean REMOVE_POTION_EFFECTS = true; public final static AttributeModifier outOfPowerSpeedModifier = new AttributeModifier(UUID.fromString("ec778ddc-9711-498b-b9aa-8e5adc436e00"), "Android Out of Power", -0.5, 2).setSaved(false); private static List<IBionicStat> wheelStats = new ArrayList<>(); private static Map<Integer,MinimapEntityInfo> entityInfoMap = new HashMap<>(); private ItemStack[] previousBionicPatts = new ItemStack[5]; public final int ENERGY_SLOT; private final EntityPlayer player; private Inventory inventory; private IBionicStat activeStat; NBTTagCompound unlocked; NBTTagCompound effects; int maxEnergy; boolean isAndroid; public AndroidPlayer(EntityPlayer player) { this.player = player; this.maxEnergy = 512000; inventory = new Inventory("Android"); inventory.AddSlot(new BionicSlot(false, Reference.BIONIC_HEAD)); inventory.AddSlot(new BionicSlot(false, Reference.BIONIC_ARMS)); inventory.AddSlot(new BionicSlot(false, Reference.BIONIC_LEGS)); inventory.AddSlot(new BionicSlot(false, Reference.BIONIC_CHEST)); inventory.AddSlot(new BionicSlot(false, Reference.BIONIC_OTHER)); ENERGY_SLOT = inventory.AddSlot(new EnergySlot(false)); unlocked = new NBTTagCompound(); effects = new NBTTagCompound(); int energyWatchID = MatterOverdrive.configHandler.getInt(ConfigurationHandler.KEY_ANDROID_ENERGY_WATCH_ID, ConfigurationHandler.CATEGORY_ABILITIES,ENERGY_WATCHER_DEFAULT); try { player.getDataWatcher().addObject(energyWatchID, this.maxEnergy); } catch (IllegalArgumentException e) { energyWatchID = 8; MOLog.log(Level.ERROR,e,"Android Energy Watch ID taken. Starting id iteration."); while (energyWatchID <= 31) { try { player.getDataWatcher().addObject(energyWatchID, this.maxEnergy); break; } catch (IllegalArgumentException ex) { MOLog.log(Level.ERROR,ex,"Android Energy Watch ID '%s' taken.", energyWatchID); energyWatchID++; } } } MatterOverdrive.configHandler.setInt(ConfigurationHandler.KEY_ANDROID_ENERGY_WATCH_ID, ConfigurationHandler.CATEGORY_ABILITIES, energyWatchID); MatterOverdrive.configHandler.save(); AndroidPlayer.energyWatchID = energyWatchID; registerAttributes(player); } public void registerAttributes(EntityPlayer player) { player.getAttributeMap().registerAttribute(AndroidAttributes.attributeGlitchTime); player.getAttributeMap().registerAttribute(AndroidAttributes.attributeBatteryUse); } public static void register(EntityPlayer player) { player.registerExtendedProperties(EXT_PROP_NAME, new AndroidPlayer(player)); } public static AndroidPlayer get(EntityPlayer player) { return (AndroidPlayer) player.getExtendedProperties(EXT_PROP_NAME); } public static void loadConfigs(ConfigurationHandler configurationHandler) { TRANSFORMATION_DEATH = configurationHandler.getBool("transformation_death", ConfigurationHandler.CATEGORY_ANDROID_PLAYER, true, "Should the player die after an Android transformation"); REMOVE_POTION_EFFECTS = configurationHandler.getBool("remove_potion_effects", ConfigurationHandler.CATEGORY_ANDROID_PLAYER, true, "Remove all potion effects while an Android"); HURT_GLITCHING = configurationHandler.getBool("hurt_glitching",ConfigurationHandler.CATEGORY_ANDROID_PLAYER,true,"Should the glitch effect be displayed every time the player gets hurt"); RECHARGE_AMOUNT_ON_RESPAWN = configurationHandler.getInt("recharge_amount_on_respawn",ConfigurationHandler.CATEGORY_ANDROID_PLAYER, RECHARGE_AMOUNT_ON_RESPAWN,"How much does the android player recharge after respawning"); } @Override public void saveNBTData(NBTTagCompound compound) { writeToNBT(compound,EnumSet.allOf(DataType.class)); } @Override public void loadNBTData(NBTTagCompound compound) { readFromNBT(compound,EnumSet.allOf(DataType.class)); } public void writeToNBT(NBTTagCompound compound,EnumSet<DataType> dataTypes) { NBTTagCompound prop = new NBTTagCompound(); if (dataTypes.contains(DataType.ENERGY)) { prop.setInteger("Energy", player.getDataWatcher().getWatchableObjectInt(energyWatchID)); prop.setInteger("MaxEnergy", this.maxEnergy); } if (dataTypes.contains(DataType.DATA)) { prop.setBoolean("isAndroid", isAndroid); } if (dataTypes.contains(DataType.STATS)) { prop.setTag("Stats", unlocked); } if (dataTypes.contains(DataType.EFFECTS)) { prop.setTag("Effects", effects); } if (dataTypes.contains(DataType.ACTIVE_ABILITY)) { if (activeStat != null) prop.setString("ActiveAbility", activeStat.getUnlocalizedName()); } if (dataTypes.contains(DataType.INVENTORY)) { inventory.writeToNBT(prop,true); }else if (dataTypes.contains(DataType.BATTERY)) { if (inventory.getStackInSlot(ENERGY_SLOT) != null) { NBTTagCompound batteryTag = new NBTTagCompound(); inventory.getStackInSlot(ENERGY_SLOT).writeToNBT(batteryTag); compound.setTag("Battery",batteryTag); } } compound.setTag(EXT_PROP_NAME, prop); } public void readFromNBT(NBTTagCompound compound,EnumSet<DataType> dataTypes) { NBTTagCompound prop = (NBTTagCompound) compound.getTag(EXT_PROP_NAME); if (prop != null) { boolean initFlag = false; if (dataTypes.contains(DataType.ENERGY)) { player.getDataWatcher().updateObject(energyWatchID, prop.getInteger("Energy")); this.maxEnergy = prop.getInteger("MaxEnergy"); } if (dataTypes.contains(DataType.DATA)) { this.isAndroid = prop.getBoolean("isAndroid"); initFlag = true; } if (dataTypes.contains(DataType.STATS)) { unlocked = prop.getCompoundTag("Stats"); } if (dataTypes.contains(DataType.EFFECTS)) { effects = prop.getCompoundTag("Effects"); } if (dataTypes.contains(DataType.ACTIVE_ABILITY)) { if (prop.hasKey("ActiveAbility")) { activeStat = MatterOverdrive.statRegistry.getStat(prop.getString("ActiveAbility")); } } if (dataTypes.contains(DataType.INVENTORY)) { this.inventory.clearItems(); this.inventory.readFromNBT(prop); initFlag = true; } else if (dataTypes.contains(DataType.DATA.BATTERY)) { inventory.setInventorySlotContents(ENERGY_SLOT, null); if (compound.hasKey("Battery", Constants.NBT.TAG_COMPOUND)) { ItemStack battery = ItemStack.loadItemStackFromNBT(compound.getCompoundTag("Battery")); inventory.setInventorySlotContents(ENERGY_SLOT, battery); } } if (initFlag) { init(this.player, this.player.worldObj); } } } @Override public void init(Entity entity, World world) { manageStatAttributeModifiers(); manageEquipmentAttributeModifiers(); } public int extractEnergyRaw(int amount,boolean simulate) { int energyExtracted; if (player.capabilities.isCreativeMode) { return amount; } if (getStackInSlot(ENERGY_SLOT) != null && getStackInSlot(ENERGY_SLOT).getItem() instanceof IEnergyContainerItem) { ItemStack battery = getStackInSlot(ENERGY_SLOT); IEnergyContainerItem energyContainerItem = (IEnergyContainerItem)battery.getItem(); energyExtracted = energyContainerItem.extractEnergy(battery,amount,simulate); if (energyExtracted > 0 && !simulate) sync(EnumSet.of(DataType.BATTERY)); } else { int energy = this.player.getDataWatcher().getWatchableObjectInt(energyWatchID); energyExtracted = Math.min(Math.min(energy, amount),BUILTIN_ENERGY_TRANSFER); if (!simulate) { energy -= energyExtracted; energy = MathHelper.clamp_int(energy,0,getMaxEnergyStored()); this.player.getDataWatcher().updateObject(energyWatchID,energy); } } return energyExtracted; } public void extractEnergyScaled(int amount) { double percent = getPlayer().getAttributeMap().getAttributeInstance(AndroidAttributes.attributeBatteryUse).getAttributeValue(); extractEnergyRaw((int) (amount * percent), false); } public boolean hasEnoughEnergyScaled(int energy) { double percent = getPlayer().getAttributeMap().getAttributeInstance(AndroidAttributes.attributeBatteryUse).getAttributeValue(); int newEnergy = (int) Math.ceil(energy * percent); return extractEnergyRaw(energy,true) >= newEnergy; } @Override public boolean isUnlocked(IBionicStat stat, int level) { return unlocked.hasKey(stat.getUnlocalizedName()) && unlocked.getInteger(stat.getUnlocalizedName()) >= level; } @Override public int getUnlockedLevel(IBionicStat stat) { if (unlocked.hasKey(stat.getUnlocalizedName())) { return unlocked.getInteger(stat.getUnlocalizedName()); } return 0; } public boolean tryUnlock(IBionicStat stat, int level) { if (stat.canBeUnlocked(this, level)) { unlock(stat,level); return true; } return false; } public void unlock(IBionicStat stat,int level) { clearAllStatAttributeModifiers(); this.unlocked.setInteger(stat.getUnlocalizedName(), level); stat.onUnlock(this,level); sync(EnumSet.of(DataType.STATS)); manageStatAttributeModifiers(); } @Override public int getEnergyStored() { if (player.capabilities.isCreativeMode) return getMaxEnergyStored(); if (getStackInSlot(ENERGY_SLOT) != null && getStackInSlot(ENERGY_SLOT).getItem() instanceof IEnergyContainerItem) { return ((IEnergyContainerItem) getStackInSlot(ENERGY_SLOT).getItem()).getEnergyStored(getStackInSlot(ENERGY_SLOT)); } else { return this.player.getDataWatcher().getWatchableObjectInt(energyWatchID); } } @Override public int getMaxEnergyStored() { if (getStackInSlot(ENERGY_SLOT) != null && getStackInSlot(ENERGY_SLOT).getItem() instanceof IEnergyContainerItem) { return ((IEnergyContainerItem) getStackInSlot(ENERGY_SLOT).getItem()).getMaxEnergyStored(getStackInSlot(ENERGY_SLOT)); } else { return maxEnergy; } } public int receiveEnergy(int amount,boolean simulate) { int energyReceived; if (getStackInSlot(ENERGY_SLOT) != null && getStackInSlot(ENERGY_SLOT).getItem() instanceof IEnergyContainerItem) { ItemStack battery = getStackInSlot(ENERGY_SLOT); IEnergyContainerItem energyContainerItem = (IEnergyContainerItem)battery.getItem(); energyReceived = energyContainerItem.receiveEnergy(battery,amount,simulate); sync(EnumSet.of(DataType.BATTERY)); } else { int energy = this.player.getDataWatcher().getWatchableObjectInt(energyWatchID); energyReceived = Math.min(Math.min(getMaxEnergyStored() - energy, amount), BUILTIN_ENERGY_TRANSFER); if (!simulate) { energy += energyReceived; energy = MathHelper.clamp_int(energy, 0, getMaxEnergyStored()); this.player.getDataWatcher().updateObject(energyWatchID, energy); } } return energyReceived; } @Override public int extractEnergy(int maxExtract, boolean simulate) { return extractEnergyRaw(maxExtract,simulate); } public void setAndroid(boolean isAndroid) { this.isAndroid = isAndroid; sync(EnumSet.allOf(DataType.class)); if (isAndroid) { previousBionicPatts = new ItemStack[5]; manageStatAttributeModifiers(); } else { clearAllStatAttributeModifiers(); clearAllEquipmentAttributeModifiers(); } } @Override public boolean isAndroid() { return isAndroid; } public void sync(EnumSet<DataType> part) { this.sync(player, part,false); } public void sync(EnumSet<DataType> part,boolean others) { this.sync(player, part,others); } public void sync(EntityPlayer player,EnumSet<DataType> syncPart,boolean toOthers) { if (player instanceof EntityPlayerMP) { if (toOthers) { MatterOverdrive.packetPipeline.sendToAllAround(new PacketSyncAndroid(this, syncPart), player, 64); } else { MatterOverdrive.packetPipeline.sendTo(new PacketSyncAndroid(this, syncPart), (EntityPlayerMP) player); } } } public void copy(AndroidPlayer player) { NBTTagCompound tagCompound = new NBTTagCompound(); player.saveNBTData(tagCompound); loadNBTData(tagCompound); manageStatAttributeModifiers(); } public Inventory getInventory() { return inventory; } public NBTTagCompound getUnlocked() { return unlocked; } public void setUnlocked(NBTTagCompound unlocked) { this.unlocked = unlocked; } public int resetUnlocked() { int xp = getResetXPRequired(); this.unlocked = new NBTTagCompound(); sync(EnumSet.of(DataType.STATS)); clearAllStatAttributeModifiers(); return xp; } public int getResetXPRequired() { int calculatedXP = 0; for (Object key : this.unlocked.func_150296_c()) { IBionicStat stat = MatterOverdrive.statRegistry.getStat(key.toString()); int unlocked = this.unlocked.getInteger(key.toString()); calculatedXP += stat.getXP(this,unlocked); } return calculatedXP / 2; } public void reset(IBionicStat stat) { if (getUnlocked().hasKey(stat.getUnlocalizedName())) { getUnlocked().removeTag(stat.getUnlocalizedName()); sync(EnumSet.of(DataType.STATS)); manageStatAttributeModifiers(); } } public NBTTagCompound getEffects() { return effects; } public void setEffects(NBTTagCompound effects) { this.effects = effects; } public void onAndroidTick(Side side) { if (side.isServer()) { if (isAndroid()) { if (getEnergyStored() > 0) { if (getPlayer().getFoodStats().needFood() && getEnergyStored() > 0) { int foodNeeded = 20 - getPlayer().getFoodStats().getFoodLevel(); int extractedEnergy = extractEnergyRaw(foodNeeded * ENERGY_FOOD_MULTIPLY, false); getPlayer().getFoodStats().addStats(extractedEnergy / ENERGY_FOOD_MULTIPLY, 0); } manageHasPower(); managePotionEffects(); } else if (getEnergyStored() <= 0) { manageOutOfPower(); } manageCharging(); manageEquipmentAttributeModifiers(); if (!getPlayer().worldObj.isRemote) { manageMinimapInfo(); } } manageTurning(); } if (side.isClient() && isAndroid()) { manageAbilityWheel(); } if (isAndroid()) { manageGlitch(); manageSwimming(); manageAir(); for (IBionicStat stat : MatterOverdrive.statRegistry.getStats()) { int unlockedLevel = getUnlockedLevel(stat); if (unlockedLevel > 0) { if (stat.isEnabled(this, unlockedLevel)) { stat.changeAndroidStats(this, unlockedLevel, true); stat.onAndroidUpdate(this, unlockedLevel); } else { stat.changeAndroidStats(this, unlockedLevel, false); } } } } } private void clearAllEquipmentAttributeModifiers() { for (int j = 0; j < 5; ++j) { ItemStack itemstack = this.previousBionicPatts[j]; ItemStack itemstack1 = this.inventory.getStackInSlot(j); if (itemstack1 != null && itemstack1.getItem() instanceof IBionicPart) { Multimap multimap = ((IBionicPart) itemstack1.getItem()).getModifiers(this, itemstack1); if (multimap != null) player.getAttributeMap().removeAttributeModifiers(multimap); } } } private void manageEquipmentAttributeModifiers() { boolean needsSync = false; for (int j = 0; j < 5; ++j) { ItemStack itemstack = this.previousBionicPatts[j]; ItemStack itemstack1 = this.inventory.getStackInSlot(j); if (!ItemStack.areItemStacksEqual(itemstack1, itemstack)) { //((WorldServer)player.worldObj).getEntityTracker().func_151247_a(player, new S04PacketEntityEquipment(player.getEntityId(), j, itemstack1)); if (itemstack != null && itemstack.getItem() instanceof IBionicPart) { Multimap multimap = ((IBionicPart) itemstack.getItem()).getModifiers(this, itemstack); if (multimap != null) player.getAttributeMap().removeAttributeModifiers(multimap); } if (itemstack1 != null && itemstack1.getItem() instanceof IBionicPart) { Multimap multimap = ((IBionicPart) itemstack1.getItem()).getModifiers(this, itemstack1); if (multimap != null) player.getAttributeMap().applyAttributeModifiers(multimap); } this.previousBionicPatts[j] = itemstack1 == null ? null : itemstack1.copy(); needsSync = true; } } if (needsSync) { sync(EnumSet.of(DataType.INVENTORY),true); } } private void clearAllStatAttributeModifiers() { for (IBionicStat stat : MatterOverdrive.statRegistry.getStats()) { int unlockedLevel = getUnlockedLevel(stat); Multimap multimap = stat.attributes(this, unlockedLevel); if (multimap != null) { player.getAttributeMap().removeAttributeModifiers(multimap); } } } private void manageStatAttributeModifiers() { for (IBionicStat stat : MatterOverdrive.statRegistry.getStats()) { int unlockedLevel = getUnlockedLevel(stat); Multimap multimap = stat.attributes(this,unlockedLevel); if (multimap != null) { if (isAndroid()) { if (unlockedLevel > 0) { if (stat.isEnabled(this, unlockedLevel)) { player.getAttributeMap().applyAttributeModifiers(multimap); } else { player.getAttributeMap().removeAttributeModifiers(multimap); } } else { player.getAttributeMap().removeAttributeModifiers(multimap); } } else { player.getAttributeMap().removeAttributeModifiers(multimap); } } } } private void manageMinimapInfo() { if (getPlayer() instanceof EntityPlayerMP && getPlayer().worldObj.getWorldTime() % MINIMAP_SEND_TIMEOUT == 0) { List<MinimapEntityInfo> entityList = new ArrayList<>(); for (Object entityObject : getPlayer().worldObj.loadedEntityList) { if (entityObject instanceof EntityLivingBase) { if (isVisibleOnMinimap((EntityLivingBase) entityObject, player, Vec3.createVectorHelper(((EntityLivingBase) entityObject).posX, ((EntityLivingBase) entityObject).posY, ((EntityLivingBase) entityObject).posZ).subtract(Vec3.createVectorHelper(player.posX, player.posY, player.posZ))) && MinimapEntityInfo.hasInfo((EntityLivingBase)entityObject, player)) { entityList.add(new MinimapEntityInfo((EntityLivingBase)entityObject, getPlayer())); } } } if (entityList.size() > 0) MatterOverdrive.packetPipeline.sendTo(new PacketSendMinimapInfo(entityList), (EntityPlayerMP)getPlayer()); } } public static boolean isVisibleOnMinimap(EntityLivingBase entityLivingBase, EntityPlayer player, Vec3 relativePosition) { return !entityLivingBase.isInvisible() && Math.abs(relativePosition.yCoord) < 16 && isInRangeToRenderDist(entityLivingBase, 256); } private static boolean isInRangeToRenderDist(EntityLivingBase entity, double distance) { double d1 = entity.boundingBox.getAverageEdgeLength(); d1 *= 64.0D * entity.renderDistanceWeight; return distance < d1 * d1; } public void manageOutOfPower() { IAttributeInstance speed = player.getEntityAttribute(SharedMonsterAttributes.movementSpeed); if (speed.getModifier(outOfPowerSpeedModifier.getID()) == null) { speed.applyModifier(outOfPowerSpeedModifier); } if (player.worldObj.isRemote) { if (player.worldObj.getWorldTime() % 60 == 0) { getEffects().setInteger("GlitchTime", 5); playGlitchSoundClient(player.worldObj.rand, 0.2f); } } } public void manageHasPower() { IAttributeInstance speed = player.getEntityAttribute(SharedMonsterAttributes.movementSpeed); if (speed.getModifier(outOfPowerSpeedModifier.getID()) != null) { speed.removeModifier(outOfPowerSpeedModifier); } } public void manageCharging() { if (player != null && player.isSneaking() && player.getHeldItem() != null && (player.getHeldItem().getItem() == MatterOverdriveItems.battery || player.getHeldItem().getItem() == MatterOverdriveItems.hc_battery)) { int freeEnergy = getMaxEnergyStored() - getEnergyStored(); int receivedAmount = ((IEnergyContainerItem) player.getHeldItem().getItem()).extractEnergy(player.getHeldItem(), freeEnergy, false); receiveEnergy(receivedAmount, false); } } @SideOnly(Side.CLIENT) private void manageAbilityWheel() { GuiAndroidHud.showRadial = ClientProxy.keyHandler.getBinding(KeyHandler.ABILITY_SWITCH_KEY).getIsKeyPressed(); if (GuiAndroidHud.showRadial) { double mag = Math.sqrt(GuiAndroidHud.radialDeltaX * GuiAndroidHud.radialDeltaX + GuiAndroidHud.radialDeltaY * GuiAndroidHud.radialDeltaY); double magAcceptance = 0.2D; double radialAngle = -720F; if (mag > magAcceptance) { double aSin = Math.toDegrees(Math.asin(GuiAndroidHud.radialDeltaX)); if(GuiAndroidHud.radialDeltaY >= 0 && GuiAndroidHud.radialDeltaX >= 0) { radialAngle = aSin; } else if(GuiAndroidHud.radialDeltaY < 0 && GuiAndroidHud.radialDeltaX >= 0) { radialAngle = 90D + (90D - aSin); } else if(GuiAndroidHud.radialDeltaY < 0 && GuiAndroidHud.radialDeltaX < 0) { radialAngle = 180D - aSin; } else if(GuiAndroidHud.radialDeltaY >= 0 && GuiAndroidHud.radialDeltaX < 0) { radialAngle = 270D + (90D + aSin); } } if(mag > 0.9999999D) { mag = Math.round(mag); } wheelStats.clear(); wheelStats.addAll(MatterOverdrive.statRegistry.getStats().stream() .filter(stat -> stat.showOnWheel(this, getUnlockedLevel(stat)) && isUnlocked(stat, 0)) .collect(Collectors.toList())); if (mag > magAcceptance) { GuiAndroidHud.radialAngle = radialAngle; } if (wheelStats.size() <= 0) { GuiAndroidHud.showRadial = false; return; } int i = 0; for (IBionicStat stat : wheelStats) { float leeway = 360f / wheelStats.size(); if (mag > magAcceptance && (i == 0 && (radialAngle < (leeway / 2) && radialAngle >= 0F || radialAngle > (360F) - (leeway / 2)) || i != 0 && radialAngle < (leeway * i) + (leeway / 2) && radialAngle > (leeway * i) - (leeway / 2))) { if (activeStat != stat) { activeStat = stat; MatterOverdrive.packetPipeline.sendToServer(new PacketAndroidChangeAbility(activeStat.getUnlocalizedName())); } break; } i++; } } } public void startConversion() { if (!MinecraftForge.EVENT_BUS.post(new MOEventAndroid.Transformation(this))) { if (player.worldObj.isRemote) { playTransformMusic(); } else { if (!isAndroid() && !isTurning()) { AndroidPlayer androidPlayer = AndroidPlayer.get(player); androidPlayer.startTurningToAndroid(); if (player instanceof EntityPlayerMP) MatterOverdrive.packetPipeline.sendTo(new PacketAndroidTransformation(), (EntityPlayerMP) player); } } } } @SideOnly(Side.CLIENT) private void playTransformMusic() { Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.func_147673_a(new ResourceLocation(Reference.MOD_ID + ":" + "transformation_music"))); } private void managePotionEffects() { if (isAndroid() && REMOVE_POTION_EFFECTS) { player.clearActivePotions(); } } public double getSpeedMultiply() { return player.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue() / player.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getBaseValue(); } private void manageGlitch() { if (effects.hasKey("GlitchTime")) { if (effects.getInteger("GlitchTime") > 0) { effects.setInteger("GlitchTime",effects.getInteger("GlitchTime")-1); }else { effects.removeTag("GlitchTime"); } } } public int modify(int amount, IAttribute attribute) { IAttributeInstance glitchAttribute = player.getEntityAttribute(attribute); if (glitchAttribute != null) { amount = (int)(amount * glitchAttribute.getAttributeValue()); } return amount; } public float modify(float amount, IAttribute attribute) { IAttributeInstance glitchAttribute = player.getEntityAttribute(attribute); if (glitchAttribute != null) { amount = (int)(amount * glitchAttribute.getAttributeValue()); } return amount; } private void manageSwimming() { if (player.isInWater()) { player.motionY = player.motionY - 0.007; } } private void manageAir() { if (player.getAir() < 0) { player.setAir(0); } } private void manageTurning() { if (effects.hasKey(EFFECT_KEY_TURNING)) { DamageSource fake = new DamageSource("android_transformation"); fake.setDamageIsAbsolute(); fake.setDamageBypassesArmor(); if (effects.getInteger(EFFECT_KEY_TURNING) > 0) { effects.setInteger(EFFECT_KEY_TURNING, effects.getInteger(EFFECT_KEY_TURNING) - 1); getPlayer().addPotionEffect(new PotionEffect(9, AndroidPlayer.TRANSFORM_TIME)); getPlayer().addPotionEffect(new PotionEffect(2, AndroidPlayer.TRANSFORM_TIME, 1)); getPlayer().addPotionEffect(new PotionEffect(17, AndroidPlayer.TRANSFORM_TIME)); getPlayer().addPotionEffect(new PotionEffect(18, AndroidPlayer.TRANSFORM_TIME)); if (effects.getInteger(EFFECT_KEY_TURNING) % 40 == 0) { player.attackEntityFrom(fake, 0.1f); playGlitchSound(this, player.worldObj.rand, 0.2f); } }else { effects.removeTag(EFFECT_KEY_TURNING); setAndroid(true); playGlitchSound(this, player.worldObj.rand, 0.8f); if (!player.capabilities.isCreativeMode && !player.worldObj.getWorldInfo().isHardcoreModeEnabled() && TRANSFORMATION_DEATH) { player.attackEntityFrom(fake, Integer.MAX_VALUE); player.setDead(); } } sync(EnumSet.of(DataType.EFFECTS)); } } public void playGlitchSound(AndroidPlayer player,Random random,float amount) { player.getPlayer().worldObj.playSoundAtEntity(player.getPlayer(),Reference.MOD_ID + ":gui.glitch", amount, 0.9f + random.nextFloat() * 0.2f); } @SideOnly(Side.CLIENT) public void playGlitchSoundClient(Random random,float amount) { player.worldObj.playSoundAtEntity(player,Reference.MOD_ID + ":" + "gui.glitch",amount, 0.9f + random.nextFloat() * 0.2f); } @Override public boolean isTurning() { return effects.hasKey(EFFECT_KEY_TURNING) && effects.getInteger(EFFECT_KEY_TURNING) > 0; } //region Events public void triggerEventOnStats(LivingEvent event) { if (event.entityLiving instanceof EntityPlayer) { AndroidPlayer androidPlayer = AndroidPlayer.get((EntityPlayer) event.entityLiving); if (androidPlayer.isAndroid()) { for (IBionicStat stat : MatterOverdrive.statRegistry.getStats()) { int unlockedLevel = androidPlayer.getUnlockedLevel(stat); if (unlockedLevel > 0 && stat.isEnabled(androidPlayer, unlockedLevel)) { stat.onLivingEvent(androidPlayer, unlockedLevel, event); } } } } } public void onPlayerLoad(PlayerEvent.LoadFromFile event) { manageStatAttributeModifiers(); } public void onPlayerDeath(LivingDeathEvent event) { } public void onPlayerRespawn() { while (getEnergyStored() < RECHARGE_AMOUNT_ON_RESPAWN) { receiveEnergy(RECHARGE_AMOUNT_ON_RESPAWN,false); } } public void onEntityHurt(LivingHurtEvent event) { if (!event.isCanceled()) { if (HURT_GLITCHING && event.ammount > 0) { effects.setInteger("GlitchTime", modify(10, AndroidAttributes.attributeGlitchTime)); sync(EnumSet.of(DataType.EFFECTS)); player.worldObj.playSoundAtEntity(player, Reference.MOD_ID + ":" + "gui.glitch", 0.2f, 0.9f + player.worldObj.rand.nextFloat() * 0.2f); } triggerEventOnStats(event); } } public void onEntityJump(LivingEvent.LivingJumpEvent event) { if (!event.entity.worldObj.isRemote) { extractEnergyScaled(ENERGY_PER_JUMP); } } public void onEntityFall(LivingFallEvent event) { triggerEventOnStats(event); event.distance = (event.distance * FALL_NEGATE); } public void onWeaponEvent(MOEventEnergyWeapon eventEnergyWeapon) { triggerEventOnStats(eventEnergyWeapon); } //endregion //region getters and setters @Override public EntityPlayer getPlayer() { return player; } private void startTurningToAndroid() { effects.setInteger("Turning",TRANSFORM_TIME); sync(EnumSet.of(DataType.EFFECTS)); } public long getEffectLong(String effect) { return getEffects().getLong(effect); } @Override public IBionicStat getActiveStat() { return activeStat; } public void setActiveStat(IBionicStat stat) { this.activeStat = stat; } //endregion //region inventory @Override public int getSizeInventory() { return inventory.getSizeInventory(); } @Override public ItemStack getStackInSlot(int slot) { return inventory.getStackInSlot(slot); } @Override public ItemStack decrStackSize(int slot, int amount) { return inventory.decrStackSize(slot, amount); } @Override public ItemStack getStackInSlotOnClosing(int slot) { return inventory.getStackInSlotOnClosing(slot); } @Override public void setInventorySlotContents(int slot, ItemStack stack) { inventory.setInventorySlotContents(slot,stack); } @Override public String getInventoryName() { return player.getDisplayName(); } @Override public boolean hasCustomInventoryName() { return true; } @Override public int getInventoryStackLimit() { return inventory.getInventoryStackLimit(); } @Override public void markDirty() { //sync(PacketSyncAndroid.SYNC_INVENTORY,true); } @Override public boolean isUseableByPlayer(EntityPlayer player) { return inventory.isUseableByPlayer(player); } @Override public void openInventory() { } @Override public void closeInventory() { } @Override public boolean isItemValidForSlot(int slot, ItemStack stack) { return inventory.isItemValidForSlot(slot,stack); } @SideOnly(Side.CLIENT) public static void setMinimapEntityInfo(List<MinimapEntityInfo> entityInfo) { entityInfoMap.clear(); for (MinimapEntityInfo info : entityInfo) { entityInfoMap.put(info.getEntityID(),info); } } @SideOnly(Side.CLIENT) public static MinimapEntityInfo getMinimapEntityInfo(EntityLivingBase entityLivingBase) { return entityInfoMap.get(entityLivingBase.getEntityId()); } //endregion public enum DataType { DATA,ENERGY,EFFECTS,STATS,ACTIVE_ABILITY,INVENTORY, BATTERY } }