From 24658067943bafef1256505b2f93848aa9f899b1 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 16 Sep 2018 00:45:44 +0200 Subject: [PATCH] Readded cloud blocks, readded/rewrote cloud entities, improved various other stuff --- .../minelittlepony/unicopia/CloudSize.java | 49 ++ .../minelittlepony/unicopia/CloudType.java | 93 +++ .../minelittlepony/unicopia/Predicates.java | 39 + .../com/minelittlepony/unicopia/Race.java | 8 +- .../com/minelittlepony/unicopia/UBlocks.java | 21 + .../minelittlepony/unicopia/UEntities.java | 34 + .../com/minelittlepony/unicopia/UItems.java | 65 +- .../minelittlepony/unicopia/UMaterials.java | 8 + .../com/minelittlepony/unicopia/Unicopia.java | 50 +- .../unicopia/block/BlockCloud.java | 186 +++++ .../unicopia/block/BlockCloudSlab.java | 227 ++++++ .../unicopia/block/BlockCloudStairs.java | 133 ++++ .../unicopia/block/ICloudBlock.java | 16 + .../particle/EntityParticleEmitter.java | 71 ++ .../client/particle/EntityRaindropFX.java | 33 + .../unicopia/client/particle/Particles.java | 6 + .../unicopia/command/CommandSpecies.java | 103 ++- .../unicopia/entity/EntityCloud.java | 670 ++++++++++++++++++ .../entity/EntityConstructionCloud.java | 20 + .../unicopia/entity/EntityRacingCloud.java | 57 ++ .../unicopia/entity/EntityWildCloud.java | 43 ++ .../unicopia/item/ItemCloud.java | 71 ++ .../unicopia/item/UItemBlock.java | 32 + .../unicopia/item/UItemMultiTexture.java | 32 + .../unicopia/item/UItemSlab.java | 33 + .../unicopia/model/ModelCloud.java | 29 + .../unicopia/player/IOwned.java | 11 + .../unicopia/player/IPlayer.java | 9 +- .../unicopia/player/IRaceContainer.java | 13 + .../unicopia/player/IUpdatable.java | 6 +- .../unicopia/player/ItemCapabilities.java | 49 ++ .../player/PlayerAbilityDelegate.java | 2 +- .../unicopia/player/PlayerAttributes.java | 50 ++ .../unicopia/player/PlayerCapabilities.java | 24 +- .../player/PlayerGravityDelegate.java | 2 +- .../unicopia/player/PlayerSpeciesList.java | 24 +- .../unicopia/power/PowerFeed.java | 9 +- .../unicopia/power/PowerTeleport.java | 2 +- .../unicopia/render/RenderCloud.java | 65 ++ .../unicopia/spell/ICaster.java | 10 +- .../minelittlepony/util/vector/VecHelper.java | 58 +- ...faultEntityCapabilitiesProxyContainer.java | 49 ++ ...faultPlayerCapabilitiesProxyContainer.java | 49 -- .../unicopia/forgebullshit/FBS.java | 24 +- .../ICapabilitiesProxyContainer.java | 15 + .../IPlayerCapabilitiesProxyContainer.java | 14 - .../unicopia/forgebullshit/Provider.java | 21 +- .../unicopia/forgebullshit/Storage.java | 7 +- .../unicopia/blockstates/cloud_block.json | 7 + .../blockstates/cloud_double_slab.json | 11 + .../unicopia/blockstates/cloud_slab.json | 10 + .../unicopia/blockstates/cloud_stairs.json | 44 ++ .../enchanted_cloud_double_slab.json | 5 + .../blockstates/enchanted_cloud_slab.json | 6 + .../blockstates/normal_cloud_double_slab.json | 5 + .../blockstates/normal_cloud_slab.json | 6 + .../blockstates/packed_cloud_double_slab.json | 5 + .../blockstates/packed_cloud_slab.json | 6 + .../resources/assets/unicopia/lang/en_US.lang | 77 +- .../unicopia/models/block/cloud_cube.json | 29 + .../models/block/cloud_inner_stairs.json | 8 + .../models/block/cloud_outer_stairs.json | 8 + .../unicopia/models/block/cloud_stairs.json | 8 + .../models/block/enchanted_cloud_block.json | 6 + .../block/half_slab_enchanted_cloud.json | 8 + .../models/block/half_slab_normal_cloud.json | 8 + .../models/block/half_slab_packed_cloud.json | 8 + .../models/block/normal_cloud_block.json | 6 + .../models/block/packed_cloud_block.json | 6 + .../block/upper_slab_enchanted_cloud.json | 8 + .../models/block/upper_slab_normal_cloud.json | 8 + .../models/block/upper_slab_packed_cloud.json | 8 + .../unicopia/models/item/cloud_large.json | 6 + .../unicopia/models/item/cloud_matter.json | 18 + .../unicopia/models/item/cloud_medium.json | 6 + .../unicopia/models/item/cloud_small.json | 6 + .../unicopia/models/item/cloud_stairs.json | 3 + .../assets/unicopia/models/item/dew_drop.json | 18 + .../models/item/enchanted_cloud_block.json | 10 + .../models/item/enchanted_cloud_slab.json | 10 + .../models/item/normal_cloud_block.json | 10 + .../models/item/normal_cloud_slab.json | 10 + .../models/item/packed_cloud_block.json | 10 + .../models/item/packed_cloud_slab.json | 10 + .../textures/blocks/cloud_enchanted.png | Bin 0 -> 2970 bytes .../unicopia/textures/blocks/cloud_normal.png | Bin 0 -> 2948 bytes .../unicopia/textures/blocks/cloud_packed.png | Bin 0 -> 2973 bytes .../unicopia/textures/entity/clouds.png | Bin 0 -> 21110 bytes .../unicopia/textures/entity/clouds_storm.png | Bin 0 -> 21913 bytes .../assets/unicopia/textures/items/cloud.png | Bin 0 -> 2965 bytes .../unicopia/textures/items/cloud_matter.png | Bin 0 -> 3188 bytes .../unicopia/textures/items/dew_drop.png | Bin 0 -> 145 bytes 92 files changed, 2833 insertions(+), 227 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/CloudSize.java create mode 100644 src/main/java/com/minelittlepony/unicopia/CloudType.java create mode 100644 src/main/java/com/minelittlepony/unicopia/Predicates.java create mode 100644 src/main/java/com/minelittlepony/unicopia/UBlocks.java create mode 100644 src/main/java/com/minelittlepony/unicopia/UEntities.java create mode 100644 src/main/java/com/minelittlepony/unicopia/UMaterials.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/BlockCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/BlockCloudSlab.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/BlockCloudStairs.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/ICloudBlock.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/EntityParticleEmitter.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/EntityRaindropFX.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/EntityCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/EntityConstructionCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/EntityRacingCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/EntityWildCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/ItemCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/UItemBlock.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/UItemMultiTexture.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/UItemSlab.java create mode 100644 src/main/java/com/minelittlepony/unicopia/model/ModelCloud.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/IOwned.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/IRaceContainer.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/ItemCapabilities.java create mode 100644 src/main/java/com/minelittlepony/unicopia/player/PlayerAttributes.java create mode 100644 src/main/java/com/minelittlepony/unicopia/render/RenderCloud.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultEntityCapabilitiesProxyContainer.java delete mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java create mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/ICapabilitiesProxyContainer.java delete mode 100644 src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java create mode 100644 src/main/resources/assets/unicopia/blockstates/cloud_block.json create mode 100644 src/main/resources/assets/unicopia/blockstates/cloud_double_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/cloud_stairs.json create mode 100644 src/main/resources/assets/unicopia/blockstates/enchanted_cloud_double_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/enchanted_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/normal_cloud_double_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/normal_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/packed_cloud_double_slab.json create mode 100644 src/main/resources/assets/unicopia/blockstates/packed_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/models/block/cloud_cube.json create mode 100644 src/main/resources/assets/unicopia/models/block/cloud_inner_stairs.json create mode 100644 src/main/resources/assets/unicopia/models/block/cloud_outer_stairs.json create mode 100644 src/main/resources/assets/unicopia/models/block/cloud_stairs.json create mode 100644 src/main/resources/assets/unicopia/models/block/enchanted_cloud_block.json create mode 100644 src/main/resources/assets/unicopia/models/block/half_slab_enchanted_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/block/half_slab_normal_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/block/half_slab_packed_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/block/normal_cloud_block.json create mode 100644 src/main/resources/assets/unicopia/models/block/packed_cloud_block.json create mode 100644 src/main/resources/assets/unicopia/models/block/upper_slab_enchanted_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/block/upper_slab_normal_cloud.json create mode 100644 src/main/resources/assets/unicopia/models/block/upper_slab_packed_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_medium.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/dew_drop.json create mode 100644 src/main/resources/assets/unicopia/models/item/enchanted_cloud_block.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_block.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_block.json create mode 100644 src/main/resources/assets/unicopia/models/item/packed_cloud_slab.json create mode 100644 src/main/resources/assets/unicopia/textures/blocks/cloud_enchanted.png create mode 100644 src/main/resources/assets/unicopia/textures/blocks/cloud_normal.png create mode 100644 src/main/resources/assets/unicopia/textures/blocks/cloud_packed.png create mode 100644 src/main/resources/assets/unicopia/textures/entity/clouds.png create mode 100644 src/main/resources/assets/unicopia/textures/entity/clouds_storm.png create mode 100644 src/main/resources/assets/unicopia/textures/items/cloud.png create mode 100644 src/main/resources/assets/unicopia/textures/items/cloud_matter.png create mode 100644 src/main/resources/assets/unicopia/textures/items/dew_drop.png diff --git a/src/main/java/com/minelittlepony/unicopia/CloudSize.java b/src/main/java/com/minelittlepony/unicopia/CloudSize.java new file mode 100644 index 00000000..e8a77ed8 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/CloudSize.java @@ -0,0 +1,49 @@ +package com.minelittlepony.unicopia; + +import java.util.function.Function; + +import com.minelittlepony.unicopia.entity.*; + +import net.minecraft.world.World; + +public enum CloudSize { + SMALL(EntityRacingCloud::new), + MEDIUM(EntityConstructionCloud::new), + LARGE(EntityWildCloud::new); + + private static final CloudSize[] META_LOOKUP = new CloudSize[values().length]; + static { + CloudSize[] values = values(); + for (CloudSize i : values) { + META_LOOKUP[i.getMetadata()] = i; + } + } + + private final String name; + + private final Function constructor; + + CloudSize(Function constructor) { + this.constructor = constructor; + this.name = name().toLowerCase(); + } + + public String getName() { + return name; + } + + public int getMetadata() { + return ordinal(); + } + + public EntityCloud createEntity(World w) { + return constructor.apply(w); + } + + public static CloudSize byMetadata(int meta) { + if (meta < 0 || meta >= META_LOOKUP.length) { + meta = 0; + } + return META_LOOKUP[meta]; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/CloudType.java b/src/main/java/com/minelittlepony/unicopia/CloudType.java new file mode 100644 index 00000000..600406fb --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/CloudType.java @@ -0,0 +1,93 @@ +package com.minelittlepony.unicopia; + +import com.minelittlepony.unicopia.entity.EntityCloud; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.IStringSerializable; + +public enum CloudType implements IStringSerializable { + NORMAL("NORMAL", 0, "normal"), + PACKED("PACKED", 1, "packed"), + ENCHANTED("ENCHANTED", 2, "enchanted"); + + private String name; + private int meta; + private String unlocalizedName; + + private static final CloudType[] META_LOOKUP = new CloudType[values().length]; + private static final String[] NAMES = new String[values().length]; + + static { + for (CloudType i : values()) { + META_LOOKUP[i.getMetadata()] = i; + NAMES[i.ordinal()] = i.getTranslationKey(); + } + } + + CloudType(String name, int meta, String unlocalised) { + this.name = name; + this.meta = meta; + this.unlocalizedName = unlocalised; + } + + public static CloudType byMetadata(int meta) { + if (meta < 0 || meta >= META_LOOKUP.length) meta = 0; + return META_LOOKUP[meta]; + } + + + public static String[] getVariants(String suffex) { + String[] result = new String[NAMES.length]; + + for (int i = 0; i < NAMES.length; i++) { + result[i] = NAMES[i] + suffex; + } + + return result; + } + + + public String toString() { + return name; + } + + public String getName() { + return unlocalizedName; + } + + public String getTranslationKey() { + return unlocalizedName; + } + + public int getMetadata() { + return meta; + } + + public boolean canInteract(Entity e) { + if (this == ENCHANTED) { + return true; + } + + if (e instanceof EntityPlayer) { + + if (this == PACKED) { + return true; + } + + return Predicates.INTERACT_WITH_CLOUDS.test((EntityPlayer)e) + || EntityCloud.getFeatherEnchantStrength((EntityPlayer)e) > 0; + } + + if (e instanceof EntityItem) { + return Predicates.ITEM_INTERACT_WITH_CLOUDS.test((EntityItem)e); + } + + if (e instanceof EntityCloud && e.isRiding()) { + return canInteract(e.getRidingEntity()); + } + + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/Predicates.java b/src/main/java/com/minelittlepony/unicopia/Predicates.java new file mode 100644 index 00000000..f36e4481 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/Predicates.java @@ -0,0 +1,39 @@ +package com.minelittlepony.unicopia; + +import com.google.common.base.Predicate; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; + +public final class Predicates { + public static final Predicate INTERACT_WITH_CLOUDS = player -> { + return player != null && PlayerSpeciesList.instance().getPlayer(player).getPlayerSpecies().canInteractWithClouds(); + }; + public static final Predicate ITEM_INTERACT_WITH_CLOUDS = item -> { + return item != null && PlayerSpeciesList.instance().getEntity(item).getPlayerSpecies().canInteractWithClouds(); + }; + + public static final Predicate ENTITY_INTERACT_WITH_CLOUDS = entity -> { + return entity != null && ( + (entity instanceof EntityPlayer && INTERACT_WITH_CLOUDS.test((EntityPlayer)entity)) + || (entity instanceof EntityItem && ITEM_INTERACT_WITH_CLOUDS.test((EntityItem)entity)) + ); + }; + + public static EntityPlayer getPlayerFromEntity(Entity entity) { + if (entity instanceof EntityPlayer) { + return (EntityPlayer) entity; + } + + if (entity instanceof EntityItem) { + EntityItem item = (EntityItem)entity; + if (item.getOwner() != null) { + return item.getEntityWorld().getPlayerEntityByName(item.getOwner()); + } + } + + return null; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index 19970abc..df8c30b3 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -48,6 +48,10 @@ public enum Race { return earth; } + public boolean canInteractWithClouds() { + return canFly() && this != CHANGELING; + } + public String getDisplayString() { return I18n.format(getTranslationString()); } @@ -62,7 +66,7 @@ public enum Race { || getDisplayString().equalsIgnoreCase(s); } - public static Race fromName(String s) { + public static Race fromName(String s, Race def) { if (!Strings.isNullOrEmpty(s)) { for (Race i : values()) { if (i.isSameAs(s)) return i; @@ -73,7 +77,7 @@ public enum Race { return fromId(Integer.parseInt(s)); } catch (NumberFormatException e) { } - return HUMAN; + return def; } public static Race fromId(int id) { diff --git a/src/main/java/com/minelittlepony/unicopia/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/UBlocks.java new file mode 100644 index 00000000..5ff0ae82 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UBlocks.java @@ -0,0 +1,21 @@ +package com.minelittlepony.unicopia; + +import com.minelittlepony.unicopia.block.BlockCloud; +import com.minelittlepony.unicopia.block.BlockCloudSlab; +import com.minelittlepony.unicopia.block.BlockCloudStairs; + +import net.minecraft.block.Block; +import net.minecraftforge.registries.IForgeRegistry; + +public class UBlocks { + public static final BlockCloud cloud = new BlockCloud(UMaterials.cloud, Unicopia.MODID, "cloud_block"); + + public static final BlockCloudStairs stairsCloud = new BlockCloudStairs(UBlocks.cloud.getDefaultState(), Unicopia.MODID, "cloud_stairs"); + + public static final BlockCloudSlab cloud_double_slab = new BlockCloudSlab(true, UMaterials.cloud, Unicopia.MODID, "cloud_double_slab"); + public static final BlockCloudSlab cloud_slab = new BlockCloudSlab(false, UMaterials.cloud, Unicopia.MODID, "cloud_slab"); + + static void registerBlocks(IForgeRegistry registry) { + registry.registerAll(cloud, stairsCloud, cloud_double_slab, cloud_slab); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/UEntities.java b/src/main/java/com/minelittlepony/unicopia/UEntities.java new file mode 100644 index 00000000..8c2baa5d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UEntities.java @@ -0,0 +1,34 @@ +package com.minelittlepony.unicopia; + +import com.minelittlepony.unicopia.entity.EntityCloud; +import com.minelittlepony.unicopia.entity.EntityConstructionCloud; +import com.minelittlepony.unicopia.entity.EntityRacingCloud; +import com.minelittlepony.unicopia.entity.EntityWildCloud; +import com.minelittlepony.unicopia.render.RenderCloud; + +import net.minecraft.entity.EntityList.EntityEggInfo; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.client.registry.RenderingRegistry; +import net.minecraftforge.fml.common.registry.EntityEntry; +import net.minecraftforge.registries.IForgeRegistry; + +public class UEntities { + private static final int BRUSHES_ROYALBLUE = 4286945; + private static final int BRUSHES_CHARTREUSE = 8388352; + + static void init(IForgeRegistry registry) { + EntityEntry entry = new EntityEntry(EntityCloud.class, "cloud").setRegistryName(Unicopia.MODID, "cloud"); + + entry.setEgg(new EntityEggInfo(new ResourceLocation("unicopia", "cloud"), BRUSHES_ROYALBLUE, BRUSHES_CHARTREUSE)); + + registry.register(entry); + + registry.register(new EntityEntry(EntityWildCloud.class, "wild_cloud").setRegistryName(Unicopia.MODID, "wild_cloud")); + registry.register(new EntityEntry(EntityRacingCloud.class, "racing_cloud").setRegistryName(Unicopia.MODID, "racing_cloud")); + registry.register(new EntityEntry(EntityConstructionCloud.class, "construction_cloud").setRegistryName(Unicopia.MODID, "construction_cloud")); + } + + static void preInit() { + RenderingRegistry.registerEntityRenderingHandler(EntityCloud.class, manager -> new RenderCloud(manager)); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/UItems.java b/src/main/java/com/minelittlepony/unicopia/UItems.java index 64defff0..80fb93d4 100644 --- a/src/main/java/com/minelittlepony/unicopia/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/UItems.java @@ -1,38 +1,77 @@ package com.minelittlepony.unicopia; import com.minelittlepony.unicopia.item.ItemApple; +import com.minelittlepony.unicopia.item.ItemCloud; +import com.minelittlepony.unicopia.item.UItemBlock; +import com.minelittlepony.unicopia.item.UItemMultiTexture; +import com.minelittlepony.unicopia.item.UItemSlab; import come.minelittlepony.unicopia.forgebullshit.RegistryLockSpinner; import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.creativetab.CreativeTabs; import net.minecraft.init.Items; 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; +import net.minecraftforge.registries.IForgeRegistry; + +import static com.minelittlepony.unicopia.Predicates.*; 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() { + public static final Item cloud_matter = new Item() + .setCreativeTab(CreativeTabs.MATERIALS) + .setTranslationKey("cloud_matter") + .setRegistryName(Unicopia.MODID, "cloud_matter"); + + public static final Item dew_drop = new Item() + .setCreativeTab(CreativeTabs.MATERIALS) + .setTranslationKey("dew_drop") + .setRegistryName(Unicopia.MODID, "dew_drop"); + + public static final ItemCloud cloud_spawner = new ItemCloud(Unicopia.MODID, "cloud"); + + public static final Item cloud_block = new UItemMultiTexture(UBlocks.cloud, stack -> { + return CloudType.byMetadata(stack.getMetadata()).getTranslationKey(); + }, INTERACT_WITH_CLOUDS).setRegistryName(Unicopia.MODID, "cloud_block"); + + public static final Item cloud_stairs = new UItemBlock(UBlocks.stairsCloud, INTERACT_WITH_CLOUDS) + .setTranslationKey("cloud_stairs") + .setRegistryName(Unicopia.MODID, "cloud_stairs"); + + public static final Item cloud_slab = new UItemSlab(UBlocks.cloud_slab, UBlocks.cloud_slab, UBlocks.cloud_double_slab, INTERACT_WITH_CLOUDS) + .setTranslationKey("cloud_slab") + .setRegistryName(Unicopia.MODID, "cloud_slab"); + + static void registerItems(IForgeRegistry registry) { RegistryLockSpinner.unlock(Item.REGISTRY); - ResourceLocation res = new ResourceLocation("apple"); - - Item.REGISTRY.register(Item.getIdFromItem(Items.APPLE), res, apple); - - if (UClient.isClientSide()) { - String[] variants = apple.getVariants(); - - for (int i = 0; i < variants.length; i++) { - ModelLoader.setCustomModelResourceLocation(apple, i, new ModelResourceLocation("unicopia:" + variants[i])); - } - // ModelBakery.registerItemVariants(apple, NoNameSpacedResource.ofAllDomained("unicopia", apple.getVariants())); - } + Item.REGISTRY.register(Item.getIdFromItem(Items.APPLE), new ResourceLocation("apple"), apple); RegistryLockSpinner.lock(Item.REGISTRY); + + registry.registerAll(cloud_spawner, dew_drop, cloud_matter, cloud_block, cloud_stairs, cloud_slab); + + if (UClient.isClientSide()) { + registerAllVariants(apple, apple.getVariants()); + registerAllVariants(cloud_spawner, "cloud_small", "cloud_medium", "cloud_large"); + registerAllVariants(dew_drop, "dew_drop"); + registerAllVariants(cloud_matter, "cloud_matter"); + registerAllVariants(cloud_stairs, "cloud_stairs"); + registerAllVariants(cloud_slab, CloudType.getVariants("_cloud_slab")); + registerAllVariants(cloud_block, CloudType.getVariants("_cloud_block")); + } + } + + private static void registerAllVariants(Item item, String... variants) { + for (int i = 0; i < variants.length; i++) { + ModelLoader.setCustomModelResourceLocation(item, i, new ModelResourceLocation("unicopia:" + variants[i])); + } } static void registerFuels() { diff --git a/src/main/java/com/minelittlepony/unicopia/UMaterials.java b/src/main/java/com/minelittlepony/unicopia/UMaterials.java new file mode 100644 index 00000000..94744efd --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UMaterials.java @@ -0,0 +1,8 @@ +package com.minelittlepony.unicopia; + +import net.minecraft.block.material.MapColor; +import net.minecraft.block.material.Material; + +public class UMaterials { + public static final Material cloud = (new Material(MapColor.SNOW)); +} diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java index 798a9d89..fd5dfac3 100644 --- a/src/main/java/com/minelittlepony/unicopia/Unicopia.java +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -1,12 +1,15 @@ package com.minelittlepony.unicopia; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.item.EnumAction; import net.minecraft.item.Item; -import net.minecraftforge.client.event.EntityViewRenderEvent; +import net.minecraftforge.client.event.FOVUpdateEvent; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.event.entity.item.ItemTossEvent; +import net.minecraftforge.event.entity.player.PlayerDropsEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; @@ -19,6 +22,7 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; +import net.minecraftforge.fml.common.registry.EntityEntry; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -26,6 +30,7 @@ import com.minelittlepony.jumpingcastle.api.IChannel; import com.minelittlepony.jumpingcastle.api.JumpingCastle; import com.minelittlepony.jumpingcastle.api.Target; import com.minelittlepony.unicopia.client.particle.EntityMagicFX; +import com.minelittlepony.unicopia.client.particle.EntityRaindropFX; import com.minelittlepony.unicopia.client.particle.Particles; import com.minelittlepony.unicopia.command.Commands; import com.minelittlepony.unicopia.input.Keyboard; @@ -47,10 +52,13 @@ public class Unicopia { public static IChannel channel; public static int MAGIC_PARTICLE; + public static int RAIN_PARTICLE; @EventHandler public void preInit(FMLPreInitializationEvent event) { - + if (UClient.isClientSide()) { + UEntities.preInit(); + } } @EventHandler @@ -68,6 +76,7 @@ public class Unicopia { .consume(MsgPlayerAbility.class); MAGIC_PARTICLE = Particles.instance().registerParticle(new EntityMagicFX.Factory()); + RAIN_PARTICLE = Particles.instance().registerParticle(new EntityRaindropFX.Factory()); PowersRegistry.instance().init(); @@ -76,7 +85,17 @@ public class Unicopia { @SubscribeEvent public static void registerItemsStatic(RegistryEvent.Register event) { - UItems.registerItems(); + UItems.registerItems(event.getRegistry()); + } + + @SubscribeEvent + public static void registerBlocksStatic(RegistryEvent.Register event) { + UBlocks.registerBlocks(event.getRegistry()); + } + + @SubscribeEvent + public static void registerEntitiesStatic(RegistryEvent.Register event) { + UEntities.init(event.getRegistry()); } @SideOnly(Side.CLIENT) @@ -96,6 +115,23 @@ public class Unicopia { } } + @SubscribeEvent + public static void onPlayerTossItem(ItemTossEvent event) { + Race race = PlayerSpeciesList.instance().getPlayer(event.getPlayer()).getPlayerSpecies(); + + PlayerSpeciesList.instance().getEntity(event.getEntityItem()).setPlayerSpecies(race); + } + + @SubscribeEvent + public static void onPlayerDropItems(PlayerDropsEvent event) { + + Race race = PlayerSpeciesList.instance().getPlayer(event.getEntityPlayer()).getPlayerSpecies(); + + event.getDrops().stream().map(PlayerSpeciesList.instance()::getEntity).forEach(item -> { + item.setPlayerSpecies(race); + }); + } + @SubscribeEvent public static void onPlayerFall(PlayerFlyableFallEvent event) { PlayerSpeciesList.instance() @@ -119,12 +155,12 @@ public class Unicopia { } @SubscribeEvent - public static void modifyFOV(EntityViewRenderEvent.FOVModifier event) { - float fov = event.getFOV(); + public static void modifyFOV(FOVUpdateEvent event) { + float fov = event.getFov(); - fov += PlayerSpeciesList.instance().getPlayer(Minecraft.getMinecraft().player).getExertion() * 5; + fov += PlayerSpeciesList.instance().getPlayer(event.getEntity()).getExertion() / 5; - event.setFOV(fov); + event.setNewfov(fov); } @SubscribeEvent diff --git a/src/main/java/com/minelittlepony/unicopia/block/BlockCloud.java b/src/main/java/com/minelittlepony/unicopia/block/BlockCloud.java new file mode 100644 index 00000000..7f2c9b8b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/BlockCloud.java @@ -0,0 +1,186 @@ +package com.minelittlepony.unicopia.block; + +import java.util.List; + +import javax.annotation.Nullable; + +import com.minelittlepony.unicopia.CloudType; + +import net.minecraft.block.Block; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.NonNullList; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockCloud extends Block implements ICloudBlock { + + public static final PropertyEnum VARIANT = PropertyEnum.create("variant", CloudType.class); + + public BlockCloud(Material material, String domain, String name) { + super(material); + setCreativeTab(CreativeTabs.MISC); + setHardness(0.5f); + setResistance(1.0F); + setSoundType(SoundType.CLOTH); + setRegistryName(domain, name); + setLightOpacity(20); + setTranslationKey(name); + useNeighborBrightness = true; + } + + @Override + public boolean isTranslucent(IBlockState state) { + return true; + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return false; + } + + @Override + //Push player out of block + public boolean isFullCube(IBlockState state) { + return false; + } + + @Override + public boolean isNormalCube(IBlockState state) { + return false; + } + + @Override + public BlockRenderLayer getRenderLayer() { + return BlockRenderLayer.TRANSLUCENT; + } + + @Deprecated + @Override + public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side) { + return super.shouldSideBeRendered(blockState, blockAccess, pos, side); + } + + @Override + public boolean doesSideBlockRendering(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing face) { + + IBlockState beside = world.getBlockState(pos.offset(face)); + + if (beside.getBlock() instanceof ICloudBlock) { + ICloudBlock cloud = ((ICloudBlock)beside.getBlock()); + + if (cloud.getCloudMaterialType(beside) == getCloudMaterialType(state)) { + return true; + } + } + + return super.doesSideBlockRendering(state, world, pos, face); + } + + //Can entities walk through? + @Override + public boolean isPassable(IBlockAccess w, BlockPos pos) { + return super.isPassable(w, pos); + } + + @Override + public void onFallenUpon(World world, BlockPos pos, Entity entityIn, float fallDistance) { + if (entityIn.isSneaking()) { + super.onFallenUpon(world, pos, entityIn, fallDistance); + } else { + entityIn.fall(fallDistance, 0); + } + } + + @Override + public void onLanded(World worldIn, Entity entity) { + if (entity.isSneaking()) { + super.onLanded(worldIn, entity); + } else if (entity.motionY < 0) { + if (Math.abs(entity.motionY) >= 0.25) { + entity.motionY = -entity.motionY * 1.2; + } else { + entity.motionY = 0; + } + } + } + + @Override + public void onEntityCollision(World w, BlockPos pos, IBlockState state, Entity entity) { + if (getCanInteract(state, entity)) { + if (!entity.isSneaking() && Math.abs(entity.motionY) >= 0.25) { + entity.motionY += 0.0155 * (entity.fallDistance < 1 ? 1 : entity.fallDistance); + } else { + entity.motionY = 0; + } + + super.onEntityCollision(w, pos, state, entity); + } + } + + @Override + public boolean canEntityDestroy(IBlockState state, IBlockAccess world, BlockPos pos, Entity entity) { + return getCanInteract(state, entity) && super.canEntityDestroy(state, world, pos, entity); + } + + @Deprecated + public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List collidingBoxes, @Nullable Entity entity, boolean p_185477_7_) { + if (getCanInteract(state, entity)) { + super.addCollisionBoxToList(state, worldIn, pos, entityBox, collidingBoxes, entity, p_185477_7_); + } + } + + @Deprecated + @Override + public float getPlayerRelativeBlockHardness(IBlockState state, EntityPlayer player, World worldIn, BlockPos pos) { + if (!CloudType.NORMAL.canInteract(player)) { + return -1; + } + return super.getPlayerRelativeBlockHardness(state, player, worldIn, pos); + } + + @Override + public int damageDropped(IBlockState state) { + return ((CloudType)state.getValue(VARIANT)).getMetadata(); + } + + @Override + public void getSubBlocks(CreativeTabs tab, NonNullList list) { + for (CloudType i : CloudType.values()) { + list.add(new ItemStack(this, 1, i.getMetadata())); + } + } + + @Override + public IBlockState getStateFromMeta(int meta) { + return getDefaultState().withProperty(VARIANT, CloudType.byMetadata(meta)); + } + + @Override + public int getMetaFromState(IBlockState state) { + return ((CloudType)state.getValue(VARIANT)).getMetadata(); + } + + @Override + protected BlockStateContainer createBlockState() { + return new BlockStateContainer(this, new IProperty[] {VARIANT}); + } + + @Override + public CloudType getCloudMaterialType(IBlockState blockState) { + return (CloudType)blockState.getValue(VARIANT); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/BlockCloudSlab.java b/src/main/java/com/minelittlepony/unicopia/block/BlockCloudSlab.java new file mode 100644 index 00000000..aec408f6 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/BlockCloudSlab.java @@ -0,0 +1,227 @@ +package com.minelittlepony.unicopia.block; + +import java.util.List; +import java.util.Random; + +import javax.annotation.Nullable; + +import com.minelittlepony.unicopia.CloudType; +import com.minelittlepony.unicopia.UBlocks; + +import net.minecraft.block.BlockSlab; +import net.minecraft.block.BlockStairs; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockStateContainer; +import net.minecraft.block.state.IBlockState; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockCloudSlab extends BlockSlab implements ICloudBlock { + + public static final PropertyEnum VARIANT = PropertyEnum.create("variant", CloudType.class); + + private boolean isDouble; + + public BlockCloudSlab(boolean isDouble, Material material, String domain, String name) { + super(material); + setCreativeTab(CreativeTabs.BUILDING_BLOCKS); + setHardness(0.5F); + setResistance(1.0F); + setSoundType(SoundType.CLOTH); + setLightOpacity(20); + setTranslationKey(name); + setRegistryName(domain, name); + this.isDouble = isDouble; + useNeighborBrightness = true; + } + + @Override + public boolean isTranslucent(IBlockState state) { + return UBlocks.cloud.isTranslucent(state); + } + + @Override + public boolean isOpaqueCube(IBlockState state) { + return isDouble() ? UBlocks.cloud.isOpaqueCube(state) : false; + } + + @Override + public boolean isFullCube(IBlockState state) { + return isDouble() ? UBlocks.cloud.isFullCube(state) : false; + } + + @Override + public boolean isNormalCube(IBlockState state) { + return isDouble() ? UBlocks.cloud.isNormalCube(state) : false; + } + + @Override + public BlockRenderLayer getRenderLayer() { + return UBlocks.cloud.getRenderLayer(); + } + + @Override + public boolean isPassable(IBlockAccess worldIn, BlockPos pos) { + return super.isPassable(worldIn, pos); + } + + @Override + public void onFallenUpon(World w, BlockPos pos, Entity entity, float fallDistance) { + UBlocks.cloud.onFallenUpon(w, pos, entity, fallDistance); + } + + @Override + public void onLanded(World w, Entity entity) { + UBlocks.cloud.onLanded(w, entity); + } + + @Override + public void onEntityCollision(World w, BlockPos pos, IBlockState state, Entity entity) { + UBlocks.cloud.onEntityCollision(w, pos, state, entity); + } + + @Override + @Deprecated + public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List collidingBoxes, @Nullable Entity entity, boolean p_185477_7_) { + if (getCanInteract(state, entity)) { + super.addCollisionBoxToList(state, worldIn, pos, entityBox, collidingBoxes, entity, p_185477_7_); + } + } + + @Deprecated + @Override + public float getPlayerRelativeBlockHardness(IBlockState state, EntityPlayer player, World worldIn, BlockPos pos) { + return UBlocks.cloud.getPlayerRelativeBlockHardness(state, player, worldIn, pos); + } + + @Override + public boolean doesSideBlockRendering(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing face) { + + IBlockState beside = world.getBlockState(pos.offset(face)); + + if (beside.getBlock() instanceof ICloudBlock) { + ICloudBlock cloud = ((ICloudBlock)beside.getBlock()); + + if (cloud.getCloudMaterialType(beside) == getCloudMaterialType(state)) { + if (isDouble) { + return true; + } + + if (face == EnumFacing.UP || face == EnumFacing.DOWN) { + return true; + } + + if (beside.getBlock() == this) { + return beside.getValue(HALF) == state.getValue(HALF); + } else { + if (beside.getBlock() instanceof BlockCloudStairs) { + return beside.getValue(BlockStairs.HALF).ordinal() == state.getValue(HALF).ordinal() + && beside.getValue(BlockStairs.FACING) == face; + } + } + } + } + + return false; + } + + @Override + public boolean canEntityDestroy(IBlockState state, IBlockAccess world, BlockPos pos, Entity entity) { + return UBlocks.cloud.canEntityDestroy(state, world, pos, entity); + } + + @Override + public Item getItemDropped(IBlockState state, Random rand, int fortune) { + return Item.getItemFromBlock(UBlocks.cloud_slab); + } + + @Override + public ItemStack getPickBlock(IBlockState state, RayTraceResult target, World world, BlockPos pos, EntityPlayer player) { + return new ItemStack(Item.getItemFromBlock(UBlocks.cloud_slab), 2, getMetaFromState(state)); + } + + @Override + public String getTranslationKey(int meta) { + return super.getTranslationKey() + "." + CloudType.byMetadata(meta).getTranslationKey(); + } + + @Override + public IProperty getVariantProperty() { + return VARIANT; + } + + public Object getVariant(ItemStack stack) { + return CloudType.byMetadata(stack.getMetadata() & 7); + } + + @Override + public void getSubBlocks(CreativeTabs tab, NonNullList list) { + for (CloudType i : CloudType.values()) { + list.add(new ItemStack(this, 1, i.getMetadata())); + } + } + + + @Override + public CloudType getCloudMaterialType(IBlockState blockState) { + return (CloudType)blockState.getValue(VARIANT); + } + + @Override + public IBlockState getStateFromMeta(int meta) { + IBlockState state = getDefaultState().withProperty(VARIANT, CloudType.byMetadata(meta & 7)); + if (!isDouble()) { + state = state.withProperty(HALF, (meta & 8) == 0 ? BlockSlab.EnumBlockHalf.BOTTOM : BlockSlab.EnumBlockHalf.TOP); + } + return state; + } + + /** + * Convert the BlockState into the correct metadata value + */ + @Override + public int getMetaFromState(IBlockState state) { + byte mask = 0; + int result = mask | getCloudMaterialType(state).getMetadata(); + if (!isDouble() && state.getValue(HALF) == BlockSlab.EnumBlockHalf.TOP) { + result |= 8; + } + return result; + } + + @Override + protected BlockStateContainer createBlockState() { + return new BlockStateContainer(this, isDouble() ? + new IProperty[] {VARIANT} + : new IProperty[] {HALF, VARIANT}); + } + + @Override + public int damageDropped(IBlockState state) { + return getCloudMaterialType(state).getMetadata(); + } + + @Override + public boolean isDouble() { + return isDouble; + } + + @Override + public Comparable getTypeForItem(ItemStack stack) { + return CloudType.byMetadata(stack.getMetadata() & 7); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/BlockCloudStairs.java b/src/main/java/com/minelittlepony/unicopia/block/BlockCloudStairs.java new file mode 100644 index 00000000..8039ac9c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/BlockCloudStairs.java @@ -0,0 +1,133 @@ +package com.minelittlepony.unicopia.block; + +import java.util.List; + +import javax.annotation.Nullable; + +import com.minelittlepony.unicopia.CloudType; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockSlab; +import net.minecraft.block.BlockStairs; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public class BlockCloudStairs extends BlockStairs implements ICloudBlock { + + protected Block theBlock; + protected IBlockState theState; + + public BlockCloudStairs(IBlockState inherited, String domain, String name) { + super(inherited); + setTranslationKey(name); + setRegistryName(domain, name); + theBlock = inherited.getBlock(); + theState = inherited; + useNeighborBrightness = true; + + fullBlock = isOpaqueCube(inherited); + } + + @Override + @Deprecated + public boolean isTranslucent(IBlockState state) { + return theBlock.isTranslucent(state); + } + + @Override + @Deprecated + public boolean isOpaqueCube(IBlockState state) { + return false; + } + + @Override + @Deprecated + public boolean isNormalCube(IBlockState state) { + return theBlock.isNormalCube(state); + } + + @Override + public boolean isPassable(IBlockAccess worldIn, BlockPos pos) { + return theBlock.isPassable(worldIn, pos); + } + + @Override + public void onFallenUpon(World w, BlockPos pos, Entity entity, float fallDistance) { + theBlock.onFallenUpon(w, pos, entity, fallDistance); + } + + @Override + public void onLanded(World w, Entity entity) { + theBlock.onLanded(w, entity); + } + + @Override + public void onEntityCollision(World w, BlockPos pos, IBlockState state, Entity entity) { + theBlock.onEntityCollision(w, pos, theState, entity); + } + + @Override + public void onEntityWalk(World w, BlockPos pos, Entity entity) { + theBlock.onEntityWalk(w, pos, entity); + } + + @Override + public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List collidingBoxes, @Nullable Entity entity, boolean p_185477_7_) { + if (getCanInteract(theState, entity)) { + super.addCollisionBoxToList(state, worldIn, pos, entityBox, collidingBoxes, entity, p_185477_7_); + } + } + + @Override + public boolean canEntityDestroy(IBlockState state, IBlockAccess world, BlockPos pos, Entity entity) { + return theBlock.canEntityDestroy(state, world, pos, entity); + } + + @Deprecated + @Override + public float getPlayerRelativeBlockHardness(IBlockState state, EntityPlayer player, World worldIn, BlockPos pos) { + return theBlock.getPlayerRelativeBlockHardness(state, player, worldIn, pos); + } + + @Override + public boolean doesSideBlockRendering(IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing face) { + + IBlockState beside = world.getBlockState(pos.offset(face)); + + if (beside.getBlock() instanceof ICloudBlock) { + ICloudBlock cloud = ((ICloudBlock)beside.getBlock()); + + EnumFacing front = state.getValue(FACING); + EnumHalf half = state.getValue(HALF); + + if (cloud.getCloudMaterialType(beside) == getCloudMaterialType(state)) { + if (beside.getBlock() == this) { + EnumFacing bfront = beside.getValue(FACING); + EnumHalf bhalf = beside.getValue(HALF); + + return (bfront == front && front != face && half == bhalf) + || (bfront.getOpposite() == front && front == face); + } else { + if (beside.getBlock() instanceof BlockCloudSlab) { + return beside.getValue(BlockSlab.HALF).ordinal() == half.ordinal(); + } + } + + return true; + } + } + + return false; + } + + @Override + public CloudType getCloudMaterialType(IBlockState blockState) { + return CloudType.NORMAL; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/block/ICloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ICloudBlock.java new file mode 100644 index 00000000..f5a1b25e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/ICloudBlock.java @@ -0,0 +1,16 @@ +package com.minelittlepony.unicopia.block; + +import com.minelittlepony.unicopia.CloudType; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; + +public interface ICloudBlock { + + CloudType getCloudMaterialType(IBlockState blockState); + + default boolean getCanInteract(IBlockState state, Entity e) { + return getCloudMaterialType(state).canInteract(e); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/EntityParticleEmitter.java b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityParticleEmitter.java new file mode 100644 index 00000000..2009b336 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityParticleEmitter.java @@ -0,0 +1,71 @@ +package com.minelittlepony.unicopia.client.particle; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; + +public class EntityParticleEmitter { + + public void emitDestructionParticles(Entity e, IBlockState blockState) { + float f = 0.1f; + int total = 64 * (int)(e.width * e.height * e.width); + for (int i = 0; i < total; i++) { + double x = MathHelper.nextDouble(e.getEntityWorld().rand, e.posX - e.width/2 - f, e.posX + e.width/2 + f); + double y = MathHelper.nextDouble(e.getEntityWorld().rand, e.posY - f, e.posY + e.height + f); + double z = MathHelper.nextDouble(e.getEntityWorld().rand, e.posZ - e.width/2 - f, e.posZ + e.width/2 + f); + spawnDigginFX(e.getEntityWorld(), x, y, z, x - (int)x - 0.5, y - (int)y - 0.5, z - (int)z - 0.5, blockState, 1, 1); + } + } + + public void emitDiggingParticles(Entity e, IBlockState blockState) { + for (int side = 0; side < 6; side++) { + addBlockHitEffectsToEntity(e, blockState, side); + } + } + + private void addBlockHitEffectsToEntity(Entity e, IBlockState blockState, int side) { + side = side % 6; + float f = 0.25f; + double x = MathHelper.nextDouble(e.getEntityWorld().rand, e.posX - e.width/2 - f, e.posX + e.width/2 + f); + double y = MathHelper.nextDouble(e.getEntityWorld().rand, e.posY - f, e.posY + e.height + f); + double z = MathHelper.nextDouble(e.getEntityWorld().rand, e.posZ - e.width/2 - f, e.posZ + e.width/2 + f); + + double vX = 0; + double vY = 0; + double vZ = 0; + + if (side == 0) y = e.posY - f; + if (side == 1) { + y = e.posY + e.height + f; + vY += 0.5; + } + if (side == 2) { + z = e.posZ - e.width/2 - f; + vZ -= 0.5; + } + if (side == 3) { + z = e.posZ + e.width/2 + f; + vZ += 0.5; + } + if (side == 4) { + x = e.posX - e.width/2 - f; + vX -= 0.5; + } + if (side == 5) { + x = e.posX + e.width/2 + f; + vX += 0.5; + } + + spawnDigginFX(e.getEntityWorld(), x, y, z, vX, vY, vZ, blockState, 0.2F, 0.6F); + } + + protected void spawnDigginFX(World w, double x, double y, double z, double vX, double vY, double vZ, IBlockState blockState, float multScale, float multVel) { + if (w instanceof WorldServer) { + ((WorldServer)w).spawnParticle(EnumParticleTypes.BLOCK_CRACK, false, x, y, z, 1, 0, 0, 0, Math.sqrt(vX * vX + vY * vY + vZ * vZ) * multVel, Block.getStateId(blockState)); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/EntityRaindropFX.java b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityRaindropFX.java new file mode 100644 index 00000000..e2ccbcf2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/EntityRaindropFX.java @@ -0,0 +1,33 @@ +package com.minelittlepony.unicopia.client.particle; + +import net.minecraft.client.particle.IParticleFactory; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleRain; +import net.minecraft.world.World; + +public class EntityRaindropFX extends ParticleRain { + + public EntityRaindropFX(World w, double x, double y, double z) { + super(w, x, y, z); + motionY = -0.1; + particleMaxAge += 19; + } + + public void onUpdate() { + super.onUpdate(); + + if (onGround) { + motionX *= 0.30000001192092896D; + motionY = Math.random() * 0.20000000298023224D + 0.10000000149011612D; + motionZ *= 0.30000001192092896D; + } + } + + 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 EntityRaindropFX(w, x, y, z); + } + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java b/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java index 53328054..8f4c841a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/Particles.java @@ -20,6 +20,12 @@ public class Particles { private final List registeredParticles = new ArrayList<>(); + private final EntityParticleEmitter entityEmitter = new EntityParticleEmitter(); + + public EntityParticleEmitter getEntityEmitter() { + return entityEmitter; + } + public int registerParticle(IParticleFactory factory) { int id = registeredParticles.size(); registeredParticles.add(factory); diff --git a/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java b/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java index 3be823ef..79a52e84 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java +++ b/src/main/java/com/minelittlepony/unicopia/command/CommandSpecies.java @@ -10,6 +10,7 @@ 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.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; import net.minecraft.util.math.BlockPos; @@ -34,12 +35,7 @@ class CommandSpecies extends CommandBase { private String getRacesString() { String values = ""; - for (Race i : Race.values()) { - if (PlayerSpeciesList.instance().speciesPermitted(i)) { - if (values != "") values += ", "; - values += i.toString(); - } - } + return values; } @@ -55,7 +51,9 @@ class CommandSpecies extends CommandBase { EntityPlayerMP player; int playerIndex = 1; - if (args[0].contentEquals("set")) playerIndex++; + if (args[0].contentEquals("set") || args[0].contentEquals("describe")) { + playerIndex++; + } if (args.length > playerIndex) { player = getPlayer(server, sender, args[playerIndex]); @@ -63,27 +61,25 @@ class CommandSpecies extends CommandBase { player = getCommandSenderAsPlayer(sender); } - if (args[0].contentEquals("set")) { - if (args.length >= 2) { - Race species = Race.fromName(args[1]); + if (args[0].contentEquals("set") && args.length >= 2) { + Race species = Race.fromName(args[1], null); - if (species == null) { - player.sendMessage(new TextComponentTranslation("commands.race.fail", args[1].toUpperCase())); + if (species == null) { + player.sendMessage(new TextComponentTranslation("commands.race.fail", args[1].toUpperCase())); + } else { + if (PlayerSpeciesList.instance().speciesPermitted(species, player)) { + PlayerSpeciesList.instance().getPlayer(player).setPlayerSpecies(species); + + 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 { - if (PlayerSpeciesList.instance().speciesPermitted(species)) { - PlayerSpeciesList.instance().getPlayer(player).setPlayerSpecies(species); - - 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")); - } + player.sendMessage(new TextComponentTranslation("commands.race.permission")); } } } else if (args[0].contentEquals("get")) { @@ -104,29 +100,68 @@ class CommandSpecies extends CommandBase { } else if (args[0].contentEquals("list")) { player.sendMessage(new TextComponentTranslation("commands.race.list")); - ITextComponent message = new TextComponentString(" " + getRacesString()); + ITextComponent message = new TextComponentString(getRacesString()); + + boolean first = true; + for (Race i : Race.values()) { + if (PlayerSpeciesList.instance().speciesPermitted(i, player)) { + message.appendSibling(new TextComponentString((!first ? "\n" : "") + " - " + i.name().toLowerCase())); + first = false; + } + } message.getStyle().setColor(TextFormatting.GOLD); player.sendMessage(message); - } + } else if (args[0].contentEquals("describe") && args.length >= 2) { + Race species = Race.fromName(args[1], null); + + if (species == null) { + player.sendMessage(new TextComponentTranslation("commands.race.fail", args[1].toUpperCase())); + } else { + String name = species.name().toLowerCase(); + + ITextComponent line1 = new TextComponentTranslation(String.format("commands.race.describe.%s.1", name)); + line1.getStyle().setColor(TextFormatting.YELLOW); + + player.sendMessage(line1); + + player.sendMessage(new TextComponentTranslation(String.format("commands.race.describe.%s.2", name))); + + ITextComponent line3 = new TextComponentTranslation(String.format("commands.race.describe.%s.3", name)); + line3.getStyle().setColor(TextFormatting.RED); + + player.sendMessage(line3); + } + } else { + throw new WrongUsageException(getUsage(sender)); + } } /** * 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) { + public List getTabCompletions(MinecraftServer server, ICommandSender sender, 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")) { + return getListOfStringsMatchingLastWord(args, "get", "set", "list", "describe"); + } + + if (args.length == 2 && (args[0].contentEquals("set") || args[0].contentEquals("describe"))) { ArrayList names = new ArrayList(); + + EntityPlayer player = sender instanceof EntityPlayer ? (EntityPlayer)sender : null; + for (Race i : Race.values()) { - if (PlayerSpeciesList.instance().speciesPermitted(i)) { - names.add(i.toString()); + if (args[0].contentEquals("describe") || PlayerSpeciesList.instance().speciesPermitted(i, player)) { + names.add(i.name().toLowerCase()); } } + 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)) { + } + + if ((args.length == 3 && args[0].contentEquals("set")) || (args[0].contentEquals("get") && args.length == 2)) { return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityCloud.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityCloud.java new file mode 100644 index 00000000..eebfa5ae --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityCloud.java @@ -0,0 +1,670 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.Map; +import java.util.Random; + +import com.minelittlepony.unicopia.Predicates; +import com.minelittlepony.unicopia.UBlocks; +import com.minelittlepony.unicopia.UItems; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.client.particle.Particles; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; +import net.minecraft.block.BlockCrops; +import net.minecraft.block.BlockFarmland; +import net.minecraft.block.BlockFire; +import net.minecraft.block.SoundType; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.passive.IAnimals; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Enchantments; +import net.minecraft.init.Items; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemSpade; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemSword; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class EntityCloud extends EntityFlying implements IAnimals { + + private double altitude; + + private final double baseWidth = 3f; + private final double baseHeight = 0.8f; + + public EntityCloud(World world) { + super(world); + preventEntitySpawning = false; + ignoreFrustumCheck = true; + + if (world.rand.nextInt(20) == 0 && canRainHere()) { + setRaining(); + if (world.rand.nextInt(20) == 0) { + setIsThundering(true); + } + } + + setCloudSize(1 + rand.nextInt(3)); + } + + private static final DataParameter RAINTIMER = EntityDataManager.createKey(EntityCloud.class, DataSerializers.VARINT); + private static final DataParameter THUNDERING = EntityDataManager.createKey(EntityCloud.class, DataSerializers.BOOLEAN); + private static final DataParameter SCALE = EntityDataManager.createKey(EntityCloud.class, DataSerializers.VARINT); + + private static final DataParameter STATIONARY = EntityDataManager.createKey(EntityCloud.class, DataSerializers.BOOLEAN); + + @Override + protected void entityInit() { + super.entityInit(); + dataManager.register(RAINTIMER, 0); + dataManager.register(THUNDERING, false); + dataManager.register(STATIONARY, false); + dataManager.register(SCALE, 1); + } + + @Override + protected SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.BLOCK_CLOTH_HIT; + } + + @Override + protected SoundEvent getDeathSound() { + return SoundEvents.BLOCK_CLOTH_STEP; + } + + @Override + protected Item getDropItem() { + return UItems.cloud_matter; + } + + @Override + protected boolean canTriggerWalking() { + return false; + } + + @Override + public boolean doesEntityNotTriggerPressurePlate() { + return true; + } + + @Override + public boolean canRenderOnFire() { + return false; + } + + @Override + public int getBrightnessForRender() { + return 15728640; + } + + @Override + protected boolean canDespawn() { + return !hasCustomName() && !getStationary() && !getOpaque(); + } + + @Override + public int getMaxSpawnedInChunk() { + return 6; + } + + @Override + public void onStruckByLightning(EntityLightningBolt lightningBolt) { + + } + + @Override + protected void collideWithEntity(Entity other) { + if (other instanceof EntityCloud) { + super.collideWithEntity(other); + } + } + + @Override + protected void collideWithNearbyEntities() { + + } + + protected void checkLocation() { + if (posY < world.provider.getCloudHeight() - 18) { + setLocationAndAngles(posX, world.provider.getCloudHeight() - 18, posZ, rotationYaw, rotationPitch); + } + super.collideWithNearbyEntities(); + } + + @Override + public void applyEntityCollision(Entity other) { + if (other instanceof EntityPlayer) { + if (Predicates.INTERACT_WITH_CLOUDS.test((EntityPlayer)other)) { + super.applyEntityCollision(other); + } + } else if (other instanceof EntityCloud) { + super.applyEntityCollision(other); + } + } + + public static double randomIn(Random rand, double min, double max) { + return ((max - min) * rand.nextFloat()); + } + + @Override + public void onUpdate() { + AxisAlignedBB boundingbox = getEntityBoundingBox(); + + if (getIsRaining()) { + if (world.isRemote) { + for (int i = 0; i < 30 * getCloudSize(); i++) { + double x = posX + randomIn(rand, boundingbox.minX, boundingbox.maxX) - width / 2; + double y = getEntityBoundingBox().minY + height/2; + double z = posZ + randomIn(rand, boundingbox.minX, boundingbox.maxX) - width / 2; + + int particleId = canSnowHere(new BlockPos(x, y, z)) ? EnumParticleTypes.SNOW_SHOVEL.getParticleID() : Unicopia.RAIN_PARTICLE; + + Particles.instance().spawnParticle(particleId, false, x, y, z, 0, 0, 0); + } + + AxisAlignedBB rainedArea = boundingbox + .expand(1, 0, 1) + .expand(0, -(posY - getGroundPosition(posX, posZ).getY()), 0); + + for (EntityPlayer j : world.getEntitiesWithinAABB(EntityPlayer.class, rainedArea)) { + j.world.playSound(j, j.getPosition(), SoundEvents.WEATHER_RAIN, SoundCategory.AMBIENT, 0.1F, 0.6F); + } + } + + BlockPos pos = getGroundPosition( + posX + rand.nextFloat() * width, + posZ + rand.nextFloat() * width + ); + + if (getIsThundering()) { + if (rand.nextInt(3000) == 0) { + spawnThunderbolt(pos); + } + + if (rand.nextInt(200) == 0) { + setIsThundering(false); + } + } + + IBlockState state = world.getBlockState(pos); + + if (state.getBlock() instanceof BlockFire) { + world.setBlockState(pos, Blocks.AIR.getDefaultState()); + } + + if (rand.nextInt(20) == 0) { + BlockPos below = pos.down(); + state = world.getBlockState(below); + if (state.getBlock() != null) { + if (world.canBlockFreezeWater(below)) { + world.setBlockState(below, Blocks.ICE.getDefaultState()); + } + + if (world.canSnowAt(pos, false)) { + world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState()); + } + + if (state.getBlock() instanceof BlockFarmland) { + int moisture = state.getValue(BlockFarmland.MOISTURE); + world.setBlockState(below, state.withProperty(BlockFarmland.MOISTURE, moisture + 1)); + } else if (state.getBlock() instanceof BlockCrops) { + int age = state.getValue(BlockCrops.AGE); + if (age < 7) { + world.setBlockState(below, state.withProperty(BlockCrops.AGE, age + 1), 2); + } + } + + state.getBlock().fillWithRain(world, below); + } + } + + if (setRainTimer(getRainTimer() - 1) == 0) { + if (rand.nextInt(20000) == 0) { + setDead(); + } + } + } else { + if (rand.nextInt(8000) == 0 && canRainHere()) { + setRaining(); + if (rand.nextInt(7000) == 0) { + setIsThundering(true); + } + } + } + + rotationPitch = 0; + rotationYawHead = 0; + rotationYaw = 0; + + for (Entity i : world.getEntitiesInAABBexcluding(this, boundingbox + .grow(1 / (1 + getCloudSize())), Predicates.ENTITY_INTERACT_WITH_CLOUDS)) { + if (i.posY > posY + 0.5) { + applyGravityCompensation(i); + } + } + + if (isBurning() && !dead) { + for (int i = 0; i < 5; i++) { + world.spawnParticle(EnumParticleTypes.CLOUD, + posX + randomIn(rand, boundingbox.minX, boundingbox.maxX), + posY + randomIn(rand, boundingbox.minY, boundingbox.maxY), + posZ + randomIn(rand, boundingbox.minZ, boundingbox.maxZ), 0, 0.25, 0); + } + } + + if (getStationary()) { + motionX = 0; + motionY = 0; + motionZ = 0; + } + + motionX /= (1 + getCloudSize()); + motionZ /= (1 + getCloudSize()); + + super.onUpdate(); + + hurtTime = 0; + } + + @Override + public double getMountedYOffset() { + return getEntityBoundingBox().maxY - getEntityBoundingBox().minY - 0.25; + } + + @Override + public void moveRelative(float strafe, float up, float forward, float friction) { + if (!getStationary()) { + super.moveRelative(strafe, up, forward, friction); + } + } + + @Override + public void onCollideWithPlayer(EntityPlayer player) { + if (player.posY >= posY) { + if (applyGravityCompensation(player)) { + double difX = player.posX - player.lastTickPosX; + double difZ = player.posZ - player.lastTickPosZ; + double difY = player.posY - player.lastTickPosY; + + player.distanceWalkedModified = (float)(player.distanceWalkedModified + MathHelper.sqrt(difX * difX + difZ * difZ) * 0.6); + player.distanceWalkedOnStepModified = (float)(player.distanceWalkedOnStepModified + MathHelper.sqrt(difX * difX + difY * difY + difZ * difZ) * 0.6); + + if (PlayerSpeciesList.instance().getPlayer(player).stepOnCloud()) { + SoundType soundtype = SoundType.CLOTH; + player.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch()); + } + } + } + super.onCollideWithPlayer(player); + } + + @Override + protected void updateAITasks() { + if (!getStationary()) { + super.updateAITasks(); + if (!isBeingRidden()) { + double diffY = altitude - posZ; + + if (rand.nextInt(700) == 0 || diffY*diffY < 3f) { + altitude += rand.nextInt(10) - 5; + } + + if (altitude > world.provider.getCloudHeight() - 8) { + altitude = world.provider.getCloudHeight() - 18; + } else if (altitude < 70) { + altitude = posY + 10; + } + + double var3 = altitude + 0.1D - posY; + motionX -= 0.001; + motionY += (Math.signum(var3) * 0.699999988079071D - motionY) * 0.10000000149011612D; + } + } + } + + @Override + public EnumActionResult applyPlayerInteraction(EntityPlayer player, Vec3d vec, EnumHand hand) { + if (!(isBeingRidden() || isRidingOrBeingRiddenBy(player)) && hand == EnumHand.MAIN_HAND) { + if (Predicates.INTERACT_WITH_CLOUDS.test(player)) { + + if (player.getItemInUseCount() > 0) { + return EnumActionResult.FAIL; + } + + ItemStack stack = player.getHeldItem(hand); + + if (stack != null) { + if (stack.getItem() instanceof ItemBlock || stack.getItem() == Items.SPAWN_EGG && stack.getItemDamage() == EntityList.getID(EntityCloud.class)) { + placeBlock(player, stack, hand); + return EnumActionResult.SUCCESS; + } + } + + if (!getStationary()) { + player.startRiding(this); + return EnumActionResult.SUCCESS; + } + } + } + return EnumActionResult.FAIL; + } + + @Override + public void handleStatusUpdate(byte type) { + if (type == 2) { + if (!isBurning()) { + for (int i = 0; i < 50 * getCloudSize(); i++) { + Particles.instance().getEntityEmitter().emitDiggingParticles(this, UBlocks.cloud.getDefaultState()); + } + } + } + super.handleStatusUpdate(type); + } + + @Override + public boolean attackEntityFrom(DamageSource source, float amount) { + Entity attacker = source.getImmediateSource(); + + if (attacker instanceof EntityPlayer) { + return onAttackByPlayer(source, amount, (EntityPlayer)attacker); + } + + return source == DamageSource.IN_WALL || super.attackEntityFrom(source, amount); + } + + private void placeBlock(EntityPlayer player, ItemStack stack, EnumHand hand) { + if (!world.isRemote || !(player instanceof EntityPlayerSP)) { + return; + } + + Minecraft mc = Minecraft.getMinecraft(); + + double distance = mc.playerController.getBlockReachDistance(); + + float ticks = mc.getRenderPartialTicks(); + + Vec3d eye = player.getPositionEyes(ticks); + Vec3d look = player.getLook(ticks); + Vec3d ray = eye.add(look.x * distance, look.y * distance, look.z * distance); + + AxisAlignedBB bounds = getEntityBoundingBox(); + + float s = 0.5F; + RayTraceResult trace = bounds + .contract(0, s, 0).contract(0, -s, 0) + .calculateIntercept(eye, ray); + + if (trace == null) { + return; + } + + setStationary(true); + + EnumFacing direction = trace.sideHit; + + BlockPos blockPos = new BlockPos(trace.hitVec); + + mc.objectMouseOver = new RayTraceResult(trace.hitVec, direction, blockPos); + + int oldCount = stack.getCount(); + EnumActionResult result = mc.playerController.processRightClickBlock(((EntityPlayerSP)player), (WorldClient)player.world, blockPos, direction, trace.hitVec, hand); + + if (result == EnumActionResult.SUCCESS) { + player.swingArm(hand); + + if (!stack.isEmpty() && (stack.getCount() != oldCount || mc.playerController.isInCreativeMode())) { + mc.entityRenderer.itemRenderer.resetEquippedProgress(hand); + } + } + } + + private boolean onAttackByPlayer(DamageSource source, float amount, EntityPlayer player) { + boolean canFly = PlayerSpeciesList.instance().getPlayer(player).getPlayerSpecies().canInteractWithClouds(); + boolean stat = getStationary(); + + if (stat || canFly) { + if (!isBurning()) { + for (int i = 0; i < 50 * getCloudSize(); i++) { + Particles.instance().getEntityEmitter().emitDiggingParticles(this, UBlocks.cloud.getDefaultState()); + } + } + + ItemStack stack = player.getHeldItemMainhand(); + if (stack != null && stack.getItem() instanceof ItemSword) { + return super.attackEntityFrom(source, amount); + } else if (stack != null && stack.getItem() instanceof ItemSpade) { + return super.attackEntityFrom(source, amount * 1.5f); + } else if (canFly) { + if (player.posY < posY) { + altitude += 5; + } else if (player.posY > posY) { + altitude -= 5; + } + } + } + return false; + } + + @Override + public void onDeath(DamageSource s) { + if (s == DamageSource.GENERIC || (s.getTrueSource() != null && s.getTrueSource() instanceof EntityPlayer)) { + if (!isBurning()) { + Particles.instance().getEntityEmitter().emitDestructionParticles(this, UBlocks.cloud.getDefaultState()); + } + + setDead(); + } + super.onDeath(s); + } + + @Override + protected void dropFewItems(boolean hitByPlayer, int looting) { + if (hitByPlayer) { + Item item = getDropItem(); + int amount = 2 + world.rand.nextInt(3 + looting); + for (int i = 0; i < amount; i++) { + dropItem(item, 1); + } + } + } + + @Override + public void readEntityFromNBT(NBTTagCompound tag) { + super.readEntityFromNBT(tag); + + setRainTimer(tag.getInteger("RainTimer")); + setIsThundering(tag.getBoolean("IsThundering")); + setCloudSize(tag.getByte("CloudSize")); + setStationary(tag.getBoolean("IsStationary")); + } + + @Override + public void writeEntityToNBT(NBTTagCompound tag) { + super.writeEntityToNBT(tag); + + tag.setInteger("RainTimer", getRainTimer()); + tag.setBoolean("IsThundering", getIsThundering()); + tag.setByte("CloudSize", (byte)getCloudSize()); + tag.setBoolean("IsStationary", getStationary()); + } + + protected boolean applyGravityCompensation(Entity entity) { + int floatStrength = getFloatStrength(entity); + + if (!isRidingOrBeingRiddenBy(entity) && floatStrength > 0) { + + double boundModifier = entity.fallDistance > 80 ? 80 : MathHelper.floor(entity.fallDistance * 10) / 10; + + entity.onGround = true; + entity.motionY += (((floatStrength > 2 ? 1 : floatStrength/2) * 0.699999998079071D) - entity.motionY + boundModifier * 0.7) * 0.10000000149011612D; + + if (!getStationary() && entity.motionY > 0.4 && world.rand.nextInt(900) == 0) { + spawnThunderbolt(getPosition()); + } + + if (entity instanceof EntityItem) { + entity.motionX /= 8; + entity.motionZ /= 8; + entity.motionY /= 16; + entity.setNoGravity(true); + } + + return true; + } + + return false; + } + + public int getFloatStrength(Entity entity) { + if (Predicates.ENTITY_INTERACT_WITH_CLOUDS.test(entity)) { + return 3; + } + + if (entity instanceof EntityPlayer) { + return getFeatherEnchantStrength((EntityPlayer)entity); + } + + return 0; + } + + public static int getFeatherEnchantStrength(EntityPlayer player) { + for (ItemStack stack : player.getArmorInventoryList()) { + if (stack != null) { + Map enchantments = EnchantmentHelper.getEnchantments(stack); + if (enchantments.containsKey(Enchantments.FEATHER_FALLING)) { + return (Integer)enchantments.get(Enchantments.FEATHER_FALLING); + } + } + } + return 0; + } + + private boolean canRainHere() { + return world.getBiome(new BlockPos(posX, posY, posZ)).canRain(); + } + + private boolean canSnowHere(BlockPos pos) { + return world.getBiome(pos).getTemperature(pos) <= 0.15f; + } + + public void spawnThunderbolt() { + spawnThunderbolt(getGroundPosition(posX, posZ)); + } + + public void spawnThunderbolt(BlockPos pos) { + world.addWeatherEffect(new EntityLightningBolt(world, pos.getX(), pos.getY(), pos.getZ(), false)); + } + + private BlockPos getGroundPosition(double x, double z) { + BlockPos pos = world.getTopSolidOrLiquidBlock(new BlockPos(x, posY, z)); + + if (pos.getY() >= posY) { + while (world.isValid(pos)) { + pos = pos.down(); + if (world.getBlockState(pos).isSideSolid(world, pos, EnumFacing.UP)) { + return pos.up(); + } + } + + } + return pos; + } + + public int getRainTimer() { + return dataManager.get(RAINTIMER); + } + + public int setRainTimer(int val) { + if (val < 0) val = 0; + dataManager.set(RAINTIMER, val); + return val; + } + + private void setRaining() { + setRainTimer(700 + rand.nextInt(20)); + } + + public void setIsRaining(boolean val) { + if (val) { + setRaining(); + } else { + setRainTimer(0); + } + } + + public boolean getIsRaining() { + return getRainTimer() > 0; + } + + public boolean getIsThundering() { + return dataManager.get(THUNDERING); + } + + public void setIsThundering(boolean val) { + dataManager.set(THUNDERING, val); + } + + public boolean getStationary() { + return dataManager.get(STATIONARY); + } + + public void setStationary(boolean val) { + dataManager.set(STATIONARY, val); + } + + public boolean getOpaque() { + return false; + } + + public int getCloudSize() { + int size = dataManager.get(SCALE); + updateSize(size); + return size; + } + + private void updateSize(int scale) { + setSize((float)baseWidth * scale, (float)baseHeight * scale); + } + + @Override + protected void setSize(float width, float height) { + if (width != this.width || height != this.height) { + super.setSize(width, height); + setPosition(posX, posY, posZ); + } + } + + public void setCloudSize(int val) { + val = Math.min(1, val); + updateSize(val); + dataManager.set(SCALE, val); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityConstructionCloud.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityConstructionCloud.java new file mode 100644 index 00000000..8ff15235 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityConstructionCloud.java @@ -0,0 +1,20 @@ +package com.minelittlepony.unicopia.entity; + +import net.minecraft.world.World; + +public class EntityConstructionCloud extends EntityCloud { + + public EntityConstructionCloud(World world) { + super(world); + } + + @Override + public boolean getStationary() { + return true; + } + + @Override + public boolean getOpaque() { + return true; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityRacingCloud.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityRacingCloud.java new file mode 100644 index 00000000..a062682c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityRacingCloud.java @@ -0,0 +1,57 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.List; + +import javax.annotation.Nullable; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +public class EntityRacingCloud extends EntityCloud { + + public EntityRacingCloud(World world) { + super(world); + setCloudSize(1); + } + + @Override + public boolean canBeSteered() { + return true; + } + + @Override + protected boolean canFitPassenger(Entity passenger) { + return getPassengers().size() < getCloudSize(); + } + + @Override + @Nullable + public Entity getControllingPassenger() { + List list = getPassengers(); + return list.isEmpty() ? null : list.get(0); + } + + @Override + public void onUpdate() { + Entity riddenByEntity = getControllingPassenger(); + + if (riddenByEntity != null && canPassengerSteer()) { + EntityLivingBase rider = (EntityLivingBase)riddenByEntity; + + double speed = 1.5F * rider.moveForward / 5; + double horizontalDriving = (riddenByEntity.rotationYaw - rider.moveStrafing * 90) * Math.PI / 180; + + motionX += -Math.sin(horizontalDriving) * speed; + motionZ += Math.cos(horizontalDriving) * speed; + + double pitch = riddenByEntity.rotationPitch * Math.PI / 180; + + motionY += -Math.sin(pitch) * (speed / 20); + } else { + motionY = 0; + } + + super.onUpdate(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityWildCloud.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityWildCloud.java new file mode 100644 index 00000000..c2be38d4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityWildCloud.java @@ -0,0 +1,43 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.List; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +public class EntityWildCloud extends EntityCloud { + + public EntityWildCloud(World world) { + super(world); + } + + @Override + public boolean isNotColliding() { + AxisAlignedBB boundingbox = getEntityBoundingBox(); + return checkNoEntityCollision(boundingbox, this) && world.getCollisionBoxes(this, boundingbox).isEmpty() && !world.containsAnyLiquid(boundingbox); + } + + /** + * Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity + * + * @ref World.checkNoEntityCollision(AxisAlignedBB area, Entity entity) + */ + public boolean checkNoEntityCollision(AxisAlignedBB area, Entity entity) { + List list = world.getEntitiesWithinAABBExcludingEntity(null, area); + for (Entity i : list) { + if (!i.isDead && (i.preventEntitySpawning || i instanceof EntityCloud) && i != entity && (!entity.isRiding() || !entity.isRidingOrBeingRiddenBy(i))) { + return false; + } + } + return true; + } + + @Override + public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) { + checkLocation(); + return super.onInitialSpawn(difficulty, livingdata); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/ItemCloud.java b/src/main/java/com/minelittlepony/unicopia/item/ItemCloud.java new file mode 100644 index 00000000..780b8546 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/ItemCloud.java @@ -0,0 +1,71 @@ +package com.minelittlepony.unicopia.item; + +import com.minelittlepony.unicopia.CloudSize; +import com.minelittlepony.unicopia.entity.EntityCloud; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +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.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; + +public class ItemCloud extends Item { + + public ItemCloud(String domain, String name) { + super(); + setHasSubtypes(true); + setMaxDamage(0); + setTranslationKey(name); + setRegistryName(domain, name); + setCreativeTab(CreativeTabs.MATERIALS); + + maxStackSize = 16; + } + + @Override + public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { + ItemStack stack = player.getHeldItem(hand); + + if (!world.isRemote) { + RayTraceResult mop = rayTrace(world, player, true); + + BlockPos pos; + + if (mop != null && mop.typeOfHit == RayTraceResult.Type.BLOCK) { + pos = mop.getBlockPos().offset(mop.sideHit); + } else { + pos = player.getPosition(); + } + + EntityCloud cloud = CloudSize.byMetadata(stack.getItemDamage()).createEntity(world); + cloud.moveToBlockPosAndAngles(pos, 0, 0); + world.spawnEntity(cloud); + + if (!player.capabilities.isCreativeMode) { + stack.shrink(1); + } + } + + return new ActionResult(EnumActionResult.SUCCESS, stack); + } + + @Override + public String getTranslationKey(ItemStack stack) { + return super.getTranslationKey(stack) + "." + CloudSize.byMetadata(stack.getItemDamage()).getName(); + } + + @Override + public void getSubItems(CreativeTabs tab, NonNullList subs) { + if (isInCreativeTab(tab)) { + for (CloudSize i : CloudSize.values()) { + subs.add(new ItemStack(this, 1, i.getMetadata())); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItemBlock.java b/src/main/java/com/minelittlepony/unicopia/item/UItemBlock.java new file mode 100644 index 00000000..66e3799e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/UItemBlock.java @@ -0,0 +1,32 @@ +package com.minelittlepony.unicopia.item; + +import java.util.function.Predicate; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class UItemBlock extends ItemBlock { + + private final Predicate abilityTest; + + public UItemBlock(Block block, Predicate abilityTest) { + super(block); + + this.abilityTest = abilityTest; + } + + @Override + public boolean canPlaceBlockOnSide(World worldIn, BlockPos pos, EnumFacing side, EntityPlayer player, ItemStack stack) { + if (!abilityTest.test(player)) { + return player.capabilities.isCreativeMode; + } + + return super.canPlaceBlockOnSide(worldIn, pos, side, player, stack); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItemMultiTexture.java b/src/main/java/com/minelittlepony/unicopia/item/UItemMultiTexture.java new file mode 100644 index 00000000..e3668641 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/UItemMultiTexture.java @@ -0,0 +1,32 @@ +package com.minelittlepony.unicopia.item; + +import java.util.function.Predicate; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemMultiTexture; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class UItemMultiTexture extends ItemMultiTexture { + + private final Predicate abilityTest; + + public UItemMultiTexture(Block block, ItemMultiTexture.Mapper mapper, Predicate abilityTest) { + super(block, block, mapper); + + this.abilityTest = abilityTest; + } + + @Override + public boolean canPlaceBlockOnSide(World worldIn, BlockPos pos, EnumFacing side, EntityPlayer player, ItemStack stack) { + if (!abilityTest.test(player)) { + return player.capabilities.isCreativeMode; + } + + return super.canPlaceBlockOnSide(worldIn, pos, side, player, stack); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItemSlab.java b/src/main/java/com/minelittlepony/unicopia/item/UItemSlab.java new file mode 100644 index 00000000..b5734ac5 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/UItemSlab.java @@ -0,0 +1,33 @@ +package com.minelittlepony.unicopia.item; + +import java.util.function.Predicate; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockSlab; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemSlab; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class UItemSlab extends ItemSlab { + + private final Predicate abilityTest; + + public UItemSlab(Block block, BlockSlab singleSlab, BlockSlab doubleSlab, Predicate abilityTest) { + super(block, singleSlab, doubleSlab); + + this.abilityTest = abilityTest; + } + + @Override + public boolean canPlaceBlockOnSide(World worldIn, BlockPos pos, EnumFacing side, EntityPlayer player, ItemStack stack) { + if (!abilityTest.test(player)) { + return player.capabilities.isCreativeMode; + } + + return super.canPlaceBlockOnSide(worldIn, pos, side, player, stack); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/model/ModelCloud.java b/src/main/java/com/minelittlepony/unicopia/model/ModelCloud.java new file mode 100644 index 00000000..3b74cd1c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/model/ModelCloud.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; + +public class ModelCloud extends ModelBase { + ModelRenderer body; + + public ModelCloud() { + init(); + } + + private void init() { + body = new ModelRenderer(this, 0, 0); + + body.addBox(-24, 5, -24, 48, 10, 48); + + body.addBox(-10, 14.999F, -10, 30, 2, 30); + body.addBox(-19.999F, 14.999F, -15, 15, 3, 25); + + body.addBox(-10, 3.001F, -10, 30, 2, 30); + body.rotationPointY += 4.2; + } + + public void render(Entity entityIn, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scale) { + body.render(scale); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IOwned.java b/src/main/java/com/minelittlepony/unicopia/player/IOwned.java new file mode 100644 index 00000000..a43fc034 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/IOwned.java @@ -0,0 +1,11 @@ +package com.minelittlepony.unicopia.player; + +public interface IOwned { + + default void setOwner(E owner) { + + } + + E getOwner(); + +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java b/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java index 1b4963a4..efce84a1 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java +++ b/src/main/java/com/minelittlepony/unicopia/player/IPlayer.java @@ -2,18 +2,13 @@ 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.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.fml.common.FMLCommonHandler; -public interface IPlayer extends ICaster, InbtSerialisable, IUpdatable { - Race getPlayerSpecies(); - - void setPlayerSpecies(Race race); +public interface IPlayer extends ICaster, IRaceContainer { void sendCapabilities(boolean full); @@ -33,6 +28,8 @@ public interface IPlayer extends ICaster, InbtSerialisable, IUpdat void onEntityEat(); + boolean stepOnCloud(); + void onFall(float distance, float damageMultiplier); static EntityPlayer getPlayerEntity(UUID playerId) { diff --git a/src/main/java/com/minelittlepony/unicopia/player/IRaceContainer.java b/src/main/java/com/minelittlepony/unicopia/player/IRaceContainer.java new file mode 100644 index 00000000..3d119754 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/IRaceContainer.java @@ -0,0 +1,13 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.unicopia.InbtSerialisable; +import com.minelittlepony.unicopia.Race; + +import net.minecraft.entity.Entity; + +public interface IRaceContainer extends InbtSerialisable, IUpdatable { + Race getPlayerSpecies(); + + void setPlayerSpecies(Race race); + +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java b/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java index 460cb2af..532052b1 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java +++ b/src/main/java/com/minelittlepony/unicopia/player/IUpdatable.java @@ -1,7 +1,7 @@ package com.minelittlepony.unicopia.player; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.Entity; -public interface IUpdatable { - void onUpdate(EntityPlayer entity); +public interface IUpdatable { + void onUpdate(T entity); } diff --git a/src/main/java/com/minelittlepony/unicopia/player/ItemCapabilities.java b/src/main/java/com/minelittlepony/unicopia/player/ItemCapabilities.java new file mode 100644 index 00000000..d40aa1ce --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/ItemCapabilities.java @@ -0,0 +1,49 @@ +package com.minelittlepony.unicopia.player; + +import com.minelittlepony.unicopia.Race; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.nbt.NBTTagCompound; + +class ItemCapabilities implements IRaceContainer, IOwned { + + private Race race = Race.HUMAN; + + private EntityItem owner; + + @Override + public void onUpdate(EntityItem entity) { + + } + + @Override + public Race getPlayerSpecies() { + return race; + } + + @Override + public void setPlayerSpecies(Race race) { + this.race = race; + } + + @Override + public void writeToNBT(NBTTagCompound compound) { + compound.setString("owner_species", race.name()); + } + + + @Override + public void readFromNBT(NBTTagCompound compound) { + race = Race.fromName(compound.getString("owner_species"), Race.HUMAN); + } + + @Override + public void setOwner(EntityItem owner) { + this.owner = owner; + } + + @Override + public EntityItem getOwner() { + return owner; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java index c670cfe0..71c5a7fb 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerAbilityDelegate.java @@ -11,7 +11,7 @@ import com.minelittlepony.unicopia.power.PowersRegistry; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; -class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable, InbtSerialisable { +class PlayerAbilityDelegate implements IAbilityReceiver, IUpdatable, InbtSerialisable { private final IPlayer player; diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerAttributes.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerAttributes.java new file mode 100644 index 00000000..9681e66b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerAttributes.java @@ -0,0 +1,50 @@ +package com.minelittlepony.unicopia.player; + +import java.util.UUID; + +import com.minelittlepony.unicopia.Race; + +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.IAttribute; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.player.EntityPlayer; + +class PlayerAttributes { + public static final int ADD = 0; + public static final int ADD_PERCENTAGE = 1; + public static final int MULTIPLY = 2; + + public static final PlayerAttributes instance = new PlayerAttributes(); + + private final AttributeModifier EARTH_PONY_STRENGTH = + new AttributeModifier(UUID.fromString("777a5505-521e-480b-b9d5-6ea54f259564"), "Earth Pony Strength", 0.6, MULTIPLY); + + private final AttributeModifier PEGASUS_SPEED = + new AttributeModifier(UUID.fromString("9e2699fc-3b8d-4f71-9d2d-fb92ee19b4f7"), "Pegasus Speed", 0.2, MULTIPLY); + + private final AttributeModifier PEGASUS_REACH = + new AttributeModifier(UUID.fromString("707b50a8-03e8-40f4-8553-ecf67025fd6d"), "Pegasus Reach", 1.5, ADD); + + public void applyAttributes(EntityPlayer entity, Race race) { + applyAttribute(entity, SharedMonsterAttributes.ATTACK_DAMAGE, EARTH_PONY_STRENGTH, race.canUseEarth()); + applyAttribute(entity, SharedMonsterAttributes.KNOCKBACK_RESISTANCE, EARTH_PONY_STRENGTH, race.canUseEarth()); + applyAttribute(entity, SharedMonsterAttributes.MOVEMENT_SPEED, PEGASUS_SPEED, race.canFly()); + applyAttribute(entity, SharedMonsterAttributes.ATTACK_SPEED, PEGASUS_SPEED, race.canFly()); + applyAttribute(entity, EntityPlayer.REACH_DISTANCE, PEGASUS_REACH, race.canFly()); + } + + private void applyAttribute(EntityPlayer entity, IAttribute attribute, AttributeModifier modifier, boolean enable) { + IAttributeInstance instance = entity.getEntityAttribute(attribute); + + if (enable) { + if (instance.getModifier(modifier.getID()) == null) { + instance.applyModifier(modifier); + } + } else { + if (instance.getModifier(modifier.getID()) != null) { + instance.removeModifier(modifier); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java index 71f9b87d..8d7cdad4 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerCapabilities.java @@ -36,6 +36,8 @@ class PlayerCapabilities implements IPlayer, ICaster { private final PlayerGravityDelegate gravity = new PlayerGravityDelegate(this); + private float nextStepDistance = 1; + private IMagicEffect effect; private EntityPlayer entity; @@ -61,7 +63,7 @@ class PlayerCapabilities implements IPlayer, ICaster { public void setPlayerSpecies(Race race) { EntityPlayer self = getOwner(); - if (!PlayerSpeciesList.instance().speciesPermitted(race)) { + if (!PlayerSpeciesList.instance().speciesPermitted(race, getOwner())) { race = Race.HUMAN; } @@ -128,6 +130,8 @@ class PlayerCapabilities implements IPlayer, ICaster { } addExertion(-1); + + PlayerAttributes.instance.applyAttributes(entity, getPlayerSpecies()); } @Override @@ -143,6 +147,22 @@ class PlayerCapabilities implements IPlayer, ICaster { } } + + @Override + public boolean stepOnCloud() { + EntityPlayer player = getOwner(); + + if ((player.fallDistance > 1) || player.distanceWalkedOnStepModified > nextStepDistance) { + nextStepDistance = player.distanceWalkedOnStepModified + 2; + player.fallDistance = 0; + + return true; + } + + return false; + } + + @Override public void onEntityEat() { if (getPlayerSpecies() == Race.CHANGELING) { EntityPlayer player = getOwner(); @@ -166,7 +186,7 @@ class PlayerCapabilities implements IPlayer, ICaster { @Override public void readFromNBT(NBTTagCompound compound) { - setPlayerSpecies(Race.fromName(compound.getString("playerSpecies"))); + setPlayerSpecies(Race.fromName(compound.getString("playerSpecies"), Race.HUMAN)); powers.readFromNBT(compound.getCompoundTag("powers")); gravity.readFromNBT(compound.getCompoundTag("gravity")); diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java index 7f76593e..a0060f95 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerGravityDelegate.java @@ -16,7 +16,7 @@ import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; -class PlayerGravityDelegate implements IUpdatable, InbtSerialisable { +class PlayerGravityDelegate implements IUpdatable, InbtSerialisable { private final IPlayer player; diff --git a/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java b/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java index d3ee2ef9..9210c2c0 100644 --- a/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java +++ b/src/main/java/com/minelittlepony/unicopia/player/PlayerSpeciesList.java @@ -7,6 +7,8 @@ import java.util.UUID; import com.minelittlepony.unicopia.Race; import come.minelittlepony.unicopia.forgebullshit.FBS; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; public class PlayerSpeciesList { @@ -19,12 +21,24 @@ public class PlayerSpeciesList { private List serverPermittedRaces = new ArrayList<>(); - public boolean speciesPermitted(Race race) { + public boolean speciesPermitted(Race race, EntityPlayer sender) { + if (race == Race.ALICORN && (sender == null || !sender.capabilities.isCreativeMode)) { + return false; + } + return race.isDefault() || serverPermittedRaces.isEmpty() || serverPermittedRaces.contains(race); } - public IPlayer emptyPlayer(EntityPlayer player) { - return new PlayerCapabilities(player); + public IRaceContainer emptyContainer(Entity entity) { + if (entity instanceof EntityPlayer) { + return new PlayerCapabilities((EntityPlayer)entity); + } + + if (entity instanceof EntityItem) { + return new ItemCapabilities(); + } + + throw new IllegalArgumentException("entity"); } public IPlayer getPlayer(EntityPlayer player) { @@ -34,4 +48,8 @@ public class PlayerSpeciesList { public IPlayer getPlayer(UUID playerId) { return getPlayer(IPlayer.getPlayerEntity(playerId)); } + + public IRaceContainer getEntity(T entity) { + return FBS.of(entity).getRaceContainer(); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java b/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java index c8d6b6b6..e9e43629 100644 --- a/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerFeed.java @@ -1,6 +1,5 @@ package com.minelittlepony.unicopia.power; -import java.util.ArrayList; import java.util.List; import org.lwjgl.input.Keyboard; @@ -78,10 +77,7 @@ public class PowerFeed implements IPower { @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); - } + List list = VecHelper.getWithinRange(player, 3, this::canDrain); Entity looked = VecHelper.getLookedAtEntity(player, 10); if (looked != null && !list.contains(looked)) { @@ -97,8 +93,7 @@ public class PowerFeed implements IPower { for (Entity i : list) { DamageSource d = MagicalDamageSource.causePlayerDamage("feed", player); - if (EnumCreatureType.CREATURE.getCreatureClass().isAssignableFrom(i.getClass()) - || player.world.rand.nextFloat() > 0.95f) { + if (EnumCreatureType.CREATURE.getCreatureClass().isAssignableFrom(i.getClass()) || player.world.rand.nextFloat() > 0.95f) { i.attackEntityFrom(d, Integer.MAX_VALUE); } else { i.attackEntityFrom(d, drained); diff --git a/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java b/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java index f05b868f..b5339879 100644 --- a/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java +++ b/src/main/java/com/minelittlepony/unicopia/power/PowerTeleport.java @@ -143,7 +143,7 @@ public class PowerTeleport implements IPower { @Override public void preApply(IPlayer player) { - player.addExertion(1); + player.addExertion(3); IPower.spawnParticles(Unicopia.MAGIC_PARTICLE, player, 5); } diff --git a/src/main/java/com/minelittlepony/unicopia/render/RenderCloud.java b/src/main/java/com/minelittlepony/unicopia/render/RenderCloud.java new file mode 100644 index 00000000..2639bb9f --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/render/RenderCloud.java @@ -0,0 +1,65 @@ +package com.minelittlepony.unicopia.render; + +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.opengl.GL11; + +import com.minelittlepony.unicopia.entity.EntityCloud; +import com.minelittlepony.unicopia.model.ModelCloud; + +public class RenderCloud extends RenderLiving { + private static final ResourceLocation cloud = new ResourceLocation("unicopia", "textures/entity/clouds.png"); + private static final ResourceLocation rainCloud = new ResourceLocation("unicopia", "textures/entity/clouds_storm.png"); + + public RenderCloud(RenderManager rendermanagerIn) { + super(rendermanagerIn, new ModelCloud(), 1f); + } + + public float prepareScale(EntityCloud entity, float par2) { + float scale = entity.getCloudSize(); + + GL11.glScalef(scale, scale, scale); + return 0.0625F; + } + + protected void renderModel(EntityCloud entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scaleFactor) { + + if (!entity.isDead) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0, -entity.height/2, 0); + + GL11.glEnable(GL11.GL_BLEND); + + if (!((EntityCloud)entity).getOpaque()) { + GL11.glColor4f(1, 1, 1, 0.8F); + } + + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + super.renderModel(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scaleFactor); + + GL11.glDisable(GL11.GL_BLEND); + GL11.glColor4f(1, 1, 1, 1); + + GlStateManager.popMatrix(); + } + } + + protected ResourceLocation getEntityTexture(EntityCloud entity) { + if (entity.getIsRaining() && entity.getIsThundering()) { + return rainCloud; + } + return cloud; + } + + protected int getColorMultiplier(EntityCloud par1EntityLivingBase, float yaw, float pitch) { + return 25; + } + + protected float getDeathMaxRotation(EntityCloud par1EntityLivingBase) { + return 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java index dcb2d34b..1226edbe 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/ICaster.java @@ -1,9 +1,11 @@ package com.minelittlepony.unicopia.spell; +import com.minelittlepony.unicopia.player.IOwned; + import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -public interface ICaster { +public interface ICaster extends IOwned { void setEffect(IMagicEffect effect); IMagicEffect getEffect(); @@ -12,12 +14,6 @@ public interface ICaster { return getEffect() != null; } - default void setOwner(E owner) { - - } - - E getOwner(); - default Entity getEntity() { return getOwner(); } diff --git a/src/main/java/com/minelittlepony/util/vector/VecHelper.java b/src/main/java/com/minelittlepony/util/vector/VecHelper.java index 25456a31..405dc075 100644 --- a/src/main/java/com/minelittlepony/util/vector/VecHelper.java +++ b/src/main/java/com/minelittlepony/util/vector/VecHelper.java @@ -2,6 +2,8 @@ package com.minelittlepony.util.vector; import java.util.List; +import javax.annotation.Nullable; + import com.google.common.base.Predicate; import net.minecraft.entity.Entity; @@ -23,11 +25,11 @@ public class VecHelper { * * @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); + public static RayTraceResult rayTrace(Entity e, double distance, float partialTicks) { + Vec3d pos = e.getPositionEyes(partialTicks); + Vec3d look = e.getLook(partialTicks).scale(distance); + + return e.world.rayTraceBlocks(pos, pos.add(look), false, false, true); } /** @@ -46,28 +48,14 @@ public class VecHelper { /** * 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)); - } + public static List getWithinRange(EntityPlayer player, double reach, @Nullable Predicate predicate) { + Vec3d look = player.getLook(0).scale(reach); - /** - * 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); - } + return player.world.getEntitiesInAABBexcluding(player, player + .getEntityBoundingBox() + .expand(look.x, look.y, look.z) + .expand(1, 1, 1), predicate); + } /** * Performs a ray trace from the given entity and returns a result for the first Entity or block that the ray intercepts. @@ -79,7 +67,7 @@ public class VecHelper { * @return RayTraceResult result or null */ public static RayTraceResult getObjectMouseOver(Entity e, double distance, float partialTick) { - return getObjectMouseOverExcept(e, distance, partialTick, EntitySelectors.NOT_SPECTATING); + return getObjectMouseOver(e, distance, partialTick, EntitySelectors.NOT_SPECTATING); } /** @@ -94,23 +82,28 @@ public class VecHelper { * * @return RayTraceResult result or null */ - public static RayTraceResult getObjectMouseOverExcept(Entity e, double distance, float partialTick, Predicate predicate) { + public static RayTraceResult getObjectMouseOver(Entity e, double distance, float partialTick, Predicate predicate) { RayTraceResult tracedBlock = rayTrace(e, distance, partialTick); double totalTraceDistance = distance; - Vec3d pos = geteEyePosition(e, partialTick); + Vec3d pos = e.getPositionEyes(partialTick); - if (tracedBlock != null) totalTraceDistance = tracedBlock.hitVec.distanceTo(pos); + 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 ray = pos.add(look.scale(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); + 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(); @@ -144,6 +137,7 @@ public class VecHelper { if (pointedEntity != null && (traceDistance < totalTraceDistance || tracedBlock == null)) { return new RayTraceResult(pointedEntity, hit); } + return tracedBlock; } } diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultEntityCapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultEntityCapabilitiesProxyContainer.java new file mode 100644 index 00000000..4d90c5f1 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultEntityCapabilitiesProxyContainer.java @@ -0,0 +1,49 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import com.minelittlepony.unicopia.player.IOwned; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.IRaceContainer; +import com.minelittlepony.unicopia.player.PlayerSpeciesList; + +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; + +class DefaultEntityCapabilitiesProxyContainer implements ICapabilitiesProxyContainer { + + @CapabilityInject(ICapabilitiesProxyContainer.class) + public static final Capability> CAPABILITY = null; + + private IRaceContainer container; + + + @Override + public IRaceContainer getRaceContainer() { + return container; + } + + @Override + public IPlayer getPlayer() { + return (IPlayer)container; + } + + public void writeToNBT(NBTTagCompound compound) { + container.writeToNBT(compound); + } + + public void readFromNBT(NBTTagCompound compound) { + container.readFromNBT(compound); + } + + @SuppressWarnings("unchecked") + public ICapabilitiesProxyContainer withEntity(T entity) { + if (this.container == null) { + this.container = (IRaceContainer)PlayerSpeciesList.instance().emptyContainer(entity); + } else if (container instanceof IOwned) { + ((IOwned)container).setOwner(entity); + } + + return this; + } +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java deleted file mode 100644 index 62a60b6b..00000000 --- a/src/main/java/come/minelittlepony/unicopia/forgebullshit/DefaultPlayerCapabilitiesProxyContainer.java +++ /dev/null @@ -1,49 +0,0 @@ -package come.minelittlepony.unicopia.forgebullshit; - -import com.minelittlepony.unicopia.player.IPlayer; -import com.minelittlepony.unicopia.player.PlayerSpeciesList; - -import net.minecraft.entity.player.EntityPlayer; -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() { - if (player == null) { - player = PlayerSpeciesList.instance().emptyPlayer(null); - } - - return player; - } - - @Override - public void setPlayer(IPlayer player) { - this.player = player; - } - - public void writeToNBT(NBTTagCompound compound) { - getPlayer().writeToNBT(compound); - } - - public void readFromNBT(NBTTagCompound compound) { - getPlayer().readFromNBT(compound); - } - - public IPlayerCapabilitiesProxyContainer withEntity(EntityPlayer player) { - if (this.player == null) { - this.player = PlayerSpeciesList.instance().emptyPlayer(player); - } else { - getPlayer().setOwner(player); - } - - return this; - } -} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java index cf7cd078..27f90468 100644 --- a/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/FBS.java @@ -1,6 +1,7 @@ package come.minelittlepony.unicopia.forgebullshit; import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; @@ -10,32 +11,37 @@ import net.minecraftforge.event.entity.player.PlayerEvent; public class FBS { + @SuppressWarnings({ "unchecked", "rawtypes" }) public static void init() { - CapabilityManager.INSTANCE.register(IPlayerCapabilitiesProxyContainer.class, - new Storage(), DefaultPlayerCapabilitiesProxyContainer::new); + CapabilityManager.INSTANCE.register(ICapabilitiesProxyContainer.class, + new Storage(), DefaultEntityCapabilitiesProxyContainer::new); } public static void attach(AttachCapabilitiesEvent event) { - if (event.getObject() instanceof EntityPlayer) { - event.addCapability(new ResourceLocation("unicopia", "race"), new Provider((EntityPlayer)event.getObject())); + if (event.getObject() instanceof EntityPlayer + || event.getObject() instanceof EntityItem) { + event.addCapability(new ResourceLocation("unicopia", "race"), new Provider(event.getObject())); } } public static void clone(PlayerEvent.Clone event) { - final IPlayerCapabilitiesProxyContainer original = of(event.getOriginal()); + final ICapabilitiesProxyContainer original = of(event.getOriginal()); if (original == null) { return; } - final IPlayerCapabilitiesProxyContainer clone = of(event.getEntity()); + final ICapabilitiesProxyContainer clone = of((EntityPlayer)event.getEntity()); clone.getPlayer().copyFrom(original.getPlayer()); } - public static IPlayerCapabilitiesProxyContainer of(Entity entity) { - if (entity.hasCapability(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN)) { - return entity.getCapability(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN).withEntity((EntityPlayer)entity); + @SuppressWarnings("unchecked") + public static ICapabilitiesProxyContainer of(T entity) { + if (entity.hasCapability(DefaultEntityCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN)) { + return ((ICapabilitiesProxyContainer)entity + .getCapability(DefaultEntityCapabilitiesProxyContainer.CAPABILITY, EnumFacing.DOWN)) + .withEntity(entity); } return null; diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/ICapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/ICapabilitiesProxyContainer.java new file mode 100644 index 00000000..23b47ca9 --- /dev/null +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/ICapabilitiesProxyContainer.java @@ -0,0 +1,15 @@ +package come.minelittlepony.unicopia.forgebullshit; + +import com.minelittlepony.unicopia.InbtSerialisable; +import com.minelittlepony.unicopia.player.IPlayer; +import com.minelittlepony.unicopia.player.IRaceContainer; + +import net.minecraft.entity.Entity; + +public interface ICapabilitiesProxyContainer extends InbtSerialisable { + IPlayer getPlayer(); + + IRaceContainer getRaceContainer(); + + ICapabilitiesProxyContainer withEntity(T entity); +} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java deleted file mode 100644 index 0350007a..00000000 --- a/src/main/java/come/minelittlepony/unicopia/forgebullshit/IPlayerCapabilitiesProxyContainer.java +++ /dev/null @@ -1,14 +0,0 @@ -package come.minelittlepony.unicopia.forgebullshit; - -import com.minelittlepony.unicopia.InbtSerialisable; -import com.minelittlepony.unicopia.player.IPlayer; - -import net.minecraft.entity.player.EntityPlayer; - -public interface IPlayerCapabilitiesProxyContainer extends InbtSerialisable { - IPlayer getPlayer(); - - void setPlayer(IPlayer player); - - IPlayerCapabilitiesProxyContainer withEntity(EntityPlayer player); -} diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java index 0cc0b361..6b399f13 100644 --- a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Provider.java @@ -1,29 +1,30 @@ package come.minelittlepony.unicopia.forgebullshit; -import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.Entity; 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(); + @SuppressWarnings("unchecked") + DefaultEntityCapabilitiesProxyContainer instance = (DefaultEntityCapabilitiesProxyContainer) DefaultEntityCapabilitiesProxyContainer.CAPABILITY.getDefaultInstance(); - private final EntityPlayer entity; + private final Entity entity; - Provider(EntityPlayer entity) { + Provider(Entity entity) { this.entity = entity; } @Override public boolean hasCapability(Capability capability, EnumFacing facing) { - return capability == DefaultPlayerCapabilitiesProxyContainer.CAPABILITY; + return capability == DefaultEntityCapabilitiesProxyContainer.CAPABILITY; } @Override public T getCapability(Capability capability, EnumFacing facing) { if (hasCapability(capability, facing)) { - return DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.cast(instance.withEntity(entity)); + return DefaultEntityCapabilitiesProxyContainer.CAPABILITY.cast(instance.withEntity(entity)); } return null; @@ -31,13 +32,13 @@ class Provider implements ICapabilitySerializable { @Override public NBTTagCompound serializeNBT() { - return (NBTTagCompound) DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.getStorage() - .writeNBT(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, instance.withEntity(entity), null); + return (NBTTagCompound) DefaultEntityCapabilitiesProxyContainer.CAPABILITY.getStorage() + .writeNBT(DefaultEntityCapabilitiesProxyContainer.CAPABILITY, instance.withEntity(entity), null); } @Override public void deserializeNBT(NBTTagCompound nbt) { - DefaultPlayerCapabilitiesProxyContainer.CAPABILITY.getStorage() - .readNBT(DefaultPlayerCapabilitiesProxyContainer.CAPABILITY, instance.withEntity(entity), null, nbt); + DefaultEntityCapabilitiesProxyContainer.CAPABILITY.getStorage() + .readNBT(DefaultEntityCapabilitiesProxyContainer.CAPABILITY, instance.withEntity(entity), null, nbt); } } diff --git a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java index c888646a..3eba1d75 100644 --- a/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java +++ b/src/main/java/come/minelittlepony/unicopia/forgebullshit/Storage.java @@ -1,20 +1,21 @@ package come.minelittlepony.unicopia.forgebullshit; +import net.minecraft.entity.Entity; 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 { +class Storage implements IStorage> { @Override - public NBTBase writeNBT(Capability capability, IPlayerCapabilitiesProxyContainer instance, EnumFacing side) { + public NBTBase writeNBT(Capability> capability, ICapabilitiesProxyContainer instance, EnumFacing side) { return instance.toNBT(); } @Override - public void readNBT(Capability capability, IPlayerCapabilitiesProxyContainer instance, EnumFacing side, NBTBase nbt) { + public void readNBT(Capability> capability, ICapabilitiesProxyContainer instance, EnumFacing side, NBTBase nbt) { instance.readFromNBT((NBTTagCompound)nbt); } } diff --git a/src/main/resources/assets/unicopia/blockstates/cloud_block.json b/src/main/resources/assets/unicopia/blockstates/cloud_block.json new file mode 100644 index 00000000..79e3cb5c --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/cloud_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "variant=normal": { "model": "unicopia:normal_cloud_block" }, + "variant=packed": { "model": "unicopia:packed_cloud_block" }, + "variant=enchanted": { "model": "unicopia:enchanted_cloud_block" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/cloud_double_slab.json b/src/main/resources/assets/unicopia/blockstates/cloud_double_slab.json new file mode 100644 index 00000000..a9f10d46 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/cloud_double_slab.json @@ -0,0 +1,11 @@ +{ + "variants": { + "half=bottom,variant=normal": { "model": "unicopia:normal_cloud_block" }, + "half=bottom,variant=packed": { "model": "unicopia:packed_cloud_block" }, + "half=bottom,variant=enchanted": { "model": "unicopia:enchanted_cloud_block" }, + + "half=top,variant=normal": { "model": "unicopia:normal_cloud_block" }, + "half=top,variant=packed": { "model": "unicopia:packed_cloud_block" }, + "half=top,variant=enchanted": { "model": "unicopia:enchanted_cloud_block" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/cloud_slab.json b/src/main/resources/assets/unicopia/blockstates/cloud_slab.json new file mode 100644 index 00000000..07f50f97 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/cloud_slab.json @@ -0,0 +1,10 @@ +{ + "variants": { + "half=bottom,variant=normal": { "model": "unicopia:half_slab_normal_cloud" }, + "half=top,variant=normal": { "model": "unicopia:upper_slab_normal_cloud" }, + "half=bottom,variant=packed": { "model": "unicopia:half_slab_packed_cloud" }, + "half=top,variant=packed": { "model": "unicopia:upper_slab_packed_cloud" }, + "half=bottom,variant=enchanted": { "model": "unicopia:half_slab_enchanted_cloud" }, + "half=top,variant=enchanted": { "model": "unicopia:upper_slab_enchanted_cloud" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/cloud_stairs.json b/src/main/resources/assets/unicopia/blockstates/cloud_stairs.json new file mode 100644 index 00000000..486ac99d --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/cloud_stairs.json @@ -0,0 +1,44 @@ +{ + "variants": { + "facing=east,half=bottom,shape=straight": { "model": "unicopia:cloud_stairs" }, + "facing=west,half=bottom,shape=straight": { "model": "unicopia:cloud_stairs", "y": 180, "uvlock": true }, + "facing=south,half=bottom,shape=straight": { "model": "unicopia:cloud_stairs", "y": 90, "uvlock": true }, + "facing=north,half=bottom,shape=straight": { "model": "unicopia:cloud_stairs", "y": 270, "uvlock": true }, + "facing=east,half=bottom,shape=outer_right": { "model": "unicopia:cloud_outer_stairs" }, + "facing=west,half=bottom,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "y": 180, "uvlock": true }, + "facing=south,half=bottom,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "y": 90, "uvlock": true }, + "facing=north,half=bottom,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "y": 270, "uvlock": true }, + "facing=east,half=bottom,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "y": 270, "uvlock": true }, + "facing=west,half=bottom,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "y": 90, "uvlock": true }, + "facing=south,half=bottom,shape=outer_left": { "model": "unicopia:cloud_outer_stairs" }, + "facing=north,half=bottom,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "y": 180, "uvlock": true }, + "facing=east,half=bottom,shape=inner_right": { "model": "unicopia:cloud_inner_stairs" }, + "facing=west,half=bottom,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "y": 180, "uvlock": true }, + "facing=south,half=bottom,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "y": 90, "uvlock": true }, + "facing=north,half=bottom,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "y": 270, "uvlock": true }, + "facing=east,half=bottom,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "y": 270, "uvlock": true }, + "facing=west,half=bottom,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "y": 90, "uvlock": true }, + "facing=south,half=bottom,shape=inner_left": { "model": "unicopia:cloud_inner_stairs" }, + "facing=north,half=bottom,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "y": 180, "uvlock": true }, + "facing=east,half=top,shape=straight": { "model": "unicopia:cloud_stairs", "x": 180, "uvlock": true }, + "facing=west,half=top,shape=straight": { "model": "unicopia:cloud_stairs", "x": 180, "y": 180, "uvlock": true }, + "facing=south,half=top,shape=straight": { "model": "unicopia:cloud_stairs", "x": 180, "y": 90, "uvlock": true }, + "facing=north,half=top,shape=straight": { "model": "unicopia:cloud_stairs", "x": 180, "y": 270, "uvlock": true }, + "facing=east,half=top,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "x": 180, "uvlock": true }, + "facing=west,half=top,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 180, "uvlock": true }, + "facing=south,half=top,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 90, "uvlock": true }, + "facing=north,half=top,shape=outer_right": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 270, "uvlock": true }, + "facing=east,half=top,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 90, "uvlock": true }, + "facing=west,half=top,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 270, "uvlock": true }, + "facing=south,half=top,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "x": 180, "y": 180, "uvlock": true }, + "facing=north,half=top,shape=outer_left": { "model": "unicopia:cloud_outer_stairs", "x": 180, "uvlock": true }, + "facing=east,half=top,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "x": 180, "uvlock": true }, + "facing=west,half=top,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 180, "uvlock": true }, + "facing=south,half=top,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 90, "uvlock": true }, + "facing=north,half=top,shape=inner_right": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 270, "uvlock": true }, + "facing=east,half=top,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 90, "uvlock": true }, + "facing=west,half=top,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 270, "uvlock": true }, + "facing=south,half=top,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "x": 180, "y": 180, "uvlock": true }, + "facing=north,half=top,shape=inner_left": { "model": "unicopia:cloud_inner_stairs", "x": 180, "uvlock": true } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_double_slab.json b/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_double_slab.json new file mode 100644 index 00000000..0abf2904 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_double_slab.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "unicopia:enchanted_cloud_block" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_slab.json b/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_slab.json new file mode 100644 index 00000000..c2e0d8cc --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/enchanted_cloud_slab.json @@ -0,0 +1,6 @@ +{ + "variants": { + "half=bottom": { "model": "unicopia:half_slab_enchanted_cloud" }, + "half=top": { "model": "unicopia:upper_slab_enchanted_cloud" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/normal_cloud_double_slab.json b/src/main/resources/assets/unicopia/blockstates/normal_cloud_double_slab.json new file mode 100644 index 00000000..0dd87d3a --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/normal_cloud_double_slab.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "unicopia:normal_cloud_block" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/normal_cloud_slab.json b/src/main/resources/assets/unicopia/blockstates/normal_cloud_slab.json new file mode 100644 index 00000000..ba725ff6 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/normal_cloud_slab.json @@ -0,0 +1,6 @@ +{ + "variants": { + "half=bottom": { "model": "unicopia:half_slab_normal_cloud" }, + "half=top": { "model": "unicopia:upper_slab_normal_cloud" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/packed_cloud_double_slab.json b/src/main/resources/assets/unicopia/blockstates/packed_cloud_double_slab.json new file mode 100644 index 00000000..eebde8cf --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/packed_cloud_double_slab.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "unicopia:packed_cloud_block" } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/packed_cloud_slab.json b/src/main/resources/assets/unicopia/blockstates/packed_cloud_slab.json new file mode 100644 index 00000000..ea955372 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/packed_cloud_slab.json @@ -0,0 +1,6 @@ +{ + "variants": { + "half=bottom": { "model": "unicopia:half_slab_packed_cloud" }, + "half=top": { "model": "unicopia:upper_slab_packed_cloud" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/lang/en_US.lang b/src/main/resources/assets/unicopia/lang/en_US.lang index 7082898f..f21a7fd3 100644 --- a/src/main/resources/assets/unicopia/lang/en_US.lang +++ b/src/main/resources/assets/unicopia/lang/en_US.lang @@ -1,17 +1,17 @@ -tile.cloudBlock.normal.name=Block of Cloud -tile.cloudBlock.packed.name=Dense Cloud -tile.cloudBlock.enchanted.name=Enchanted Cloud +tile.cloud_block.normal.name=Block of Cloud +tile.cloud_block.packed.name=Dense Cloud +tile.cloud_block.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.cloud_slab.normal.name=Cloud Slab +tile.cloud_slab.packed.name=Dense Cloud Slab +tile.cloud_slab.enchanted.name=Enchanted Cloud Slab -tile.stairsCloud.name=Cloud Stairs +tile.cloud_stairs.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.cloud_matter.name=Lump of Cloud +item.cloud.small.name=Bucking Bronco +item.cloud.medium.name=Construction Cloud +item.cloud.large.name=Wild Cloud item.spell.name=Gem item.spell.shield.name=Gem of Repulsion @@ -29,17 +29,18 @@ 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.racing_cloud.name=Bucking Bronco +entity.construction_cloud.name=Construction Cloud +entity.wild_cloud.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.usage=/race [player] commands.race.list=The available races are: -commands.race.permission=Selected Race is not permitted +commands.race.permission=Selected Race is not permitted by your current server commands.race.fail="%s" is not a recognised Race commands.race.tell.self=You are a @@ -47,29 +48,59 @@ commands.race.tell.self.alt=You are an commands.race.tell.other=%s is a commands.race.tell.other.alt=%s is an +commands.race.describe.title=%s +commands.race.describe.human.1=This is the default race for new players. +commands.race.describe.human.2=It has no special abilities. + +commands.race.describe.earth.1=Earth Ponies can grow crops using mana and punch trees for apples (yeeeeeeehaaaaawwwwwwwwwww) +commands.race.describe.earth.2=Their offensive ability is to kick down whilst jumping for a deafening ground smash. +commands.race.describe.earth.3=They are strong but slow. + +commands.race.describe.unicorn.1=Unicorns are the primary magic users. They can teleport and cast powerful spells. +commands.race.describe.unicorn.2=Their defensive features a powerful shield powered by their mana. +commands.race.describe.unicorn.3=They are prone to tiring quickly. + +commands.race.describe.pegasus.1=Pegasi are the masters of the skies. +commands.race.describe.pegasus.2=They live mostly in the air and are the only species with the ability to mould and harness cloud materials for their homes. +commands.race.describe.pegasus.3=They are fast and light but easy to knock down. + +commands.race.describe.alicorn.1=Praise the sun! +commands.race.describe.alicorn.2=Alicorns have all abilities of the other races. +commands.race.describe.alicorn.3=Only available to CREATIVE mode players. + +commands.race.describe.changeling.1=Beware the changeling, for they can appear when least expected. +commands.race.describe.changeling.2=Changelings can fly but but do not interact with clouds. +commands.race.describe.changeling.3=They have to feed on mobs and other players to eat. + commands.decloud.success=%s clouds removed commands.decloud.usage=/decloud unicopia.race.human=Human +unicopia.race.human.alt=Humans unicopia.race.earth=Earth Pony +unicopia.race.earth.alt=Earth Ponies unicopia.race.unicorn=Unicorn +unicopia.race.unicorn.alt=Unicorns unicopia.race.pegasus=Pegasus +unicopia.race.pegasus.alt=Pegasi unicopia.race.alicorn=Alicorn +unicopia.race.alicorn.alt=Alicorns unicopia.race.changeling=Changeling +unicopia.race.changeling.alt=Changelings unicopia.category.name=Pony Abilities -unicopia.power.grow=Earth Pony (Primary) -unicopia.power.earth=Earth Pony (Secondary) +unicopia.power.grow=Primary Earth Pony ability +unicopia.power.earth=Secondary Earth Pony ability -unicopia.power.teleport=Unicorn (Primary) -unicopia.power.magic=Unicorn (Secondary) +unicopia.power.teleport=Primary Unicorn ability +unicopia.power.magic=Secondary Unicorn ability -unicopia.power.rain=Pegasus (Primary) -unicopia.power.thunder=Pegasus (Secondary) +unicopia.power.rain=Primary Pegasus ability +unicopia.power.thunder=Secondary Pegasus ability -unicopia.power.feed=Changeling (Primary) -unicopia.power.disguise=Changeling (Secondary) +unicopia.power.feed=Primary Changeling ability +unicopia.power.disguise=Secondary Changeling ability death.attack.feed=%1$s was drained of all life death.attack.feed.player=%1$s died to feed %2$s diff --git a/src/main/resources/assets/unicopia/models/block/cloud_cube.json b/src/main/resources/assets/unicopia/models/block/cloud_cube.json new file mode 100644 index 00000000..5bf3dc59 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/cloud_cube.json @@ -0,0 +1,29 @@ +{ + "parent": "block/cube_all", + "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 ] + } + }, + "elements": [ + { + "from": [ 2, 4, 2 ], + "to": [ 14, 12, 14 ], + "faces": { + "down": { "texture": "#down" }, + "up": { "texture": "#up" }, + "north": { "texture": "#north" }, + "south": { "texture": "#south" }, + "west": { "texture": "#west" }, + "east": { "texture": "#east" } + } + } + ] +} diff --git a/src/main/resources/assets/unicopia/models/block/cloud_inner_stairs.json b/src/main/resources/assets/unicopia/models/block/cloud_inner_stairs.json new file mode 100644 index 00000000..80bb90f8 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/cloud_inner_stairs.json @@ -0,0 +1,8 @@ +{ + "parent": "block/inner_stairs", + "textures": { + "bottom": "unicopia:blocks/cloud_normal", + "top": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/cloud_outer_stairs.json b/src/main/resources/assets/unicopia/models/block/cloud_outer_stairs.json new file mode 100644 index 00000000..2922183b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/cloud_outer_stairs.json @@ -0,0 +1,8 @@ +{ + "parent": "block/outer_stairs", + "textures": { + "bottom": "unicopia:blocks/cloud_normal", + "top": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/cloud_stairs.json b/src/main/resources/assets/unicopia/models/block/cloud_stairs.json new file mode 100644 index 00000000..7d616a63 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/cloud_stairs.json @@ -0,0 +1,8 @@ +{ + "parent": "block/stairs", + "textures": { + "bottom": "unicopia:blocks/cloud_normal", + "top": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/enchanted_cloud_block.json b/src/main/resources/assets/unicopia/models/block/enchanted_cloud_block.json new file mode 100644 index 00000000..5e55735e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/enchanted_cloud_block.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "unicopia:blocks/cloud_enchanted" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/half_slab_enchanted_cloud.json b/src/main/resources/assets/unicopia/models/block/half_slab_enchanted_cloud.json new file mode 100644 index 00000000..e565bd53 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/half_slab_enchanted_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/half_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_enchanted", + "top": "unicopia:blocks/cloud_enchanted", + "side": "unicopia:blocks/cloud_enchanted" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/half_slab_normal_cloud.json b/src/main/resources/assets/unicopia/models/block/half_slab_normal_cloud.json new file mode 100644 index 00000000..d30a7d5f --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/half_slab_normal_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/half_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_normal", + "top": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/half_slab_packed_cloud.json b/src/main/resources/assets/unicopia/models/block/half_slab_packed_cloud.json new file mode 100644 index 00000000..2b1f7157 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/half_slab_packed_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/half_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_packed", + "top": "unicopia:blocks/cloud_packed", + "side": "unicopia:blocks/cloud_packed" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/normal_cloud_block.json b/src/main/resources/assets/unicopia/models/block/normal_cloud_block.json new file mode 100644 index 00000000..0cbb5c35 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/normal_cloud_block.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "unicopia:blocks/cloud_normal" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/packed_cloud_block.json b/src/main/resources/assets/unicopia/models/block/packed_cloud_block.json new file mode 100644 index 00000000..19e796ef --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/packed_cloud_block.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "unicopia:blocks/cloud_packed" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/upper_slab_enchanted_cloud.json b/src/main/resources/assets/unicopia/models/block/upper_slab_enchanted_cloud.json new file mode 100644 index 00000000..a4d9fe78 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/upper_slab_enchanted_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/upper_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_enchanted", + "top": "unicopia:blocks/cloud_enchanted", + "side": "unicopia:blocks/cloud_enchanted" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/upper_slab_normal_cloud.json b/src/main/resources/assets/unicopia/models/block/upper_slab_normal_cloud.json new file mode 100644 index 00000000..64300044 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/upper_slab_normal_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/upper_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_normal", + "top": "unicopia:blocks/cloud_normal", + "side": "unicopia:blocks/cloud_normal" + } +} diff --git a/src/main/resources/assets/unicopia/models/block/upper_slab_packed_cloud.json b/src/main/resources/assets/unicopia/models/block/upper_slab_packed_cloud.json new file mode 100644 index 00000000..dad8d4fe --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/upper_slab_packed_cloud.json @@ -0,0 +1,8 @@ +{ + "parent": "block/upper_slab", + "textures": { + "bottom": "unicopia:blocks/cloud_packed", + "top": "unicopia:blocks/cloud_packed", + "side": "unicopia:blocks/cloud_packed" + } +} 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..ee7f4484 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_large.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:block/cloud_cube", + "textures": { + "all": "unicopia:items/cloud" + } +} 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..c6439a05 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_matter.json @@ -0,0 +1,18 @@ +{ + "parent": "item/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_medium.json b/src/main/resources/assets/unicopia/models/item/cloud_medium.json new file mode 100644 index 00000000..ee7f4484 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_medium.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:block/cloud_cube", + "textures": { + "all": "unicopia:items/cloud" + } +} 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..ee7f4484 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_small.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:block/cloud_cube", + "textures": { + "all": "unicopia:items/cloud" + } +} 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..971b7b9a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/cloud_stairs.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/cloud_stairs" +} diff --git a/src/main/resources/assets/unicopia/models/item/dew_drop.json b/src/main/resources/assets/unicopia/models/item/dew_drop.json new file mode 100644 index 00000000..649b1b57 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/dew_drop.json @@ -0,0 +1,18 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:items/dew_drop" + }, + "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/enchanted_cloud_block.json b/src/main/resources/assets/unicopia/models/item/enchanted_cloud_block.json new file mode 100644 index 00000000..60932b39 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/enchanted_cloud_block.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/enchanted_cloud_block", + "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_block.json b/src/main/resources/assets/unicopia/models/item/normal_cloud_block.json new file mode 100644 index 00000000..0ec38890 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/normal_cloud_block.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/normal_cloud_block", + "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_block.json b/src/main/resources/assets/unicopia/models/item/packed_cloud_block.json new file mode 100644 index 00000000..b0acce3e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/packed_cloud_block.json @@ -0,0 +1,10 @@ +{ + "parent": "unicopia:block/packed_cloud_block", + "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/textures/blocks/cloud_enchanted.png b/src/main/resources/assets/unicopia/textures/blocks/cloud_enchanted.png new file mode 100644 index 0000000000000000000000000000000000000000..00a0a0bab74f25d4224745992f95fddae1827857 GIT binary patch literal 2970 zcmV;L3uW|)P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002RNklP=;-Mi+E2xbLUb0m;nAl%vfV`}{IuJ@VVD_UC<6{NG_fI~0ue<-1QkJoA_k0xBC#Thg@9ne9*`iQ#9$Or zQF$}6R&?d%y_c8YA7_1QpS|}zXYYO1x&V;8{kgn!SPFnNo`4_X6{c}T{8k*B#$jdxfFg<9uYy1K45IaYvHg`_dOZM)Sy63ve6hvv z1)yUy0P^?0*fb9UASvow`@mQCp^4`uNg&9uGcn1|&Nk+9SjOUl{-OWr@Hh0;_l(8q z{wNRKos+;6rV8ldy0Owz(}jF`W(JeRp&R{qi2rfmU!TJ;gp(Kmm5I1s5m_f-n#TRsj}B0%?E`vOzxB2#P=n*a3EfYETOrKoe*ICqM@{4K9Go;5xVgZi5G4 z1dM~{UdP6d+Yd3o?MrAqM0Kc|iV92owdyL5UC#5<>aVCa44|hpM4E zs0sQWIt5*Tu0n&*J!lk~f_{hI!w5`*sjxDv4V%CW*ah~3!{C*0BD@;TgA3v9a1~q+ zAA{TB3-ERLHar49hi4Ih5D^-ph8Q6X#0?2VqLBoIkE}zAkxHZUgRb+f=nat zP#6>iMMoK->`~sRLq)(kHo*Vn{;LcG6+edD1=7D>9j^O?D{Qg|tCDK{ym)H7&wDr6*;uGTJg8GHjVbnL{!cWyUB7MT6o-VNo_w8Yq`2<5Ub)hw4L3rj}5@qxMs0 zWMyP6Wy582WNT#4$d1qunl{acmP#w5ouJ*Jy_Zv#bCKi7ZIf$}8d zZdVy&)LYdbX%I9R8VMQ|8r>Q*nyQ)sn)#Z|n)kKvS`4iu ztvy=3T65Yu+7a4Yv^%sXb>ww?bn(=Yu(!=O6^iuTp>)p_Y^{w=i z^lS773}6Fm1Fpe-gF!>Ip{*g$u-szvGhed;vo5pW&GpS$<~8QGEXWp~7V9lKEnZq0SaK{6Sl+dwSOr*Z zvFf(^Xl-N7w{EeXveC4Ov)N}e%%C!Y7^RFWwrE>d+x51mZQt2h+X?JW*!^a2WS?Sx z)P8cQ&Qi|OhNWW;>JChYI)@QQx?`Nj^#uJBl~d&PK+RZLOLos~K(b5>qmrMN0})tOkySZ3_W zICNY@+|jrX%s^&6b2i>5eqa0y%Z;^%^_=a@u3%4b9605ii3Ep)@`TAmhs0fpQ%O!q zl}XcFH*PieWwLj2ZSq`7V9Mc?h17`D)-+sNT-qs~3@?S(ldh7UlRlVXkWrK|vf6I- z?$tAVKYn8-l({mqQ$Q8{O!WzMg`0(=S&msXS#Pt$vrpzo=kRj+a`kh!z=6$;c zwT88(J6|n-WB%w`m$h~4pmp)YIh_ z3ETV2tjiAU!0h1dxU-n=E9e!)6|Z;4?!H=SSy{V>ut&IOq{_dl zbFb#!9eY1iCsp6Bajj|Hr?hX|zPbJE{X++w546-O*Ot`2Kgd0Jx6Z4syT zu9enWavU5N9)I?I-1m1*_?_rJ$vD~agVqoG+9++s?NEDe`%Fht$4F;X=in*dQ{7$m zU2Q)a|9JSc+Uc4zvS-T963!N$T{xF_ZuWe}`RNOZ7sk3{yB}PPym+f8xTpV;-=!;; zJuhGEb?H5K#o@~7t9DmUU1MD9xNd#Dz0azz?I)|B+WM{g+Xrk0I&awC=o(x)cy`EX z=)z6+o0o6-+`4{y+3mqQ%kSJBju{@g%f35#FZJHb`&swrA8dGtepviS>QUumrN{L@ z>;2q1Vm)$Z)P1z?N$8UYW2~{~zhwUMVZ87u`Dx{Z>O|9|`Q+&->FRy-Sjp7DHs zy69KwU-!MxeeuI@&cF4|M9z%AfP?@5 z`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0Le*2K~y-)g;To~!!QUulq^8S3fjNY zCT0aUVg#oNeh8!FItz}^ai;6K1^}ok93lc{hMD0!PgD>Q0Khvw_THByW?l+7DZaHG zSyf?XHz~u5(42EHGXVS?d50Fs8F{J-bR5UXyX}Zk=tYApR+YG&AfC}Xdx892Y7qC2 zI#lG|`{OD!=N!Jqu3OYdL{=a8{(0<5%*qoD09C|)C-iu8w^csaA?CLjFtbOYe^zdB u4G*a5tx5Js^)tP`1t@p%X*PP0h|mY`lY}detMvW=0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002UNkld|(9nqUR9YR-YtFjD&Q7yJu#ms-~)@ zyJvC5yL+nYjjQXz24)5T5D@^t%+S%f)j#-lc*3d!U4@w;A|I~qcMbqvC#8Kb5q%uh zfQW#IV0EPlw2`aJ0&7S#685_W?!p^U1IB7Wjk+Xx91XD zr`;;$QlEk*qVs_6;uMj; Tq>U?I00000NkvXXu0mjf(f)~S literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/entity/clouds.png b/src/main/resources/assets/unicopia/textures/entity/clouds.png new file mode 100644 index 0000000000000000000000000000000000000000..da1c38778ccc6f0ab0a825165c3c406e87ab6033 GIT binary patch literal 21110 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 N002ovPDHLkV1i)co9O@m literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/entity/clouds_storm.png b/src/main/resources/assets/unicopia/textures/entity/clouds_storm.png new file mode 100644 index 0000000000000000000000000000000000000000..f83d16cf78639cd9797c029b63d3b8bd1103208f GIT binary patch literal 21913 zcmV)WK(4=uP)pPPiaF#P*7-ZbZ>KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z002tFNkl zezxAX{$0JUzm|PZbWQX-f3M2-qW7)$yu7^pT*I}l>)+8D`t^T%drQBs@>tH`)f97S@zyIzmOhpj9a4Mf&hv4n) z4Q_Px;QQ;M?`IoZQ7Ha;Y@^Z33al4i%cE%6DfhrlUN4?9lQxEj69C+VV-y?2dpD6(SGmTW7qg8`>3vaB%Aj0=-IuT z-(Aa|S(oTq%JseL!d#k)!ub02YfBhVND^sxbfCQ8L>k{KRK@C|b&eFWy>F|akoy`v z=gC9g`7HCW)-!gzJN3ZsJQvzKb={-$w|?&TH}ahBolQVDX|ES)OjXgK_=UMnyVg*- zUbH#(@SxvC8?tL*5mul)<2pDtUY@j;iYKwS89db%E=#iI?WD4O-B6AxBh$ErZEd)x6mCiY_eEVBI1&(FUpZU32_5RHMLudlDa z{Mp?*iy1AUw;lre4z_Q0EuwWr7*`kbVc)0XYk&Rq*Du~dBzx<*KmGI1KR>$ej<@j9 z&1ctIRuzF)>S64Lo(rR~=s<-ymf+&LHLlEXJGLPoe^D5toVr1kAC^G1+??k9z z*R#4Wy8s}{24&azumAk#KmYH`moHC${`u!GzxeXy3;cpV|NQgkAAj@cI?=W4>%Q-& z`~Dv|;`{gSKmUK<_tW?9-_wUz-V=S!emA<7eb3jgU)$e>gauuTt>gOn+Jm+J(P!-I z&p-cs`ts$=)A#S+e}3=x@86%kef#$G#~*+E^4>rG_~YkyWc%lzCwi{z@V~sgJbnB2 z?dLVt-rMVWIZ+P(+uPeO_p`3&pUbXa<+;~$thN2}<;&BzZ{MEo`+oZO-+!OJeEIU* z{=B}vK7IT4?djit|NTwYy7J|}6>hXhoF9+6CQqEWC1U3TkDc(L~C6u?@Q zh0Dhs{y`Xc<>MB+xRt}NEX34eSv+BZBoT+LRG6WfmJQJGus+bb` zS(Fm)0EtNc-rnBA6si5ro8^!rfX`Q0G1pWD{Z4^=6(zv36>1SkHzP460MR|JeVRyN zG0SZa5?hNXkL_h6y9e|vs1zFAS0~)avId?>%Ok(toy0O)xlt|~k7^+@r2#U&TZ935JHTt<1Vg#=F0~nR& zR25a6V;R@(fnbL}vO+ALLe{4fqwO9-pVg=wv>lIySr&UQ9S#RI#A7++>{&8gCtB<7 zUUs-6PKAi*V5?*<6SAy^hISpUs^Fm3Q&tPGe(w(lOM68z+aptgS*vs9b30m_9aN=T zZRfQVf!znB7;GhIru?QlFcqacae)1Ip#rJuwa~TIcJZtQINN_OGgn6-S!EgPNB1ri z0tOX6yVFV_N^>!aE(O@CJO`q*ia^6oSb*;1<>dvgcT{Ry*%JgXva;-IEyy%(6JatZ z!@$h5iYhZB<*l6GtEI|^3_$Cc5I=HXSbbVwpGK0$Q#>5gqUy*PT6wB_v@XLq;lF z3EvozX|!%WG`8eA*&vuKoX%v|hwLveFTcs<^Yb$g0qg@Xp;b5m?e7>%?x{FejgPX! zk2X`10TrcpB~>-Js44jgT-=dAu|f9%(7Jv)vj{z157gI8+GpO zmh+LkC2xYJ-esmX0dSQ9%tZS2iwRnryY>Da}6~2u_}8Alw_&o?EIiJ zTUqPNU!-#IZ0$bM#>&JT7NbnMUeN)_TAGnJvd~mS#-R#?0#czhP^h_-!K=PcBPuJ- zIQLXsV|Onr^AfA!LZrBMy9d=c+*g=Jhde4-_MSoI9`7i|RW;B@ZQb3C`w<$fSTyh2 zsfQuc{t=p6aXsz7E0zX%2;I2`9qMgcG}E!%o$+RvR_?BpblLfDkGN0gZl+w zE{-~k@+0K)yZA*^v=j-*QV?u1@YLFL>85rvEEBZ~-pkx!f}R?xl8w&JuA`2;+ffL9 z53;di)lp|x2W*>UtUYmnyTVxom8?}>DZ$L*OmQqKg5(dKa(@bN*p(UNl2`Kl)0v{q zK+Z_!(DR@a;x0hyS)5R~;Gc=jNC>k4{zK}a-rwUe~(SjqLOuR}QBd z14n7Ju50Y?T~P!Pl{r!ZULkbGU^5DKRjEvu=T<|CN_%(~dRCqRmFKecVtxM>Kj{`n z?Has{oTn;Vcg6*ml_%Qaj***FO~Ikh*;;EP+?CWJQ+NpQLa4XxYKxSpGLky`eU*=X zDs-^*@0I&iT=gy`_zG=E6jNr_&Nq?lu)(tYcVwZ}P042YF#u>{^cVUbq!@Sbj9nnK1Au1VTP?X& zXCR`pfVeR#_m<%+>KVS!@Pta7Vcjxc53mzgqnO9%JiByTNQg2{HdKWcrT7?gQ07rp z<6*^0>;R^#V$+9S)wbkGOWp||s=&4KUN%(3V>VV^s(k>eN44IYW%2CmuG~m>%Ip%L z3G3vo(asEmEPU|YX_?lou45%dIN31e-Fn~k$#@x+`T@Vs6Vr}Q#LyFAiGnRfG- zJWA>>Y6C`!^Y!)hS3UsmI#f4KEtQOfFLIyQfr;|--qJ^D5d`S@VHhaC&pZm|o@eVf z2_$uB%uWDW(S4`}p<_5AMcct;CSTneh6rb2>xn{O|GFnNbC)NLxmd`OAVp&>dBi@9 z?v)KLRS&-hDXU&m*R@sGHY#^U1Q93iBXV1-_3w(Yu%5_Asacuqabcc}Ez!@O{q9(* zDT~nA7@LBz3cYf>G(?@bY-A!X4j9hJ+{6UhDT0t&=MFvNL5Rdta8tu+}BxNmLqbb}Q4OLzW@P zp0jZM?eTz}`-+}x@&IavU;Br z3e%@{FQ#QG)nAC%WBm+~f7pY6N;9Gj?N&@FUPF`?sto&lFBjYQrAI&Ogh1UA=try( zOH*bnR*D6-3SnygUzYAwmHKaU0WwauXu@gahChm}r6VMMV^_Ka6}(j9IzRV7Is7VG z-Cd99TIl|wJwxG8w+wjdP^*+(8L0{~ZYV5JdtoxU@04S!g;>$|M0nV(y42CZ$>swv zWl))zqqwtH7$Psl(|NI+cL%ZcVMSK`rvNt{#g+j^OwiDKZRs=OM~He4)R_BJxP#^q zD&UyKz8Xah53bWl;Z~1>|2WFgp ztc#72Sb8iVn>k;JU6^u;`m)#G-ZCPx&Z-c-x8O(Xo++Q|lpN2@<4bH===dIoJ$oKs zH^S}@DF}p>_%noz>U1hJYp?eAVE=t1H>|Q@sO)q79D#loZMRcbyoRf~g=!H?X9Luj z?LCAWH7BX}p)^EgZPn)5I zk|^#+8-wbU)qHj}R zDD2E>+&R<=0If8Cp>X|Y2+-B=YRrgG^-fn@L@U+d8rVsfv3QOtty8157!6g%6swjf zTcsF1cN7ELVwh;%PLXLKLQm^;X0dctPJCh5QZ$jVWJ+6YS&yNZ7MVK>vF*?#x>g^htq){h012y3cV`C8LI1rgfv?NpZ;XTCcB%-cnc~agnwTI#xg~Yo;iAc zTWq9Gw)F~Y*A?I0m(w9lwezrp3qQb5mMB~hJH<|w(QXsC2`mK4KG>yX z^3+V7a#w_MN8IdLo^{7_=%g%_PzAlGfckJOcr{peUjUTWhf7>yF-BUD)cuiF(LcTCW->dnK`(Zs`DF>UW`X{@fE9P0i&afv}_wXwf5(D?2o{ zIAS8jb}9yBIEP;ktLQ2?h(es(g=eZui zSErVSvO)VYGC=OuVpD22f~TN zfWXxbISMK<_Jku-G490Y)L1_9Y}n6~>rS_sTTj`_(JvvuwD2fy$Eph4nKPe#&TfJ_ zxbwZOzCZl_@UY^NiMjI;&cc+8?IvZ-LhRWpM(9KpVC1@=QX-LTvQV(nwaG@wuPViL z_QDV#i|;abJVIf!f|i|)MpJ-JgLuC%JH;KU5HJm|)WM_AMlmO1I{L^M09totlPAGVzduok zDCE!HhgEEJfb`E#Gvo#4WKv~ zrTx&M>5iG|owNtTJ82gUJATlcO_ZA|X!SL4Jb16n4&3l2B zkpjxP7^`&IQGwF*zA24Q*?XtHJEG6?U@$A*@@LrBU|dX#F0?QbfD;paqBuuI@|F1S zM4P69RWM<(8-t3akRo__c^N~>v$b9`16XsDT?3!8W$U1(Gb6qOR3)gkjaLr(?qSd5 zlC7_DRyqdzMlrT^86t*LHm_W*5L08(4l8fg$%dMgx?zBNZwiN?9qnunyhm>p_ixfg zt}@vAOa@a19Yd1!iXf{v5#}NG>@4f0%pB-cSF-(K83J2~b)%SAb=%ZIk-Ola5&~4f z8D9eWC=f84R%GM<^>YjIc)&-=qI48N2k`UX8I6%!&#ctyD~G{)9uZa$g}9Yxtx$z` zfWe(1xh!p%VMlkiq1Ho)w3}LYjI8iUkPY3F%11CABCtWLXnq2lQXC18Jy?@Olj?J2 zfK|t9%Dxwo!$vrk{avN0rZd@zDZpKWnBiq!88U@)`J1%|1(gr_7^#y5*om_%F@I%p z@adPocg8AQ?PBxqKZ%Z4nTI@JS!rbI_B}96VTWm`+UkpF)w&>L_l2f*+kLsR29BKx zuvLp)-P8<~MT$!CJ|@XMqkCuFicGnkDg~Yh#~A;k4P&MlZYqjy;yWwTv3ER)69y-m ze0?c^SqC}IHg0+`vRE<-V|F%F*#HENDU*rqb*vRd?8=Ymlw%~V6{1huGZ6I-C<9-* z3S**YqQj0AtqtN-tb`AW(nB8DPg2mz;h*T8J{gnx&B>|xcpUZ9><1wE&D3dHY5zFjHcg^g9 zV5zJqza3T2sj;oc3Oa>f-a=>AN&z$$OZH(_)?(OM{}Z&V-S z|9^s(FY-Wt!2ovS$Bgx7X{Kl}!|vgmTEr7!NQO5kTydFouU3PpL>yTsp7p|PY_A-C z76w%)xVxkx2t?eS)Qbk=L~Nwaj%grrMoSv%A2R>o6f^Ra_|E zW#2VX25i&6e($0^Lwn3xRM&eeZQq?y?TlkG`&q^CFmJD0B;=5k2Vf%gqtsrX1fE!3 zI1_+1&=fWOWOUv;IEuYKqEJ`eWIqNR9YCc*&;LxRYEzV(zuvCAR(f94QSz0lL3QA0 zHe>TrZu*qf^e(MhNC1;#ri61%kU2S zyDGEL1p1%F>0Q3dY(KNT_GbyLq1~HHy|HO4S*bS6dcYa@@~Br-shDl5FZxg`|2p7a zcYm|)LqCq4g)vIce{|p(*jWWrEBlJF1T0mzLLsl1qI*{MEvsCuOscYLaX-D|E1F$4 z6`xJT?7N;tlxFhzVKlJvbcwHT8&wXk%26M&+C+m~Rrfq9o9-wX|NNDT)`(S#k?8s` zLXmZA+$_Zx!bqwZ)LmKl?jchV(2q{-wA;I`9K?mtDxaNDm{yo9poc2{P<=W3Kf5nB z{;j&+>O^K`Z_qU+NLMOVnr!0qQc|7$_8!S!fBp3x?Qvgq`ppoN)up32f#W3 zR!(3575C5!D{_yO(p5Hu)jnUxv&fW#XE^ettd%);5%!=~JWd>bJ1ZjtBvE%?h6$nl z^%In*&Z580joz@6laH8rP}*ejKxG2YI15?FU^dHbibD|bGIlnebvpXJ=>mTK+0j&> zsN|WY2^G1p6z__lpX5W_8P#o{*&gl)ZYeX}(dmetI4kEwRx$Fnl=kipsvRl8 zs07xB1q!Qi6nA zG{+FF!Rppy?)MJ8l)@%^3iz$cUPbY+XSJbFw7x1n&{e2Ap@{NcM51%Eizs#NyR^sA z!9tYNop#XfCfGx*&L&jvC9~wHtIl3u>4fa^9A;UF)$C)}BnvYtps!U}UI{MnuR;c* z$R`T%yt3;~$=(4Ocd8QA^LS+-UDt(28x_UcHYcpO`|L0wo_$0_f&|?wo9JH9&pTnj zB!}OrHfW?6*}g`hP3L~1z3V=|_YiCsqzB4&!+=TaozdT^^Agd|ZzYb7QfLaeg<6ZN zDH$q&4A4aG`%m3>BTeuQ{iLPQVx`6)Zu z;;7b;HSn;87j!@9vr0IiTFC5BJc3y{I%J489VrFGqRS>Qsfu)YdRf}H3JyjSkgU8^ zn53pcvZtp8E3h&~+{fNi-b7{@)#)70m97VgTwQFxS$ej;dD83W1nw+2wYhig~jbj{!$n zTv`DI{hFw2xRZ&P!~?3&+IeN3;0a<}pY@6QP#*QVvKSp%8ub52J9iebL*1-A0~&X} zKC2-6_B)uOVa+n$qgdbDNkOSxa)=406SGfS#EFoj^Lbl}%xVmFdkYvIfT~U(MFaO+ z2vDg%MlSdXLJc-jyHl0r0hQ`T+qgBOTJCru3KOhf=P0$)anYxlnoa_R(NN9G;hdbn zqgZ>pXIAN;`hiYnp<9r4d-t#FmM}|SxkhAltYAh(7l=YB3oUkCfB*T;H8Uq%_M5#sKDrZ1RRt!>iZCmfd4tGftWNIj$Vm2k_ME=?r`t%w z(zlVPnR#B1etr7<`SXt{%NvKNm; zsCT|gnNV!kg2_WfJ*HBZWwLd`;#b%1o^;zQ02>M|DwJD{cQD#?uQ(VhO~lqS9hY?u zzhXJOg|V%~p6g)Z;3*zTl#c9tpIZqMD8!zB%6X`yld!CBbUG8Vs_p1P7Ir+-V`B^?$eI{W(OYO532W6QC87Zq|Cbd z_f*rUh;z>t1*V3osJK#>iCa~kzj**? zpaIjGevu+Z)QOmG9oPtXv5hNL*EG7Wu)Kij3B3qB!Ug);+p^Zy@dpF@~K2%ZgqQPdtj%BOozy)otCn zQpDkbuPDA#C(`W3ca`;E-ig0IZ~6RSj&2P{w8qmVvegu-H9k?J5R<2DFbBFWYq3^k z=8-8rrs%EA-8S~wrSevkiMLdd+ZNqRWrC~X5>9SP2H`V!mSE9 zYid!Z+ajy)W4LPSEyF9abPrQ3l|#3~gI59nI?B$*vnnZw-wQieq0_W^JKjg- zz*m6Djsl7D?YsAV8YIgq?bOCC#3Vgg1=`I7RpmF5n@|7z^Un`7no2$J3RfOd5E1AS z6|h7b=)p#o!tq@8iUzG{)yNuD+~o?Ny$Y=E;18+*PFyn@I9mNI8;4cFtUTVdt{cgE zh35T{9hd7K5q@@cD&5d9L*?)zdMpcY`SHtaL76L>aS*D9tJ*rOD3^*7*!3Wg#-FIV zV_w;Wpn6ZTDwC|G7ReUMqS!Fi(#cH&b&q;QwJkRqORWrURKS#qR@|vZM4^Fwj-BV3 z;Dzf9bes?ng*mBGvW$$yWZCd;>>Ro1S*(sy!b($_>S-`JN9$Vk68sdWN(udi+=R@+ zvY3s1jxn;TWyDoWzTBRr)mI5DAbt67+o5$<%>uQH|R@*h%#cV|Klx;Bfecfc=S`Q>TBGD@=vR~89Hcbr&v zZagDYu?H;LSC*2TQgTYkG7{>Ha!Zv#Ut!3|Vyhn4BSM7=wcXDUVAt(M8^v50G~Ri~ zG9Ylpx=|N{#%Kl0*YWqrs$z`8blX4t5ZE zvTQAt2~EoLXP?QYQ?uVA4|~1;&IGMGDMnd<9Wn%u6t8=-59wXGD)sUNyU`9NV87pE z7*QN+L8_OR7sSb&l$9n(W-K+>; zQR}6v!#N}6!~iKZl{8g#S6N5K1IT6__(Or7Z-qU(hoEu|rJo=gv(6gGEH)LjB%^A- z&a+PtuXMmlm1(O3mU~(9pHJrkRNay2KrsPehc&YJ-395o;S=jp6)d$VOHropQJI!i zSytA9c>>v-Naa3}Y-TifCpi5xb|z#j*t%v6T(@!$dOAk?ktbxa#uRAM=@aN$*lFO$quZI=t|;ZI z<%?z!o>aW5;HnmrVM!DBXe&vpZReB2Qz}?dwXtoj;Z-^O>)$KQ#7102^i|!LpQXp* zVdYRfIv%#l%l4@=O0&)&kI2oBT%%*;4V8dfiR@SVQBv-TdhdV zJk{!)$VyDlW;-G|nZgl}GFS%aRyQ%TQj9-NSzMONs=CV6c<>SNzDKE}PzKdgD9;_a z_MXF0750hZmgoRvotz3r;JuiRl2X`@Eg0vEfGV+g2Vhj)`Dz$vpXC<}ppr|D=A0|> zo?5I?ncc^(r!b?Oh=Zf=Rj0;um1XrXL>va4m^<6s4xnb`tbz+fgKjGpKjVbxlCE|X zK$bS{ysj#uY*FOZSd+2zbmQ3QbBK@NBf9;aJ_l7fM$bHP_^a^(rRb?+IQ(nnQn)ljk0@PhCF z+rg=Z4_ORcwVIJ(i@X+uzxb(G2pfsgzi3Xwgy(ws#Xm9Tx&thAW4f+ZRPjS5g~JQc;1_Qrg3%6q4kD!k|w9H26jpmP9OC!6Y-F{<{eQdNs+vXpGLH>g|f zlw3?Q8Fc{RsGL1xBHESl=yw;2pRCK3qwiPoCx%&>qYxD&A8hI z0R1}KJ*qtG3PH2+R}4hkiQy}s^-NalkRyiWg{@7@r(^?Se+vQpA;?lIKBvgOSeg9W zb)k`4rz}F1Z>>wubV{fu!>yAFD?-$6hj_5h7IF5h+72Ry7znnFguu@2*u_hG3X{(4(-U+LpZ9Jo8 zw`JNDz_5#s?|Mb+uy^V-GTucbTNQ7C5p?cS*)2BW3df@-N!gv{`AY~8r7jfLW2J6< z4l^3xjIf5C1Ijje$4yrz%XHgOqc+lLy^tb}QbL`)`w695#d*9z=n6QZ?ed>f31uto z%xWFYPMX)F`IUh%XpQddfGFNU#H-F)T2)lF!^GMV+~|2DWPKO#V+AHCgj#~Q1jzCr zqSjS)DC-1pxl6#=+3OW5*{;2viNfrDcBeWsx$3^Wm&l9>lkbbgW?jp$5Z$F1BE;P< zX2x}gG<>!($Xm&*R`&0SL!;mqsEEW{jY!+GZp?|MuSxK8SD|+Y$TH8YB4e3C%+j(b zoxZ$jgz2!upM@^JVE~hy?n*wGxxQV2VxZp&PyD8O24Ob+_@lp&n_CgqopxFz)Vs!G z*DCJh`qzQ;^CB2Vfx?2Q`xD99y1t65Wou?VMRZ*YvVQve@4tU^8%P*KEo%@u!NcFt z_Su-MDiO(4D3Z+v@Q8m@R(ygeG*jMF>$KY~H(4&rLdFcFRGZnNtWQ^IMntC}2&it> zvh>!1BF(lt>u1%QK%GL%c>fNfrjI@N6ej_EodH^!i z-@kr_02x@}2_;p)?u47Xt7R3}cxS*wM9#tD&T1hK3j->i4C@`}y2X=rRjFB_QrLYnEv*uhcqQNqH>%n`6V?jj5d#Jl4`E8ED|BU*kzKp= zS=Rb55X^g2i~#geOwm;$7gj>Q8!}z$!dgISxmv6o5n%w9@FD9qR6K&|TK#U^%B(KZ z+-v=^wqN^v{<_iqMIORrh?IFvJFY)F{23(}dmoyLgWf;sb*i1T2t$jWrOHi^DZfi` z&u!dc1Q6#IxSxStz(A zzavF28)VsD4tAs(iZflXo@kAzweU3(M*v z?7Bu4uq!xf%wPH5DF$4YCd^Q6U!toj&53(wW$p}*iS|$}(y5LkW4$%ey~aBQr3^ekD+a%ABqgf~LC znYYF;k4Vtinonh1BE>ZMT}qQwlcS)J*Qv>e1_A3D?*5P*)m~|z^dH_ zmL0`ZjVsZpZPpxBiDN43Mi+ABa}Rx5BSPhTyU?o71moG&|8-2N<@&~0T< zo&0=v9ZW z!stLUkT3EICnhOXlsOx#hn}l)_Aq7AL3-;;iO_ zyOm&Q#-1{34t;0!pjW&)rfjnc(+c=l^)ep8tel0few4gz$@fD0-jj}2m|{=Gklr$#7{XDR z1cSQ9GMaWJB+$Cswv)&4LhT-%kwTqp3JT+Wt@-|&r4MpD6IPQ&p-eC zh#E#z;Aq0VL5s@j-O*uNn-pn8TOe~PwylOg)7PpT>h~Jqri^RPJer+Z0IIwzdGy_T zjKK3sm>0=BQzGl!+-jlBr0}ALnVMUt%qBd6bZ1tsLNXa$PYE$1fbVqv{AB#=Ni!?d zTPQBhNKUM0*{uew4k}g739G+|WFxzeU($dP*V(kyXRci4#zn3S0|s}FXCPgWon0lT+d zFdHLJRIb?p7QR8Idia?(R>?(9zkF%%M4TKQaFlUDGX%X-@0RhN3ZYPVl=b#UG5va8 zrJ7J_{_()!gk|q2vuJ-ZFHd>3DgnC(qxyi+bv_zb|1Jk!wdn&@hoNg^LqT{)OC23DozFrPEUnW6sxia(f%`e@M~LE03AYu%*A_zn>sy0dJ3)H84ay` zHgkO|Yw;1V19T5jnXC^zBNpIDX(FOiM(#QJY?Um6LT(x<(0f|&vfS(=Tyo~xtuDBU zjhorT`$=H3u& zH3LN{r>O(hy-p+;e$yu{+BAauh{M`ZTGO6Xbxl~q6LWh}!&avpc$em@8WLq`!pOp_ zM&+t*NS4}Tt-b7LyTBCkG9qdzn_AidI$3q&#G3Bh7Zb{8_{DBm(g|-E39BFMf%ef? z@mX3hd(ctkUk6lX_c^_Ok=KUSBASYoy>3+_A$Jv7&;`6O>-{L|Wrc@MNw=vnS(ll0 z!s=$BSCmsn&&&#KSUGX$5JX*VyY4eun}~*8jrpgGfOeI_1b4!%9{%-j-)`vBF;RJL z*2Z{B2~1kBEZ3^6q4kv1B9#h}WaiQ;*#owYs;97<;@Wvdsk`iZ9yJ19xqpTErxTX? zpIyr4cr6&+oHE?9Bg8wPVKk0B8}~dJ&qd*n9atKP{}G) z&x+%fm}LQ0N2WdUg=oJ0{rS$=Y>iO&ZYczqAVW|`j!glAEN*8VS=~4;Ba%SVcI^hT z6jwK-LTo!(dZ-Ix?f{l31y%_wJRMpzd?g}Lnet2~($$sXBOlsTh;hO!0Lzamo5&Q1 zQi0W(tDfB#ii4G|w2m^U7TTQXg3Dss4&HDgTTyv_Wo_Tvh2JSy0Z0|M>Z@c~lxD9i zReh_QnYlxLyb`i_JMO>54!WI#E4EU5tSd@D75*K!GV30Uy5dxxe}8YN*WgqLP+8+l z@R#*Om4#JrBvXE<9$^aP+wsWQn%ZlXXu-31EF*VN1aZY~mSW2aCyJ?0#9x&dTFJBf z=Q(9{yai-y?%9{Kkf}3Qp&CwAsvEOVV0o=m+>Ofpo{-1N`Z9};Sfa1aW+-_`J zx(Y9w>resum|?!M!{LV^sJZTls~#18Fu}9jZ73t56m3KWLDjsq4JA~|5N+bAVU$c5 z{kp5!9luVM5$03gmy&|D!X9M+RD2*Ja8dDxD&B%TVFja#UUu*cGqmz}@Ji)eRJE0T zhmFyhg{io8>OreE*QlGKct)oIsY=+S9NsA1TRFVwKGA+xOOG?45KSYh+^4U5sVH)$ zu>9UAm`n5?TmGDRKlc>iP_a_>CYbflz?JOx%CLz^z^aOxR{*G!uZpEw>9wqgNj>x` zK$+2jSHBO*PNk0C7H3tAOl2>WW-|{$`g4VfqU?9v>`S{=QrQImol&tvlrqcmq}AdW zR^l90?L|#Rkrh%>RFi%B*~H##wgTE0+hy0Gk!NMb>`+GcR#Cc9S#Or@$!Y~2!55%D zr|pG^44_@-TlwN_AVX)^hT6V}SbUWti4O7`6=5LKMMSm{kyiTaW(RL)Dow}gqoEtf z(q(ZeyMI)GwVNkLie$>;ithbns8(GQ27G!270J}4Xl=F#U3ZmrII{*w{G|jP4lqT@W-hs6S-D-WP%U(-rUIiH4Ua3?&RM^IQSFUw zb*_?ueEs_M|0@8S&ggcesY2W#p780GsBN<|QJ5Vpq!g^%<*hr~=;e~y17T;lTa3fU zqfFsdo-+d0l=aEFCbRqT6jc5>S?>rdp7Ko7NItq4Lzh-PGA5Bni_QQFCD{2x^)Z#_ zxR~%(4=Ni{p)_)MdO$cis-#6O2xtB~cnD{=UV0kTLLu0N#)Vwcg|Dq*0u|{*4a0*YM0LqtVc4O(H>Ohad~t*kD)1bF=rH&8_cxIEvD%$chhN zx@Q6@Tm;i_3TiavvtfYH!=vjSU|-)$~;lDhm|f{m8~f&%Ch(7L#2y- zF^box(tg#0Y`YON;Xr_3hLmTnr>ALJla#!Bj}A#>by>kmBuG^XFMl6i2vmYeADeoi zQ(SHPT^5Mk1ym=vCv6_x7pZkQ7?k@!hj&M*Rtb525`tti(>e#fGw49ogq=QbRr9k+ zvr*+5yGOR^(R;FDp$vIOyo~I7kjIu0qgI)nJD*3&_ta~m47)PuHhQiuj{&=vNRBRV z@tHMHi8LlLh04LI#*~T&!Cbr!$f#D+t$XOS`7%V@Tk=Yuf9LRK^z6(UMi|OzB`{$; zl=VT+6?r1N-ce)(u59>#cl|RmjLCAz%tcmXpG*m4iee{2qBPEQKu)#LMRCg~gluPq zLv$cA?B+yiZf8BZYwuIQ*|TNYpsWoaz3!a_$$%r|38*~$+5Md8np5ewO4R>saKS0wez%C@f)4iXx){N1jKOS;^C)S3N7_;;K9X5h(c4lz_U2Q($GK z$VsgSxT8Z}b0_~@A`nJ)j{%rI3ge+vX; zB!yl600x*Xfy`W3HvbKgk17^sG9SEqeOnBpjQZLc%+m4bRQFiv4`CS0q!LhLDPlO=sbt#)0x$J6dZ{|Jtg+#ONnI&YuLxnhuV(BFBTi5dV?!nbhj_UF4zpF(+ zjEcPy=SB@&m22C*m?U87SW4e3(rw_`9hq*cL!%yo>YiQMhocxU`?BmkUJ0n(RuynW zamzaQ{vJxeKHmZzt(9N`I>DQmH8AbY4)n_BL~s5Mo`SqdWTg>Zcy+K2lLH_7qiZAM z8Y!+TTj&fRtj2TI2d%m}i#w1}Jy)&Jlrq>EIiAfHv{nIiqfPc>uuRXXwR?|)AGL(r z-`fn32kDk9`mn8gF6jf%h!%>)gAr0Jh?N7g|6^QD@+0&D6C$6K+DNroN)S5^2 z>pP>=vjL^2JuCFRmcA{!=PHL|g$vSp6Uqc7#J&u>0TCs%eZWpH!1RIG9ma`3Cp1+R zxuB0oJ!Zn-@0}g~33AMxy{r&uX5P*8{a5MJk@b&g+oEuQx#g8}fq+QWaItj!t+;vW z!Bw*XodOf}{aFrLsbqYVm!q^=vx>NGwO#cdr!zd&wVv3I&f!O17fMH~W74{%>-Kw? zJZJ6vFa%(Tyn2(~a(cR`N5#-f*pYkmQOimz?lpVgqU`e3k`w5;8WK*28IK0}vctC% zma=uKgjv}>c$vsv_jLb0%Gvt?hRbED<}$mPf}Nc9d%UV6q3!6gwXTmUqm2qyCcJ=0 zF#%0^HR`_E{bFO;r`C~qf>#s+vv`?lS=M;6QW2hG?ju-7LXlm zH7$I~JLq0}a;h(~z&oDbZkk;+j-Nh%{`_N#3oUY!5Owzd^>)vb;f}A@_vEA*!Nvq` zknuOD4P=ve!o;D^x;_2+s>59oKPGFKiqh@V>v5kHRJ6F6d%9cVI5oWVyN_6&>`DifUPJ+HXgrw_cZ@2@n}M1-iGay<$QsxrLx zFd7e+RmS?6lTn=mB?Q^Ax~yzc8A!pppOi+9rKuHH`Xjvj#zs?mR0U?6sZ<)aYRd2g zU7OOyIlb1M&rjUXx=z#~8MXRG6mQuctx(a*(TDBhR~y4B_pRY+{LS7luH>KOFjk#z)4r3&qLD8#2|Bzly;Ue}wf3bglSdq-Fg z3gd|M2TYmQsA6dmy_;QUwQdA?-nYN`9rq?fL?S@ zC>3jih&+_}g(sE1;wJlhTYC^qUhagXO5I3SNyn~b&x;b4viC({Ph@b`x<7O1dtj5OtY@`cf9w9+pRsj}!sKlXAWA9iu*E8yj^Z{YR=3=D zDDJL+SV*f@8GF?OsdfM=)?f)kxOCuKn2u|t9p9X8LI z&?Qv$13DuCWP}fovi6nmECVm2HRy&g>RNQ_K%!x=o%>OS&?Aps6vGfu*FM!{@P;!5gqrG7f=DYyK9-@7&dhp zc~6o2BN=#kd65mK|8rFrxy#nO-jCvcmP_`PPl)Z5eZErd7lAzLKn-;oGCFMjXG~$* zgVYgXgvPT>1QX5(gpJlG1JbNKQ~|e4jw`(s%yK?Gt?W*di|sBF*ZWmFTfHERXCKa#+0k4 zb6&bN~Pr1-lMH+nUEPzd#CQZ;>mOes6sw~EVfj}Oj)$F zW0lpl$jzw{wvFp6_@oJ}Z#{eN{|F@lGlQK$Qb-0l!=$oTf6D!VRJ8byHq^`lEqdOh0u{B)^MkmNZa?`nH8`_4=e@5$Tw{I zDoYd6y2*WvYHG7)N~v3dQiVTdRA6ov>V^5 zij@J-OX042R)?}=&(`q@CSD#0y_AQEEWEb}9td0@hrfA$_|=VL0+Auh49G#40!zgx zC~jpK1dHRD)s5()ItprnWj2~e$9mPf1uV>HD4Yc7y^O@h;seoiAn#s3^40Y$Z&kxE znkJ;pmjyUE*NyS2sVN_&cYJ}Hew`|O?#%j6Fy~mWt!gdSUa=61rAIUG%V&7JR~Z#% z?RHjHsMETZ)UU-P2g8z!iS%>ynY*fKk;aY&Co*@*qoWv41$af zH&c2OU6-C4uu8D9tK6!ghAq#H8j^fy>O-bXiP-o0iPI>Io#% z`k6<$^_3wl7mI1TaT#mKVPl{ag%bghQ5>5w|3uI44}(&-Kk-t_k(CzH4j{JkOsd7o zsy~(e@yc6aM?&k&+Sj84g&2|8x@EKa6lQG2DEsGUm51x!m%5P|pS(h(5Ot_UXtpYh zS%b{_)5*Da4{SE2xbn)wT_h+YUiD1u z;mS~DwqaR|^zH_F;qjhSR6$tvPCm*26jffBHM0XXRE1ud&g1ujy$_{g?W~3fIL|x- zWxfImQ`n?tWF&th1!Y4tI)doGG|S~zzf+k?%odSN;6bjIa;T~bLYepyc{CMfB!;7O z@rtSpDCyD6k~23yikl-3E0Q^1a*pIn<>!kk4!fa2#Iw+usB}l#6ItqxMM2ef-fhbM zAN@R=Lx52YooSGlwZyI{h4J^3X*&pGpcIKdb$}uV)X!JYwxSy zl+@q^X=>Mt@a?ZmDM!D{n!qYE0g!xngW=cJW}7l#*@w-K2*VvI}`RS zEoaeSlnb{xZ(Ea%^1Zev!CsHe0hj<>lsW_Uo=UTb??&`WGgBBI=~u>7R}|C4`XX*a zWC++447ha1V!N}XQnrYgMpSxjU9zuP*Am}nvZk=4wj+<#Tz zcZ9+p>d3Q-ybR_US+=A{@Yt(+h7S4kcyOf{M)49@X25@suWjhcxA#nOKcsKp`pq_-sQ*d zd_OvLUB%%)=MjHj&hDSr;{|M1g@5J=xj@D)0}p*W^y};E|9Ak_VyFU85n|@*KjNi? zCyKYRi=Z<^oRL372ZyCL7wETJ;fJm}dA-W-l^8!#3QSor&I@1U!x(vd2jMar))Q0R k`}JNm70jM5TCe{e0F7Q4wsgLEmjD0&07*qoM6N<$g4&$$OaK4? literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/items/cloud.png b/src/main/resources/assets/unicopia/textures/items/cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..1758f53ae214daebd6a710e7befdeb93bc684926 GIT binary patch literal 2965 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 LNkvXXu0mjf3O|JA literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/items/cloud_matter.png b/src/main/resources/assets/unicopia/textures/items/cloud_matter.png new file mode 100644 index 0000000000000000000000000000000000000000..5df77d6e19ea83a1ee568f4898b6a9bc4b643dc8 GIT binary patch literal 3188 zcmV-)42$!LP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0004>NklWmWz=;|+qUF+E>8^7GzH*(zat{VaST8hh76F)vLwqgj4^LZ2mqZ< zr(GC^oK7cBrxV5)M1<991%US+?>){reKP2}u1k_+>_F2r+-|p#!=S3F-a}k077XCp zwnaqPY&HNiO+#JRWLZWOMU-X9<#KtR18XhI<#Mc7Rr&h*Vm6!6_x%e4=iIXa5L#=y zVW>oe?RLxIaNv8}{k5z^8?1(w&l4