From c2b80ce5fd042e3c946914858e5d669591add24a Mon Sep 17 00:00:00 2001 From: Cheeserolls Date: Fri, 3 Apr 2015 18:33:58 +0100 Subject: [PATCH] Add double-height decorations (flax) --- .../biomesoplenty/api/block/BOPBlocks.java | 2 + .../common/block/BlockDecoration.java | 9 +- .../common/block/BlockDoubleDecoration.java | 229 ++++++++++++++++++ .../common/block/BlockDoubleFoliage.java | 178 ++++++++++++++ .../biomesoplenty/common/init/ModBlocks.java | 3 +- .../blockstates/double_foliage.json | 6 + .../models/block/flax_lower.json | 6 + .../models/block/flax_upper.json | 8 + .../biomesoplenty/models/item/flax.json | 18 ++ .../textures/blocks/flax_flowers.png | Bin 0 -> 567 bytes .../textures/blocks/flax_lower.png | Bin 0 -> 596 bytes .../textures/blocks/flax_upper.png | Bin 0 -> 524 bytes .../biomesoplenty/textures/items/flax.png | Bin 0 -> 15571 bytes 13 files changed, 453 insertions(+), 6 deletions(-) create mode 100644 src/main/java/biomesoplenty/common/block/BlockDoubleDecoration.java create mode 100644 src/main/java/biomesoplenty/common/block/BlockDoubleFoliage.java create mode 100644 src/main/resources/assets/biomesoplenty/blockstates/double_foliage.json create mode 100644 src/main/resources/assets/biomesoplenty/models/block/flax_lower.json create mode 100644 src/main/resources/assets/biomesoplenty/models/block/flax_upper.json create mode 100644 src/main/resources/assets/biomesoplenty/models/item/flax.json create mode 100644 src/main/resources/assets/biomesoplenty/textures/blocks/flax_flowers.png create mode 100644 src/main/resources/assets/biomesoplenty/textures/blocks/flax_lower.png create mode 100644 src/main/resources/assets/biomesoplenty/textures/blocks/flax_upper.png create mode 100644 src/main/resources/assets/biomesoplenty/textures/items/flax.png diff --git a/src/main/java/biomesoplenty/api/block/BOPBlocks.java b/src/main/java/biomesoplenty/api/block/BOPBlocks.java index e0b56e4cc..749cf1b74 100644 --- a/src/main/java/biomesoplenty/api/block/BOPBlocks.java +++ b/src/main/java/biomesoplenty/api/block/BOPBlocks.java @@ -117,6 +117,8 @@ public class BOPBlocks public static Block pine_sapling; public static Block mahogany_sapling; + public static Block double_foliage; + } diff --git a/src/main/java/biomesoplenty/common/block/BlockDecoration.java b/src/main/java/biomesoplenty/common/block/BlockDecoration.java index 0419f2046..3267713a5 100644 --- a/src/main/java/biomesoplenty/common/block/BlockDecoration.java +++ b/src/main/java/biomesoplenty/common/block/BlockDecoration.java @@ -55,21 +55,20 @@ public class BlockDecoration extends Block implements IBOPBlock } // utility function for setting the block bounds - typically decoration blocks are smaller than full block size - public BlockDecoration setBlockBoundsByRadiusAndHeight(float radius, float height) + public void setBlockBoundsByRadiusAndHeight(float radius, float height) { - return this.setBlockBoundsByRadiusAndHeight(radius,height,false); + this.setBlockBoundsByRadiusAndHeight(radius,height,false); } - public BlockDecoration setBlockBoundsByRadiusAndHeight(float radius, float height, boolean fromTop) + public void setBlockBoundsByRadiusAndHeight(float radius, float height, boolean fromTop) { this.setBlockBounds(0.5F - radius, (fromTop ? 1.0F - height : 0.0F), 0.5F - radius, 0.5F + radius, (fromTop ? 1.0F : height), 0.5F + radius); - return this; } // add a canBlockStay() check before placing this block @Override public boolean canReplace(World world, BlockPos pos, EnumFacing side, ItemStack stack) { - return super.canReplace(world, pos, side, stack) && this.canBlockStay(world, pos, this.getStateFromMeta(stack.getMetadata())); + return world.getBlockState(pos).getBlock().isReplaceable(world, pos) && this.canBlockStay(world, pos, this.getStateFromMeta(stack.getMetadata())); } // check this block is still able to remain after neighbor change diff --git a/src/main/java/biomesoplenty/common/block/BlockDoubleDecoration.java b/src/main/java/biomesoplenty/common/block/BlockDoubleDecoration.java new file mode 100644 index 000000000..78ee2035f --- /dev/null +++ b/src/main/java/biomesoplenty/common/block/BlockDoubleDecoration.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright 2014, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.block; + +import java.util.List; + +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockState; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.IStringSerializable; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +class BlockDoubleDecoration extends BlockDecoration { + + // add half property + public static enum Half implements IStringSerializable {LOWER, UPPER; public String getName() {return this.name().toLowerCase();}}; + public static final PropertyEnum HALF = PropertyEnum.create("half", Half.class); + protected BlockState createBlockState() {return new BlockState(this, new IProperty[] { HALF });} + + public float radius; + public float height; + public boolean fromTop; + + public BlockDoubleDecoration() + { + this(Material.plants); + } + public BlockDoubleDecoration(Material material) + { + super(material); + this.radius = 0.5F; + this.height = 1.0F; + this.fromTop = false; + this.setDefaultState(this.blockState.getBaseState().withProperty(HALF, Half.LOWER)); + } + + // map from state to meta and vice verca + @Override + public IBlockState getStateFromMeta(int meta) + { + return this.getDefaultState().withProperty(HALF, Half.values()[meta]); + } + @Override + public int getMetaFromState(IBlockState state) + { + return ((Half) state.getValue(HALF)).ordinal(); + } + + + + + // utility functions + public BlockPos getLowerPos(IBlockAccess world, BlockPos pos) + { + IBlockState state = world.getBlockState(pos); + if (state.getBlock() != this) {return pos;} + return world.getBlockState(pos).getValue(HALF) == Half.UPPER ? pos.down() : pos; + } + public BlockPos getUpperPos(IBlockAccess world, BlockPos pos) + { + IBlockState state = world.getBlockState(pos); + if (state.getBlock() != this) {return pos.up();} + return world.getBlockState(pos).getValue(HALF) == Half.UPPER ? pos : pos.up(); + } + public IBlockState getLowerState(IBlockAccess world, BlockPos pos) + { + return world.getBlockState(this.getLowerPos(world, pos)); + } + public IBlockState getUpperState(IBlockAccess world, BlockPos pos) + { + return world.getBlockState(this.getUpperPos(world, pos)); + } + public boolean isValidDoubleBlock(IBlockAccess world, BlockPos pos) + { + IBlockState lowerState = this.getLowerState(world, pos); + IBlockState upperState = this.getUpperState(world, pos); + return lowerState.getBlock() == this && lowerState.getValue(HALF) == Half.LOWER && upperState.getBlock() == this && upperState.getValue(HALF) == Half.UPPER; + } + + + + // drop block as item if it cannot remain here - return whether on not it could stay + @Override + protected boolean checkAndDropBlock(World worldIn, BlockPos pos, IBlockState state) + { + if (this.isValidDoubleBlock(worldIn, pos) && this.canBlockStay(worldIn, this.getLowerPos(worldIn, pos), state)) + { + return true; + } + else + { + this.dropBlockAsItem(worldIn, pos, state, 0); + worldIn.setBlockState(pos, Blocks.air.getDefaultState(), 3); + return false; + } + } + + // This DoubleDecoration can replace the block at pos if both the block and the one above it are replaceable and the environment is suitable (canBlockStay) + @Override + public boolean canReplace(World world, BlockPos pos, EnumFacing side, ItemStack stack) + { + return world.getBlockState(pos).getBlock().isReplaceable(world, pos) && world.getBlockState(pos.up()).getBlock().isReplaceable(world, pos.up()) && this.canBlockStay(world, pos, this.getStateFromMeta(stack.getMetadata())); + } + + // Called by ItemBlock before the block is placed - the placed block must always be Half.LOWER + public IBlockState onBlockPlaced(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer) + { + return this.getStateFromMeta(meta).withProperty(HALF, Half.LOWER); + } + + // Called by ItemBlock after the (lower) block has been placed + // Use it to add the top half of the block + public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) { + worldIn.setBlockState(pos.up(), this.getStateFromMeta(stack.getMetadata()).withProperty(HALF, Half.UPPER), 3); + } + + @Override + // child classes should put code in here which checks the ground is ok + public boolean canBlockStay(World world, BlockPos lowerPos, IBlockState state) + { + return true; + } + + + + + + @Override + // utility function for setting the block bounds - + public void setBlockBoundsByRadiusAndHeight(float radius, float height, boolean fromTop) + { + this.radius = radius; + this.height = height; + this.fromTop = fromTop; + } + @Override + public void setBlockBoundsBasedOnState(IBlockAccess worldIn, BlockPos pos) + { + IBlockState state = worldIn.getBlockState(pos); + switch ((Half) state.getValue(HALF)) + { + case LOWER: + super.setBlockBoundsByRadiusAndHeight(this.radius, this.fromTop ? this.height : 1.0F, this.fromTop); + break; + case UPPER: + super.setBlockBoundsByRadiusAndHeight(this.radius, this.fromTop ? 1.0F : this.height, this.fromTop); + break; + } + } + + + // handle drops from UPPER and LOWER separately + @Override + public List getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) + { + if (state.getValue(HALF) == Half.UPPER) + { + return this.getUpperDrops(world, this.getUpperPos(world, pos), this.getUpperState(world, pos), fortune); + } + else + { + return this.getLowerDrops(world, this.getLowerPos(world, pos), this.getLowerState(world, pos), fortune); + } + } + + // default behavior is that UPPER drops nothing, and LOWER drops the default item + public List getUpperDrops(IBlockAccess world, BlockPos upperPos, IBlockState upperState, int fortune) + { + return new java.util.ArrayList(); + } + public List getLowerDrops(IBlockAccess world, BlockPos lowerPos, IBlockState lowerState, int fortune) + { + return super.getDrops(world, lowerPos, lowerState, fortune); + } + + // if a child class chooses to implement IShearable make shearing the upper or lower block act as shearing both + public List onSheared(ItemStack item, IBlockAccess world, BlockPos pos, int fortune) { + + List drops = new java.util.ArrayList(); + drops.addAll( this.getUpperShearDrops(item, world, this.getUpperPos(world, pos), this.getUpperState(world, pos), fortune) ); + drops.addAll( this.getLowerShearDrops(item, world, this.getLowerPos(world, pos), this.getLowerState(world, pos), fortune) ); + + // whichever half was sheared, turn the other to air (to prevent it dropping an additional item when it pops) + if (world instanceof World) + { + ((World)world).setBlockToAir( pos.add(0, world.getBlockState(pos).getValue(HALF) == Half.UPPER ? -1 : 1, 0) ); + } + + return drops; + } + + // default behavior is that UPPER drops nothing, and LOWER drops the default item + public List getUpperShearDrops(ItemStack item, IBlockAccess world, BlockPos upperPos, IBlockState upperState, int fortune) { + return new java.util.ArrayList(); + } + public List getLowerShearDrops(ItemStack item, IBlockAccess world, BlockPos lowerPos, IBlockState lowerState, int fortune) { + return super.getDrops(world, lowerPos, lowerState, fortune); + } + + + + // discard the HALF info in the items dropped - make them all Half.LOWER so that they can stack - keep other state info such as VARIANT + @Override + public int damageDropped(IBlockState state) + { + return this.getMetaFromState( state.withProperty(HALF, Half.LOWER) ); + } + + + + + + + +} \ No newline at end of file diff --git a/src/main/java/biomesoplenty/common/block/BlockDoubleFoliage.java b/src/main/java/biomesoplenty/common/block/BlockDoubleFoliage.java new file mode 100644 index 000000000..eb7095427 --- /dev/null +++ b/src/main/java/biomesoplenty/common/block/BlockDoubleFoliage.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright 2014, the Biomes O' Plenty Team + * + * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. + * + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + ******************************************************************************/ + +package biomesoplenty.common.block; + +import java.util.List; +import java.util.Random; + +import biomesoplenty.api.block.BOPBlocks; +import net.minecraft.block.Block; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockState; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.BlockPos; +import net.minecraft.util.IStringSerializable; +import net.minecraft.world.ColorizerGrass; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeColorHelper; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.IShearable; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public class BlockDoubleFoliage extends BlockDoubleDecoration implements IShearable +{ + + // add properties (note we inherit HALF from BlockDoubleDecoration) + public static enum FoliageType implements IStringSerializable {FLAX; public String getName() {return this.name().toLowerCase();}}; + public static final PropertyEnum VARIANT = PropertyEnum.create("variant", FoliageType.class); + @Override + protected BlockState createBlockState() {return new BlockState(this, new IProperty[] { HALF, VARIANT });} + + public BlockDoubleFoliage() + { + super(); + + // define named states + for(FoliageType foliageType : FoliageType.values()) + { + this.namedStates.put(foliageType.getName(), this.blockState.getBaseState().withProperty(HALF, Half.LOWER) .withProperty(VARIANT, foliageType)); + } + this.setDefaultState(this.getNamedState("flax")); + } + + // map from state to meta and vice verca - use highest bit for Half, and lower bits for variant + @Override + public IBlockState getStateFromMeta(int meta) + { + return this.getDefaultState().withProperty(HALF, Half.values()[meta >> 3]).withProperty(VARIANT, FoliageType.values()[meta & 7]); + } + @Override + public int getMetaFromState(IBlockState state) + { + return ((Half) state.getValue(HALF)).ordinal() * 8 + ((FoliageType) state.getValue(VARIANT)).ordinal(); + } + + + + // TODO: comment these + @Override + @SideOnly(Side.CLIENT) + public int getBlockColor() + { + return ColorizerGrass.getGrassColor(0.5D, 1.0D); + } + + @Override + @SideOnly(Side.CLIENT) + public int getRenderColor(IBlockState state) + { + return this.getBlockColor(); + } + + @Override + @SideOnly(Side.CLIENT) + public int colorMultiplier(IBlockAccess worldIn, BlockPos pos, int renderPass) + { + switch ((FoliageType) worldIn.getBlockState(pos).getValue(VARIANT)) + { + default: + return BiomeColorHelper.getGrassColorAtPos(worldIn, pos); + } + } + + + // different variants have different sizes + @Override + public void setBlockBoundsBasedOnState(IBlockAccess worldIn, BlockPos pos) + { + switch ((FoliageType) worldIn.getBlockState(pos).getValue(VARIANT)) + { + default: + this.setBlockBoundsByRadiusAndHeight(0.4F, 0.8F); + break; + } + } + + + + @Override + public boolean canBlockStay(World world, BlockPos lowerPos, IBlockState state) + { + IBlockState groundState = world.getBlockState(lowerPos.down()); + Block groundBlock = groundState.getBlock(); + boolean onFertile = (groundBlock == Blocks.dirt || groundBlock == BOPBlocks.dirt || groundBlock == Blocks.mycelium || groundBlock == Blocks.grass); + if (groundBlock instanceof BlockBOPGrass) + { + switch ((BlockBOPGrass.BOPGrassType) groundState.getValue(BlockBOPGrass.VARIANT)) + { + case SPECTRAL_MOSS: case SMOLDERING: + break; + case OVERGROWN_NETHERRACK: case LOAMY: case SANDY: case SILTY: case ORIGIN: default: + onFertile = true; + break; + } + } + return onFertile; + + } + + + // get the items dropped when you bash the bush + @Override + public List getLowerDrops(IBlockAccess world, BlockPos lowerPos, IBlockState lowerState, int fortune) + { + Random rand = world instanceof World ? ((World)world).rand : RANDOM; + + // start with an empty stack + List ret = new java.util.ArrayList(); + + // add items based on the VARIANT - default is to return nothing (require shears to collect the block) + switch ((FoliageType) lowerState.getValue(VARIANT)) + { + case FLAX: + if (rand.nextInt(8) == 0) + { + // 1 in 8 chance of getting a seed from this grass + ret.add(ForgeHooks.getGrassSeed(rand)); + } + + default: + break; + } + return ret; + } + + + @Override + public boolean isShearable(ItemStack item, IBlockAccess world, BlockPos pos) { + return true; + } + + @Override + public List getLowerShearDrops(ItemStack item, IBlockAccess world, BlockPos lowerPos, IBlockState lowerState, int fortune) { + // start with an empty stack + List ret = new java.util.ArrayList(); + + // add items based on the VARIANT + switch ((FoliageType) lowerState.getValue(VARIANT)) + { + default: + // default is to get the (lower) block unaltered + ret.add(new ItemStack(this, 1, this.getMetaFromState(lowerState.withProperty(HALF, Half.LOWER) ))); + } + return ret; + } + + +} \ No newline at end of file diff --git a/src/main/java/biomesoplenty/common/init/ModBlocks.java b/src/main/java/biomesoplenty/common/init/ModBlocks.java index bb1a4ddd2..f96d0c939 100644 --- a/src/main/java/biomesoplenty/common/init/ModBlocks.java +++ b/src/main/java/biomesoplenty/common/init/ModBlocks.java @@ -46,6 +46,7 @@ import biomesoplenty.common.block.BlockBamboo; import biomesoplenty.common.block.BlockBones; import biomesoplenty.common.block.BlockCoral; import biomesoplenty.common.block.BlockCrystal; +import biomesoplenty.common.block.BlockDoubleFoliage; import biomesoplenty.common.block.BlockFoliage; import biomesoplenty.common.block.BlockFruit; import biomesoplenty.common.block.BlockGem; @@ -231,7 +232,7 @@ public class ModBlocks dark_leaves = registerBlock( new BlockBOPLeaves( new ItemStack(dark_sapling, 1, 0), new ItemStack(Items.apple, 1, 0), 20, true ), "dark_leaves" ); bamboo_leaves = registerBlock( new BlockBOPLeaves( new ItemStack(bamboo_sapling, 1, 0), new ItemStack(Items.apple, 1, 0), 20, true ), "bamboo_leaves" ); - + double_foliage = registerBlock( new BlockDoubleFoliage(), "double_foliage" ); } diff --git a/src/main/resources/assets/biomesoplenty/blockstates/double_foliage.json b/src/main/resources/assets/biomesoplenty/blockstates/double_foliage.json new file mode 100644 index 000000000..3e645d109 --- /dev/null +++ b/src/main/resources/assets/biomesoplenty/blockstates/double_foliage.json @@ -0,0 +1,6 @@ +{ + "variants": { + "half=upper,variant=flax": { "model": "biomesoplenty:flax_upper" }, + "half=lower,variant=flax": { "model": "biomesoplenty:flax_lower" } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/biomesoplenty/models/block/flax_lower.json b/src/main/resources/assets/biomesoplenty/models/block/flax_lower.json new file mode 100644 index 000000000..3d976e09c --- /dev/null +++ b/src/main/resources/assets/biomesoplenty/models/block/flax_lower.json @@ -0,0 +1,6 @@ +{ + "parent": "block/tallgrass", + "textures": { + "cross": "biomesoplenty:blocks/flax_lower" + } +} diff --git a/src/main/resources/assets/biomesoplenty/models/block/flax_upper.json b/src/main/resources/assets/biomesoplenty/models/block/flax_upper.json new file mode 100644 index 000000000..dfab1d144 --- /dev/null +++ b/src/main/resources/assets/biomesoplenty/models/block/flax_upper.json @@ -0,0 +1,8 @@ +{ + "parent": "biomesoplenty:block/cross_with_overlay", + "textures": { + "colored": "biomesoplenty:blocks/flax_flowers", + "greyscale": "biomesoplenty:blocks/flax_upper", + "particle": "biomesoplenty:blocks/flax_upper" + } +} diff --git a/src/main/resources/assets/biomesoplenty/models/item/flax.json b/src/main/resources/assets/biomesoplenty/models/item/flax.json new file mode 100644 index 000000000..020c4d495 --- /dev/null +++ b/src/main/resources/assets/biomesoplenty/models/item/flax.json @@ -0,0 +1,18 @@ +{ + "parent": "builtin/generated", + "textures": { + "layer0": "biomesoplenty:items/flax" + }, + "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/biomesoplenty/textures/blocks/flax_flowers.png b/src/main/resources/assets/biomesoplenty/textures/blocks/flax_flowers.png new file mode 100644 index 0000000000000000000000000000000000000000..f136990328c59774a21d34d94d2b2ec9e1d4a690 GIT binary patch literal 567 zcmV-70?7S|P)N2bZe?^J zG%heMGmPe!Pyhe{wMj%lR5(w4l1Xn9K@f%e4|6~WK`bGJL}Z*`Cnka*C1hbcz=b<^ zlzXJyxLL8hFEif8%VWMW)tLy1!|U!URlR=ox`QM`qtOV{>D11P#UiZN>#$m_4CFSO zO{^{AUO;1(W!BX1_hY`->xJcVX`sf*sEb98{d0-J!C(+(vso;DT>I1GoM zr8G^WPmo6fW@L9FP^t~3-v@fQ+swl|l@M?dYKcRq(+RbY+qp?#3`TN(`93oRoPm#Y z@JYJ;`9SPpr#g^sUI`MMM<$Z-pw(*GyiQR6BJtduPk-cVzT1t=N5BXuDZaV?gXc#R zGx*7V&uSgE(BM+J`Q0~((TYUm_s%3Ibl5T)jbh?iJ<;ZRSFbPf=x@%6*m1s?j{$Ha zd2i2kyItEc3>BVt{F88}0k>gd z$Pxrd<$Dst%u{;9|6p_boB+x0g~LBqyWNgCE=bcf`~e;E_^>?(v_1d;002ovPDHLk FV1k^s{*3?t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/biomesoplenty/textures/blocks/flax_lower.png b/src/main/resources/assets/biomesoplenty/textures/blocks/flax_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..c7417022ec3c8de2bd56c5657fe8a43551969492 GIT binary patch literal 596 zcmV-a0;~OrP)N2bZe?^J zG%heMGmPe!Pyhe{(n&-?R5(vnQ)i2^K@5C63zkpf@3SBluotjk?+q*V@?$xc>FQLEMDV-oB2n!eBHqdyvrOuX4_()M^f^od+9C(Gs19C|*Vv=@to z*zI;}yWL8=-6jVcd$-#aQvnzbhcX_I85|4-(&=y^T2`&vO~ua74l zj|;Ab-RX3G3kVd8MIJx`ybTBnwZY+P`~BXC_NVf0w-cAkWtB=LL!!d(JK_T!BT%o` z1)_6OXajRcc03Nh-%sxGc<^b%#)1aeF(rtsycGxp*hMOpB8kTYwILE-A~=jp<5&<^ zZT@7lSt@=$pQTVJNGKE%a9*#Mu}CB$+5yhRVlj@@B*2PBqo!s2wIL#Gx7$s9!r?Hd i6buH*qoYhFBmV&4jZOV^#Wy1W0000N2bZe?^J zG%heMGmPe!Pyhe{ib+I4R5(w4Q^~3VF%XRV?#aKJU+|)+AmXkAinv_VgNh5H;?b-B z*pDiw;{@Rq45{=|>F#8VBAreL%tbUB4cHfI%jI%h9mHa>fEg2y$89ipCX*poWV6{1 z(V#>kVIq+TxdJwzSS&J5{Z1^@!+`U0xrkPyBL5Ohnt7&1OtoEEYea4lv>Mdd(o;_odV6*b^(2icF_d z?w`-6wf~^o?b3HXA2tKA(%;9IvNVt1)0W9CCfX-)1}>lcVI-YUNba zXf(cp`T|Fzk)Q=6>(}e`7>EfqTWPgga=+gxg0j&&eBd~!TrSJ;c%<$1 zdPzQ?H)gln(V+cne>(lg<3T}vwiqU2Q>oPVJ)l2L03cGWR_$JW{eE9iMYGwoKB8W) z%k6ff5vO~eC*jFM5Lc@$fw$Xj;hR7iAj{>FPW=PJg9G;ay(9Qx#{2=d=(Y2n*@lh) O0000AtX$a7Dz~4nn0%pLTH0Y+v$v%kOWVLBos(O83#j4_as@8 z*Yb|rrqh2`Gy3uN?f1RC&%U?1e{Ms~!UYBS-^-^cs=!@UQ47B(sqeho;pe?qZ}IT! z+u^FEGDS`7Ro^+=N+wRGsM3D1zCmg5{D2drppN%SO+Xh5hT&|Avd@i$d7%X;bQAE4 zA&2J5;UgMa^g1+4%pScb>;lbVRYwHWbu6qGI$DHMuV$_@-yY*&fFMwKIu;CsWG?2= z#N%@Cw>r#d==c<+#i1!z6VeTy8rmgA0BzQpwSwNj($-QPYc^Z0hB>rB&zcxL%dkc* zTgsVCoL)~SUm9mV{AG`LeOztD{A4<4IW*0R66P4Dt*uShX4FX$Kf{)mmNI$+V=!pp z46WQAQuvrQB;S_^G90G@$U;O6E20#l)wq0<6jdA=jhZO+N?unmoJtgulk6ZxCdP*u zR;OpOiM&Ee6OKj#@uhhM1_VG5gcKR-*sQv6v!qCJvouD^lzYs?a8Ep*RPLJ`zK-kcGURL_`e2QhIBnd(W%;-!$- zAv?TQ&J_W?B1P&YDd0?0SS1<`O*%%Q_JwV&_8LD`haqf_;@F4-X zE1YmZCyHLK2^einR+~jDu!5j9HJJo0Z?hV-7T#tt`OLrzY-}8W$CD`^6}mzaqH3!o zLwjLptKMfYTWvnA!6%rtCf3@dsONq_{2%Os%&-3M_F#NX^q=iP)&uGPxe@6i7Ml5xA9$V2aQzMsjBoD} zJ36wlhR=i1jc8Z@ia6NBu&VfY^T2ay)L1%mVt#|#jDcu;eyK$dUks~$CZ(BfRJs%| z&2*#Ev~qDsRGfxnHe@(MrT$+g&ht8WYX_Vbv)O32Gw3j?s1aiz&`=@5gI89M6V{T{ zppLA7Xk!t5?pvbh?n3(fZ$Dn z3(p7f5?lxnyh(83`5<0`3ju;R2`)Sz#7l4?K=3BPh3A8K2`&T(-Xys2d=M|eg#f{u z1Q(tU;w88cAb6AD!t+7A1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J5WGon;rSq5f(rqH zHwi90AH+*=AwcjZ!G-68cnK~92;L;P@O%(2!G!?9n*!X`!mtLs1>Y6xI0{MP0uIzdxg>sGg!eZ=@)02SpW1TOK|(pQ64c zyDQ4;WBo5|IaPhvqM~DWyfJj4=*^kC>0{fMY+j#p+wyg1X7v`%e|52tcW2q_wDYbH zJLh-@&N*78^Y2aFJZYNiX~WbVJy+#Lxvua9&*k$sF28j|UifxN*{y?vrsL-xyU>1M z&O^tV7L`xR>-*UmV6j+U@9CLcwrSI*M{>3ve6s7b{)^u}&dz`BVt(Ji6OE6a`YJMU z9sML&+TY*5C3v>Dtm>M*TDX!^W?p(gUYhcCXWiPbs-r2Wj!1k5Bdh5aB=~{*i#;R_(0W z59jFL|JrnOsAI#IV&A0`-@REh-8!MArR9OU^YrUuXwc^>?$Z@EkN?d6lx=AHfI<@`CP-v4y@MDJn