2021-02-18 21:30:43 +01:00
|
|
|
package com.minelittlepony.unicopia.item;
|
|
|
|
|
2022-09-30 18:48:34 +02:00
|
|
|
import java.util.*;
|
2023-01-09 11:38:32 +01:00
|
|
|
import java.util.function.Predicate;
|
2022-09-30 18:48:34 +02:00
|
|
|
import java.util.function.Supplier;
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2022-09-30 18:48:34 +02:00
|
|
|
import com.google.common.base.Suppliers;
|
|
|
|
import com.minelittlepony.unicopia.*;
|
2023-09-01 22:38:59 +02:00
|
|
|
import com.minelittlepony.unicopia.advancement.UCriteria;
|
2023-06-02 21:20:30 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
2023-09-02 19:34:57 +02:00
|
|
|
import com.minelittlepony.unicopia.entity.mob.FloatingArtefactEntity;
|
|
|
|
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
|
|
|
import com.minelittlepony.unicopia.entity.mob.FloatingArtefactEntity.State;
|
2021-02-18 21:30:43 +01:00
|
|
|
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;
|
2022-09-30 18:48:34 +02:00
|
|
|
import net.minecraft.entity.*;
|
2021-02-18 21:30:43 +01:00
|
|
|
import net.minecraft.entity.mob.MobEntity;
|
|
|
|
import net.minecraft.entity.passive.TameableEntity;
|
|
|
|
import net.minecraft.entity.player.PlayerEntity;
|
2022-09-30 18:48:34 +02:00
|
|
|
import net.minecraft.item.*;
|
2021-02-18 21:30:43 +01:00
|
|
|
import net.minecraft.particle.ParticleTypes;
|
|
|
|
import net.minecraft.predicate.entity.EntityPredicates;
|
|
|
|
import net.minecraft.server.world.ServerWorld;
|
|
|
|
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 {
|
2023-01-09 11:38:32 +01:00
|
|
|
static final Predicate<Entity> TARGET_PREDICATE = EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(EntityPredicates.VALID_ENTITY).and(e -> (e instanceof PlayerEntity || e instanceof MobEntity));
|
2022-09-30 18:48:34 +02:00
|
|
|
private static final Supplier<Map<Item, Item>> ITEM_MAP = Suppliers.memoize(() -> {
|
|
|
|
return Map.of(
|
|
|
|
Items.BUCKET, UItems.LOVE_BUCKET,
|
|
|
|
Items.GLASS_BOTTLE, UItems.LOVE_BOTTLE,
|
|
|
|
UItems.MUG, UItems.LOVE_MUG
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
private static boolean isFillable(ItemStack stack) {
|
|
|
|
return ITEM_MAP.get().containsKey(stack.getItem());
|
|
|
|
}
|
|
|
|
|
|
|
|
private static ItemStack fill(ItemStack stack) {
|
|
|
|
Item item = ITEM_MAP.get().getOrDefault(stack.getItem(), stack.getItem());
|
|
|
|
if (item == stack.getItem()) {
|
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
ItemStack newStack = item.getDefaultStack();
|
|
|
|
newStack.setNbt(stack.getNbt());
|
|
|
|
newStack.setCount(stack.getCount());
|
|
|
|
return newStack;
|
|
|
|
}
|
2021-02-18 21:30:43 +01:00
|
|
|
|
|
|
|
public CrystalHeartItem(Settings settings) {
|
|
|
|
super(settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ActionResult useOnBlock(ItemUsageContext context) {
|
|
|
|
|
|
|
|
World world = context.getWorld();
|
|
|
|
BlockPos blockPos = new ItemPlacementContext(context).getBlockPos();
|
|
|
|
|
2021-02-22 20:56:06 +01:00
|
|
|
Box placementArea = UEntities.FLOATING_ARTEFACT.getDimensions().getBoxAt(Vec3d.ofBottomCenter(blockPos));
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2021-12-22 10:15:09 +01:00
|
|
|
if (!world.isSpaceEmpty(null, placementArea)) {
|
2021-02-18 21:30:43 +01:00
|
|
|
return ActionResult.FAIL;
|
|
|
|
}
|
|
|
|
|
2022-09-30 18:48:34 +02:00
|
|
|
if (world instanceof ServerWorld serverWorld) {
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2022-12-18 22:07:24 +01:00
|
|
|
FloatingArtefactEntity entity = UEntities.FLOATING_ARTEFACT.create(serverWorld, context.getStack().getNbt(), null, blockPos, SpawnReason.SPAWN_EGG, false, true);
|
2021-02-18 21:30:43 +01:00
|
|
|
|
|
|
|
if (entity == null) {
|
|
|
|
return ActionResult.FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
entity.setStack(context.getStack().split(1));
|
2023-09-01 22:38:59 +02:00
|
|
|
serverWorld.spawnEntity(entity);
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2023-09-01 22:38:59 +02:00
|
|
|
if (findStructure(entity)) {
|
|
|
|
entity.playSound(USounds.ENTITY_CRYSTAL_HEART_ACTIVATE, 0.75F, 0.8F);
|
|
|
|
UCriteria.POWER_UP_HEART.trigger(context.getPlayer());
|
|
|
|
}
|
2021-02-18 21:30:43 +01:00
|
|
|
} else {
|
|
|
|
context.getStack().decrement(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ActionResult.success(world.isClient);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onArtifactTick(FloatingArtefactEntity entity) {
|
|
|
|
|
|
|
|
if (entity.getState() == State.INITIALISING) {
|
2023-08-28 20:47:23 +02:00
|
|
|
if (entity.age % 30 == 0 && findStructure(entity)) {
|
2021-02-18 21:30:43 +01:00
|
|
|
entity.setState(State.RUNNING);
|
2023-08-28 20:47:23 +02:00
|
|
|
entity.setRotationSpeed(4, 30);
|
2021-02-18 21:30:43 +01:00
|
|
|
}
|
|
|
|
} else {
|
2023-08-28 20:47:23 +02:00
|
|
|
if (entity.age % 30 == 0 && !findStructure(entity)) {
|
2021-02-18 21:30:43 +01:00
|
|
|
entity.setState(State.INITIALISING);
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockPos pos = entity.getBlockPos();
|
2023-06-03 13:40:54 +02:00
|
|
|
entity.getWorld().addParticle(ParticleTypes.COMPOSTER,
|
|
|
|
pos.getX() + entity.getWorld().getRandom().nextFloat(),
|
|
|
|
pos.getY() + entity.getWorld().getRandom().nextFloat(),
|
|
|
|
pos.getZ() + entity.getWorld().getRandom().nextFloat(),
|
2021-02-18 21:30:43 +01:00
|
|
|
0, 0, 0);
|
|
|
|
|
2023-08-28 20:47:23 +02:00
|
|
|
if (entity.age % 80 == 0 && !entity.getWorld().isClient) {
|
2021-02-18 21:30:43 +01:00
|
|
|
List<LivingEntity> inputs = new ArrayList<>();
|
|
|
|
List<LivingEntity> outputs = new ArrayList<>();
|
2022-09-30 18:48:34 +02:00
|
|
|
List<ItemEntity> containers = new ArrayList<>();
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2023-06-03 13:40:54 +02:00
|
|
|
VecHelper.findInRange(entity, entity.getWorld(), entity.getPos(), 20, TARGET_PREDICATE).forEach(e -> {
|
2021-02-18 21:30:43 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
2023-06-03 13:40:54 +02:00
|
|
|
VecHelper.findInRange(entity, entity.getWorld(), entity.getPos(), 20, i -> {
|
2023-09-01 20:09:13 +02:00
|
|
|
return i instanceof ItemEntity ie && isFillable(ie.getStack()) && EquinePredicates.CHANGELING.test(i);
|
2022-09-30 18:48:34 +02:00
|
|
|
}).forEach(i -> containers.add((ItemEntity)i));
|
2021-02-18 21:30:43 +01:00
|
|
|
|
2022-09-30 18:48:34 +02:00
|
|
|
int demand = outputs.size() + containers.stream().mapToInt(i -> i.getStack().getCount()).sum();
|
2021-02-18 21:30:43 +01:00
|
|
|
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 -> {
|
2023-06-02 21:20:30 +02:00
|
|
|
input.damage(entity.damageOf(UDamageTypes.LIFE_DRAINING), takes);
|
2021-02-18 21:30:43 +01:00
|
|
|
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);
|
|
|
|
});
|
2022-09-30 18:48:34 +02:00
|
|
|
containers.forEach(container -> {
|
|
|
|
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, container, 0.2F), entity, 1);
|
|
|
|
container.setStack(fill(container.getStack()));
|
|
|
|
});
|
2021-02-21 21:17:37 +01:00
|
|
|
|
2023-08-28 20:47:23 +02:00
|
|
|
if (gives > 0) {
|
|
|
|
entity.setRotationSpeed(37, 80);
|
|
|
|
}
|
2021-02-18 21:30:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ActionResult onArtifactDestroyed(FloatingArtefactEntity entity) {
|
2022-01-04 23:43:07 +01:00
|
|
|
entity.playSound(USounds.ENTITY_CRYSTAL_HEART_DEACTIVATE, 0.75F, 1);
|
2023-06-03 13:40:54 +02:00
|
|
|
entity.dropStack(new ItemStack(UItems.CRYSTAL_SHARD, 1 + entity.getWorld().random.nextInt(5)), 0);
|
2021-02-19 09:34:26 +01:00
|
|
|
return ActionResult.SUCCESS;
|
2021-02-18 21:30:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2023-06-03 13:40:54 +02:00
|
|
|
BlockState tipState = entity.getWorld().getBlockState(tip);
|
2021-02-18 21:30:43 +01:00
|
|
|
if (!tipState.isIn(UTags.CRYSTAL_HEART_ORNAMENT) || (!tipState.contains(EndRodBlock.FACING)|| tipState.get(EndRodBlock.FACING) != direction.getOpposite())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
tip = tip.offset(direction);
|
2023-06-03 13:40:54 +02:00
|
|
|
if (!isDiamond(entity.getWorld().getBlockState(tip))) {
|
2021-02-18 21:30:43 +01:00
|
|
|
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())
|
2023-06-03 13:40:54 +02:00
|
|
|
.map(entity.getWorld()::getBlockState)
|
2021-02-18 21:30:43 +01:00
|
|
|
.allMatch(this::isDiamond);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isDiamond(BlockState state) {
|
|
|
|
return state.isIn(UTags.CRYSTAL_HEART_BASE);
|
|
|
|
}
|
|
|
|
}
|