add a way to stop positive effects from happening

This commit is contained in:
Ellpeck 2018-12-14 00:47:01 +01:00
parent 304834893e
commit 8f33476403
21 changed files with 345 additions and 9 deletions

View file

@ -6,9 +6,11 @@ import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk;
import de.ellpeck.naturesaura.api.multiblock.IMultiblock;
import de.ellpeck.naturesaura.blocks.multi.Multiblock;
import de.ellpeck.naturesaura.compat.Compat;
import de.ellpeck.naturesaura.entities.EntityEffectInhibitor;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
@ -18,6 +20,7 @@ import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.mutable.MutableObject;
import org.lwjgl.util.vector.Vector3f;
import java.util.List;
import java.util.function.BiConsumer;
public class InternalHooks implements NaturesAuraAPI.IInternalHooks {
@ -73,6 +76,15 @@ public class InternalHooks implements NaturesAuraAPI.IInternalHooks {
return new Multiblock(name, pattern, rawMatchers);
}
@Override
public boolean isEffectInhibited(World world, BlockPos pos, ResourceLocation name, int radius) {
List<EntityEffectInhibitor> inhibitors = world.getEntitiesWithinAABB(
EntityEffectInhibitor.class,
new AxisAlignedBB(pos).grow(radius),
entity -> entity.getDistanceSq(pos) <= radius * radius && name.equals(entity.getInhibitedEffect()));
return !inhibitors.isEmpty();
}
@Override
public void getAuraSpotsInArea(World world, BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) {
world.profiler.func_194340_a(() -> NaturesAura.MOD_ID + ":getSpotsInArea");

View file

@ -9,6 +9,7 @@ import de.ellpeck.naturesaura.blocks.multi.Multiblocks;
import de.ellpeck.naturesaura.chunk.effect.DrainSpotEffects;
import de.ellpeck.naturesaura.commands.CommandAura;
import de.ellpeck.naturesaura.compat.Compat;
import de.ellpeck.naturesaura.entities.ModEntities;
import de.ellpeck.naturesaura.events.CommonEvents;
import de.ellpeck.naturesaura.items.ModItems;
import de.ellpeck.naturesaura.packet.PacketHandler;
@ -68,6 +69,7 @@ public final class NaturesAura {
Compat.preInit();
PacketHandler.init();
ModRegistry.preInit(event);
ModEntities.init();
new Multiblocks();
MinecraftForge.EVENT_BUS.register(new CommonEvents());

View file

@ -91,6 +91,15 @@ public final class NaturesAuraAPI {
* once a second for every drain spot currently loaded.
*/
public static final Map<ResourceLocation, Supplier<IDrainSpotEffect>> DRAIN_SPOT_EFFECTS = new HashMap<>();
/**
* A map of all {@link IDrainSpotEffect} names (registered in {@link
* #DRAIN_SPOT_EFFECTS}) that can be inhibited using the inhibiting powder.
* The integer the effect is registered to is the color that the powder and
* its effect should have. To check if an effect should be inhibited, use
* {@link IInternalHooks#isEffectInhibited(World, BlockPos,
* ResourceLocation, int)}
*/
public static final Map<ResourceLocation, Integer> INHIBITED_EFFECTS = new HashMap<>();
/**
* A map of all {@link IMultiblock} objects which are multiblock structures
* that can easily be looped through and checked, and also easily created
@ -222,6 +231,20 @@ public final class NaturesAuraAPI {
*/
IMultiblock createMultiblock(ResourceLocation name, String[][] pattern, Object... rawMatchers);
/**
* Returns true if there is an effect inhibitor entity in the given
* radius from the given position that inhibits the {@link
* IDrainSpotEffect} with the given name.
*
* @param world The world
* @param pos The center position
* @param name The registry name of the {@link IDrainSpotEffect} to
* check for
* @param radius The radius around the center to check for
* @return If the effect is currently inhibited by any inhibitors
*/
boolean isEffectInhibited(World world, BlockPos pos, ResourceLocation name, int radius);
/**
* @see IAuraChunk#getSpotsInArea(World, BlockPos, int, BiConsumer)
*/

View file

@ -31,6 +31,11 @@ public class StubHooks implements NaturesAuraAPI.IInternalHooks {
return new StubMultiblock();
}
@Override
public boolean isEffectInhibited(World world, BlockPos pos, ResourceLocation name, int radius) {
return false;
}
@Override
public void getAuraSpotsInArea(World world, BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) {

View file

@ -12,5 +12,7 @@ public final class DrainSpotEffects {
NaturesAuraAPI.DRAIN_SPOT_EFFECTS.put(ExplosionEffect.NAME, ExplosionEffect::new);
NaturesAuraAPI.DRAIN_SPOT_EFFECTS.put(BreathlessEffect.NAME, BreathlessEffect::new);
NaturesAuraAPI.DRAIN_SPOT_EFFECTS.put(SpreadEffect.NAME, SpreadEffect::new);
NaturesAuraAPI.INHIBITED_EFFECTS.put(PlantBoostEffect.NAME, 0xc2f442);
}
}

View file

@ -39,6 +39,9 @@ public class PlantBoostEffect implements IDrainSpotEffect {
int z = MathHelper.floor(pos.getZ() + world.rand.nextGaussian() * dist);
BlockPos plantPos = new BlockPos(x, world.getHeight(x, z), z);
if (plantPos.distanceSq(pos) <= dist * dist && world.isBlockLoaded(plantPos)) {
if (NaturesAuraAPI.instance().isEffectInhibited(world, plantPos, NAME, 15))
continue;
IBlockState state = world.getBlockState(plantPos);
Block block = state.getBlock();
if (block instanceof IGrowable &&

View file

@ -61,7 +61,7 @@ public final class OfferingTweaker {
private static class Remove extends BaseMapRemoval<ResourceLocation, OfferingRecipe> {
protected Remove(Map<ResourceLocation, OfferingRecipe> map) {
super("Tree Ritual", NaturesAuraAPI.OFFERING_RECIPES, map);
super("Offering", NaturesAuraAPI.OFFERING_RECIPES, map);
}
@Override

View file

@ -0,0 +1,91 @@
package de.ellpeck.naturesaura.entities;
import de.ellpeck.naturesaura.NaturesAura;
import de.ellpeck.naturesaura.items.ItemInhibitingPowder;
import de.ellpeck.naturesaura.items.ModItems;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
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.DamageSource;
import net.minecraft.util.EntityDamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
public class EntityEffectInhibitor extends Entity {
private static final DataParameter<String> INHIBITED_EFFECT = EntityDataManager.createKey(EntityEffectInhibitor.class, DataSerializers.STRING);
private static final DataParameter<Integer> COLOR = EntityDataManager.createKey(EntityEffectInhibitor.class, DataSerializers.VARINT);
public EntityEffectInhibitor(World worldIn) {
super(worldIn);
}
@Override
protected void entityInit() {
this.setSize(0.25F, 0.25F);
this.dataManager.register(INHIBITED_EFFECT, null);
this.dataManager.register(COLOR, 0);
}
@Override
protected void readEntityFromNBT(NBTTagCompound compound) {
this.setInhibitedEffect(new ResourceLocation(compound.getString("effect")));
this.setColor(compound.getInteger("color"));
}
@Override
protected void writeEntityToNBT(NBTTagCompound compound) {
compound.setString("effect", this.getInhibitedEffect().toString());
compound.setInteger("color", this.getColor());
}
@Override
public void onEntityUpdate() {
if (this.world.isRemote) {
if (this.world.getTotalWorldTime() % 5 == 0) {
NaturesAura.proxy.spawnMagicParticle(
this.posX + this.world.rand.nextGaussian() * 0.1F,
this.posY,
this.posZ + this.world.rand.nextGaussian() * 0.1F,
this.world.rand.nextGaussian() * 0.005F,
this.world.rand.nextFloat() * 0.03F,
this.world.rand.nextGaussian() * 0.005F,
this.getColor(), this.world.rand.nextFloat() * 3F + 1F, 120, 0F, true, true);
}
}
}
@Override
public boolean canBeCollidedWith() {
return true;
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount) {
if (source instanceof EntityDamageSource && !this.world.isRemote) {
this.setDead();
this.entityDropItem(ItemInhibitingPowder.setEffect(new ItemStack(ModItems.INHIBITING_POWDER), this.getInhibitedEffect()), 0F);
return true;
} else
return super.attackEntityFrom(source, amount);
}
public void setInhibitedEffect(ResourceLocation effect) {
this.dataManager.set(INHIBITED_EFFECT, effect.toString());
}
public ResourceLocation getInhibitedEffect() {
return new ResourceLocation(this.dataManager.get(INHIBITED_EFFECT));
}
public void setColor(int color) {
this.dataManager.set(COLOR, color);
}
public int getColor() {
return this.dataManager.get(COLOR);
}
}

View file

@ -0,0 +1,17 @@
package de.ellpeck.naturesaura.entities;
import de.ellpeck.naturesaura.NaturesAura;
import de.ellpeck.naturesaura.entities.render.RenderEffectInhibitor;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.EntityRegistry;
public final class ModEntities {
public static void init() {
EntityRegistry.registerModEntity(
new ResourceLocation(NaturesAura.MOD_ID, "effect_inhibitor"),
EntityEffectInhibitor.class, NaturesAura.MOD_ID + ".effect_inhibitor",
0, NaturesAura.MOD_ID, 64, 1, false);
NaturesAura.proxy.registerEntityRenderer(EntityEffectInhibitor.class, () -> RenderEffectInhibitor::new);
}
}

View file

@ -0,0 +1,48 @@
package de.ellpeck.naturesaura.entities.render;
import de.ellpeck.naturesaura.Helper;
import de.ellpeck.naturesaura.entities.EntityEffectInhibitor;
import de.ellpeck.naturesaura.items.ItemInhibitingPowder;
import de.ellpeck.naturesaura.items.ModItems;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
@SideOnly(Side.CLIENT)
public class RenderEffectInhibitor extends Render<EntityEffectInhibitor> {
private final Map<ResourceLocation, ItemStack> items = new HashMap<>();
public RenderEffectInhibitor(RenderManager renderManager) {
super(renderManager);
}
@Nullable
@Override
protected ResourceLocation getEntityTexture(EntityEffectInhibitor entity) {
return TextureMap.LOCATION_BLOCKS_TEXTURE;
}
@Override
public void doRender(EntityEffectInhibitor entity, double x, double y, double z, float entityYaw, float partialTicks) {
GlStateManager.pushMatrix();
float time = entity.world.getTotalWorldTime() + entity.getEntityId() + partialTicks;
float bob = (float) Math.sin(time / 10F) * 0.05F;
GlStateManager.translate(x, y + 0.15F + bob, z);
GlStateManager.rotate((time * 3) % 360, 0F, 1F, 0F);
GlStateManager.scale(0.5F, 0.5F, 0.5F);
ResourceLocation effect = entity.getInhibitedEffect();
Helper.renderItemInWorld(this.items.computeIfAbsent(effect,
res -> ItemInhibitingPowder.setEffect(new ItemStack(ModItems.INHIBITING_POWDER), effect)));
GlStateManager.popMatrix();
}
}

View file

@ -0,0 +1,76 @@
package de.ellpeck.naturesaura.items;
import de.ellpeck.naturesaura.api.NaturesAuraAPI;
import de.ellpeck.naturesaura.entities.EntityEffectInhibitor;
import de.ellpeck.naturesaura.reg.IColorProvidingItem;
import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.translation.I18n;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class ItemInhibitingPowder extends ItemImpl implements IColorProvidingItem {
public ItemInhibitingPowder() {
super("inhibiting_powder");
}
@Override
public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
if (!worldIn.isRemote) {
ItemStack stack = player.getHeldItem(hand);
ResourceLocation effect = getEffect(stack);
EntityEffectInhibitor entity = new EntityEffectInhibitor(worldIn);
entity.setInhibitedEffect(effect);
entity.setColor(NaturesAuraAPI.INHIBITED_EFFECTS.get(effect));
entity.setPosition(pos.getX() + hitX, pos.getY() + hitY + 1, pos.getZ() + hitZ);
worldIn.spawnEntity(entity);
stack.shrink(1);
}
return EnumActionResult.SUCCESS;
}
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items) {
if (this.isInCreativeTab(tab)) {
for (ResourceLocation effect : NaturesAuraAPI.INHIBITED_EFFECTS.keySet()) {
ItemStack stack = new ItemStack(this);
setEffect(stack, effect);
items.add(stack);
}
}
}
@Override
public String getItemStackDisplayName(ItemStack stack) {
return I18n.translateToLocal(this.getUnlocalizedNameInefficiently(stack) + "." + getEffect(stack) + ".name").trim();
}
public static ResourceLocation getEffect(ItemStack stack) {
if (!stack.hasTagCompound())
return null;
String effect = stack.getTagCompound().getString("effect");
if (effect.isEmpty())
return null;
return new ResourceLocation(effect);
}
public static ItemStack setEffect(ItemStack stack, ResourceLocation effect) {
if (!stack.hasTagCompound())
stack.setTagCompound(new NBTTagCompound());
stack.getTagCompound().setString("effect", effect.toString());
return stack;
}
@Override
@SideOnly(Side.CLIENT)
public IItemColor getItemColor() {
return (stack, tintIndex) -> NaturesAuraAPI.INHIBITED_EFFECTS.getOrDefault(getEffect(stack), 0xFFFFFF);
}
}

View file

@ -40,4 +40,5 @@ public final class ModItems {
public static final Item FARMING_STENCIL = new ItemImpl("farming_stencil");
public static final Item SKY_INGOT = new ItemImpl("sky_ingot");
public static final Item CALLING_SPIRIT = new ItemGlowing("calling_spirit");
public static final Item INHIBITING_POWDER = new ItemInhibitingPowder();
}

View file

@ -1,11 +1,5 @@
package de.ellpeck.naturesaura.proxy;
import de.ellpeck.naturesaura.blocks.tiles.TileEntityNatureAltar;
import de.ellpeck.naturesaura.blocks.tiles.TileEntityOfferingTable;
import de.ellpeck.naturesaura.blocks.tiles.TileEntityWoodStand;
import de.ellpeck.naturesaura.blocks.tiles.render.RenderNatureAltar;
import de.ellpeck.naturesaura.blocks.tiles.render.RenderOfferingTable;
import de.ellpeck.naturesaura.blocks.tiles.render.RenderWoodStand;
import de.ellpeck.naturesaura.events.ClientEvents;
import de.ellpeck.naturesaura.particles.ParticleHandler;
import de.ellpeck.naturesaura.particles.ParticleMagic;
@ -20,17 +14,21 @@ import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.client.renderer.color.ItemColors;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Tuple;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.client.registry.IRenderFactory;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import java.util.Map;
import java.util.function.Supplier;
public class ClientProxy implements IProxy {
@ -82,6 +80,11 @@ public class ClientProxy implements IProxy {
ClientRegistry.bindTileEntitySpecialRenderer(tesr.getFirst(), tesr.getSecond());
}
@Override
public <T extends Entity> void registerEntityRenderer(Class<T> entityClass, Supplier<IRenderFactory<T>> renderFactory) {
RenderingRegistry.registerEntityRenderingHandler(entityClass, renderFactory.get());
}
@Override
public void spawnMagicParticle(double posX, double posY, double posZ, double motionX, double motionY, double motionZ, int color, float scale, int maxAge, float gravity, boolean collision, boolean fade) {
ParticleHandler.spawnParticle(() -> new ParticleMagic(Minecraft.getMinecraft().world,

View file

@ -4,11 +4,15 @@ import de.ellpeck.naturesaura.reg.IColorProvidingBlock;
import de.ellpeck.naturesaura.reg.IColorProvidingItem;
import de.ellpeck.naturesaura.reg.ITESRProvider;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.client.registry.IRenderFactory;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import java.util.function.Supplier;
public interface IProxy {
void preInit(FMLPreInitializationEvent event);
@ -24,6 +28,8 @@ public interface IProxy {
void registerTESR(ITESRProvider provider);
<T extends Entity> void registerEntityRenderer(Class<T> entityClass, Supplier<IRenderFactory<T>> renderFactory);
void spawnMagicParticle(double posX, double posY, double posZ, double motionX, double motionY, double motionZ, int color, float scale, int maxAge, float gravity, boolean collision, boolean fade);
void scheduleTask(Runnable runnable);

View file

@ -4,12 +4,16 @@ import de.ellpeck.naturesaura.reg.IColorProvidingBlock;
import de.ellpeck.naturesaura.reg.IColorProvidingItem;
import de.ellpeck.naturesaura.reg.ITESRProvider;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.client.registry.IRenderFactory;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import java.util.function.Supplier;
public class ServerProxy implements IProxy {
@Override
@ -47,6 +51,11 @@ public class ServerProxy implements IProxy {
}
@Override
public <T extends Entity> void registerEntityRenderer(Class<T> entityClass, Supplier<IRenderFactory<T>> renderFactory) {
}
@Override
public void spawnMagicParticle(double posX, double posY, double posZ, double motionX, double motionY, double motionZ, int color, float scale, int maxAge, float gravity, boolean collision, boolean fade) {

View file

@ -9,7 +9,9 @@ import de.ellpeck.naturesaura.api.recipes.TreeRitualRecipe;
import de.ellpeck.naturesaura.api.recipes.ing.AmountIngredient;
import de.ellpeck.naturesaura.api.recipes.ing.NBTIngredient;
import de.ellpeck.naturesaura.blocks.ModBlocks;
import de.ellpeck.naturesaura.chunk.effect.PlantBoostEffect;
import de.ellpeck.naturesaura.items.ItemAuraBottle;
import de.ellpeck.naturesaura.items.ItemInhibitingPowder;
import de.ellpeck.naturesaura.items.ModItems;
import net.minecraft.block.Block;
import net.minecraft.block.BlockFlower;
@ -64,6 +66,13 @@ public final class ModRecipes {
Ingredient.fromItem(ModItems.SKY_INGOT),
Ingredient.fromItem(ModItems.GOLD_LEAF),
Helper.blockIng(Blocks.GLOWSTONE)).register();
new TreeRitualRecipe(new ResourceLocation(NaturesAura.MOD_ID, "plant_powder"),
Ingredient.fromStacks(new ItemStack(Blocks.SAPLING)),
ItemInhibitingPowder.setEffect(new ItemStack(ModItems.INHIBITING_POWDER), PlantBoostEffect.NAME), 400,
Helper.blockIng(ModBlocks.GOLD_POWDER),
Helper.blockIng(ModBlocks.GOLD_POWDER),
Ingredient.fromItem(ModItems.SKY_INGOT),
Ingredient.fromItem(Items.WHEAT)).register();
new AltarRecipe(new ResourceLocation(NaturesAura.MOD_ID, "infused_iron"),
Ingredient.fromItem(Items.IRON_INGOT), new ItemStack(ModItems.INFUSED_IRON),

View file

@ -63,6 +63,7 @@ item.naturesaura.infused_iron_helmet.name=Botanist's Headwear
item.naturesaura.infused_iron_chest.name=Botanist's Chestplate
item.naturesaura.infused_iron_pants.name=Botanist's Leggings
item.naturesaura.infused_iron_shoes.name=Botanist's Shoes
item.naturesaura.inhibiting_powder.naturesaura:plant_boost.name=Powder of Steady Growth
container.naturesaura.tree_ritual.name=Ritual of the Forest
container.naturesaura.altar.name=Natural Altar Infusion

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "naturesaura:items/inhibiting_powder"
}
}

View file

@ -0,0 +1,17 @@
{
"name": "Inhibition Powder",
"icon": "naturesaura:inhibiting_powder{effect:'naturesaura:plant_boost'}",
"category": "effects",
"advancement": "naturesaura:flower_generator",
"priority": true,
"pages": [
{
"type": "text",
"text": "Sometimes, the effects of an excess of $(aura) can become troublesome in certain situations. For this, $(item)Inhibition Powder$() can be of huge assistance: Some positive effects can be $(thing)inhibited$() in a certain radius by making a powder of their kind."
},
{
"type": "text",
"text": "Then, simply placing it down on the ground will cause it to stop the effect it is bound to from happening. Hitting it while it is on the ground will cause it to go back into its item form.$(p)Each effect that has an $(item)Inhibition Powder$() attached to it will show the recipe and radius in its chapter."
}
]
}

View file

@ -11,6 +11,11 @@
{
"type": "text",
"text": "Effects like these become apparent around the time that the meter of the $(l:items/eye)Environmental Eye$() fills up by about three quarters.$(br)This effect is known to only occur in the $(thing)overworld$()."
},
{
"type": "naturesaura:tree_ritual",
"recipe": "naturesaura:plant_powder",
"text": "This effect can be inhibited in a radius of about 15 blocks using $(l:effects/inhibiting_powder)Powder of Steady Growth$()."
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B