Added the Crystal Heart + multi-block structure

This commit is contained in:
Sollace 2021-02-18 22:30:43 +02:00
parent ee3fdc14c9
commit 73b1f8bd42
18 changed files with 570 additions and 15 deletions

View file

@ -6,8 +6,11 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
public interface EquinePredicates { public interface EquinePredicates {
@ -23,6 +26,8 @@ public interface EquinePredicates {
Predicate<Entity> IS_CASTER = e -> !e.removed && (e instanceof Caster || PLAYER_UNICORN.test(e)); Predicate<Entity> IS_CASTER = e -> !e.removed && (e instanceof Caster || PLAYER_UNICORN.test(e));
Predicate<LivingEntity> HAS_WANT_IT_NEED_IT = e -> EnchantmentHelper.getEquipmentLevel(UEnchantments.WANT_IT_NEED_IT, e) > 0;
static Predicate<Entity> carryingSpell(Class<? extends Spell> type) { static Predicate<Entity> carryingSpell(Class<? extends Spell> type) {
return IS_PLAYER.and(entity -> Pony.of((PlayerEntity)entity).getSpellOrEmpty(type, false).isPresent()); return IS_PLAYER.and(entity -> Pony.of((PlayerEntity)entity).getSpellOrEmpty(type, false).isPresent());
} }

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia; package com.minelittlepony.unicopia;
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
@ -16,6 +17,9 @@ public interface UEntities {
.trackRangeBlocks(100) .trackRangeBlocks(100)
.trackedUpdateRate(2) .trackedUpdateRate(2)
.dimensions(EntityDimensions.fixed(0.25F, 0.25F))); .dimensions(EntityDimensions.fixed(0.25F, 0.25F)));
EntityType<FloatingArtefactEntity> FLOATING_ARTEFACT = register("floating_artefact", FabricEntityTypeBuilder.create(SpawnGroup.MISC, FloatingArtefactEntity::new)
.trackRangeBlocks(200)
.dimensions(EntityDimensions.fixed(1, 1)));
static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) { static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) {
EntityType<T> type = builder.build(); EntityType<T> type = builder.build();

View file

@ -17,6 +17,9 @@ public interface UTags {
Tag<Block> FRAGILE = block("fragile"); Tag<Block> FRAGILE = block("fragile");
Tag<Block> INTERESTING = block("interesting"); Tag<Block> INTERESTING = block("interesting");
Tag<Block> CRYSTAL_HEART_BASE = block("crystal_heart_base");
Tag<Block> CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament");
static Tag<Item> item(String name) { static Tag<Item> item(String name) {
return TagRegistry.item(new Identifier("unicopia", name)); return TagRegistry.item(new Identifier("unicopia", name));
} }

View file

@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.client.particle.RainboomParticle;
import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle; import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle;
import com.minelittlepony.unicopia.client.particle.RaindropsParticle; import com.minelittlepony.unicopia.client.particle.RaindropsParticle;
import com.minelittlepony.unicopia.client.particle.SphereParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle;
import com.minelittlepony.unicopia.client.render.FloatingArtefactEntityRenderer;
import com.minelittlepony.unicopia.item.ChameleonItem; import com.minelittlepony.unicopia.item.ChameleonItem;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
@ -44,6 +45,7 @@ public interface URenderers {
ParticleFactoryRegistry.getInstance().register(UParticles.GROUND_POUND, GroundPoundParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.GROUND_POUND, GroundPoundParticle::new);
EntityRendererRegistry.INSTANCE.register(UEntities.THROWN_ITEM, (manager, context) -> new FlyingItemEntityRenderer<>(manager, context.getItemRenderer())); EntityRendererRegistry.INSTANCE.register(UEntities.THROWN_ITEM, (manager, context) -> new FlyingItemEntityRenderer<>(manager, context.getItemRenderer()));
EntityRendererRegistry.INSTANCE.register(UEntities.FLOATING_ARTEFACT, FloatingArtefactEntityRenderer::new);
ColorProviderRegistry.ITEM.register((stack, i) -> i > 0 ? -1 : ((DyeableItem)stack.getItem()).getColor(stack), UItems.FRIENDSHIP_BRACELET); ColorProviderRegistry.ITEM.register((stack, i) -> i > 0 ? -1 : ((DyeableItem)stack.getItem()).getColor(stack), UItems.FRIENDSHIP_BRACELET);
BuiltinItemRendererRegistry.INSTANCE.register(UItems.FILLED_JAR, (stack, mode, matrices, vertexConsumers, light, overlay) -> { BuiltinItemRendererRegistry.INSTANCE.register(UItems.FILLED_JAR, (stack, mode, matrices, vertexConsumers, light, overlay) -> {

View file

@ -0,0 +1,63 @@
package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity;
import com.minelittlepony.unicopia.item.UItems;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
public class FloatingArtefactEntityRenderer extends EntityRenderer<FloatingArtefactEntity> {
private final ItemRenderer itemRenderer;
public FloatingArtefactEntityRenderer(EntityRenderDispatcher manager, EntityRendererRegistry.Context context) {
super(manager);
itemRenderer = context.getItemRenderer();
}
@Override
public void render(FloatingArtefactEntity entity, float yaw, float timeDelta, MatrixStack transforms, VertexConsumerProvider renderContext, int lightUv) {
ItemStack stack = entity.getStack();
if (stack.isEmpty()) {
stack = UItems.EMPTY_JAR.getDefaultStack();
}
final BakedModel model = this.itemRenderer.getHeldItemModel(stack, entity.world, null);
final float variance = 0.25F;
final float verticalOffset = entity.getVerticalOffset(timeDelta);
final float modelScaleY = model.getTransformation().getTransformation(ModelTransformation.Mode.GROUND).scale.getY();
float scale = 1.6F;
transforms.push();
transforms.scale(scale, scale, scale);
transforms.translate(0, verticalOffset + variance * modelScaleY, 0);
transforms.multiply(Vector3f.POSITIVE_Y.getRadialQuaternion(entity.getRotation(timeDelta)));
itemRenderer.renderItem(stack, ModelTransformation.Mode.GROUND, false, transforms, renderContext, lightUv, OverlayTexture.DEFAULT_UV, model);
transforms.pop();
super.render(entity, yaw, timeDelta, transforms, renderContext, lightUv);
}
@Override
public Identifier getTexture(FloatingArtefactEntity entity) {
return SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE;
}
}

View file

@ -6,11 +6,13 @@ import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry; import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
import com.minelittlepony.unicopia.entity.ai.WantItNeedItTargetGoal; import com.minelittlepony.unicopia.entity.ai.WantItNeedItTargetGoal;
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal; import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;
import com.minelittlepony.unicopia.entity.player.PlayerAttributes; import com.minelittlepony.unicopia.entity.player.PlayerAttributes;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.ai.goal.GoalSelector; import net.minecraft.entity.ai.goal.GoalSelector;
import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.attribute.EntityAttributes;
@ -37,6 +39,9 @@ public class Creature extends Living<LivingEntity> {
public void initAi(GoalSelector goals, GoalSelector targets) { public void initAi(GoalSelector goals, GoalSelector targets) {
targets.add(1, new WantItNeedItTargetGoal((MobEntity)entity)); targets.add(1, new WantItNeedItTargetGoal((MobEntity)entity));
goals.add(1, new WantItTakeItGoal((MobEntity)entity)); goals.add(1, new WantItTakeItGoal((MobEntity)entity));
if (entity.getType().getSpawnGroup() == SpawnGroup.MONSTER) {
goals.add(2, new BreakHeartGoal((MobEntity)entity));
}
} }
public static void registerAttributes(DefaultAttributeContainer.Builder builder) { public static void registerAttributes(DefaultAttributeContainer.Builder builder) {

View file

@ -0,0 +1,172 @@
package com.minelittlepony.unicopia.entity;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.Packet;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class FloatingArtefactEntity extends Entity {
private static final TrackedData<ItemStack> ITEM = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.ITEM_STACK);
private static final TrackedData<Byte> STATE = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.BYTE);
private int age;
private float health = 1;
public final float positionSeed;
public FloatingArtefactEntity(EntityType<?> entityType, World world) {
super(entityType, world);
positionSeed = (float)(Math.random() * Math.PI * 2);
}
@Override
protected void initDataTracker() {
dataTracker.startTracking(ITEM, ItemStack.EMPTY);
dataTracker.startTracking(STATE, (byte)0);
}
public ItemStack getStack() {
return dataTracker.get(ITEM);
}
public void setStack(ItemStack stack) {
dataTracker.set(ITEM, stack);
}
public State getState() {
return State.valueOf(dataTracker.get(STATE));
}
public void setState(State state) {
dataTracker.set(STATE, (byte)state.ordinal());
}
public void addSpin(int spin) {
if (age != -32768) {
age += spin;
}
}
@Override
public void tick() {
Vec3d pos = Vec3d.ofBottomCenter(getBlockPos());
setPos(pos.x, pos.y, pos.z);
super.tick();
ItemStack stack = getStack();
if (stack.isEmpty()) {
setStack(UItems.EMPTY_JAR.getDefaultStack());
}
if (stack.getItem() instanceof Artifact) {
((Artifact)stack.getItem()).onArtifactTick(this);
}
if (world.getTime() % 80 == 0) {
State state = getState();
playSound(SoundEvents.BLOCK_BEACON_AMBIENT, state.getVolume(), state.getPitch());
}
addSpin(1);
}
public float getVerticalOffset(float tickDelta) {
return MathHelper.sin((age + tickDelta) / 10F + positionSeed) * 0.025F + 0.05F;
}
public float getRotation(float tickDelta) {
return (age + tickDelta) / 20 + positionSeed;
}
@Override
protected void readCustomDataFromTag(CompoundTag compound) {
ItemStack itemStack = ItemStack.fromTag(compound.getCompound("Item"));
setStack(itemStack);
setState(State.valueOf(compound.getInt("State")));
}
@Override
protected void writeCustomDataToTag(CompoundTag compound) {
ItemStack stack = getStack();
if (!stack.isEmpty()) {
compound.put("Item", stack.toTag(new CompoundTag()));
}
compound.putInt("State", getState().ordinal());
}
@Override
public boolean damage(DamageSource damageSource, float amount) {
if (isInvulnerableTo(damageSource) || !getStack().getItem().damage(damageSource)) {
return false;
}
scheduleVelocityUpdate();
health -= amount;
if (health <= 0) {
remove();
ItemStack stack = getStack();
if (!(stack.getItem() instanceof Artifact) || ((Artifact)stack.getItem()).onArtifactDestroyed(this) != ActionResult.SUCCESS) {
dropStack(stack);
}
}
return false;
}
@Override
public boolean collides() {
return true;
}
@Override
public Packet<?> createSpawnPacket() {
return Channel.SERVER_SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this));
}
public enum State {
INITIALISING,
RUNNING,
SHUTTING_DOWN;
static final State[] VALUES = values();
public float getVolume() {
return this == SHUTTING_DOWN ? 1 : 0.2F;
}
public float getPitch() {
return this == INITIALISING ? 1 : this == RUNNING ? 2 : 0.5F;
}
static State valueOf(int state) {
return state <= 0 || state >= VALUES.length ? INITIALISING : VALUES[state];
}
}
public interface Artifact {
void onArtifactTick(FloatingArtefactEntity entity);
ActionResult onArtifactDestroyed(FloatingArtefactEntity entity);
}
}

View file

@ -0,0 +1,92 @@
package com.minelittlepony.unicopia.entity.ai;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.Optional;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.mob.MobEntity;
public class BreakHeartGoal extends Goal {
private final MobEntity mob;
@Nullable
private FloatingArtefactEntity target;
private int cooldown;
public BreakHeartGoal(MobEntity mob) {
this.mob = mob;
this.setControls(EnumSet.of(Goal.Control.MOVE, Goal.Control.LOOK));
}
@Override
public boolean canStart() {
Optional<FloatingArtefactEntity> item = VecHelper.findInRange(mob, mob.world, mob.getPos(), 16,
e -> !e.removed && e instanceof FloatingArtefactEntity && ((FloatingArtefactEntity)e).getStack().getItem() == UItems.CRYSTAL_HEART)
.stream()
.map(e -> (FloatingArtefactEntity)e)
.sorted(Comparator.comparing((Entity e) -> mob.distanceTo(e)))
.findFirst();
if (item.isPresent()) {
this.target = item.get();
return true;
}
return false;
}
@Override
public boolean shouldContinue() {
return (target == null || (target.isAlive() && mob.squaredDistanceTo(target) <= 225)) || canStart();
}
@Override
public void stop() {
target = null;
mob.getNavigation().stop();
}
@Override
public void tick() {
if (target == null || target.removed) {
target = null;
return;
}
mob.getLookControl().lookAt(target, 30, 30);
double reach = mob.getWidth() * 2 * mob.getWidth() * 2;
double distance = mob.squaredDistanceTo(target.getX(), target.getY(), target.getZ());
double speed = 0.9D;
if (distance > reach && distance < 16) {
speed = 1.23;
} else if (distance < 225) {
speed = 0.6;
}
mob.getNavigation().startMovingTo(target, speed);
cooldown = Math.max(this.cooldown - 1, 0);
if (target.getY() >= mob.getY() + 2) {
reach += 5;
}
if (distance <= reach && cooldown <= 0) {
cooldown = 20;
mob.tryAttack(target);
}
}
}

View file

@ -3,8 +3,7 @@ package com.minelittlepony.unicopia.entity.ai;
import java.util.Comparator; import java.util.Comparator;
import java.util.Optional; import java.util.Optional;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.EquinePredicates;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.TargetPredicate; import net.minecraft.entity.ai.TargetPredicate;
@ -22,7 +21,7 @@ public class WantItNeedItTargetGoal extends Goal {
public WantItNeedItTargetGoal(MobEntity mob) { public WantItNeedItTargetGoal(MobEntity mob) {
this.predicate = new TargetPredicate() this.predicate = new TargetPredicate()
.setBaseMaxDistance(64) .setBaseMaxDistance(64)
.setPredicate(WantItNeedItTargetGoal::canTarget); .setPredicate(EquinePredicates.HAS_WANT_IT_NEED_IT);
this.mob = mob; this.mob = mob;
} }
@ -51,9 +50,4 @@ public class WantItNeedItTargetGoal extends Goal {
LivingEntity target = mob.getTarget(); LivingEntity target = mob.getTarget();
return target != null && mob.isTarget(target, TargetPredicate.DEFAULT); return target != null && mob.isTarget(target, TargetPredicate.DEFAULT);
} }
static boolean canTarget(LivingEntity e) {
return EnchantmentHelper.getEquipmentLevel(UEnchantments.WANT_IT_NEED_IT, e) > 0;
}
} }

View file

@ -7,6 +7,7 @@ import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -41,10 +42,10 @@ public class WantItTakeItGoal extends Goal {
@Override @Override
public boolean canStart() { public boolean canStart() {
LivingEntity target = mob.getTarget(); LivingEntity target = mob.getTarget();
if (target == null || !WantItNeedItTargetGoal.canTarget(target)) { if (target == null || !EquinePredicates.HAS_WANT_IT_NEED_IT.test(target)) {
Optional<ItemEntity> item = VecHelper.findInRange(mob, mob.world, mob.getPos(), 16, Optional<ItemEntity> item = VecHelper.findInRange(mob, mob.world, mob.getPos(), 16,
e -> !e.removed && e instanceof ItemEntity && EnchantmentHelper.getLevel(UEnchantments.WANT_IT_NEED_IT, ((ItemEntity)e).getStack()) > 0) e -> !e.removed && mob.canSee(e) && e instanceof ItemEntity && EnchantmentHelper.getLevel(UEnchantments.WANT_IT_NEED_IT, ((ItemEntity)e).getStack()) > 0)
.stream() .stream()
.map(e -> (ItemEntity)e) .map(e -> (ItemEntity)e)
.sorted(Comparator.comparing((Entity e) -> mob.distanceTo(e))) .sorted(Comparator.comparing((Entity e) -> mob.distanceTo(e)))
@ -66,7 +67,7 @@ public class WantItTakeItGoal extends Goal {
public boolean shouldContinue() { public boolean shouldContinue() {
return (target == null || ( return (target == null || (
target.isAlive() target.isAlive()
&& WantItNeedItTargetGoal.canTarget(target) && EquinePredicates.HAS_WANT_IT_NEED_IT.test(target)
&& mob.squaredDistanceTo(target) <= 225)) && mob.squaredDistanceTo(target) <= 225))
&& (item == null || item.isAlive()) && (item == null || item.isAlive())
&& (!mob.getNavigation().isIdle() || canStart()); && (!mob.getNavigation().isIdle() || canStart());

View file

@ -0,0 +1,186 @@
package com.minelittlepony.unicopia.item;
import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.unicopia.UEntities;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity;
import com.minelittlepony.unicopia.entity.FloatingArtefactEntity.State;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.EndRodBlock;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.Saddleable;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.passive.TameableEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class CrystalHeartItem extends Item implements FloatingArtefactEntity.Artifact {
public CrystalHeartItem(Settings settings) {
super(settings);
}
@Override
public ActionResult useOnBlock(ItemUsageContext context) {
World world = context.getWorld();
BlockPos blockPos = new ItemPlacementContext(context).getBlockPos();
Box placementArea = UEntities.FLOATING_ARTEFACT.getDimensions().method_30757(Vec3d.ofBottomCenter(blockPos));
if (!world.isSpaceEmpty(null, placementArea, (entity) -> !(entity instanceof ItemEntity))
|| !world.getOtherEntities(null, placementArea).stream().noneMatch(e -> !(e instanceof ItemEntity))) {
return ActionResult.FAIL;
}
if (world instanceof ServerWorld) {
FloatingArtefactEntity entity = UEntities.FLOATING_ARTEFACT.create((ServerWorld)world, context.getStack().getTag(), null, context.getPlayer(), blockPos, SpawnReason.SPAWN_EGG, true, true);
if (entity == null) {
return ActionResult.FAIL;
}
entity.setStack(context.getStack().split(1));
((ServerWorld)world).spawnEntityAndPassengers(entity);
entity.playSound(SoundEvents.BLOCK_BEACON_ACTIVATE, 0.75F, 0.8F);
} else {
context.getStack().decrement(1);
}
return ActionResult.success(world.isClient);
}
@Override
public void onArtifactTick(FloatingArtefactEntity entity) {
if (entity.getState() == State.INITIALISING) {
if (findStructure(entity)) {
entity.setState(State.RUNNING);
}
} else {
if (!findStructure(entity)) {
entity.setState(State.INITIALISING);
}
entity.addSpin(2);
BlockPos pos = entity.getBlockPos();
entity.world.addParticle(ParticleTypes.COMPOSTER,
pos.getX() + entity.world.getRandom().nextFloat(),
pos.getY() + entity.world.getRandom().nextFloat(),
pos.getZ() + entity.world.getRandom().nextFloat(),
0, 0, 0);
if (entity.world.getTime() % 80 == 0 && !entity.world.isClient) {
List<LivingEntity> inputs = new ArrayList<>();
List<LivingEntity> outputs = new ArrayList<>();
VecHelper.findInRange(entity, entity.world, entity.getPos(), 20, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(e -> !e.removed && (e instanceof PlayerEntity || e instanceof MobEntity))).forEach(e -> {
LivingEntity living = (LivingEntity)e;
if (e instanceof PlayerEntity
|| (living instanceof TameableEntity && ((TameableEntity)living).isTamed())
|| (living instanceof Saddleable && ((Saddleable)living).isSaddled())) {
if (living.getHealth() < living.getMaxHealth()) {
outputs.add(living);
}
} else if (e.getType().getSpawnGroup() == SpawnGroup.MONSTER) {
inputs.add(living);
}
});
int demand = outputs.size();
int supply = inputs.size();
if (demand == 0 || supply == 0) {
return;
}
float gives;
float takes;
if (supply > demand) {
gives = supply / demand;
takes = 1;
} else if (demand > supply) {
takes = demand / supply;
gives = 1;
} else {
gives = 1;
takes = 1;
}
inputs.forEach(input -> {
input.damage(DamageSource.MAGIC, takes);
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, entity, 0.2F), input, 1);
});
outputs.forEach(output -> {
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, output, 0.2F), entity, 1);
output.heal(gives);
});
}
}
}
@Override
public ActionResult onArtifactDestroyed(FloatingArtefactEntity entity) {
entity.playSound(SoundEvents.BLOCK_BEACON_DEACTIVATE, 0.75F, 1);
return ActionResult.PASS;
}
private boolean findStructure(FloatingArtefactEntity entity) {
return findPyramid(entity, Direction.UP) && findPyramid(entity, Direction.DOWN);
}
private boolean findPyramid(FloatingArtefactEntity entity, Direction direction) {
BlockPos tip = entity.getBlockPos().offset(direction);
BlockState tipState = entity.world.getBlockState(tip);
if (!tipState.isIn(UTags.CRYSTAL_HEART_ORNAMENT) || (!tipState.contains(EndRodBlock.FACING)|| tipState.get(EndRodBlock.FACING) != direction.getOpposite())) {
return false;
}
tip = tip.offset(direction);
if (!isDiamond(entity.world.getBlockState(tip))) {
return false;
}
tip = tip.offset(direction);
final BlockPos center = tip;
return BlockPos.streamOutwards(center, 1, 0, 1)
.filter(p -> p.getX() == center.getX() || p.getZ() == center.getZ())
.map(entity.world::getBlockState)
.allMatch(this::isDiamond);
}
private boolean isDiamond(BlockState state) {
return state.isIn(UTags.CRYSTAL_HEART_BASE);
}
}

View file

@ -55,6 +55,8 @@ public interface UItems {
Item LIGHTNING_JAR = register("lightning_jar", new JarItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1).fireproof(), false, false, true)); Item LIGHTNING_JAR = register("lightning_jar", new JarItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1).fireproof(), false, false, true));
Item ZAP_APPLE_JAM_JAR = register("zap_apple_jam_jar", new JarItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1).fireproof(), false, false, true)); Item ZAP_APPLE_JAM_JAR = register("zap_apple_jam_jar", new JarItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1).fireproof(), false, false, true));
Item CRYSTAL_HEART = register("crystal_heart", new CrystalHeartItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1)));
static <T extends Item> T register(String name, T item) { static <T extends Item> T register(String name, T item) {
ITEMS.add(item); ITEMS.add(item);
if (item instanceof BlockItem) { if (item instanceof BlockItem) {

View file

@ -5,12 +5,12 @@ import java.util.Optional;
import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.Owned;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket;
import net.minecraft.world.World;
public class MsgSpawnProjectile extends EntitySpawnS2CPacket implements Channel.Packet { public class MsgSpawnProjectile extends EntitySpawnS2CPacket implements Channel.Packet {
@ -36,7 +36,7 @@ public class MsgSpawnProjectile extends EntitySpawnS2CPacket implements Channel.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void handle(PlayerEntity sender) { public void handle(PlayerEntity sender) {
World world = sender.world; ClientWorld world = MinecraftClient.getInstance().world;
Entity entity = getEntityTypeId().create(world); Entity entity = getEntityTypeId().create(world);
entity.updateTrackedPosition(getX(), getY(), getZ()); entity.updateTrackedPosition(getX(), getY(), getZ());
@ -48,10 +48,10 @@ public class MsgSpawnProjectile extends EntitySpawnS2CPacket implements Channel.
entity.setUuid(getUuid()); entity.setUuid(getUuid());
if (entity instanceof Owned) { if (entity instanceof Owned) {
((Owned<Entity>) entity).setMaster(world.getEntityById(this.getEntityData())); ((Owned<Entity>) entity).setMaster(world.getEntityById(getEntityData()));
} }
((ClientWorld)world).addEntity(getId(), entity); world.addEntity(getId(), entity);
} }
} }

View file

@ -23,6 +23,8 @@
"item.unicopia.lightning_jar": "Lightning in a Jar", "item.unicopia.lightning_jar": "Lightning in a Jar",
"item.unicopia.zap_apple_jam_jar": "Zap Apple Jam", "item.unicopia.zap_apple_jam_jar": "Zap Apple Jam",
"item.unicopia.crystal_heart": "Crystal Heart",
"item.unicopia.music_disc_pet": "Music Disc", "item.unicopia.music_disc_pet": "Music Disc",
"item.unicopia.music_disc_pet.desc": "Danial Ingram - pet", "item.unicopia.music_disc_pet.desc": "Danial Ingram - pet",
"item.unicopia.music_disc_popular": "Music Disc", "item.unicopia.music_disc_popular": "Music Disc",

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "unicopia:item/crystal_heart"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,12 @@
{
"replace": false,
"values": [
"minecraft:diamond_block",
"minecraft:quartz_block",
"minecraft:smooth_quartz",
"minecraft:chiseled_quartz_block",
"minecraft:quartz_pillar",
"minecraft:quartz_stairs",
"minecraft:smooth_quartz_stairs"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"minecraft:end_rod"
]
}