mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 19:46:42 +01:00
Added the Crystal Heart + multi-block structure
This commit is contained in:
parent
ee3fdc14c9
commit
73b1f8bd42
18 changed files with 570 additions and 15 deletions
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) -> {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "unicopia:item/crystal_heart"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"replace": false,
|
||||||
|
"values": [
|
||||||
|
"minecraft:end_rod"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue