[Feature] Particle Effects for the Eternal Portal

This commit is contained in:
Frank 2023-06-21 01:42:51 +02:00
parent 2cf510b160
commit 55985d0660
5 changed files with 271 additions and 38 deletions

View file

@ -1,5 +1,6 @@
package org.betterx.betterend; package org.betterx.betterend;
import org.betterx.bclib.api.v2.dataexchange.DataExchangeAPI;
import org.betterx.bclib.api.v2.generator.BiomeDecider; import org.betterx.bclib.api.v2.generator.BiomeDecider;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI; import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.betterend.advancements.BECriteria; import org.betterx.betterend.advancements.BECriteria;
@ -9,6 +10,7 @@ import org.betterx.betterend.config.Configs;
import org.betterx.betterend.effects.EndPotions; import org.betterx.betterend.effects.EndPotions;
import org.betterx.betterend.integration.Integrations; import org.betterx.betterend.integration.Integrations;
import org.betterx.betterend.integration.trinkets.Elytra; import org.betterx.betterend.integration.trinkets.Elytra;
import org.betterx.betterend.network.RitualUpdate;
import org.betterx.betterend.recipe.builders.InfusionRecipe; import org.betterx.betterend.recipe.builders.InfusionRecipe;
import org.betterx.betterend.registry.*; import org.betterx.betterend.registry.*;
import org.betterx.betterend.tab.CreativeTabs; import org.betterx.betterend.tab.CreativeTabs;
@ -25,6 +27,8 @@ import net.minecraft.world.level.biome.Biomes;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import java.util.List;
public class BetterEnd implements ModInitializer { public class BetterEnd implements ModInitializer {
public static final String MOD_ID = "betterend"; public static final String MOD_ID = "betterend";
public static final Logger LOGGER = new Logger(MOD_ID); public static final Logger LOGGER = new Logger(MOD_ID);
@ -81,6 +85,10 @@ public class BetterEnd implements ModInitializer {
} }
}); });
DataExchangeAPI.registerDescriptors(List.of(
RitualUpdate.DESCRIPTOR
));
if (RUNS_TRINKETS) { if (RUNS_TRINKETS) {
Elytra.register(); Elytra.register();
} }

View file

