Reimplement Fire and Ice gems

This commit is contained in:
Sollace 2019-01-20 01:07:59 +02:00
parent ceab6d6914
commit 96809095c2
15 changed files with 728 additions and 55 deletions

View file

@ -7,6 +7,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.IParticleFactory;
import net.minecraft.client.particle.Particle;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Vec3d;
public class Particles {
@ -32,6 +33,10 @@ public class Particles {
return -id - 1;
}
public Particle spawnParticle(int particleId, boolean ignoreDistance, Vec3d pos, double speedX, double speedY, double speedZ, int ...pars) {
return spawnParticle(particleId, ignoreDistance, pos, speedX, speedY, speedZ, pars);
}
public Particle spawnParticle(int particleId, boolean ignoreDistance, double posX, double posY, double posZ, double speedX, double speedY, double speedZ, int ...pars) {
Entity entity = mc.getRenderViewEntity();

View file

@ -146,6 +146,20 @@ public class ItemSpell extends Item implements ICastable {
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
}
IMagicEffect effect = SpellRegistry.instance().getSpellFromItemStack(stack);
if (effect instanceof IUseAction) {
SpellCastResult result = ((IUseAction)effect).onUse(stack, player, world, target);
if (result != SpellCastResult.NONE) {
if (result == SpellCastResult.PLACE && !player.capabilities.isCreativeMode) {
stack.shrink(1);
}
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
}
}
return new ActionResult<ItemStack>(EnumActionResult.PASS, stack);
}

View file

@ -1,10 +1,17 @@
package com.minelittlepony.unicopia.spell;
import java.util.Random;
import java.util.function.Consumer;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.player.IOwned;
import com.minelittlepony.util.shape.IShape;
import com.minelittlepony.util.vector.VecHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public interface ICaster<E extends EntityLivingBase> extends IOwned<E>, ILevelled {
@ -30,4 +37,20 @@ public interface ICaster<E extends EntityLivingBase> extends IOwned<E>, ILevelle
default BlockPos getOrigin() {
return getEntity().getPosition();
}
default void spawnParticles(IShape area, int count, Consumer<Vec3d> particleSpawner) {
Random rand = getWorld().rand;
int x = getOrigin().getX();
int y = getOrigin().getY();
int z = getOrigin().getZ();
for (int i = 0; i < count; i++) {
particleSpawner.accept(area.computePoint(rand).add(x, y, z));
}
}
default Stream<Entity> findAllEntitiesInRange(double radius) {
return VecHelper.findAllEntitiesInRange(getEntity(), getWorld(), getOrigin(), radius);
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.spell;
import javax.annotation.Nonnull;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
@ -12,10 +14,10 @@ import net.minecraft.world.World;
*
*/
public interface IUseAction {
/**
* Triggered when the player right clicks a block
*
*
* @param stack The current itemstack
* @param player The player
* @param world The player's world
@ -24,20 +26,20 @@ public interface IUseAction {
* @param hitX X offset inside the block
* @param hitY Y offset inside the block
* @param hitZ Z offset inside the block
*
*
* @return ActionResult for the type of action to perform
*/
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ);
/**
* Triggered when the player right clicks
*
* @param stack The current itemstack
*
* @param stack The current itemstack
* @param player The player
* @param world The player's world
* @param hitEntity The entity in focus, if any
*
*
* @return ActionResult for the type of action to perform
*/
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, Entity hitEntity);
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, @Nonnull Entity hitEntity);
}

View file

