From 69b340e640a786039a3456359621ee09ee81cf8f Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 12 Sep 2018 01:29:49 +0200 Subject: [PATCH] Code --- .../unicopia/InbtSerialisable.java | 29 ++ .../com/minelittlepony/unicopia/Race.java | 77 ++++ .../com/minelittlepony/unicopia/UClient.java | 9 + .../com/minelittlepony/unicopia/UItems.java | 42 ++ .../com/minelittlepony/unicopia/Unicopia.java | 120 ++++++ .../client/particle/EntityMagicFX.java | 80 ++++ .../unicopia/client/particle/Particles.java | 84 ++++ .../command/CommandOverrideGameMode.java | 48 +++ .../unicopia/command/CommandSpecies.java | 148 +++++++ .../unicopia/command/Commands.java | 11 + .../unicopia/input/IKeyBind.java | 11 + .../unicopia/input/IKeyHandler.java | 10 + .../unicopia/input/Keyboard.java | 20 + .../unicopia/input/UKeyHandler.java | 68 +++ .../unicopia/item/ItemApple.java | 156 +++++++ .../unicopia/network/MsgPlayerAbility.java | 51 +++ .../network/MsgPlayerCapabilities.java | 28 ++ .../unicopia/player/DefaultPlayerSpecies.java | 74 ++++ .../unicopia/player/IAbilityReceiver.java | 12 + .../unicopia/player/IPlayer.java | 32 ++ .../unicopia/player/IUpdatable.java | 7 + .../player/PlayerAbilityDelegate.java | 119 +++++ .../unicopia/player/PlayerCapabilities.java | 144 +++++++ .../player/PlayerGravityDelegate.java | 68 +++ .../unicopia/player/PlayerSpeciesList.java | 62 +++ .../minelittlepony/unicopia/power/IData.java | 5 + .../minelittlepony/unicopia/power/IPower.java | 129 ++++++ .../unicopia/power/PowerFeed.java | 137 ++++++ .../unicopia/power/PowerGrow.java | 115 +++++ .../unicopia/power/PowerMagic.java | 81 ++++ .../unicopia/power/PowerStomp.java | 405 ++++++++++++++++++ .../unicopia/power/PowerTeleport.java | 152 +++++++ .../unicopia/power/PowersRegistry.java | 58 +++ .../unicopia/power/data/Hit.java | 7 + .../unicopia/power/data/Location.java | 30 ++ .../unicopia/spell/AbstractSpell.java | 29 ++ .../unicopia/spell/ActionResult.java | 21 + .../unicopia/spell/ICaster.java | 24 ++ .../unicopia/spell/IDispenceable.java | 22 + .../unicopia/spell/IMagicEffect.java | 87 ++++ .../unicopia/spell/IUseAction.java | 43 ++ .../unicopia/spell/SpellRegistry.java | 40 ++ .../unicopia/spell/SpellShield.java | 165 +++++++ .../util/MagicalDamageSource.java | 57 +++ .../com/minelittlepony/util/PosHelper.java | 25 ++ .../minelittlepony/util/ProjectileUtil.java | 84 ++++ .../com/minelittlepony/util/shape/IShape.java | 61 +++ .../com/minelittlepony/util/shape/Sphere.java | 123 ++++++ .../minelittlepony/util/vector/VecHelper.java | 149 +++++++ ...faultPlayerCapabilitiesProxyContainer.java | 42 ++ .../unicopia/forgebullshit/FBS.java | 43 ++ .../IPlayerCapabilitiesProxyContainer.java | 10 + .../unicopia/forgebullshit/Provider.java | 36 ++ .../forgebullshit/RegistryLockSpinner.java | 28 ++ .../unicopia/forgebullshit/Storage.java | 24 ++ .../resources/assets/unicopia/lang/en_US.lang | 77 ++++ .../assets/unicopia/models/item/cloud.json | 18 + .../unicopia/models/item/cloud_large.json | 27 ++ .../unicopia/models/item/cloud_matter.json | 18 + .../unicopia/models/item/cloud_med.json | 16 + .../unicopia/models/item/cloud_small.json | 16 + .../unicopia/models/item/cloud_stairs.json | 13 + .../unicopia/models/item/enchanted_cloud.json | 10 + .../models/item/enchanted_cloud_slab.json | 10 + .../unicopia/models/item/normal_cloud.json | 10 + .../models/item/normal_cloud_slab.json | 10 + .../unicopia/models/item/packed_cloud.json | 10 + .../models/item/packed_cloud_slab.json | 10 + .../assets/unicopia/models/item/spell.json | 19 + .../unicopia/models/item/spellbook.json | 18 + .../assets/unicopia/recipes/apple.json | 18 + .../assets/unicopia/recipes/apple_2.json | 18 + .../assets/unicopia/recipes/apple_3.json | 18 + .../assets/unicopia/recipes/apple_4.json | 17 + .../unicopia/textures/items/apple_green.png | Bin 0 -> 3122 bytes .../unicopia/textures/items/apple_rotten.png | Bin 0 -> 3090 bytes .../unicopia/textures/items/apple_sweet.png | Bin 0 -> 3077 bytes .../unicopia/textures/items/apple_zap.png | Bin 0 -> 3376 bytes .../textures/items/apple_zap_cooked.png | Bin 0 -> 3455 bytes src/main/resources/mcmod.info | 16 + 80 files changed, 4111 insertions(+) create mode 100644 src/main/java/com/minelittlepony/unicopia/InbtSerialisable.java create mode 100644 src/main/java/com/minelittlepony/unicopia/Race.java create mode 100644 src/main/java/com/minelittlepony/unicopia/UClient.java create mode 100644 src/main/java/com/minelittlepony/unicopia/UItems.java create mode 100644 src/main/java/com/minelittlepony/unicopia/Unicopia.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/EntityMagicFX.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java create mode 100644 src/main/java/com/minelittlepony/unicopia/command/CommandOverrideGameMode.java create mode 100644 src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java create mode 100644 src/main/java/com/minelittlepony/unicopia/command/Commands.java create mode 100644 src/main/java/com/minelittlepony/unicopia/input/IKeyBind.java create mode 100644 src/main/java/com/minelittlepony/unicopia/input/IKeyHandler.java create mode 100644 src/main/java/com/minelittlepony/unicopia/input/Keyboard.java create mode 100644 src/main/java/com/minelittlepony/unicopia/input/UKeyHandler.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/ItemApple.java create mode 100644 src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAbility.java create mode 100644 src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/DefaultPlayerSpecies.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/IAbilityReceiver.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/IPlayer.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/IData.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/IPower.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowerGrow.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowerMagic.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowerStomp.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/PowersRegistry.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/data/Hit.java create mode 100644 src/main/java/com/minelittlepony/unicopia/power/data/Location.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/ActionResult.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/ICaster.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/IDispenceable.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/IUseAction.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java create mode 100644 src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java create mode 100644 src/main/java/com/minelittlepony/util/MagicalDamageSource.java create mode 100644 src/main/java/com/minelittlepony/util/PosHelper.java create mode 100644 src/main/java/com/minelittlepony/util/ProjectileUtil.java create mode 100644 src/main/java/com/minelittlepony/util/shape/IShape.java create mode 100644 src/main/java/com/minelittlepony/util/shape/Sphere.java create mode 100644 src/main/java/com/minelittlepony/util/vector/VecHelper.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/RegistryLockSpinner.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java create mode 100644 src/main/resources/assets/unicopia/lang/en_US.lang create mode 100644 src/main/resources/assets/unicopia/models/item/cloud.json create mode 100644 src/main/resources/assets/unicopia/models/item/cloud_large.json create mode 100644 src/main/resources/assets/unicopia/models/item/cloud_matter.json create mode 100644 src/main/resources/assets/unicopia/models/item/cloud_med.json create mode 100644 src/main/resources/assets/unicopia/models/item/cloud_small.json create mode 100644 src/main/resources/assets/unicopia/models/item/cloud_stairs.json create mode 100644 src/main/resources/assets/unicopia/models/item/enchanted_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/item/enchanted_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/models/item/normal_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/item/normal_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/models/item/packed_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/item/packed_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/models/item/spell.json create mode 100644 src/main/resources/assets/unicopia/models/item/spellbook.json create mode 100644 src/main/resources/assets/unicopia/recipes/apple.json create mode 100644 src/main/resources/assets/unicopia/recipes/apple_2.json create mode 100644 src/main/resources/assets/unicopia/recipes/apple_3.json create mode 100644 src/main/resources/assets/unicopia/recipes/apple_4.json create mode 100644 src/main/resources/assets/unicopia/textures/items/apple_green.png create mode 100644 src/main/resources/assets/unicopia/textures/items/apple_rotten.png create mode 100644 src/main/resources/assets/unicopia/textures/items/apple_sweet.png create mode 100644 src/main/resources/assets/unicopia/textures/items/apple_zap.png create mode 100644 src/main/resources/assets/unicopia/textures/items/apple_zap_cooked.png create mode 100644 src/main/resources/mcmod.info diff --git a/src/main/java/com/minelittlepony/unicopia/InbtSerialisable.java b/src/main/java/com/minelittlepony/unicopia/InbtSerialisable.java new file mode 100644 index 00000000..b0be0ef9 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/InbtSerialisable.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia; + +import net.minecraft.nbt.NBTTagCompound; + +public interface InbtSerialisable { + /** + * Called to save this to nbt to persist state on file or to transmit over the network + * + * @param compound Compound tag to write to. + */ + default void writeToNBT(NBTTagCompound compound) { + + } + + /** + * Called to load this state from nbt + * + * @param compound Compound tag to read from. + */ + default void readFromNBT(NBTTagCompound compound) { + + } + + default NBTTagCompound toNBT() { + NBTTagCompound compound = new NBTTagCompound(); + writeToNBT(compound); + return compound; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java new file mode 100644 index 00000000..c3190f8a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -0,0 +1,77 @@ +package com.minelittlepony.unicopia; + +import com.google.common.base.Strings; + +import net.minecraft.client.resources.I18n; + +public enum Race { + HUMAN(false, false, false), + EARTH(false, false, true), + UNICORN(true, false, false), + PEGASUS(false, true, false), + ALICORN(true, true, true), + CHANGELING(false, true, false); + + private final boolean magic; + private final boolean flight; + private final boolean earth; + + Race(boolean magic, boolean flight, boolean earth) { + this.magic = magic; + this.flight = flight; + this.earth = earth; + } + + public boolean isDefault() { + return this == HUMAN; + } + + public boolean canFly() { + return flight; + } + + public boolean canCast() { + return magic; + } + + public boolean canUseEarth() { + return earth; + } + + public String getDisplayString() { + return I18n.format(getTranslationString()); + } + + public String getTranslationString() { + return String.format("unicopia.race.%s", name()); + } + + public boolean isSameAs(String s) { + return name().equalsIgnoreCase(s) + || getTranslationString().equalsIgnoreCase(s) + || getDisplayString().equalsIgnoreCase(s); + } + + public static Race fromName(String s) { + if (!Strings.isNullOrEmpty(s)) { + for (Race i : values()) { + if (i.isSameAs(s)) return i; + } + } + + return fromId(s); + } + + public static Race fromId(String s) { + try { + int id = Integer.parseInt(s); + Race[] values = values(); + if (id >= 0 || id < values.length) { + return values[id]; + } + } catch (NumberFormatException e) { } + + return HUMAN; + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/UClient.java b/src/main/java/com/minelittlepony/unicopia/UClient.java new file mode 100644 index 00000000..a29d3cbb --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UClient.java @@ -0,0 +1,9 @@ +package com.minelittlepony.unicopia; + +import net.minecraftforge.fml.common.FMLCommonHandler; + +public interface UClient { + static boolean isClientSide() { + return FMLCommonHandler.instance().getSide().isServer(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/UItems.java b/src/main/java/com/minelittlepony/unicopia/UItems.java new file mode 100644 index 00000000..2662c2a4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UItems.java @@ -0,0 +1,42 @@ +package com.minelittlepony.unicopia; + +import com.minelittlepony.unicopia.item.ItemApple; + +import come.minelittlepony.unicopia.forgebullshit.RegistryLockSpinner; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.ModelLoader; + +public class UItems { + public static final ItemApple apple = new ItemApple() + .setSubTypes("apple", "green", "sweet", "rotten", "zap", "zap_cooked") + .setTypeRarities(10, 20, 10, 30); + + static void registerItems() { + RegistryLockSpinner.unlock(Item.REGISTRY); + + Item.REGISTRY.register(260, new ResourceLocation("minecraft", "apple"), apple); + + if (UClient.isClientSide()) { + String[] variants = apple.getVariants(); + + ResourceLocation app = new ResourceLocation("minecraft", "apple"); + + for (int i = 0; i < variants.length; i++) { + ModelLoader.setCustomModelResourceLocation(apple, i, new ModelResourceLocation(app, variants[i])); + } + } + + RegistryLockSpinner.lock(Item.REGISTRY); + } + + static void registerFuels() { + int zap = apple.getZapAppleMetadata(); + FurnaceRecipes.instance().addSmeltingRecipe( + new ItemStack(UItems.apple, 1, zap), + new ItemStack(UItems.apple, 1, zap + 1), 0.1F); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java new file mode 100644 index 00000000..27ec1d5c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -0,0 +1,120 @@ +package com.minelittlepony.unicopia; + +import net.minecraft.entity.Entity; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartingEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import com.minelittlepony.jumpingcastle.api.IChannel; +import com.minelittlepony.jumpingcastle.api.JumpingCastle; +import com.minelittlepony.unicopia.client.particle.EntityMagicFX; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.command.Commands; +import com.minelittlepony.unicopia.input.Keyboard; +import com.minelittlepony.unicopia.network.MsgPlayerAbility; +import com.minelittlepony.unicopia.network.MsgPlayerCapabilities; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import com.minelittlepony.unicopia.power.PowersRegistry; + +import come.minelittlepony.unicopia.forgebullshit.FBS; + +@Mod(modid = Unicopia.MODID, name = Unicopia.NAME, version = Unicopia.VERSION) +@EventBusSubscriber +public class Unicopia { + public static final String MODID = "unicopia"; + public static final String NAME = "@NAME@"; + public static final String VERSION = "@VERSION@"; + + public static IChannel channel; + + public static int MAGIC_PARTICLE; + + @EventHandler + public void preInit(FMLPreInitializationEvent event) { + + } + + @EventHandler + public void init(FMLInitializationEvent event) { + channel = JumpingCastle.listen(MODID) + .consume(MsgPlayerCapabilities.class, (msg, channel) -> { + PlayerSpeciesList.instance().handleSpeciesChange(msg.senderId, msg.newRace); + }) + .consume(MsgPlayerAbility.class, (msg, channel) -> { + msg.applyServerAbility(); + }); + + MAGIC_PARTICLE = Particles.instance().registerParticle(new EntityMagicFX.Factory()); + + PowersRegistry.instance().init(); + + FBS.init(); + } + + @SubscribeEvent + public static void registerItemsStatic(RegistryEvent.Register event) { + // Why won't you run!? + UItems.registerItems(); + } + + @SubscribeEvent + public static void onPlayerJoin(PlayerLoggedInEvent event) { + PlayerSpeciesList.instance().sendCapabilities(event.player.getGameProfile().getId()); + } + + @SideOnly(Side.CLIENT) + @SubscribeEvent + public static void onGameTick(TickEvent.ClientTickEvent event) { + if (event.phase == Phase.END) { + Keyboard.getKeyHandler().onKeyInput(); + } + } + + @SubscribeEvent + public static void onPlyerTick(TickEvent.PlayerTickEvent event) { + if (event.phase == Phase.END) { + PlayerSpeciesList.instance().getPlayer(event.player).onEntityUpdate(); + } + } + + @EventHandler + public void onServerStarted(FMLServerStartingEvent event) { + Commands.init(event); + } + + @EventHandler + public void onPlayerRightClick(PlayerInteractEvent.RightClickItem event) { + // Why won't you run!? + if (!event.isCanceled() + && event.getItemStack().getItemUseAction() == EnumAction.EAT) { + PlayerSpeciesList.instance().getPlayer(event.getEntityPlayer()).onEntityEat(); + } + } + + @SubscribeEvent + public static void attachCapabilities(AttachCapabilitiesEvent event) { + // Why won't you run!? + FBS.attach(event); + } + + @SubscribeEvent + public static void clonePlayer(PlayerEvent.Clone event) { + FBS.clone(event); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/EntityMagicFX.java b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityMagicFX.java new file mode 100644 index 00000000..574ed55b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityMagicFX.java @@ -0,0 +1,80 @@ +package com.minelittlepony.unicopia.client.particle; + +import net.minecraft.client.particle.Particle; + +import net.minecraft.client.particle.IParticleFactory; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.entity.Entity; +import net.minecraft.world.World; + +public class EntityMagicFX extends Particle { + private float portalParticleScale; + private double portalPosX; + private double portalPosY; + private double portalPosZ; + + public EntityMagicFX(World w, double x, double y, double z, double vX, double vY, double vZ) { + super(w, x, y, z, vX, vY, vZ); + motionX = vX; + motionY = vY; + motionZ = vZ; + portalPosX = posX = x; + portalPosY = posY = y; + portalPosZ = posZ = z; + portalParticleScale = particleScale = rand.nextFloat() * 0.2F + 0.5F; + particleMaxAge = (int)(Math.random() * 10) + 40; + + setParticleTextureIndex((int)(Math.random() * 8)); + + particleRed = particleGreen = particleBlue = 1; + particleGreen *= 0.3F; + + if (rand.nextBoolean()) particleBlue *= 0.4F; + if (rand.nextBoolean()) particleRed *= 0.9F; + if (rand.nextBoolean()) particleGreen += 0.5F; + + if (rand.nextBoolean()) { + particleGreen *= 2F; + } else if (rand.nextBoolean()) { + particleRed *= 3.9F; + } + } + + public void renderParticle(BufferBuilder renderer, Entity e, float p_70539_2_, float p_70539_3_, float p_70539_4_, float p_70539_5_, float p_70539_6_, float p_70539_7_) { + float f6 = 1 - ((particleAge + p_70539_2_) / particleMaxAge); + f6 = 1 - f6 * f6; + particleScale = portalParticleScale * f6; + super.renderParticle(renderer, e, p_70539_2_, p_70539_3_, p_70539_4_, p_70539_5_, p_70539_6_, p_70539_7_); + } + + public int getBrightnessForRender(float p_70070_1_) { + int i = super.getBrightnessForRender(p_70070_1_); + float f1 = (float)particleAge / (float)particleMaxAge; + f1 *= f1; + f1 *= f1; + int j = i & 255; + int k = i >> 16 & 255; + k += f1 * 15 * 16; + if (k > 240) k = 240; + return j | k << 16; + } + + public void onUpdate() { + prevPosX = posX; + prevPosY = posY; + prevPosZ = posZ; + float var1 = (float)particleAge / (float)particleMaxAge; + var1 = 1 + var1 - var1 * var1 * 2; + posX = portalPosX + motionX * var1; + posY = portalPosY + motionY; + posZ = portalPosZ + motionZ * var1; + if (particleAge++ >= particleMaxAge) setExpired(); + } + + public static class Factory implements IParticleFactory { + @Override + public Particle createParticle(int id, World w, double x, double y, double z, double vX, double vY, double vZ, int... args) { + return new EntityMagicFX(w, x, y, z, vX, vY, vZ); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java b/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java new file mode 100644 index 00000000..6a044d67 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java @@ -0,0 +1,84 @@ +package com.minelittlepony.unicopia.client.particle; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.IParticleFactory; +import net.minecraft.client.particle.Particle; +import net.minecraft.entity.Entity; + +public class Particles { + + private static final Particles instance = new Particles(); + + public static Particles instance() { + return instance; + } + + private final Minecraft mc = Minecraft.getMinecraft(); + + private final List registeredParticles = new ArrayList<>(); + + public int registerParticle(IParticleFactory factory) { + int id = registeredParticles.size(); + registeredParticles.add(factory); + return -id; + } + + public Particle spawnParticle(int particleId, boolean ignoreDistance, double posX, double posY, double posZ, double speedX, double speedY, double speedZ, int ...pars) { + Entity entity = mc.getRenderViewEntity(); + + if (entity == null && mc.effectRenderer == null) { + return null; + } + + double dX = entity.posX - posX; + double dY = entity.posY - posY; + double dZ = entity.posZ - posZ; + double distance = dX * dX + dY * dY + dZ * dZ; + + if (ignoreDistance || (distance <= 1024 && calculateParticleLevel(false) < 2)) { + return spawnEffectParticle(particleId, posX, posY, posZ, speedX, speedY, speedZ, pars); + } + + return null; + } + + private Particle spawnEffectParticle(int particleId, double posX, double posY, double posZ, double speedX, double speedY, double speedZ, int ...pars) { + if (particleId >= 0) { + // Not ours, delegate to mojang + return mc.effectRenderer.spawnEffectParticle(particleId, posX, posY, posZ, speedX, speedY, speedZ, pars); + } + + + IParticleFactory iparticlefactory = registeredParticles.get(-particleId); + + if (iparticlefactory == null) { + return null; + } + + Particle particle = iparticlefactory.createParticle(particleId, mc.world, posX, posY, posZ, speedX, speedY, speedZ, pars); + + if (particle == null) { + return null; + } + + mc.effectRenderer.addEffect(particle); + return particle; + } + + private int calculateParticleLevel(boolean minimiseLevel) { + int level = mc.gameSettings.particleSetting; + + if (minimiseLevel && level == 2 && mc.world.rand.nextInt(10) == 0) { + level = 1; + } + + if (level == 1 && mc.world.rand.nextInt(3) == 0) { + level = 2; + } + + return level; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/command/CommandOverrideGameMode.java b/src/main/java/com/minelittlepony/unicopia/command/CommandOverrideGameMode.java new file mode 100644 index 00000000..e4c08765 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/CommandOverrideGameMode.java @@ -0,0 +1,48 @@ +package com.minelittlepony.unicopia.command; + +import com.minelittlepony.unicopia.player.IPlayer; + +import net.minecraft.command.CommandException; +import net.minecraft.command.CommandGameMode; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.GameType; + +class CommandOverrideGameMode extends CommandGameMode { + public void execute(MinecraftServer server, ICommandSender sender, String[] params) throws CommandException { + if (params.length <= 0) { + throw new WrongUsageException("commands.gamemode.usage"); + } + + GameType gametype = getGameModeFromCommand(sender, params[0]); + + EntityPlayerMP entityplayermp = params.length >= 2 ? getPlayer(server, sender, params[1]) : getCommandSenderAsPlayer(sender); + + updateGameMode(entityplayermp, gametype); + + ITextComponent chatcomponenttranslation = new TextComponentTranslation("gameMode." + gametype.getName(), new Object[0]); + + if (entityplayermp != sender) { + notifyCommandListener(sender, this, 1, "commands.gamemode.success.other", entityplayermp.getName(), chatcomponenttranslation); + } else { + notifyCommandListener(sender, this, 1, "commands.gamemode.success.self", chatcomponenttranslation); + } + } + + private void updateGameMode(EntityPlayerMP player, GameType m) { + boolean flying = player.capabilities.isFlying; + + player.setGameType(m); + player.capabilities.isFlying = ((IPlayer)player).getPlayerSpecies().canFly(); + + if (flying != player.capabilities.isFlying) { + player.sendPlayerAbilities(); + } + + player.fallDistance = 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java b/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java new file mode 100644 index 00000000..7507bcc5 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java @@ -0,0 +1,148 @@ +package com.minelittlepony.unicopia.command; + +import java.util.ArrayList; +import java.util.List; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; + +class CommandSpecies extends CommandBase { + + public String getName() { + return "race"; + } + + public int getRequiredPermissionLevel() { + return 0; + } + + public boolean checkPermission(MinecraftServer server, ICommandSender sender) { + return sender.canUseCommand(getRequiredPermissionLevel(), "help"); + } + + private String getRacesString() { + String values = ""; + for (Race i : Race.values()) { + if (PlayerSpeciesList.instance().speciesPermitted(i)) { + if (values != "") values += ", "; + values += i.toString(); + } + } + return values; + } + + public String getUsage(ICommandSender sender) { + return "commands.race.usage"; + } + + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (args.length < 1) { + throw new WrongUsageException(getUsage(sender)); + } + + EntityPlayerMP player; + int playerIndex = 1; + + if (args[0].contentEquals("set")) playerIndex++; + + if (args.length > playerIndex) { + player = getPlayer(server, sender, args[playerIndex]); + } else { + player = getCommandSenderAsPlayer(sender); + } + + if (args[0].contentEquals("set")) { + if (args.length >= 2) { + Race species = Race.fromName(args[1]); + + if (species == null) { + player.sendMessage(new TextComponentTranslation("commands.race.fail", args[1].toUpperCase())); + } else { + if (PlayerSpeciesList.instance().speciesPermitted(species)) { + IPlayer iplayer = PlayerSpeciesList.instance().getPlayer(player); + + iplayer.setPlayerSpecies(species); + iplayer.sendCapabilities(); + + TextComponentTranslation formattedName = new TextComponentTranslation(species.name().toLowerCase()); + + if (player != sender) { + notifyCommandListener(sender, this, 1, "commands.race.success.other", player.getName(), formattedName); + } else { + player.sendMessage(new TextComponentTranslation("commands.race.success.self")); + notifyCommandListener(sender, this, 1, "commands.race.success.otherself", player.getName(), formattedName); + } + } else { + player.sendMessage(new TextComponentTranslation("commands.race.permission")); + } + } + } + } else if (args[0].contentEquals("get")) { + Race spec = PlayerSpeciesList.instance().getPlayer(player).getPlayerSpecies(); + + String name = "commands.race.tell."; + name += player == sender ? "self" : "other"; + + ITextComponent race = new TextComponentString(spec.getTranslationString()); + + TextComponentTranslation message = new TextComponentTranslation(name); + + race.getStyle().setColor(TextFormatting.GOLD); + + message.appendSibling(race); + + player.sendMessage(message); + } else if (args[0].contentEquals("list")) { + player.sendMessage(new TextComponentTranslation("commands.race.list")); + + ITextComponent message = new TextComponentString(" " + getRacesString()); + + message.getStyle().setColor(TextFormatting.GOLD); + + player.sendMessage(message); + } + } + + /** + * Adds the strings available in this command to the given list of tab completion options. + */ + public List getTabCompletions(MinecraftServer server, ICommandSender par1ICommandSender, String[] args, BlockPos pos) { + if (args.length == 1) { + return getListOfStringsMatchingLastWord(args, new String[] { "get", "set", "list" }); + } else if (args.length == 2 && args[0].contentEquals("set")) { + ArrayList names = new ArrayList(); + for (Race i : Race.values()) { + if (PlayerSpeciesList.instance().speciesPermitted(i)) { + names.add(i.toString()); + } + } + return getListOfStringsMatchingLastWord(args, names.toArray(new String[names.size()])); + } else if ((args.length == 3 && args[0].contentEquals("set")) || (args[0].contentEquals("get") && args.length == 2)) { + return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()); + } + + return null; + } + + public boolean isUsernameIndex(String[] args, int index) { + if (args[0].contentEquals("get")) { + return index == 1; + } else if (args[0].contentEquals("set")) { + return index == 2; + } + return false; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/command/Commands.java b/src/main/java/com/minelittlepony/unicopia/command/Commands.java new file mode 100644 index 00000000..c4a2de2d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/command/Commands.java @@ -0,0 +1,11 @@ +package com.minelittlepony.unicopia.command; + +import net.minecraftforge.fml.common.event.FMLServerStartingEvent; + +public class Commands { + + public static void init(FMLServerStartingEvent event) { + event.registerServerCommand(new CommandOverrideGameMode()); + event.registerServerCommand(new CommandSpecies()); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/input/IKeyBind.java b/src/main/java/com/minelittlepony/unicopia/input/IKeyBind.java new file mode 100644 index 00000000..c28cb852 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/input/IKeyBind.java @@ -0,0 +1,11 @@ +package com.minelittlepony.unicopia.input; + +public interface IKeyBind { + + String getKeyCategory(); + + String getKeyName(); + + int getKeyCode(); + +} diff --git a/src/main/java/com/minelittlepony/unicopia/input/IKeyHandler.java b/src/main/java/com/minelittlepony/unicopia/input/IKeyHandler.java new file mode 100644 index 00000000..d83d4fbb --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/input/IKeyHandler.java @@ -0,0 +1,10 @@ +package com.minelittlepony.unicopia.input; + +public interface IKeyHandler { + + void addKeybind(IKeyBind bind); + + default void onKeyInput() { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/input/Keyboard.java b/src/main/java/com/minelittlepony/unicopia/input/Keyboard.java new file mode 100644 index 00000000..5925baa2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/input/Keyboard.java @@ -0,0 +1,20 @@ +package com.minelittlepony.unicopia.input; + +import com.minelittlepony.unicopia.UClient; + +public final class Keyboard { + private static IKeyHandler keyHandler; + + public static IKeyHandler getKeyHandler() { + + if (keyHandler == null) { + if (UClient.isClientSide()) { + keyHandler = bind -> {}; + } + + keyHandler = new UKeyHandler(); + } + + return keyHandler; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/input/UKeyHandler.java b/src/main/java/com/minelittlepony/unicopia/input/UKeyHandler.java new file mode 100644 index 00000000..cbb13004 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/input/UKeyHandler.java @@ -0,0 +1,68 @@ +package com.minelittlepony.unicopia.input; + +import java.util.ArrayList; + +import org.lwjgl.input.Keyboard; + +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import com.minelittlepony.unicopia.power.PowersRegistry; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.settings.KeyBinding; +import net.minecraftforge.fml.client.registry.ClientRegistry; + +public class UKeyHandler implements IKeyHandler { + private static ArrayList bindings = new ArrayList(); + private static ArrayList removed = new ArrayList(); + + private static ArrayList pressed = new ArrayList(); + + @Override + public void addKeybind(IKeyBind p) { + KeyBinding b = new KeyBinding(p.getKeyName(), p.getKeyCode(), p.getKeyCategory()); + + ClientRegistry.registerKeyBinding(b); + + bindings.add(b); + } + + @Override + public void onKeyInput() { + if (Minecraft.getMinecraft().currentScreen != null + || Minecraft.getMinecraft().player == null) { + return; + } + IPlayer iplayer = PlayerSpeciesList.instance().getPlayer(Minecraft.getMinecraft().player); + + for (KeyBinding i : bindings) { + if (Keyboard.isKeyDown(i.getKeyCode())) { + + if (!pressed.contains(i)) { + pressed.add(i); + } + + if (!PowersRegistry.instance().hasRegisteredPower(i.getKeyCodeDefault())) { + removed.add(i); + System.out.println("Error: Keybinding(" + i.getKeyDescription() + ") does not have a registered pony power. Keybinding will be removed from event."); + } else { + PowersRegistry.instance() + .getCapablePowerFromKey(i.getKeyCodeDefault(), iplayer.getPlayerSpecies()) + .ifPresent(iplayer.getAbilities()::tryUseAbility); + } + } else { + if (pressed.contains(i)) { + pressed.remove(i); + + iplayer.getAbilities().tryClearAbility(); + } + } + } + + for (KeyBinding i : removed) { + removed.remove(i); + bindings.remove(i); + pressed.remove(i); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/ItemApple.java b/src/main/java/com/minelittlepony/unicopia/item/ItemApple.java new file mode 100644 index 00000000..798f2a49 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/ItemApple.java @@ -0,0 +1,156 @@ +package com.minelittlepony.unicopia.item; + +import java.util.Random; + +import com.minelittlepony.unicopia.UItems; +import com.minelittlepony.util.MagicalDamageSource; +import com.minelittlepony.util.vector.VecHelper; + +import net.minecraft.block.BlockPlanks; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.ItemFood; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class ItemApple extends ItemFood { + + private int[] typeRarities = new int[0]; + private String[] subTypes = new String[0]; + private String[] variants = subTypes; + + public int getRandomAppleMetadata(Random rand, Object variant) { + int[] rarity = typeRarities; + int result = 0; + for (int i = 0; i < rarity.length && i < subTypes.length; i++) { + if (rand.nextInt(rarity[i]) == 0) { + result++; + } + } + if (variant == BlockPlanks.EnumType.JUNGLE) { + result = result == 0 ? 1 : result == 1 ? 0 : result; + } + if (variant == BlockPlanks.EnumType.SPRUCE) { + result = result == 0 ? 3 : result == 3 ? 0 : result; + } + if (variant == BlockPlanks.EnumType.BIRCH) { + result = result == 0 ? 2 : result == 2 ? 0 : result; + } + if (variant == BlockPlanks.EnumType.DARK_OAK) { + result = result == 1 ? getZapAppleMetadata() : result == getZapAppleMetadata() ? 1 : result; + } + return result; + } + + public ItemApple() { + super(4, 3, false); + setHasSubtypes(true); + setMaxDamage(0); + setTranslationKey("apple"); + } + + @Override + public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { + RayTraceResult mop = VecHelper.getObjectMouseOver(player, 5, 0); + if (mop != null && mop.typeOfHit == RayTraceResult.Type.ENTITY) { + ItemStack stack = player.getHeldItem(hand); + if (canFeedTo(stack, mop.entityHit)) { + return onFedTo(stack, player, mop.entityHit); + } + } + return super.onItemRightClick(world, player, hand); + } + + @Override + protected void onFoodEaten(ItemStack stack, World w, EntityPlayer player) { + super.onFoodEaten(stack, w, player); + if (isZapApple(stack)) { + player.attackEntityFrom(MagicalDamageSource.create("zap"), 120); + w.addWeatherEffect(new EntityLightningBolt(w, player.posX, player.posY, player.posZ, false)); + } + } + + public boolean canFeedTo(ItemStack stack, Entity e) { + return isZapApple(stack) && (e instanceof EntityVillager || e instanceof EntityCreeper || e instanceof EntityPig); + } + + public boolean isZapApple(ItemStack stack) { + int meta = stack.getMetadata(); + return meta == getZapAppleMetadata() || meta >= subTypes.length; + } + + public ActionResult onFedTo(ItemStack stack, EntityPlayer player, Entity e) { + e.onStruckByLightning(new EntityLightningBolt(e.world, e.posX, e.posY, e.posZ, false)); + if (!player.capabilities.isCreativeMode) stack.shrink(1); + return new ActionResult(EnumActionResult.SUCCESS, stack); + } + + public int getZapAppleMetadata() { + return 4; + } + + public ItemApple setSubTypes(String... types) { + subTypes = types; + variants = new String[subTypes.length * 2]; + setTranslationKey(variants[0] = types[0]); + for (int i = 1; i < variants.length; i++) { + variants[i] = variants[0] + (i % subTypes.length != 0 ? "_" + subTypes[i % subTypes.length] : ""); + } + return this; + } + + public ItemApple setTypeRarities(int ... rarity) { + typeRarities = rarity; + return this; + } + + public String[] getVariants() { + return variants; + } + + @Override + public void getSubItems(CreativeTabs tab, NonNullList items) { + for (int i = 0; i < subTypes.length; i++) { + items.add(new ItemStack(UItems.apple, 1, i)); + } + } + + @Override + public EnumRarity getRarity(ItemStack stack) { + int meta = stack.getMetadata(); + if (meta == getZapAppleMetadata()) return EnumRarity.EPIC; + if (meta >= subTypes.length) return EnumRarity.RARE; + return EnumRarity.COMMON; + } + + /*public String getItemStackDisplayName(ItemStack stack) { + String result = super.getItemStackDisplayName(stack); + if (stack.getMetadata() >= subTypes.length) { + return ChatColor.ITALIC + result; + } + return result; + }*/ + + @Override + public String getTranslationKey(ItemStack stack) { + int meta = stack.getMetadata() % subTypes.length; + if (meta < 0) meta = 0; + return super.getTranslationKey(stack) + (meta > 0 ? "." + subTypes[meta] : ""); + } + + @Override + public int getItemBurnTime(ItemStack stack) { + return stack.getMetadata() == 2 ? 150 : 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAbility.java b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAbility.java new file mode 100644 index 00000000..69aab133 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAbility.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.network; + +import java.util.UUID; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; +import com.minelittlepony.jumpingcastle.api.IMessage; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.power.IData; +import com.minelittlepony.unicopia.power.IPower; +import com.minelittlepony.unicopia.power.PowersRegistry; + +import net.minecraft.entity.player.EntityPlayer; + +@IMessage.Id(2) +public class MsgPlayerAbility implements IMessage { + + private static final Gson gson = new GsonBuilder() + .excludeFieldsWithoutExposeAnnotation() + .create(); + + @Expose + private UUID senderId; + + @Expose + private String powerIdentifier; + + @Expose + private String abilityJson; + + public MsgPlayerAbility(IPower power, IData data) { + powerIdentifier = power.getKeyName(); + abilityJson = gson.toJson(data, power.getPackageType()); + } + + public void applyServerAbility() { + PowersRegistry.instance().getPowerFromName(powerIdentifier).ifPresent(this::apply); + } + + private void apply(IPower power) { + EntityPlayer player = IPlayer.getPlayerEntity(senderId); + if (player == null) { + return; + } + + T data = gson.fromJson(abilityJson, power.getPackageType()); + + power.apply(player, data); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java new file mode 100644 index 00000000..f92fe9b1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java @@ -0,0 +1,28 @@ +package com.minelittlepony.unicopia.network; + +import java.util.UUID; + +import com.google.gson.annotations.Expose; +import com.minelittlepony.jumpingcastle.api.IMessage; +import com.minelittlepony.unicopia.Race; + +import net.minecraft.entity.player.EntityPlayer; + +@IMessage.Id(1) +public class MsgPlayerCapabilities implements IMessage { + @Expose + public Race newRace; + + @Expose + public UUID senderId; + + public MsgPlayerCapabilities(Race race, EntityPlayer player) { + newRace = race; + senderId = player.getGameProfile().getId(); + } + + public MsgPlayerCapabilities(Race race, UUID playerId) { + newRace = race; + senderId = playerId; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/DefaultPlayerSpecies.java b/src/main/java/com/minelittlepony/unicopia/player/DefaultPlayerSpecies.java new file mode 100644 index 00000000..afad6166 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/DefaultPlayerSpecies.java @@ -0,0 +1,74 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.power.IPower; +import com.minelittlepony.unicopia.spell.IMagicEffect; + +import net.minecraft.entity.player.EntityPlayer; + +final class DefaultPlayerSpecies implements IPlayer, IAbilityReceiver { + + public static final IPlayer INSTANCE = new DefaultPlayerSpecies(); + + private DefaultPlayerSpecies() { + } + + @Override + public Race getPlayerSpecies() { + return Race.EARTH; + } + + @Override + public void setPlayerSpecies(Race race) { + } + + @Override + public void sendCapabilities() { + + } + + @Override + public void tryUseAbility(IPower power) { + + } + + @Override + public void tryClearAbility() { + + } + + @Override + public int getRemainingCooldown() { + return 0; + } + + @Override + public IAbilityReceiver getAbilities() { + return this; + } + + @Override + public boolean isClientPlayer() { + return false; + } + + @Override + public void onEntityUpdate() { + + } + + @Override + public void setEffect(IMagicEffect effect) { + + } + + @Override + public IMagicEffect getEffect() { + return null; + } + + @Override + public EntityPlayer getOwner() { + return null; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IAbilityReceiver.java b/src/main/java/com/minelittlepony/unicopia/player/IAbilityReceiver.java new file mode 100644 index 00000000..0f293e12 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/IAbilityReceiver.java @@ -0,0 +1,12 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.unicopia.power.IPower; + +public interface IAbilityReceiver { + + void tryUseAbility(IPower power); + + void tryClearAbility(); + + int getRemainingCooldown(); +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java b/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java new file mode 100644 index 00000000..b709fdca --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java @@ -0,0 +1,32 @@ +package com.minelittlepony.unicopia.player; + +import java.util.UUID; + +import com.minelittlepony.unicopia.InbtSerialisable; +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.spell.ICaster; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.fml.common.FMLCommonHandler; + +public interface IPlayer extends ICaster, InbtSerialisable { + Race getPlayerSpecies(); + + void setPlayerSpecies(Race race); + + void sendCapabilities(); + + IAbilityReceiver getAbilities(); + + boolean isClientPlayer(); + + void onEntityUpdate(); + + default void onEntityEat() { + + } + + static EntityPlayer getPlayerEntity(UUID playerId) { + return FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayerByUUID(playerId); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java b/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java new file mode 100644 index 00000000..460cb2af --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java @@ -0,0 +1,7 @@ +package com.minelittlepony.unicopia.player; + +import net.minecraft.entity.player.EntityPlayer; + +public interface IUpdatable { + void onUpdate(EntityPlayer entity); +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java new file mode 100644 index 00000000..4e794e21 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java @@ -0,0 +1,119 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.jumpingcastle.api.Target; +import com.minelittlepony.unicopia.InbtSerialisable; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.network.MsgPlayerAbility; +import com.minelittlepony.unicopia.power.IData; +import com.minelittlepony.unicopia.power.IPower; +import com.minelittlepony.unicopia.power.PowersRegistry; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; + +class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable, InbtSerialisable { + + private final IPlayer player; + + private boolean abilityTriggered = false; + + private int warmup = 0; + + private int cooldown = 0; + + private IPower activeAbility = null; + + public PlayerAbilityDelegate(IPlayer player) { + this.player = player; + } + + boolean canSwitchStates() { + return abilityTriggered && cooldown <= 0; + } + + @Override + public void tryUseAbility(IPower power) { + if (canSwitchStates() || activeAbility != power) { + abilityTriggered = false; + activeAbility = power; + warmup = 0; + cooldown = power.getCooldownTime(player); + } + } + + @Override + public void tryClearAbility() { + if (canSwitchStates() || activeAbility != null) { + abilityTriggered = false; + activeAbility = null; + warmup = 0; + cooldown = 0; + } + } + + @Override + public int getRemainingCooldown() { + return cooldown; + } + + @Override + public void onUpdate(EntityPlayer entity) { + if (activeAbility != null && activeAbility.canUse(player.getPlayerSpecies())) { + if (!abilityTriggered) { + if (warmup < activeAbility.getWarmupTime(player)) { + activeAbility.preApply(entity); + warmup++; + } else if (player.isClientPlayer()) { + if (activeAbility.canActivate(entity.getEntityWorld(), player)) { + abilityTriggered = activateAbility(entity); + if (!abilityTriggered) { + activeAbility = null; + cooldown = 0; + } + } else { + activeAbility = null; + cooldown = 0; + } + } + } else if (cooldown > 0) { + activeAbility.postApply(entity); + cooldown--; + } + } + } + + @Override + public void writeToNBT(NBTTagCompound compound) { + compound.setBoolean("triggered", abilityTriggered); + compound.setInteger("warmup", warmup); + compound.setInteger("cooldown", cooldown); + + if (activeAbility != null) { + compound.setString("activeAbility", activeAbility.getKeyName()); + } + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + activeAbility = null; + abilityTriggered = compound.getBoolean("triggered"); + warmup = compound.getInteger("warmup"); + cooldown = compound.getInteger("cooldown"); + + if (compound.hasKey("activeAbility")) { + PowersRegistry.instance().getPowerFromName(compound.getString("activeAbility")).ifPresent(p -> { + activeAbility = p; + }); + } + } + + protected boolean activateAbility(EntityPlayer entity) { + IData data = activeAbility.tryActivate(entity, entity.getEntityWorld()); + + if (data != null) { + Unicopia.channel.send(new MsgPlayerAbility(activeAbility, data), Target.SERVER); + } + + return data != null; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java new file mode 100644 index 00000000..86d18f7e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java @@ -0,0 +1,144 @@ +package com.minelittlepony.unicopia.player; + +import java.util.UUID; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UClient; +import com.minelittlepony.unicopia.spell.ICaster; +import com.minelittlepony.unicopia.spell.IMagicEffect; +import com.minelittlepony.unicopia.spell.SpellRegistry; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.PotionEffect; + +class PlayerCapabilities implements IPlayer, ICaster { + + private Race playerSpecies = Race.HUMAN; + + private UUID playerId; + + private final PlayerAbilityDelegate powers = new PlayerAbilityDelegate(this); + + private final PlayerGravityDelegate gravity = new PlayerGravityDelegate(this); + + private IMagicEffect effect; + + PlayerCapabilities(UUID playerId) { + this.playerId = playerId; + } + + @Override + public Race getPlayerSpecies() { + return playerSpecies; + } + + @Override + public void setPlayerSpecies(Race race) { + if (race == playerSpecies) { + return; + } + + playerSpecies = race; + + EntityPlayer self = getOwner(); + + self.capabilities.allowFlying = race.canFly(); + gravity.updateFlightStat(self, self.capabilities.isFlying); + + self.sendPlayerAbilities(); + } + + @Override + public void sendCapabilities() { + PlayerSpeciesList.instance().sendCapabilities(playerId); + } + + @Override + public IAbilityReceiver getAbilities() { + return powers; + } + + @Override + public boolean isClientPlayer() { + return UClient.isClientSide() && + Minecraft.getMinecraft().player.getGameProfile().getId().equals(playerId); + } + + @Override + public void onEntityUpdate() { + EntityPlayer player = getOwner(); + + powers.onUpdate(player); + gravity.onUpdate(player); + + if (!getPlayerSpecies().canCast()) { + effect = null; + } + + if (effect != null) { + if (player.getEntityWorld().isRemote && player.getEntityWorld().getWorldTime() % 10 == 0) { + effect.render(player); + } + + if (!effect.update(player)) { + effect = null; + } + } + } + + public void onEntityEat() { + if (getPlayerSpecies() == Race.CHANGELING) { + EntityPlayer player = getOwner(); + + player.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 2000, 2)); + player.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 2000, 2)); + } + } + + @Override + public void writeToNBT(NBTTagCompound compound) { + compound.setUniqueId("playerId", playerId); + compound.setString("playerSpecies", playerSpecies.name()); + compound.setTag("powers", powers.toNBT()); + compound.setTag("gravity", gravity.toNBT()); + + if (effect != null) { + compound.setString("effect_id", effect.getName()); + compound.setTag("effect", effect.toNBT()); + } + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + playerId = compound.getUniqueId("playerId"); + playerSpecies = Race.fromName(compound.getString("playerSpecies")); + powers.readFromNBT(compound.getCompoundTag("powers")); + gravity.readFromNBT(compound.getCompoundTag("gravity")); + + if (compound.hasKey("effect_id") && compound.hasKey("effect")) { + effect = null; + SpellRegistry.instance().getSpellFromName(compound.getString("effect_id")).ifPresent(f -> { + effect = f; + effect.readFromNBT(compound.getCompoundTag("effect")); + }); + } + } + + @Override + public void setEffect(IMagicEffect effect) { + this.effect = effect; + } + + @Override + public IMagicEffect getEffect() { + return effect; + } + + @Override + public EntityPlayer getOwner() { + return IPlayer.getPlayerEntity(playerId); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java new file mode 100644 index 00000000..c80d5c25 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java @@ -0,0 +1,68 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.unicopia.InbtSerialisable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; + +class PlayerGravityDelegate implements IUpdatable, InbtSerialisable { + + private final IPlayer player; + + private int ticksSinceLanding = 0; + + public boolean isFlying = false; + + public PlayerGravityDelegate(IPlayer player) { + this.player = player; + } + + @Override + public void onUpdate(EntityPlayer entity) { + if (!entity.capabilities.isCreativeMode) { + if (player.getPlayerSpecies().canFly()) { + if (ticksSinceLanding < 2) { + ticksSinceLanding++; + } + + entity.capabilities.allowFlying = entity.capabilities.isFlying = false; + } + } + + if (entity.capabilities.isFlying) { + entity.fallDistance = 0; + } + } + + 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) { + ticksSinceLanding = 0; + } + + } else { + entity.capabilities.isFlying = false; + isFlying = false; + } + } + } + + @Override + public void writeToNBT(NBTTagCompound compound) { + compound.setInteger("ticksOnGround", ticksSinceLanding); + compound.setBoolean("isFlying", isFlying); + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + ticksSinceLanding = compound.getInteger("ticksOnGround"); + isFlying = compound.getBoolean("isFlying"); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java new file mode 100644 index 00000000..396e1397 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java @@ -0,0 +1,62 @@ +package com.minelittlepony.unicopia.player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.minelittlepony.jumpingcastle.api.Target; +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.network.MsgPlayerCapabilities; + +import come.minelittlepony.unicopia.forgebullshit.FBS; +import come.minelittlepony.unicopia.forgebullshit.IPlayerCapabilitiesProxyContainer; +import net.minecraft.entity.player.EntityPlayer; + +public class PlayerSpeciesList { + + private static final PlayerSpeciesList instance = new PlayerSpeciesList(); + + public static PlayerSpeciesList instance() { + return instance; + } + + private List serverPermittedRaces = new ArrayList<>(); + + public boolean speciesPermitted(Race race) { + return race.isDefault() || serverPermittedRaces.isEmpty() || serverPermittedRaces.contains(race); + } + + public void sendCapabilities(UUID playerId) { + Unicopia.channel.send(new MsgPlayerCapabilities(getPlayer(playerId).getPlayerSpecies(), playerId), Target.SERVER_AND_CLIENTS); + } + + public void handleSpeciesChange(UUID playerId, Race race) { + getPlayer(playerId).setPlayerSpecies(race); + } + + public IPlayer emptyPlayer(UUID playerId) { + return new PlayerCapabilities(playerId); + } + + public IPlayer getPlayer(EntityPlayer player) { + if (player == null) { + return DefaultPlayerSpecies.INSTANCE; + } + + IPlayerCapabilitiesProxyContainer container = FBS.of(player); + + IPlayer ply = container.getPlayer(); + if (ply == null) { + ply = emptyPlayer(player.getGameProfile().getId()); + + container.setPlayer(ply); + } + + return ply; + } + + public IPlayer getPlayer(UUID playerId) { + return getPlayer(IPlayer.getPlayerEntity(playerId)); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/IData.java b/src/main/java/com/minelittlepony/unicopia/power/IData.java new file mode 100644 index 00000000..be5f6efc --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/IData.java @@ -0,0 +1,5 @@ +package com.minelittlepony.unicopia.power; + +public interface IData { + +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/IPower.java b/src/main/java/com/minelittlepony/unicopia/power/IPower.java new file mode 100644 index 00000000..2f6e3016 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/IPower.java @@ -0,0 +1,129 @@ +package com.minelittlepony.unicopia.power; + +import java.util.Random; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.input.IKeyBind; +import com.minelittlepony.unicopia.player.IPlayer; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public interface IPower extends IKeyBind { + + /** + * Subtracts a given food amount from the player. + * Harms the player if there is not enough enough hunger available. + */ + static boolean takeFromPlayer(EntityPlayer player, double foodSubtract) { + if (!player.capabilities.isCreativeMode) { + int food = (int)(player.getFoodStats().getFoodLevel() - foodSubtract); + if (food < 0) { + player.getFoodStats().addStats(-player.getFoodStats().getFoodLevel(), 0); + player.attackEntityFrom(DamageSource.MAGIC, -food); + } else { + player.getFoodStats().addStats((int)-foodSubtract, 0); + } + } + + return player.getHealth() > 0; + } + + static double getPlayerEyeYPos(EntityPlayer player) { + if (player.getEntityWorld().isRemote) { + return player.posY + player.getEyeHeight() - player.getYOffset(); + } + return player.posY + player.getEyeHeight() - 1; + } + + static void spawnParticles(int particleId, EntityPlayer player, int count) { + double halfDist = player.getEyeHeight() / 1.5; + double middle = player.getEntityBoundingBox().minY + halfDist; + + Random rand = player.getEntityWorld().rand; + for (int i = 0; i < count; i++) { + double x = (rand.nextFloat() * halfDist) - halfDist; + double y = (rand.nextFloat() * halfDist) - halfDist; + double z = (rand.nextFloat() * halfDist) - halfDist; + + Particles.instance().spawnParticle(particleId, false, + player.posX + x, + middle + y, + player.posZ + z, + 0, 0, 0); + } + } + + @Override + default String getKeyCategory() { + return "unicopia.category.name"; + } + + /** + * Returns the number of ticks the player must hold the ability key to trigger this ability. + */ + int getWarmupTime(IPlayer player); + + + /** + * Returns the number of ticks allowed for cooldown + */ + int getCooldownTime(IPlayer player); + + /** + * Called to check preconditions for activating the ability. + * + * @param w The world + * @param player The player + * @return True to allow activation + */ + default boolean canActivate(World w, IPlayer player) { + return true; + } + + /** + * Checks if the given race is permitted to use this ability + * @param playerSpecies The player's species + */ + boolean canUse(Race playerSpecies); + + /** + * Called on the client to activate the ability. + * + * @param player The player activating the ability + * @param w The player's world + * @return Data to be sent, or null if activation failed + */ + T tryActivate(EntityPlayer player, World w); + + Class getPackageType(); + + /** + * Called to actually apply the ability. + * Only called on the server side. + * + * @param player The player that triggered the ability + * @param data Data previously sent from the client + */ + @SideOnly(Side.SERVER) + void apply(EntityPlayer player, T data); + + /** + * Called just before the ability is activated. + * @param player The current player + */ + @SideOnly(Side.CLIENT) + void preApply(EntityPlayer player); + + /** + * Called every tick until the cooldown timer runs out. + * @param player The current player + */ + @SideOnly(Side.CLIENT) + void postApply(EntityPlayer player); + +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java b/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java new file mode 100644 index 00000000..919efcb1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java @@ -0,0 +1,137 @@ +package com.minelittlepony.unicopia.power; + +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.input.Keyboard; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.power.data.Hit; +import com.minelittlepony.util.MagicalDamageSource; +import com.minelittlepony.util.vector.VecHelper; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.entity.passive.EntityCow; +import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.world.World; + +public class PowerFeed implements IPower { + + @Override + public String getKeyName() { + return "unicopia.power.feed"; + } + + @Override + public int getKeyCode() { + return Keyboard.KEY_N; + } + + @Override + public int getWarmupTime(IPlayer player) { + return 20; + } + + @Override + public int getCooldownTime(IPlayer player) { + return 50; + } + + @Override + public boolean canUse(Race playerSpecies) { + return playerSpecies == Race.CHANGELING; + } + + @Override + public Hit tryActivate(EntityPlayer player, World w) { + if (player.getHealth() < player.getMaxHealth() || player.canEat(false)) { + Entity i = VecHelper.getLookedAtEntity(player, 10); + if (i != null && canDrain(i)) { + return new Hit(); + } + } + + return null; + } + + private boolean canDrain(Entity e) { + return e instanceof EntityCow + || e instanceof EntityVillager + || e instanceof EntityPlayer + || EnumCreatureType.MONSTER.getCreatureClass().isAssignableFrom(e.getClass()); + } + + @Override + public Class getPackageType() { + return Hit.class; + } + + @Override + public void apply(EntityPlayer player, Hit data) { + List list = new ArrayList(); + for (Entity i : VecHelper.getWithinRange(player, 3)) { + if (canDrain(i)) list.add(i); + } + + Entity looked = VecHelper.getLookedAtEntity(player, 10); + if (looked != null && !list.contains(looked)) { + list.add(looked); + } + + float lostHealth = player.getMaxHealth() - player.getHealth(); + + if (lostHealth > 0 || player.canEat(false)) { + float totalDrained = (lostHealth < 2 ? lostHealth : 2); + float drained = totalDrained / list.size(); + + for (Entity i : list) { + DamageSource d = MagicalDamageSource.causePlayerDamage("feed", player); + + if (EnumCreatureType.CREATURE.getCreatureClass().isAssignableFrom(i.getClass()) + || player.world.rand.nextFloat() > 0.95f) { + i.attackEntityFrom(d, Integer.MAX_VALUE); + } else { + i.attackEntityFrom(d, drained); + } + } + + if (lostHealth > 0) { + player.getFoodStats().addStats(3, 0.125f); + player.heal(totalDrained); + } else { + player.getFoodStats().addStats(3, 0.25f); + } + + if (player.world.rand.nextFloat() > 0.9f) { + player.addPotionEffect(new PotionEffect(MobEffects.WITHER, 20, 1)); + } + + if (player.world.rand.nextFloat() > 0.4f) { + player.removePotionEffect(MobEffects.NAUSEA); + } + } + } + + @Override + public void preApply(EntityPlayer player) { } + + @Override + public void postApply(EntityPlayer player) { + for (int i = 0; i < 10; i++) { + Particles.instance().spawnParticle(EnumParticleTypes.HEART.getParticleID(), false, + player.posX + player.world.rand.nextFloat() * 2 - 1, + player.posY + player.world.rand.nextFloat() * 2 - 1, + player.posZ + player.world.rand.nextFloat() * 2 - 1, + 0, 0.25, 0); + } + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerGrow.java b/src/main/java/com/minelittlepony/unicopia/power/PowerGrow.java new file mode 100644 index 00000000..4085cbc1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerGrow.java @@ -0,0 +1,115 @@ +package com.minelittlepony.unicopia.power; + +import org.lwjgl.input.Keyboard; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.power.data.Location; +import com.minelittlepony.util.vector.VecHelper; + +import net.minecraft.block.BlockDoublePlant; +import net.minecraft.block.BlockGrass; +import net.minecraft.block.IGrowable; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.ItemDye; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class PowerGrow implements IPower { + + @Override + public String getKeyName() { + return "unicopia.power.grow"; + } + + @Override + public int getKeyCode() { + return Keyboard.KEY_N; + } + + @Override + public int getWarmupTime(IPlayer player) { + return 10; + } + + @Override + public int getCooldownTime(IPlayer player) { + return 50; + } + + @Override + public boolean canUse(Race playerSpecies) { + return playerSpecies == Race.EARTH; + } + + @Override + public Location tryActivate(EntityPlayer player, World w) { + RayTraceResult ray = VecHelper.getObjectMouseOver(player, 3, 1); + + if (ray != null && ray.typeOfHit == RayTraceResult.Type.BLOCK) { + return new Location(ray.getBlockPos()); + } + + return null; + } + + @Override + public Class getPackageType() { + return Location.class; + } + + @Override + public void apply(EntityPlayer player, Location data) { + int count = 0; + + for (BlockPos pos : BlockPos.getAllInBox( + new BlockPos(data.x - 2, data.y - 2, data.z - 2), + new BlockPos(data.x + 2, data.y + 2, data.z + 2))) { + count += applySingle(player.world, player.world.getBlockState(pos), pos); + } + + if (count > 0) { + IPower.takeFromPlayer(player, count * 5); + } + } + + protected int applySingle(World w, IBlockState state, BlockPos pos) { + if (state.getBlock() instanceof IGrowable + && !(state.getBlock() instanceof BlockGrass)) { + + IGrowable g = ((IGrowable)state.getBlock()); + + if (g.canGrow(w, pos, state, w.isRemote) && g.canUseBonemeal(w, w.rand, pos, state)) { + do { + if (ItemDye.applyBonemeal(new ItemStack(Items.DYE, 1), w, pos)) { + w.playEvent(2005, pos, 0); + + if (g instanceof BlockDoublePlant) { + w.playEvent(2005, pos.up(), 0); + } + } + + state = w.getBlockState(pos); + g = ((IGrowable)state.getBlock()); + } while (g.canGrow(w, pos, state, w.isRemote)); + + return 1; + } + } + return 0; + } + + @Override + public void preApply(EntityPlayer player) { + IPower.spawnParticles(com.minelittlepony.unicopia.Unicopia.MAGIC_PARTICLE, player, 1); + } + + @Override + public void postApply(EntityPlayer player) { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerMagic.java b/src/main/java/com/minelittlepony/unicopia/power/PowerMagic.java new file mode 100644 index 00000000..55fb6e10 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerMagic.java @@ -0,0 +1,81 @@ +package com.minelittlepony.unicopia.power; + +import org.lwjgl.input.Keyboard; + +import com.google.gson.annotations.Expose; +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import com.minelittlepony.unicopia.spell.SpellShield; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; + +public class PowerMagic implements IPower { + + @Override + public String getKeyName() { + return "unicopia.power.magic"; + } + + @Override + public int getKeyCode() { + return Keyboard.KEY_P; + } + + @Override + public int getWarmupTime(IPlayer player) { + if (player.hasEffect() && "shield".contentEquals(player.getEffect().getName())) { + return 0; + } + + return 20; + } + + @Override + public int getCooldownTime(IPlayer player) { + return 0; + } + + @Override + public boolean canUse(Race playerSpecies) { + return playerSpecies.canCast(); + } + + @Override + public Magic tryActivate(EntityPlayer player, World w) { + return new Magic(0); + } + + @Override + public Class getPackageType() { + return Magic.class; + } + + @Override + public void apply(EntityPlayer player, Magic data) { + IPlayer prop = PlayerSpeciesList.instance().getPlayer(player); + + if (prop.getEffect() instanceof SpellShield) { + prop.setEffect(null); + } else { + prop.setEffect(new SpellShield(data.type)); + } + } + + @Override + public void preApply(EntityPlayer player) { } + + @Override + public void postApply(EntityPlayer player) { } + + class Magic implements IData { + + @Expose + public int type; + + public Magic(int strength) { + type = strength; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerStomp.java b/src/main/java/com/minelittlepony/unicopia/power/PowerStomp.java new file mode 100644 index 00000000..5de9e3b4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerStomp.java @@ -0,0 +1,405 @@ +package com.minelittlepony.unicopia.power; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import org.lwjgl.input.Keyboard; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UItems; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import com.minelittlepony.unicopia.power.data.Location; +import com.minelittlepony.util.MagicalDamageSource; +import com.minelittlepony.util.PosHelper; +import com.minelittlepony.util.shape.IShape; +import com.minelittlepony.util.shape.Sphere; +import com.minelittlepony.util.vector.VecHelper; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockLeaves; +import net.minecraft.block.BlockLog; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import static net.minecraft.util.EnumFacing.*; + +public class PowerStomp implements IPower { + + @Override + public String getKeyName() { + return "unicopia.power.earth"; + } + + @Override + public int getKeyCode() { + return Keyboard.KEY_M; + } + + @Override + public int getWarmupTime(IPlayer player) { + return 0; + } + + @Override + public int getCooldownTime(IPlayer player) { + return 500; + } + + @Override + public boolean canUse(Race playerSpecies) { + return playerSpecies.canUseEarth(); + } + + @Override + public PowerStomp.Data tryActivate(EntityPlayer player, World w) { + RayTraceResult mop = VecHelper.getObjectMouseOver(player, 2, 1); + if (mop != null && mop.typeOfHit == RayTraceResult.Type.BLOCK) { + BlockPos pos = mop.getBlockPos(); + IBlockState state = w.getBlockState(pos); + if (state.getBlock() instanceof BlockLog) { + pos = getBaseOfTree(w, state, pos); + if (measureTree(w, state, pos) > 0) { + return new Data(pos.getX(), pos.getY(), pos.getZ(), 1); + } + } + } + + if (!player.onGround && !player.capabilities.isFlying) { + player.addVelocity(0, -6, 0); + return new Data(0, 0, 0, 0); + } + return null; + } + + @Override + public Class getPackageType() { + return PowerStomp.Data.class; + } + + @Override + public void apply(EntityPlayer player, Data data) { + double rad = 4; + if (data.hitType == 0) { + player.addVelocity(0, -6, 0); + BlockPos pos = player.getPosition(); + AxisAlignedBB box = new AxisAlignedBB(player.posX - rad, player.posY - rad, player.posZ - rad, player.posX + rad, player.posY + rad, player.posZ + rad); + List entities = player.world.getEntitiesWithinAABBExcludingEntity(player, box); + for (Entity i : entities) { + double dist = Math.sqrt(i.getDistanceSq(pos)); + if (dist <= rad + 3) { + i.addVelocity(i.posX - player.posX, i.posY - player.posY, i.posZ - player.posZ); + DamageSource damage = MagicalDamageSource.causePlayerDamage("smash", player); + float amount = 4 / (float)dist; + if (i instanceof EntityPlayer) { + Race race = PlayerSpeciesList.instance().getPlayer((EntityPlayer)i).getPlayerSpecies(); + if (race.canUseEarth()) amount /= 3; + if (race.canFly()) amount *= 4; + } + i.attackEntityFrom(damage, amount); + } + } + Iterable area = BlockPos.getAllInBox(pos.add(-rad, -rad, -rad), pos.add(rad, rad, rad)); + for (BlockPos i : area) { + if (i.distanceSqToCenter(player.posX, player.posY, player.posZ) <= rad*rad) { + spawnEffect(player.world, i); + } + } + for (int i = 1; i < 202; i+= 2) { + spawnParticleRing(player, i); + } + IPower.takeFromPlayer(player, 4); + } else if (data.hitType == 1) { + if (player.world.rand.nextInt(30) == 0) { + removeTree(player.world, new BlockPos(data.x, data.y, data.z)); + } else { + dropApples(player.world, new BlockPos(data.x, data.y, data.z)); + } + IPower.takeFromPlayer(player, 1); + } + } + + private void spawnEffect(World w, BlockPos pos) { + IBlockState state = w.getBlockState(pos); + + if (state.getBlock() != Blocks.AIR) { + if (w.getBlockState(pos.up()).getBlock() == Blocks.AIR) { + w.playEvent(2001, pos, Block.getStateId(state)); + } + } + } + + @Override + public void preApply(EntityPlayer player) { + player.spawnRunningParticles(); + } + + @Override + public void postApply(EntityPlayer player) { + IPlayer prop = PlayerSpeciesList.instance().getPlayer(player); + + int timeDiff = getCooldownTime(prop) - prop.getAbilities().getRemainingCooldown(); + + if (player.world.getWorldTime() % 1 == 0 || timeDiff == 0) { + spawnParticleRing(player, timeDiff, 1); + } + } + + private void spawnParticleRing(EntityPlayer player, int timeDiff) { + spawnParticleRing(player, timeDiff, 0); + } + + private void spawnParticleRing(EntityPlayer player, int timeDiff, double yVel) { + int animationTicks = (int)(timeDiff / 10); + if (animationTicks < 6) { + IShape shape = new Sphere(true, animationTicks, 1, 0, 1); + + double y = 0.5 + (Math.sin(animationTicks) * 1.5); + + yVel *= y * 5; + + for (int i = 0; i < shape.getVolumeOfSpawnableSpace(); i++) { + Vec3d point = shape.computePoint(player.getEntityWorld().rand); + Particles.instance().spawnParticle(EnumParticleTypes.BLOCK_CRACK.getParticleID(), false, + player.posX + point.x, + player.posY + y + point.y, + player.posZ + point.z, + 0, yVel, 0, + Block.getStateId(Blocks.DIRT.getDefaultState())); + } + } + } + + private void removeTree(World w, BlockPos pos) { + IBlockState log = w.getBlockState(pos); + int size = measureTree(w, log, pos); + if (size > 0) { + pos = ascendTrunk(new ArrayList(), w, pos, log, 0); + removeTreePart(w, log, pos, 0); + } + } + + private BlockPos ascendTrunk(List done, World w, BlockPos pos, IBlockState log, int level) { + if (level < 3 && !done.contains(pos)) { + done.add(pos); + + BlockPos result = ascendTree(w, log, pos, true); + + if (variantAndBlockEquals(w.getBlockState(pos.east()), log)) { + result = ascendTrunk(done, w, pos.east(), log, level + 1); + } + + if (variantAndBlockEquals(w.getBlockState(pos.west()), log)) { + result = ascendTrunk(done, w, pos.west(), log, level + 1); + } + + if (variantAndBlockEquals(w.getBlockState(pos.north()), log)) { + result = ascendTrunk(done, w, pos.north(), log, level + 1); + } + + if (variantAndBlockEquals(w.getBlockState(pos.south()), log)) { + result = ascendTrunk(done, w, pos.south(), log, level + 1); + } + return result; + } + return pos; + } + + private void removeTreePart(World w, IBlockState log, BlockPos pos, int level) { + if (level < 10 && isWoodOrLeaf(w, log, pos)) { + if (level < 5) { + w.destroyBlock(pos, true); + } else { + IBlockState state = w.getBlockState(pos); + state.getBlock().dropBlockAsItem(w, pos, state, 0); + w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3); + } + + PosHelper.all(pos, p -> { + removeTreePart(w, log, p, level + 1); + }, UP, NORTH, SOUTH, EAST, WEST); + + } + } + + private BlockPos ascendTree(World w, IBlockState log, BlockPos pos, boolean remove) { + int breaks = 0; + IBlockState state; + while (variantAndBlockEquals(w.getBlockState(pos.up()), log)) { + if (PosHelper.some(pos, p -> isLeaves(w.getBlockState(p), log), HORIZONTALS)) { + break; + } + + if (remove) { + if (breaks < 10) { + w.destroyBlock(pos, true); + } else { + state = w.getBlockState(pos); + state.getBlock().dropBlockAsItem(w, pos, state, 0); + w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3); + } + breaks++; + } + pos = pos.up(); + } + return pos; + } + + private void dropApples(World w, BlockPos pos) { + IBlockState log = w.getBlockState(pos); + int size = measureTree(w, log, pos); + if (size > 0) { + dropApplesPart(new ArrayList(), w, log, pos, 0); + } + } + + + private void dropApplesPart(List done, World w, IBlockState log, BlockPos pos, int level) { + if (!done.contains(pos)) { + done.add(pos); + pos = ascendTree(w, log, pos, false); + if (level < 10 && isWoodOrLeaf(w, log, pos)) { + IBlockState state = w.getBlockState(pos); + if (state.getBlock() instanceof BlockLeaves && w.getBlockState(pos.down()).getMaterial() == Material.AIR) { + w.playEvent(2001, pos, Block.getStateId(state)); + EntityItem item = new EntityItem(w); + item.setPosition(pos.getX() + 0.5, pos.getY() - 0.5, pos.getZ() + 0.5); + item.setItem(new ItemStack(Items.APPLE, 1, getAppleMeta(w, log))); + w.spawnEntity(item); + } + + PosHelper.all(pos, p -> { + dropApplesPart(done, w, log, p, level + 1); + }, UP, NORTH, SOUTH, EAST, WEST); + } + } + } + + private int getAppleMeta(World w, IBlockState log) { + return UItems.apple.getRandomAppleMetadata(w.rand, getVariant(log)); + } + + private int measureTree(World w, IBlockState log, BlockPos pos) { + List logs = new ArrayList(); + List leaves = new ArrayList(); + countParts(logs, leaves, w, log, pos); + return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0; + } + + private BlockPos getBaseOfTree(World w, IBlockState log, BlockPos pos) { + return getBaseOfTreePart(new ArrayList(), w, log, pos); + } + + private BlockPos getBaseOfTreePart(List done, World w, IBlockState log, BlockPos pos) { + if (done.contains(pos) || !variantAndBlockEquals(w.getBlockState(pos), log)) { + return null; + } + done.add(pos); + + while (variantAndBlockEquals(w.getBlockState(pos.down()), log)) { + pos = pos.down(); + done.add(pos); + } + + BlockPos adjacent = getBaseOfTreePart(done, w, log, pos.north()); + if (adjacent != null && adjacent.getY() < pos.getY()) { + pos = adjacent; + } + + adjacent = getBaseOfTreePart(done, w, log, pos.south()); + if (adjacent != null && adjacent.getY() < pos.getY()) { + pos = adjacent; + } + + adjacent = getBaseOfTreePart(done, w, log, pos.east()); + if (adjacent != null && adjacent.getY() < pos.getY()) { + pos = adjacent; + } + + adjacent = getBaseOfTreePart(done, w, log, pos.west()); + if (adjacent != null && adjacent.getY() < pos.getY()) { + pos = adjacent; + } + + if (!done.contains(pos)) { + done.add(pos); + } + + return pos; + } + + private boolean isWoodOrLeaf(World w, IBlockState log, BlockPos pos) { + IBlockState state = w.getBlockState(pos); + return variantAndBlockEquals(state, log) || (isLeaves(state, log) && ((Boolean)state.getValue(BlockLeaves.DECAYABLE)).booleanValue()); + } + + private void countParts(List logs, List leaves, World w, IBlockState log, BlockPos pos) { + if (logs.contains(pos) || leaves.contains(pos)) { + return; + } + + IBlockState state = w.getBlockState(pos); + boolean yay = false; + + if (state.getBlock() instanceof BlockLeaves && ((Boolean)state.getValue(BlockLeaves.DECAYABLE)).booleanValue() && variantEquals(state, log)) { + leaves.add(pos); + yay = true; + } else if (variantAndBlockEquals(state, log)) { + logs.add(pos); + yay = true; + } + + if (yay) { + PosHelper.all(pos, p -> { + countParts(logs, leaves, w, log, p); + }, UP, NORTH, SOUTH, EAST, WEST); + } + } + + private boolean isLeaves(IBlockState state, IBlockState log) { + return state.getBlock() instanceof BlockLeaves && variantEquals(state, log); + } + + private boolean variantAndBlockEquals(IBlockState one, IBlockState two) { + return (one.getBlock() == two.getBlock()) && variantEquals(one, two); + } + + private boolean variantEquals(IBlockState one, IBlockState two) { + return getVariant(one) == getVariant(two); + } + + private Object getVariant(IBlockState state) { + for (Entry, ?> i : state.getProperties().entrySet()) { + if (i.getKey().getName().contentEquals("variant")) { + return i.getValue(); + } + } + return null; + } + + protected static class Data extends Location { + + public int hitType; + + public Data(int x, int y, int z, int hit) { + super(x, y, z); + hitType = hit; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java b/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java new file mode 100644 index 00000000..6c971c0d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java @@ -0,0 +1,152 @@ +package com.minelittlepony.unicopia.power; + +import org.lwjgl.input.Keyboard; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.power.data.Location; +import com.minelittlepony.util.vector.VecHelper; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockFence; +import net.minecraft.block.BlockLeaves; +import net.minecraft.block.BlockWall; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class PowerTeleport implements IPower { + + @Override + public String getKeyName() { + return "unicopia.power.teleport"; + } + + @Override + public int getKeyCode() { + return Keyboard.KEY_O; + } + + @Override + public int getWarmupTime(IPlayer player) { + return 20; + } + + @Override + public int getCooldownTime(IPlayer player) { + return 50; + } + + @Override + public boolean canUse(Race playerSpecies) { + return playerSpecies.canCast(); + } + + @Override + public Location tryActivate(EntityPlayer player, World w) { + RayTraceResult ray = VecHelper.getObjectMouseOver(player, 100, 1); + + if (ray != null && ray.typeOfHit != RayTraceResult.Type.MISS) { + BlockPos pos; + + if (ray.typeOfHit == RayTraceResult.Type.ENTITY) { + pos = new BlockPos(ray.entityHit); + } else { + pos = ray.getBlockPos(); + } + + boolean airAbove = enterable(w, pos.up()) && enterable(w, pos.up(2)); + if (exception(w, pos)) { + EnumFacing sideHit = ray.sideHit; + + if (player.isSneaking()) { + sideHit = sideHit.getOpposite(); + } + + pos = pos.offset(sideHit); + } + + if (enterable(w, pos.down())) { + pos = pos.down(); + + if (enterable(w, pos.down())) { + if (airAbove) { + pos = new BlockPos( + ray.getBlockPos().getX(), + pos.getY() + 2, + ray.getBlockPos().getZ()); + } else { + return null; + } + } + } + + if ((!enterable(w, pos) && exception(w, pos)) + || (!enterable(w, pos.up()) && exception(w, pos.up()))) { + return null; + } + + return new Location(pos.getX(), pos.getY(), pos.getZ()); + } + + return null; + } + + + + @Override + public Class getPackageType() { + return Location.class; + } + + @Override + public void apply(EntityPlayer player, Location data) { + player.world.playSound(player.posX, player.posY, player.posZ, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1, true); + + double distance = player.getDistance(data.x, data.y, data.z) / 10; + player.dismountRidingEntity(); + + player.setPositionAndUpdate(data.x + (player.posX - Math.floor(player.posX)), data.y, data.z + (player.posZ - Math.floor(player.posZ))); + IPower.takeFromPlayer(player, distance); + player.fallDistance /= distance; + player.world.playSound(data.x, data.y, data.z, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1, true); + } + + private boolean enterable(World w, BlockPos pos) { + IBlockState state = w.getBlockState(pos); + + Block block = state.getBlock(); + + return w.isAirBlock(pos) + || block.isReplaceable(w, pos) + || (block instanceof BlockLeaves); + } + + private boolean exception(World w, BlockPos pos) { + IBlockState state = w.getBlockState(pos); + + Block c = state.getBlock(); + return state.isSideSolid(w, pos, EnumFacing.UP) + || state.getMaterial().isLiquid() + || (c instanceof BlockWall) + || (c instanceof BlockFence) + || (c instanceof BlockLeaves); + } + + @Override + public void preApply(EntityPlayer player) { + postApply(player); + } + + @Override + public void postApply(EntityPlayer player) { + IPower.spawnParticles(Unicopia.MAGIC_PARTICLE, player, 1); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowersRegistry.java b/src/main/java/com/minelittlepony/unicopia/power/PowersRegistry.java new file mode 100644 index 00000000..324e2596 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/PowersRegistry.java @@ -0,0 +1,58 @@ +package com.minelittlepony.unicopia.power; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.input.Keyboard; + +public class PowersRegistry { + + private static PowersRegistry instance = new PowersRegistry(); + + public static PowersRegistry instance() { + return instance; + } + + private final Map>> keyToPowerMap = new HashMap<>(); + + private final Map> powerNamesMap = new HashMap<>(); + + private PowersRegistry() { + } + + public void init() { + registerPower(new PowerTeleport()); + registerPower(new PowerMagic()); + registerPower(new PowerStomp()); + registerPower(new PowerGrow()); + registerPower(new PowerFeed()); + } + + public boolean hasRegisteredPower(int keyCode) { + return keyToPowerMap.containsKey(keyCode); + } + + public Optional> getCapablePowerFromKey(int keyCode, Race race) { + return getKeyCodePool(keyCode).stream() + .filter(power -> power.canUse(race)) + .findFirst(); + } + + public Optional> getPowerFromName(String name) { + return Optional.ofNullable(powerNamesMap.get(name)); + } + + private List> getKeyCodePool(int keyCode) { + return keyToPowerMap.computeIfAbsent(keyCode, ArrayList::new); + } + + public void registerPower(IPower power) { + getKeyCodePool(power.getKeyCode()).add(power); + powerNamesMap.put(power.getKeyName(), power); + Keyboard.getKeyHandler().addKeybind(power); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/data/Hit.java b/src/main/java/com/minelittlepony/unicopia/power/data/Hit.java new file mode 100644 index 00000000..85303334 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/data/Hit.java @@ -0,0 +1,7 @@ +package com.minelittlepony.unicopia.power.data; + +import com.minelittlepony.unicopia.power.IData; + +public class Hit implements IData { + +} diff --git a/src/main/java/com/minelittlepony/unicopia/power/data/Location.java b/src/main/java/com/minelittlepony/unicopia/power/data/Location.java new file mode 100644 index 00000000..10b98b87 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/power/data/Location.java @@ -0,0 +1,30 @@ +package com.minelittlepony.unicopia.power.data; + +import com.google.gson.annotations.Expose; +import com.minelittlepony.unicopia.power.IData; + +import net.minecraft.util.math.BlockPos; + +public class Location implements IData { + + @Expose + public int x; + + @Expose + public int y; + + @Expose + public int z; + + public Location(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Location(BlockPos pos) { + x = pos.getX(); + y = pos.getY(); + z = pos.getZ(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java b/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java new file mode 100644 index 00000000..e60efdb1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/AbstractSpell.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia.spell; + +import net.minecraft.entity.Entity; +import net.minecraft.world.World; + +public abstract class AbstractSpell implements IMagicEffect { + + protected boolean isDead = false; + + @Override + public void setDead() { + isDead = true; + } + + @Override + public boolean getDead() { + return isDead; + } + + @Override + public boolean update(Entity source) { + return false; + } + + @Override + public boolean updateAt(ICaster source, World w, double x, double y, double z, int level) { + return false; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/ActionResult.java b/src/main/java/com/minelittlepony/unicopia/spell/ActionResult.java new file mode 100644 index 00000000..bc932269 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/ActionResult.java @@ -0,0 +1,21 @@ +package com.minelittlepony.unicopia.spell; + +/** + * A type of action to perform after a spell has completed its handling. + */ +public enum ActionResult { + /** + * No action. + */ + NONE, + /** + * Place block/gem into the world. + */ + PLACE, + /** + * Vanilla behaviour. + * In the case of dispensers the item will be ejected into the world. + * When right clicking a block the itemstack will be decremented. + */ + DEFAULT; +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java new file mode 100644 index 00000000..b64cc3f1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java @@ -0,0 +1,24 @@ +package com.minelittlepony.unicopia.spell; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; + +public interface ICaster { + void setEffect(IMagicEffect effect); + + IMagicEffect getEffect(); + + default boolean hasEffect() { + return getEffect() != null; + } + + default void setOwner(EntityLivingBase owner) { + + } + + E getOwner(); + + default Entity getEntity() { + return getOwner(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/IDispenceable.java b/src/main/java/com/minelittlepony/unicopia/spell/IDispenceable.java new file mode 100644 index 00000000..8e335686 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/IDispenceable.java @@ -0,0 +1,22 @@ +package com.minelittlepony.unicopia.spell; + +import net.minecraft.dispenser.IBlockSource; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.EnumFacing; + +/** + * Represents an object with an action to perform when dispensed from a dispenser. + * + */ +public interface IDispenceable { + + /** + * Called when dispensed. + * + * @param pos Block position in front of the dispenser + * @param facing Direction of the dispenser + * @param source The dispenser currently dispensing + * @return an ActionResult for the type of action to perform. + */ + public ActionResult onDispenced(BlockPos pos, EnumFacing facing, IBlockSource source); +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java b/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java new file mode 100644 index 00000000..0e45822e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/IMagicEffect.java @@ -0,0 +1,87 @@ +package com.minelittlepony.unicopia.spell; + +import com.minelittlepony.unicopia.InbtSerialisable; + +import net.minecraft.entity.Entity; +import net.minecraft.world.World; + +/** + * + * Interface for a magic spell + * + */ +public interface IMagicEffect extends InbtSerialisable { + + /** + * Maximum level this spell can reach or -1 for unlimited. + *
+ * If a gem goes past this level it is more likely to explode. + */ + default int getMaxLevel() { + return 0; + } + + String getName(); + + /** + * Sets this effect as dead. + */ + void setDead(); + + /** + * Returns true if this spell is dead, and must be cleaned up. + */ + boolean getDead(); + + /** + * Called every tick when attached to a player. + * + * @param source The entity we are currently attached to. + * @return true to keep alive + */ + boolean update(Entity source); + + /** + * Called every tick when attached to a player. Used to apply particle effects. + * Is only called on the client side. + * + * @param source The entity we are currently attached to. + */ + default void render(Entity source) { + + } + + /** + * Called every tick when attached to a gem. + * + * @param source The entity we are attached to. + * @param w The world + * @param x Entity position x + * @param y Entity position y + * @param z Entity position z + * @param level Current spell level + */ + boolean updateAt(ICaster source, World w, double x, double y, double z, int level); + + /** + * Called every tick when attached to an entity to produce particle effects. + * Is only called on the client side. + * + * @param source The entity we are attached to. + * @param w The world + * @param x Entity position x + * @param y Entity position y + * @param z Entity position z + * @param level Current spell level + */ + default void renderAt(ICaster source, World w, double x, double y, double z, int level) { + + } + + /** + * Return true to allow the gem update and move. + */ + default boolean allowAI() { + return false; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/IUseAction.java b/src/main/java/com/minelittlepony/unicopia/spell/IUseAction.java new file mode 100644 index 00000000..8611eed4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/IUseAction.java @@ -0,0 +1,43 @@ +package com.minelittlepony.unicopia.spell; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +/** + * Interface for right-click actions. + * + */ +public interface IUseAction { + + /** + * Triggered when the player right clicks a block + * + * @param stack The current itemstack + * @param player The player + * @param world The player's world + * @param pos The location clicked + * @param side The side of the block clicked + * @param hitX X offset inside the block + * @param hitY Y offset inside the block + * @param hitZ Z offset inside the block + * + * @return ActionResult for the type of action to perform + */ + public ActionResult onUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ); + + /** + * Triggered when the player right clicks + * + * @param stack The current itemstack + * @param player The player + * @param world The player's world + * @param hitEntity The entity in focus, if any + * + * @return ActionResult for the type of action to perform + */ + public ActionResult onUse(ItemStack stack, EntityPlayer player, World world, Entity hitEntity); +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java new file mode 100644 index 00000000..aada1e61 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java @@ -0,0 +1,40 @@ +package com.minelittlepony.unicopia.spell; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class SpellRegistry { + + private static final SpellRegistry instance = new SpellRegistry(); + + public static SpellRegistry instance() { + return instance; + } + + private final Map> factories = new HashMap<>(); + + private SpellRegistry() { + } + + public void init() { + registerSpell("shield", SpellShield::new); + } + + public Optional getSpellFromName(String name) { + try { + if (factories.containsKey(name)) { + return Optional.ofNullable(factories.get(name).call()); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return Optional.empty(); + } + + public void registerSpell(String key, Callable factory) { + factories.put(key, factory); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java new file mode 100644 index 00000000..22dfe54b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellShield.java @@ -0,0 +1,165 @@ +package com.minelittlepony.unicopia.spell; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UClient; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import com.minelittlepony.unicopia.power.IPower; +import com.minelittlepony.util.ProjectileUtil; +import com.minelittlepony.util.shape.IShape; +import com.minelittlepony.util.shape.Sphere; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class SpellShield extends AbstractSpell { + + private int strength = 0; + + public SpellShield() { + } + + public SpellShield(int type) { + setStrength(type); + } + + public void setStrength(int level) { + strength = level; + } + + @Override + public String getName() { + return "shield"; + } + + @Override + public int getMaxLevel() { + return -1; + } + + @Override + public void render(Entity source) { + if (UClient.isClientSide()) { + spawnParticles(source.getEntityWorld(), source.posX, source.posY, source.posZ, 4 + (strength * 2)); + } + } + + public void renderAt(ICaster source, World w, double x, double y, double z, int level) { + if (UClient.isClientSide()) { + if (w.rand.nextInt(4 + level * 4) == 0) { + spawnParticles(w, x, y, z, 4 + (level * 2)); + } + } + } + + protected void spawnParticles(World w, double x, double y, double z, int strength) { + IShape sphere = new Sphere(true, strength); + + Vec3d pos = sphere.computePoint(w.rand); + Particles.instance().spawnParticle(Unicopia.MAGIC_PARTICLE, false, + pos.x + x, pos.y + y, pos.z + z, + 0, 0, 0); + } + + @Override + public boolean update(Entity source) { + applyEntities(null, source, source.getEntityWorld(), source.posX, source.posY, source.posZ, strength); + if (source.getEntityWorld().getWorldTime() % 50 == 0) { + double radius = 4 + (strength * 2); + if (!IPower.takeFromPlayer((EntityPlayer)source, radius/4)) { + setDead(); + } + } + return !isDead; + } + + @Override + public boolean updateAt(ICaster source, World w, double x, double y, double z, int level) { + return applyEntities(source, source.getOwner(), w, x, y, z, level); + } + + private boolean applyEntities(ICaster source, Entity owner, World w, double x, double y, double z, int level) { + double radius = 4 + (level * 2); + + AxisAlignedBB bb = new AxisAlignedBB(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius); + + for (Entity i : w.getEntitiesWithinAABBExcludingEntity(source.getEntity(), bb)) { + if ((!i.equals(owner) + || (owner instanceof EntityPlayer + && !PlayerSpeciesList.instance().getPlayer((EntityPlayer)owner).getPlayerSpecies().canCast()))) { + + double dist = i.getDistance(x, y, z); + double dist2 = i.getDistance(x, y - i.getEyeHeight(), z); + + boolean projectile = ProjectileUtil.isProjectile(i); + + if (dist <= radius || dist2 <= radius) { + if (projectile) { + if (!ProjectileUtil.isProjectileThrownBy(i, owner)) { + if (dist < radius/2) { + i.playSound(SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, 0.1f, 1); + i.setDead(); + } else { + ricochet(i, x, y, z); + } + } + } else if (i instanceof EntityLivingBase) { + double force = dist; + if (i instanceof EntityPlayer) { + force = calculateForce((EntityPlayer)i); + } + + i.addVelocity( + -(x - i.posX) / force, + -(y - i.posY) / force + (dist < 1 ? dist : 0), + -(z - i.posZ) / force); + } + } + } + } + return true; + } + + protected double calculateForce(EntityPlayer player) { + Race race = PlayerSpeciesList.instance().getPlayer(player).getPlayerSpecies(); + + double force = 4 * 8; + + if (race.canUseEarth()) { + if (player.isSneaking()) { + force *= 16; + } + } else if (race.canFly()) { + force /= 2; + } + + return force; + } + + private void ricochet(Entity projectile, double x, double y, double z) { + Vec3d position = new Vec3d(projectile.posX, projectile.posY, projectile.posZ); + Vec3d motion = new Vec3d(projectile.motionX, projectile.motionY, projectile.motionZ); + + Vec3d normal = position.subtract(x, y, z).normalize(); + Vec3d approach = motion.subtract(normal); + + if (approach.length() >= motion.length()) { + ProjectileUtil.setThrowableHeading(projectile, normal.x, normal.y, normal.z, (float)motion.length(), 0); + } + } + + public void writeToNBT(NBTTagCompound compound) { + compound.setInteger("spell_strength", strength); + } + + public void readFromNBT(NBTTagCompound compound) { + strength = compound.getInteger("spell_strength"); + } +} diff --git a/src/main/java/com/minelittlepony/util/MagicalDamageSource.java b/src/main/java/com/minelittlepony/util/MagicalDamageSource.java new file mode 100644 index 00000000..329fdb96 --- /dev/null +++ b/src/main/java/com/minelittlepony/util/MagicalDamageSource.java @@ -0,0 +1,57 @@ +package com.minelittlepony.util; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.translation.I18n; + +@SuppressWarnings("deprecation") +public class MagicalDamageSource extends EntityDamageSource { + + public static DamageSource create(String type) { + return new MagicalDamageSource(type); + } + + public static DamageSource causePlayerDamage(String type, EntityPlayer player) { + return new MagicalDamageSource(type, player); + } + + public static DamageSource causeMobDamage(String type, EntityLivingBase source) { + return new MagicalDamageSource(type, source); + } + + protected MagicalDamageSource(String type) { + this(type, null); + } + + protected MagicalDamageSource(String type, Entity source) { + super(type, source); + setMagicDamage(); + } + + public ITextComponent getDeathMessage(EntityLivingBase target) { + Entity attacker = damageSourceEntity instanceof EntityLivingBase ? (EntityLivingBase)damageSourceEntity : target.getRidingEntity(); + String basic = "death.attack." + this.damageType; + + if (attacker != null && attacker instanceof EntityLivingBase) { + String withAttecker = basic + ".player"; + ItemStack held = attacker instanceof EntityLivingBase ? ((EntityLivingBase)attacker).getHeldItemMainhand() : ItemStack.EMPTY; + + String withItem = withAttecker + ".item"; + if (held != null && held.hasDisplayName() && I18n.canTranslate(withItem)) { + return new TextComponentTranslation(withItem, target.getDisplayName(), attacker.getDisplayName(), held.getTextComponent()); + } + + if (I18n.canTranslate(withAttecker)) { + return new TextComponentTranslation(withAttecker, target.getDisplayName(), attacker.getDisplayName()); + } + } + + return new TextComponentTranslation(basic, target.getDisplayName()); + } +} diff --git a/src/main/java/com/minelittlepony/util/PosHelper.java b/src/main/java/com/minelittlepony/util/PosHelper.java new file mode 100644 index 00000000..2d04ce3e --- /dev/null +++ b/src/main/java/com/minelittlepony/util/PosHelper.java @@ -0,0 +1,25 @@ +package com.minelittlepony.util; + +import java.util.function.Consumer; +import java.util.function.Function; + +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; + +public class PosHelper { + + public static void all(BlockPos origin, Consumer consumer, EnumFacing... directions) { + for (EnumFacing facing : directions) { + consumer.accept(origin.offset(facing)); + } + } + + public static boolean some(BlockPos origin, Function consumer, EnumFacing... directions) { + for (EnumFacing facing : directions) { + if (consumer.apply(origin.offset(facing))) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/minelittlepony/util/ProjectileUtil.java b/src/main/java/com/minelittlepony/util/ProjectileUtil.java new file mode 100644 index 00000000..d04324f1 --- /dev/null +++ b/src/main/java/com/minelittlepony/util/ProjectileUtil.java @@ -0,0 +1,84 @@ +package com.minelittlepony.util; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.entity.projectile.EntityFireball; +import net.minecraft.entity.projectile.EntityLlamaSpit; +import net.minecraft.entity.projectile.EntityThrowable; + +public class ProjectileUtil { + + /** + * Checks if the given entity is a projectile. + */ + public static boolean isProjectile(Entity e) { + return e instanceof IProjectile + || e instanceof EntityFireball; + } + + /** + * Checks if an entity is a thrown projectile. + */ + public static boolean isThrowable(Entity e) { + return e instanceof EntityThrowable || + e instanceof EntityArrow || + e instanceof EntityFireball; + } + + /** + * Checks if the given projectile was thrown by the given entity + */ + @SuppressWarnings("unchecked") + public static boolean isProjectileThrownBy(Entity throwable, T e) { + if (e == null || !isProjectile(throwable)) { + return false; + } + + return e.equals(getThrowingEntity(throwable)); + } + + /** + * Gets the thrower for a projectile or null + */ + @SuppressWarnings("unchecked") + public static T getThrowingEntity(Entity throwable) { + + if (throwable instanceof EntityArrow) { + return (T)((EntityArrow) throwable).shootingEntity; + } + + if (throwable instanceof EntityFireball) { + return (T)((EntityFireball) throwable).shootingEntity; + } + + if (throwable instanceof EntityLlamaSpit) { + return (T)((EntityLlamaSpit) throwable).owner; + } + + if (throwable instanceof EntityThrowable) { + return (T)((EntityThrowable) throwable).getThrower(); + } + + return null; + } + + /** + * Sets the velocity and heading for a projectile. + * + * @param throwable The projectile + * @param x X Direction component + * @param y Y Direction component + * @param z Z Direction component + * @param velocity Velocity + * @param inaccuracy Inaccuracy + * @return True the projectile's heading was set, false otherwise + */ + public static void setThrowableHeading(Entity throwable, double x, double y, double z, float velocity, float inaccuracy) { + if (throwable instanceof IProjectile) { + ((IProjectile)throwable).shoot(x, y, z, velocity, inaccuracy); + } else { + ((Entity)throwable).setVelocity(x, y, z); + } + } +} diff --git a/src/main/java/com/minelittlepony/util/shape/IShape.java b/src/main/java/com/minelittlepony/util/shape/IShape.java new file mode 100644 index 00000000..5e6a5fed --- /dev/null +++ b/src/main/java/com/minelittlepony/util/shape/IShape.java @@ -0,0 +1,61 @@ +package com.minelittlepony.util.shape; + +import java.util.Random; + +import net.minecraft.util.math.Vec3d; + +/** + * + *Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for). + */ +public interface IShape { + + /** + * Rotates this shape around it's center. + * + * @param u Rotate yaw + * @param v Rotate pitch + * + * @return This Shape + */ + public IShape setRotation(float u, float v); + + /** + * Get the volume of space filled by this shape, or the surface area if hollow. + * + * @return double volume + */ + public double getVolumeOfSpawnableSpace(); + + /** + * X offset from the shape's origin. + * + * @return X + */ + public double getXOffset(); + + /** + * Y offset from the shape's origin. + * + * @return Y + */ + public double getYOffset(); + + /** + * Z offset from the shape's origin. + * + * @return Z + */ + public double getZOffset(); + + /** + * Computes a random coordinate that falls within this shape's designated area. + */ + public Vec3d computePoint(Random rand); + + /** + * Checks if the given point is on the edge, or if not hollow the inside, of this shape. + * @return + */ + public boolean isPointInside(Vec3d point); +} diff --git a/src/main/java/com/minelittlepony/util/shape/Sphere.java b/src/main/java/com/minelittlepony/util/shape/Sphere.java new file mode 100644 index 00000000..750e76b2 --- /dev/null +++ b/src/main/java/com/minelittlepony/util/shape/Sphere.java @@ -0,0 +1,123 @@ +package com.minelittlepony.util.shape; + +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +import java.util.Random; + +/** + * A sphere, or 2d circle of you so desire. + * + */ +public class Sphere implements IShape { + + protected final Vec3d stretch; + private final boolean hollow; + private final double rad; + + private float yaw = 0; + private float pitch = 0; + + private final double volume; + + /** + * Creates a uniform sphere. + * + * @param hollow True if this shape must be hollow. + * @param radius Sphere radius + */ + public Sphere(boolean hollow, double radius) { + this(hollow, radius, 1, 1, 1); + } + + /** + * Creates a sphere of arbitrary dimensions. + *

+ * Can be used to create a flat circle by setting one of the stretch parameters to 0. + * If you set two of them to 0 it will probably produce a line. + * + * @param hollow True if this shape must be hollow. + * @param radius Sphere radius + * @param stretchX Warp this shape's X-axis + * @param stretchY Warp this shape's Y-axis + * @param stretchZ Warp this shape's Z-axis + * + */ + public Sphere(boolean hollow, double radius, float stretchX, float stretchY, float stretchZ) { + this.hollow = hollow; + stretch = new Vec3d(stretchX, stretchY, stretchZ); + rad = radius; + volume = computeSpawnableSpace(); + } + + public double getVolumeOfSpawnableSpace() { + return volume; + } + + private double computeSpawnableSpace() { + if (hollow) { + if (stretch.x == stretch.x && stretch.y == stretch.z) { + double radius = rad * stretch.x; + return 4 * Math.PI * radius * radius; + } + return computeEllipsoidArea(rad, stretch); + } + return computeEllipsoidVolume(rad, stretch); + } + + public static double computeEllipsoidArea(double rad, Vec3d stretch) { + double p = 1.6075; + double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p); + result += Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.z, p); + result += Math.pow(rad * stretch.y, p) * Math.pow(rad * stretch.y, p); + result /= 3; + return 2 * Math.PI * Math.pow(result, 1/p); + } + + public static double computeEllipsoidVolume(double rad, Vec3d stretch) { + double result = (4/3) * Math.PI; + result *= (rad * stretch.x); + result *= (rad * stretch.y); + result *= (rad * stretch.z); + return result; + } + + public double getXOffset() { + return 0; + } + + public double getYOffset() { + return 0; + } + + public double getZOffset() { + return 0; + } + + public Vec3d computePoint(Random rand) { + double rad = this.rad; + + if (!hollow) { + rad = MathHelper.nextDouble(rand, 0, rad); + } + + double z = MathHelper.nextDouble(rand, -rad, rad); + double phi = MathHelper.nextDouble(rand, 0, Math.PI * 2); + double theta = Math.asin(z / rad); + + return new Vec3d(rad * Math.cos(theta) * Math.cos(phi) * stretch.x, rad * Math.cos(theta) * Math.sin(phi) * stretch.y, z * stretch.z).rotateYaw(yaw).rotatePitch(pitch); + } + + public Sphere setRotation(float u, float v) { + yaw = u; + pitch = v; + return this; + } + + public boolean isPointInside(Vec3d point) { + point = point.rotateYaw(-yaw).rotatePitch(-pitch); + point = new Vec3d(point.x / stretch.x, point.y / stretch.y, point.z / stretch.z); + double dist = point.length(); + return hollow ? dist == rad : dist <= rad; + } +} diff --git a/src/main/java/com/minelittlepony/util/vector/VecHelper.java b/src/main/java/com/minelittlepony/util/vector/VecHelper.java new file mode 100644 index 00000000..25456a31 --- /dev/null +++ b/src/main/java/com/minelittlepony/util/vector/VecHelper.java @@ -0,0 +1,149 @@ +package com.minelittlepony.util.vector; + +import java.util.List; + +import com.google.common.base.Predicate; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EntitySelectors; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; + +public class VecHelper { + + /** + * Performs a ray cast from the given entity and returns a result for the first block that ray intercepts. + * + * @param e Entity to start from + * @param distance Maximum distance + * @param partialTick Client partial ticks + * + * @return RayTraceResult result or null + */ + public static RayTraceResult rayTrace(Entity e, double distance, float partialTick) { + Vec3d pos = geteEyePosition(e, partialTick); + Vec3d look = e.getLook(partialTick); + Vec3d ray = pos.add(look.x * distance, look.y * distance, look.z * distance); + return e.world.rayTraceBlocks(pos, ray, false, false, true); + } + + /** + * Gets the entity the player is currently looking at, or null. + */ + public static Entity getLookedAtEntity(EntityLivingBase e, int reach) { + RayTraceResult objectMouseOver = getObjectMouseOver(e, reach, 1); + + if (objectMouseOver != null && objectMouseOver.typeOfHit == RayTraceResult.Type.ENTITY) { + return objectMouseOver.entityHit; + } + + return null; + } + + /** + * Gets all entities within a given range from the player. + */ + public static List getWithinRange(EntityPlayer player, double reach) { + Vec3d look = player.getLook(0); + float var9 = 1.0F; + return player.world.getEntitiesWithinAABBExcludingEntity(player, player.getEntityBoundingBox().expand(look.x * reach, look.y * reach, look.z * reach).expand((double)var9, (double)var9, (double)var9)); + } + + /** + * Gets the position vector of an entity's eyes for ray tracing. + * + * @param e Entity + * @param partialTick Client partial ticks + * + * @return A vector of the entity's eye position + */ + public static Vec3d geteEyePosition(Entity e, float partialTick) { + double eyeHeight = e.getEyeHeight(); + if (partialTick == 1) return new Vec3d(e.posX, e.posY + eyeHeight, e.posZ); + double x = e.prevPosX + (e.posX - e.prevPosX) * partialTick; + double y = e.prevPosY + (e.posY - e.prevPosY) * partialTick + eyeHeight; + double z = e.prevPosZ + (e.posZ - e.prevPosZ) * partialTick; + return new Vec3d(x, y, z); + } + + /** + * Performs a ray trace from the given entity and returns a result for the first Entity or block that the ray intercepts. + * + * @param e Entity to start from + * @param distance Maximum distance + * @param partialTick Client partial ticks + * + * @return RayTraceResult result or null + */ + public static RayTraceResult getObjectMouseOver(Entity e, double distance, float partialTick) { + return getObjectMouseOverExcept(e, distance, partialTick, EntitySelectors.NOT_SPECTATING); + } + + /** + * Performs a ray trace from the given entity and returns a result for the first Entity that passing the given predicate or block that the ray intercepts. + *

+ * + * + * @param e Entity to start from + * @param distance Maximum distance + * @param partialTick Client partial ticks + * @param predicate Predicate test to filter entities + * + * @return RayTraceResult result or null + */ + public static RayTraceResult getObjectMouseOverExcept(Entity e, double distance, float partialTick, Predicate predicate) { + RayTraceResult tracedBlock = rayTrace(e, distance, partialTick); + + double totalTraceDistance = distance; + + Vec3d pos = geteEyePosition(e, partialTick); + + if (tracedBlock != null) totalTraceDistance = tracedBlock.hitVec.distanceTo(pos); + + Vec3d look = e.getLook(partialTick); + Vec3d ray = pos.add(look.x * distance, look.y * distance, look.z * distance); + + Vec3d hit = null; + Entity pointedEntity = null; + List entitiesWithinRange = e.world.getEntitiesInAABBexcluding(e, e.getEntityBoundingBox().grow(look.x * distance, look.y * distance, look.z * distance).expand(1, 1, 1), predicate); + + double traceDistance = totalTraceDistance; + for (Entity entity : entitiesWithinRange) { + if (entity.canBeCollidedWith()) { + double size = entity.getCollisionBorderSize(); + AxisAlignedBB entityAABB = entity.getEntityBoundingBox().expand(size, size, size); + RayTraceResult intercept = entityAABB.calculateIntercept(pos, ray); + + if (entityAABB.contains(pos)) { + if (0 < traceDistance || traceDistance == 0) { + pointedEntity = entity; + hit = intercept == null ? pos : intercept.hitVec; + traceDistance = 0; + } + } else if (intercept != null) { + double distanceToHit = pos.distanceTo(intercept.hitVec); + if (distanceToHit < traceDistance || traceDistance == 0) { + if (entity == e.getRidingEntity()) { + if (traceDistance == 0) { + pointedEntity = entity; + hit = intercept.hitVec; + } + } else { + pointedEntity = entity; + hit = intercept.hitVec; + traceDistance = distanceToHit; + } + } + } + } + } + + if (pointedEntity != null && (traceDistance < totalTraceDistance || tracedBlock == null)) { + return new RayTraceResult(pointedEntity, hit); + } + return tracedBlock; + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java new file mode 100644 index 00000000..19a1fb2b --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java @@ -0,0 +1,42 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; + +class DefaultPlayerCapabilitiesProxyContainer implements IPlayerCapabilitiesProxyContainer { + + @CapabilityInject(IPlayerCapabilitiesProxyContainer.class) + public static final Capability CAPABILITY = null; + + private IPlayer player; + + @Override + public IPlayer getPlayer() { + return player; + } + + @Override + public void setPlayer(IPlayer player) { + this.player = player; + } + + public void writeToNBT(NBTTagCompound compound) { + if (player == null) { + return; + } + + player.writeToNBT(compound); + } + + public void readFromNBT(NBTTagCompound compound) { + if (player == null) { + player = PlayerSpeciesList.instance().emptyPlayer(null); + } + + player.readFromNBT(compound); + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java new file mode 100644 index 00000000..27653832 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java @@ -0,0 +1,43 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; + +public class FBS { + + public static void init() { + CapabilityManager.INSTANCE.register(IPlayerCapabilitiesProxyContainer.class, + new Storage(), DefaultPlayerCapabilitiesProxyContainer::new); + } + + public static void attach(AttachCapabilitiesEvent event) { + if (event.getObject() instanceof EntityPlayer) { + event.addCapability(new ResourceLocation("unicopia", "race"), new Provider()); + } + } + + public static void clone(PlayerEvent.Clone event) { + final IPlayerCapabilitiesProxyContainer original = of(event.getOriginal()); + + if (original == null) { + return; + } + + final IPlayerCapabilitiesProxyContainer clone = of(event.getEntity()); + + clone.setPlayer(original.getPlayer()); + } + + public static IPlayerCapabilitiesProxyContainer of(Entity entity) { + if (entity.hasCapability(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN)) { + return entity.getCapability(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN); + } + + return null; + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java new file mode 100644 index 00000000..5a064632 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java @@ -0,0 +1,10 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import com.minelittlepony.unicopia.InbtSerialisable; +import com.minelittlepony.unicopia.player.IPlayer; + +public interface IPlayerCapabilitiesProxyContainer extends InbtSerialisable { + IPlayer getPlayer(); + + void setPlayer(IPlayer player); +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java new file mode 100644 index 00000000..90bf06f0 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java @@ -0,0 +1,36 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; + +class Provider implements ICapabilitySerializable { + DefaultPlayerCapabilitiesProxyContainer instance = (DefaultPlayerCapabilitiesProxyContainer) DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.getDefaultInstance(); + + @Override + public boolean hasCapability(Capability capability, EnumFacing facing) { + return capability == DefaultPlayerCapabilitiesProxyContainer.CAPABILITY; + } + + @Override + public T getCapability(Capability capability, EnumFacing facing) { + if (hasCapability(capability, facing)) { + return DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.cast(instance); + } + + return null; + } + + @Override + public NBTTagCompound serializeNBT() { + return (NBTTagCompound) DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.getStorage() + .writeNBT(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, instance, null); + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.getStorage() + .readNBT(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, instance, null, nbt); + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/RegistryLockSpinner.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/RegistryLockSpinner.java new file mode 100644 index 00000000..cc279083 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/RegistryLockSpinner.java @@ -0,0 +1,28 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import java.lang.reflect.Field; + +import net.minecraft.util.registry.RegistryNamespaced; +import net.minecraftforge.registries.ILockableRegistry; + +public class RegistryLockSpinner { + + public static void unlock(RegistryNamespaced registry) { + if (registry instanceof ILockableRegistry) { + try { + Field f = registry.getClass().getDeclaredField("locked"); + + f.setAccessible(true); + f.setBoolean(registry, false); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } + } + } + + public static void lock(RegistryNamespaced registry) { + if (registry instanceof ILockableRegistry) { + ((ILockableRegistry) registry).lock(); + } + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java new file mode 100644 index 00000000..3a6b3546 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java @@ -0,0 +1,24 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.Capability.IStorage; + +class Storage implements IStorage { + + @Override + public NBTBase writeNBT(Capability capability, IPlayerCapabilitiesProxyContainer instance, EnumFacing side) { + NBTTagCompound compound = new NBTTagCompound(); + + instance.writeToNBT(compound); + + return compound; + } + + @Override + public void readNBT(Capability capability, IPlayerCapabilitiesProxyContainer instance, EnumFacing side, NBTBase nbt) { + instance.readFromNBT((NBTTagCompound)nbt); + } +} diff --git a/src/main/resources/assets/unicopia/lang/en_US.lang b/src/main/resources/assets/unicopia/lang/en_US.lang new file mode 100644 index 00000000..6ebec2b3 --- /dev/null +++ b/src/main/resources/assets/unicopia/lang/en_US.lang @@ -0,0 +1,77 @@ +tile.cloudBlock.normal.name=Block of Cloud +tile.cloudBlock.packed.name=Dense Cloud +tile.cloudBlock.enchanted.name=Enchanted Cloud + +tile.cloudSlab.normal.name=Cloud Slab +tile.cloudSlab.packed.name=Dense Cloud Slab +tile.cloudSlab.enchanted.name=Enchanted Cloud Slab + +tile.stairsCloud.name=Cloud Stairs + +item.cloud_matter.name=Cloud Matter +item.cloud.small.name=Small Cloud +item.cloud.medium.name=Medium Cloud +item.cloud.large.name=Large Cloud + +item.spell.name=Gem +item.spell.shield.name=Gem of Repulsion +item.spell.fire.name=Gem of Flame +item.spell.inferno.name=Gem of Inferno +item.spell.ice.name=Gem of Freezing +item.spell.portal.name=Gem of Teleportation +item.spell.attract.name=Gem of Retention +item.spell.minion.name=Gem of Obedience +item.spellbook.name=Spellbook + +item.apple.green.name=Granny Smith Apple +item.apple.sweet.name=Sweet Apple Acres Apple +item.apple.rotten.name=Rotten Apple +item.apple.zap.name=Zap Apple +item.apple.zap_cooked.name=Cooked Zap Apple + +entity.cloud.name=Cloud +entity.cloud_natural.name=Cloud +entity.spell.name=Magic + + +commands.race.success.self=Your race has been updated +commands.race.success.otherself=%s changed race to %s +commands.race.success.other=Changed %s's race to %s +commands.race.usage=/race [player] +commands.race.list=The available races are: +commands.race.permission=Selected Race is not permitted +commands.race.fail="%s" is not a recognised Race + +commands.race.tell.self=You are a +commands.race.tell.self.alt=You are an +commands.race.tell.other=%s is a +commands.race.tell.other.alt=%s is an + +commands.decloud.success=%s clouds removed +commands.decloud.usage=/decloud + +unicopia.category.name=Pony Abilities + +unicopia.power.grow=Earth Pony (Primary) +unicopia.power.earth=Earth Pony (Secondary) + +unicopia.power.teleport=Unicorn (Primary) +unicopia.power.magic=Unicorn (Secondary) + +unicopia.power.rain=Pegasus (Primary) +unicopia.power.thunder=Pegasus (Secondary) + +unicopia.power.feed=Changeling (Primary) +unicopia.power.disguise=Changeling (Secondary) + +death.attack.feed=%1$s was drained of all life +death.attack.feed.player=%1$s died to feed %2$s +death.attack.cold=%1$s died of frost bite +death.attack.cold.player=%1$s was frozen to death by %2$s +death.attack.cold.player.item=%1$s was frozen to death by %2$s using %3$s +death.attack.smash=%1$s was crushed under hoof +death.attack.smash.player=%1$s was crushed by %2$s +death.attack.fire=%1$s was burnt to a crisp by magic +death.attack.fire.player=%1$s was burnt to a crisp by %2$s +death.attack.fire.own=%1$s was burnt to a crisp by their own spell +death.attack.zap=%1$s ate a Zap Apple diff --git a/src/main/resources/assets/unicopia/models/item/cloud.json b/src/main/resources/assets/unicopia/models/item/cloud.json new file mode 100644 index 00000000..cd6cd2b7 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud.json @@ -0,0 +1,18 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "unicopia:items/cloud" + }, + "display": { + "thirdperson": { + "rotation": [ -90, 0, 0 ], + "translation": [ 0, 1, -3 ], + "scale": [ 0.55, 0.55, 0.55 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.7, 1.7, 1.7 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/cloud_large.json b/src/main/resources/assets/unicopia/models/item/cloud_large.json new file mode 100644 index 00000000..8caf074c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_large.json @@ -0,0 +1,27 @@ +{ + "textures": { + "top": "unicopia:blocks/cloud_normal", + "bottom": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 16, 8, 16 ], + "faces": { + "down": { "uv": [ 1, 1, 15, 15 ], "texture": "#bottom", "cullface": "down" }, + "up": { "uv": [ 1, 1, 15, 15 ], "texture": "#top" }, + "north": { "uv": [ 0, 4, 16, 12 ], "texture": "#side", "cullface": "north" }, + "south": { "uv": [ 0, 4, 16, 12 ], "texture": "#side", "cullface": "south" }, + "west": { "uv": [ 0, 4, 16, 12 ], "texture": "#side", "cullface": "west" }, + "east": { "uv": [ 0, 4, 16, 12 ], "texture": "#side", "cullface": "east" } + } + } + ], + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/cloud_matter.json b/src/main/resources/assets/unicopia/models/item/cloud_matter.json new file mode 100644 index 00000000..3d8fed7e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_matter.json @@ -0,0 +1,18 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "unicopia:items/cloud_matter" + }, + "display": { + "thirdperson": { + "rotation": [ -90, 0, 0 ], + "translation": [ 0, 1, -3 ], + "scale": [ 0.55, 0.55, 0.55 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.7, 1.7, 1.7 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/cloud_med.json b/src/main/resources/assets/unicopia/models/item/cloud_med.json new file mode 100644 index 00000000..d35bfb5c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_med.json @@ -0,0 +1,16 @@ +{ + "parent": "unicopia:item/cloud_large", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.25, 0.25, 0.25 ] + }, + "firstperson": { + "scale": [ 0.7, 0.7, 0.7 ] + }, + "gui": { + "scale": [ 0.7, 0.7, 0.7 ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/cloud_small.json b/src/main/resources/assets/unicopia/models/item/cloud_small.json new file mode 100644 index 00000000..47fd3b7f --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_small.json @@ -0,0 +1,16 @@ +{ + "parent": "unicopia:item/cloud_large", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.15, 0.15, 0.15 ] + }, + "firstperson": { + "scale": [ 0.5, 0.5, 0.5 ] + }, + "gui": { + "scale": [ 0.5, 0.5, 0.5 ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/cloud_stairs.json b/src/main/resources/assets/unicopia/models/item/cloud_stairs.json new file mode 100644 index 00000000..6ef53b10 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_stairs.json @@ -0,0 +1,13 @@ +{ + "parent": "unicopia:block/cloud_stairs", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "gui": { + "rotation": [ 0, 180, 0 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/enchanted_cloud.json b/src/main/resources/assets/unicopia/models/item/enchanted_cloud.json new file mode 100644 index 00000000..6f24bde5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/enchanted_cloud.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/enchanted_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/enchanted_cloud_slab.json b/src/main/resources/assets/unicopia/models/item/enchanted_cloud_slab.json new file mode 100644 index 00000000..52913f8c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/enchanted_cloud_slab.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/half_slab_enchanted_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/normal_cloud.json b/src/main/resources/assets/unicopia/models/item/normal_cloud.json new file mode 100644 index 00000000..222bbd06 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/normal_cloud.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/normal_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/normal_cloud_slab.json b/src/main/resources/assets/unicopia/models/item/normal_cloud_slab.json new file mode 100644 index 00000000..5e9d4e1b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/normal_cloud_slab.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/half_slab_normal_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/packed_cloud.json b/src/main/resources/assets/unicopia/models/item/packed_cloud.json new file mode 100644 index 00000000..49bc498d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/packed_cloud.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/packed_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/packed_cloud_slab.json b/src/main/resources/assets/unicopia/models/item/packed_cloud_slab.json new file mode 100644 index 00000000..2d1617b5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/packed_cloud_slab.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/half_slab_packed_cloud", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spell.json b/src/main/resources/assets/unicopia/models/item/spell.json new file mode 100644 index 00000000..b50f90a1 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spell.json @@ -0,0 +1,19 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "unicopia:items/gem", + "layer1": "unicopia:items/gem_overlay" + }, + "display": { + "thirdperson": { + "rotation": [ -90, 0, 0 ], + "translation": [ 0, 1, -3 ], + "scale": [ 0.55, 0.55, 0.55 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.7, 1.7, 1.7 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spellbook.json b/src/main/resources/assets/unicopia/models/item/spellbook.json new file mode 100644 index 00000000..82e46601 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spellbook.json @@ -0,0 +1,18 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "unicopia:items/spellbook" + }, + "display": { + "thirdperson": { + "rotation": [ -90, 0, 0 ], + "translation": [ 0, 1, -3 ], + "scale": [ 0.55, 0.55, 0.55 ] + }, + "firstperson": { + "rotation": [ 0, -135, 25 ], + "translation": [ 0, 4, 2 ], + "scale": [ 1.7, 1.7, 1.7 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/recipes/apple.json b/src/main/resources/assets/unicopia/recipes/apple.json new file mode 100644 index 00000000..44658f65 --- /dev/null +++ b/src/main/resources/assets/unicopia/recipes/apple.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:apple", + "data": 4 + }, + { + "item": "minecraft:dye", + "data": 1 + } + ], + "result": { + "item": "minecraft:apple", + "count": 1, + "data": 6 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/recipes/apple_2.json b/src/main/resources/assets/unicopia/recipes/apple_2.json new file mode 100644 index 00000000..9aba8c11 --- /dev/null +++ b/src/main/resources/assets/unicopia/recipes/apple_2.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:apple", + "data": 4 + }, + { + "item": "minecraft:dye", + "data": 2 + } + ], + "result": { + "item": "minecraft:apple", + "count": 1, + "data": 7 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/recipes/apple_3.json b/src/main/resources/assets/unicopia/recipes/apple_3.json new file mode 100644 index 00000000..627c6b23 --- /dev/null +++ b/src/main/resources/assets/unicopia/recipes/apple_3.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:apple", + "data": 4 + }, + { + "item": "minecraft:dye", + "data": 14 + } + ], + "result": { + "item": "minecraft:apple", + "count": 1, + "data": 8 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/recipes/apple_4.json b/src/main/resources/assets/unicopia/recipes/apple_4.json new file mode 100644 index 00000000..423b1f6e --- /dev/null +++ b/src/main/resources/assets/unicopia/recipes/apple_4.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:apple", + "data": 4 + }, + { + "item": "minecraft:rotten_flesh" + } + ], + "result": { + "item": "minecraft:apple", + "count": 1, + "data": 9 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/textures/items/apple_green.png b/src/main/resources/assets/unicopia/textures/items/apple_green.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2f9769d81c266e129c1e175b8c66304d067fdf GIT binary patch literal 3122 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 M07*qoM6N<$f<__Dz5oCK literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/items/apple_rotten.png b/src/main/resources/assets/unicopia/textures/items/apple_rotten.png new file mode 100644 index 0000000000000000000000000000000000000000..84869075a4b96527326dda2548c3b5f7e6ae41dd GIT binary patch literal 3090 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 T#{-w700000NkvXXu0mjfbAh?R literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/items/apple_zap.png b/src/main/resources/assets/unicopia/textures/items/apple_zap.png new file mode 100644 index 0000000000000000000000000000000000000000..62b9bef4797efbef593f9ec17928e7ca94bb4d3f GIT binary patch literal 3376 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/items/apple_zap_cooked.png b/src/main/resources/assets/unicopia/textures/items/apple_zap_cooked.png new file mode 100644 index 0000000000000000000000000000000000000000..508bb454c00deb03f9deaaefaf9a4fae38c995f3 GIT binary patch literal 3455 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