@ -1,16 +1,24 @@
package org.betterx.betterend.blocks; package org.betterx.betterend.blocks;
import de.ambertation.wunderlib.math.Float3;
import org.betterx.bclib.behaviours.interfaces.BehaviourStone; import org.betterx.bclib.behaviours.interfaces.BehaviourStone;
import org.betterx.bclib.interfaces.ClientLevelAccess;
import org.betterx.betterend.blocks.basis.PedestalBlock; import org.betterx.betterend.blocks.basis.PedestalBlock;
import org.betterx.betterend.blocks.entities.EternalPedestalEntity; import org.betterx.betterend.blocks.entities.EternalPedestalEntity;
import org.betterx.betterend.client.render.EternalCrystalRenderer;
import org.betterx.betterend.client.render.PedestalItemRenderer;
import org.betterx.betterend.registry.EndBlocks; import org.betterx.betterend.registry.EndBlocks;
import org.betterx.betterend.registry.EndPortals; import org.betterx.betterend.registry.EndPortals;
import org.betterx.betterend.rituals.EternalRitual; import org.betterx.betterend.rituals.EternalRitual;
import net.minecraft.client.particle.Particle;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@ -26,6 +34,9 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
@ -48,6 +59,7 @@ public class EternalPedestal extends PedestalBlock implements BehaviourStone {
if (pedestal.hasRitual()) { if (pedestal.hasRitual()) {
EternalRitual ritual = pedestal.getRitual(); EternalRitual ritual = pedestal.getRitual();
if (ritual.isActive()) { if (ritual.isActive()) {
if (ritual.getWorld() == null) ritual.setWorld(sourceLevel);
ResourceLocation targetWorld = ritual.getTargetWorldId(); ResourceLocation targetWorld = ritual.getTargetWorldId();
int portalId; int portalId;
if (targetWorld != null) { if (targetWorld != null) {
@ -68,9 +80,11 @@ public class EternalPedestal extends PedestalBlock implements BehaviourStone {
updatedState.setValue(ACTIVATED, true).setValue(HAS_LIGHT, true) updatedState.setValue(ACTIVATED, true).setValue(HAS_LIGHT, true)
); );
if (pedestal.hasRitual()) { if (pedestal.hasRitual()) {
if (pedestal.getRitual().getWorld() == null) pedestal.getRitual().setWorld(sourceLevel);
pedestal.getRitual().checkStructure(player); pedestal.getRitual().checkStructure(player);
} else { } else {
EternalRitual ritual = new EternalRitual(sourceLevel, pos); EternalRitual ritual = new EternalRitual(sourceLevel, pos);
pedestal.linkRitual(ritual);
ritual.checkStructure(player); ritual.checkStructure(player);
} }
} }
@ -146,4 +160,61 @@ public class EternalPedestal extends PedestalBlock implements BehaviourStone {
public boolean hasUniqueEntity() { public boolean hasUniqueEntity() {
return true; return true;
} }
@Environment(EnvType.CLIENT)
private void dispatchParticles(Level level, BlockPos blockPos, RandomSource random) {
if (level instanceof ClientLevelAccess clientLevel) {
if (level.getBlockEntity(blockPos) instanceof EternalPedestalEntity pedestal
&& pedestal.hasRitual()) {
BlockState state = level.getBlockState(blockPos);
//if (state.getOptionalValue(ACTIVATED).orElse(false))
{
EternalRitual ritual = pedestal.getRitual();
if (ritual != null
&& ritual.getCenter() != null
&& (ritual.isActive() || ritual.willActivate())
) {
final var start = Float3.of(blockPos);
final var dir = Float3
.of(ritual.getCenter())
.sub(start)
.normalized()
.mul(ritual.willActivate() ? 0.2 : 0.05);
float[] color = EternalCrystalRenderer.colors(PedestalItemRenderer.getGemAge());
for (int i = 0; i < random.nextInt(
ritual.willActivate() ? 30 : 2,
ritual.willActivate() ? 60 : 10
); i++) {
Float3 rnd = Float3.of(
random.nextFloat() * 0.3 - 0.15,
random.nextFloat() * -0.1,
random.nextFloat() * 0.3 - 0.15
).add(dir);
SimpleParticleType particleOptions = ParticleTypes.EFFECT;
final Particle particle = clientLevel.bcl_addParticle(
particleOptions,
blockPos.getX() + 0.2 + random.nextFloat() * 0.6,
blockPos.getY() + 1 + random.nextFloat() * 0.7,
blockPos.getZ() + 0.2 + random.nextFloat() * 0.6,
0,
0,
0
);
if (particle == null) continue;
particle.setColor(color[0], color[1], color[2]);
particle.setParticleSpeed(rnd.x, rnd.y, rnd.z);
}
}
}
}
}
}
@Override
@Environment(EnvType.CLIENT)
public void animateTick(BlockState blockState, Level level, BlockPos blockPos, RandomSource randomSource) {
super.animateTick(blockState, level, blockPos, randomSource);
dispatchParticles(level, blockPos, randomSource);
}
} }

View file

@ -60,7 +60,7 @@ public class PedestalItemRenderer<T extends PedestalBlockEntity> implements Bloc
} else { } else {
matrices.scale(1.25F, 1.25F, 1.25F); matrices.scale(1.25F, 1.25F, 1.25F);
} }
int age = (int) (minecraft.level.getGameTime() % 314); int age = getGemAge();
if (state.is(EndBlocks.ETERNAL_PEDESTAL) && state.getValue(EternalPedestal.ACTIVATED)) { if (state.is(EndBlocks.ETERNAL_PEDESTAL) && state.getValue(EternalPedestal.ACTIVATED)) {
float[] colors = EternalCrystalRenderer.colors(age); float[] colors = EternalCrystalRenderer.colors(age);
int y = blockEntity.getBlockPos().getY(); int y = blockEntity.getBlockPos().getY();
@ -101,4 +101,8 @@ public class PedestalItemRenderer<T extends PedestalBlockEntity> implements Bloc
} }
matrices.popPose(); matrices.popPose();
} }
public static int getGemAge() {
return (int) (Minecraft.getInstance().level.getGameTime() % 314);
}
} }

View file

