package WayofTime.bloodmagic.ritual; import WayofTime.bloodmagic.soul.DemonWillHolder; import WayofTime.bloodmagic.soul.EnumDemonWillType; import WayofTime.bloodmagic.demonAura.WorldDemonWillHandler; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import org.apache.commons.lang3.builder.ToStringBuilder; import java.util.*; import java.util.Map.Entry; /** * Abstract class for creating new rituals. Rituals need be registered with * {@link RitualRegistry#registerRitual(Ritual, String)} */ public abstract class Ritual { protected final Map modableRangeMap = new HashMap<>(); protected final Map volumeRangeMap = new HashMap<>(); protected final Map horizontalRangeMap = new HashMap<>(); protected final Map verticalRangeMap = new HashMap<>(); private final String name; private final int crystalLevel; private final int activationCost; private final RitualRenderer renderer; private final String unlocalizedName; public Ritual(String name, int crystalLevel, int activationCost, RitualRenderer renderer, String unlocalizedName) { this.name = name; this.crystalLevel = crystalLevel; this.activationCost = activationCost; this.renderer = renderer; this.unlocalizedName = unlocalizedName; } /** * @param name - The name of the ritual * @param crystalLevel - Required Activation Crystal tier * @param activationCost - Base LP cost for activating the ritual */ public Ritual(String name, int crystalLevel, int activationCost, String unlocalizedName) { this(name, crystalLevel, activationCost, null, unlocalizedName); } public void readFromNBT(NBTTagCompound tag) { NBTTagList tags = tag.getTagList("areas", 10); if (tags.hasNoTags()) { return; } for (int i = 0; i < tags.tagCount(); i++) { NBTTagCompound newTag = tags.getCompoundTagAt(i); String rangeKey = newTag.getString("key"); NBTTagCompound storedTag = newTag.getCompoundTag("area"); AreaDescriptor desc = this.getBlockRange(rangeKey); if (desc != null) { desc.readFromNBT(storedTag); } } } public void writeToNBT(NBTTagCompound tag) { NBTTagList tags = new NBTTagList(); for (Entry entry : modableRangeMap.entrySet()) { NBTTagCompound newTag = new NBTTagCompound(); newTag.setString("key", entry.getKey()); NBTTagCompound storedTag = new NBTTagCompound(); entry.getValue().writeToNBT(storedTag); newTag.setTag("area", storedTag); tags.appendTag(newTag); } tag.setTag("areas", tags); } /** * Called when the player attempts to activate the ritual. *

* {@link WayofTime.bloodmagic.tile.TileMasterRitualStone#activateRitual(ItemStack, EntityPlayer, Ritual)} * * @param masterRitualStone - The {@link IMasterRitualStone} that the ritual is bound to * @param player - The activating player * @param owner - Owner of the crystal activating this ritual, or the current * owner of the ritual if being reactivated. * @return - Whether activation was successful */ public boolean activateRitual(IMasterRitualStone masterRitualStone, EntityPlayer player, UUID owner) { return true; } /** * Called every {@link #getRefreshTime()} ticks while active. *

* {@link WayofTime.bloodmagic.tile.TileMasterRitualStone#performRitual(World, BlockPos)} * * @param masterRitualStone - The {@link IMasterRitualStone} that the ritual is bound to */ public abstract void performRitual(IMasterRitualStone masterRitualStone); /** * Called when the ritual is stopped for a given {@link Ritual.BreakType}. *

* {@link WayofTime.bloodmagic.tile.TileMasterRitualStone#stopRitual(Ritual.BreakType)} * * @param masterRitualStone - The {@link IMasterRitualStone} that the ritual is bound to * @param breakType - The type of break that caused the stoppage. */ public void stopRitual(IMasterRitualStone masterRitualStone, BreakType breakType) { } /** * Used to set the amount of LP drained every {@link #getRefreshTime()} * ticks. * * @return - The amount of LP drained per refresh */ public abstract int getRefreshCost(); /** * Used to set the refresh rate of the ritual. (How often * {@link #performRitual(IMasterRitualStone)} is called. * * @return - How often to perform the effect in ticks. */ public int getRefreshTime() { return 20; } public void addBlockRange(String range, AreaDescriptor defaultRange) { modableRangeMap.put(range, defaultRange); } /** * Used to grab the range of a ritual for a given effect. * * @param range - Range that needs to be pulled. * @return - */ public AreaDescriptor getBlockRange(String range) { if (modableRangeMap.containsKey(range)) { return modableRangeMap.get(range); } return null; } public List getListOfRanges() { return new ArrayList<>(modableRangeMap.keySet()); } public String getNextBlockRange(String range) { List rangeList = getListOfRanges(); if (rangeList.isEmpty()) { return ""; } if (!rangeList.contains(range)) { return rangeList.get(0); } boolean hasMatch = false; for (String rangeCheck : rangeList) { if (hasMatch) { return rangeCheck; } else if (rangeCheck.equals(range)) { hasMatch = true; } } return rangeList.get(0); } public boolean setBlockRangeByBounds(String range, IMasterRitualStone master, BlockPos offset1, BlockPos offset2) { AreaDescriptor descriptor = this.getBlockRange(range); World world = master.getWorldObj(); BlockPos masterPos = master.getBlockPos(); DemonWillHolder holder = WorldDemonWillHandler.getWillHolder(world, masterPos); if (canBlockRangeBeModified(range, descriptor, master, offset1, offset2, holder)) { descriptor.modifyAreaByBlockPositions(offset1, offset2); return true; } return false; } protected boolean canBlockRangeBeModified(String range, AreaDescriptor descriptor, IMasterRitualStone master, BlockPos offset1, BlockPos offset2, DemonWillHolder holder) { List willConfig = master.getActiveWillConfig(); int maxVolume = getMaxVolumeForRange(range, willConfig, holder); int maxVertical = getMaxVerticalRadiusForRange(range, willConfig, holder); int maxHorizontal = getMaxHorizontalRadiusForRange(range, willConfig, holder); return (maxVolume <= 0 || descriptor.getVolumeForOffsets(offset1, offset2) <= maxVolume) && descriptor.isWithinRange(offset1, offset2, maxVertical, maxHorizontal); } protected void setMaximumVolumeAndDistanceOfRange(String range, int volume, int horizontalRadius, int verticalRadius) { volumeRangeMap.put(range, volume); horizontalRangeMap.put(range, horizontalRadius); verticalRangeMap.put(range, verticalRadius); } protected boolean checkDescriptorIsWithinRange(AreaDescriptor descriptor, int maxVolume, int maxHorizontal, int maxVertical) { return descriptor.getVolume() <= maxVolume && descriptor.isWithinRange(maxVertical, maxHorizontal); } public int getMaxVolumeForRange(String range, List activeTypes, DemonWillHolder holder) { return volumeRangeMap.get(range); } public int getMaxVerticalRadiusForRange(String range, List activeTypes, DemonWillHolder holder) { return verticalRangeMap.get(range); } public int getMaxHorizontalRadiusForRange(String range, List activeTypes, DemonWillHolder holder) { return horizontalRangeMap.get(range); } public ITextComponent getErrorForBlockRangeOnFail(EntityPlayer player, String range, IMasterRitualStone master, BlockPos offset1, BlockPos offset2) { AreaDescriptor descriptor = this.getBlockRange(range); if (descriptor == null) { return new TextComponentTranslation("ritual.bloodmagic.blockRange.tooBig", "?"); } List willConfig = master.getActiveWillConfig(); DemonWillHolder holder = WorldDemonWillHandler.getWillHolder(master.getWorldObj(), master.getBlockPos()); int maxVolume = this.getMaxVolumeForRange(range, willConfig, holder); int maxVertical = this.getMaxVerticalRadiusForRange(range, willConfig, holder); int maxHorizontal = this.getMaxHorizontalRadiusForRange(range, willConfig, holder); if (maxVolume > 0 && descriptor.getVolumeForOffsets(offset1, offset2) > maxVolume) { return new TextComponentTranslation("ritual.bloodmagic.blockRange.tooBig", maxVolume); } else { return new TextComponentTranslation("ritual.bloodmagic.blockRange.tooFar", maxVertical, maxHorizontal); } } public ITextComponent[] provideInformationOfRitualToPlayer(EntityPlayer player) { return new ITextComponent[]{new TextComponentTranslation(this.getUnlocalizedName() + ".info")}; } public ITextComponent provideInformationOfRangeToPlayer(EntityPlayer player, String range) { if (getListOfRanges().contains(range)) { return new TextComponentTranslation(this.getUnlocalizedName() + "." + range + ".info"); } else { return new TextComponentTranslation("ritual.bloodmagic.blockRange.noRange"); } } public abstract void gatherComponents(List components); protected final void addRune(List components, int offset1, int y, int offset2, EnumRuneType rune) { components.add(new RitualComponent(new BlockPos(offset1, y, offset2), rune)); } protected final void addOffsetRunes(List components, int offset1, int offset2, int y, EnumRuneType rune) { addRune(components, offset1, y, offset2, rune); addRune(components, offset2, y, offset1, rune); addRune(components, offset1, y, -offset2, rune); addRune(components, -offset2, y, offset1, rune); addRune(components, -offset1, y, offset2, rune); addRune(components, offset2, y, -offset1, rune); addRune(components, -offset1, y, -offset2, rune); addRune(components, -offset2, y, -offset1, rune); } protected final void addCornerRunes(List components, int offset, int y, EnumRuneType rune) { addRune(components, offset, y, offset, rune); addRune(components, offset, y, -offset, rune); addRune(components, -offset, y, -offset, rune); addRune(components, -offset, y, offset, rune); } protected final void addParallelRunes(List components, int offset, int y, EnumRuneType rune) { addRune(components, offset, y, 0, rune); addRune(components, -offset, y, 0, rune); addRune(components, 0, y, -offset, rune); addRune(components, 0, y, offset, rune); } public double getWillRespectingConfig(World world, BlockPos pos, EnumDemonWillType type, List willConfig) { return willConfig.contains(type) ? WorldDemonWillHandler.getCurrentWill(world, pos, type) : 0; } public abstract Ritual getNewCopy(); public String getName() { return name; } public int getCrystalLevel() { return crystalLevel; } public int getActivationCost() { return activationCost; } public RitualRenderer getRenderer() { return renderer; } public String getUnlocalizedName() { return unlocalizedName; } public Map getModableRangeMap() { return modableRangeMap; } public Map getVolumeRangeMap() { return volumeRangeMap; } public Map getHorizontalRangeMap() { return horizontalRangeMap; } public Map getVerticalRangeMap() { return verticalRangeMap; } @Override public String toString() { return new ToStringBuilder(this) .append("name", name) .append("crystalLevel", crystalLevel) .append("activationCost", activationCost) .append("renderer", renderer) .append("unlocalizedName", unlocalizedName) .append("modableRangeMap", modableRangeMap) .append("volumeRangeMap", volumeRangeMap) .append("horizontalRangeMap", horizontalRangeMap) .append("verticalRangeMap", verticalRangeMap) .append("refreshTime", getRefreshTime()) .append("listOfRanges", getListOfRanges()) .toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Ritual)) return false; Ritual ritual = (Ritual) o; if (crystalLevel != ritual.crystalLevel) return false; if (activationCost != ritual.activationCost) return false; if (name != null ? !name.equals(ritual.name) : ritual.name != null) return false; return unlocalizedName != null ? unlocalizedName.equals(ritual.unlocalizedName) : ritual.unlocalizedName == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + crystalLevel; result = 31 * result + activationCost; result = 31 * result + (unlocalizedName != null ? unlocalizedName.hashCode() : 0); return result; } public enum BreakType { REDSTONE, BREAK_MRS, BREAK_STONE, ACTIVATE, DEACTIVATE, EXPLOSION, } }