feat: Regenerating Shrines. The following config options were added:

- 'shrineRegenerationEnabled': Whether conquered shrines should regenerate after a period of time. Regeneration means wizards and loot will spawn again after a while.
      - 'shrineRegenerationTime': Time in minutes for a conquered shrine to regenerate.
      - 'shrineAllowMultipleLoot': Whether players can loot shrines multiple times. If false, each player can only loot each shrine once.
      When shrineRegenerationEnabled, Shrine chests behave a bit differently - upon defeating all wizards, the chest breaks immediately dropping or not dropping loot based on shrineAllowMultipleLoot. When shrineRegenerationEnabled, the shrine's altar block is unbreakable.
This commit is contained in:
WinDanesz
2025-08-31 17:36:54 +02:00
parent f817f06dbe
commit 5ac053ddd9
7 changed files with 330 additions and 63 deletions
+3
View File
@@ -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 {
@@ -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")};
/** <b>[Server-only]</b> Whether conquered shrines should regenerate after a period of time. */
public boolean shrineRegenerationEnabled = true;
/** <b>[Server-only]</b> Time in minutes for a conquered shrine to regenerate. */
public int shrineRegenerationTime = 1;
/** <b>[Server-only]</b> Whether players can loot shrines multiple times. If false, each player can only loot each shrine once. */
public boolean shrineAllowMultipleLoot = false;
/** <b>[Server-only]</b> List of dimension ids in which to generate library ruins. */
public int[] libraryDimensions = {0};
/** <b>[Server-only]</b> 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);
@@ -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);
}
@@ -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));
}
}
@@ -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<UUID> 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<EntityLivingBase> 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<EntityLivingBase> 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<EntityLivingBase> 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);
@@ -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);
}
@@ -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!