@ -0,0 +1,73 @@
package org.betterx.betterend.network;
import org.betterx.bclib.api.v2.dataexchange.BaseDataHandler;
import org.betterx.bclib.api.v2.dataexchange.DataHandler;
import org.betterx.bclib.api.v2.dataexchange.DataHandlerDescriptor;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.rituals.EternalRitual;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.FriendlyByteBuf;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
public class RitualUpdate extends DataHandler.FromServer {
public static final DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(
BetterEnd.makeID("ritual_update"),
RitualUpdate::new,
false,
false
);
public RitualUpdate() {
super(DESCRIPTOR.IDENTIFIER);
}
private static final byte ACTIVE_FLAG = 1;
private static final byte WILL_ACTIVATE_FLAG = 2;
public RitualUpdate(EternalRitual ritual) {
this();
this.center = ritual.getCenter();
this.axis = ritual.getAxis();
if (ritual.isActive()) {
this.flags |= ACTIVE_FLAG;
}
if (ritual.willActivate()) {
this.flags |= WILL_ACTIVATE_FLAG;
}
}
byte flags;
BlockPos center;
Direction.Axis axis;
@Override
protected void serializeDataOnServer(FriendlyByteBuf buf) {
buf.writeBlockPos(center);
BaseDataHandler.writeString(buf, axis.getName());
buf.writeByte(flags);
}
@Override
protected void deserializeIncomingDataOnClient(FriendlyByteBuf buf, PacketSender responseSender) {
center = buf.readBlockPos();
axis = Direction.Axis.byName(BaseDataHandler.readString(buf));
flags = buf.readByte();
}
@Override
protected void runOnClientGameThread(Minecraft client) {
EternalRitual.updateActiveStateOnPedestals(
center,
axis,
(flags & ACTIVE_FLAG) != 0,
(flags & WILL_ACTIVATE_FLAG) != 0,
client.level,
null
);
}
}

View file