@ -0,0 +1,262 @@
package com.minelittlepony.unicopia.spell;
import javax.annotation.Nonnull;
import com.minelittlepony.unicopia.Predicates;
import com.minelittlepony.unicopia.entity.IMagicals;
import com.minelittlepony.util.MagicalDamageSource;
import com.minelittlepony.util.PosHelper;
import com.minelittlepony.util.blockstate.IStateMapping;
import com.minelittlepony.util.blockstate.StateMapList;
import com.minelittlepony.util.shape.IShape;
import com.minelittlepony.util.shape.Sphere;
import com.minelittlepony.util.vector.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockBush;
import net.minecraft.block.BlockDirt;
import net.minecraft.block.BlockFarmland;
import net.minecraft.block.BlockLeaves;
import net.minecraft.block.BlockRedstoneWire;
import net.minecraft.block.BlockSilverfish;
import net.minecraft.block.BlockStoneBrick;
import net.minecraft.block.BlockWall;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.SoundCategory;
import net.minecraft.world.World;
public class SpellFire extends AbstractSpell implements IUseAction, IDispenceable {
public final StateMapList affected = new StateMapList();
private static final IShape visual_effect_region = new Sphere(false, 0.5);
private static final IShape effect_range = new Sphere(false, 4);
public SpellFire() {
affected.removeBlock(s -> s.getBlock() == Blocks.SNOW_LAYER || s.getBlock() == Blocks.SNOW);
affected.removeBlock(s -> s.getBlock() instanceof BlockBush);
affected.replaceBlock(Blocks.CLAY, Blocks.HARDENED_CLAY);
affected.replaceBlock(Blocks.OBSIDIAN, Blocks.LAVA);
affected.replaceBlock(Blocks.GRASS, Blocks.DIRT);
affected.replaceBlock(Blocks.MOSSY_COBBLESTONE, Blocks.COBBLESTONE);
affected.replaceProperty(Blocks.COBBLESTONE_WALL, BlockWall.VARIANT, BlockWall.EnumType.MOSSY, BlockWall.EnumType.NORMAL);
affected.replaceProperty(Blocks.STONEBRICK, BlockStoneBrick.VARIANT, BlockStoneBrick.EnumType.MOSSY, BlockStoneBrick.EnumType.DEFAULT);
affected.replaceProperty(Blocks.MONSTER_EGG, BlockSilverfish.VARIANT, BlockSilverfish.EnumType.MOSSY_STONEBRICK, BlockSilverfish.EnumType.STONEBRICK);
affected.replaceProperty(Blocks.DIRT, BlockDirt.VARIANT, BlockDirt.DirtType.PODZOL, BlockDirt.DirtType.COARSE_DIRT);
affected.setProperty(Blocks.FARMLAND, BlockFarmland.MOISTURE, 0);
affected.add(IStateMapping.build(
s -> s.getBlock() == Blocks.DIRT && s.getValue(BlockDirt.VARIANT) == BlockDirt.DirtType.DIRT,
s -> Math.random() <= 0.15 ?
s.withProperty(BlockDirt.VARIANT, BlockDirt.DirtType.COARSE_DIRT)
: s));
}
@Override
public int getCurrentLevel() {
return 0;
}
@Override
public void setCurrentLevel(int level) {
}
@Override
public String getName() {
return "fire";
}
@Override
public boolean update(ICaster<?> source, int level) {
return false;
}
@Override
public void render(ICaster<?> source, int level) {
source.spawnParticles(visual_effect_region, level * 6, pos -> {
source.getWorld().spawnParticle(EnumParticleTypes.SMOKE_LARGE, pos.x, pos.y, pos.z, 0, 0, 0);
});
}
@Override
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) {
boolean result = false;
if (player == null || player.isSneaking()) {
result = applyBlocks(player, world, pos);
} else {
for (BlockPos i : PosHelper.getAllInRegionMutable(pos, effect_range)) {
result |= applyBlocks(player, world, i);
}
}
if (!result) {
result = applyEntities(player, world, pos);
}
return result ? SpellCastResult.DEFAULT : SpellCastResult.NONE;
}
@Override
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, @Nonnull Entity hitEntity) {
return applyEntitySingle(player, world, hitEntity) ? SpellCastResult.DEFAULT : SpellCastResult.NONE;
}
@Override
public SpellCastResult onDispenced(BlockPos pos, EnumFacing facing, IBlockSource source) {
pos = pos.offset(facing, 4);
boolean result = false;
for (BlockPos i : PosHelper.getAllInRegionMutable(pos, effect_range)) {
result |= applyBlocks(null, source.getWorld(), i);
}
if (!result) {
result = applyEntities(null, source.getWorld(), pos);
}
return result ? SpellCastResult.NONE : SpellCastResult.DEFAULT;
}
protected boolean applyBlocks(EntityPlayer owner, World world, BlockPos pos) {
IBlockState state = world.getBlockState(pos);
Block id = state.getBlock();
if (id != Blocks.AIR) {
if (id == Blocks.ICE || id == Blocks.PACKED_ICE) {
world.setBlockState(pos, (world.provider.doesWaterVaporize() ? Blocks.AIR : Blocks.WATER).getDefaultState());
playEffect(world, pos);
return true;
} else if (id == Blocks.NETHERRACK) {
if (world.getBlockState(pos.up()).getMaterial() == Material.AIR) {
if (world.rand.nextInt(300) == 0) {
world.setBlockState(pos.up(), Blocks.FIRE.getDefaultState());
}
return true;
}
} else if (id == Blocks.REDSTONE_WIRE) {
int power = world.rand.nextInt(5) == 3 ? 15 : 3;
sendPower(world, pos, power, 3, 0);
return true;
} else if (id == Blocks.SAND && world.rand.nextInt(10) == 0) {
if (isSurroundedBySand(world, pos)) {
world.setBlockState(pos, Blocks.GLASS.getDefaultState());
playEffect(world, pos);
return true;
}
} else if (id instanceof BlockLeaves) {
if (world.getBlockState(pos.up()).getMaterial() == Material.AIR) {
world.setBlockState(pos.up(), Blocks.FIRE.getDefaultState());
playEffect(world, pos);
return true;
}
} else {
IBlockState newState = affected.getConverted(state);
if (!state.equals(newState)) {
world.setBlockState(pos, newState, 3);
playEffect(world, pos);
return true;
}
}
}
return false;
}
protected boolean applyEntities(EntityPlayer owner, World world, BlockPos pos) {
return VecHelper
.findAllEntitiesInRange(owner, world, pos, 3)
.filter(i -> applyEntitySingle(owner, world, i))
.count() > 0;
}
protected boolean applyEntitySingle(Entity owner, World world, Entity e) {
if ((!e.equals(owner) ||
(owner instanceof EntityPlayer && !Predicates.MAGI.test(owner))) && !(e instanceof EntityItem)
&& !(e instanceof IMagicals)) {
e.setFire(60);
e.attackEntityFrom(getDamageCause(e, (EntityLivingBase)owner), 0.1f);
playEffect(world, e.getPosition());
return true;
}
return false;
}
protected DamageSource getDamageCause(Entity target, EntityLivingBase attacker) {
return MagicalDamageSource.causeMobDamage("fire", attacker);
}
/**
* Transmists power to a piece of redstone
*/
private void sendPower(World w, BlockPos pos, int power, int max, int i) {
IBlockState state = w.getBlockState(pos);
Block id = state.getBlock();
if (i < max && id == Blocks.REDSTONE_WIRE) {
i++;
w.setBlockState(pos, state.withProperty(BlockRedstoneWire.POWER, power));
sendPower(w, pos.up(), power, max, i);
sendPower(w, pos.down(), power, max, i);
sendPower(w, pos.north(), power, max, i);
sendPower(w, pos.south(), power, max, i);
sendPower(w, pos.east(), power, max, i);
sendPower(w, pos.west(), power, max, i);
}
}
protected void playEffect(World world, BlockPos pos) {
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
world.playSound((double)((float)x + 0.5F), (double)((float)y + 0.5F), (double)((float)z + 0.5F), SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE, SoundCategory.AMBIENT, 0.5F, 2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F, true);
for (int i = 0; i < 8; ++i) {
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, (double)x + Math.random(), (double)y + Math.random(), (double)z + Math.random(), 0.0D, 0.0D, 0.0D);
}
}
public static boolean isSurroundedBySand(World w, BlockPos pos) {
return isSand(w, pos.up()) && isSand(w, pos.down()) &&
isSand(w, pos.north()) && isSand(w, pos.south()) &&
isSand(w, pos.east()) && isSand(w, pos.west());
}
public static boolean isSand(World world, BlockPos pos) {
Block id = world.getBlockState(pos).getBlock();
return id == Blocks.SAND || id == Blocks.GLASS;
}
}

