add the ability to see passive effects to the ocular

This commit is contained in:
Ellpeck 2019-02-09 21:55:40 +01:00
parent ea54997ae5
commit 9aa2e498d2
14 changed files with 398 additions and 163 deletions

View file

@ -3,6 +3,7 @@ package de.ellpeck.naturesaura;
import de.ellpeck.naturesaura.api.NaturesAuraAPI; import de.ellpeck.naturesaura.api.NaturesAuraAPI;
import de.ellpeck.naturesaura.api.aura.item.IAuraRecharge; import de.ellpeck.naturesaura.api.aura.item.IAuraRecharge;
import de.ellpeck.naturesaura.blocks.tiles.TileEntityImpl; import de.ellpeck.naturesaura.blocks.tiles.TileEntityImpl;
import de.ellpeck.naturesaura.chunk.AuraChunk;
import net.minecraft.advancements.Advancement; import net.minecraft.advancements.Advancement;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.IProperty;
@ -28,6 +29,7 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.ChunkProviderServer; import net.minecraft.world.gen.ChunkProviderServer;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
@ -41,25 +43,46 @@ import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
public final class Helper { public final class Helper {
public static boolean getTileEntitiesInArea(World world, BlockPos pos, int radius, Function<TileEntity, Boolean> consumer) { public static boolean getTileEntitiesInArea(World world, BlockPos pos, int radius, Function<TileEntity, Boolean> consumer) {
world.profiler.func_194340_a(() -> NaturesAura.MOD_ID + ":getAuraChunksInArea");
for (int x = (pos.getX() - radius) >> 4; x <= (pos.getX() + radius) >> 4; x++) { for (int x = (pos.getX() - radius) >> 4; x <= (pos.getX() + radius) >> 4; x++) {
for (int z = (pos.getZ() - radius) >> 4; z <= (pos.getZ() + radius) >> 4; z++) { for (int z = (pos.getZ() - radius) >> 4; z <= (pos.getZ() + radius) >> 4; z++) {
if (isChunkLoaded(world, x, z)) { if (isChunkLoaded(world, x, z)) {
for (TileEntity tile : world.getChunk(x, z).getTileEntityMap().values()) { for (TileEntity tile : world.getChunk(x, z).getTileEntityMap().values()) {
if (tile.getPos().distanceSq(pos) <= radius * radius) if (tile.getPos().distanceSq(pos) <= radius * radius)
if (consumer.apply(tile)) if (consumer.apply(tile)) {
world.profiler.endSection();
return true; return true;
} }
} }
} }
} }
}
world.profiler.endSection();
return false; return false;
} }
public static void getAuraChunksInArea(World world, BlockPos pos, int radius, Consumer<AuraChunk> consumer) {
world.profiler.func_194340_a(() -> NaturesAura.MOD_ID + ":getAuraChunksInArea");
for (int x = (pos.getX() - radius) >> 4; x <= (pos.getX() + radius) >> 4; x++) {
for (int z = (pos.getZ() - radius) >> 4; z <= (pos.getZ() + radius) >> 4; z++) {
if (Helper.isChunkLoaded(world, x, z)) {
Chunk chunk = world.getChunk(x, z);
if (chunk.hasCapability(NaturesAuraAPI.capAuraChunk, null)) {
AuraChunk auraChunk = (AuraChunk) chunk.getCapability(NaturesAuraAPI.capAuraChunk, null);
consumer.accept(auraChunk);
}
}
}
}
world.profiler.endSection();
}
public static List<EntityItemFrame> getAttachedItemFrames(World world, BlockPos pos) { public static List<EntityItemFrame> getAttachedItemFrames(World world, BlockPos pos) {
List<EntityItemFrame> frames = world.getEntitiesWithinAABB(EntityItemFrame.class, new AxisAlignedBB(pos).grow(0.25)); List<EntityItemFrame> frames = world.getEntitiesWithinAABB(EntityItemFrame.class, new AxisAlignedBB(pos).grow(0.25));
for (int i = frames.size() - 1; i >= 0; i--) { for (int i = frames.size() - 1; i >= 0; i--) {

View file

@ -15,7 +15,6 @@ import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import org.apache.commons.lang3.mutable.MutableFloat; import org.apache.commons.lang3.mutable.MutableFloat;
import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.mutable.MutableInt;
@ -114,17 +113,7 @@ public class InternalHooks implements NaturesAuraAPI.IInternalHooks {
@Override @Override
public void getAuraSpotsInArea(World world, BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) { public void getAuraSpotsInArea(World world, BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) {
world.profiler.func_194340_a(() -> NaturesAura.MOD_ID + ":getSpotsInArea"); world.profiler.func_194340_a(() -> NaturesAura.MOD_ID + ":getSpotsInArea");
for (int x = (pos.getX() - radius) >> 4; x <= (pos.getX() + radius) >> 4; x++) { Helper.getAuraChunksInArea(world, pos, radius, chunk -> chunk.getSpotsInArea(pos, radius, consumer));
for (int z = (pos.getZ() - radius) >> 4; z <= (pos.getZ() + radius) >> 4; z++) {
if (Helper.isChunkLoaded(world, x, z)) {
Chunk chunk = world.getChunk(x, z);
if (chunk.hasCapability(NaturesAuraAPI.capAuraChunk, null)) {
IAuraChunk auraChunk = chunk.getCapability(NaturesAuraAPI.capAuraChunk, null);
auraChunk.getSpotsInArea(pos, radius, consumer);
}
}
}
}
world.profiler.endSection(); world.profiler.endSection();
} }

View file

@ -46,11 +46,6 @@ public interface IAuraChunk extends ICapabilityProvider, INBTSerializable<NBTTag
* This method uses the supplied consumer to iterate over all the drain * This method uses the supplied consumer to iterate over all the drain
* spots, represented as a position and the number of Aura in them, in any * spots, represented as a position and the number of Aura in them, in any
* given area. * given area.
* <p>
* Notice that this is different from {@link #getSpotsInArea(BlockPos, int,
* BiConsumer)} because this method iterates over several chunks while the
* former only uses the current aura chunk instance. Most of the time, you
* will want to use this method.
* *
* @param world The world * @param world The world
* @param pos The center position * @param pos The center position
@ -133,11 +128,6 @@ public interface IAuraChunk extends ICapabilityProvider, INBTSerializable<NBTTag
return NaturesAuraAPI.instance().getHighestAuraDrainSpot(world, pos, radius, defaultSpot); return NaturesAuraAPI.instance().getHighestAuraDrainSpot(world, pos, radius, defaultSpot);
} }
/**
* @see #getSpotsInArea(World, BlockPos, int, BiConsumer)
*/
void getSpotsInArea(BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer);
/** /**
* Drains the given amount of Aura from the given position. Returns the * Drains the given amount of Aura from the given position. Returns the
* amount of Aura that was drained. * amount of Aura that was drained.

View file

@ -1,6 +1,8 @@
package de.ellpeck.naturesaura.api.aura.chunk; package de.ellpeck.naturesaura.api.aura.chunk;
import de.ellpeck.naturesaura.api.aura.type.IAuraType; import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -13,4 +15,12 @@ public interface IDrainSpotEffect {
boolean appliesHere(Chunk chunk, IAuraChunk auraChunk, IAuraType type); boolean appliesHere(Chunk chunk, IAuraChunk auraChunk, IAuraType type);
ResourceLocation getName(); ResourceLocation getName();
default int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
return -1;
}
default ItemStack getDisplayIcon() {
return ItemStack.EMPTY;
}
} }

View file

@ -6,10 +6,14 @@ import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect;
import de.ellpeck.naturesaura.api.aura.type.IAuraType; import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import de.ellpeck.naturesaura.packet.PacketAuraChunk; import de.ellpeck.naturesaura.packet.PacketAuraChunk;
import de.ellpeck.naturesaura.packet.PacketHandler; import de.ellpeck.naturesaura.packet.PacketHandler;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
@ -45,16 +49,6 @@ public class AuraChunk implements IAuraChunk {
} }
} }
@Override
public void getSpotsInArea(BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) {
for (Map.Entry<BlockPos, MutableInt> entry : this.drainSpots.entrySet()) {
BlockPos drainPos = entry.getKey();
if (drainPos.distanceSq(pos) <= radius * radius) {
consumer.accept(drainPos, entry.getValue().intValue());
}
}
}
@Override @Override
public int drainAura(BlockPos pos, int amount, boolean aimForZero, boolean simulate) { public int drainAura(BlockPos pos, int amount, boolean aimForZero, boolean simulate) {
if (amount <= 0) if (amount <= 0)
@ -174,6 +168,34 @@ public class AuraChunk implements IAuraChunk {
return new PacketAuraChunk(this.chunk.x, this.chunk.z, this.drainSpots); return new PacketAuraChunk(this.chunk.x, this.chunk.z, this.drainSpots);
} }
public void getSpotsInArea(BlockPos pos, int radius, BiConsumer<BlockPos, Integer> consumer) {
for (Map.Entry<BlockPos, MutableInt> entry : this.drainSpots.entrySet()) {
BlockPos drainPos = entry.getKey();
if (drainPos.distanceSq(pos) <= radius * radius) {
consumer.accept(drainPos, entry.getValue().intValue());
}
}
}
public void getActiveEffectIcons(EntityPlayer player, Map<ResourceLocation, Tuple<ItemStack, Boolean>> icons) {
for (IDrainSpotEffect effect : this.effects) {
Tuple<ItemStack, Boolean> alreadyThere = icons.get(effect.getName());
if (alreadyThere != null && alreadyThere.getSecond())
continue;
for (Map.Entry<BlockPos, MutableInt> entry : this.drainSpots.entrySet()) {
BlockPos pos = entry.getKey();
MutableInt amount = entry.getValue();
int state = effect.isActiveHere(player, this.chunk, this, pos, amount.intValue());
if (state < 0)
continue;
ItemStack stack = effect.getDisplayIcon();
if (stack.isEmpty())
continue;
icons.put(effect.getName(), new Tuple<>(stack, state == 0));
}
}
}
@Override @Override
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing) { public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing) {
return capability == NaturesAuraAPI.capAuraChunk; return capability == NaturesAuraAPI.capAuraChunk;

View file

@ -9,6 +9,8 @@ import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityChicken; import net.minecraft.entity.passive.EntityChicken;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.ItemEgg; import net.minecraft.item.ItemEgg;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.EnumParticleTypes;
@ -28,25 +30,50 @@ public class AnimalEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "animal"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "animal");
@Override private int chance;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private AxisAlignedBB bb;
private boolean calcValues(World world, BlockPos pos, Integer spot) {
if (spot <= 0) if (spot <= 0)
return; return false;
int aura = IAuraChunk.getAuraInArea(world, pos, 30); int aura = IAuraChunk.getAuraInArea(world, pos, 30);
if (aura < 1500000) if (aura < 1500000)
return; return false;
int chance = Math.min(50, Math.abs(aura) / 500000); this.chance = Math.min(50, Math.abs(aura) / 500000);
if (chance <= 0) if (this.chance <= 0)
return; return false;
int dist = MathHelper.clamp(Math.abs(aura) / 150000, 5, 35); int dist = MathHelper.clamp(Math.abs(aura) / 150000, 5, 35);
AxisAlignedBB bb = new AxisAlignedBB(pos).grow(dist); this.bb = new AxisAlignedBB(pos).grow(dist);
return true;
}
List<EntityAnimal> animals = world.getEntitiesWithinAABB(EntityAnimal.class, bb); @Override
public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(player.world, pos, spot))
return -1;
if (!this.bb.contains(player.getPositionVector()))
return -1;
if (NaturesAuraAPI.instance().isEffectPowderActive(player.world, player.getPosition(), NAME))
return 0;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(Items.EGG);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(world, pos, spot))
return;
List<EntityAnimal> animals = world.getEntitiesWithinAABB(EntityAnimal.class, this.bb);
if (animals.size() >= 200) if (animals.size() >= 200)
return; return;
if (world.getTotalWorldTime() % 200 == 0) { if (world.getTotalWorldTime() % 200 == 0) {
List<EntityItem> items = world.getEntitiesWithinAABB(EntityItem.class, bb); List<EntityItem> items = world.getEntitiesWithinAABB(EntityItem.class, this.bb);
for (EntityItem item : items) { for (EntityItem item : items) {
if (item.isDead) if (item.isDead)
continue; continue;
@ -78,7 +105,7 @@ public class AnimalEffect implements IDrainSpotEffect {
} }
} }
if (world.rand.nextInt(200) <= chance) { if (world.rand.nextInt(200) <= this.chance) {
if (animals.size() < 2) if (animals.size() < 2)
return; return;
EntityAnimal first = animals.get(world.rand.nextInt(animals.size())); EntityAnimal first = animals.get(world.rand.nextInt(animals.size()));

View file

@ -2,11 +2,15 @@ package de.ellpeck.naturesaura.chunk.effect;
import de.ellpeck.naturesaura.ModConfig; import de.ellpeck.naturesaura.ModConfig;
import de.ellpeck.naturesaura.NaturesAura; import de.ellpeck.naturesaura.NaturesAura;
import de.ellpeck.naturesaura.api.NaturesAuraAPI;
import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk; import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk;
import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect; import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect;
import de.ellpeck.naturesaura.api.aura.type.IAuraType; import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import de.ellpeck.naturesaura.potion.ModPotions; import de.ellpeck.naturesaura.potion.ModPotions;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.PotionEffect; import net.minecraft.potion.PotionEffect;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
@ -21,22 +25,46 @@ public class BreathlessEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "breathless"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "breathless");
@Override private int amp;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private AxisAlignedBB bb;
if (spot >= 0 || world.getTotalWorldTime() % 100 != 0)
return; private boolean calcValues(World world, BlockPos pos, Integer spot) {
if (spot >= 0)
return false;
int aura = IAuraChunk.getAuraInArea(world, pos, 50); int aura = IAuraChunk.getAuraInArea(world, pos, 50);
if (aura > 0) if (aura > 0)
return; return false;
int dist = Math.min(Math.abs(aura) / 50000, 75); int dist = Math.min(Math.abs(aura) / 50000, 75);
if (dist < 10) if (dist < 10)
return; return false;
int amp = Math.min(MathHelper.floor(Math.abs(aura) / 2500000F), 3); this.amp = Math.min(MathHelper.floor(Math.abs(aura) / 2500000F), 3);
this.bb = new AxisAlignedBB(pos).grow(dist);
return true;
}
List<EntityLivingBase> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, @Override
new AxisAlignedBB(pos).grow(dist)); public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(player.world, pos, spot))
return -1;
if (!this.bb.contains(player.getPositionVector()))
return -1;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(Blocks.WOOL);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (world.getTotalWorldTime() % 100 != 0)
return;
if (!this.calcValues(world, pos, spot))
return;
List<EntityLivingBase> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, this.bb);
for (EntityLivingBase entity : entities) for (EntityLivingBase entity : entities)
entity.addPotionEffect(new PotionEffect(ModPotions.BREATHLESS, 300, amp)); entity.addPotionEffect(new PotionEffect(ModPotions.BREATHLESS, 300, this.amp));
} }
@Override @Override

View file

@ -6,7 +6,9 @@ import de.ellpeck.naturesaura.api.NaturesAuraAPI;
import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk; import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk;
import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect; import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect;
import de.ellpeck.naturesaura.api.aura.type.IAuraType; import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import de.ellpeck.naturesaura.items.ModItems;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -20,23 +22,48 @@ public class CacheRechargeEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "cache_recharge"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "cache_recharge");
@Override private int amount;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private AxisAlignedBB bb;
private boolean calcValues(World world, BlockPos pos, Integer spot) {
if (spot < 100000) if (spot < 100000)
return; return false;
int aura = IAuraChunk.getAuraInArea(world, pos, 20); int aura = IAuraChunk.getAuraInArea(world, pos, 20);
if (aura < 1500000) if (aura < 1500000)
return; return false;
if (NaturesAuraAPI.instance().isEffectPowderActive(world, pos, NAME))
return;
int dist = MathHelper.clamp(aura / 3500, 3, 15); int dist = MathHelper.clamp(aura / 3500, 3, 15);
int amount = aura / 250000 - 2; this.bb = new AxisAlignedBB(pos).grow(dist);
this.amount = aura / 250000 - 2;
return true;
}
List<EntityPlayer> players = world.getEntitiesWithinAABB(EntityPlayer.class, new AxisAlignedBB(pos).grow(dist)); @Override
public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(player.world, pos, spot))
return -1;
if (!this.bb.contains(player.getPositionVector()))
return -1;
if (NaturesAuraAPI.instance().isEffectPowderActive(player.world, player.getPosition(), NAME))
return 0;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(ModItems.AURA_CACHE);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(world, pos, spot))
return;
List<EntityPlayer> players = world.getEntitiesWithinAABB(EntityPlayer.class, this.bb);
for (EntityPlayer player : players) { for (EntityPlayer player : players) {
if (NaturesAuraAPI.instance().insertAuraIntoPlayer(player, amount, true)) { if (NaturesAuraAPI.instance().isEffectPowderActive(world, player.getPosition(), NAME))
NaturesAuraAPI.instance().insertAuraIntoPlayer(player, amount, false); continue;
auraChunk.drainAura(pos, amount); if (NaturesAuraAPI.instance().insertAuraIntoPlayer(player, this.amount, true)) {
NaturesAuraAPI.instance().insertAuraIntoPlayer(player, this.amount, false);
auraChunk.drainAura(pos, this.amount);
} }
} }
} }

View file

@ -5,6 +5,9 @@ import de.ellpeck.naturesaura.NaturesAura;
import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk; import de.ellpeck.naturesaura.api.aura.chunk.IAuraChunk;
import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect; import de.ellpeck.naturesaura.api.aura.chunk.IDrainSpotEffect;
import de.ellpeck.naturesaura.api.aura.type.IAuraType; import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -15,28 +18,54 @@ public class ExplosionEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "explosions"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "explosions");
@Override private int chance;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private float strength;
if (spot >= 0 || world.getTotalWorldTime() % 40 != 0) private int dist;
return;
private boolean calcValues(World world, BlockPos pos, Integer spot){
if (spot >= 0)
return false;
int aura = IAuraChunk.getAuraInArea(world, pos, 85); int aura = IAuraChunk.getAuraInArea(world, pos, 85);
if (aura > -5000000) if (aura > -5000000)
return; return false;
int chance = 140 - Math.abs(aura) / 200000; this.chance = 140 - Math.abs(aura) / 200000;
if (chance > 1 && world.rand.nextInt(chance) != 0) if (this.chance > 1 && world.rand.nextInt(this.chance) != 0)
return; return false;
float strength = Math.min(Math.abs(aura) / 5000000F, 5F); this.strength = Math.min(Math.abs(aura) / 5000000F, 5F);
if (strength <= 0) if (this.strength <= 0)
return; return false;
int dist = MathHelper.clamp(Math.abs(aura) / 200000, 25, 100); this.dist = MathHelper.clamp(Math.abs(aura) / 200000, 25, 100);
return true;
}
int x = MathHelper.floor(pos.getX() + world.rand.nextGaussian() * dist); @Override
int z = MathHelper.floor(pos.getZ() + world.rand.nextGaussian() * dist); public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(player.world, pos, spot))
return -1;
if (player.getDistanceSq(pos) > this.dist * this.dist)
return -1;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(Blocks.TNT);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if(world.getTotalWorldTime() % 40 != 0)
return;
if(!this.calcValues(world, pos, spot))
return;
int x = MathHelper.floor(pos.getX() + world.rand.nextGaussian() * this.dist);
int z = MathHelper.floor(pos.getZ() + world.rand.nextGaussian() * this.dist);
BlockPos chosenPos = new BlockPos(x, world.getHeight(x, z), z); BlockPos chosenPos = new BlockPos(x, world.getHeight(x, z), z);
if (chosenPos.distanceSq(pos) <= dist * dist && world.isBlockLoaded(chosenPos)) { if (chosenPos.distanceSq(pos) <= this.dist * this.dist && world.isBlockLoaded(chosenPos)) {
world.newExplosion(null, world.newExplosion(null,
chosenPos.getX() + 0.5, chosenPos.getY() + 0.5, chosenPos.getZ() + 0.5, chosenPos.getX() + 0.5, chosenPos.getY() + 0.5, chosenPos.getZ() + 0.5,
strength, false, true); this.strength, false, true);
} }
} }

View file

@ -9,7 +9,9 @@ import de.ellpeck.naturesaura.api.aura.type.IAuraType;
import de.ellpeck.naturesaura.blocks.ModBlocks; import de.ellpeck.naturesaura.blocks.ModBlocks;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -20,21 +22,48 @@ public class GrassDieEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "grass_die"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "grass_die");
@Override private int amount;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private int dist;
private boolean calcValues(World world, BlockPos pos, Integer spot) {
if (spot < 0) { if (spot < 0) {
int aura = IAuraChunk.getAuraInArea(world, pos, 50); int aura = IAuraChunk.getAuraInArea(world, pos, 50);
if (aura < 0) { if (aura < 0) {
int amount = Math.min(300, Math.abs(aura) / 100000); this.amount = Math.min(300, Math.abs(aura) / 100000);
if (amount > 1) { if (this.amount > 1) {
int dist = MathHelper.clamp(Math.abs(aura) / 75000, 5, 75); this.dist = MathHelper.clamp(Math.abs(aura) / 75000, 5, 75);
for (int i = amount / 2 + world.rand.nextInt(amount / 2); i >= 0; i--) { return true;
}
}
}
return false;
}
@Override
public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(player.world, pos, spot))
return -1;
if (player.getDistanceSq(pos) > this.dist * this.dist)
return -1;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(ModBlocks.DECAYED_LEAVES);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(world, pos, spot))
return;
for (int i = this.amount / 2 + world.rand.nextInt(this.amount / 2); i >= 0; i--) {
BlockPos grassPos = new BlockPos( BlockPos grassPos = new BlockPos(
pos.getX() + world.rand.nextGaussian() * dist, pos.getX() + world.rand.nextGaussian() * this.dist,
pos.getY() + world.rand.nextGaussian() * dist, pos.getY() + world.rand.nextGaussian() * this.dist,
pos.getZ() + world.rand.nextGaussian() * dist pos.getZ() + world.rand.nextGaussian() * this.dist
); );
if (grassPos.distanceSq(pos) <= dist * dist && world.isBlockLoaded(grassPos)) { if (grassPos.distanceSq(pos) <= this.dist * this.dist && world.isBlockLoaded(grassPos)) {
IBlockState state = world.getBlockState(grassPos); IBlockState state = world.getBlockState(grassPos);
Block block = state.getBlock(); Block block = state.getBlock();
@ -51,9 +80,6 @@ public class GrassDieEffect implements IDrainSpotEffect {
} }
} }
} }
}
}
}
@Override @Override
public boolean appliesHere(Chunk chunk, IAuraChunk auraChunk, IAuraType type) { public boolean appliesHere(Chunk chunk, IAuraChunk auraChunk, IAuraType type) {

View file

@ -11,7 +11,10 @@ import de.ellpeck.naturesaura.packet.PacketParticles;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.IGrowable; import net.minecraft.block.IGrowable;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -22,23 +25,47 @@ public class PlantBoostEffect implements IDrainSpotEffect {
public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "plant_boost"); public static final ResourceLocation NAME = new ResourceLocation(NaturesAura.MOD_ID, "plant_boost");
@Override private int amount;
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) { private int dist;
private boolean calcValues(World world, BlockPos pos, Integer spot) {
if (spot <= 0) if (spot <= 0)
return; return false;
int aura = IAuraChunk.getAuraInArea(world, pos, 30); int aura = IAuraChunk.getAuraInArea(world, pos, 30);
if (aura < 1500000) if (aura < 1500000)
return; return false;
int amount = Math.min(45, Math.abs(aura) / 100000); this.amount = Math.min(45, Math.abs(aura) / 100000);
if (amount <= 1) if (this.amount <= 1)
return; return false;
int dist = MathHelper.clamp(Math.abs(aura) / 150000, 5, 35); this.dist = MathHelper.clamp(Math.abs(aura) / 150000, 5, 35);
return true;
}
for (int i = amount / 2 + world.rand.nextInt(amount / 2); i >= 0; i--) { @Override
int x = MathHelper.floor(pos.getX() + world.rand.nextGaussian() * dist); public int isActiveHere(EntityPlayer player, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
int z = MathHelper.floor(pos.getZ() + world.rand.nextGaussian() * dist); if (!this.calcValues(player.world, pos, spot))
return -1;
if (player.getDistanceSq(pos) > this.dist * this.dist)
return -1;
if (NaturesAuraAPI.instance().isEffectPowderActive(player.world, player.getPosition(), NAME))
return 0;
return 1;
}
@Override
public ItemStack getDisplayIcon() {
return new ItemStack(Items.WHEAT_SEEDS);
}
@Override
public void update(World world, Chunk chunk, IAuraChunk auraChunk, BlockPos pos, Integer spot) {
if (!this.calcValues(world, pos, spot))
return;
for (int i = this.amount / 2 + world.rand.nextInt(this.amount / 2); i >= 0; i--) {
int x = MathHelper.floor(pos.getX() + world.rand.nextGaussian() * this.dist);
int z = MathHelper.floor(pos.getZ() + world.rand.nextGaussian() * this.dist);
BlockPos plantPos = new BlockPos(x, world.getHeight(x, z), z); BlockPos plantPos = new BlockPos(x, world.getHeight(x, z), z);
if (plantPos.distanceSq(pos) <= dist * dist && world.isBlockLoaded(plantPos)) { if (plantPos.distanceSq(pos) <= this.dist * this.dist && world.isBlockLoaded(plantPos)) {
if (NaturesAuraAPI.instance().isEffectPowderActive(world, plantPos, NAME)) if (NaturesAuraAPI.instance().isEffectPowderActive(world, plantPos, NAME))
continue; continue;

View file

@ -29,6 +29,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -58,6 +59,10 @@ import java.util.Map;
public class ClientEvents { public class ClientEvents {
public static final ResourceLocation OVERLAYS = new ResourceLocation(NaturesAura.MOD_ID, "textures/gui/overlays.png"); public static final ResourceLocation OVERLAYS = new ResourceLocation(NaturesAura.MOD_ID, "textures/gui/overlays.png");
private static final Map<ResourceLocation, Tuple<ItemStack, Boolean>> SHOWING_EFFECTS = new HashMap<>();
private static ItemStack heldCache = ItemStack.EMPTY;
private static ItemStack heldEye = ItemStack.EMPTY;
private static ItemStack heldOcular = ItemStack.EMPTY;
@SubscribeEvent @SubscribeEvent
public void onDebugRender(RenderGameOverlayEvent.Text event) { public void onDebugRender(RenderGameOverlayEvent.Text event) {
@ -95,6 +100,10 @@ public class ClientEvents {
@SubscribeEvent @SubscribeEvent
public void onClientTick(ClientTickEvent event) { public void onClientTick(ClientTickEvent event) {
if (event.phase == Phase.END) { if (event.phase == Phase.END) {
heldCache = ItemStack.EMPTY;
heldEye = ItemStack.EMPTY;
heldOcular = ItemStack.EMPTY;
Minecraft mc = Minecraft.getMinecraft(); Minecraft mc = Minecraft.getMinecraft();
if (mc.world == null) { if (mc.world == null) {
ParticleHandler.clearParticles(); ParticleHandler.clearParticles();
@ -138,6 +147,41 @@ public class ClientEvents {
if (!mc.isGamePaused()) if (!mc.isGamePaused())
ParticleHandler.updateParticles(); ParticleHandler.updateParticles();
mc.profiler.endSection(); mc.profiler.endSection();
if (mc.player != null) {
if (Compat.baubles) {
IItemHandler baubles = BaublesApi.getBaublesHandler(mc.player);
for (int i = 0; i < baubles.getSlots(); i++) {
ItemStack slot = baubles.getStackInSlot(i);
if (!slot.isEmpty()) {
if (slot.getItem() == ModItems.AURA_CACHE)
heldCache = slot;
else if (slot.getItem() == ModItems.EYE)
heldEye = slot;
else if (slot.getItem() == ModItems.EYE_IMPROVED)
heldOcular = slot;
}
}
}
for (int i = 0; i < mc.player.inventory.getSizeInventory(); i++) {
ItemStack slot = mc.player.inventory.getStackInSlot(i);
if (!slot.isEmpty()) {
if (slot.getItem() == ModItems.AURA_CACHE)
heldCache = slot;
else if (slot.getItem() == ModItems.EYE && i <= 8)
heldEye = slot;
else if (slot.getItem() == ModItems.EYE_IMPROVED)
heldOcular = slot;
}
}
if (!heldOcular.isEmpty() && mc.world.getTotalWorldTime() % 20 == 0) {
SHOWING_EFFECTS.clear();
Helper.getAuraChunksInArea(mc.world, mc.player.getPosition(), 100,
chunk -> chunk.getActiveEffectIcons(mc.player, SHOWING_EFFECTS));
}
}
} }
} }
} }
@ -237,39 +281,8 @@ public class ClientEvents {
if (event.getType() == ElementType.ALL) { if (event.getType() == ElementType.ALL) {
ScaledResolution res = event.getResolution(); ScaledResolution res = event.getResolution();
if (mc.player != null) { if (mc.player != null) {
ItemStack cache = ItemStack.EMPTY; if (!heldCache.isEmpty()) {
ItemStack eye = ItemStack.EMPTY; IAuraContainer container = heldCache.getCapability(NaturesAuraAPI.capAuraContainer, null);
ItemStack eyeImproved = ItemStack.EMPTY;
if (Compat.baubles) {
IItemHandler baubles = BaublesApi.getBaublesHandler(mc.player);
for (int i = 0; i < baubles.getSlots(); i++) {
ItemStack slot = baubles.getStackInSlot(i);
if (!slot.isEmpty()) {
if (slot.getItem() == ModItems.AURA_CACHE)
cache = slot;
else if (slot.getItem() == ModItems.EYE)
eye = slot;
else if (slot.getItem() == ModItems.EYE_IMPROVED)
eyeImproved = slot;
}
}
}
for (int i = 0; i < mc.player.inventory.getSizeInventory(); i++) {
ItemStack slot = mc.player.inventory.getStackInSlot(i);
if (!slot.isEmpty()) {
if (slot.getItem() == ModItems.AURA_CACHE)
cache = slot;
else if (slot.getItem() == ModItems.EYE && i <= 8)
eye = slot;
else if (slot.getItem() == ModItems.EYE_IMPROVED)
eyeImproved = slot;
}
}
if (!cache.isEmpty()) {
IAuraContainer container = cache.getCapability(NaturesAuraAPI.capAuraContainer, null);
int width = MathHelper.ceil(container.getStoredAura() / (float) container.getMaxAura() * 80); int width = MathHelper.ceil(container.getStoredAura() / (float) container.getMaxAura() * 80);
int x = res.getScaledWidth() / 2 - 173 - (mc.player.getHeldItemOffhand().isEmpty() ? 0 : 29); int x = res.getScaledWidth() / 2 - 173 - (mc.player.getHeldItemOffhand().isEmpty() ? 0 : 29);
int y = res.getScaledHeight() - 8; int y = res.getScaledHeight() - 8;
@ -286,14 +299,14 @@ public class ClientEvents {
float scale = 0.75F; float scale = 0.75F;
GlStateManager.scale(scale, scale, scale); GlStateManager.scale(scale, scale, scale);
String s = cache.getDisplayName(); String s = heldCache.getDisplayName();
mc.fontRenderer.drawString(s, (x + 80) / scale - mc.fontRenderer.getStringWidth(s), (y - 7) / scale, color, true); mc.fontRenderer.drawString(s, (x + 80) / scale - mc.fontRenderer.getStringWidth(s), (y - 7) / scale, color, true);
GlStateManager.color(1F, 1F, 1F); GlStateManager.color(1F, 1F, 1F);
GlStateManager.popMatrix(); GlStateManager.popMatrix();
} }
if (!eye.isEmpty() || !eyeImproved.isEmpty()) { if (!heldEye.isEmpty() || !heldOcular.isEmpty()) {
GlStateManager.pushMatrix(); GlStateManager.pushMatrix();
mc.getTextureManager().bindTexture(OVERLAYS); mc.getTextureManager().bindTexture(OVERLAYS);
@ -307,19 +320,19 @@ public class ClientEvents {
float textScale = 0.75F; float textScale = 0.75F;
int startX = conf % 2 == 0 ? 3 : res.getScaledWidth() - 3 - 6; int startX = conf % 2 == 0 ? 3 : res.getScaledWidth() - 3 - 6;
int startY = conf < 2 ? 10 : (!eyeImproved.isEmpty() && (totalPercentage > 1F || totalPercentage < 0) ? -26 : 0) + res.getScaledHeight() - 60; int startY = conf < 2 ? 10 : (!heldOcular.isEmpty() && (totalPercentage > 1F || totalPercentage < 0) ? -26 : 0) + res.getScaledHeight() - 60;
float plusOffX = conf % 2 == 0 ? 7 : -1 - 6; float plusOffX = conf % 2 == 0 ? 7 : -1 - 6;
float textX = conf % 2 == 0 ? 3 : res.getScaledWidth() - 3 - mc.fontRenderer.getStringWidth(text) * textScale; float textX = conf % 2 == 0 ? 3 : res.getScaledWidth() - 3 - mc.fontRenderer.getStringWidth(text) * textScale;
float textY = conf < 2 ? 3 : res.getScaledHeight() - 3 - 6; float textY = conf < 2 ? 3 : res.getScaledHeight() - 3 - 6;
int tHeight = MathHelper.ceil(MathHelper.clamp(totalPercentage, 0F, 1F) * 50); int tHeight = MathHelper.ceil(MathHelper.clamp(totalPercentage, 0F, 1F) * 50);
int y = !eyeImproved.isEmpty() && totalPercentage > 1F ? startY + 26 : startY; int y = !heldOcular.isEmpty() && totalPercentage > 1F ? startY + 26 : startY;
if (tHeight < 50) if (tHeight < 50)
Gui.drawModalRectWithCustomSizedTexture(startX, y, 6, 12, 6, 50 - tHeight, 256, 256); Gui.drawModalRectWithCustomSizedTexture(startX, y, 6, 12, 6, 50 - tHeight, 256, 256);
if (tHeight > 0) if (tHeight > 0)
Gui.drawModalRectWithCustomSizedTexture(startX, y + 50 - tHeight, 0, 12 + 50 - tHeight, 6, tHeight, 256, 256); Gui.drawModalRectWithCustomSizedTexture(startX, y + 50 - tHeight, 0, 12 + 50 - tHeight, 6, tHeight, 256, 256);
if (!eyeImproved.isEmpty()) { if (!heldOcular.isEmpty()) {
GlStateManager.color(160 / 255F, 83 / 255F, 8 / 255F); GlStateManager.color(160 / 255F, 83 / 255F, 8 / 255F);
int topHeight = MathHelper.ceil(MathHelper.clamp((totalPercentage - 1F) * 2F, 0F, 1F) * 25); int topHeight = MathHelper.ceil(MathHelper.clamp((totalPercentage - 1F) * 2F, 0F, 1F) * 25);
@ -336,16 +349,37 @@ public class ClientEvents {
} }
} }
int color = eyeImproved.isEmpty() ? 0x53a008 : 0xa05308; int color = heldOcular.isEmpty() ? 0x53a008 : 0xa05308;
if (totalPercentage > (eyeImproved.isEmpty() ? 1F : 1.5F)) if (totalPercentage > (heldOcular.isEmpty() ? 1F : 1.5F))
mc.fontRenderer.drawString("+", startX + plusOffX, startY - 0.5F, color, true); mc.fontRenderer.drawString("+", startX + plusOffX, startY - 0.5F, color, true);
if (totalPercentage < (eyeImproved.isEmpty() ? 0F : -0.5F)) if (totalPercentage < (heldOcular.isEmpty() ? 0F : -0.5F))
mc.fontRenderer.drawString("-", startX + plusOffX, startY - 0.5F + (eyeImproved.isEmpty() ? 44 : 70), color, true); mc.fontRenderer.drawString("-", startX + plusOffX, startY - 0.5F + (heldOcular.isEmpty() ? 44 : 70), color, true);
GlStateManager.pushMatrix(); GlStateManager.pushMatrix();
GlStateManager.scale(textScale, textScale, textScale); GlStateManager.scale(textScale, textScale, textScale);
mc.fontRenderer.drawString(text, textX / textScale, textY / textScale, 0x53a008, true); mc.fontRenderer.drawString(text, textX / textScale, textY / textScale, 0x53a008, true);
GlStateManager.popMatrix(); GlStateManager.popMatrix();
if (!heldOcular.isEmpty()) {
float scale = 0.75F;
GlStateManager.pushMatrix();
GlStateManager.scale(scale, scale, scale);
int stackY = 15;
for (Tuple<ItemStack, Boolean> effect : SHOWING_EFFECTS.values()) {
int theX = (int) (10 / scale);
int theY = (int) (stackY / scale);
ItemStack stack = effect.getFirst();
Helper.renderItemInGui(stack, theX, theY, 1F);
if (effect.getSecond()) {
GlStateManager.disableDepth();
mc.getTextureManager().bindTexture(OVERLAYS);
Gui.drawModalRectWithCustomSizedTexture(theX, theY, 240, 0, 16, 16, 256, 256);
GlStateManager.enableDepth();
}
stackY += 8;
}
GlStateManager.popMatrix();
}
} }
if (mc.objectMouseOver != null) { if (mc.objectMouseOver != null) {

View file

@ -10,7 +10,10 @@
}, },
{ {
"type": "text", "type": "text",
"text": "It needs be noted that the extended bar only shows if $(aura) levels are high or low enough.$(br)An additional perk is that, while the $(item)Environmental Eye$() needs to be held in the hotbar, the $(item)Environmental Ocular$() can be anywhere in the user's inventory to work.$(p)All other properties of the $(item)Environmental Eye$() carry over to the $(item)Environmental Ocular$() as expected." "text": "It needs be noted that the extended bar only shows if $(aura) levels are high or low enough.$(p)Secondly, the $(item)Environmental Ocular$() has the ability to display all $(thing)Aura Imbalance$() effects that are currently occuring in the place the user is standing. The icons it displays are the same as the ones depicted for each effect in the $(thing)Aura Imbalance$() section of this book."
},{
"type": "text",
"text": "An additional perk is that, while the $(item)Environmental Eye$() needs to be held in the hotbar, the $(item)Environmental Ocular$() can be anywhere in the user's inventory to work.$(p)All other properties of the $(item)Environmental Eye$() carry over to the $(item)Environmental Ocular$() as expected."
}, },
{ {
"type": "naturesaura:tree_ritual", "type": "naturesaura:tree_ritual",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB