Cleanup
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user