View file

@ -0,0 +1,168 @@
package com.minelittlepony.unicopia.spell;
import com.minelittlepony.unicopia.UMaterials;
import com.minelittlepony.util.MagicalDamageSource;
import com.minelittlepony.util.PosHelper;
import com.minelittlepony.util.blockstate.IStateMapping;
import com.minelittlepony.util.blockstate.StateMapList;
import com.minelittlepony.util.shape.Sphere;
import com.minelittlepony.util.vector.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockBush;
import net.minecraft.block.BlockLeaves;
import net.minecraft.block.BlockRedstoneWire;
import net.minecraft.block.BlockSnow;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityTNTPrimed;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.world.World;
public class SpellIce extends AbstractSpell implements IUseAction, IDispenceable {
public static final StateMapList affected = new StateMapList();
static {
affected.add(IStateMapping.build(
s -> s.getMaterial() == Material.WATER,
s -> Blocks.ICE.getDefaultState()));
affected.add(IStateMapping.build(
s -> s.getMaterial() == Material.LAVA,
s -> Blocks.OBSIDIAN.getDefaultState()));
affected.add(IStateMapping.build(
s -> s.getBlock() == Blocks.SNOW_LAYER,
s -> s.cycleProperty(BlockSnow.LAYERS)));
affected.replaceBlock(Blocks.FIRE, Blocks.AIR);
affected.setProperty(Blocks.REDSTONE_WIRE, BlockRedstoneWire.POWER, 0);
}
protected int rad = 3;
@Override
public int getCurrentLevel() {
return 0;
}
@Override
public void setCurrentLevel(int level) {
}
@Override
public String getName() {
return "ice";
}
@Override
public boolean update(ICaster<?> source, int level) {
return false;
}
@Override
public void render(ICaster<?> source, int level) {
}
@Override
public SpellCastResult onDispenced(BlockPos pos, EnumFacing facing, IBlockSource source) {
return applyBlocks(null, source.getWorld(), pos.offset(facing, rad)) ? SpellCastResult.NONE : SpellCastResult.DEFAULT;
}
@Override
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) {
if (player != null && player.isSneaking()) {
applyBlockSingle(world, pos);
} else {
applyBlocks(player, world, pos);
}
return SpellCastResult.DEFAULT;
}
@Override
public SpellCastResult onUse(ItemStack stack, EntityPlayer player, World world, Entity hitEntity) {
if (hitEntity != null) {
applyEntitySingle(player, hitEntity);
return SpellCastResult.DEFAULT;
}
return SpellCastResult.NONE;
}
private boolean applyBlocks(EntityPlayer owner, World world, BlockPos pos) {
for (BlockPos i : PosHelper.getAllInRegionMutable(pos, new Sphere(false, rad))) {
applyBlockSingle(world, i);
}
return applyEntities(owner, world, pos);
}
protected boolean applyEntities(EntityPlayer owner, World world, BlockPos pos) {
return VecHelper.findAllEntitiesInRange(owner, world, pos, 3).filter(i -> {
applyEntitySingle(owner, i);
return true;
}).count() > 0;
}
protected void applyEntitySingle(EntityPlayer owner, Entity e) {
if (e instanceof EntityTNTPrimed) {
e.setDead();
e.getEntityWorld().setBlockState(e.getPosition(), Blocks.TNT.getDefaultState());
} else {
if (e.isBurning()) {
e.extinguish();
} else {
DamageSource d = MagicalDamageSource.causePlayerDamage("cold", owner);
e.attackEntityFrom(d, 2);
}
}
}
private void applyBlockSingle(World world, BlockPos pos) {
IBlockState state = world.getBlockState(pos);
Block id = state.getBlock();
IBlockState converted = affected.getConverted(state);
if (!state.equals(converted)) {
world.setBlockState(pos, converted, 3);
} else if (state.getMaterial() != UMaterials.cloud && state.isSideSolid(world, pos, EnumFacing.UP)
|| (id == Blocks.SNOW)
|| (id instanceof BlockLeaves)) {
incrementIce(world, pos.up());
} else if (id == Blocks.ICE && world.rand.nextInt(10) == 0) {
if (isSurroundedByIce(world, pos)) {
world.setBlockState(pos, Blocks.PACKED_ICE.getDefaultState());
}
}
world.spawnParticle(EnumParticleTypes.WATER_SPLASH, pos.getX() + world.rand.nextFloat(), pos.getY() + 1, pos.getZ() + world.rand.nextFloat(), 0, 0, 0);
}
public static boolean isSurroundedByIce(World w, BlockPos pos) {
return isIce(w, pos.up()) && isIce(w, pos.down()) &&
isIce(w, pos.north()) && isIce(w, pos.south()) &&
isIce(w, pos.east()) && isIce(w, pos.west());
}
public static boolean isIce(World world, BlockPos pos) {
return world.getBlockState(pos).getMaterial() == Material.ICE;
}
private void incrementIce(World world, BlockPos pos) {
IBlockState state = world.getBlockState(pos);
Block id = state.getBlock();
if (id == Blocks.AIR || (id instanceof BlockBush)) {
world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState(), 3);
}
}
}

