Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0328f41564 | |||
| 9ab4684aef | |||
| e64cf9a6af | |||
| f13f220564 | |||
| 311b5750df | |||
| e54aee332d | |||
| e4bf09f15a | |||
| aa166814ea | |||
| d37ab6072f | |||
| ba83cb7985 | |||
| 3052e56d03 | |||
| b682740751 | |||
| 38429cb697 | |||
| dc7b6790b8 | |||
| 7a247df3e1 | |||
| de2087c0e8 | |||
| f5da195605 | |||
| 6cd6411c25 | |||
| 40e77728ad | |||
| d50ff5729f | |||
| 76b4f2f72f | |||
| a608375433 | |||
| c3f8ba245d | |||
| 34b3fe93bb | |||
| 996ec9a7f7 | |||
| 2b38ca9a89 | |||
| 67788a2bd8 | |||
| 58fcaa8af2 | |||
| 24e89c8b8a | |||
| 1293b89905 | |||
| 807fc61541 | |||
| 9741c751b3 | |||
| d3003ae473 | |||
| 9fde24f341 | |||
| 452eb17b5f | |||
| 36963aac30 | |||
| f161cdfe7c | |||
| 455c5a5e07 | |||
| 9852f61d0f | |||
| a2c8cde4c4 | |||
| 497d28eafd | |||
| f82d2871d5 |
@@ -7,10 +7,10 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
|
||||
|
||||
Minecraft version: 1.12.2 [change as necessary]
|
||||
Wizardry version: 4.2.2 [change as necessary]
|
||||
Wizardry version: 4.2.8 [change as necessary]
|
||||
Environment: Singleplayer/LAN game/Server [delete as necessary]
|
||||
|
||||
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
|
||||
|
||||
@@ -7,7 +7,7 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
|
||||
|
||||
Minecraft versions: [if this suggestion only applies to some versions, list them here]
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
|
||||
|
||||
Minecraft version: 1.12.2 [change as necessary]
|
||||
Wizardry version: 4.2.2 [change as necessary]
|
||||
Wizardry version: 4.2.8 [change as necessary]
|
||||
|
||||
Question details: [explain your question here]
|
||||
|
||||
|
||||
+8
-7
@@ -1,7 +1,7 @@
|
||||
# Credits
|
||||
|
||||
Electroblob's Wizardry
|
||||
Version 4.2.6
|
||||
Version 4.2.8
|
||||
For Minecraft 1.12.2
|
||||
|
||||
Designed, coded and textured by Electroblob
|
||||
@@ -23,15 +23,16 @@ In addition, I'd like to thank the following individuals for their contributions
|
||||
- Aeronica
|
||||
- UltraHex
|
||||
- Azim-Palmer
|
||||
- raoulvdberge
|
||||
|
||||
#### Translations
|
||||
|
||||
- Spanish and Mexican Spanish: MadWrist
|
||||
- Russian: VilagVil, kellixon
|
||||
- French: Hahdrim
|
||||
- Brazilian Portuguese: lorrampi
|
||||
- Chinese: ZHENGLOC, dragon-evol
|
||||
- Korean: shejery, rewi_wire
|
||||
- Spanish and Mexican Spanish: MadWrist
|
||||
- Russian: VilagVil, kellixon, bigenergy
|
||||
- French: Hahdrim
|
||||
- Brazilian Portuguese: lorrampi
|
||||
- Chinese: ZHENGLOC, dragon-evol
|
||||
- Korean: shejery, rewi_wire
|
||||
- Polish: Trozuu
|
||||
|
||||
Lightning ray sound effect from OhhWowProductions
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
|
||||
|
||||
Minecraft version: 1.12.2 [change as necessary]
|
||||
Wizardry version: 4.1.4 [change as necessary]
|
||||
Wizardry version: 4.2.8 [change as necessary]
|
||||
Environment: Singleplayer/LAN game/Server [delete as necessary]
|
||||
|
||||
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
|
||||
|
||||
version = "4.2.6" // There, it matches semver, happy now?
|
||||
version = "4.2.8" // There, it matches semver, happy now?
|
||||
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
archivesBaseName = "ElectroblobsWizardry"
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ public class CommonProxy {
|
||||
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
|
||||
}
|
||||
|
||||
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style)}, but style defaults to light grey. */
|
||||
public void addMultiLineDescription(List<String> tooltip, String key){
|
||||
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY));
|
||||
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style, Object...)}, but style defaults to light grey. */
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Object... args){
|
||||
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY), args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,7 @@ public class CommonProxy {
|
||||
* @param key The translation key for the description
|
||||
* @param style A style to apply
|
||||
*/
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){}
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){}
|
||||
|
||||
// SECTION Packet Handlers
|
||||
// ===============================================================================================================
|
||||
@@ -140,8 +140,6 @@ public class CommonProxy {
|
||||
|
||||
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){}
|
||||
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){}
|
||||
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){}
|
||||
|
||||
public void handlePossessionPacket(PacketPossession.Message message){}
|
||||
|
||||
@@ -433,7 +433,7 @@ public final class Settings {
|
||||
|
||||
Property property;
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true,
|
||||
I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc"));
|
||||
// Uses the same config key as the spell name, because - well, that's what it's called!
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Wizardry {
|
||||
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
|
||||
* 1.11.x versions, and so on.
|
||||
*/
|
||||
public static final String VERSION = "4.2.6";
|
||||
public static final String VERSION = "4.2.8";
|
||||
|
||||
// IDEA: Triggering of inbuilt Forge events in relevant places?
|
||||
// IDEA: Abstract the vanilla particles behind the particle builder
|
||||
|
||||
@@ -33,6 +33,9 @@ import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.storage.loot.LootContext;
|
||||
import net.minecraft.world.storage.loot.LootTable;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
|
||||
@@ -46,6 +49,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* General-purpose event handler for things that don't fit anywhere else or groups of related behaviours that are better
|
||||
@@ -343,18 +347,21 @@ public final class WizardryEventHandler {
|
||||
|
||||
Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell();
|
||||
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
|
||||
int count = ((ISpellCaster)event.getEntity()).getSpellCounter();
|
||||
|
||||
if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check!
|
||||
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(),
|
||||
modifiers, 0))){
|
||||
modifiers, count))){
|
||||
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0,
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, count,
|
||||
// TODO: This implementation of modifiers relies on them being accessible client-side.
|
||||
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future
|
||||
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
((ISpellCaster)event.getEntity()).setSpellCounter(count + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,6 +373,22 @@ public final class WizardryEventHandler {
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getSource().getTrueSource();
|
||||
|
||||
// Compatibility with "Lycanites Mobs" -it uses custom loot drop logic which can't be hooked, given the
|
||||
// number of mobs that spawn as Lycanites when this mod is active it would massively nerf the wizard drops
|
||||
// if we didn't handle this
|
||||
if(event.getEntity().getClass().getCanonicalName().contains("lycanitesmobs"))
|
||||
{
|
||||
WorldServer world = (WorldServer)event.getEntity().getEntityWorld();
|
||||
LootTable table = world.getLootTableManager().getLootTableFromLocation(new ResourceLocation(Wizardry.MODID, "entities/mob_additions"));
|
||||
|
||||
LootContext ctx = new LootContext.Builder(world).withPlayer(player).build();
|
||||
List<ItemStack> stacks = table.generateLootForPools(world.rand, ctx);
|
||||
|
||||
for(ItemStack stack : stacks) {
|
||||
event.getEntity().entityDropItem(stack, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
|
||||
|
||||
if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
|
||||
@@ -444,7 +467,8 @@ public final class WizardryEventHandler {
|
||||
// Wizardry.logger.info("Replaced fall distance {} with effective distance {} based on entity velocity", event.getDistance(), y);
|
||||
// }
|
||||
|
||||
event.setDistance((float)y);
|
||||
// Don't let it increase fall damage beyond vanilla values
|
||||
if(y < event.getDistance()) event.setDistance((float)y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class ClientProxy extends CommonProxy {
|
||||
// Armour Model
|
||||
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
|
||||
|
||||
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style)}). */
|
||||
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style, Object...)}). */
|
||||
private static final int TOOLTIP_WRAP_WIDTH = 140;
|
||||
|
||||
// SECTION Registry
|
||||
@@ -287,8 +287,8 @@ public class ClientProxy extends CommonProxy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){
|
||||
String description = style.getFormattingCode() + I18n.format(key);
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
|
||||
String description = style.getFormattingCode() + I18n.format(key, args);
|
||||
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
|
||||
}
|
||||
|
||||
@@ -434,6 +434,7 @@ public class ClientProxy extends CommonProxy {
|
||||
if(caster instanceof ISpellCaster){
|
||||
if(spell.isContinuous || spell instanceof None){
|
||||
((ISpellCaster)caster).setContinuousSpell(spell);
|
||||
((ISpellCaster)caster).setSpellCounter(spell instanceof None ? 0 : 1);
|
||||
((EntityLiving)caster).setAttackTarget((EntityLivingBase)target);
|
||||
}
|
||||
}
|
||||
@@ -547,7 +548,7 @@ public class ClientProxy extends CommonProxy {
|
||||
data.randomNames = new HashMap<>();
|
||||
data.randomDescriptions = new HashMap<>();
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
// -1 because the none spell isn't included
|
||||
// This is a case where we must use the network ID, not the metadata
|
||||
data.randomNames.put(spell, message.names.get(spell.networkID() - 1));
|
||||
@@ -574,13 +575,6 @@ public class ClientProxy extends CommonProxy {
|
||||
GuiWizardHandbook.updateUnlockStatus(message.showToasts, message.completedAdvancements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.hostID);
|
||||
if(entity instanceof EntityLivingBase) PotionSlowTime.unblockNearbyEntities((EntityLivingBase)entity);
|
||||
else Wizardry.logger.warn("Received a PacketEndSlowTime, but the entity ID did not match any living entity");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
|
||||
|
||||
@@ -9,6 +9,7 @@ import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.item.ItemSpectralBow;
|
||||
import electroblob.wizardry.potion.PotionSlowTime;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
@@ -153,6 +154,7 @@ public final class WizardryClientEventHandler {
|
||||
}
|
||||
|
||||
SpellEmitterData.update(world);
|
||||
PotionSlowTime.cleanUpEntities(world);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@@ -525,30 +525,42 @@ public abstract class ParticleWizardry extends Particle {
|
||||
|
||||
|
||||
// =============================================== Helper Methods ===============================================
|
||||
|
||||
|
||||
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int)} which uses wizardry's mod
|
||||
* ID automatically. */
|
||||
public static ResourceLocation[] generateTextures(String stem, int n){
|
||||
return generateTextures(Wizardry.MODID, stem, n);
|
||||
}
|
||||
|
||||
/** Static helper method that generates an array of n ResourceLocations using the particle file naming convention,
|
||||
* which is the given stem plus an underscore plus the integer index. */
|
||||
public static ResourceLocation[] generateTextures(String stem, int n){
|
||||
public static ResourceLocation[] generateTextures(String modID, String stem, int n){
|
||||
|
||||
ResourceLocation[] textures = new ResourceLocation[n];
|
||||
|
||||
for(int i=0; i<n; i++){
|
||||
textures[i] = new ResourceLocation(Wizardry.MODID, "particle/" + stem + "_" + i);
|
||||
textures[i] = new ResourceLocation(modID, "particle/" + stem + "_" + i);
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int, int)} which uses wizardry's
|
||||
* mod ID automatically. */
|
||||
public static ResourceLocation[][] generateTextures(String stem, int m, int n){
|
||||
return generateTextures(Wizardry.MODID, stem, m, n);
|
||||
}
|
||||
|
||||
/** Static helper method that generates a 2D m x n array of ResourceLocations using the particle file naming
|
||||
* convention, which is the given stem plus an underscore plus the first index, plus an underscore plus the second
|
||||
* index. Useful for animated particles that also pick a random animation strip. */
|
||||
public static ResourceLocation[][] generateTextures(String stem, int m, int n){
|
||||
public static ResourceLocation[][] generateTextures(String modID, String stem, int m, int n){
|
||||
|
||||
ResourceLocation[][] textures = new ResourceLocation[m][n];
|
||||
|
||||
for(int i=0; i<m; i++){
|
||||
for(int j=0; j<n; j++){
|
||||
textures[i][j] = new ResourceLocation(Wizardry.MODID, "particle/" + stem + "_" + i + "_" + j);
|
||||
textures[i][j] = new ResourceLocation(modID, "particle/" + stem + "_" + i + "_" + j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,8 @@ public class CommandCastSpell extends CommandBase {
|
||||
}
|
||||
|
||||
if(spell.isContinuous){
|
||||
|
||||
// We need not query Spell#canBeCastByDispensers since with commands there's no difference between
|
||||
// a spell that can't be cast positionally and one that can be cast positionally but fails
|
||||
if(spell.cast(world, origin.x, origin.y, origin.z, direction, 0, duration, modifiers)){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.COMMAND, spell, world, origin.x, origin.y, origin.z, direction, modifiers));
|
||||
|
||||
@@ -113,7 +113,7 @@ public class CommandDiscoverSpell extends CommandBase {
|
||||
if(server.sendCommandFeedback()) sender.sendMessage(
|
||||
new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.clear", player.getName()));
|
||||
}else if(all){
|
||||
data.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells));
|
||||
data.spellsDiscovered.addAll(Spell.getAllSpells());
|
||||
if(server.sendCommandFeedback()) sender.sendMessage(
|
||||
new TextComponentTranslation("commands." + Wizardry.MODID + ":discoverspell.all", player.getName()));
|
||||
}else{
|
||||
|
||||
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -129,7 +130,7 @@ public class SpellEmitter implements ITickable {
|
||||
nbt.setDouble("z", z);
|
||||
nbt.setInteger("direction", direction.getIndex());
|
||||
nbt.setInteger("duration", duration);
|
||||
nbt.setTag("modifiers", modifiers.toNBT());
|
||||
NBTExtras.storeTagSafely(nbt, "modifiers", modifiers.toNBT());
|
||||
nbt.setInteger("castingTick", castingTick);
|
||||
|
||||
return nbt;
|
||||
|
||||
@@ -7,6 +7,7 @@ import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.None;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
@@ -141,7 +142,7 @@ public abstract class BlockCastingData<T extends TileEntity> implements INBTSeri
|
||||
|
||||
nbt.setInteger("spell", spell.metadata());
|
||||
nbt.setInteger("castingTick", castingTick);
|
||||
nbt.setTag("modifiers", modifiers.toNBT());
|
||||
NBTExtras.storeTagSafely(nbt, "modifiers", modifiers.toNBT());
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package electroblob.wizardry.data;
|
||||
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -89,7 +90,7 @@ public interface IStoredVariable<T> extends IVariable<T> {
|
||||
|
||||
@Override
|
||||
public void write(NBTTagCompound nbt, T value){
|
||||
if(value != null) nbt.setTag(key, serialiser.apply(value));
|
||||
if(value != null) NBTExtras.storeTagSafely(nbt, key, serialiser.apply(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -91,7 +91,7 @@ public class SpellEmitterData extends WorldSavedData {
|
||||
|
||||
@Override
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound compound){
|
||||
compound.setTag("emitters", NBTExtras.listToNBT(emitters, SpellEmitter::toNBT));
|
||||
NBTExtras.storeTagSafely(compound, "emitters", NBTExtras.listToNBT(emitters, SpellEmitter::toNBT));
|
||||
return compound;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.packet.PacketGlyphData;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -43,11 +44,11 @@ public class SpellGlyphData extends WorldSavedData {
|
||||
/** Generates random names and descriptions for any spells which don't already have them. */
|
||||
public void generateGlyphNames(World world){
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
if(!randomNames.containsKey(spell)) randomNames.put(spell, generateRandomName(world.rand));
|
||||
}
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
if(!randomDescriptions.containsKey(spell))
|
||||
randomDescriptions.put(spell, generateRandomDescription(world.rand));
|
||||
}
|
||||
@@ -161,7 +162,7 @@ public class SpellGlyphData extends WorldSavedData {
|
||||
|
||||
NBTTagList tagList = new NBTTagList();
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
// Much like the enchantments tag for items, this stores a list of spell-id-to-name tag pairs
|
||||
// The description is now also included; there's no point in making a second compound tag!
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
@@ -171,7 +172,7 @@ public class SpellGlyphData extends WorldSavedData {
|
||||
tagList.appendTag(tag);
|
||||
}
|
||||
|
||||
nbt.setTag("spellGlyphData", tagList);
|
||||
NBTExtras.storeTagSafely(nbt, "spellGlyphData", tagList);
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@@ -515,12 +515,12 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
|
||||
NBTTagCompound properties = new NBTTagCompound();
|
||||
|
||||
properties.setTag("imbuements", NBTExtras.mapToNBT(this.imbuementDurations,
|
||||
NBTExtras.storeTagSafely(properties, "imbuements", NBTExtras.mapToNBT(this.imbuementDurations,
|
||||
imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new));
|
||||
|
||||
// Mmmmmm Java 8....
|
||||
properties.setTag("allies", NBTExtras.listToNBT(this.allies, NBTUtil::createUUIDTag));
|
||||
properties.setTag("allyNames", NBTExtras.listToNBT(this.allyNames, NBTTagString::new));
|
||||
NBTExtras.storeTagSafely(properties, "allies", NBTExtras.listToNBT(this.allies, NBTUtil::createUUIDTag));
|
||||
NBTExtras.storeTagSafely(properties, "allyNames", NBTExtras.listToNBT(this.allyNames, NBTTagString::new));
|
||||
|
||||
// Might be worth converting this over to WizardryUtilities.listToNBT.
|
||||
int[] spells = new int[this.spellsDiscovered.size()];
|
||||
@@ -531,7 +531,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
}
|
||||
properties.setIntArray("discoveredSpells", spells);
|
||||
|
||||
properties.setTag("recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata())));
|
||||
NBTExtras.storeTagSafely(properties, "recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata())));
|
||||
|
||||
storedVariables.forEach(k -> k.write(properties, this.spellData.get(k)));
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -183,7 +184,7 @@ public class EntityLevitatingBlock extends EntityFallingBlock implements IEntity
|
||||
NBTBase nbtbase = this.tileEntityData.getTag(s);
|
||||
|
||||
if(!"x".equals(s) && !"y".equals(s) && !"z".equals(s)){
|
||||
nbttagcompound.setTag(s, nbtbase.copy());
|
||||
NBTExtras.storeTagSafely(nbttagcompound, s, nbtbase.copy());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class EntityBubble extends EntityMagicConstruct {
|
||||
if(isDarkOrb){
|
||||
|
||||
if(WizardryUtilities.getRider(this) != null
|
||||
&& this.ticksExisted % Spells.entrapment.getProperty(Entrapment.DAMAGE_INTERVAL).intValue() == 0){
|
||||
&& WizardryUtilities.getRider(this).ticksExisted % Spells.entrapment.getProperty(Entrapment.DAMAGE_INTERVAL).intValue() == 0){
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.getRider(this).attackEntityFrom(
|
||||
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package electroblob.wizardry.entity.construct;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
|
||||
@@ -80,8 +80,8 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
|
||||
if(this.world.isRemote && this.ticksExisted % 3 == 0){
|
||||
ParticleBuilder.create(Type.SPARK)
|
||||
.pos(this.posX - 0.5d + rand.nextDouble(), this.posY + 2 * rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble())
|
||||
.spawn(world);
|
||||
.pos(this.posX - 0.5d + rand.nextDouble(), this.posY + 2 * rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble())
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
this.prevPosX = this.posX;
|
||||
@@ -103,39 +103,37 @@ public class EntityHammer extends EntityMagicConstruct {
|
||||
this.rotationPitch = 0;
|
||||
this.spin = false;
|
||||
|
||||
if(this.ticksExisted % Spells.lightning_hammer.getProperty(LightningHammer.ATTACK_INTERVAL).floatValue() == 0){
|
||||
double seekerRange = Spells.lightning_hammer.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
|
||||
double seekerRange = Spells.lightning_hammer.getProperty(Spell.EFFECT_RADIUS).doubleValue();
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX,
|
||||
this.posY + 1, this.posZ, world);
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, this.posX,
|
||||
this.posY + 1, this.posZ, world);
|
||||
int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue();
|
||||
while(targets.size() > maxTargets) targets.remove(targets.size() - 1);
|
||||
|
||||
int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue();
|
||||
while(targets.size() > maxTargets) targets.remove(targets.size() - 1);
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(WizardryUtilities.isLiving(target) && this.isValidTarget(target)
|
||||
&& target.ticksExisted % Spells.lightning_hammer.getProperty(LightningHammer.ATTACK_INTERVAL).floatValue() == 0){
|
||||
|
||||
if(WizardryUtilities.isLiving(target) && this.isValidTarget(target)){
|
||||
if(world.isRemote){
|
||||
|
||||
if(world.isRemote){
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world);
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(posX, posY + height - 0.1, posZ) .target(target).spawn(world);
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height, target.posZ);
|
||||
}
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height, target.posZ);
|
||||
}
|
||||
target.playSound(WizardrySounds.ENTITY_HAMMER_ATTACK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
|
||||
|
||||
target.playSound(WizardrySounds.ENTITY_HAMMER_ATTACK, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
|
||||
float damage = Spells.lightning_hammer.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
float damage = Spells.lightning_hammer.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier;
|
||||
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(
|
||||
this, getCaster(), DamageType.SHOCK), damage);
|
||||
WizardryUtilities.applyStandardKnockback(this, target);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, damage);
|
||||
}
|
||||
if(this.getCaster() != null){
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(
|
||||
this, getCaster(), DamageType.SHOCK), damage);
|
||||
WizardryUtilities.applyStandardKnockback(this, target);
|
||||
}else{
|
||||
target.attackEntityFrom(DamageSource.MAGIC, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class EntityHealAura extends EntityMagicConstruct {
|
||||
target.motionZ = velZ;
|
||||
}
|
||||
|
||||
}else if(target.getHealth() < target.getMaxHealth() && this.ticksExisted % 5 == 0){
|
||||
}else if(target.getHealth() < target.getMaxHealth() && target.ticksExisted % 5 == 0){
|
||||
target.heal(Spells.healing_aura.getProperty(Spell.HEALTH).floatValue() * damageMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ public class EntityAIAttackSpell<T extends EntityLiving & ISpellCaster> extends
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is only called when spell casting starts so ticksInUse is always zero
|
||||
if(spell.cast(attacker.world, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
|
||||
|
||||
if(spell.isContinuous){
|
||||
|
||||
@@ -74,6 +74,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
// Field implementations
|
||||
private List<Spell> spells = new ArrayList<Spell>(4);
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
public EntityEvilWizard(World world){
|
||||
|
||||
@@ -175,6 +176,16 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
public Spell getContinuousSpell(){
|
||||
return this.continuousSpell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAimingError(EnumDifficulty difficulty){
|
||||
@@ -252,7 +263,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
// When right-clicked with a spell book in creative, sets one of the spells to that spell
|
||||
if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){
|
||||
Spell spell = Spell.byMetadata(stack.getItemDamage());
|
||||
if(this.spells.size() >= 4 && spell.canBeCastByNPCs()){
|
||||
if(this.spells.size() >= 4 && spell.canBeCastBy(this, true)){
|
||||
// The set(...) method returns the element that was replaced - neat!
|
||||
player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard",
|
||||
this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(),
|
||||
@@ -269,9 +280,9 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
super.writeEntityToNBT(nbt);
|
||||
nbt.setInteger("element", this.getElement().ordinal());
|
||||
nbt.setInteger("skin", this.textureIndex);
|
||||
nbt.setTag("spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
|
||||
NBTExtras.storeTagSafely(nbt, "spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
|
||||
nbt.setBoolean("hasStructure", this.hasStructure);
|
||||
nbt.setTag("groupUUIDs", NBTExtras.listToNBT(groupUUIDs, NBTUtil::createUUIDTag));
|
||||
NBTExtras.storeTagSafely(nbt, "groupUUIDs", NBTExtras.listToNBT(groupUUIDs, NBTUtil::createUUIDTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -369,7 +380,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
// All wizards know magic missile, even if it is disabled.
|
||||
spells.add(Spells.magic_missile);
|
||||
|
||||
Tier maxTier = EntityWizard.populateSpells(spells, element, hasStructure, 3, rand);
|
||||
Tier maxTier = EntityWizard.populateSpells(this, spells, element, hasStructure, 3, rand);
|
||||
|
||||
// Now done after the spells so it can take the tier into account. For evil wizards this is slightly different;
|
||||
// it picks a random wand which is at least a high enough tier for the spells the wizard has.
|
||||
|
||||
@@ -19,9 +19,6 @@ import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EntityLightningWraith extends EntityBlazeMinion {
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
|
||||
private EntityAIAttackSpell<EntityPhoenix> spellAttackAI = new EntityAIAttackSpell<>(this, AISpeed, 15f, 60, 140);
|
||||
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
private static final List<Spell> attack = Collections.singletonList(Spells.flame_ray);
|
||||
|
||||
@@ -76,6 +77,16 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
|
||||
continuousSpell = spell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRangedAttack(){
|
||||
return true;
|
||||
|
||||
@@ -61,21 +61,6 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
|
||||
return attack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellModifiers getModifiers(){
|
||||
return new SpellModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContinuousSpell(Spell spell){
|
||||
// Doesn't use continuous spells.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEntityAttributes(){
|
||||
super.applyEntityAttributes();
|
||||
|
||||
@@ -59,21 +59,6 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
|
||||
return attack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellModifiers getModifiers(){
|
||||
return new SpellModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContinuousSpell(Spell spell){
|
||||
// Doesn't use continuous spells.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEntityAttributes(){
|
||||
super.applyEntityAttributes();
|
||||
|
||||
@@ -83,6 +83,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
// Field implementations
|
||||
private List<Spell> spells = new ArrayList<Spell>(4);
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
/** A set of the positions of the blocks that are part of this wizard's tower. */
|
||||
private Set<BlockPos> towerBlocks;
|
||||
@@ -187,7 +188,17 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
public Spell getContinuousSpell(){
|
||||
return this.continuousSpell;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAimingError(EnumDifficulty difficulty){
|
||||
// Being more intelligent than skeletons, wizards are a little more accurate.
|
||||
@@ -359,7 +370,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
// When right-clicked with a spell book in creative, sets one of the spells to that spell
|
||||
if(player.isCreative() && stack.getItem() instanceof ItemSpellBook){
|
||||
Spell spell = Spell.byMetadata(stack.getItemDamage());
|
||||
if(this.spells.size() >= 4 && spell.canBeCastByNPCs()){
|
||||
if(this.spells.size() >= 4 && spell.canBeCastBy(this, true)){
|
||||
// The set(...) method returns the element that was replaced - neat!
|
||||
player.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":spell_book.apply_to_wizard",
|
||||
this.getDisplayName(), this.spells.set(rand.nextInt(3) + 1, spell).getNameForTranslationFormatted(),
|
||||
@@ -389,15 +400,15 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
super.writeEntityToNBT(nbt);
|
||||
|
||||
if(this.trades != null){
|
||||
nbt.setTag("trades", this.trades.getRecipiesAsTags());
|
||||
NBTExtras.storeTagSafely(nbt, "trades", this.trades.getRecipiesAsTags());
|
||||
}
|
||||
|
||||
nbt.setInteger("element", this.getElement().ordinal());
|
||||
nbt.setInteger("skin", this.textureIndex);
|
||||
nbt.setTag("spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
|
||||
NBTExtras.storeTagSafely(nbt, "spells", NBTExtras.listToNBT(spells, spell -> new NBTTagInt(spell.metadata())));
|
||||
|
||||
if(this.towerBlocks != null && this.towerBlocks.size() > 0){
|
||||
nbt.setTag("towerBlocks", NBTExtras.listToNBT(this.towerBlocks, NBTUtil::createPosTag));
|
||||
NBTExtras.storeTagSafely(nbt, "towerBlocks", NBTExtras.listToNBT(this.towerBlocks, NBTUtil::createPosTag));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,7 +763,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
// All wizards know magic missile, even if it is disabled.
|
||||
spells.add(Spells.magic_missile);
|
||||
|
||||
Tier maxTier = populateSpells(spells, element, false, 3, rand);
|
||||
Tier maxTier = populateSpells(this, spells, element, false, 3, rand);
|
||||
|
||||
// Now done after the spells so it can take the tier into account.
|
||||
ItemStack wand = new ItemStack(WizardryItems.getWand(maxTier, element));
|
||||
@@ -769,19 +780,21 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
/**
|
||||
* Adds n random spells to the given list. The spells will be of the given element if possible. Extracted as a
|
||||
* separate function since it was the same in both EntityWizard and EntityEvilWizard.
|
||||
*
|
||||
*
|
||||
* @param wizard The wizard whose spells are to be populated.
|
||||
* @param spells The spell list to be populated.
|
||||
* @param e The element that the spells should belong to, or {@link Element#MAGIC} for a random element each time.
|
||||
* @param master Whether to include master spells.
|
||||
* @param n The number of spells to add.
|
||||
* @param random A random number generator to use.
|
||||
* @return The tier of the highest-tier spell that was added to the list.
|
||||
*/
|
||||
static Tier populateSpells(List<Spell> spells, Element e, boolean master, int n, Random random){
|
||||
static Tier populateSpells(final EntityLiving wizard, List<Spell> spells, Element e, boolean master, int n, Random random){
|
||||
|
||||
// This is the tier of the highest tier spell added.
|
||||
Tier maxTier = Tier.NOVICE;
|
||||
|
||||
List<Spell> npcSpells = Spell.getSpells(Spell.npcSpells);
|
||||
List<Spell> npcSpells = Spell.getSpells(s -> s.canBeCastBy(wizard, false));
|
||||
npcSpells.removeIf(s -> !s.applicableForItem(WizardryItems.spell_book));
|
||||
|
||||
for(int i = 0; i < n; i++){
|
||||
|
||||
@@ -19,7 +19,9 @@ import java.util.List;
|
||||
* packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select.
|
||||
* <p></p>
|
||||
* This class also allows Wizardry to do all the syncing necessary for continuous spell casting. All the implementor
|
||||
* needs to do is store the actual fields involved.
|
||||
* needs to do is store the actual fields involved, by implementing {@link ISpellCaster#setContinuousSpell(Spell)},
|
||||
* {@link ISpellCaster#getContinuousSpell()}, {@link ISpellCaster#setSpellCounter(int)}, {@link ISpellCaster#getSpellCounter()}
|
||||
* and {@link ISpellCaster#getModifiers()}.
|
||||
*/
|
||||
/* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla
|
||||
* classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, and
|
||||
@@ -36,10 +38,10 @@ public interface ISpellCaster {
|
||||
*
|
||||
* @return A list of {@link Spell} instances. A random spell from this list will be cast when the entity attacks.
|
||||
* The list will not be modified by the AI class and can therefore be an immutable list. The spells in the
|
||||
* list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true).
|
||||
* list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastBy(net.minecraft.entity.EntityLiving, boolean)} returns true).
|
||||
*/
|
||||
@Nonnull
|
||||
public List<Spell> getSpells();
|
||||
List<Spell> getSpells();
|
||||
|
||||
/**
|
||||
* Called each time the entity attacks to get the modifiers to apply to the spell.
|
||||
@@ -48,31 +50,47 @@ public interface ISpellCaster {
|
||||
* required, pass in an empty {@code SpellModifiers} object.
|
||||
*/
|
||||
@Nonnull
|
||||
public SpellModifiers getModifiers();
|
||||
default SpellModifiers getModifiers(){
|
||||
return new SpellModifiers(); // May seem wasteful but this should never be called so it doesn't matter
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors
|
||||
* should simply store this as a private field and return it here. Will be synced by the AI class, but whether it is
|
||||
* saved to NBT is up to you. If the implementing class does not deal with continuous spells, just return
|
||||
* {@link Spells#none}. If the implementing class only ever uses one continuous spell, do <b>not</b> just return
|
||||
* that spell; the field must still be stored.
|
||||
* saved to NBT is up to you. If the implementing class only ever uses one continuous spell, do <b>not</b> just
|
||||
* return that spell; the field must still be stored.
|
||||
*/
|
||||
@Nonnull
|
||||
public Spell getContinuousSpell();
|
||||
default Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should
|
||||
* simply store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved
|
||||
* to NBT is up to you. If the implementing class does not deal with continuous spells, leave this method blank.
|
||||
*/
|
||||
public void setContinuousSpell(Spell spell);
|
||||
default void setContinuousSpell(Spell spell){
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/** Returns the number of ticks the current spell has been cast for. Implementors should simply store this as a
|
||||
* private field and return it here. This is only used client-side. */
|
||||
default int getSpellCounter(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Sets the number of ticks the current spell has been cast for. Implementors should simply store this as a
|
||||
* private field and assign it here. This is only used client-side. */
|
||||
default void setSpellCounter(int count){
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aiming arror for the given difficulty, used in projectile spells. Defaults to the values used by
|
||||
* Returns the aiming error for the given difficulty, used in projectile spells. Defaults to the values used by
|
||||
* skeletons, which are: Easy - 10, Normal - 6, Hard - 2, Peaceful - 10 (rarely used).
|
||||
*/
|
||||
// This is what default methods are actually intended for!
|
||||
public default int getAimingError(EnumDifficulty difficulty) {
|
||||
default int getAimingError(EnumDifficulty difficulty) {
|
||||
return WizardryUtilities.getDefaultAimingError(difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,15 @@ import electroblob.wizardry.util.ParticleBuilder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.projectile.EntitySmallFireball;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
@@ -131,6 +135,54 @@ public class EntityMagicFireball extends EntityMagicProjectile {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCollidedWith(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCollisionBorderSize(){
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attackEntityFrom(DamageSource source, float amount){
|
||||
|
||||
if(this.isEntityInvulnerable(source)){
|
||||
return false;
|
||||
|
||||
}else{
|
||||
|
||||
this.markVelocityChanged();
|
||||
|
||||
if(source.getTrueSource() != null){
|
||||
|
||||
Vec3d vec3d = source.getTrueSource().getLookVec();
|
||||
|
||||
if(vec3d != null){
|
||||
|
||||
double speed = MathHelper.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ);
|
||||
|
||||
this.motionX = vec3d.x * speed;
|
||||
this.motionY = vec3d.y * speed;
|
||||
this.motionZ = vec3d.z * speed;
|
||||
|
||||
this.lifetime = 160;
|
||||
|
||||
}
|
||||
|
||||
if(source.getTrueSource() instanceof EntityLivingBase){
|
||||
this.setCaster((EntityLivingBase)source.getTrueSource());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLifetime(int lifetime){
|
||||
this.lifetime = lifetime;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public abstract class EntityMagicProjectile extends EntityThrowable implements I
|
||||
double dy = !this.hasNoGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0f) - this.posY
|
||||
: target.getEntityBoundingBox().minY + (double)(target.height / 2.0f) - this.posY;
|
||||
double dz = target.posZ - caster.posZ;
|
||||
double horizontalDistance = (double)MathHelper.sqrt(dx * dx + dz * dz);
|
||||
double horizontalDistance = MathHelper.sqrt(dx * dx + dz * dz);
|
||||
|
||||
if(horizontalDistance >= 1.0E-7D){
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
@@ -66,12 +65,11 @@ public class EntitySmokeBomb extends EntityBomb {
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(target != this.getThrower()){
|
||||
// Gives the target blindness if it is a player, mind trick otherwise (since this has the desired
|
||||
// Gives the target blindness, and mind trick if it's not a player (since this has the desired
|
||||
// effect of preventing targeting)
|
||||
if(target instanceof EntityPlayer){
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
|
||||
}else if(target instanceof EntityLiving){
|
||||
// New AI
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
|
||||
|
||||
if(target instanceof EntityLiving){
|
||||
((EntityLiving)target).setAttackTarget(null);
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, duration, 0));
|
||||
}
|
||||
|
||||
@@ -61,11 +61,10 @@ public class ItemArcaneTome extends Item {
|
||||
|
||||
Tier tier = Tier.values()[stack.getItemDamage()];
|
||||
Tier tier2 = Tier.values()[stack.getItemDamage() - 1];
|
||||
|
||||
tooltip.add(tier.getDisplayNameWithFormatting());
|
||||
tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":arcane_tome.desc1",
|
||||
tier2.getDisplayNameWithFormatting()));
|
||||
tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":arcane_tome.desc2",
|
||||
tier.getDisplayNameWithFormatting() + "\u00A77"));
|
||||
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc",
|
||||
tier2.getDisplayNameWithFormatting() + "\u00A77", tier.getDisplayNameWithFormatting() + "\u00A77");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,10 +48,14 @@ public class ItemBlankScroll extends Item implements IWorkbenchItem {
|
||||
int cost = spell.getCost() * centre.getStack().getCount();
|
||||
// Continuous spell scrolls require enough mana to cast them for the duration defined in ItemScroll.
|
||||
if(spell.isContinuous) cost *= ItemScroll.CASTING_TIME / 20;
|
||||
|
||||
int manaPerItem = Constants.MANA_PER_CRYSTAL;
|
||||
if(crystals.getStack().getItem() == WizardryItems.crystal_shard) manaPerItem = Constants.MANA_PER_SHARD;
|
||||
if(crystals.getStack().getItem() == WizardryItems.grand_crystal) manaPerItem = Constants.GRAND_CRYSTAL_MANA;
|
||||
|
||||
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL > cost){
|
||||
if(crystals.getStack().getCount() * manaPerItem > cost){
|
||||
// Rounds up to the nearest whole crystal
|
||||
crystals.decrStackSize(cost / Constants.MANA_PER_CRYSTAL + 1);
|
||||
crystals.decrStackSize(cost / manaPerItem + 1);
|
||||
centre.putStack(new ItemStack(WizardryItems.scroll, centre.getStack().getCount(), spell.metadata()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
@@ -20,7 +19,6 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -52,7 +50,7 @@ public class ItemScroll extends Item implements ISpellCastingItem {
|
||||
|
||||
if(tab == WizardryTabs.SPELLS){
|
||||
|
||||
List<Spell> spells = Spell.getSpells(Spell.allSpells);
|
||||
List<Spell> spells = Spell.getAllSpells();
|
||||
spells.removeIf(s -> !s.applicableForItem(this));
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ItemSpellBook extends Item {
|
||||
|
||||
if(tab == WizardryTabs.SPELLS){
|
||||
|
||||
List<Spell> spells = Spell.getSpells(Spell.allSpells);
|
||||
List<Spell> spells = Spell.getAllSpells();
|
||||
spells.removeIf(s -> !s.applicableForItem(this));
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
@@ -27,7 +27,10 @@ import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -397,7 +400,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.WAND, spell, caster, modifiers, castingTick))) return false;
|
||||
}
|
||||
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST));
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f); // Weird floaty rounding
|
||||
|
||||
// As of wizardry 4.2 mana cost is only divided over two intervals each second
|
||||
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
|
||||
@@ -433,7 +436,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
|
||||
caster.setActiveHand(hand);
|
||||
|
||||
// Mana cost
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST));
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f); // Weird floaty rounding
|
||||
// As of wizardry 4.2 mana cost is only divided over two intervals each second
|
||||
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
|
||||
|
||||
@@ -494,7 +497,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
|
||||
|
||||
int castingTick = stack.getMaxItemUseDuration() - timeLeft; // Might as well include this
|
||||
|
||||
int cost = getDistributedCost((int)(spell.getCost() * modifiers.get(SpellModifiers.COST)), castingTick);
|
||||
int cost = getDistributedCost((int)(spell.getCost() * modifiers.get(SpellModifiers.COST) + 0.1f), castingTick);
|
||||
|
||||
// Still need to check there's enough mana or the spell will finish twice, since running out of mana is
|
||||
// handled separately.
|
||||
|
||||
@@ -330,7 +330,7 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
|
||||
IAttributeInstance attribute = event.getEntityLiving().getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
double followRange = attribute == null ? 16 : attribute.getAttributeValue();
|
||||
if(event.getTarget().isSneaking()) followRange *= 0.8;
|
||||
float f = armourPieces / ((EntityPlayer)event.getTarget()).inventory.armorInventory.size();
|
||||
float f = (float)armourPieces / ((EntityPlayer)event.getTarget()).inventory.armorInventory.size();
|
||||
if(f < 0.1F) f = 0.1F;
|
||||
followRange *= (double)(0.7F * f);
|
||||
// Don't need to worry about the isSuitableTarget check since it must already have been checked to get this far
|
||||
|
||||
@@ -62,7 +62,7 @@ public class BehaviourSpellDispense extends BehaviorDispenseOptional {
|
||||
if(world.isSideSolid(source.getBlockPos().offset(direction), direction.getOpposite())) return stack;
|
||||
|
||||
// If the scroll can never be cast by a dispenser, it should be dispensed as an item.
|
||||
if(!spell.canBeCastByDispensers()) return super.dispenseStack(source, stack);
|
||||
if(!spell.canBeCastBy(source.getBlockTileEntity())) return super.dispenseStack(source, stack);
|
||||
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package electroblob.wizardry.packet;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
|
||||
|
||||
/** <b>[Server -> Client]</b> This packet is sent when the slow time potion effect expires or is removed from an
|
||||
* entity to unblock all nearby entities' updates. */
|
||||
public class PacketEndSlowTime implements IMessageHandler<PacketEndSlowTime.Message, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(Message message, MessageContext ctx){
|
||||
|
||||
// Just to make sure that the side is correct
|
||||
if(ctx.side.isClient()){
|
||||
net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(() -> Wizardry.proxy.handleEndSlowTimePacket(message));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Message implements IMessage {
|
||||
|
||||
public int hostID;
|
||||
|
||||
// This constructor is required otherwise you'll get errors (used somewhere in fml through reflection)
|
||||
public Message(){
|
||||
}
|
||||
|
||||
public Message(Entity host){
|
||||
this.hostID = host.getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf){
|
||||
this.hostID = buf.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf){
|
||||
buf.writeInt(hostID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ public class WizardryPacketHandler {
|
||||
registerMessage(PacketSpellProperties.class, PacketSpellProperties.Message.class);
|
||||
registerMessage(PacketSyncAdvancements.class, PacketSyncAdvancements.Message.class);
|
||||
registerMessage(PacketRequestAdvancementSync.class, PacketRequestAdvancementSync.Message.class);
|
||||
registerMessage(PacketEndSlowTime.class, PacketEndSlowTime.Message.class);
|
||||
registerMessage(PacketResurrection.class, PacketResurrection.Message.class);
|
||||
registerMessage(PacketCastSpellAtPos.class, PacketCastSpellAtPos.Message.class);
|
||||
registerMessage(PacketEmitterData.class, PacketEmitterData.Message.class);
|
||||
|
||||
@@ -2,6 +2,7 @@ package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -40,7 +41,7 @@ public class PotionContainment extends PotionMagicEffect {
|
||||
|
||||
// Initialise the containment position to the entity's position if it wasn't set already
|
||||
if(!target.getEntityData().hasKey(ENTITY_TAG)){
|
||||
target.getEntityData().setTag(ENTITY_TAG, NBTUtil.createPosTag(new BlockPos(target.getPositionVector().subtract(0.5, 0.5, 0.5))));
|
||||
NBTExtras.storeTagSafely(target.getEntityData(), ENTITY_TAG, NBTUtil.createPosTag(new BlockPos(target.getPositionVector().subtract(0.5, 0.5, 0.5))));
|
||||
}
|
||||
|
||||
Vec3d origin = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(target.getEntityData().getCompoundTag(ENTITY_TAG)));
|
||||
|
||||
@@ -2,8 +2,6 @@ package electroblob.wizardry.potion;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketEndSlowTime;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
@@ -17,11 +15,15 @@ import net.minecraft.entity.IProjectile;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.living.PotionEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
@@ -29,6 +31,8 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
|
||||
|
||||
// FIXME: Minecarts with entities in them (and, I suspect, any other ridden entities) go crazy when time-slowed
|
||||
|
||||
public static final String NBT_KEY = "time_slowed";
|
||||
|
||||
public PotionSlowTime(boolean isBadEffect, int liquidColour){
|
||||
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icon_slow_time.png"));
|
||||
this.setPotionName("potion." + Wizardry.MODID + ":slow_time");
|
||||
@@ -61,6 +65,8 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
|
||||
|
||||
for(Entity entity : targetsInRange){
|
||||
|
||||
entity.getEntityData().setBoolean(NBT_KEY, true);
|
||||
|
||||
// If time is stopped, block all updates; otherwise block all updates except every [interval] ticks
|
||||
entity.updateBlocked = stopTime || host.ticksExisted % interval != 0;
|
||||
|
||||
@@ -130,6 +136,29 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through every entity in the given world and does the following:<br>
|
||||
* 1. Checks if they have the slow time NBT tag<br>
|
||||
* 2. If so, scans the area nearby for players or NPCs with the slow time effect<br>
|
||||
* 3. If none are found, removes the slow time NBT tag and unblocks the entity's updates
|
||||
*/
|
||||
public static void cleanUpEntities(World world){
|
||||
// Had trouble with accessing loadedTileEntityList from tick events causing random CMEs so I'm making a
|
||||
// copy of this too just in case
|
||||
List<Entity> loadedEntityList = new ArrayList<>(world.loadedEntityList);
|
||||
|
||||
for(Entity entity : loadedEntityList){
|
||||
if(entity.getEntityData().getBoolean(NBT_KEY)){
|
||||
// Currently only players can cast slow time, but you could apply the effect to NPCs with commands
|
||||
List<EntityLivingBase> nearby = WizardryUtilities.getEntitiesWithinRadius(getEffectRadius(), entity.posX, entity.posY, entity.posZ, entity.world, EntityLivingBase.class);
|
||||
if(nearby.stream().noneMatch(e -> e.isPotionActive(WizardryPotions.slow_time))){
|
||||
entity.getEntityData().removeTag(NBT_KEY);
|
||||
entity.updateBlocked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
@@ -150,23 +179,16 @@ public class PotionSlowTime extends PotionMagicEffect implements ISyncedPotion {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionExpiryEvent(PotionEvent.PotionExpiryEvent event){
|
||||
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){
|
||||
unblockNearbyEntities(event.getEntityLiving());
|
||||
if(!event.getEntity().world.isRemote){
|
||||
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
|
||||
}
|
||||
}
|
||||
public static void tick(TickEvent.WorldTickEvent event){
|
||||
if(!event.world.isRemote && event.phase == TickEvent.Phase.END) cleanUpEntities(event.world);
|
||||
}
|
||||
|
||||
// We still need this as well as tick events because the player hasn't moved anywhere, they just logged out
|
||||
// In fact, it won't really matter since the tick event fixes it on login anyway, but if the mod is uninstalled or
|
||||
// something else weird happens...
|
||||
@SubscribeEvent
|
||||
public static void onPotionRemoveEvent(PotionEvent.PotionRemoveEvent event){
|
||||
if(event.getPotionEffect() != null && event.getPotionEffect().getPotion() == WizardryPotions.slow_time){
|
||||
unblockNearbyEntities(event.getEntityLiving());
|
||||
if(!event.getEntity().world.isRemote){
|
||||
WizardryPacketHandler.net.sendToDimension(new PacketEndSlowTime.Message(event.getEntityLiving()), event.getEntity().dimension);
|
||||
}
|
||||
}
|
||||
public static void onPlayerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event){
|
||||
if(event.player.updateBlocked) event.player.updateBlocked = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import electroblob.wizardry.entity.construct.*;
|
||||
import electroblob.wizardry.entity.living.*;
|
||||
import electroblob.wizardry.entity.projectile.*;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import net.minecraft.entity.projectile.EntitySnowball;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -251,7 +252,7 @@ public final class Spells {
|
||||
}.addProperties(Spell.DAMAGE).soundValues(1, 1.4f, 0.4f));
|
||||
registry.register(new Ignite());
|
||||
registry.register(new Freeze());
|
||||
registry.register(new Snowball());
|
||||
registry.register(new SpellThrowable<>("snowball", EntitySnowball::new).npcSelector((e, o) -> o).soundValues(0.5f, 0.4f, 0.2f)); // Let's spare wizards the pain of the snowball spell
|
||||
registry.register(new Arc());
|
||||
registry.register(new SpellProjectile<>("thunderbolt", EntityThunderbolt::new).addProperties(Spell.DAMAGE, EntityThunderbolt.KNOCKBACK_STRENGTH).soundValues(0.8f, 0.9f, 0.2f));
|
||||
registry.register(new SummonZombie());
|
||||
@@ -321,7 +322,7 @@ public final class Spells {
|
||||
registry.register(new SpellProjectile<>("darkness_orb", EntityDarknessOrb::new).addProperties(Spell.DAMAGE, Spell.EFFECT_DURATION, Spell.EFFECT_STRENGTH).soundValues(0.5f, 0.4f, 0.2f));
|
||||
registry.register(new ShadowWard());
|
||||
registry.register(new Decay());
|
||||
registry.register(new SpellBuff("water_breathing", 0.3f, 0.3f, 1, () -> MobEffects.WATER_BREATHING){ @Override public boolean canBeCastByNPCs(){ return false; } }.soundValues(0.7f, 1.2f, 0.4f));
|
||||
registry.register(new SpellBuff("water_breathing", 0.3f, 0.3f, 1, () -> MobEffects.WATER_BREATHING).npcSelector((e, o) -> false).soundValues(0.7f, 1.2f, 0.4f));
|
||||
registry.register(new Tornado());
|
||||
registry.register(new Glide());
|
||||
registry.register(new SummonSpiritHorse());
|
||||
@@ -372,7 +373,7 @@ public final class Spells {
|
||||
registry.register(new Intimidate());
|
||||
registry.register(new Banish());
|
||||
registry.register(new SixthSense());
|
||||
registry.register(new SpellBuff("darkvision", 0, 0.4f, 0.7f, () -> MobEffects.NIGHT_VISION){ @Override public boolean canBeCastByNPCs(){ return false; } }.soundValues(0.7f, 1.2f, 0.4f));
|
||||
registry.register(new SpellBuff("darkvision", 0, 0.4f, 0.7f, () -> MobEffects.NIGHT_VISION).npcSelector((e, o) -> false).soundValues(0.7f, 1.2f, 0.4f));
|
||||
registry.register(new Clairvoyance());
|
||||
registry.register(new PocketWorkbench());
|
||||
registry.register(new ImbueWeapon());
|
||||
|
||||
@@ -5,7 +5,6 @@ import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.loot.RandomSpell;
|
||||
import electroblob.wizardry.loot.WizardSpell;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.storage.loot.*;
|
||||
|
||||
@@ -133,10 +133,17 @@ public final class WizardrySounds {
|
||||
public static final SoundEvent MISC_PAGE_TURN = createSound("misc.page_turn");
|
||||
public static final SoundEvent MISC_FREEZE = createSound("misc.freeze");
|
||||
|
||||
/** Trick borrowed from the Twilight Forest, makes things neater. */
|
||||
// Trick borrowed from the Twilight Forest, makes things neater.
|
||||
|
||||
/** Overload for {@link WizardrySounds#createSound(String, String)} which assigns wizardry's mod ID automatically. */
|
||||
public static SoundEvent createSound(String name){
|
||||
return createSound(Wizardry.MODID, name);
|
||||
}
|
||||
|
||||
/** Creates a sound with the given name, to be read from {@code assets/[modID]/sounds.json}. */
|
||||
public static SoundEvent createSound(String modID, String name){
|
||||
// All the setRegistryName methods delegate to this one, it doesn't matter which you use.
|
||||
return new SoundEvent(new ResourceLocation(Wizardry.MODID, name)).setRegistryName(name);
|
||||
return new SoundEvent(new ResourceLocation(modID, name)).setRegistryName(name);
|
||||
}
|
||||
|
||||
// For some reason, sound events seem to work even when they aren't registered, without even so much as a warning.
|
||||
@@ -251,7 +258,7 @@ public final class WizardrySounds {
|
||||
event.getRegistry().register(MISC_PAGE_TURN);
|
||||
event.getRegistry().register(MISC_FREEZE);
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
for(Spell spell : Spell.getAllSpells()){
|
||||
event.getRegistry().registerAll(spell.getSounds());
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,13 @@ import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -32,9 +34,9 @@ public class ArcaneLock extends SpellRay {
|
||||
|
||||
@Override public boolean requiresPacket(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers(){ return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -136,7 +136,7 @@ public class Blink extends Spell {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.monster.EntityZombie;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -21,6 +22,7 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.pathfinding.Path;
|
||||
import net.minecraft.pathfinding.PathNodeType;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
@@ -44,8 +46,8 @@ public class Clairvoyance extends Spell {
|
||||
WizardData.registerStoredVariables(LOCATION_KEY, DIMENSION_KEY);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -3,6 +3,7 @@ package electroblob.wizardry.spell;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import electroblob.wizardry.item.IConjuredItem;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -40,7 +41,7 @@ public class ConjureArmour extends SpellConjuration {
|
||||
armour = new ItemStack(SPECTRAL_ARMOUR_MAP.get(slot));
|
||||
IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade));
|
||||
// Sets a blank "ench" tag to trick the renderer into showing the enchantment effect on the armour model
|
||||
armour.getTagCompound().setTag("ench", new NBTTagList());
|
||||
NBTExtras.storeTagSafely(armour.getTagCompound(), "ench", new NBTTagList());
|
||||
caster.setItemStackToSlot(slot, armour);
|
||||
flag = true;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -45,9 +47,9 @@ public class CurseOfSoulbinding extends SpellRay {
|
||||
WizardData.registerStoredVariables(TARGETS_KEY);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
// You can't damage a dispenser so this would be nonsense!
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -21,7 +21,7 @@ public class Decoy extends Spell {
|
||||
addProperties(DECOY_LIFETIME, MOB_TRICK_CHANCE);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -85,7 +85,7 @@ public class DragonFireball extends Spell {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ public class EmpoweringPresence extends Spell {
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Empowerment stacks extra potency on top of the existing potency.
|
||||
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)){
|
||||
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)
|
||||
&& !(event.getSpell() instanceof EmpoweringPresence)){ // Prevent exponential empowerment stacking!
|
||||
|
||||
float potency = 1 + Constants.EMPOWERMENT_POTENCY_PER_LEVEL
|
||||
* (event.getCaster().getActivePotionEffect(WizardryPotions.empowerment).getAmplifier() + 1);
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -29,8 +30,8 @@ public class ForestOfThorns extends Spell {
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
@Override public boolean canBeCastByDispensers(){ return true; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Glide extends Spell {
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
@@ -58,12 +59,12 @@ public class Grapple extends Spell {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
return super.canBeCastByNPCs();
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
public boolean canBeCastBy(TileEntityDispenser dispenser){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,14 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityTNTPrimed;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -41,8 +43,8 @@ public class GreaterTelekinesis extends SpellRay {
|
||||
this.soundValues(0.8f, 1, 0.2f);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
|
||||
@@ -10,6 +10,7 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.*;
|
||||
import net.minecraft.entity.passive.*;
|
||||
@@ -52,7 +53,7 @@ public class Metamorphosis extends SpellRay {
|
||||
this.soundValues(0.5f, 1f, 0);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -43,8 +44,8 @@ public class MindControl extends SpellRay {
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -8,6 +8,7 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
@@ -28,7 +29,7 @@ public class Poison extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import electroblob.wizardry.packet.PacketPossession;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -43,6 +44,7 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.potion.PotionUtils;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -123,8 +125,8 @@ public class Possession extends SpellRay {
|
||||
addProperties(EFFECT_DURATION, CRITICAL_HEALTH);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
@@ -267,7 +269,7 @@ public class Possession extends SpellRay {
|
||||
// Inventory and items
|
||||
|
||||
if(possessor.getEntityData() != null){
|
||||
possessor.getEntityData().setTag(INVENTORY_NBT_KEY, possessor.inventory.writeToNBT(new NBTTagList()));
|
||||
NBTExtras.storeTagSafely(possessor.getEntityData(), INVENTORY_NBT_KEY, possessor.inventory.writeToNBT(new NBTTagList()));
|
||||
}
|
||||
|
||||
possessor.inventory.clear();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -17,7 +18,7 @@ public class ReplenishHunger extends SpellBuff {
|
||||
addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
@@ -25,6 +25,8 @@ public class Resurrection extends Spell {
|
||||
|
||||
public static final String WAIT_TIME = "wait_time";
|
||||
|
||||
private static final String POTION_CORE_FIX_NBT_KEY = "Potion Core - Health Fix";
|
||||
|
||||
public Resurrection(){
|
||||
super("resurrection", EnumAction.NONE, false);
|
||||
addProperties(EFFECT_RADIUS, WAIT_TIME);
|
||||
@@ -90,6 +92,8 @@ public class Resurrection extends Spell {
|
||||
player.experience = 0;
|
||||
player.experienceLevel = 0;
|
||||
player.experienceTotal = 0;
|
||||
// Not sure what potion core is 'fixing' but it breaks my resurrection, so let's unfix it!
|
||||
player.getEntityData().removeTag(POTION_CORE_FIX_NBT_KEY);
|
||||
|
||||
if(player.world.isRemote){
|
||||
ParticleBuilder.spawnHealParticles(player.world, player);
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -28,7 +29,7 @@ public class Reversal extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
public boolean canBeCastBy(TileEntityDispenser dispenser){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -17,7 +18,7 @@ public class Satiety extends SpellBuff {
|
||||
addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -12,6 +13,7 @@ import net.minecraft.entity.projectile.EntityShulkerBullet;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -29,9 +31,9 @@ public class ShulkerBullet extends Spell {
|
||||
addProperties(RANGE);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
@Override public boolean canBeCastBy(EntityLiving npc, boolean override){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers(){ return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser){ return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
@@ -89,7 +91,7 @@ public class ShulkerBullet extends Spell {
|
||||
targetTag.setInteger("X", pos.getX());
|
||||
targetTag.setInteger("Y", pos.getY());
|
||||
targetTag.setInteger("Z", pos.getZ());
|
||||
nbt.setTag("Target", targetTag);
|
||||
NBTExtras.storeTagSafely(nbt, "Target", targetTag);
|
||||
bullet.readFromNBT(nbt); // LOL I just modified private fields without reflection
|
||||
|
||||
world.spawnEntity(bullet);
|
||||
|
||||
@@ -3,7 +3,9 @@ package electroblob.wizardry.spell;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
@@ -35,12 +37,12 @@ public class SlowTime extends SpellBuff {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
public boolean canBeCastBy(TileEntityDispenser dispenser){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntitySnowball;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Snowball extends Spell {
|
||||
|
||||
public Snowball(){
|
||||
super("snowball", EnumAction.NONE, false);
|
||||
addProperties(RANGE);
|
||||
soundValues(0.5f, 0.4f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
// Trajectory calculation - see SpellProjectile for a more detailed explanation
|
||||
float g = 0.03f;
|
||||
float launchHeight = caster.getEyeHeight();
|
||||
float range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
float velocity = MathHelper.sqrt(MathHelper.sqrt(g*g * (launchHeight*launchHeight + range*range)) - g*launchHeight);
|
||||
|
||||
EntitySnowball snowball = new EntitySnowball(world, caster);
|
||||
snowball.shoot(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, velocity, 1.0f);
|
||||
world.spawnEntity(snowball);
|
||||
}
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -42,6 +43,7 @@ import net.minecraftforge.registries.IForgeRegistryEntry;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -149,6 +151,17 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
/** The pitch variation of the sound played when this spell is cast. Defaults to 0. */
|
||||
protected float pitchVariation = 0;
|
||||
|
||||
// The following two fields are supposed to eliminate the need for boilerplate classes in spell packs.
|
||||
// As an example, spells in the twilight forest spell pack only appear on custom spell book and scroll items and
|
||||
// NPCs can only cast them if they spawned in the twilight forest. Without these convenience fields, spells that
|
||||
// would not otherwise require their own classes have to have one just so they can override those two behaviours.
|
||||
|
||||
/** List of items for which this spell is applicable (used by default behaviour of {@link Spell#applicableForItem(Item)}). */
|
||||
protected Item[] applicableItems;
|
||||
/** Predicate that specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (used by
|
||||
* default behaviour of {@link Spell#canBeCastBy(EntityLiving, boolean)}). */
|
||||
protected BiPredicate<EntityLiving, Boolean> npcSelector; // Kinda ugly but it's better than boilerplate classes
|
||||
|
||||
private static int nextSpellId = 0;
|
||||
/** The spell's integer ID, mainly used for networking. */
|
||||
// This was added after I learnt the hard way why you can't assume Forge's registry IDs are sequential...
|
||||
@@ -186,6 +199,8 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
this.icon = new ResourceLocation(modID, "textures/spells/" + name + ".png");
|
||||
this.sounds = createSounds();
|
||||
this.id = nextSpellId++;
|
||||
this.items(WizardryItems.spell_book, WizardryItems.scroll);
|
||||
this.npcSelector((e, o) -> canBeCastByNPCs()); // Fallback to old behaviour until we remove it entirely
|
||||
}
|
||||
|
||||
// ========================================= Initialisation methods ===========================================
|
||||
@@ -204,7 +219,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
* @see Spell#playSound(World, double, double, double, int, int, SpellModifiers, String...)
|
||||
*/
|
||||
protected SoundEvent[] createSounds(){
|
||||
return new SoundEvent[]{WizardrySounds.createSound("spell." + this.getRegistryName().getPath())};
|
||||
return new SoundEvent[]{WizardrySounds.createSound(this.getRegistryName().getNamespace(), "spell." + this.getRegistryName().getPath())};
|
||||
}
|
||||
|
||||
// Note 1: The aim here is conciseness. Keeping the identifiers in the spell classes means we don't usually have to
|
||||
@@ -294,7 +309,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
* @see Spell#createSoundsWithSuffixes(String[])
|
||||
*/
|
||||
public final SoundEvent createSoundWithSuffix(String suffix){
|
||||
return WizardrySounds.createSound("spell." + this.getRegistryName().getPath() + "." + suffix);
|
||||
return WizardrySounds.createSound(this.getRegistryName().getNamespace(), "spell." + this.getRegistryName().getPath() + "." + suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +369,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
* work if the caster is on full health).
|
||||
* <p></p>
|
||||
* This method is intended for use by NPCs (see {@link EntityWizard}) so that they can cast spells. Override it if
|
||||
* you want a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastByNPCs()} to
|
||||
* you want a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastBy(EntityLiving, boolean)} to
|
||||
* return true to allow wizards to select the spell. For some spells, this method may well be exactly the same as
|
||||
* the regular cast method; for others it won't be - for example, projectile-based spells are normally done using
|
||||
* the player's look vector, but NPCs need to use a target-based method instead.
|
||||
@@ -390,7 +405,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
* won't work if the caster is on full health).
|
||||
* <p></p>
|
||||
* This method is intended for use by dispensers and command blocks so that they can cast spells. Override it if
|
||||
* you want a spell to be cast by dispensers. Note that you must also override {@link Spell#canBeCastByDispensers()} to
|
||||
* you want a spell to be cast by dispensers. Note that you must also override {@link Spell#canBeCastBy(TileEntityDispenser)} to
|
||||
* return true to allow dispensers to select the spell. For some spells, this method may well be exactly the same as
|
||||
* the regular cast method; for others it won't be - for example, projectile-based spells are normally done using
|
||||
* the player's look vector, but dispensers need to use a facing-based method instead.
|
||||
@@ -446,20 +461,50 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
public void finishCasting(World world, @Nullable EntityLivingBase caster, double x, double y, double z,
|
||||
@Nullable EnumFacing direction, int duration, SpellModifiers modifiers){}
|
||||
|
||||
/**
|
||||
* Whether the given entity can cast this spell. If you have overridden
|
||||
* {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override
|
||||
* this to return true (either always or under certain circumstances), or alternatively assign an NPC selector via
|
||||
* {@link Spell#npcSelector(BiPredicate)} (recommended for general spell classes).
|
||||
* @param npc The entity to query.
|
||||
* @param override True if a player in creative mode is assigning this spell to the given entity, false otherwise.
|
||||
* Usually this means situational conditions should be ignored.
|
||||
*/
|
||||
// We could make this final and force everyone to move over to the predicate system, but for particularly complex
|
||||
// behaviour (i.e. several lines of code) it gets too ugly, and then you end up moving the contents of the predicate
|
||||
// to a static method anyway and referring to it via method reference... so we may as well leave people the option.
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return npcSelector.test(npc, override);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether NPCs such as wizards can cast this spell. If you have overridden
|
||||
* {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override
|
||||
* this to return true.
|
||||
* @deprecated Use the entity-sensitive version {@link Spell#canBeCastBy(EntityLiving, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canBeCastByNPCs(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given dispenser can cast this spell. If you have overridden
|
||||
* {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this
|
||||
* to return true (either always or under certain circumstances).
|
||||
* @param dispenser The dispenser to query.
|
||||
*/
|
||||
public boolean canBeCastBy(TileEntityDispenser dispenser){
|
||||
return canBeCastByDispensers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether dispensers can cast this spell. If you have overridden
|
||||
* {@link Spell#cast(World, double, double, double, EnumFacing, int, int, SpellModifiers)}, you should override this
|
||||
* to return true.
|
||||
* @deprecated Use the tileentity-sensitive version {@link Spell#canBeCastBy(TileEntityDispenser)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canBeCastByDispensers(){
|
||||
return false;
|
||||
}
|
||||
@@ -561,26 +606,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
return properties.getBaseValue(identifier);
|
||||
}
|
||||
|
||||
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
|
||||
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
|
||||
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
|
||||
* false if the spell is completely disabled in all contexts. */
|
||||
public final boolean isEnabled(SpellProperties.Context... contexts){
|
||||
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
|
||||
}
|
||||
|
||||
/** Sets whether the spell is enabled or not. */
|
||||
public final void setEnabled(boolean isEnabled){
|
||||
this.enabled = isEnabled;
|
||||
}
|
||||
|
||||
/** Returns true if the given item has a variant for this spell. By default, returns true if the given item is
|
||||
* either {@link WizardryItems#spell_book} or {@link WizardryItems#scroll}. Override to give the spell a special
|
||||
* type of book or scroll. */
|
||||
public boolean applicableForItem(Item item){
|
||||
return item == WizardryItems.spell_book || item == WizardryItems.scroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". <b>This should
|
||||
* only be used for translation purposes.</b>
|
||||
@@ -767,6 +792,51 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================= Misc methods ===============================================
|
||||
|
||||
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
|
||||
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
|
||||
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
|
||||
* false if the spell is completely disabled in all contexts. */
|
||||
public final boolean isEnabled(SpellProperties.Context... contexts){
|
||||
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
|
||||
}
|
||||
|
||||
/** Sets whether the spell is enabled or not. */
|
||||
public final void setEnabled(boolean isEnabled){
|
||||
this.enabled = isEnabled;
|
||||
}
|
||||
|
||||
/** Returns true if the given item has a variant for this spell. By default, returns true if the given item is
|
||||
* in this spell's {@link Spell#applicableItems} list (set using {@link Spell#items(Item...)}). Override to do
|
||||
* something more complex. */
|
||||
public boolean applicableForItem(Item item){
|
||||
return Arrays.asList(applicableItems).contains(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets which items this spell can appear on (these default to the regular spell book and scroll).
|
||||
* @param applicableItems The items this spell should naturally appear on (or no items at all).
|
||||
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
|
||||
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
|
||||
*/
|
||||
public Spell items(Item... applicableItems){
|
||||
this.applicableItems = applicableItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (this defaults to always
|
||||
* true).
|
||||
* @param selector A condition that NPCs must satisfy in order to spawn with this spell equipped.
|
||||
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
|
||||
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
|
||||
*/
|
||||
public Spell npcSelector(BiPredicate<EntityLiving, Boolean> selector){
|
||||
this.npcSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Spells are sorted according to tier and element. Where several spells have the same tier and element,
|
||||
// they will remain in the order they were registered.
|
||||
@Override
|
||||
@@ -785,7 +855,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
|
||||
/**
|
||||
* Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would be
|
||||
* returned by {@code Spell.getSpells(Spell.allSpells).size()}, but this method is more efficient.
|
||||
* returned by {@code Spell.getAllSpells().size()}, but this method is more efficient.
|
||||
*/
|
||||
public static int getTotalSpellCount(){
|
||||
return registry.getValuesCollection().size() - 1;
|
||||
@@ -833,31 +903,37 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
|
||||
/**
|
||||
* Returns a list containing all spells matching the given {@link Predicate}. The returned list is separate from the
|
||||
* internal spells list; any changes you make to the returned list will have no effect on wizardry since the
|
||||
* returned list is local to this method. Never includes the {@link None} spell. For convenience, there are some
|
||||
* predefined predicates in the Spell class (some of these really aren't shortcuts any more):
|
||||
* <p></p>
|
||||
* {@link Spell#allSpells} will allow all spells to be returned<br>
|
||||
* {@link Spell#npcSpells} will only allow enabled spells that can be cast by NPCs (see
|
||||
* {@link Spell#canBeCastByNPCs()})<br>
|
||||
* {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells<br>
|
||||
* {@link TierElementFilter} will only allow enabled spells of the specified tier and element
|
||||
* returned list is local to this method. Never includes the {@link None} spell.
|
||||
*
|
||||
* @param filter A <code>Predicate<Spell></code> that the returned spells must satisfy.
|
||||
*
|
||||
* @return A <b>local, modifiable</b> list of spells matching the given predicate. <i>Note that this list may be
|
||||
* empty.</i>
|
||||
*
|
||||
* @see TierElementFilter
|
||||
*/
|
||||
public static List<Spell> getSpells(Predicate<Spell> filter){
|
||||
return registry.getValuesCollection().stream().filter(filter.and(s -> s != Spells.none)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/** Predicate which allows all spells. */
|
||||
/** Returns all registered spells, except the {@link None} spell. */
|
||||
public static List<Spell> getAllSpells(){
|
||||
return getSpells(s -> true);
|
||||
}
|
||||
|
||||
/** Predicate which allows all spells.
|
||||
* @deprecated Use {@link Spell#getAllSpells()}. */
|
||||
@Deprecated
|
||||
public static Predicate<Spell> allSpells = s -> true;
|
||||
|
||||
/** Predicate which allows all non-continuous spells, even those that have been disabled. */
|
||||
/** Predicate which allows all non-continuous spells, even those that have been disabled.
|
||||
* @deprecated Nobody ever uses this now we have continuous scrolls, if you really need it just use a lambda. */
|
||||
@Deprecated
|
||||
public static Predicate<Spell> nonContinuousSpells = s -> !s.isContinuous;
|
||||
|
||||
/** Predicate which allows all enabled spells for which {@link Spell#canBeCastByNPCs()} returns true. */
|
||||
/** Predicate which allows all enabled spells for which {@link Spell#canBeCastBy(EntityLiving, boolean)} returns true.
|
||||
* @deprecated in favour of entity-sensitive version, use a lambda expression directly. */
|
||||
@Deprecated
|
||||
public static Predicate<Spell> npcSpells = s -> s.isEnabled(SpellProperties.Context.NPCS) && s.canBeCastByNPCs();
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
@@ -29,9 +30,9 @@ import java.util.function.Function;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link Spell#RANGE}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -58,13 +59,12 @@ public class SpellArrow<T extends EntityMagicArrow> extends Spell {
|
||||
super(modID, name, EnumAction.NONE, false);
|
||||
this.arrowFactory = arrowFactory;
|
||||
this.addProperties(RANGE);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
/** Computes the velocity the projectile should be launched at to achieve the required range. */
|
||||
// Long story short, it doesn't make much sense to me to have the JSON file specify the velocity - even less so if
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
@@ -32,9 +33,9 @@ import java.util.stream.Collectors;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link SpellBuff#getDurationKey(Potion)}, {@link SpellBuff#getStrengthKey(Potion)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -67,6 +68,7 @@ public class SpellBuff extends Spell {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,9 +105,7 @@ public class SpellBuff extends Spell {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -24,9 +24,9 @@ import net.minecraft.world.World;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link SpellConjuration#ITEM_LIFETIME}
|
||||
* <p></p>
|
||||
* By default, this type of spell cannot be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell cannot be cast by NPCs. {@link Spell#canBeCastBy(net.minecraft.entity.EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell cannot be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell cannot be cast by dispensers. {@link Spell#canBeCastBy(net.minecraft.tileentity.TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -29,9 +30,9 @@ import java.util.function.Function;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link Spell#DURATION} (if the construct is not permanent)
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -58,14 +59,13 @@ public class SpellConstruct<T extends EntityMagicConstruct> extends Spell {
|
||||
super(modID, name, action, false);
|
||||
this.constructFactory = constructFactory;
|
||||
this.permanent = permanent;
|
||||
this.npcSelector((e, o) -> true);
|
||||
if(!permanent) this.addProperties(DURATION);
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
/**
|
||||
* Sets whether the construct must be spawned on the ground.
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -30,9 +31,9 @@ import java.util.function.Function;
|
||||
* Properties added by this type of spell: {@link Spell#RANGE}, {@link Spell#DURATION} (if the construct is not
|
||||
* permanent)
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -54,6 +55,7 @@ public class SpellConstructRanged<T extends EntityMagicConstruct> extends SpellC
|
||||
public SpellConstructRanged(String modID, String name, Function<World, T> constructFactory, boolean permanent){
|
||||
super(modID, name, EnumAction.NONE, constructFactory, permanent);
|
||||
this.addProperties(RANGE);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,10 +81,8 @@ public class SpellConstructRanged<T extends EntityMagicConstruct> extends SpellC
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -32,9 +33,9 @@ import java.util.function.Function;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link SpellMinion#MINION_LIFETIME}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -66,6 +67,7 @@ public class SpellMinion<T extends EntityLiving & ISummonedCreature> extends Spe
|
||||
super(modID, name, EnumAction.BOW, false);
|
||||
this.minionFactory = minionFactory;
|
||||
addProperties(MINION_LIFETIME, MINION_COUNT, SUMMON_RADIUS);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,9 +82,7 @@ public class SpellMinion<T extends EntityLiving & ISummonedCreature> extends Spe
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
@@ -29,9 +30,9 @@ import java.util.function.Function;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link Spell#RANGE}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -58,13 +59,12 @@ public class SpellProjectile<T extends EntityMagicProjectile> extends Spell {
|
||||
super(modID, name, EnumAction.NONE, false);
|
||||
this.projectileFactory = projectileFactory;
|
||||
addProperties(RANGE);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
/** Computes the velocity the projectile should be launched at to achieve the required range. */
|
||||
// Long story short, it doesn't make much sense to me to have the JSON file specify the velocity - even less so if
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -22,7 +23,7 @@ import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Generic superclass for all spells which use a raytrace to do something and (optionally) spawn particles along that
|
||||
* trajectory. This is for both continuous ('stream') spells and non-continuous ('bolt') spells This allows all the
|
||||
* trajectory. This is for both continuous ('stream') spells and non-continuous ('bolt') spells. This allows all the
|
||||
* relevant code to be centralised. This class differs from most other spell superclasses in that it is abstract and as
|
||||
* such must be subclassed to define what the spell actually does. This is because ray-like spells do a wider variety of
|
||||
* different things, so it does not make sense to define more specific functions in this class since they would be
|
||||
@@ -34,9 +35,9 @@ import javax.annotation.Nullable;
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link Spell#RANGE}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
|
||||
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
@@ -72,6 +73,7 @@ public abstract class SpellRay extends Spell {
|
||||
public SpellRay(String modID, String name, boolean isContinuous, EnumAction action){
|
||||
super(modID, name, action, isContinuous);
|
||||
this.addProperties(RANGE);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
// Although this class is abstract, someone might instantiate one of its subclasses more than once to make two
|
||||
@@ -150,10 +152,8 @@ public abstract class SpellRay extends Spell {
|
||||
this.aimAssist = aimAssist;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers() { return true; }
|
||||
|
||||
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return true; }
|
||||
|
||||
// Finally everything in here is standardised and written in a form that's actually readable - it was long overdue!
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityThrowable;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Similar to {@link electroblob.wizardry.spell.SpellProjectile}, but for any {@link EntityThrowable}.
|
||||
* This allows all the relevant code to be centralised, since these spells all work in the same way. Usually, a simple
|
||||
* instantiation of this class is sufficient to create a projectile spell; if something extra needs to be done, such as
|
||||
* particle spawning, then methods can be overridden (perhaps using an anonymous class) to add the required functionality.
|
||||
* <p></p>
|
||||
* <i>N.B. It is advised that this class is only used where the projectile to be launched belongs to vanilla Minecraft
|
||||
* or another mod; no guarantees are made as to the behaviour of such projectiles!</i>
|
||||
* <p></p>
|
||||
* Properties added by this type of spell: {@link Spell#RANGE}
|
||||
* <p></p>
|
||||
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
|
||||
* <p></p>
|
||||
* By default, this type of spell cannot be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
|
||||
* <p></p>
|
||||
* By default, this type of spell does not require a packet to be sent. {@link Spell#requiresPacket()}
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2.8
|
||||
*/
|
||||
// TODO: Use events to make these projectiles seek targets when the caster is wearing a ring of attraction (is this possible?)
|
||||
public class SpellThrowable<T extends EntityThrowable> extends Spell {
|
||||
|
||||
private static final float LAUNCH_Y_OFFSET = 0.1f;
|
||||
|
||||
protected final BiFunction<World, EntityLivingBase, T> projectileFactory;
|
||||
|
||||
public SpellThrowable(String name, BiFunction<World, EntityLivingBase, T> projectileFactory){
|
||||
this(Wizardry.MODID, name, projectileFactory);
|
||||
}
|
||||
|
||||
public SpellThrowable(String modID, String name, BiFunction<World, EntityLivingBase, T> projectileFactory){
|
||||
super(modID, name, EnumAction.NONE, false);
|
||||
this.projectileFactory = projectileFactory;
|
||||
addProperties(RANGE);
|
||||
this.npcSelector((e, o) -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Trajectory calculation - see {@link SpellProjectile} for a more detailed explanation */
|
||||
protected float calculateVelocity(SpellModifiers modifiers, float launchHeight){
|
||||
float g = 0.03f;
|
||||
float range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
return range / MathHelper.sqrt(2 * launchHeight/g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
float velocity = calculateVelocity(modifiers, caster.getEyeHeight() - LAUNCH_Y_OFFSET);
|
||||
T projectile = projectileFactory.apply(world, caster);
|
||||
projectile.shoot(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, velocity, 1.0f);
|
||||
addProjectileExtras(projectile, caster, modifiers);
|
||||
world.spawnEntity(projectile);
|
||||
}
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
|
||||
if(target != null){
|
||||
|
||||
if(!world.isRemote){
|
||||
float velocity = calculateVelocity(modifiers, caster.getEyeHeight() - LAUNCH_Y_OFFSET);
|
||||
T projectile = projectileFactory.apply(world, caster);
|
||||
int aimingError = caster instanceof ISpellCaster ? ((ISpellCaster)caster).getAimingError(world.getDifficulty())
|
||||
: WizardryUtilities.getDefaultAimingError(world.getDifficulty());
|
||||
aim(projectile, caster, target, velocity, aimingError);
|
||||
addProjectileExtras(projectile, caster, modifiers);
|
||||
world.spawnEntity(projectile);
|
||||
}
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copied from EntityMagicProjectile (ugh what a mess)
|
||||
private void aim(T throwable, EntityLivingBase caster, Entity target, float speed, float aimingError){
|
||||
|
||||
throwable.ignoreEntity = caster;
|
||||
|
||||
throwable.posY = caster.getEntityBoundingBox().minY + (double)caster.getEyeHeight() - LAUNCH_Y_OFFSET;
|
||||
double dx = target.posX - caster.posX;
|
||||
double dy = !throwable.hasNoGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0f) - throwable.posY
|
||||
: target.getEntityBoundingBox().minY + (double)(target.height / 2.0f) - throwable.posY;
|
||||
double dz = target.posZ - caster.posZ;
|
||||
double horizontalDistance = MathHelper.sqrt(dx * dx + dz * dz);
|
||||
|
||||
if(horizontalDistance >= 1.0E-7D){
|
||||
|
||||
double dxNormalised = dx / horizontalDistance;
|
||||
double dzNormalised = dz / horizontalDistance;
|
||||
throwable.setPosition(caster.posX + dxNormalised, throwable.posY, caster.posZ + dzNormalised);
|
||||
|
||||
// Depends on the horizontal distance between the two entities and accounts for bullet drop,
|
||||
// but of course if gravity is ignored throwable should be 0 since there is no bullet drop.
|
||||
float bulletDropCompensation = !throwable.hasNoGravity() ? (float)horizontalDistance * 0.2f : 0;
|
||||
// It turns out that throwable method normalises the input (x, y, z) anyway
|
||||
throwable.shoot(dx, dy + (double)bulletDropCompensation, dz, speed, aimingError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing by default, but can be overridden to call extra methods or set additional fields on the launched
|
||||
* projectile.
|
||||
*/
|
||||
protected void addProjectileExtras(T projectile, EntityLivingBase caster, SpellModifiers modifiers){}
|
||||
|
||||
}
|
||||
@@ -10,11 +10,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class SummonSnowGolem extends Spell {
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Tornado extends SpellConstruct<EntityTornado> {
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public class WitherSkull extends Spell {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
public boolean canBeCastBy(EntityLiving npc, boolean override){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import electroblob.wizardry.item.ItemCrystal;
|
||||
import electroblob.wizardry.item.ItemSpellBook;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -205,7 +206,7 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory,
|
||||
}
|
||||
}
|
||||
|
||||
tagCompound.setTag("Inventory", itemList);
|
||||
NBTExtras.storeTagSafely(tagCompound, "Inventory", itemList);
|
||||
return tagCompound;
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ public class TileEntityShrineCore extends TileEntity implements ITickable {
|
||||
|
||||
for(EntityLivingBase entity : entities){
|
||||
entity.addPotionEffect(new PotionEffect(WizardryPotions.containment, 219));
|
||||
entity.getEntityData().setTag(PotionContainment.ENTITY_TAG, NBTUtil.createPosTag(this.pos));
|
||||
NBTExtras.storeTagSafely(entity.getEntityData(), PotionContainment.ENTITY_TAG, NBTUtil.createPosTag(this.pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,13 +192,13 @@ public class TileEntityShrineCore extends TileEntity implements ITickable {
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound compound){
|
||||
|
||||
compound.setBoolean("activated", this.activated);
|
||||
if(linkedContainer != null) compound.setTag("linkedContainerPos", NBTUtil.createPosTag(linkedContainer.getPos()));
|
||||
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));
|
||||
}
|
||||
compound.setTag("wizards", tagList);
|
||||
NBTExtras.storeTagSafely(compound, "wizards", tagList);
|
||||
|
||||
return super.writeToNBT(compound);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package electroblob.wizardry.tileentity;
|
||||
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityList;
|
||||
@@ -152,7 +153,7 @@ public class TileEntityStatue extends TileEntity implements ITickable {
|
||||
tagCompound.setFloat("entityYawHead", creature.rotationYawHead);
|
||||
tagCompound.setFloat("entityYawOffset", creature.renderYawOffset);
|
||||
}
|
||||
tagCompound.setTag("entity", entityCompound);
|
||||
NBTExtras.storeTagSafely(tagCompound, "entity", entityCompound);
|
||||
tagCompound.setInteger("timer", timer);
|
||||
tagCompound.setInteger("lifetime", lifetime);
|
||||
tagCompound.setBoolean("isIce", isIce);
|
||||
|
||||
@@ -8,7 +8,10 @@ import net.minecraft.entity.monster.*;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EntityDamageSource;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
// A note on the use of the vanilla damagesources:
|
||||
// When using indirect damage sources, the SECOND argument is the original entity (i.e. the caster), and the
|
||||
|
||||
@@ -51,8 +51,8 @@ public final class NBTExtras {
|
||||
|
||||
for(Map.Entry<K, V> entry : map.entrySet()){
|
||||
NBTTagCompound mapping = new NBTTagCompound();
|
||||
mapping.setTag(keyTagName, keyFunction.apply(entry.getKey()));
|
||||
mapping.setTag(valueTagName, valueFunction.apply(entry.getValue()));
|
||||
NBTExtras.storeTagSafely(mapping, keyTagName, keyFunction.apply(entry.getKey()));
|
||||
NBTExtras.storeTagSafely(mapping, valueTagName, valueFunction.apply(entry.getValue()));
|
||||
tagList.appendTag(mapping);
|
||||
}
|
||||
|
||||
@@ -205,6 +205,51 @@ public final class NBTExtras {
|
||||
tag.removeTag(key + "Least");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given NBT tag inside the given NBT tag compound using the given key. Under normal circumstances, this
|
||||
* is equivalent to {@link NBTTagCompound#setTag(String, NBTBase)}, but this method performs safety checks to
|
||||
* prevent circular references. If storing the given tag would cause a circular reference, the tag is not stored
|
||||
* and an error is printed to the console.
|
||||
* @param compound The {@link NBTTagCompound} in which to store the tag.
|
||||
* @param key The key to store the tag under.
|
||||
* @param tag The tag to store.
|
||||
*/
|
||||
// This is a catch-all fix for issue #299.
|
||||
public static void storeTagSafely(NBTTagCompound compound, String key, NBTBase tag){
|
||||
|
||||
if(compound == tag || deepContains(tag, compound)){
|
||||
Wizardry.logger.error("Cannot store tag of type {} under key '{}' as it would result in a circular reference! Please report this (including your full log) to wizardry's issue tracker.",
|
||||
NBTBase.getTypeName(tag.getId()), key);
|
||||
}else{
|
||||
compound.setTag(key, tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches within the first NBT tag for the second NBT tag. This handles both compound and list tags.
|
||||
* @param toSearch The NBT tag to search inside. If this is not a compound or list tag, this method will always
|
||||
* return false.
|
||||
* @param searchFor The NBT tag to search for.
|
||||
* @return True if the second tag appears anywhere within the NBT tree contained within the first tag, false if not.
|
||||
*/
|
||||
public static boolean deepContains(NBTBase toSearch, NBTBase searchFor){
|
||||
|
||||
if(toSearch instanceof NBTTagCompound){
|
||||
|
||||
for(String subKey : ((NBTTagCompound)toSearch).getKeySet()){
|
||||
NBTBase subTag = ((NBTTagCompound)toSearch).getTag(subKey);
|
||||
if(subTag == searchFor || deepContains(subTag, searchFor)) return true;
|
||||
}
|
||||
|
||||
}else if(toSearch instanceof NBTTagList){
|
||||
for(NBTBase subTag : (NBTTagList)toSearch){
|
||||
if(subTag == searchFor || deepContains(subTag, searchFor)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NBTTagCompound which contains only the given UUID, stored using
|
||||
* {@link NBTTagCompound#setUniqueId(String, UUID)}. Allows for neater storage to NBTTagLists.
|
||||
|
||||
@@ -247,7 +247,7 @@ public final class SpellProperties {
|
||||
public static void init(){
|
||||
|
||||
// Collecting to a set should give us one of each mod ID
|
||||
Set<String> modIDs = Spell.getSpells(Spell.allSpells).stream().map(s -> s.getRegistryName().getNamespace()).collect(Collectors.toSet());
|
||||
Set<String> modIDs = Spell.getAllSpells().stream().map(s -> s.getRegistryName().getNamespace()).collect(Collectors.toSet());
|
||||
|
||||
boolean flag = loadConfigSpellProperties();
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ public final class WandHelper {
|
||||
if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound()));
|
||||
|
||||
if(!wand.getTagCompound().hasKey(UPGRADES_KEY))
|
||||
wand.getTagCompound().setTag(UPGRADES_KEY, new NBTTagCompound());
|
||||
NBTExtras.storeTagSafely(wand.getTagCompound(), UPGRADES_KEY, new NBTTagCompound());
|
||||
|
||||
NBTTagCompound upgrades = wand.getTagCompound().getCompoundTag(UPGRADES_KEY);
|
||||
|
||||
@@ -406,7 +406,7 @@ public final class WandHelper {
|
||||
|
||||
if(key != null) upgrades.setInteger(key, upgrades.getInteger(key) + 1);
|
||||
|
||||
wand.getTagCompound().setTag(UPGRADES_KEY, upgrades);
|
||||
NBTExtras.storeTagSafely(wand.getTagCompound(), UPGRADES_KEY, upgrades);
|
||||
}
|
||||
|
||||
/** Returns true if the given item is a valid special wand upgrade. */
|
||||
|
||||
@@ -3,6 +3,7 @@ package electroblob.wizardry.worldgen;
|
||||
import com.google.common.math.Quantiles;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
@@ -256,7 +257,7 @@ public abstract class WorldGenSurfaceStructure implements IWorldGenerator {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setInteger("ChunkX", chunkX);
|
||||
tag.setInteger("ChunkZ", chunkZ);
|
||||
tag.setTag("BB", settings.getBoundingBox().toNBTTagIntArray());
|
||||
NBTExtras.storeTagSafely(tag, "BB", settings.getBoundingBox().toNBTTagIntArray());
|
||||
structureData.writeInstance(tag, chunkX, chunkZ);
|
||||
structureData.markDirty();
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ item.ebwizardry\:spell_book.name=Spell Book
|
||||
item.ebwizardry\:spell_book.apply_to_wizard=Replaced %1$s's spell %2$s with %3$s
|
||||
|
||||
item.ebwizardry\:arcane_tome.name=Tome of Arcana
|
||||
item.ebwizardry\:arcane_tome.desc1=Upgrades any %1$s
|
||||
item.ebwizardry\:arcane_tome.desc2=wand to %1$s tier
|
||||
item.ebwizardry\:arcane_tome.desc=Upgrades any %1$s wand with sufficient progression to %2$s tier
|
||||
|
||||
item.ebwizardry\:wizard_handbook.name=The Wizard's Handbook
|
||||
item.ebwizardry\:wizard_handbook.desc=by %1$s
|
||||
|
||||
@@ -44,8 +44,7 @@ item.ebwizardry\:spell_book.name=Spell Book
|
||||
item.ebwizardry\:spell_book.apply_to_wizard=Replaced %1$s's spell %2$s with %3$s
|
||||
|
||||
item.ebwizardry\:arcane_tome.name=Tome of Arcana
|
||||
item.ebwizardry\:arcane_tome.desc1=Upgrades any %1$s
|
||||
item.ebwizardry\:arcane_tome.desc2=wand to %1$s tier
|
||||
item.ebwizardry\:arcane_tome.desc=Upgrades any %1$s wand with sufficient progression to %2$s tier
|
||||
|
||||
item.ebwizardry\:wizard_handbook.name=The Wizard's Handbook
|
||||
item.ebwizardry\:wizard_handbook.desc=by %1$s
|
||||
|
||||
@@ -7,8 +7,30 @@ tile.ebwizardry:snare.name=Ловушка
|
||||
tile.ebwizardry:transportation_stone.name=Камень Транспортировки
|
||||
tile.ebwizardry:spectral_block.name=Спектральный Блок
|
||||
tile.ebwizardry:crystal_block.name=Кристальный Блок
|
||||
tile.ebwizardry:runestone.name=Рунический камень
|
||||
tile.ebwizardry:runestone_pedestal.name=Пьедестал рунического камня
|
||||
tile.ebwizardry:thorns.name=Колючки
|
||||
tile.ebwizardry:obsidian_crust.name=Обсидиановая кора
|
||||
tile.ebwizardry:dry_frosted_ice.name=Сухой матовый лед
|
||||
|
||||
tile.ebwizardry:magic_crystal_block.name=Блок кристалла
|
||||
tile.ebwizardry:fire_crystal_block.name=Блок огненного кристалла
|
||||
tile.ebwizardry:ice_crystal_block.name=Блок ледяного кристалла
|
||||
tile.ebwizardry:lightning_crystal_block.name=Блок грозового кристалла
|
||||
tile.ebwizardry:necromancy_crystal_block.name=Блок темного кристалла
|
||||
tile.ebwizardry:earth_crystal_block.name=Блок зеленого кристалла
|
||||
tile.ebwizardry:sorcery_crystal_block.name=Блок мистического кристалла
|
||||
tile.ebwizardry:healing_crystal_block.name=Блок сияющего кристалла
|
||||
|
||||
item.ebwizardry:crystal_magic.name=Магический Кристалл
|
||||
item.ebwizardry:crystal_fire.name=Огненный кристалл
|
||||
item.ebwizardry:crystal_ice.name=Ледяной кристалл
|
||||
item.ebwizardry:crystal_lightning.name=Грозовой кристалл
|
||||
item.ebwizardry:crystal_necromancy.name=Темный кристалл
|
||||
item.ebwizardry:crystal_earth.name=Зелёный кристалл
|
||||
item.ebwizardry:crystal_sorcery.name=Мистический кристалл
|
||||
item.ebwizardry:crystal_healing.name=Сияющий кристалл
|
||||
|
||||
item.ebwizardry:magic_crystal.name=Магический Кристалл
|
||||
item.ebwizardry:magic_wand.name=Волшебный Жезл
|
||||
item.ebwizardry:apprentice_wand.name=Жезл Ученика
|
||||
item.ebwizardry:advanced_wand.name=Продвинутый Жезл
|
||||
@@ -62,7 +84,21 @@ item.ebwizardry:spectral_sword.name=Спектральный Меч
|
||||
item.ebwizardry:spectral_pickaxe.name=Спектральная Кирка
|
||||
item.ebwizardry:spectral_bow.name=Спектральный Лук
|
||||
|
||||
item.ebwizardry:mana_flask.name=Бутылка Маны
|
||||
item.ebwizardry:spectral_sword_upgraded.name=Спектральный Меч
|
||||
item.ebwizardry:spectral_pickaxe_upgraded.name=Спектральная Кирка
|
||||
|
||||
item.ebwizardry:small_mana_flask.name=Маленькая Бутылка Маны
|
||||
item.ebwizardry:medium_mana_flask.name=Средняя Бутылка Маны
|
||||
item.ebwizardry:large_mana_flask.name=Большая Бутылка Маны
|
||||
|
||||
item.ebwizardry:grand_crystal.name=Большой Магический Кристалл
|
||||
item.ebwizardry:crystal_shard.name=Осколок Магического Кристалла
|
||||
|
||||
item.ebwizardry:astral_diamond.name=Звёздный Алмаз
|
||||
|
||||
item.ebwizardry:purifying_elixir.name=Очищающий Эликсир
|
||||
item.ebwizardry:purifying_elixir.desc=Снимает проклятия при употреблении
|
||||
|
||||
item.ebwizardry:storage_upgrade.name=Улучшение Хранилища Маны Жезла
|
||||
item.ebwizardry:siphon_upgrade.name=Улучшение Сифона Жезла
|
||||
item.ebwizardry:condenser_upgrade.name=Улучшение Конденсатора Жезла
|
||||
@@ -75,9 +111,13 @@ item.ebwizardry:attunement_upgrade.name=Улучшение Хранилища З
|
||||
item.ebwizardry:flaming_axe.name=Огненный Топор
|
||||
item.ebwizardry:frost_axe.name=Ледяной Топор
|
||||
|
||||
item.ebwizardry:flaming_axe_upgraded.name=Огненный Топор
|
||||
item.ebwizardry:frost_axe_upgraded.name=Ледяной Топор
|
||||
|
||||
item.ebwizardry:firebomb.name=Огненная Бомба
|
||||
item.ebwizardry:poison_bomb.name=Ядовитая Бомба
|
||||
item.ebwizardry:smoke_bomb.name=Дымовая Бомба
|
||||
item.ebwizardry:spark_bomb.name=Бомба Искр
|
||||
|
||||
item.ebwizardry:blank_scroll.name=Пустой Свиток
|
||||
item.ebwizardry:scroll.name=Свиток %1$s
|
||||
@@ -137,6 +177,75 @@ item.ebwizardry:wizard_leggings_healing.name=Поножи Целителя
|
||||
item.ebwizardry:wizard_boots_healing.name=Ботинки Целителя
|
||||
|
||||
item.ebwizardry:spawn_wizard.name=Спаун Волшебника
|
||||
item.ebwizardry:spawn_evil_wizard.name=Спаун Злого Волшебника
|
||||
|
||||
item.ebwizardry:spectral_helmet.name=Призрачный шлем
|
||||
item.ebwizardry:spectral_chestplate.name=Призрачный нагрудник
|
||||
item.ebwizardry:spectral_leggings.name=Призрачные поножи
|
||||
item.ebwizardry:spectral_boots.name=Призрачные ботинки
|
||||
|
||||
item.ebwizardry:lightning_hammer.name=Молот Шторма
|
||||
|
||||
item.ebwizardry:ring_condensing.name=Кольцо Конденсации
|
||||
item.ebwizardry:ring_condensing.desc=Медленно восстанавливает ману для всех жезлов на вашем хотбаре
|
||||
item.ebwizardry:ring_siphoning.name=Кольцо Сифонинга
|
||||
item.ebwizardry:ring_siphoning.desc=Увеличивает сифоновую ману на 30 %%
|
||||
item.ebwizardry:ring_battlemage.name=Кольцо Боевого Мага
|
||||
item.ebwizardry:ring_battlemage.desc=Держа палочку в руке и меч в основной руке, вы получаете 10 %% дополнительного магического урона
|
||||
item.ebwizardry:ring_combustion.name=Кольцо Горения
|
||||
item.ebwizardry:ring_combustion.desc=Существа, убитые огненными заклинаниями, взрываются
|
||||
item.ebwizardry:ring_fire_melee.name=Кольцо Огненного Прикосновения
|
||||
item.ebwizardry:ring_fire_melee.desc=Удар по существу огненной палочкой поджигает его
|
||||
item.ebwizardry:ring_fire_biome.name=Кольцо Пустынного Солнца
|
||||
item.ebwizardry:ring_fire_biome.desc=Огненные заклинания на 30% более сильны в горячих биомах
|
||||
item.ebwizardry:ring_disintegration.name=Кольцо Раскаленных Углей
|
||||
item.ebwizardry:ring_disintegration.desc=Все заклинания огненной атаки разрушают их жертв
|
||||
item.ebwizardry:ring_ice_melee.name=Кольцо Ледяного Прикосновения
|
||||
item.ebwizardry:ring_ice_melee.desc=Удар по существу ледяной палочкой дает эффект обморожения
|
||||
item.ebwizardry:ring_ice_biome.name=Кольцо Оледенения
|
||||
item.ebwizardry:ring_ice_biome.desc=Заклинания льда в снежных биомах на 30% более мощные
|
||||
item.ebwizardry:ring_arcane_frost.name=Кольцо Чародейского Мороза
|
||||
item.ebwizardry:ring_arcane_frost.desc=Существа, убитые ледяными заклинаниями, выпускают осколки льда во всех направлениях
|
||||
item.ebwizardry:ring_shattering.name=Кольцо Разрушителя
|
||||
item.ebwizardry:ring_shattering.desc=Атаки ближнего боя на мобов с эффектом обморожения могут разбить их на ледяные осколки
|
||||
item.ebwizardry:ring_lightning_melee.name=Кольцо Цепи
|
||||
item.ebwizardry:ring_lightning_melee.desc=Удар молнии о существо выстреливает молнией в другого находящегося поблизости существа
|
||||
item.ebwizardry:ring_storm.name=Кольцо Собирающейся Бури
|
||||
item.ebwizardry:ring_storm.desc=Во время грозы заклинания молнии резко сокращают время восстановления
|
||||
item.ebwizardry:ring_seeking.name=Кольцо Притяжения
|
||||
item.ebwizardry:ring_seeking.desc=Все снарядовые заклинания ищут свои цели
|
||||
item.ebwizardry:ring_hammer.name=Кольцо Талека Всемогущего
|
||||
item.ebwizardry:ring_hammer.desc=Молнии могут быть подняты и брошены
|
||||
item.ebwizardry:ring_soulbinding.name=Кольцо Странника Душ
|
||||
item.ebwizardry:ring_soulbinding.desc=Существа, поврежденные заклинаниями некромантии, становятся привязанными к вам
|
||||
item.ebwizardry:ring_leeching.name=Кольцо Пиявки
|
||||
item.ebwizardry:ring_leeching.desc=Все атаки некромантии с вероятностью 30% могут вызвать эффект истощения жизни
|
||||
item.ebwizardry:ring_necromancy_melee.name=Кольцо Некротического Прикосновения
|
||||
item.ebwizardry:ring_necromancy_melee.desc=Удар по существу с помощью палочки некромантии дает ему эффект увядания
|
||||
item.ebwizardry:ring_mind_control.name=Кольцо Экстрасенса
|
||||
item.ebwizardry:ring_mind_control.desc=Контролируемые разумом существа могут контролировать свои близлежащие существа
|
||||
item.ebwizardry:ring_poison.name=Кольцо Змеи
|
||||
item.ebwizardry:ring_poison.desc=Все заклинания земли отравляют свою цель
|
||||
item.ebwizardry:ring_earth_melee.name=Кольцо ядовитого прикосновения
|
||||
item.ebwizardry:ring_earth_melee.desc=Удар по существу земной палочкой отравляет его
|
||||
item.ebwizardry:ring_earth_biome.name=Кольцо Дриады
|
||||
item.ebwizardry:ring_earth_biome.desc=Заклинания земли сильнее на 30% в лесах и крытых лесах
|
||||
item.ebwizardry:ring_full_moon.name=Кольцо Воющего Волка
|
||||
item.ebwizardry:ring_full_moon.desc=Земные заклинания резко сократили время восстановления при полной луне
|
||||
item.ebwizardry:ring_extraction.name=Кольцо Извлечения
|
||||
item.ebwizardry:ring_extraction.desc=Убийства заклинаниями магии дают бонус маны
|
||||
item.ebwizardry:ring_mana_return.name=Кольцо Перфекциониста
|
||||
item.ebwizardry:ring_mana_return.desc=Силовые стрелы, которые не попадают в цель, возвращают ману, которую они использовали к вашей палочке
|
||||
item.ebwizardry:ring_blockwrangler.name=Кольцо Странника
|
||||
item.ebwizardry:ring_blockwrangler.desc=Брошенные блоки наносят в два раза больше обычного урона
|
||||
item.ebwizardry:ring_conjurer.name=Кольцо фокусника
|
||||
item.ebwizardry:ring_conjurer.desc=Колдовские вещи длятся в два раза дольше
|
||||
item.ebwizardry:ring_defender.name=Кольцо Защитника
|
||||
item.ebwizardry:ring_defender.desc=Ваши снаряды проходят через силовые поля, принадлежащие вам или союзнику
|
||||
item.ebwizardry:ring_paladin.name=Кольцо Паладина
|
||||
item.ebwizardry:ring_paladin.desc=Когда вы исцеляете себя или союзника, близлежащие союзники также получают немного здоровья
|
||||
item.ebwizardry:ring_interdiction.name=Кольцо Запрета
|
||||
item.ebwizardry:ring_interdiction.desc=Ваше силовое поле наносит урон существам, которые его касаются
|
||||
|
||||
entity.ebwizardry:zombie_minion.name=Зомби
|
||||
entity.ebwizardry:skeleton_minion.name=Скелет
|
||||
@@ -609,4 +718,4 @@ config.ebwizardry.show_summoned_creature_names=Show Summoned Creature Names
|
||||
config.ebwizardry.spell_hud_position=Spell HUD Position
|
||||
|
||||
config.ebwizardry.category.spells=Configure Spells
|
||||
config.ebwizardry.category.spells.tooltip=Select which spells are enabled.
|
||||
config.ebwizardry.category.spells.tooltip=Select which spells are enabled.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user