diff --git a/build.gradle b/build.gradle index 5a4790ad..ee2094e8 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,8 @@ minecraft { // Default run configurations. // These can be tweaked, removed, or duplicated as needed. + + def argsz = ['--username', 'WinDanesz', '--user', 'WinDanesz', '--uuid', '7faee354-8c60-4f5c-9862-fc0ce5f7f575'] runs { client { workingDirectory project.file('run') @@ -68,6 +70,7 @@ minecraft { // Recommended logging level for the console property 'forge.logging.console.level', 'info' + args argsz } server { diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index 0b715922..ccf01dec 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -138,6 +138,12 @@ public final class Settings { new ResourceLocation(Wizardry.MODID, "shrine_5"), new ResourceLocation(Wizardry.MODID, "shrine_6"), new ResourceLocation(Wizardry.MODID, "shrine_7")}; + /** [Server-only] Whether conquered shrines should regenerate after a period of time. */ + public boolean shrineRegenerationEnabled = true; + /** [Server-only] Time in minutes for a conquered shrine to regenerate. */ + public int shrineRegenerationTime = 1; + /** [Server-only] Whether players can loot shrines multiple times. If false, each player can only loot each shrine once. */ + public boolean shrineAllowMultipleLoot = false; /** [Server-only] List of dimension ids in which to generate library ruins. */ public int[] libraryDimensions = {0}; /** [Server-only] The rarity of library ruins, used by the world generator. Larger numbers are rarer. */ @@ -1124,6 +1130,22 @@ public final class Settings { shrineFiles = getResourceLocationList(property); propOrder.add(property.getName()); + property = config.get(WORLDGEN_CATEGORY, "shrineRegenerationEnabled", true, "Whether conquered shrines should regenerate after a period of time. When disabled, shrines remain conquered permanently."); + property.setLanguageKey("config." + Wizardry.MODID + ".shrine_regeneration_enabled"); + shrineRegenerationEnabled = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(WORLDGEN_CATEGORY, "shrineRegenerationTime", 1, "Time in minutes for a conquered shrine to regenerate. Minimum 1 minute, maximum 1440 minutes (24 hours).", 1, 1440); + property.setLanguageKey("config." + Wizardry.MODID + ".shrine_regeneration_time"); + Wizardry.proxy.setToNumberSliderEntry(property); + shrineRegenerationTime = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(WORLDGEN_CATEGORY, "shrineAllowMultipleLoot", false, "Whether players can loot shrines multiple times. If false, each player can only loot each shrine once until it regenerates."); + property.setLanguageKey("config." + Wizardry.MODID + ".shrine_allow_multiple_loot"); + shrineAllowMultipleLoot = property.getBoolean(); + propOrder.add(property.getName()); + property = config.get(WORLDGEN_CATEGORY, "libraryDimensions", new int[]{0}, "List of dimension ids in which library ruins will generate. Remove all dimensions to disable library ruins completely."); property.setLanguageKey("config." + Wizardry.MODID + ".library_dimensions"); property.setRequiresWorldRestart(true); diff --git a/src/main/java/electroblob/wizardry/block/BlockPedestal.java b/src/main/java/electroblob/wizardry/block/BlockPedestal.java index 53bb1bb4..a6177b2d 100644 --- a/src/main/java/electroblob/wizardry/block/BlockPedestal.java +++ b/src/main/java/electroblob/wizardry/block/BlockPedestal.java @@ -1,5 +1,6 @@ package electroblob.wizardry.block; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Element; import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.tileentity.TileEntityShrineCore; @@ -81,11 +82,25 @@ public class BlockPedestal extends Block implements ITileEntityProvider { @Override public float getBlockHardness(IBlockState state, World world, BlockPos pos){ + // If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation + if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){ + TileEntity tileEntity = world.getTileEntity(pos); + if(tileEntity instanceof TileEntityShrineCore){ + return -1; // Unbreakable if it has a shrine core + } + } return state.getValue(NATURAL) ? -1 : super.getBlockHardness(state, world, pos); } @Override public float getExplosionResistance(World world, BlockPos pos, @Nullable Entity exploder, Explosion explosion){ + // If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation + if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){ + TileEntity tileEntity = world.getTileEntity(pos); + if(tileEntity instanceof TileEntityShrineCore){ + return 6000000.0F; // Unbreakable if it has a shrine core + } + } return world.getBlockState(pos).getValue(NATURAL) ? 6000000.0F : super.getExplosionResistance(world, pos, exploder, explosion); } diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java index 7c532079..60816844 100644 --- a/src/main/java/electroblob/wizardry/spell/ArcaneLock.java +++ b/src/main/java/electroblob/wizardry/spell/ArcaneLock.java @@ -13,14 +13,18 @@ import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.event.entity.living.LivingDestroyBlockEvent; + import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.ExplosionEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + + @Mod.EventBusSubscriber public class ArcaneLock extends SpellRay { @@ -167,4 +171,8 @@ public class ArcaneLock extends SpellRay { && event.getWorld().getTileEntity(pos).getTileData().hasUniqueId(NBT_KEY)); } + + + + } diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityShrineCore.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityShrineCore.java index 6bde7079..bf9e3feb 100644 --- a/src/main/java/electroblob/wizardry/tileentity/TileEntityShrineCore.java +++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityShrineCore.java @@ -2,6 +2,7 @@ package electroblob.wizardry.tileentity; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockPedestal; +import electroblob.wizardry.constants.Element; import electroblob.wizardry.entity.living.EntityEvilWizard; import electroblob.wizardry.entity.living.EntityWizard; import electroblob.wizardry.packet.PacketConquerShrine; @@ -13,87 +14,120 @@ import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.ArcaneLock; import electroblob.wizardry.util.*; import electroblob.wizardry.util.ParticleBuilder.Type; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTUtil; import net.minecraft.potion.PotionEffect; import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityChest; import net.minecraft.util.ITickable; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.TextComponentTranslation; import net.minecraftforge.common.util.Constants; import net.minecraftforge.fml.common.network.NetworkRegistry; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; +import java.util.*; public class TileEntityShrineCore extends TileEntity implements ITickable { private static final double ACTIVATION_RADIUS = 5; private boolean activated = false; + private boolean conquered = false; + private long regenerationTime = 0; + private long lastRegenerationTime = 0; + private Element shrineElement; private AxisAlignedBB containmentField; private final UUID[] linkedWizards = new UUID[3]; private TileEntity linkedContainer; private BlockPos linkedContainerPos; // Temporary stores the container position read from NBT until the world is set + private final Set lootedPlayers = new HashSet<>(); @Override - public void setPos(BlockPos pos){ + public void setPos(BlockPos pos) { super.setPos(pos); initContainmentField(pos); } - private void initContainmentField(BlockPos pos){ + private void initContainmentField(BlockPos pos) { float r = PotionContainment.getContainmentDistance(0); this.containmentField = new AxisAlignedBB(-r, -r, -r, r, r, r).offset(GeometryUtils.getCentre(pos)); } - public void linkContainer(TileEntity container){ + public void linkContainer(TileEntity container) { this.linkedContainer = container; } - @Override - public void update(){ + public void setShrineElement(Element element) { + this.shrineElement = element; + } - if(this.linkedContainer == null && this.linkedContainerPos != null){ + public boolean canPlayerLoot(EntityPlayer player) { + if (Wizardry.settings.shrineAllowMultipleLoot) { + return true; // Allow multiple looting if enabled + } + return !lootedPlayers.contains(player.getUniqueID()); + } + + public void recordPlayerLoot(EntityPlayer player) { + if (!Wizardry.settings.shrineAllowMultipleLoot) { + lootedPlayers.add(player.getUniqueID()); + this.markDirty(); + } + } + + @Override + public void update() { + + if (this.linkedContainer == null && this.linkedContainerPos != null) { this.linkContainer(world.getTileEntity(this.linkedContainerPos)); } + // Handle shrine regeneration + if (conquered && Wizardry.settings.shrineRegenerationEnabled && regenerationTime > 0) { + if (world.getTotalWorldTime() >= regenerationTime) { + regenerate(); + return; // Don't process other logic during regeneration + } + } + double x = this.pos.getX() + 0.5; double y = this.pos.getY() + 0.5; double z = this.pos.getZ() + 0.5; - if(!activated && world.getClosestPlayer(x, y, z, ACTIVATION_RADIUS, false) != null){ + if (!activated && !conquered && world.getClosestPlayer(x, y, z, ACTIVATION_RADIUS, false) != null && (lastRegenerationTime == 0 || world.getTotalWorldTime() - lastRegenerationTime > 100)) { // 5 second delay after regeneration this.activated = true; - if(world.isRemote){ + if (world.isRemote) { ParticleBuilder.create(Type.SPHERE).pos(x, y + 1, z).clr(0xf06495).scale(5).time(12).spawn(world); } - world.playSound(x, y, z, - WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false); + world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false); - if(!world.isRemote){ + if (!world.isRemote) { EntityEvilWizard[] wizards = new EntityEvilWizard[linkedWizards.length]; - for(int i = 0; i < linkedWizards.length; i++){ + for (int i = 0; i < linkedWizards.length; i++) { EntityEvilWizard wizard = new EntityEvilWizard(world); - float angle = world.rand.nextFloat() * 2 * (float)Math.PI; + float angle = world.rand.nextFloat() * 2 * (float) Math.PI; double x1 = this.pos.getX() + 0.5 + 5 * MathHelper.sin(angle); double z1 = this.pos.getZ() + 0.5 + 5 * MathHelper.cos(angle); Integer y1 = BlockUtils.getNearestFloor(world, new BlockPos(x1, this.pos.getY(), z1), 8); - if(y1 == null){ + if (y1 == null) { // Fallback to the position of the shrine core if it failed to find a position (unlikely) x1 = this.pos.getX() + 1; // Offset it so the wizard isn't inside the block y1 = this.pos.getY(); @@ -110,110 +144,279 @@ public class TileEntityShrineCore extends TileEntity implements ITickable { linkedWizards[i] = wizard.getUniqueID(); } - for(EntityEvilWizard wizard : wizards) wizard.groupUUIDs.addAll(Arrays.asList(linkedWizards)); + for (EntityEvilWizard wizard : wizards) wizard.groupUUIDs.addAll(Arrays.asList(linkedWizards)); } containNearbyTargets(); } - if(activated && world.getTotalWorldTime() % 20L == 0) containNearbyTargets(); + if (!areWizardsDead() && activated && world.getTotalWorldTime() % 20L == 0) containNearbyTargets(); - if(activated && areWizardsDead() && !world.isRemote){ + if (activated && areWizardsDead() && !world.isRemote) { conquer(); } } - private boolean areWizardsDead(){ + private boolean areWizardsDead() { - for(UUID uuid : linkedWizards){ + for (UUID uuid : linkedWizards) { Entity entity = EntityUtils.getEntityByUUID(world, uuid); - if(entity instanceof EntityEvilWizard && entity.isEntityAlive()) return false; + if (entity instanceof EntityEvilWizard && entity.isEntityAlive()) return false; } return true; } - public void conquer(){ + public void conquer() { double x = this.pos.getX() + 0.5; double y = this.pos.getY() + 0.5; double z = this.pos.getZ() + 0.5; - if(!world.isRemote){ + if (!world.isRemote) { - WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos), - new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64)); + WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos), new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64)); - if(world.getBlockState(pos).getBlock() == WizardryBlocks.runestone_pedestal){ - world.setBlockState(pos, WizardryBlocks.runestone_pedestal.getDefaultState() - .withProperty(BlockPedestal.ELEMENT, world.getBlockState(pos).getValue(BlockPedestal.ELEMENT))); - }else{ - Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!"); + // Remove containment effects from nearby targets when shrine is conquered + removeContainmentFromNearbyTargets(); + + // If regeneration is enabled, schedule regeneration instead of removing the tile entity + if (Wizardry.settings.shrineRegenerationEnabled) { + this.conquered = true; + this.regenerationTime = world.getTotalWorldTime() + (Wizardry.settings.shrineRegenerationTime * 1200L); // Convert minutes to ticks (20 ticks per second * 60 seconds) + this.activated = false; + Arrays.fill(this.linkedWizards, null); // Clear wizard references + // Keep lootedPlayers list - it's permanent tracking for this shrine instance + + // Handle chest breaking and loot dropping + handleChestLootDrop(); + + // Mark the tile entity for update + this.markDirty(); + + if (world.getBlockState(pos).getBlock() == WizardryBlocks.runestone_pedestal) { + // Keep the block state the same, just mark as conquered + this.shrineElement = world.getBlockState(pos).getValue(BlockPedestal.ELEMENT); + } else { + Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!"); + } + } else { + // Original behavior: remove the tile entity + BlockPos chestPos = this.pos.up(); + TileEntity chestTileEntity = world.getTileEntity(chestPos); + if (chestTileEntity instanceof TileEntityChest) { + TileEntityChest chest = (TileEntityChest) chestTileEntity; + NBTExtras.removeUniqueId(chest.getTileData(), ArcaneLock.NBT_KEY); + chest.markDirty(); + world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3); + } + if (world.getBlockState(pos).getBlock() == WizardryBlocks.runestone_pedestal) { + world.setBlockState(pos, WizardryBlocks.runestone_pedestal.getDefaultState().withProperty(BlockPedestal.ELEMENT, world.getBlockState(pos).getValue(BlockPedestal.ELEMENT))); + } else { + Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!"); + } + world.markTileEntityForRemoval(this); } } - world.markTileEntityForRemoval(this); - - if(!world.isRemote){ - if(linkedContainer != null) NBTExtras.removeUniqueId(linkedContainer.getTileData(), ArcaneLock.NBT_KEY); - }else{ - TileEntity tileEntity = world.getTileEntity(this.pos.up()); - if(tileEntity != null){ // Bit of a dirty fix but it's only visual, so meh - NBTExtras.removeUniqueId(tileEntity.getTileData(), ArcaneLock.NBT_KEY); - } + if (linkedContainer == null) { + linkedContainer = world.getTileEntity(pos.up()); } + if (linkedContainer != null) { + NBTExtras.removeUniqueId(linkedContainer.getTileData(), ArcaneLock.NBT_KEY); + //linkedContainer.getTileData().removeTag("arcaneLockOwnerMost"); + //linkedContainer.getTileData().removeTag("arcaneLockOwnerLeast"); + } world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_CONQUER, SoundCategory.BLOCKS, 1, 1, false); - if(world.isRemote){ + if (world.isRemote) { ParticleBuilder.create(Type.SPHERE).scale(5).pos(x, y + 1, z).clr(0xf06495).time(12).spawn(world); - for(int i=0; i<5; i++){ + for (int i = 0; i < 5; i++) { float brightness = 0.8f + world.rand.nextFloat() * 0.2f; - ParticleBuilder.create(Type.SPARKLE, world.rand, x, y + 1, z, 1, true) - .clr(1, brightness, brightness).spawn(world); + ParticleBuilder.create(Type.SPARKLE, world.rand, x, y + 1, z, 1, true).clr(1, brightness, brightness).spawn(world); } } } - private void containNearbyTargets(){ + private void regenerate() { - List entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField, - e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard); + if (world.isRemote) return; - for(EntityLivingBase entity : entities){ + // Remove containment effects from nearby targets when shrine regenerates + removeContainmentFromNearbyTargets(); + + // Reset shrine state + this.conquered = false; + this.regenerationTime = 0; + this.activated = false; + Arrays.fill(this.linkedWizards, null); + // Keep lootedPlayers list - it's permanent for this shrine instance + + // Force a short delay before allowing reactivation to prevent immediate re-activation + this.lastRegenerationTime = world.getTotalWorldTime(); + + // Forcibly replace whatever block is above the altar with a fresh chest + BlockPos chestPos = this.pos.up(); + + // Always replace the block above with a fresh chest during regeneration + world.setBlockState(chestPos, Blocks.CHEST.getDefaultState()); + + // Set up the loot table for the shrine chest + TileEntity chestTileEntity = world.getTileEntity(chestPos); + if (chestTileEntity instanceof TileEntityChest) { + TileEntityChest chest = (TileEntityChest) chestTileEntity; + chest.setLootTable(new ResourceLocation(Wizardry.MODID, "chests/shrine"), world.rand.nextLong()); + } + + // Link and apply arcane lock to the container + if (chestTileEntity != null) { + this.linkContainer(chestTileEntity); + chestTileEntity.getTileData().setUniqueId(ArcaneLock.NBT_KEY, new UUID(0, 0)); // Nil UUID for shrine lock + chestTileEntity.markDirty(); // Mark tile entity as dirty for client sync + + // Trigger visual update for arcane lock effect + IBlockState blockState = world.getBlockState(chestPos); + world.markAndNotifyBlock(chestPos, null, blockState, blockState, 3); + world.notifyBlockUpdate(chestPos, blockState, blockState, 3); // Additional client sync + } + + // Visual and audio effects for regeneration + double x = this.pos.getX() + 0.5; + double y = this.pos.getY() + 0.5; + double z = this.pos.getZ() + 0.5; + + // WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos), new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64)); + + if (world.isRemote) { + ParticleBuilder.create(Type.SPHERE).pos(x, y + 1, z).clr(0xf06495).scale(5).time(12).spawn(world); + } + + world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false); + + this.markDirty(); + } + + private void handleChestLootDrop() { + BlockPos chestPos = this.pos.up(); + + // Check if there's a chest at the expected position + if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) { + TileEntity chestTileEntity = world.getTileEntity(chestPos); + + if (chestTileEntity instanceof TileEntityChest) { + TileEntityChest chest = (TileEntityChest) chestTileEntity; + + // Find the player who conquered the shrine (closest player) + EntityPlayer conqueringPlayer = world.getClosestPlayer(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, 20.0, false); + + if (conqueringPlayer != null && !canPlayerLoot(conqueringPlayer)) { + // Send message to player that they've already looted this shrine + if (!world.isRemote) { + conqueringPlayer.sendMessage(new TextComponentTranslation("wizardry.shrine_already_looted")); + } + chest.setLootTable(null, world.rand.nextLong()); + if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) { + world.setBlockToAir(chestPos); + } + return; + } + + if (conqueringPlayer != null && canPlayerLoot(conqueringPlayer)) { + // Record that this player has looted the shrine + recordPlayerLoot(conqueringPlayer); + } + // If player has already looted, don't drop anything + } + } + + // Break the chest regardless + if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) { + world.setBlockToAir(chestPos); + } + } + + private void containNearbyTargets() { + List entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField, e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard); + + for (EntityLivingBase entity : entities) { entity.addPotionEffect(new PotionEffect(WizardryPotions.containment, 219)); NBTExtras.storeTagSafely(entity.getEntityData(), PotionContainment.ENTITY_TAG, NBTUtil.createPosTag(this.pos)); } } + private void removeContainmentFromNearbyTargets() { +// List entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField, +// e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard); +// +// for(EntityLivingBase entity : entities){ +// // Remove the containment potion effect +// if(entity.isPotionActive(WizardryPotions.containment)){ +// entity.removePotionEffect(WizardryPotions.containment); +// } +// // Also remove the containment position tag +// if(entity.getEntityData().hasKey(PotionContainment.ENTITY_TAG)){ +// BlockPos containmentPos = NBTUtil.getPosFromTag(entity.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)); +// // Only remove if the containment position matches this shrine +// if(containmentPos.equals(this.pos)){ +// entity.getEntityData().removeTag(PotionContainment.ENTITY_TAG); +// } +// } +// } + } + @Override - public NBTTagCompound writeToNBT(NBTTagCompound compound){ + public NBTTagCompound writeToNBT(NBTTagCompound compound) { compound.setBoolean("activated", this.activated); - if(linkedContainer != null) NBTExtras.storeTagSafely(compound, "linkedContainerPos", NBTUtil.createPosTag(linkedContainer.getPos())); + compound.setBoolean("conquered", this.conquered); + compound.setLong("regenerationTime", this.regenerationTime); + compound.setLong("lastRegenerationTime", this.lastRegenerationTime); + if (shrineElement != null) compound.setInteger("shrineElement", this.shrineElement.ordinal()); + if (linkedContainer != null) + NBTExtras.storeTagSafely(compound, "linkedContainerPos", NBTUtil.createPosTag(linkedContainer.getPos())); - NBTTagList tagList = new NBTTagList(); - for(UUID uuid : linkedWizards){ - if(uuid != null) tagList.appendTag(NBTUtil.createUUIDTag(uuid)); + NBTTagList wizardTagList = new NBTTagList(); + for (UUID uuid : linkedWizards) { + if (uuid != null) wizardTagList.appendTag(NBTUtil.createUUIDTag(uuid)); } - NBTExtras.storeTagSafely(compound, "wizards", tagList); + NBTExtras.storeTagSafely(compound, "wizards", wizardTagList); + + NBTTagList playerTagList = new NBTTagList(); + for (UUID uuid : lootedPlayers) { + playerTagList.appendTag(NBTUtil.createUUIDTag(uuid)); + } + NBTExtras.storeTagSafely(compound, "lootedPlayers", playerTagList); return super.writeToNBT(compound); } @Override - public void readFromNBT(NBTTagCompound compound){ + public void readFromNBT(NBTTagCompound compound) { this.activated = compound.getBoolean("activated"); + this.conquered = compound.getBoolean("conquered"); + this.regenerationTime = compound.getLong("regenerationTime"); + this.lastRegenerationTime = compound.getLong("lastRegenerationTime"); + if (compound.hasKey("shrineElement")) + this.shrineElement = Element.values()[compound.getInteger("shrineElement")]; this.linkedContainerPos = NBTUtil.getPosFromTag(compound.getCompoundTag("linkedContainerPos")); - NBTTagList tagList = compound.getTagList("wizards", Constants.NBT.TAG_COMPOUND); + NBTTagList wizardTagList = compound.getTagList("wizards", Constants.NBT.TAG_COMPOUND); int i = 0; - for(NBTBase tag : tagList){ - if(tag instanceof NBTTagCompound) linkedWizards[i++] = NBTUtil.getUUIDFromTag((NBTTagCompound)tag); + for (NBTBase tag : wizardTagList) { + if (tag instanceof NBTTagCompound) linkedWizards[i++] = NBTUtil.getUUIDFromTag((NBTTagCompound) tag); else Wizardry.logger.warn("Unexpected tag type in NBT tag list of compound tags!"); } + this.lootedPlayers.clear(); + if (compound.hasKey("lootedPlayers")) { + NBTTagList playerTagList = compound.getTagList("lootedPlayers", Constants.NBT.TAG_COMPOUND); + for (NBTBase tag : playerTagList) { + if (tag instanceof NBTTagCompound) lootedPlayers.add(NBTUtil.getUUIDFromTag((NBTTagCompound) tag)); + } + } + super.readFromNBT(compound); // Must be after super initContainmentField(this.pos); diff --git a/src/main/java/electroblob/wizardry/worldgen/WorldGenShrine.java b/src/main/java/electroblob/wizardry/worldgen/WorldGenShrine.java index 7e9b4d5d..2a735446 100644 --- a/src/main/java/electroblob/wizardry/worldgen/WorldGenShrine.java +++ b/src/main/java/electroblob/wizardry/worldgen/WorldGenShrine.java @@ -74,13 +74,27 @@ public class WorldGenShrine extends WorldGenSurfaceStructure { if(container != null){ container.getTileData().setUniqueId(ArcaneLock.NBT_KEY, new UUID(0, 0)); // Nil UUID + container.markDirty(); // Mark tile entity as dirty for client sync - if(core instanceof TileEntityShrineCore){ - ((TileEntityShrineCore)core).linkContainer(container); - }else{ - Wizardry.logger.info("What?!"); + // Trigger visual update for arcane lock effect + BlockPos chestPos = entry.getKey().up(); + net.minecraft.block.state.IBlockState blockState = world.getBlockState(chestPos); + world.markAndNotifyBlock(chestPos, null, blockState, blockState, 3); + world.notifyBlockUpdate(chestPos, blockState, blockState, 3); // Additional client sync + + // Set up the loot table for the shrine chest + if(container instanceof net.minecraft.tileentity.TileEntityChest){ + net.minecraft.tileentity.TileEntityChest chest = (net.minecraft.tileentity.TileEntityChest) container; + chest.setLootTable(new net.minecraft.util.ResourceLocation(Wizardry.MODID, "chests/shrine"), world.rand.nextLong()); } + if(core instanceof TileEntityShrineCore){ + ((TileEntityShrineCore)core).linkContainer(container); + ((TileEntityShrineCore)core).setShrineElement(element); + }else{ + Wizardry.logger.info("What?!"); + } + }else{ Wizardry.logger.info("Expected chest or other container at {} in structure {}, found no tile entity", entry.getKey(), structureFile); } diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 412aabcc..34f6d9bd 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -1832,3 +1832,5 @@ spell.ebwizardry\:invigorating_presence_festive=Invigorating Presents spell.ebwizardry\:empowering_presence_festive=Empowering Presents wizard.debug=%1$s, %2$s, %3$s + +wizardry.shrine_already_looted=You have already looted this shrine! \ No newline at end of file