View file

@ -25,8 +25,10 @@ public class SpellRegistry {
private final Map<String, Entry> entries = new HashMap<>();
private SpellRegistry() {
registerSpell("shield", 0xffff00, SpellShield::new);
registerSpell("charge", 0x0000ff, SpellCharge::new);
registerSpell("shield", 0x66CDAA, SpellShield::new);
registerSpell("charge", 0x0000AA, SpellCharge::new);
registerSpell("fire", 0xFF0000, SpellFire::new);
registerSpell("ice", 0xADD8E6, SpellIce::new);
}
public IMagicEffect getSpellFromName(String name) {

View file

@ -1,7 +1,5 @@
package com.minelittlepony.unicopia.spell;
import java.util.Random;
import com.minelittlepony.unicopia.Predicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
@ -9,7 +7,6 @@ import com.minelittlepony.unicopia.client.particle.Particles;
import com.minelittlepony.unicopia.player.PlayerSpeciesList;
import com.minelittlepony.unicopia.power.IPower;
import com.minelittlepony.util.ProjectileUtil;
import com.minelittlepony.util.shape.IShape;
import com.minelittlepony.util.shape.Sphere;
import net.minecraft.entity.Entity;
@ -17,7 +14,6 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
@ -51,20 +47,10 @@ public class SpellShield extends AbstractSpell {
}
protected void spawnParticles(ICaster<?> source, int strength) {
IShape sphere = new Sphere(true, strength);
Random rand = source.getWorld().rand;
int x = source.getOrigin().getX();
int y = source.getOrigin().getY();
int z = source.getOrigin().getZ();
for (int i = 0; i < strength * 6; i++) {
Vec3d pos = sphere.computePoint(rand);
Particles.instance().spawnParticle(Unicopia.MAGIC_PARTICLE, false,
pos.x + x, pos.y + y, pos.z + z,
0, 0, 0);
}
source.spawnParticles(new Sphere(true, strength), strength * 6, pos -> {
Particles.instance().spawnParticle(Unicopia.MAGIC_PARTICLE, false, pos.x, pos.y, pos.z, 0, 0, 0);
});
}
@Override
@ -90,20 +76,10 @@ public class SpellShield extends AbstractSpell {
int x = pos.getX(), y = pos.getY(), z = pos.getZ();
BlockPos begin = pos.add(-radius, -radius, -radius);
BlockPos end = pos.add(radius, radius, radius);
AxisAlignedBB bb = new AxisAlignedBB(begin, end);
boolean ownerIsValid = Predicates.MAGI.test(owner);
for (Entity i : source.getWorld().getEntitiesInAABBexcluding(source.getEntity(), bb, entity -> !(ownerIsValid && entity.equals(owner)))) {
source.findAllEntitiesInRange(radius).filter(entity -> !(ownerIsValid && entity.equals(owner))).forEach(i -> {
double dist = i.getDistance(x, y, z);
double dist2 = i.getDistance(x, y - i.getEyeHeight(), z);
if (dist > radius && dist2 > radius) {
continue;
}
if (ProjectileUtil.isProjectile(i)) {
if (!ProjectileUtil.isProjectileThrownBy(i, owner)) {
@ -126,7 +102,7 @@ public class SpellShield extends AbstractSpell {
-(y - i.posY) / force + (dist < 1 ? dist : 0),
-(z - i.posZ) / force);
}
}
});
return true;
}

View file

@ -1,13 +1,17 @@
package com.minelittlepony.util;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Streams;
import com.minelittlepony.util.shape.IShape;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.BlockPos.MutableBlockPos;
public class PosHelper {
@ -27,6 +31,31 @@ public class PosHelper {
return false;
}
public static Iterable<MutableBlockPos> getAllInRegionMutable(BlockPos origin, IShape shape) {
Iterator<MutableBlockPos> iter = BlockPos.getAllInBoxMutable(
origin.add(new BlockPos(shape.getLowerBound())),
origin.add(new BlockPos(shape.getUpperBound()))
).iterator();
return () -> new AbstractIterator<MutableBlockPos>() {
@Override
protected MutableBlockPos computeNext() {
while (iter.hasNext()) {
MutableBlockPos pos = iter.next();
if (shape.isPointInside(new Vec3d(pos.subtract(origin)))) {
return pos;
}
}
endOfData();
return null;
}
};
}
/**
* Creates a stream of mutable block positions ranging from the beginning position to end.
*/

View file

@ -0,0 +1,77 @@
package com.minelittlepony.util.blockstate;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
public interface IStateMapping extends Predicate<IBlockState>, Function<IBlockState, IBlockState> {
static IStateMapping removeBlock(Predicate<IBlockState> mapper) {
return build(
mapper,
s -> Blocks.AIR.getDefaultState());
}
static IStateMapping replaceBlock(Block from, Block to) {
return build(
s -> s.getBlock() == from,
s -> to.getDefaultState());
}
static <T extends Comparable<T>> IStateMapping replaceProperty(Block block, IProperty<T> property, T from, T to) {
return build(
s -> s.getBlock() == block && s.getValue(property) == from,
s -> s.withProperty(property, to));
}
static <T extends Comparable<T>> IStateMapping setProperty(Block block, IProperty<T> property, T to) {
return build(
s -> s.getBlock() == block,
s -> s.withProperty(property, to));
}
static IStateMapping build(Predicate<IBlockState> predicate, Function<IBlockState, IBlockState> converter) {
return new IStateMapping() {
@Override
public boolean test(IBlockState state) {
return predicate.test(state);
}
@Override
public IBlockState apply(IBlockState state) {
return converter.apply(state);
}
};
}
/**
* Checks if this state can be converted by this mapping
*
* @param state State to check
*
* @return True if the state can be converted
*/
@Override
default boolean test(@Nonnull IBlockState state) {
return true;
}
/**
* Converts the given state based on this mapping
*
* @param state State to convert
*
* @return The converted state
*/
@Nonnull
@Override
default IBlockState apply(@Nonnull IBlockState state) {
return state;
}
}

View file

@ -0,0 +1,64 @@
package com.minelittlepony.util.blockstate;
import java.util.ArrayList;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
/**
* A collection of block-state mappings.
*
*/
public class StateMapList extends ArrayList<IStateMapping> {
private static final long serialVersionUID = 2602772651960588745L;
public void removeBlock(Predicate<IBlockState> mapper) {
add(IStateMapping.removeBlock(mapper));
}
public void replaceBlock(Block from, Block to) {
add(IStateMapping.replaceBlock(from, to));
}
public <T extends Comparable<T>> void replaceProperty(Block block, IProperty<T> property, T from, T to) {
add(IStateMapping.replaceProperty(block, property, from, to));
}
public <T extends Comparable<T>> void setProperty(Block block, IProperty<T> property, T to) {
add(IStateMapping.setProperty(block, property, to));
}
/**
* Checks if this collection contains a mapping capable of converting the given state.
*
* @param state State to check
*
* @return True if the state can be converted
*/
public boolean canConvert(@Nullable IBlockState state) {
return state != null && stream().anyMatch(i -> i.test(state));
}
/**
* Attempts to convert the given state based on the known mappings in this collection.
*
* @param state State to convert
*
* @return The converted state if there is one, otherwise null
*/
@Nonnull
public IBlockState getConverted(@Nonnull IBlockState state) {
for (IStateMapping i : this) {
if (i.test(state)) {
return i.apply(state);
}
}
return state;
}
}

View file

@ -5,54 +5,64 @@ import java.util.Random;
import net.minecraft.util.math.Vec3d;
/**
*
*
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
*/
public interface IShape {
/**
* Rotates this shape around it's center.
*
*
* @param u Rotate yaw
* @param v Rotate pitch
*
*
* @return This Shape
*/
public IShape setRotation(float u, float v);
/**
* Get the volume of space filled by this shape, or the surface area if hollow.
*
*
* @return double volume
*/
public double getVolumeOfSpawnableSpace();
/**
* X offset from the shape's origin.
*
*
* @return X
*/
public double getXOffset();
/**
* Y offset from the shape's origin.
*
*
* @return Y
*/
public double getYOffset();
/**
* Z offset from the shape's origin.
*
*
* @return Z
*/
public double getZOffset();
/**
* Gets the lower bounds of the region occupied by this shape.
*/
public Vec3d getLowerBound();
/**
* Gets the upper bound of the region occupied by this shape.
*/
public Vec3d getUpperBound();
/**
* Computes a random coordinate that falls within this shape's designated area.
*/
public Vec3d computePoint(Random rand);
/**
* Checks if the given point is on the edge, or if not hollow the inside, of this shape.
* @return

View file

@ -78,7 +78,7 @@ public class Line implements IShape {
public Vec3d computePoint(Random rand) {
double distance = MathHelper.nextDouble(rand, 0, len);
return (new Vec3d(distance * dX, distance * dY, distance * dZ)).rotateYaw(yaw).rotatePitch(pitch);
return new Vec3d(dX, dY, dZ).scale(distance).add(sX, sY, sZ).rotateYaw(yaw).rotatePitch(pitch);
}
public Line setRotation(float u, float v) {
@ -89,6 +89,17 @@ public class Line implements IShape {
public boolean isPointInside(Vec3d point) {
point = point.rotateYaw(-yaw).rotatePitch(-pitch);
return point.x/dX == point.y/dY && point.x/dX == point.z/dZ;
}
@Override
public Vec3d getLowerBound() {
return new Vec3d(sX, sY, sZ).rotateYaw(yaw).rotatePitch(pitch);
}
@Override
public Vec3d getUpperBound() {
return new Vec3d(sX + dX, sY + dY, sZ + dZ).scale(len).rotateYaw(yaw).rotatePitch(pitch);
}
}

View file

@ -117,7 +117,19 @@ public class Sphere implements IShape {
public boolean isPointInside(Vec3d point) {
point = point.rotateYaw(-yaw).rotatePitch(-pitch);
point = new Vec3d(point.x / stretch.x, point.y / stretch.y, point.z / stretch.z);
double dist = point.length();
return hollow ? dist == rad : dist <= rad;
}
@Override
public Vec3d getLowerBound() {
return new Vec3d(-rad * stretch.x, -rad * stretch.y, -rad * stretch.z).rotateYaw(yaw).rotatePitch(pitch);
}
@Override
public Vec3d getUpperBound() {
return new Vec3d(rad * stretch.x, rad * stretch.y, rad * stretch.z).rotateYaw(yaw).rotatePitch(pitch);
}
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.util.vector;
import java.util.List;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@ -11,8 +12,10 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EntitySelectors;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class VecHelper {
@ -45,10 +48,25 @@ public class VecHelper {
return null;
}
public static Stream<Entity> findAllEntitiesInRange(@Nullable Entity origin, World w, BlockPos pos, double radius) {
BlockPos begin = pos.add(-radius, -radius, -radius);
BlockPos end = pos.add(radius, radius, radius);
AxisAlignedBB bb = new AxisAlignedBB(begin, end);
return w.getEntitiesInAABBexcluding(origin, bb, null).stream().filter(e -> {
double dist = e.getDistance(pos.getX(), pos.getY(), pos.getZ());
double dist2 = e.getDistance(pos.getX(), pos.getY() - e.getEyeHeight(), pos.getZ());
return dist <= radius || dist2 <= radius;
});
}
/**
* Gets all entities within a given range from the player.
*/
public static List<Entity> getWithinRange(EntityPlayer player, double reach, @Nullable Predicate <? super Entity > predicate) {
public static List<Entity> getWithinRange(EntityPlayer player, double reach, @Nullable Predicate<? super Entity> predicate) {
Vec3d look = player.getLook(0).scale(reach);
return player.world.getEntitiesInAABBexcluding(player, player