This commit is contained in:
Electroblob77
2019-01-05 22:18:14 +00:00
parent b374f71533
commit 224ee281ef
33 changed files with 160 additions and 273 deletions
@@ -134,12 +134,12 @@ public class CommonProxy {
* *
* @param entity The source of the sound * @param entity The source of the sound
* @param sound The SoundEvent to play * @param sound The SoundEvent to play
* @param category The SoundCategory to use
* @param volume Volume relative to 1 * @param volume Volume relative to 1
* @param pitch Pitch relative to 1 * @param pitch Pitch relative to 1
* @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually) * @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually)
*/ */
public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){ public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){}
}
/** /**
* Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns * Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns
@@ -153,5 +153,4 @@ public class CommonProxy {
public Set<String> getSpellHUDSkins(){ public Set<String> getSpellHUDSkins(){
return null; return null;
} }
} }
@@ -144,7 +144,7 @@ public final class Settings {
// Gamemodes // Gamemodes
/** /**
* <b>[Synchronised]</b> When set to true, spells a player hasn't cast yet will be unreadable until they are cast * <b>[Synchronised]</b> When set to true, spells a player hasn't cast yet will be unreadable until they are cast
* (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in * (on a per-world basis). Has no effect when in creative mode. Scrolls of identification will be unobtainable in
* survival mode if this is false. * survival mode if this is false.
*/ */
public boolean discoveryMode = true; public boolean discoveryMode = true;
@@ -507,7 +507,6 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
NBTTagCompound properties = new NBTTagCompound(); NBTTagCompound properties = new NBTTagCompound();
// ...so Java 8 allows you to do stuff like this:
properties.setTag("imbuements", WizardryUtilities.mapToNBT(this.imbuementDurations, properties.setTag("imbuements", WizardryUtilities.mapToNBT(this.imbuementDurations,
imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new)); imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new));
@@ -523,7 +522,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
properties.setLong("clairvoyanceLocation", this.clairvoyanceLocation.toLong()); properties.setLong("clairvoyanceLocation", this.clairvoyanceLocation.toLong());
properties.setInteger("clairvoyanceDimension", this.getClairvoyanceDimension()); properties.setInteger("clairvoyanceDimension", this.getClairvoyanceDimension());
// THIS is why I wrote the list/map <-> NBT methods. Look how neat this is! // Mmmmmm Java 8....
properties.setTag("allies", WizardryUtilities.listToNBT(this.allies, WizardryUtilities::UUIDtoTagCompound)); properties.setTag("allies", WizardryUtilities.listToNBT(this.allies, WizardryUtilities::UUIDtoTagCompound));
properties.setTag("allyNames", WizardryUtilities.listToNBT(this.allyNames, NBTTagString::new)); properties.setTag("allyNames", WizardryUtilities.listToNBT(this.allyNames, NBTTagString::new));
properties.setTag("soulboundCreatures", WizardryUtilities.listToNBT(this.soulboundCreatures, WizardryUtilities::UUIDtoTagCompound)); properties.setTag("soulboundCreatures", WizardryUtilities.listToNBT(this.soulboundCreatures, WizardryUtilities::UUIDtoTagCompound));
@@ -661,54 +660,4 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
} }
// Ended up deleting IWizardData because it was unnecessary. This is the comment that was at the start of it:
/* I'm not going to lie, I will never find the capabilities system even remotely intuitive so this is a bare-minimum
* approach just to get things working (four classes where one would have done?!) At one point I considered simply
* wrapping my old IEEP inside a single-field capability, but I eventually decided I would at least *try* to do it
* properly.
*
* "...without having to directly implement many interfaces." - Forge Docs. I still can't see what's wrong with
* implementing many interfaces; surely that's what Java interfaces are designed for?
*
* Other things I find annoying: - IStorage. It's completely redundant in the majority of cases, and I don't
* understand why we need yet another separate class. - Making an interface, only to implement it once and once
* only. This completely defeats the point of interfaces. - The EnumFacing parameter, which is again redundant for
* everything that isn't a tile entity. So much for a clean, neat system.
*
* What Forge has effectively done is conflated two different functions: attaching data to stuff and cross-mod
* integration/soft dependencies. I think this is bad design; it would have been better to keep the two features
* separate.
*
* Here's my current understanding of how the capability system works: - You make an interface which defines the
* things your capability can do (this class). I will call this the TEMPLATE. - You implement that interface with
* your default implementation (WizardData). This is the closest analog to your old IEEP implementation class. THIS
* CLASS STORES ALL THE VARIABLES, and hence has one instance for each instance of whatever it is attached to. I
* will call this the DATA. - The DATA class implements INBTSerializable (assuming you want it to be saved, which is
* nearly always the case) - Despite its name, Capability<T> does NOT represent a capability itself. Instead, it
* acts as a sort of identifier/key, the idea being that you can access a particular instance of your DATA given the
* key (which tells forge that you want a capability of type TEMPLATE) and the object you want the DATA for. This is
* what Entity.getCapability(...) does.
*
* To really understand what's going on though, you need to sift through Forge's verbose data structures and find
* where capabilities are actually hooked into vanilla: - Anything that implements ICapabilityProvider will have a
* private CapabilityDispatcher field. This holds other ICapabilityProviders. (I know. This inheritance pattern DOES
* NOT MAKE SENSE, because these could, in theory, be OTHER ENTITIES!) - This field is assigned a value through
* Forge's event factory, which, as we are all familiar with, calls all the methods marked with @SubscribeEvent.
* These methods add individual ICapabilityProviders to a Map stored in the event, which the event factory then
* wraps in a CapabilityDispatcher (which is itself an ICapabilityProvider) for the object that called it. - In your
* event handler, you return a custom ICapabilityProvider which is effectively bolted on to the player, and
* duplicates the ICapabilityProvider methods so you can hook into them and return an instance of your DATA class. -
* Where before there was a simple collection of IEEPs stored in the player, there is now a tree of
* ICapabilityProviders:
*
* - Entity/TileEntity/ItemStack - Vanilla ICapabilityProviders, mostly IItemHandlers, stored as fields. -
* CapabilityDispatcher, stored as a field. - Custom ICapabilityProviders - Custom CapabilityDispatchers - ...
*
* Most importantly, EACH PLAYER HOLDS THEIR OWN INSTANCE OF THIS TREE.
*
* When a capability is retrieved, the following process happens: 1. For the Entity/TileEntity/ItemStack instance,
* ICapabilityProvider.getCapability(...) is called. 2. The request propogates through the tree and finds the
* requested capability. */
} }
@@ -79,7 +79,6 @@ public class Wizardry {
// TODO: Switch from IInventory to IItemHandler (Or don't. It's only useful for automation really.) // TODO: Switch from IInventory to IItemHandler (Or don't. It's only useful for automation really.)
// TODO: Have particles obey Minecraft's particle setting where appropriate // TODO: Have particles obey Minecraft's particle setting where appropriate
// (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java) // (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java)
// TODO: Interfaces for various things, like 'stuff that can be put in the central slot of an arcane workbench'
// TODO: Go over all the worldgen code, use IWorldGenerator // TODO: Go over all the worldgen code, use IWorldGenerator
// TODO: Implement a continuous sound system using MovingSoundEntity, allowing continuous spells to have a long sound // TODO: Implement a continuous sound system using MovingSoundEntity, allowing continuous spells to have a long sound
// loop as well as a start and end sound // loop as well as a start and end sound
@@ -88,6 +87,9 @@ public class Wizardry {
// TODO: Replace spell IDs in packets with ResourceLocation strings // TODO: Replace spell IDs in packets with ResourceLocation strings
// TODO: Forcefield needs looking at, esp. with regards to projectiles and explosions // TODO: Forcefield needs looking at, esp. with regards to projectiles and explosions
// TODO: TileEntityArcaneWorkbench needs looking at, esp. regarding inventory and markDirty // TODO: TileEntityArcaneWorkbench needs looking at, esp. regarding inventory and markDirty
// TODO: Fireskin somehow lost its particles
// TODO: Convert settings over to the @Config system
// TODO: Go through listeners of LivingHurtEvent and decide whether they should change to LivingDamageEvent
// NOTE: Add melee upgrades to loot tables when they are added. // NOTE: Add melee upgrades to loot tables when they are added.
@@ -208,7 +210,7 @@ public class Wizardry {
for(RegistryEvent.MissingMappings.Mapping<Item> mapping : event.getAllMappings()){ for(RegistryEvent.MissingMappings.Mapping<Item> mapping : event.getAllMappings()){
if(mapping.key.getResourceDomain().equals(Wizardry.MODID)){ if(mapping.key.getResourceDomain().equals(Wizardry.MODID)){
Item replacement = null; Item replacement;
switch(mapping.key.getResourcePath()){ switch(mapping.key.getResourcePath()){
@@ -1,5 +1,6 @@
package electroblob.wizardry; package electroblob.wizardry;
import electroblob.wizardry.client.gui.handbook.GuiWizardHandbook;
import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWizardHandbook; import electroblob.wizardry.item.ItemWizardHandbook;
import electroblob.wizardry.spell.Spell; import electroblob.wizardry.spell.Spell;
@@ -45,7 +46,7 @@ public class WizardryGuiHandler implements IGuiHandler {
} }
}else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook }else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook
|| player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){ || player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){
return new electroblob.wizardry.client.gui.GuiWizardHandbook(); return new GuiWizardHandbook();
}else if(id == SPELL_BOOK){ }else if(id == SPELL_BOOK){
if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){ if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.get(player.getHeldItemMainhand().getItemDamage())); return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.get(player.getHeldItemMainhand().getItemDamage()));
@@ -533,8 +533,8 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new); RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
// Creatures // Creatures
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, manager -> new RenderSpiritWolf(manager)); RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, RenderSpiritWolf::new);
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, manager -> new RenderSpiritHorse(manager)); RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, RenderSpiritHorse::new);
RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new); RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new); RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new); RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new);
@@ -48,7 +48,7 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.fml.relauncher.SideOnly;
/** /**
* Event handler responsible for all client-side only events, mostly rendering. * Event handler responsible for client-side only events, mostly rendering.
* *
* @author Electroblob * @author Electroblob
* @since Wizardry 1.0 * @since Wizardry 1.0
@@ -4,9 +4,14 @@ import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.item.EntityArmorStand;
public class ModelWtfMojang extends ModelBiped { /**
* Fixes custom armour models 'breathing' on the stand and rotates the helmet properly.
* @author Shadows-of-Fire
* @since Wizardry 4.1.2
*/
public class ModelArmourFixer extends ModelBiped {
public ModelWtfMojang(float modelSize, float rotationYOffset, int textureWidth, int textureHeight) { public ModelArmourFixer(float modelSize, float rotationYOffset, int textureWidth, int textureHeight) {
super(modelSize, rotationYOffset, textureWidth, textureHeight); super(modelSize, rotationYOffset, textureWidth, textureHeight);
} }
@@ -5,64 +5,71 @@ import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
public class ModelHammer extends ModelBase { public class ModelHammer extends ModelBase {
ModelRenderer Shape1;
ModelRenderer Shape2; ModelRenderer hammerHead;
ModelRenderer Shape3; ModelRenderer handle;
ModelRenderer Shape4; ModelRenderer handleEnd;
ModelRenderer Shape5; ModelRenderer handleBase;
ModelRenderer Shape6; ModelRenderer ring1;
ModelRenderer ring2;
public ModelHammer(){ public ModelHammer(){
textureWidth = 64; textureWidth = 64;
textureHeight = 64; textureHeight = 64;
Shape1 = new ModelRenderer(this, 0, 0); hammerHead = new ModelRenderer(this, 0, 0);
Shape1.addBox(0F, 0F, 0F, 20, 12, 12); hammerHead.addBox(0F, 0F, 0F, 20, 12, 12);
Shape1.setRotationPoint(-10F, 12F, -6F); hammerHead.setRotationPoint(-10F, 12F, -6F);
Shape1.setTextureSize(64, 64); hammerHead.setTextureSize(64, 64);
Shape1.mirror = true; hammerHead.mirror = true;
setRotation(Shape1, 0F, 0F, 0F); setRotation(hammerHead, 0F, 0F, 0F);
Shape2 = new ModelRenderer(this, 0, 24);
Shape2.addBox(0F, 0F, 0F, 4, 14, 4); handle = new ModelRenderer(this, 0, 24);
Shape2.setRotationPoint(-2F, -2F, -2F); handle.addBox(0F, 0F, 0F, 4, 14, 4);
Shape2.setTextureSize(64, 64); handle.setRotationPoint(-2F, -2F, -2F);
Shape2.mirror = true; handle.setTextureSize(64, 64);
setRotation(Shape2, 0F, 0F, 0F); handle.mirror = true;
Shape3 = new ModelRenderer(this, 0, 49); setRotation(handle, 0F, 0F, 0F);
Shape3.addBox(0F, 0F, 0F, 5, 5, 5);
Shape3.setRotationPoint(-2.5F, -7F, -2.5F); handleEnd = new ModelRenderer(this, 0, 49);
Shape3.setTextureSize(64, 64); handleEnd.addBox(0F, 0F, 0F, 5, 5, 5);
Shape3.mirror = true; handleEnd.setRotationPoint(-2.5F, -7F, -2.5F);
setRotation(Shape3, 0F, 0F, 0F); handleEnd.setTextureSize(64, 64);
Shape4 = new ModelRenderer(this, 0, 42); handleEnd.mirror = true;
Shape4.addBox(0F, 0F, 0F, 5, 2, 5); setRotation(handleEnd, 0F, 0F, 0F);
Shape4.setRotationPoint(-2.5F, 10F, -2.5F);
Shape4.setTextureSize(64, 64); handleBase = new ModelRenderer(this, 0, 42);
Shape4.mirror = true; handleBase.addBox(0F, 0F, 0F, 5, 2, 5);
setRotation(Shape4, 0F, 0F, 0F); handleBase.setRotationPoint(-2.5F, 10F, -2.5F);
Shape5 = new ModelRenderer(this, 20, 24); handleBase.setTextureSize(64, 64);
Shape5.addBox(0F, 0F, 0F, 2, 14, 14); handleBase.mirror = true;
Shape5.setRotationPoint(-8F, 11F, -7F); setRotation(handleBase, 0F, 0F, 0F);
Shape5.setTextureSize(64, 64);
Shape5.mirror = true; ring1 = new ModelRenderer(this, 20, 24);
setRotation(Shape5, 0F, 0F, 0F); ring1.addBox(0F, 0F, 0F, 2, 14, 14);
Shape6 = new ModelRenderer(this, 20, 24); ring1.setRotationPoint(-8F, 11F, -7F);
Shape6.addBox(0F, 0F, 0F, 2, 14, 14); ring1.setTextureSize(64, 64);
Shape6.setRotationPoint(6F, 11F, -7F); ring1.mirror = true;
Shape6.setTextureSize(64, 64); setRotation(ring1, 0F, 0F, 0F);
Shape6.mirror = true;
setRotation(Shape6, 0F, 0F, 0F); ring2 = new ModelRenderer(this, 20, 24);
ring2.addBox(0F, 0F, 0F, 2, 14, 14);
ring2.setRotationPoint(6F, 11F, -7F);
ring2.setTextureSize(64, 64);
ring2.mirror = true;
setRotation(ring2, 0F, 0F, 0F);
} }
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){ public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
super.render(entity, f, f1, f2, f3, f4, f5); super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5, entity); setRotationAngles(f, f1, f2, f3, f4, f5, entity);
Shape1.render(f5); hammerHead.render(f5);
Shape2.render(f5); handle.render(f5);
Shape3.render(f5); handleEnd.render(f5);
Shape4.render(f5); handleBase.render(f5);
Shape5.render(f5); ring1.render(f5);
Shape6.render(f5); ring2.render(f5);
} }
private void setRotation(ModelRenderer model, float x, float y, float z){ private void setRotation(ModelRenderer model, float x, float y, float z){
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelRenderer; import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
public class ModelWizardArmour extends ModelWtfMojang { public class ModelWizardArmour extends ModelArmourFixer {
ModelRenderer Shape1; ModelRenderer Shape1;
ModelRenderer Shape2; ModelRenderer Shape2;
ModelRenderer Shape3; ModelRenderer Shape3;
@@ -166,9 +166,8 @@ public final class WizardryItemModels {
* Registers an item model, using the item's registry name as the model name (this convention makes it easier to * Registers an item model, using the item's registry name as the model name (this convention makes it easier to
* keep track of everything). Variant defaults to "normal". Registers the model for metadata 0 automatically, plus * keep track of everything). Variant defaults to "normal". Registers the model for metadata 0 automatically, plus
* all the other metadata values that the item can take, as defined in * all the other metadata values that the item can take, as defined in
* {@link Item#getSubItems(Item, net.minecraft.creativetab.CreativeTabs, java.util.List)}. The passed in item * {@link Item#getSubItems(Item, net.minecraft.creativetab.CreativeTabs, java.util.List)}. The creative tab supplied
* <b>must</b> allow null to be passed in for the creative tab parameter in the aforementioned method, or a * to the aforementioned method will be whichever one the item is in.
* {@link NullPointerException} will result.
*/ */
private static void registerItemModel(Item item){ private static void registerItemModel(Item item){
@@ -68,8 +68,7 @@ public enum Tier {
int totalWeight = 0; int totalWeight = 0;
for(Tier tier : tiers) for(Tier tier : tiers) totalWeight += tier.weight;
totalWeight += tier.weight;
int randomiser = random.nextInt(totalWeight); int randomiser = random.nextInt(totalWeight);
int cumulativeWeight = 0; int cumulativeWeight = 0;
@@ -137,8 +137,7 @@ public class EntityTornado extends EntityMagicConstruct {
Type type = null; Type type = null;
if(block.getMaterial() == Material.LEAVES) type = Type.LEAF; if(block.getMaterial() == Material.LEAVES) type = Type.LEAF;
if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW) if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW) type = Type.SNOW;
type = Type.SNOW;
if(type != null){ if(type != null){
double yPos1 = rand.nextDouble() * 8; double yPos1 = rand.nextDouble() * 8;
@@ -108,6 +108,7 @@ public class EntityAIAttackSpell<T extends EntityLiving & ISpellCaster> extends
new PacketNPCCastSpell.Message(attacker.getEntityId(), target == null ? -1 : target.getEntityId(), new PacketNPCCastSpell.Message(attacker.getEntityId(), target == null ? -1 : target.getEntityId(),
EnumHand.MAIN_HAND, spell.id(), modifiers), EnumHand.MAIN_HAND, spell.id(), modifiers),
// Particles are usually only visible from 16 blocks away, so 128 is more than far enough. // Particles are usually only visible from 16 blocks away, so 128 is more than far enough.
// TODO: Why is this one a 128 block radius, whilst the other one is all in dimension?
new TargetPoint(attacker.dimension, attacker.posX, attacker.posY, attacker.posZ, 128)); new TargetPoint(attacker.dimension, attacker.posX, attacker.posY, attacker.posZ, 128));
} }
@@ -146,29 +146,26 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); this.tasks.addTask(7, new EntityAIWander(this, 0.6D));
this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
this.targetSelector = new Predicate<Entity>(){ this.targetSelector = entity -> {
public boolean apply(Entity entity){ // If the target is valid and not invisible...
if(entity != null && !entity.isInvisible()
&& WizardryUtilities.isValidTarget(EntityWizard.this, entity)){
// If the target is valid and not invisible... // ... and is a mob, a summoned creature ...
if(entity != null && !entity.isInvisible() if((entity instanceof IMob || entity instanceof ISummonedCreature
&& WizardryUtilities.isValidTarget(EntityWizard.this, entity)){ // ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist)
// ... and is a mob, a summoned creature ... .contains(EntityList.getKey(entity.getClass())))
if((entity instanceof IMob || entity instanceof ISummonedCreature // ... and isn't in the blacklist ...
// ... or in the whitelist ... && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist)
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist) .contains(EntityList.getKey(entity.getClass()))){
.contains(EntityList.getKey(entity.getClass()))) // ... it can be attacked.
// ... and isn't in the blacklist ... return true;
&& !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist)
.contains(EntityList.getKey(entity.getClass()))){
// ... it can be attacked.
return true;
}
} }
return false;
} }
return false;
}; };
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true));
@@ -157,7 +157,7 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
* Called by the client when it receives a Entity spawn packet. Data should be read out of the stream in the same * Called by the client when it receives a Entity spawn packet. Data should be read out of the stream in the same
* way as it was written. <b>Implementors must call super when overriding.</b> * way as it was written. <b>Implementors must call super when overriding.</b>
* *
* @param additionalData The packet data stream * @param buffer The packet data stream
*/ */
@Override @Override
default void readSpawnData(ByteBuf buffer){ default void readSpawnData(ByteBuf buffer){
@@ -241,11 +241,11 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
* to do something when a successful attack is made. This was added because the event-based damage source system can * to do something when a successful attack is made. This was added because the event-based damage source system can
* cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled. * cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled.
* <p> * <p>
* Usage examples: {@link EntitySliverfishMinion} uses this to summon more silverfish if the target is killed, * Usage examples: {@link EntitySilverfishMinion} uses this to summon more silverfish if the target is killed,
* {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target. * {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target.
*/ */
default void onSuccessfulAttack(EntityLivingBase target){ default void onSuccessfulAttack(EntityLivingBase target){
}; }
// Delegates // Delegates
@@ -34,8 +34,7 @@ public class EntityFirebolt extends EntityMagicProjectile {
if(world.isRemote){ if(world.isRemote){
for(int i = 0; i < 8; i++){ for(int i = 0; i < 8; i++){
world.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5, world.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5,
this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, this.posY + this.height / 2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0);
0);
} }
} }
@@ -7,7 +7,7 @@ import net.minecraft.item.ItemStack;
/** /**
* Items that implement this interface may be placed in the central slot of the arcane workbench as long as * Items that implement this interface may be placed in the central slot of the arcane workbench as long as
* {@link IWorkbenchItem#canPlace(ItemStack)} returns true.The number of spell book slots displayed is also specified * {@link IWorkbenchItem#canPlace(ItemStack)} returns true. The number of spell book slots displayed is also specified
* using {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}. * using {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}.
* <p> * <p>
* Items that implement this interface define what happens if they are in the central slot of the arcane workbench and * Items that implement this interface define what happens if they are in the central slot of the arcane workbench and
@@ -52,7 +52,7 @@ import net.minecraftforge.fml.relauncher.SideOnly;
* written the {@link WandHelper} class.<i> I strongly recommend you use it for interacting with wand items wherever * written the {@link WandHelper} class.<i> I strongly recommend you use it for interacting with wand items wherever
* possible.</i> * possible.</i>
* <p> * <p>
* It's unikely that anything in this class will be of much use externally, but should you wish to use it for whatever * It's unlikely that anything in this class will be of much use externally, but should you wish to use it for whatever
* reason (perhaps if you extend it), it works as follows: * reason (perhaps if you extend it), it works as follows:
* <p> * <p>
* - onItemRightClick is where non-continuous spells are cast, and it sets the item in use for continuous spells<br> * - onItemRightClick is where non-continuous spells are cast, and it sets the item in use for continuous spells<br>
@@ -37,9 +37,9 @@ import net.minecraftforge.fml.relauncher.SideOnly;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem { public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
//VanillaCopy, ItemArmor has this set to private for some reason. // VanillaCopy, ItemArmor has this set to private for some reason.
public static final UUID[] ARMOR_MODIFIERS = new UUID[] {UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")}; public static final UUID[] ARMOR_MODIFIERS = new UUID[] {UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")};
//Damage reduction values that used to be in WizardryItems.SILK [feet, legs, chest, head] // Damage reduction values that used to be in WizardryItems.SILK [feet, legs, chest, head]
private static int[] reductions = new int[]{2, 4, 5, 2}; private static int[] reductions = new int[]{2, 4, 5, 2};
public Element element; public Element element;
@@ -34,11 +34,11 @@ public class PotionDecay extends Potion {
} }
@Override @Override
public boolean isReady(int p_76397_1_, int p_76397_2_){ public boolean isReady(int duration, int amplifier){
// Copied from the vanilla wither effect. It does the timing stuff. 25 is the number of ticks between hits at // Copied from the vanilla wither effect. It does the timing stuff. 25 is the number of ticks between hits at
// amplifier 0 // amplifier 0
int k = 25 >> p_76397_2_; int k = 25 >> amplifier;
return k > 0 ? p_76397_1_ % k == 0 : true; return k > 0 ? duration % k == 0 : true;
} }
@Override @Override
@@ -54,7 +54,8 @@ import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryBuilder; import net.minecraftforge.registries.RegistryBuilder;
/** /**
* Class responsible for defining, storing and registering all of wizardry's spells. * Class responsible for defining, storing and registering all of wizardry's spells. Use this to access individual
* spell instances, similar to the {@code Blocks} and {@code Items} classes.
* *
* @author Electroblob * @author Electroblob
* @since Wizardry 2.1 * @since Wizardry 2.1
@@ -75,7 +76,8 @@ public final class Spells {
public static void createRegistry(RegistryEvent.NewRegistry event){ public static void createRegistry(RegistryEvent.NewRegistry event){
// Beats me why we need both of these. Surely the type parameter means it already knows? // Beats me why we need both of these. Surely the type parameter means it already knows?
RegistryBuilder<Spell> builder = new RegistryBuilder<Spell>(); // EDIT: It's probably because of type erasure, thinking about it.
RegistryBuilder<Spell> builder = new RegistryBuilder<>();
builder.setType(Spell.class); builder.setType(Spell.class);
builder.setName(new ResourceLocation(Wizardry.MODID, "spells")); builder.setName(new ResourceLocation(Wizardry.MODID, "spells"));
builder.setIDRange(0, 5000); // Is there any penalty for using a larger number? builder.setIDRange(0, 5000); // Is there any penalty for using a larger number?
@@ -26,13 +26,6 @@ import net.minecraftforge.registries.IForgeRegistry;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public final class WizardryBlocks { public final class WizardryBlocks {
// I get registry events, they make sense - even I doubted myself with when to do various things in the load
// process,
// so I can see why the folks at Forge wanted to save us having to think about it.
// What I do not understand is why you would use @ObjectHolder for your own blocks and items. What's the point in
// making your code longer and more complicated, when you can just define the blocks as constants and register them
// later?
// Found a very nice way of registering things using arrays, which might make @ObjectHolder actually useful. // Found a very nice way of registering things using arrays, which might make @ObjectHolder actually useful.
// http://www.minecraftforge.net/forum/topic/49497-1112-is-using-registryevent-this-way-ok/ // http://www.minecraftforge.net/forum/topic/49497-1112-is-using-registryevent-this-way-ok/
@@ -17,12 +17,11 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public final class WizardryEnchantments { public final class WizardryEnchantments {
// At the moment these enchantments generate on books in dungeon chests due to a bad bit of code // At the moment these enchantments generate on books in dungeon chests due to a bad bit of code (EnchantRandomly:49).
// (EnchantRandomly:50).
// No idea how to fix this because I have no way of hooking into that code... removing the enchantments from the // No idea how to fix this because I have no way of hooking into that code... removing the enchantments from the
// registry works, but breaks everything else! // registry works, but breaks everything else!
// TODO: For the time being, a dynamic solution will have to do, i.e. intercept the book when it is generated and // For the time being, a dynamic solution will have to do, i.e. intercept the book when it is generated and
// reassign its enchantment. // reassign its enchantment.
// All of these have custom classes, so the unlocalised name (referred to simply as 'name' for enchantments) is // All of these have custom classes, so the unlocalised name (referred to simply as 'name' for enchantments) is
@@ -1,74 +1,18 @@
package electroblob.wizardry.registry; package electroblob.wizardry.registry;
import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import electroblob.wizardry.Wizardry; import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.Tier; import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.entity.EntityArc; import electroblob.wizardry.entity.EntityArc;
import electroblob.wizardry.entity.EntityMeteor; import electroblob.wizardry.entity.EntityMeteor;
import electroblob.wizardry.entity.EntityShield; import electroblob.wizardry.entity.EntityShield;
import electroblob.wizardry.entity.construct.EntityArrowRain; import electroblob.wizardry.entity.construct.*;
import electroblob.wizardry.entity.construct.EntityBlackHole; import electroblob.wizardry.entity.living.*;
import electroblob.wizardry.entity.construct.EntityBlizzard; import electroblob.wizardry.entity.projectile.*;
import electroblob.wizardry.entity.construct.EntityBubble;
import electroblob.wizardry.entity.construct.EntityDecay;
import electroblob.wizardry.entity.construct.EntityEarthquake;
import electroblob.wizardry.entity.construct.EntityFireRing;
import electroblob.wizardry.entity.construct.EntityFireSigil;
import electroblob.wizardry.entity.construct.EntityForcefield;
import electroblob.wizardry.entity.construct.EntityFrostSigil;
import electroblob.wizardry.entity.construct.EntityHailstorm;
import electroblob.wizardry.entity.construct.EntityHammer;
import electroblob.wizardry.entity.construct.EntityHealAura;
import electroblob.wizardry.entity.construct.EntityIceSpike;
import electroblob.wizardry.entity.construct.EntityLightningPulse;
import electroblob.wizardry.entity.construct.EntityLightningSigil;
import electroblob.wizardry.entity.construct.EntityTornado;
import electroblob.wizardry.entity.living.EntityBlazeMinion;
import electroblob.wizardry.entity.living.EntityDecoy;
import electroblob.wizardry.entity.living.EntityEvilWizard;
import electroblob.wizardry.entity.living.EntityIceGiant;
import electroblob.wizardry.entity.living.EntityIceWraith;
import electroblob.wizardry.entity.living.EntityLightningWraith;
import electroblob.wizardry.entity.living.EntityMagicSlime;
import electroblob.wizardry.entity.living.EntityPhoenix;
import electroblob.wizardry.entity.living.EntityShadowWraith;
import electroblob.wizardry.entity.living.EntitySilverfishMinion;
import electroblob.wizardry.entity.living.EntitySkeletonMinion;
import electroblob.wizardry.entity.living.EntitySpiderMinion;
import electroblob.wizardry.entity.living.EntitySpiritHorse;
import electroblob.wizardry.entity.living.EntitySpiritWolf;
import electroblob.wizardry.entity.living.EntityStormElemental;
import electroblob.wizardry.entity.living.EntityWitherSkeletonMinion;
import electroblob.wizardry.entity.living.EntityWizard;
import electroblob.wizardry.entity.living.EntityZombieMinion;
import electroblob.wizardry.entity.projectile.EntityDarknessOrb;
import electroblob.wizardry.entity.projectile.EntityDart;
import electroblob.wizardry.entity.projectile.EntityFirebolt;
import electroblob.wizardry.entity.projectile.EntityFirebomb;
import electroblob.wizardry.entity.projectile.EntityForceArrow;
import electroblob.wizardry.entity.projectile.EntityForceOrb;
import electroblob.wizardry.entity.projectile.EntityIceCharge;
import electroblob.wizardry.entity.projectile.EntityIceLance;
import electroblob.wizardry.entity.projectile.EntityIceShard;
import electroblob.wizardry.entity.projectile.EntityLightningArrow;
import electroblob.wizardry.entity.projectile.EntityLightningDisc;
import electroblob.wizardry.entity.projectile.EntityMagicMissile;
import electroblob.wizardry.entity.projectile.EntityPoisonBomb;
import electroblob.wizardry.entity.projectile.EntitySmokeBomb;
import electroblob.wizardry.entity.projectile.EntitySpark;
import electroblob.wizardry.entity.projectile.EntitySparkBomb;
import electroblob.wizardry.entity.projectile.EntityThunderbolt;
import electroblob.wizardry.loot.RandomSpell; import electroblob.wizardry.loot.RandomSpell;
import electroblob.wizardry.loot.WizardSpell; import electroblob.wizardry.loot.WizardSpell;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.tileentity.*;
import electroblob.wizardry.tileentity.TileEntityMagicLight;
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
import electroblob.wizardry.tileentity.TileEntityStatue;
import electroblob.wizardry.tileentity.TileEntityTimer;
import electroblob.wizardry.util.WizardryUtilities; import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EnumCreatureType; import net.minecraft.entity.EnumCreatureType;
@@ -91,6 +35,8 @@ import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapelessOreRecipe; import net.minecraftforge.oredict.ShapelessOreRecipe;
import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistry;
import java.util.List;
/** /**
* Class responsible for registering all the things that don't have (or need) instances: entities, loot tables, recipes, * Class responsible for registering all the things that don't have (or need) instances: entities, loot tables, recipes,
* etc. * etc.
@@ -101,13 +47,11 @@ import net.minecraftforge.registries.IForgeRegistry;
@Mod.EventBusSubscriber @Mod.EventBusSubscriber
public final class WizardryRegistry { public final class WizardryRegistry {
// NOTE: In 1.12, recipes have a registry (they can still stay here though since we don't keep references to them)
/** Called from the preInit method in the main mod class to register the custom dungeon loot. */ /** Called from the preInit method in the main mod class to register the custom dungeon loot. */
public static void registerLoot(){ public static void registerLoot(){
/* Loot tables work as follows: Minecraft goes through each pool in turn. For each pool, it does a certain /* Loot tables work as follows: Minecraft goes through each pool in turn. For each pool, it does a certain
* number or rolls, which can either be set to always be one number or a random number from a range. Each roll, * number of rolls, which can either be set to always be one number or a random number from a range. Each roll,
* it generates one stack of a single random entry in that pool, weighted according to the weights of the * it generates one stack of a single random entry in that pool, weighted according to the weights of the
* entries. Functions allow properties of that stack (stack size, damage, nbt) to be set, and even allow it to * entries. Functions allow properties of that stack (stack size, damage, nbt) to be set, and even allow it to
* be replaced dynamically with a completely different item (though there's very little point in doing that as * be replaced dynamically with a completely different item (though there's very little point in doing that as
@@ -246,7 +190,7 @@ public final class WizardryRegistry {
/** Now only deals with the dynamic crafting recipes and the smelting recipes. */ /** Now only deals with the dynamic crafting recipes and the smelting recipes. */
@SubscribeEvent @SubscribeEvent
public static void registerRecipes(RegistryEvent.Register<IRecipe> event){ public static void registerRecipes(RegistryEvent.Register<IRecipe> event){
IForgeRegistry<IRecipe> registry = event.getRegistry(); IForgeRegistry<IRecipe> registry = event.getRegistry();
FurnaceRecipes.instance().addSmeltingRecipeForBlock(WizardryBlocks.crystal_ore, new ItemStack(WizardryItems.magic_crystal), 0.5f); FurnaceRecipes.instance().addSmeltingRecipeForBlock(WizardryBlocks.crystal_ore, new ItemStack(WizardryItems.magic_crystal), 0.5f);
@@ -8,8 +8,7 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/** /**
* Class responsible for defining, storing and registering all of wizardry's sound events. For some reason, these worked * Class responsible for defining, storing and registering all of wizardry's sound events.
* in the beta versions despite not being registered...
* *
* @author Electroblob * @author Electroblob
* @since Wizardry 2.1 * @since Wizardry 2.1
@@ -59,7 +59,6 @@ public class Light extends Spell {
BlockPos pos = new BlockPos(x, y, z); BlockPos pos = new BlockPos(x, y, z);
if(world.isAirBlock(pos)){ if(world.isAirBlock(pos)){
// world.playSound(x, y, z, "sound.ambient.cave.cave", 1.0f, 1.5f, false);
if(!world.isRemote){ if(!world.isRemote){
world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState()); world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityTimer){ if(world.getTileEntity(pos) instanceof TileEntityTimer){
@@ -272,7 +272,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
return modID; return modID;
} }
/** Returns the ResourceLocation for this spell's icon. */ /** Returns the {@code ResourceLocation} for this spell's icon. */
public final ResourceLocation getIcon(){ public final ResourceLocation getIcon(){
return icon; return icon;
} }
@@ -368,7 +368,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 * Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would be
* returned by Spell.getSpells(Spell.allSpells).size(), but this method is more efficient. * returned by {@code Spell.getSpells(Spell.allSpells).size()}, but this method is more efficient.
*/ */
public static int getTotalSpellCount(){ public static int getTotalSpellCount(){
return registry.getValuesCollection().size() - 1; return registry.getValuesCollection().size() - 1;
@@ -40,7 +40,7 @@ import net.minecraft.world.World;
*/ */
public class SpellConstruct<T extends EntityMagicConstruct> extends Spell { public class SpellConstruct<T extends EntityMagicConstruct> extends Spell {
/** A factory that creates projectile entities. */ /** A factory that creates construct entities. */
protected final Function<World, T> constructFactory; protected final Function<World, T> constructFactory;
/** The base lifetime of the construct created by this spell, or -1 if the construct does not despawn. */ /** The base lifetime of the construct created by this spell, or -1 if the construct does not despawn. */
protected final int baseDuration; protected final int baseDuration;
@@ -276,6 +276,10 @@ public abstract class SpellRay extends Spell {
spawnParticle(world, x, y, z, velocity.x, velocity.y, velocity.z); spawnParticle(world, x, y, z, velocity.x, velocity.y, velocity.z);
} }
} }
// The exact behaviour of the returned values of the following three methods can be a little confusing. Normally,
// either onEntityHit or onBlockHit (or both) will return true when the spell succeeded in hitting the block or
// entity, and false if not (note that those two methods are mutually exclusive). If false is returned, onMiss will
// be called - onMiss will never be called if either of the other methods returns true.
/** /**
* Called when the spell hits an entity. Will never be called if ignoreEntities is true. * Called when the spell hits an entity. Will never be called if ignoreEntities is true.
@@ -26,10 +26,8 @@ public class ContainerArcaneWorkbench extends Container {
/** The arcane workbench tile entity associated with this container. */ /** The arcane workbench tile entity associated with this container. */
public TileEntityArcaneWorkbench tileentity; public TileEntityArcaneWorkbench tileentity;
public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_crystal");
"gui/empty_slot_crystal"); public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_upgrade");
public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID,
"gui/empty_slot_upgrade");
public static final int CRYSTAL_SLOT = 8; public static final int CRYSTAL_SLOT = 8;
public static final int CENTRE_SLOT = 9; public static final int CENTRE_SLOT = 9;
@@ -260,7 +258,7 @@ public class ContainerArcaneWorkbench extends Container {
if(((IWorkbenchItem)centre.getStack().getItem()) if(((IWorkbenchItem)centre.getStack().getItem())
.onApplyButtonPressed(player, centre, this.getSlot(CRYSTAL_SLOT), this.getSlot(UPGRADE_SLOT), spellBooks)){ .onApplyButtonPressed(player, centre, this.getSlot(CRYSTAL_SLOT), this.getSlot(UPGRADE_SLOT), spellBooks)){
// TODO: Sound and possibly animation for spell binding // Probably don't need to do anything here, unless we're going to use packets to control the animation
} }
} }
} }
@@ -26,8 +26,8 @@ import net.minecraftforge.fml.common.network.ByteBufUtils;
* that not all wand upgrades affect spells). SpellModifiers objects are <i>mutable</i>, so you can simply change the * that not all wand upgrades affect spells). SpellModifiers objects are <i>mutable</i>, so you can simply change the
* values they contain to modify the spell. * values they contain to modify the spell.
* <p> * <p>
* To use a SpellModifiers object within the <code>Spell.cast</code> methods, simply retrieve the desired multiplier * To use a SpellModifiers object within the <code>Spell.cast</code> methods, simply retrieve the desired modifier
* using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the multiplier is * using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the modifier is
* not from a wand upgrade. * not from a wand upgrade.
* *
* @author Electroblob * @author Electroblob
@@ -139,6 +139,9 @@ public final class SpellModifiers {
buf.writeFloat(entry.getValue()); buf.writeFloat(entry.getValue());
} }
} }
// These two don't use the Map <-> NBT methods in WizardryUtilities because it's better to use the strings as keys
// themselves rather than storing them separately.
/** /**
* Creates a new SpellModifiers object from the given NBTTagCompound. The NBTTagCompound should have 1 or more float * Creates a new SpellModifiers object from the given NBTTagCompound. The NBTTagCompound should have 1 or more float
@@ -80,17 +80,15 @@ import net.minecraftforge.fml.relauncher.SideOnly;
*/ */
public final class WizardryUtilities { public final class WizardryUtilities {
/** /** Constant which is simply an array of the four armour slots. (Could've sworn this exists somewhere in vanilla,
* Constant which is simply an array of the four armour slots. (Could've sworn this exists somewhere in vanilla, but * but I can't find it anywhere...) */
* I can't find it anywhere...)
*/
public static final EntityEquipmentSlot[] ARMOUR_SLOTS; public static final EntityEquipmentSlot[] ARMOUR_SLOTS;
/** Changed to a constant in wizardry 2.1, since this is a lot more efficient. */ /** Changed to a constant in wizardry 2.1, since this is a lot more efficient. */
private static final DataParameter<Boolean> POWERED; private static final DataParameter<Boolean> POWERED;
static{ static {
// The list of slots needs to be mutable. // The list of slots needs to be mutable.
List<EntityEquipmentSlot> slots = new ArrayList<EntityEquipmentSlot>( List<EntityEquipmentSlot> slots = new ArrayList<>(
Arrays.asList(EntityEquipmentSlot.values())); Arrays.asList(EntityEquipmentSlot.values()));
slots.removeIf(slot -> slot.getSlotType() != Type.ARMOR); slots.removeIf(slot -> slot.getSlotType() != Type.ARMOR);
ARMOUR_SLOTS = slots.toArray(new EntityEquipmentSlot[0]); ARMOUR_SLOTS = slots.toArray(new EntityEquipmentSlot[0]);
@@ -146,13 +144,11 @@ public final class WizardryUtilities {
/** /**
* Returns whether the block at the given coordinates is unbreakable in survival mode. In vanilla this is true for * Returns whether the block at the given coordinates is unbreakable in survival mode. In vanilla this is true for
* bedrock and end portal frame, for example. This is a shortcut for * bedrock and end portal frame, for example. This is a shortcut for:<p>
* world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f. Not much of a shortcut any more, since block ids * {@code world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f}
* have been phased out.
*/ */
public static boolean isBlockUnbreakable(World world, BlockPos pos){ public static boolean isBlockUnbreakable(World world, BlockPos pos){
return world.isAirBlock(new BlockPos(pos)) ? false return !world.isAirBlock(new BlockPos(pos)) && world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f;
: world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f;
} }
/** /**
@@ -160,11 +156,9 @@ public final class WizardryUtilities {
* Liquids and other blocks that cannot be built on top of do not count, but stuff like signs does. (Technically any * Liquids and other blocks that cannot be built on top of do not count, but stuff like signs does. (Technically any
* block is allowed to be the floor according to the code, but seeing as it searches upwards and non-solid blocks * block is allowed to be the floor according to the code, but seeing as it searches upwards and non-solid blocks
* usually need a supporting block, the floor is likely to always be solid). * usually need a supporting block, the floor is likely to always be solid).
* *
* @param world * @param world The world to search in
* @param x The x coordinate to search in * @param pos The coordinates to search from
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search. * @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the
* floor as would be seen in the debug screen when the player is standing on it. * floor as would be seen in the debug screen when the player is standing on it.
@@ -189,10 +183,8 @@ public final class WizardryUtilities {
* Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. Only * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. Only
* works if the block above the floor is actually air and the floor is solid or a liquid. * works if the block above the floor is actually air and the floor is solid or a liquid.
* *
* @param world * @param world The world to search in
* @param x The x coordinate to search in * @param pos The coordinates to search from
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search. * @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the
* floor as would be seen in the debug screen when the player is standing on it. * floor as would be seen in the debug screen when the player is standing on it.
@@ -216,11 +208,9 @@ public final class WizardryUtilities {
/** /**
* Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords.
* Everything that is not air is treated as floor, even stuff that can't be walked on. * Everything that is not air is treated as floor, even stuff that can't be walked on.
* *
* @param world * @param world The world to search in
* @param x The x coordinate to search in * @param pos The coordinates to search from
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search. * @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the
* floor as would be seen in the debug screen when the player is standing on it. * floor as would be seen in the debug screen when the player is standing on it.
@@ -300,7 +290,7 @@ public final class WizardryUtilities {
/** /**
* Gets the blockstate of the block the specified entity is standing on. Uses * Gets the blockstate of the block the specified entity is standing on. Uses
* {@link MathHelper#floor_double(double)} because casting to int will not return the correct coordinate when x or z * {@link MathHelper#floor(double)} because casting to int will not return the correct coordinate when x or z
* is negative. * is negative.
*/ */
public static IBlockState getBlockEntityIsStandingOn(Entity entity){ public static IBlockState getBlockEntityIsStandingOn(Entity entity){
@@ -331,7 +321,7 @@ public final class WizardryUtilities {
* this does not exclude any entities; if any specific entities are to be excluded this must be checked when * this does not exclude any entities; if any specific entities are to be excluded this must be checked when
* iterating through the list. * iterating through the list.
* *
* @see {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)} * @see WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)
* @param radius The search radius * @param radius The search radius
* @param x The x coordinate to search around * @param x The x coordinate to search around
* @param y The y coordinate to search around * @param y The y coordinate to search around
@@ -396,12 +386,11 @@ public final class WizardryUtilities {
/** /**
* Returns the entity riding the given entity, or null if there is none. Allows for neater code now that entities * Returns the entity riding the given entity, or null if there is none. Allows for neater code now that entities
* have a list of passengers, because it is necessary to check that the list is not null or empty first. * have a list of passengers, because it is necessary to check that the list is not empty first.
*/ */
@Nullable @Nullable
public static Entity getRider(Entity entity){ public static Entity getRider(Entity entity){
return entity.getPassengers() != null && !entity.getPassengers().isEmpty() ? entity.getPassengers().get(0) return !entity.getPassengers().isEmpty() ? entity.getPassengers().get(0) : null;
: null;
} }
/** /**
@@ -542,8 +531,8 @@ public final class WizardryUtilities {
/** /**
* Turns the given creeper into a charged creeper. In 1.10, this requires reflection since the DataManager keys are * Turns the given creeper into a charged creeper. In 1.10, this requires reflection since the DataManager keys are
* private. (You <i>could</i> call {@link EntityCreeper#onStruckByLightning(...)} and then heal it and extinguish * private. (You <i>could</i> call {@link EntityCreeper#onStruckByLightning(EntityLightningBolt)} and then heal it
* it, but that's a bit awkward.) * and extinguish it, but that's a bit awkward, and it'll trigger events and stuff...)
*/ */
// The reflection here only gets done once to initialise the POWERED field, so it's not a performance issue at all. // The reflection here only gets done once to initialise the POWERED field, so it's not a performance issue at all.
public static void chargeCreeper(EntityCreeper creeper){ public static void chargeCreeper(EntityCreeper creeper){
@@ -578,7 +567,7 @@ public final class WizardryUtilities {
* Helper method which performs a ray trace for blocks and entities from an entity's eye position in the direction * Helper method which performs a ray trace for blocks and entities from an entity's eye position in the direction
* they are looking, over a specified range, using {@link WizardryUtilities#rayTrace(World, Vec3d, Vec3d, float, * they are looking, over a specified range, using {@link WizardryUtilities#rayTrace(World, Vec3d, Vec3d, float,
* boolean, Class, Predicate)}. Aim assist is zero, the entity type is simply {@code Entity} (all entities), and the * boolean, Class, Predicate)}. Aim assist is zero, the entity type is simply {@code Entity} (all entities), and the
* filter removes the given entity and allows all others. * filter removes the given entity and any dying entities and allows all others.
* *
* @param world The world in which to perform the ray trace. * @param world The world in which to perform the ray trace.
* @param entity The entity from which to perform the ray trace. The ray trace will start from this entity's eye * @param entity The entity from which to perform the ray trace. The ray trace will start from this entity's eye