diff --git a/src/client/java/com/example/ExampleModClient.java b/src/client/java/com/example/ExampleModClient.java index fa96838..7da2ee2 100644 --- a/src/client/java/com/example/ExampleModClient.java +++ b/src/client/java/com/example/ExampleModClient.java @@ -7,7 +7,8 @@ import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.minecraft.client.render.entity.FlyingItemEntityRenderer; - +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; +import net.minecraft.client.render.entity.EmptyEntityRenderer; public class ExampleModClient implements ClientModInitializer { @Override @@ -19,6 +20,12 @@ public void onInitializeClient() { EntityRendererRegistry.register(ModEntities.GOD_BOSS, GodBossEntityRenderer::new); + EntityRendererRegistry.register(ModEntities.GOD_BOSS, GodBossEntityRenderer::new); + + + EntityRendererRegistry.register(ModEntities.DEVASTATION_DOME_ENTITY, EmptyEntityRenderer::new); + + System.out.println("GodMod client initialized with entity renderers and custom boss model"); diff --git a/src/main/java/com/example/DevastationDomeEntity.java b/src/main/java/com/example/DevastationDomeEntity.java new file mode 100644 index 0000000..406d90c --- /dev/null +++ b/src/main/java/com/example/DevastationDomeEntity.java @@ -0,0 +1,200 @@ +package com.example; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.damage.DamageTypes; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; + +import java.util.List; + +public class DevastationDomeEntity extends Entity { + private static final int DOME_RADIUS = 15; + private static final int DOME_DURATION_TICKS = 200; + private static final float DAMAGE_PER_TICK = 100000.0F; + private static final int DAMAGE_INTERVAL = 1; + + private int lifeTicks = 0; + private BlockPos domeCenter; + + public DevastationDomeEntity(EntityType type, World world) { + super(type, world); + this.noClip = true; + this.setInvulnerable(true); + } + + public DevastationDomeEntity(EntityType type, World world, BlockPos center) { + this(type, world); + this.domeCenter = center; + this.setPosition(center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5); + } + + @Override + protected void initDataTracker(net.minecraft.entity.data.DataTracker.Builder builder) { + // No data to track + } + + @Override + public void tick() { + super.tick(); + + if (!this.getWorld().isClient) { + lifeTicks++; + + // Apply damage every tick + if (lifeTicks % DAMAGE_INTERVAL == 0) { + applyDomeDamage(); + } + + // Spawn particles every 5 ticks + if (lifeTicks % 5 == 0) { + spawnDomeParticles(); + } + + // Remove after duration + if (lifeTicks >= DOME_DURATION_TICKS) { + removeDome(); + this.discard(); + } + } + } + + private void applyDomeDamage() { + if (!(this.getWorld() instanceof ServerWorld serverWorld)) { + return; + } + + if (domeCenter == null) { + domeCenter = this.getBlockPos(); + } + + // Create damage box + Box damageBox = new Box(domeCenter).expand(DOME_RADIUS); + + // Find all living entities except players + List entities = this.getWorld().getEntitiesByClass( + LivingEntity.class, + damageBox, + entity -> { + double distance = entity.getPos().distanceTo(domeCenter.toCenterPos()); + return distance <= DOME_RADIUS && !(entity instanceof PlayerEntity); + } + ); + + // Create damage source using the server world's damage sources + DamageSource lethalDamage = serverWorld.getDamageSources().outOfWorld(); + + // Apply damage to all entities + for (LivingEntity entity : entities) { + entity.damage(serverWorld, lethalDamage, DAMAGE_PER_TICK); + } + } + + private void spawnDomeParticles() { + if (!(this.getWorld() instanceof ServerWorld serverWorld)) { + return; + } + + if (domeCenter == null) { + domeCenter = this.getBlockPos(); + } + + double spin = (lifeTicks % 360) * Math.PI / 180.0; + + // Create dome shell particles + for (int yStep = 2; yStep <= DOME_RADIUS; yStep += 3) { + double height = yStep; + double radius = Math.sqrt(DOME_RADIUS * DOME_RADIUS - height * height); + + for (int i = 0; i < 360; i += 24) { + double angle = Math.toRadians(i) + spin * 0.5; + double x = domeCenter.getX() + 0.5 + Math.cos(angle) * radius; + double z = domeCenter.getZ() + 0.5 + Math.sin(angle) * radius; + double y = domeCenter.getY() + height; + + serverWorld.spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + x, y, z, 1, 0.0, 0.0, 0.0, 0.01 + ); + serverWorld.spawnParticles( + ParticleTypes.DRAGON_BREATH, + x, y + 0.2, z, 1, 0.0, 0.0, 0.0, 0.0 + ); + } + } + + // Create inner spiral + double progress = (double) lifeTicks / DOME_DURATION_TICKS; + int spiralPoints = (int) (30.0 + 50.0 * Math.sin(Math.min(progress, 1.0) * Math.PI)); + double spiralRadius = 6.75; + + for (int i = 0; i < spiralPoints; i++) { + double angle = spin + i * 0.35; + double x = domeCenter.getX() + 0.5 + Math.cos(angle) * spiralRadius * (0.9 + 0.1 * Math.sin(spin)); + double z = domeCenter.getZ() + 0.5 + Math.sin(angle) * spiralRadius * (0.9 + 0.1 * Math.cos(spin)); + double y = domeCenter.getY() + 4.5 + Math.sin(angle * 0.9) * 2.0; + + serverWorld.spawnParticles( + ParticleTypes.ENCHANT, + x, y, z, 1, 0.0, 0.02, 0.0, 0.0 + ); + serverWorld.spawnParticles( + ParticleTypes.WARPED_SPORE, + x, y, z, 1, 0.0, 0.0, 0.0, 0.01 + ); + } + } + + private void removeDome() { + if (!(this.getWorld() instanceof ServerWorld serverWorld)) { + return; + } + + if (domeCenter == null) { + domeCenter = this.getBlockPos(); + } + + // Spawn explosion particle + serverWorld.spawnParticles( + ParticleTypes.EXPLOSION_EMITTER, + domeCenter.getX(), domeCenter.getY(), domeCenter.getZ(), + 1, 0.0, 0.0, 0.0, 0.0 + ); + } + + @Override + protected void readCustomDataFromNbt(NbtCompound nbt) { + lifeTicks = nbt.getInt("LifeTicks"); + + if (nbt.contains("DomeCenterX")) { + domeCenter = new BlockPos( + nbt.getInt("DomeCenterX"), + nbt.getInt("DomeCenterY"), + nbt.getInt("DomeCenterZ") + ); + } + } + + @Override + protected void writeCustomDataToNbt(NbtCompound nbt) { + nbt.putInt("LifeTicks", lifeTicks); + + if (domeCenter != null) { + nbt.putInt("DomeCenterX", domeCenter.getX()); + nbt.putInt("DomeCenterY", domeCenter.getY()); + nbt.putInt("DomeCenterZ", domeCenter.getZ()); + } + } + + @Override + public boolean damage(ServerWorld world, DamageSource source, float amount) { + return false; // Invulnerable + } +} \ No newline at end of file diff --git a/src/main/java/com/example/DevastationDomeScrollItem.java b/src/main/java/com/example/DevastationDomeScrollItem.java new file mode 100644 index 0000000..172f5c0 --- /dev/null +++ b/src/main/java/com/example/DevastationDomeScrollItem.java @@ -0,0 +1,166 @@ +package com.example; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.tooltip.TooltipType; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Hand; +import net.minecraft.util.ActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +import java.util.List; + +public class DevastationDomeScrollItem extends Item { + private static final int DOME_RADIUS = 15; + public static final SoundEvent DEVASTATION_SOUND = registerSound("destructive_dome_burst"); + + public DevastationDomeScrollItem(Settings settings) { + super(settings); + } + + private static SoundEvent registerSound(String name) { + Identifier id = Identifier.of("godmod", name); + return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id)); + } + + @Override + public ActionResult use(World world, PlayerEntity user, Hand hand) { + ItemStack stack = user.getStackInHand(hand); + + if (!world.isClient) { + ServerWorld serverWorld = (ServerWorld) world; + BlockPos centerPos = user.getBlockPos(); + + // Play sound (will be silent for now without sound file) + world.playSound(null, centerPos, DEVASTATION_SOUND, SoundCategory.PLAYERS, 2.0F, 1.0F); + + // Spawn particle burst + spawnParticleBurst(serverWorld, centerPos); + + // Spawn dome entity + DevastationDomeEntity domeEntity = new DevastationDomeEntity( + ModEntities.DEVASTATION_DOME_ENTITY, + serverWorld, + centerPos + ); + serverWorld.spawnEntity(domeEntity); + + // Send message to player + user.sendMessage( + Text.literal("Devastation Dome Activated!") + .formatted(Formatting.RED, Formatting.BOLD), + true + ); + + // Consume item if not in creative + if (!user.getAbilities().creativeMode) { + stack.decrement(1); + } + } + + return ActionResult.SUCCESS; + } + + private void spawnParticleBurst(ServerWorld world, BlockPos center) { + // Main explosion burst + for (int i = 0; i < 600; i++) { + double angle = Math.random() * Math.PI * 2.0; + double pitch = Math.random() * Math.PI; + double speed = 0.6 + Math.random(); + + double velX = Math.sin(pitch) * Math.cos(angle) * speed; + double velY = Math.cos(pitch) * speed; + double velZ = Math.sin(pitch) * Math.sin(angle) * speed; + + world.spawnParticles( + ParticleTypes.FLAME, + center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5, + 1, velX, velY, velZ, 0.08 + ); + world.spawnParticles( + ParticleTypes.SMOKE, + center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5, + 1, velX, velY, velZ, 0.08 + ); + world.spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5, + 1, velX * 0.6, velY * 0.6, velZ * 0.6, 0.02 + ); + world.spawnParticles( + ParticleTypes.WARPED_SPORE, + center.getX() + 0.5, center.getY() + 0.5, center.getZ() + 0.5, + 1, velX * 0.4, velY * 0.4, velZ * 0.4, 0.01 + ); + } + + // Ring particles + double innerRadius = 8.25; + double outerRadius = 12.75; + + for (int i = 0; i < 360; i += 8) { + double radians = Math.toRadians(i); + double innerX = center.getX() + 0.5 + Math.cos(radians) * innerRadius; + double innerZ = center.getZ() + 0.5 + Math.sin(radians) * innerRadius; + double outerX = center.getX() + 0.5 + Math.cos(radians) * outerRadius; + double outerZ = center.getZ() + 0.5 + Math.sin(radians) * outerRadius; + + world.spawnParticles( + ParticleTypes.ENCHANT, + innerX, center.getY() + 0.5, innerZ, + 2, 0.0, 0.02, 0.0, 0.0 + ); + world.spawnParticles( + ParticleTypes.DRAGON_BREATH, + outerX, center.getY() + 1.0, outerZ, + 1, 0.0, 0.02, 0.0, 0.0 + ); + } + + // Vertical pillars + for (int i = 0; i < 15; i += 2) { + world.spawnParticles( + ParticleTypes.END_ROD, + center.getX() + 0.5 + innerRadius, center.getY() + i, center.getZ() + 0.5, + 8, 0.02, 0.02, 0.02, 0.01 + ); + world.spawnParticles( + ParticleTypes.END_ROD, + center.getX() + 0.5 - innerRadius, center.getY() + i, center.getZ() + 0.5, + 8, 0.02, 0.02, 0.02, 0.01 + ); + world.spawnParticles( + ParticleTypes.END_ROD, + center.getX() + 0.5, center.getY() + i, center.getZ() + 0.5 + innerRadius, + 8, 0.02, 0.02, 0.02, 0.01 + ); + world.spawnParticles( + ParticleTypes.END_ROD, + center.getX() + 0.5, center.getY() + i, center.getZ() + 0.5 - innerRadius, + 8, 0.02, 0.02, 0.02, 0.01 + ); + } + } + + @Override + public void appendTooltip(ItemStack stack, TooltipContext context, List tooltip, TooltipType type) { + tooltip.add(Text.translatable("item.godmod.devastation_dome_scroll.tooltip").formatted(Formatting.GRAY)); + tooltip.add(Text.translatable("item.godmod.devastation_dome_scroll.tooltip2").formatted(Formatting.DARK_RED)); + tooltip.add(Text.translatable("item.godmod.devastation_dome_scroll.tooltip3").formatted(Formatting.DARK_GRAY)); + } + + @Override + public boolean hasGlint(ItemStack stack) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/GodMod.java b/src/main/java/com/example/GodMod.java index 6b271f4..6859f97 100644 --- a/src/main/java/com/example/GodMod.java +++ b/src/main/java/com/example/GodMod.java @@ -21,6 +21,10 @@ public void onInitialize() { ModScrolls.initialize(); // This was missing! LOGGER.info("ModScrolls.initialize() completed!"); ModEntities.initialize(); + LOGGER.info("ModEntities.initialize() completed!"); + LOGGER.info("DEVASTATION_DOME_SCROLL is: " + (ModItems.Devastation_DOME_SCROLL != null ? "NOT NULL" : "NULL")); + + //ModBlocks.initialize(); diff --git a/src/main/java/com/example/ModEntities.java b/src/main/java/com/example/ModEntities.java index 5e048c1..9323c42 100644 --- a/src/main/java/com/example/ModEntities.java +++ b/src/main/java/com/example/ModEntities.java @@ -14,6 +14,7 @@ public class ModEntities { public static final EntityType THROWABLE_GOD_AXE; public static final EntityType GOD_BOSS; + public static final EntityType DEVASTATION_DOME_ENTITY; static { // Register Throwable God Axe Entity @@ -43,6 +44,20 @@ public class ModEntities { .trackedUpdateRate(1) // Boss entities should update frequently .build(bossKey) ); + + // Register Devastation Dome Entity + Identifier domeId = Identifier.of(GodMod.MOD_ID, "devastation_dome"); + RegistryKey> domeKey = RegistryKey.of(Registries.ENTITY_TYPE.getKey(), domeId); + + DEVASTATION_DOME_ENTITY = Registry.register( + Registries.ENTITY_TYPE, + domeId, + FabricEntityTypeBuilder.create(SpawnGroup.MISC, DevastationDomeEntity::new) + .dimensions(EntityDimensions.fixed(0.5f, 0.5f)) + .trackRangeBlocks(64) + .trackedUpdateRate(20) + .build(domeKey) + ); } public static void initialize() { @@ -52,6 +67,7 @@ public static void initialize() { FabricDefaultAttributeRegistry.register(GOD_BOSS, GodBossEntity.createAttributes()); // Note: ThrowableGodAxeEntity doesn't need attributes as it's a projectile + // Note: DevastationDomeEntity doesn't need attributes as it's not a living entity System.out.println("Entity registration complete!"); } diff --git a/src/main/java/com/example/ModItems.java b/src/main/java/com/example/ModItems.java index 3b324bb..b12b481 100644 --- a/src/main/java/com/example/ModItems.java +++ b/src/main/java/com/example/ModItems.java @@ -34,6 +34,7 @@ public class ModItems { public static Item MAGIC_CRYSTAL; public static Item GOD_AXE; public static Item SUMMONING_CRYSTAL; + public static Item Devastation_DOME_SCROLL; public static void initialize() { System.out.println("Registering items for " + GodMod.MOD_ID); @@ -42,6 +43,7 @@ public static void initialize() { GOD_SWORD = registerItem("god_sword", key -> new GodSwordItem(GOD_MATERIAL, new Item.Settings().registryKey(key).maxCount(1))); MAGIC_CRYSTAL = registerItem("magic_crystal", key -> new Item(new Item.Settings().registryKey(key))); GOD_AXE = registerItem("god_axe", key ->new GodAxe(GOD_MATERIAL, new Item.Settings().registryKey(key).maxCount(1))); + Devastation_DOME_SCROLL = registerItem("devastation_dome_scroll", key -> new DevastationDomeScrollItem(new Item.Settings().registryKey(key).maxCount(16))); SUMMONING_CRYSTAL = registerItem("summoning_crystal", key -> new SummoningCrystalItem(new Item.Settings().registryKey(key).maxCount(1))); @@ -56,6 +58,8 @@ public static void initialize() { ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS) .register(entries -> entries.add(new ItemStack(SUMMONING_CRYSTAL))); + ItemGroupEvents.modifyEntriesEvent(ItemGroups.COMBAT) + .register(entries -> entries.add(new ItemStack(Devastation_DOME_SCROLL))); } public static Item registerItem(String name, Function, Item> function) { diff --git a/src/main/resources/assets/godmod/Models/item/devastation_dome_scroll.json b/src/main/resources/assets/godmod/Models/item/devastation_dome_scroll.json new file mode 100644 index 0000000..8e8cfda --- /dev/null +++ b/src/main/resources/assets/godmod/Models/item/devastation_dome_scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "godmod:item/devastation_dome_scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/godmod/items/devastation_dome_scroll.json b/src/main/resources/assets/godmod/items/devastation_dome_scroll.json new file mode 100644 index 0000000..4beee3b --- /dev/null +++ b/src/main/resources/assets/godmod/items/devastation_dome_scroll.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "godmod:item/devastation_dome_scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/godmod/lang/en_us.json b/src/main/resources/assets/godmod/lang/en_us.json index ea370e6..716ec06 100644 --- a/src/main/resources/assets/godmod/lang/en_us.json +++ b/src/main/resources/assets/godmod/lang/en_us.json @@ -4,5 +4,6 @@ "item.godmod.fireball_scroll": "Fireball Scroll", "item.godmod.god_axe": "God Axe", "item.godmod.god_boss_spawn_egg": "God Boss Spawner", - "item.godmod.summoning_crystal": "Summonning Eye" + "item.godmod.summoning_crystal": "Summonning Eye", + "item.godmod.devastation_dome": "Devastation Dome" } \ No newline at end of file diff --git a/src/main/resources/assets/godmod/textures/item/devastation_dome_scroll.png b/src/main/resources/assets/godmod/textures/item/devastation_dome_scroll.png new file mode 100644 index 0000000..1831f0e Binary files /dev/null and b/src/main/resources/assets/godmod/textures/item/devastation_dome_scroll.png differ diff --git a/src/main/resources/data/godmod/recipe/devastation_dome_scroll.json b/src/main/resources/data/godmod/recipe/devastation_dome_scroll.json new file mode 100644 index 0000000..25ba4ff --- /dev/null +++ b/src/main/resources/data/godmod/recipe/devastation_dome_scroll.json @@ -0,0 +1,17 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "equipment", + "pattern": [ + " C ", + "C P C", + " C " + ], + "key": { + "C": "godmod:magic_crystal", + "P": "minecraft:paper" + }, + "result": { + "id": "godmod:devastaion_dome_scroll", + "count": 1 + } +} \ No newline at end of file