Compare commits

..
2 Commits
Author SHA1 Message Date
xoiox 19dcbb3412 ritual pattern detection working
Verifying Everything Compiles / build (push) Has been cancelled
2026-09-16 19:09:31 +02:00
xoiox 8521d309e0 Early working ritual using a snow block & crash fixes 2026-09-14 00:30:07 +02:00
10 changed files with 261 additions and 10 deletions

No files matched your search

@@ -5,7 +5,9 @@ import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.util.Config;
import dev.hytalemodding.config.RitualConfig;
import dev.hytalemodding.events.RitualBreakBlockEvent;
import dev.hytalemodding.events.RitualDropItemEvent;
import dev.hytalemodding.events.RitualPlaceBlockEvent;
import dev.hytalemodding.commands.ExampleCommand;
import dev.hytalemodding.config.ExampleConfig;
@@ -17,11 +19,11 @@ public class RitualPlugin extends JavaPlugin {
public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private static Config<ExampleConfig> config = null;
private static Config<RitualConfig> config = null;
public RitualPlugin(@Nonnull JavaPluginInit init) {
super(init);
config = this.withConfig("example_config", ExampleConfig.CODEC);
config = this.withConfig("ritual_config", RitualConfig.CODEC);
}
@Override
@@ -32,12 +34,13 @@ public class RitualPlugin extends JavaPlugin {
RitualSpawner ritualSpawner = new RitualSpawner();
this.getEntityStoreRegistry().registerSystem(new RitualPlaceBlockEvent(ritualSpawner));
this.getEntityStoreRegistry().registerSystem(new RitualBreakBlockEvent(ritualSpawner));
this.getEntityStoreRegistry().registerSystem(new RitualDropItemEvent(ritualSpawner));
// Example
this.getCommandRegistry().registerCommand(new ExampleCommand("example", "An example command"));
/*this.getCommandRegistry().registerCommand(new ExampleCommand("example", "An example command"));
if (getConfig().get().isEnabledWelcomeMessage()) {
this.getEventRegistry().registerGlobal(PlayerReadyEvent.class, ExampleEvent::onPlayerReady);
}
}*/
}
@Override
@@ -48,7 +51,7 @@ public class RitualPlugin extends JavaPlugin {
protected void shutdown() {
}
public static Config<ExampleConfig> getConfig() {
public static Config<RitualConfig> getConfig() {
return config;
}
}
@@ -1,13 +1,89 @@
package dev.hytalemodding;
import com.hypixel.hytale.protocol.SoundCategory;
import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent;
import com.hypixel.hytale.server.core.universe.world.SoundUtil;
import com.hypixel.hytale.server.core.universe.world.World;
import dev.hytalemodding.definitions.RitualDefinitions;
import dev.hytalemodding.definitions.RitualPattern;
import dev.hytalemodding.definitions.RitualTypes;
import org.joml.Vector3d;
import org.joml.Vector3i;
import java.util.List;
import java.util.Objects;
public class RitualSpawner {
RitualDefinitions ritualDefinitions = new RitualDefinitions();
public void blockChangeEvent(String itemId, Vector3i targetPosition, World world) {
String blockId = world.getBlockType(targetPosition).getId();
String blockId = Objects.requireNonNull(world.getBlockType(targetPosition)).getId();
RitualPlugin.LOGGER.atInfo().log(
"a block got placed or removed at the position: %d - %d - %d with the block id: %s and with the item id in hand: %s",
targetPosition.x, targetPosition.y, targetPosition.z, blockId, itemId);
/* if (itemId.compareTo("Soil_Snow") == 0 && blockId.compareTo("Empty") == 0) {
RitualPlugin.LOGGER.atInfo().log("Ritual complete");
world.execute(() -> {
NPCPlugin.get().spawnNPC(world.getEntityStore().getStore(), "Yeti", null, blockPosToEntityPos(targetPosition), new Rotation3f(0, 0, 0));
});
} */
RitualTypes ritualType = checkSetups(ritualDefinitions.containsCenterBlock(itemId), targetPosition, world);
if(!ritualType.equals(RitualTypes.NONE)) {
RitualPlugin.LOGGER.atInfo().log(
"a ritual pattern got completed"
);
int soundIndex = SoundEvent.getAssetMap().getIndex("SFX_Skeleton_Praetorian_Alerted");
world.execute(() -> {
// Play sound
SoundUtil.playSoundEvent3d(soundIndex, SoundCategory.SFX, blockToEntityPos(targetPosition), world.getEntityStore().getStore());
});
}
}
public void sacrificeDropEvent(String itemId, Vector3d targetPosition, World world) {
Vector3i blockPosition = entityToBlockPos(targetPosition);
RitualPlugin.LOGGER.atInfo().log(
"an item got thrown at the position: %f - %f - %f with the item id: %s",
blockPosition.x, blockPosition.y, blockPosition.z, itemId);
}
RitualTypes checkSetups(List<Integer> ritualSetups, Vector3i centerPosition, World world) {
for (Integer ritualSetup : ritualSetups) {
List<RitualPattern> ritualPatterns = ritualDefinitions.getPatterns(ritualSetup);
boolean isSetupValid = true;
for (RitualPattern ritualPattern : ritualPatterns) {
if(!checkPattern(ritualPattern, centerPosition, world)) {
isSetupValid = false;
}
}
if (isSetupValid) {
return ritualDefinitions.getRitualType(ritualSetup);
}
}
return RitualTypes.NONE;
}
boolean checkPattern(RitualPattern ritualPattern, Vector3i centerPosition, World world) {
int countMatches = 0;
int radius = ritualPattern.radius();
for (int ix = centerPosition.x - radius; ix <= centerPosition.x + radius; ix++) {
for (int iy = centerPosition.y - radius; iy <= centerPosition.y + radius; iy++) {
for (int iz = centerPosition.z - radius; iz <= centerPosition.z + radius; iz++) {
Vector3i samplePosition = new Vector3i(ix, iy, iz);
String blockId = Objects.requireNonNull(world.getBlockType(samplePosition)).getId();
if (blockId.equals(ritualPattern.blockId())) {
countMatches++;
}
}
}
}
return countMatches >= ritualPattern.amount();
}
Vector3d blockToEntityPos(Vector3i blockPosition) {
return new Vector3d(blockPosition).add(new Vector3d(0.5, 1, 0.5));
}
Vector3i entityToBlockPos(Vector3d entityPosition) {
return new Vector3i(entityPosition, 0);
}
}
@@ -0,0 +1,23 @@
package dev.hytalemodding.config;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
public class RitualConfig {
public static final BuilderCodec<RitualConfig> CODEC = BuilderCodec.builder(RitualConfig.class, RitualConfig::new)
.append(
new KeyedCodec<>("EnableLogging", Codec.BOOLEAN),
(ritConfig, aBoolean, extraInfo) -> ritConfig.enableLogging = aBoolean,
(ritConfig, extraInfo) -> ritConfig.enableLogging)
.add()
.build();
private boolean enableLogging;
private RitualConfig() {}
public boolean isLoggingEnabled() {
return enableLogging;
}
}
@@ -0,0 +1,41 @@
package dev.hytalemodding.definitions;
import java.util.ArrayList;
import java.util.List;
public class RitualDefinitions {
List<RitualSetup> ritualSetups = new ArrayList<>();
public RitualDefinitions() {
// Crude skeleton ritual
ritualSetups.add(new RitualSetup("Deco_Bone_Skulls", RitualTypes.SKELETON_ARMY)
.addPatern("Furniture_Crude_Torch", 2, 4, true)
.addSacrifice("Ingredient_Life_Essence"));
}
public List<Integer> containsCenterBlock(String blockId) {
List<Integer> possibleSetups = new ArrayList<>();
for(int i = 0; i < ritualSetups.size(); i++) {
if(ritualSetups.get(i).getCenterBlockId().equals(blockId)) {
possibleSetups.add(i);
}
}
return possibleSetups;
}
public RitualTypes getRitualType(int index) {
return ritualSetups.get(index).getRitualType();
}
public String getCenterBlockId(int index) {
return ritualSetups.get(index).getCenterBlockId();
}
public List<RitualPattern> getPatterns(int setupIndex) {
return ritualSetups.get(setupIndex).getPatterns();
}
public String getSacrificeItemId(int setupIndex, int sacrificeIndex) {
return ritualSetups.get(setupIndex).getSacrificeItemId(sacrificeIndex);
}
}
@@ -0,0 +1,3 @@
package dev.hytalemodding.definitions;
public record RitualPattern(String blockId, int radius, int amount, boolean shouldConsume) { }
@@ -0,0 +1,46 @@
package dev.hytalemodding.definitions;
import java.util.ArrayList;
import java.util.List;
public class RitualSetup {
RitualTypes ritualType;
String centerBlockId;
List<RitualPattern> patterns = new ArrayList<>();
List<String> sacrificeItemIds = new ArrayList<>();
public RitualSetup(String centerBlockId, RitualTypes ritualType) {
this.ritualType = ritualType;
this.centerBlockId = centerBlockId;
}
public RitualSetup addPatern(String blockId, int radius, int amount, boolean shouldConsume) {
patterns.add(new RitualPattern(blockId, radius, amount, shouldConsume));
return this;
}
public RitualSetup addSacrifice(String sacrifice) {
sacrificeItemIds.add(sacrifice);
return this;
}
public RitualTypes getRitualType() {
return ritualType;
}
public String getCenterBlockId() {
return centerBlockId;
}
public RitualPattern getPattern(int index) {
return patterns.get(index);
}
public List<RitualPattern> getPatterns() {
return patterns;
}
public String getSacrificeItemId(int index) {
return sacrificeItemIds.get(index);
}
}
@@ -0,0 +1,7 @@
package dev.hytalemodding.definitions;
public enum RitualTypes {
NONE,
SKELETON_ARMY,
YEDI
}
@@ -7,6 +7,7 @@ import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import dev.hytalemodding.RitualSpawner;
@@ -27,10 +28,14 @@ public class RitualBreakBlockEvent extends EntityEventSystem<EntityStore, BreakB
@NonNullDecl Store<EntityStore> store,
@NonNullDecl CommandBuffer<EntityStore> commandBuffer,
@NonNullDecl BreakBlockEvent breakBlockEvent) {
String itemHolding = breakBlockEvent.getItemInHand().getItemId();
ItemStack itemHolding = breakBlockEvent.getItemInHand();
String itemId = "Empty";
if(itemHolding != null) {
itemId = itemHolding.getItemId();
}
Vector3i blockPosition = breakBlockEvent.getTargetBlock();
World world = store.getExternalData().getWorld();
ritualSpawner.blockChangeEvent(itemHolding, blockPosition, world);
ritualSpawner.blockChangeEvent(itemId, blockPosition, world);
}
@NullableDecl
@@ -0,0 +1,42 @@
package dev.hytalemodding.events;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import dev.hytalemodding.RitualSpawner;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.joml.Vector3d;
public class RitualDropItemEvent extends EntityEventSystem<EntityStore, DropItemEvent.Drop> {
private final RitualSpawner ritualSpawner;
public RitualDropItemEvent(RitualSpawner ritualSpawner) {
super(DropItemEvent.Drop.class);
this.ritualSpawner = ritualSpawner;
}
@Override
public void handle(int index,
@NonNullDecl ArchetypeChunk<EntityStore> archetypeChunk,
@NonNullDecl Store<EntityStore> store,
@NonNullDecl CommandBuffer<EntityStore> commandBuffer,
@NonNullDecl DropItemEvent.Drop dropItemEvent) {
String itemId = dropItemEvent.getItemStack().getItemId();
Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
assert transform != null;
Vector3d targetPosition = transform.getPosition();
World world = store.getExternalData().getWorld();
ritualSpawner.sacrificeDropEvent(itemId, targetPosition, world);
}
@NullableDecl
@Override
public Query<EntityStore> getQuery() {
return Archetype.empty();
}
}
@@ -6,6 +6,7 @@ import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.universe.world.World;
import dev.hytalemodding.RitualSpawner;
import com.hypixel.hytale.component.system.EntityEventSystem;
@@ -27,10 +28,14 @@ public class RitualPlaceBlockEvent extends EntityEventSystem<EntityStore, PlaceB
@NonNullDecl Store<EntityStore> store,
@NonNullDecl CommandBuffer<EntityStore> commandBuffer,
@NonNullDecl PlaceBlockEvent placeBlockEvent) {
String blockId = placeBlockEvent.getItemInHand().getItemId();
ItemStack itemHolding = placeBlockEvent.getItemInHand();
String itemId = "Empty";
if(itemHolding != null) {
itemId = itemHolding.getItemId();
}
Vector3i targetPosition = placeBlockEvent.getTargetBlock();
World world = store.getExternalData().getWorld();
ritualSpawner.blockChangeEvent(blockId, targetPosition, world);
ritualSpawner.blockChangeEvent(itemId, targetPosition, world);
}
@NullableDecl