@ -1,11 +1,13 @@
package org.betterx.betterend.rituals; package org.betterx.betterend.rituals;
import org.betterx.bclib.api.v2.dataexchange.DataExchangeAPI;
import org.betterx.bclib.blocks.BlockProperties; import org.betterx.bclib.blocks.BlockProperties;
import org.betterx.betterend.BetterEnd; import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.advancements.BECriteria; import org.betterx.betterend.advancements.BECriteria;
import org.betterx.betterend.blocks.EndPortalBlock; import org.betterx.betterend.blocks.EndPortalBlock;
import org.betterx.betterend.blocks.RunedFlavolite; import org.betterx.betterend.blocks.RunedFlavolite;
import org.betterx.betterend.blocks.entities.EternalPedestalEntity; import org.betterx.betterend.blocks.entities.EternalPedestalEntity;
import org.betterx.betterend.network.RitualUpdate;
import org.betterx.betterend.portal.PortalBuilder; import org.betterx.betterend.portal.PortalBuilder;
import org.betterx.betterend.registry.EndBlocks; import org.betterx.betterend.registry.EndBlocks;
import org.betterx.betterend.registry.EndPortals; import org.betterx.betterend.registry.EndPortals;
@ -60,6 +62,7 @@ public class EternalRitual {
private BlockPos center; private BlockPos center;
private BlockPos exit; private BlockPos exit;
private boolean active = false; private boolean active = false;
private boolean willActivate = false;
public EternalRitual(Level world) { public EternalRitual(Level world) {
this.world = world; this.world = world;
@ -70,10 +73,22 @@ public class EternalRitual {
this.configure(initial); this.configure(initial);
} }
public BlockPos getCenter() {
return center;
}
public Direction.Axis getAxis() {
return axis;
}
public void setWorld(Level world) { public void setWorld(Level world) {
this.world = world; this.world = world;
} }
public Level getWorld() {
return world;
}
@Nullable @Nullable
public ResourceLocation getTargetWorldId() { public ResourceLocation getTargetWorldId() {
return targetWorldId; return targetWorldId;
@ -119,6 +134,52 @@ public class EternalRitual {
} }
} }
public void updateActiveStateOnPedestals() {
if (world == null) return;
updateActiveStateOnPedestals(center, axis, active, willActivate, world, this);
DataExchangeAPI.send(new RitualUpdate(this));
}
public static void updateActiveStateOnPedestals(
BlockPos center,
Direction.Axis axis,
boolean active,
boolean willActivate,
Level world,
EternalRitual fallback
) {
Direction moveX, moveY;
if (Direction.Axis.X == axis) {
moveX = Direction.EAST;
moveY = Direction.NORTH;
} else {
moveX = Direction.SOUTH;
moveY = Direction.EAST;
}
for (Point pos : PEDESTAL_POSITIONS) {
BlockPos.MutableBlockPos checkPos = center.mutable();
checkPos.move(moveX, pos.x).move(moveY, pos.y);
if (world.getBlockEntity(checkPos) instanceof EternalPedestalEntity pedestal) {
if (pedestal.hasRitual()) {
if (fallback == null) fallback = pedestal.getRitual();
pedestal.getRitual().active = active;
pedestal.getRitual().willActivate = willActivate;
} else {
if (fallback == null) {
fallback = new EternalRitual(world);
fallback.center = center;
fallback.axis = axis;
fallback.willActivate = willActivate;
fallback.active = active;
}
pedestal.linkRitual(fallback);
}
}
}
}
private boolean checkFrame(Level world, BlockPos framePos) { private boolean checkFrame(Level world, BlockPos framePos) {
Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST; Direction moveDir = Direction.Axis.X == axis ? Direction.NORTH : Direction.EAST;
boolean valid = true; boolean valid = true;
@ -137,8 +198,15 @@ public class EternalRitual {
return active; return active;
} }
public boolean willActivate() {
return willActivate;
}
private void activatePortal(Player player, Item keyItem) { private void activatePortal(Player player, Item keyItem) {
if (active) return; if (active) return;
willActivate = true;
updateActiveStateOnPedestals();
ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(keyItem); ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(keyItem);
int portalId = EndPortals.getPortalIdByItem(itemId); int portalId = EndPortals.getPortalIdByItem(itemId);
Level targetWorld = getTargetWorld(portalId); Level targetWorld = getTargetWorld(portalId);
@ -156,13 +224,19 @@ public class EternalRitual {
activatePortal(targetWorld, exit, portalId); activatePortal(targetWorld, exit, portalId);
} }
activatePortal(world, center, portalId); activatePortal(world, center, portalId);
doEffects((ServerLevel) world, center); if (world instanceof ServerLevel serverLevel) {
doEffects(serverLevel, center);
}
willActivate = false;
active = true; active = true;
updateActiveStateOnPedestals();
} catch (Exception ex) { } catch (Exception ex) {
BetterEnd.LOGGER.error("Create End portals error.", ex); BetterEnd.LOGGER.error("Create End portals error.", ex);
removePortal(targetWorld, exit); removePortal(targetWorld, exit);
removePortal(world, center); removePortal(world, center);
willActivate = false;
active = false; active = false;
updateActiveStateOnPedestals();
} }
} }
@ -238,8 +312,7 @@ public class EternalRitual {
.setValue(EndPortalBlock.AXIS, portalAxis) .setValue(EndPortalBlock.AXIS, portalAxis)
.setValue(EndPortalBlock.PORTAL, portalId); .setValue(EndPortalBlock.PORTAL, portalId);
ParticleOptions effect = new BlockParticleOption(ParticleTypes.BLOCK, portal); ParticleOptions effect = new BlockParticleOption(ParticleTypes.BLOCK, portal);
ServerLevel serverWorld = (ServerLevel) world; if (world instanceof ServerLevel serverWorld) {
PortalBuilder.PORTAL_POSITIONS.forEach(point -> { PortalBuilder.PORTAL_POSITIONS.forEach(point -> {
BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y); BlockPos pos = center.mutable().move(moveDir, point.x).move(Direction.UP, point.y);
if (!world.getBlockState(pos).is(PortalBuilder.PORTAL)) { if (!world.getBlockState(pos).is(PortalBuilder.PORTAL)) {
@ -273,11 +346,14 @@ public class EternalRitual {
0.5, 0.5, 0.5, 0.3 0.5, 0.5, 0.5, 0.3
); );
} }
}); });
} }
}
public void disablePortal(int state) { public void disablePortal(int state) {
if (!active || isInvalid()) return; if (!active || isInvalid()) return;
if (!world.isClientSide())
removePortal(getTargetWorld(state), exit); removePortal(getTargetWorld(state), exit);
removePortal(world, center); removePortal(world, center);
} }
@ -308,6 +384,7 @@ public class EternalRitual {
} }
}); });
this.active = false; this.active = false;
updateActiveStateOnPedestals();
} }
private Level getTargetWorld(int state) { private Level getTargetWorld(int state) {