That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -1,33 +1,32 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import electroblob.wizardry.client.particle.ParticleWizardry;
|
||||
import electroblob.wizardry.item.ItemSpectralBow;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.PacketClairvoyance;
|
||||
import electroblob.wizardry.packet.PacketGlyphData;
|
||||
import electroblob.wizardry.packet.PacketNPCCastSpell.Message;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.packet.*;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.translation.I18n;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The common proxy for wizardry, serving the usual purpose of dealing with all things that need to be handled
|
||||
* differently on the client and the server. A lot of the methods here appear to do absolutely nothing; this is because
|
||||
* they do client-only things which are only handled in the client proxy.
|
||||
*
|
||||
* @see {@link electroblob.wizardry.client.ClientProxy}
|
||||
* @see electroblob.wizardry.client.ClientProxy ClientProxy
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
@@ -51,15 +50,19 @@ public class CommonProxy {
|
||||
|
||||
public void registerResourceReloadListeners(){}
|
||||
|
||||
public void registerSoundEventListener(){}
|
||||
|
||||
public void registerAtlasMarkers(){}
|
||||
|
||||
// SECTION Particles
|
||||
// ===============================================================================================================
|
||||
|
||||
/** Called from init() in the main mod class to initialise the particle factories. */
|
||||
public void initParticleFactories(){} // Does nothing since particles are client-side only
|
||||
public void registerParticles(){} // Does nothing since particles are client-side only
|
||||
|
||||
/** Creates a new particle of the specified type from the appropriate particle factory. <i>Does not actually spawn the
|
||||
* particle; use {@link electroblob.wizardry.util.ParticleBuilder ParticleBuilder} to spawn particles.</i> */
|
||||
public ParticleWizardry createParticle(Type type, World world, double x, double y, double z){
|
||||
public electroblob.wizardry.client.particle.ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -70,19 +73,23 @@ public class CommonProxy {
|
||||
// SECTION Items
|
||||
// ===============================================================================================================
|
||||
|
||||
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translated name of the scroll, taking spell discovery into account. If, for some reason, this gets
|
||||
* called server-side, it uses the deprecated server version of I18n, with a warning. Since any likely server-side
|
||||
* use of this method will be for text-based logic purposes (like Bibliocraft's book checking system), and since no
|
||||
* particular player instance can be accessed, spell discovery is ignored.
|
||||
* called server-side, it uses the deprecated server version of I18n. Since any likely server-side
|
||||
* use of this method will be for text-based logic purposes (like Bibliocraft's book checking system), and since
|
||||
* no particular player instance can be accessed, spell discovery is ignored.
|
||||
*/
|
||||
public String getScrollDisplayName(ItemStack scroll){
|
||||
|
||||
Spell spell = Spell.get(scroll.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(scroll.getItemDamage());
|
||||
|
||||
return I18n.translateToLocalFormatted("item." + Wizardry.MODID + ":scroll.name",
|
||||
I18n.translateToLocal("spell." + spell.getUnlocalisedName())).trim();
|
||||
@@ -92,29 +99,53 @@ public class CommonProxy {
|
||||
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
|
||||
}
|
||||
|
||||
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style)}, but style defaults to light grey. */
|
||||
public void addMultiLineDescription(List<String> tooltip, String key){
|
||||
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a multi-line description to the given tooltip list. The description is first translated using the given
|
||||
* translation key, then the formatting code for the given style is appended, and finally the string is word-wrapped
|
||||
* to the standard width (100).
|
||||
* @param tooltip The tooltip list to add to
|
||||
* @param key The translation key for the description
|
||||
* @param style A style to apply
|
||||
*/
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){}
|
||||
|
||||
// SECTION Packet Handlers
|
||||
// ===============================================================================================================
|
||||
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){
|
||||
}
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){}
|
||||
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){
|
||||
}
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){}
|
||||
|
||||
public void handleCastSpellPacket(PacketCastSpell.Message message){
|
||||
}
|
||||
public void handleEmitterDataPacket(PacketEmitterData.Message message){}
|
||||
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){
|
||||
}
|
||||
public void handleCastSpellPacket(PacketCastSpell.Message message){}
|
||||
|
||||
public void handleNPCCastSpellPacket(Message message){
|
||||
}
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){}
|
||||
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){
|
||||
}
|
||||
public void handleNPCCastSpellPacket(PacketNPCCastSpell.Message message){}
|
||||
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){
|
||||
}
|
||||
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){}
|
||||
|
||||
public void handleCastSpellAtPosPacket(PacketCastSpellAtPos.Message message){}
|
||||
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){}
|
||||
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){}
|
||||
|
||||
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){}
|
||||
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){}
|
||||
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){}
|
||||
|
||||
public void handlePossessionPacket(PacketPossession.Message message){}
|
||||
|
||||
public void handleConquerShrinePacket(PacketConquerShrine.Message message){}
|
||||
|
||||
// SECTION Misc
|
||||
// ===============================================================================================================
|
||||
@@ -122,6 +153,8 @@ public class CommonProxy {
|
||||
public void setToNumberSliderEntry(Property property){}
|
||||
|
||||
public void setToHUDChooserEntry(Property property){}
|
||||
|
||||
public void setToNamedBooleanEntry(Property property){}
|
||||
|
||||
// public void setToEntityNameEntry(Property property){}
|
||||
|
||||
@@ -137,6 +170,75 @@ public class CommonProxy {
|
||||
*/
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound which moves with the given entity.
|
||||
*
|
||||
* @param entity The source of the sound
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param start The starting SoundEvent to play
|
||||
* @param loop The main looped SoundEvent to play
|
||||
* @param end The ending SoundEvent to play
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
*/
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound which moves with the given entity.
|
||||
*
|
||||
* @param entity The source of the sound
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param sounds An array of the sound events to play, which must have three elements in the order: start, loop, end
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @throws IllegalArgumentException if the given array contains less than 3 sound events
|
||||
*/
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent[] sounds, SoundCategory category, float volume, float pitch){
|
||||
if(sounds.length < 3) throw new IllegalArgumentException("Tried to play a continuous spell sound using an array "
|
||||
+ "of sound events, but the given array contained less than 3 sound events!");
|
||||
playSpellSoundLoop(entity, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound at the given position.
|
||||
* @param world The world in which to play the sound
|
||||
* @param x The x coordinate to play the sound at
|
||||
* @param y The y coordinate to play the sound at
|
||||
* @param z The z coordinate to play the sound at
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param start The starting SoundEvent to play
|
||||
* @param loop The main looped SoundEvent to play
|
||||
* @param end The ending SoundEvent to play
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @param duration The duration of the sound, or -1 to link it to a dispenser at the given coordinates
|
||||
*/
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch, int duration){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound at the given position.
|
||||
*
|
||||
* @param world The world in which to play the sound
|
||||
* @param x The x coordinate to play the sound at
|
||||
* @param y The y coordinate to play the sound at
|
||||
* @param z The z coordinate to play the sound at
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param sounds An array of the sound events to play, which must have three elements in the order: start, loop, end
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @param duration The duration of the sound, or -1 to link it to a dispenser at the given coordinates
|
||||
* @throws IllegalArgumentException if the given array contains less than 3 sound events
|
||||
*/
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent[] sounds, SoundCategory category, float volume, float pitch, int duration){
|
||||
if(sounds.length < 3) throw new IllegalArgumentException("Tried to play a continuous spell sound using an array "
|
||||
+ "of sound events, but the given array contained less than 3 sound events!");
|
||||
playSpellSoundLoop(world, x, y, z, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns
|
||||
* null on the server side.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,22 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import electroblob.wizardry.command.CommandCastSpell;
|
||||
import electroblob.wizardry.command.CommandDiscoverSpell;
|
||||
import electroblob.wizardry.command.CommandSetAlly;
|
||||
import electroblob.wizardry.command.CommandViewAllies;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
|
||||
import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration;
|
||||
import electroblob.wizardry.misc.Forfeit;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryRegistry;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.CustomSoundCategory;
|
||||
import electroblob.wizardry.util.SpellProperties;
|
||||
import electroblob.wizardry.worldgen.*;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.Capability.IStorage;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.Mod.EventHandler;
|
||||
@@ -30,12 +29,13 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* <i>"Electroblob's Wizardry adds an RPG-like system of spells to Minecraft, with the aim of being as playable as
|
||||
* possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master
|
||||
* the arcane! - But you knew that, right?"</i>
|
||||
* <p>
|
||||
* <p></p>
|
||||
* Main mod class for Wizardry. Contains the logger and settings instances, along with all the other stuff that's normally
|
||||
* in a main mod class.
|
||||
* @author Electroblob
|
||||
@@ -50,9 +50,9 @@ public class Wizardry {
|
||||
public static final String NAME = "Electroblob's Wizardry";
|
||||
/**
|
||||
* The version number for this version of wizardry. The following system is used for version numbers:
|
||||
* <p>
|
||||
* <p></p>
|
||||
* <center><b>[major Minecraft version].[major mod version].[minor mod version/patch]
|
||||
* <p>
|
||||
* <p></p>
|
||||
* </center></b> The major mod version is consistent across Minecraft versions, i.e. Wizardry 1.1 has the same
|
||||
* features as Wizardry 2.1, but they are for different versions of Minecraft and have separate minor versioning.
|
||||
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
|
||||
@@ -60,56 +60,28 @@ public class Wizardry {
|
||||
*/
|
||||
public static final String VERSION = "4.2.0";
|
||||
|
||||
// IDEA: Improve the algorithm that finds a place to summon creatures to take walls into account.
|
||||
// IDEA: Replace all uses of Math.cos and Math.sin with MathHelper versions
|
||||
// IDEA: Triggering of inbuilt Forge events in relevant places?
|
||||
// IDEA: Abstract the vanilla particles behind the particle builder
|
||||
|
||||
/* Minor bugs that need fixing at some point:
|
||||
* - Player skin hat layer shows through wizard hats - Wizard armour breaks rather than just running out of mana (I
|
||||
* can't seem to replicate this bug, but I have had it happen to me before...)
|
||||
* - Shift-clicking a stack of special upgrades when in the arcane workbench causes the whole stack to be
|
||||
* transferred when it should be just one (this is a bug with vanilla as well - try putting a stack of bottles into
|
||||
* a brewing stand). I have at least made it so only one gets used now, so it has no impact on the game.
|
||||
* - When a spell is on cooldown, you can't break blocks when holding a wand. */
|
||||
|
||||
// TODO: So somehow I have managed to overlook the fact that health is actually a float. What this means is that
|
||||
// I can make the healing spells use damage multipliers - hurrah!
|
||||
|
||||
// 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
|
||||
// (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java)
|
||||
// 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
|
||||
// loop as well as a start and end sound
|
||||
// TODO: EntityMagicArrow needs attention
|
||||
// TODO: Go over particle spawning on projectile impact and make sure hitvec is used wherever appropriate
|
||||
// TODO: Replace spell IDs in packets with ResourceLocation strings
|
||||
// TODO: Forcefield needs looking at, esp. with regards to projectiles and explosions
|
||||
// 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.
|
||||
// TODO: See what can be done with the illager spell sounds (and go over sounds in general)
|
||||
// TODO: Fix possession
|
||||
// TODO: Fix charge
|
||||
|
||||
/** Static instance of the {@link Settings} object for Wizardry. */
|
||||
public static final Settings settings = new Settings();
|
||||
|
||||
/** Static instance of the {@link Logger} object for Wizardry.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* Logging conventions for wizardry (only these levels are used currently):
|
||||
* <p>
|
||||
* <p></p>
|
||||
* - <b>ERROR</b>: Anything that threw an exception; may or may not crash the game.<br>
|
||||
* - <b>WARN</b>: Anything that isn't supposed to happen during normal operation, but didn't throw an exception.<br>
|
||||
* - <b>INFO</b>: Anything that might happen during normal mod operation that the user needs to know about. */
|
||||
public static Logger logger;
|
||||
|
||||
// private static Pattern entityNamePattern;
|
||||
|
||||
// EventManager
|
||||
WizardryWorldGenerator generator = new WizardryWorldGenerator();
|
||||
|
||||
// The instance of wizardry that Forge uses.
|
||||
@Instance(Wizardry.MODID)
|
||||
public static Wizardry instance;
|
||||
@@ -123,40 +95,27 @@ public class Wizardry {
|
||||
|
||||
logger = event.getModLog();
|
||||
|
||||
proxy.registerResourceReloadListener();
|
||||
proxy.registerResourceReloadListeners();
|
||||
|
||||
settings.initConfig(event);
|
||||
|
||||
// Yes - by the looks of it, having an interface is completely unnecessary in this case.
|
||||
CapabilityManager.INSTANCE.register(WizardData.class, new IStorage<WizardData>(){
|
||||
// These methods are only called by Capability.writeNBT() or Capability.readNBT(), which in turn are
|
||||
// NEVER CALLED. Unless I'm missing some reflective invocation, that means this entire class serves only
|
||||
// to allow capabilities to be saved and loaded manually. What that would be useful for I don't know.
|
||||
// (If an API forces most users to write redundant code for no reason, it's not user friendly, is it?)
|
||||
// ... well, that's my rant for today!
|
||||
@Override
|
||||
public NBTBase writeNBT(Capability<WizardData> capability, WizardData instance, EnumFacing side){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(Capability<WizardData> capability, WizardData instance, EnumFacing side, NBTBase nbt){
|
||||
}
|
||||
}, WizardData::new);
|
||||
|
||||
WizardryRegistry.registerTileEntities();
|
||||
|
||||
WizardryRegistry.registerEntities();
|
||||
// The check for the generateLoot setting is now done within this method.
|
||||
WizardryRegistry.registerLoot();
|
||||
// Capabilities
|
||||
WizardData.register();
|
||||
DispenserCastingData.register();
|
||||
|
||||
// Register things that don't have registries
|
||||
WizardryBlocks.registerTileEntities();
|
||||
WizardryLoot.register();
|
||||
WizardryAdvancementTriggers.register();
|
||||
Forfeit.register();
|
||||
|
||||
// Moved to preInit, because apparently it has to be here now.
|
||||
// Client-side stuff (via proxies)
|
||||
proxy.registerRenderers();
|
||||
// It seems this also has to be here
|
||||
proxy.registerKeyBindings();
|
||||
|
||||
WizardryBaublesIntegration.init();
|
||||
WizardryAntiqueAtlasIntegration.init();
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -164,26 +123,41 @@ public class Wizardry {
|
||||
|
||||
settings.initConfigExtras();
|
||||
|
||||
// Event Handlers
|
||||
GameRegistry.registerWorldGenerator(generator, 0);
|
||||
MinecraftForge.EVENT_BUS.register(instance);
|
||||
// World generators
|
||||
// Weight is a misnomer, it's actually the priority (where lower numbers get generated first)
|
||||
// Literally nothing on typical 'weight' values here, there isn't even an upper limit
|
||||
// Examples I've managed to find:
|
||||
// - Tinker's construct slime islands use 25
|
||||
GameRegistry.registerWorldGenerator(new WorldGenCrystalOre(), 0);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenCrystalFlower(), 50);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenWizardTower(), 20);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenObelisk(), 20);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenShrine(), 20);
|
||||
|
||||
// This is for the config change and missing mappings events
|
||||
MinecraftForge.EVENT_BUS.register(instance); // Since there's already an instance we might as well use it
|
||||
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(this, new WizardryGuiHandler());
|
||||
WizardryPacketHandler.initPackets();
|
||||
|
||||
// Post-registry extras
|
||||
WizardryItems.populateWandMap();
|
||||
WizardryItems.populateArmourMap();
|
||||
WizardryItems.registerDispenseBehaviours();
|
||||
Spell.registry.forEach(Spell::init);
|
||||
SpellProperties.init();
|
||||
|
||||
// Client-side stuff (via proxies)
|
||||
proxy.initGuiBits();
|
||||
proxy.initParticleFactories();
|
||||
proxy.registerParticles();
|
||||
proxy.registerSoundEventListener();
|
||||
|
||||
WizardrySounds.SPELLS = CustomSoundCategory.add(Wizardry.MODID + ":spells");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void postInit(FMLPostInitializationEvent event){
|
||||
// This needs to be here or it won't necessarily include all the mods' entities.
|
||||
// TODO: Re-implement this when the Forge bug is fixed.
|
||||
/* Doesn't seem to be doing anything... String entityNames = ""; for(Object name :
|
||||
* EntityList.classToStringMapping.values()){ if(name instanceof String){ entityNames = entityNames + name +
|
||||
* '|'; } } // Cuts off the last '|' entityNames = entityNames.substring(0, entityNames.length()-1);
|
||||
* entityNamePattern = Pattern.compile(entityNames); */
|
||||
proxy.initialiseLayers();
|
||||
WizardryTabs.sort();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -205,23 +179,30 @@ public class Wizardry {
|
||||
// 2.1 changed some item ids, so this fixes them for existing worlds
|
||||
// Nobody updates minecraft versions when using mods, but I may as well leave this here just in case.
|
||||
@SubscribeEvent
|
||||
public static void onMissingMappingEvent(RegistryEvent.MissingMappings<Item> event){
|
||||
public static void onMissingItemMappingEvent(RegistryEvent.MissingMappings<Item> event){
|
||||
// Just get, not getAll, since the mod id didn't change!
|
||||
for(RegistryEvent.MissingMappings.Mapping<Item> mapping : event.getAllMappings()){
|
||||
if(mapping.key.getResourceDomain().equals(Wizardry.MODID)){
|
||||
if(mapping.key.getNamespace().equals(Wizardry.MODID)){
|
||||
|
||||
Item replacement;
|
||||
|
||||
switch(mapping.key.getResourcePath()){
|
||||
switch(mapping.key.getPath()){
|
||||
|
||||
case "wand_basic": replacement = WizardryItems.magic_wand; break;
|
||||
case "wand_basic_fire": replacement = WizardryItems.basic_fire_wand; break;
|
||||
case "wand_basic_ice": replacement = WizardryItems.basic_ice_wand; break;
|
||||
case "wand_basic_lightning": replacement = WizardryItems.basic_lightning_wand; break;
|
||||
case "wand_basic_necromancy": replacement = WizardryItems.basic_necromancy_wand; break;
|
||||
case "wand_basic_earth": replacement = WizardryItems.basic_earth_wand; break;
|
||||
case "wand_basic_sorcery": replacement = WizardryItems.basic_sorcery_wand; break;
|
||||
case "wand_basic_healing": replacement = WizardryItems.basic_healing_wand; break;
|
||||
case "wand_basic_fire": replacement = WizardryItems.novice_fire_wand; break;
|
||||
case "wand_basic_ice": replacement = WizardryItems.novice_ice_wand; break;
|
||||
case "wand_basic_lightning": replacement = WizardryItems.novice_lightning_wand; break;
|
||||
case "wand_basic_necromancy": replacement = WizardryItems.novice_necromancy_wand; break;
|
||||
case "wand_basic_earth": replacement = WizardryItems.novice_earth_wand; break;
|
||||
case "wand_basic_sorcery": replacement = WizardryItems.novice_sorcery_wand; break;
|
||||
case "wand_basic_healing": replacement = WizardryItems.novice_healing_wand; break;
|
||||
case "basic_fire_wand": replacement = WizardryItems.novice_fire_wand; break;
|
||||
case "basic_ice_wand": replacement = WizardryItems.novice_ice_wand; break;
|
||||
case "basic_lightning_wand": replacement = WizardryItems.novice_lightning_wand; break;
|
||||
case "basic_necromancy_wand": replacement = WizardryItems.novice_necromancy_wand; break;
|
||||
case "basic_earth_wand": replacement = WizardryItems.novice_earth_wand; break;
|
||||
case "basic_sorcery_wand": replacement = WizardryItems.novice_sorcery_wand; break;
|
||||
case "basic_healing_wand": replacement = WizardryItems.novice_healing_wand; break;
|
||||
case "wand_apprentice": replacement = WizardryItems.apprentice_wand; break;
|
||||
case "wand_apprentice_fire": replacement = WizardryItems.apprentice_fire_wand; break;
|
||||
case "wand_apprentice_ice": replacement = WizardryItems.apprentice_ice_wand; break;
|
||||
@@ -264,4 +245,13 @@ public class Wizardry {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onMissingSpellMappingEvent(RegistryEvent.MissingMappings<Spell> event){
|
||||
for(RegistryEvent.MissingMappings.Mapping<Spell> mapping : event.getAllMappings()){
|
||||
if(mapping.key.getNamespace().equals(Wizardry.MODID)){
|
||||
if(mapping.key.getPath().equals("firestorm")) mapping.remap(Spells.fire_breath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +1,137 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.entity.EntityArc;
|
||||
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.item.ItemWizardArmour;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.item.IManaStoringItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketSyncAdvancements;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.FreezingWeapon;
|
||||
import electroblob.wizardry.spell.ImbueWeapon;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.monster.IMob;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.ContainerPlayer;
|
||||
import net.minecraft.inventory.ContainerWorkbench;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootEntry;
|
||||
import net.minecraft.world.storage.loot.LootEntryTable;
|
||||
import net.minecraft.world.storage.loot.LootPool;
|
||||
import net.minecraft.world.storage.loot.RandomValueRange;
|
||||
import net.minecraft.world.storage.loot.conditions.LootCondition;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.event.LootTableLoadEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
|
||||
import net.minecraftforge.event.entity.living.*;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only a few
|
||||
* miscellaneous things that don't make much sense anywhere else, or that are better kept together. Previously, this was
|
||||
* a gigantic class with about half of the entire mod's logic in it!
|
||||
* General-purpose event handler for things that don't fit anywhere else or groups of related behaviours that are better
|
||||
* kept together. As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only
|
||||
* a few miscellaneous things that don't make much sense anywhere else, or that are better kept together (previously,
|
||||
* this was a gigantic class with about half of the entire mod's logic in it!)
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
// The general rules for where event-based logic goes are:
|
||||
// - If the logic relates only to vanilla things or is general (e.g. gameplay settings) it lives in here
|
||||
// - If there is an obvious class for the thing relevant to the logic being performed, it goes in there for the sake
|
||||
// of modularity/separation-of-concerns (e.g. armour cost reductions go in ItemWizardArmour)
|
||||
// - If the thing has an instance but not a separate class (e.g. most custom potions), it goes in a related class
|
||||
// where applicable (e.g. a spell class), or if not it goes in here
|
||||
// - If several things share a significant amount of logic, to avoid duplicate code and potentially improve efficiency
|
||||
// they should be kept together either in a class relevant to all of them (probably a common superclass) or in here
|
||||
// - Client-side logic goes in WizardryClientEventHandler or another relevant client-side class
|
||||
@Mod.EventBusSubscriber
|
||||
public final class WizardryEventHandler {
|
||||
|
||||
// IDEA: Config option allowing users to specify loot locations
|
||||
private static final String[] LOOT_INJECTION_LOCATIONS = {"minecraft:chests/simple_dungeon",
|
||||
"minecraft:chests/abandoned_mineshaft", "minecraft:chests/desert_pyramid", "minecraft:chests/jungle_temple",
|
||||
"minecraft:chests/stronghold_corridor", "minecraft:chests/stronghold_crossing",
|
||||
"minecraft:chests/stronghold_library", "minecraft:chests/igloo_chest", "minecraft:chests/woodland_mansion",
|
||||
"minecraft:chests/end_city_treasure"};
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLootTableLoadEvent(LootTableLoadEvent event){
|
||||
if(Wizardry.settings.generateLoot){
|
||||
for(String location : LOOT_INJECTION_LOCATIONS){
|
||||
if(event.getName().toString().matches(location)){
|
||||
event.getTable().addPool(getAdditive(Wizardry.MODID + ":chests/dungeon_additions"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LootPool getAdditive(String entryName){
|
||||
return new LootPool(new LootEntry[]{getAdditiveEntry(entryName, 1)}, new LootCondition[0],
|
||||
new RandomValueRange(1), new RandomValueRange(0, 1), Wizardry.MODID + "_additive_pool");
|
||||
}
|
||||
|
||||
private static LootEntryTable getAdditiveEntry(String name, int weight){
|
||||
return new LootEntryTable(new ResourceLocation(name), weight, 0, new LootCondition[0],
|
||||
Wizardry.MODID + "_additive_entry");
|
||||
}
|
||||
private WizardryEventHandler(){} // No instances!
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){
|
||||
// When a player logs in, they are sent the glyph data and the server's settings.
|
||||
// When a player logs in, they are sent the glyph data, server settings and spell properties.
|
||||
if(event.player instanceof EntityPlayerMP){
|
||||
SpellGlyphData.get(event.player.world).sync((EntityPlayerMP)event.player);
|
||||
SpellEmitterData.get(event.player.world).sync((EntityPlayerMP)event.player);
|
||||
Wizardry.settings.sync((EntityPlayerMP)event.player);
|
||||
syncAdvancements((EntityPlayerMP)event.player, false);
|
||||
}
|
||||
Spell.syncProperties(event.player);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void onPlaySoundAtEntityEvent(PlaySoundAtEntityEvent event){
|
||||
// Muffle (there's no spell class for it so it's here instead)
|
||||
if(event.getEntity() instanceof EntityLivingBase
|
||||
&& ((EntityLivingBase)event.getEntity()).isPotionActive(WizardryPotions.muffle)){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAdvancementEvent(AdvancementEvent event){
|
||||
// Forge has no hook for revoked advancements :(
|
||||
// Guess we'll just have to make do
|
||||
// Also, this seems to get fired on player login, so to prevent the toasts from appearing every login the
|
||||
// only way I can see to do it is by testing the player has been around long enough.
|
||||
if(event.getEntityPlayer() instanceof EntityPlayerMP && event.getEntityPlayer().ticksExisted > 0){
|
||||
syncAdvancements((EntityPlayerMP)event.getEntityPlayer(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void syncAdvancements(EntityPlayerMP player, boolean showToasts){
|
||||
|
||||
Wizardry.logger.info("Synchronising advancements for " + player.getName());
|
||||
|
||||
ArrayList<ResourceLocation> advancements = new ArrayList<>();
|
||||
|
||||
for(Advancement advancement : player.getServer().getAdvancementManager().getAdvancements()){
|
||||
if(player.getAdvancements().getProgress(advancement).isDone()) advancements.add(advancement.getId());
|
||||
}
|
||||
|
||||
WizardryPacketHandler.net.sendTo(new PacketSyncAdvancements.Message(showToasts, advancements.toArray(new ResourceLocation[0])), player);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH) // Disabling of specific spells comes after arcane jammer but before everything else
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
|
||||
boolean enabled = true;
|
||||
|
||||
switch(event.getSource()){
|
||||
case WAND: enabled = event.getSpell().isEnabled(SpellProperties.Context.WANDS); break;
|
||||
case SCROLL: enabled = event.getSpell().isEnabled(SpellProperties.Context.SCROLL); break;
|
||||
case COMMAND: enabled = event.getSpell().isEnabled(SpellProperties.Context.COMMANDS); break;
|
||||
case NPC: enabled = event.getSpell().isEnabled(SpellProperties.Context.NPCS); break;
|
||||
case DISPENSER: enabled = event.getSpell().isEnabled(SpellProperties.Context.DISPENSERS); break;
|
||||
case OTHER: enabled = event.getSpell().isEnabled(); break; // Any enabled context will do for this one
|
||||
}
|
||||
|
||||
// If a spell is disabled in the config, it will not work.
|
||||
if(!event.getSpell().isEnabled()){
|
||||
if(!event.getEntityLiving().world.isRemote) event.getEntity().sendMessage(
|
||||
if(!enabled){
|
||||
if(event.getCaster() != null && !event.getCaster().world.isRemote) event.getCaster().sendMessage(
|
||||
new TextComponentTranslation("spell.disabled", event.getSpell().getNameForTranslationFormatted()));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
@@ -123,32 +140,36 @@ public final class WizardryEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onSpellCastPostEvent(SpellCastEvent.Post event){
|
||||
|
||||
// Spell discovery (only players can discover spells, obviously)
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
if(event.getCaster() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
EntityPlayer player = (EntityPlayer)event.getCaster();
|
||||
|
||||
// Advancement triggers
|
||||
if(player instanceof EntityPlayerMP){
|
||||
WizardryAdvancementTriggers.cast_spell.trigger((EntityPlayerMP)player, event.getSpell(), player.getHeldItem(player.getActiveHand()));
|
||||
}
|
||||
|
||||
// Spell discovery (only players can discover spells, obviously)
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
if(data != null){
|
||||
// Data is updated on both sides (This line was added client-side to fix a bug back in 1.1.3, so now
|
||||
// it's in common code, which is nice!)
|
||||
// Short-circuiting AND means that discoverSpell is only called if the event isn't cancelled.
|
||||
if(!MinecraftForge.EVENT_BUS
|
||||
.post(new DiscoverSpellEvent(player, event.getSpell(), DiscoverSpellEvent.Source.CASTING))
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, event.getSpell(), DiscoverSpellEvent.Source.CASTING))
|
||||
&& data.discoverSpell(event.getSpell())){
|
||||
|
||||
// If the spell wasn't already discovered, other stuff happens:
|
||||
if(event.getSource() == SpellCastEvent.Source.COMMAND){
|
||||
// If the spell didn't send a packet itself, the extended player needs to be synced so the
|
||||
// spell discovery updates on the client.
|
||||
if(!event.getSpell().doesSpellRequirePacket()) data.sync();
|
||||
if(!event.getSpell().requiresPacket()) data.sync();
|
||||
|
||||
}else if(!event.getEntity().world.isRemote && !player.capabilities.isCreativeMode
|
||||
}else if(!event.getCaster().world.isRemote && !player.isCreative()
|
||||
&& Wizardry.settings.discoveryMode){
|
||||
// Sound and text only happen server-side, in survival, with discovery mode on, and only when
|
||||
// the spell wasn't cast using commands.
|
||||
WizardryUtilities.playSoundAtPlayer(player, SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.MISC_DISCOVER_SPELL, 1.25f, 1);
|
||||
player.sendMessage(new TextComponentTranslation("spell.discover",
|
||||
event.getSpell().getNameForTranslationFormatted()));
|
||||
}
|
||||
@@ -157,33 +178,59 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
public static void onDiscoverSpellEvent(DiscoverSpellEvent event){
|
||||
if(event.getEntityPlayer() instanceof EntityPlayerMP){
|
||||
WizardryAdvancementTriggers.discover_spell.trigger((EntityPlayerMP)event.getEntityPlayer(), event.getSpell(), event.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
||||
|
||||
if(event.getTarget() != null && event.getEntityLiving() instanceof EntityLiving
|
||||
&& event.getTarget().isPotionActive(WizardryPotions.muffle)){
|
||||
|
||||
Vec3d vec = event.getTarget().getPositionEyes(1).subtract(event.getEntity().getPositionEyes(1));
|
||||
// Find the angle between the direction the mob is looking and the direction the player is in
|
||||
// Angle between a and b = acos((a.b) / (|a|*|b|))
|
||||
double angle = Math.acos(vec.dotProduct(event.getEntity().getLookVec()) / vec.length());
|
||||
System.out.println(angle);
|
||||
// If the player is not within the 144-degree arc in front of the mob, it won't detect them
|
||||
if(angle > 0.4 * Math.PI){
|
||||
((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* There is a subtle but important difference between LivingAttackEvent and LivingHurtEvent - LivingAttackEvent
|
||||
* fires immediately when attackEntityFrom is called, whereas LivingHurtEvent only fires if the attack actually
|
||||
* succeeded, i.e. if the entity in question takes damage (though the event is fired before that so you can cancel
|
||||
* the damage). Things are processed in the following order: * LivingAttackEvent * - Invulnerability -
|
||||
* Already-dead-ness - Fire resistance - Helmets vs. falling things - Hurt resistant time - Invulnerability (again)
|
||||
* * LivingHurtEvent * - Armour - Potions - Health is finally changed Of course, there are no guarantees that other
|
||||
* the damage). Things are processed in the following order:
|
||||
*
|
||||
* * LivingAttackEvent *
|
||||
* - Invulnerability
|
||||
* - Already-dead-ness
|
||||
* - Fire resistance
|
||||
* - Helmets vs. falling things
|
||||
* - Hurt resistant time
|
||||
* - Invulnerability (again)
|
||||
* * LivingHurtEvent *
|
||||
* - Armour
|
||||
* - Potions
|
||||
* * LivingDamageEvent *
|
||||
* - Health is finally changed
|
||||
*
|
||||
* Of course, there are no guarantees that other
|
||||
* mods hooking into these two events will be called before or after yours, but you can have some degree of control
|
||||
* by choosing which event to use. EDIT: Actually, there are. Firstly, you can set a priority in the @SubscribeEvent
|
||||
* annotation which defines how early (higher priority) or late (lower priority) the method is called. Methods with
|
||||
* the same priority are sorted alphabetically by mod id (so it's safe to assume wizardry would be fairly late on!).
|
||||
* I wonder if there are any conventions for what sort of things take what priority...? */
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Low priority in case the event gets cancelled at default priority
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
|
||||
// Prevents any damage to allies from magic if friendly fire is disabled
|
||||
if(!Wizardry.settings.friendlyFire && event.getSource() != null
|
||||
&& event.getSource().getTrueSource() instanceof EntityPlayer && event.getEntity() instanceof EntityPlayer
|
||||
&& event.getSource() instanceof IElementalDamage){
|
||||
if(WizardryUtilities.isPlayerAlly((EntityPlayer)event.getSource().getTrueSource(),
|
||||
(EntityPlayer)event.getEntity())){
|
||||
event.setCanceled(true);
|
||||
// This needs to be here, since if the event is cancelled nothing else needs to happen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Retaliatory effects
|
||||
// These are better off here because the revenge effects are pretty similar, and I'd rather keep the (lengthy)
|
||||
// if statement in one place.
|
||||
@@ -194,38 +241,43 @@ public final class WizardryEventHandler {
|
||||
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
World world = event.getEntityLiving().world;
|
||||
|
||||
// Fireskin
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving()))
|
||||
attacker.setFire(5);
|
||||
if(attacker.getDistance(event.getEntityLiving()) < 10){
|
||||
|
||||
// Ice Shroud
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())
|
||||
&& !(event.getEntityLiving() instanceof FakePlayer))
|
||||
attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
|
||||
// Fireskin
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving()))
|
||||
attacker.setFire(Spells.fire_breath.getProperty(Spell.BURN_DURATION).intValue());
|
||||
|
||||
// Static Aura
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){
|
||||
// Ice Shroud
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())
|
||||
&& !(attacker instanceof FakePlayer)) // Fake players cause problems
|
||||
attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
Spells.ice_shroud.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.ice_shroud.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(event.getEntity()).pos(0, event.getEntity().height/2, 0)
|
||||
.target(attacker).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, attacker.posX,
|
||||
attacker.getEntityBoundingBox().minY + attacker.height, attacker.posZ);
|
||||
// Static Aura
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(event.getEntity()).pos(0, event.getEntity().height / 2, 0)
|
||||
.target(attacker).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, attacker.posX,
|
||||
attacker.getEntityBoundingBox().minY + attacker.height / 2, attacker.posZ);
|
||||
}
|
||||
|
||||
DamageSafetyChecker.attackEntitySafely(attacker, MagicDamage.causeDirectMagicDamage(event.getEntityLiving(),
|
||||
DamageType.SHOCK, true), Spells.static_aura.getProperty(Spell.DAMAGE).floatValue(), event.getSource().getDamageType());
|
||||
attacker.playSound(WizardrySounds.SPELL_STATIC_AURA_RETALIATE, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
}
|
||||
|
||||
DamageSafetyChecker.attackEntitySafely(attacker, MagicDamage.causeDirectMagicDamage(event.getEntityLiving(),
|
||||
DamageType.SHOCK, true), 4.0f, event.getSource().getDamageType());
|
||||
attacker.playSound(WizardrySounds.SPELL_STATIC_AURA_RETALIATE, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Again, we don't want these effects if the event is cancelled
|
||||
public static void onLivingHurtEvent(LivingHurtEvent event){
|
||||
|
||||
// Flaming and freezing swords
|
||||
@@ -234,7 +286,7 @@ public final class WizardryEventHandler {
|
||||
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
|
||||
// Players can only ever attack with their main hand, so this is the right method to use here.
|
||||
if(!attacker.getHeldItemMainhand().isEmpty() && attacker.getHeldItemMainhand().getItem() instanceof ItemSword){
|
||||
if(!attacker.getHeldItemMainhand().isEmpty() && ImbueWeapon.isSword(attacker.getHeldItemMainhand().getItem())){
|
||||
|
||||
int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon,
|
||||
attacker.getHeldItemMainhand());
|
||||
@@ -275,30 +327,13 @@ public final class WizardryEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
if(player.world.isRemote) hackilyFixContinuousSpellCasting(player);
|
||||
|
||||
// Mana flask crafting
|
||||
if(player.openContainer instanceof ContainerWorkbench){
|
||||
|
||||
IInventory craftMatrix = ((ContainerWorkbench)player.openContainer).craftMatrix;
|
||||
ItemStack output = ((ContainerWorkbench)player.openContainer).craftResult.getStackInSlot(0);
|
||||
processManaFlaskCrafting(craftMatrix, output);
|
||||
|
||||
}else if(player.openContainer instanceof ContainerPlayer){
|
||||
|
||||
IInventory craftMatrix = ((ContainerPlayer)player.openContainer).craftMatrix;
|
||||
ItemStack output = ((ContainerPlayer)player.openContainer).craftResult.getStackInSlot(0);
|
||||
// Unfortunately I have no choice but to call this method every tick when the player isn't using another
|
||||
// inventory, since the only thing tracking whether the player is looking at their inventory is the GUI
|
||||
// itself, which is client-side only.
|
||||
processManaFlaskCrafting(craftMatrix, output);
|
||||
}
|
||||
|
||||
}
|
||||
// Experimental animation feature
|
||||
// if(event.getEntityLiving().isHandActive() && event.getEntityLiving().getActiveItemStack().getItemUseAction() == WizardryUtilities.POINT){
|
||||
// event.getEntityLiving().isSwingInProgress = true;
|
||||
// event.getEntityLiving().swingProgress = 1f;
|
||||
// event.getEntityLiving().prevSwingProgress = 1;
|
||||
// event.getEntityLiving().swingingHand = event.getEntityLiving().getActiveHand();
|
||||
// }
|
||||
|
||||
if(event.getEntityLiving().world.isRemote){
|
||||
|
||||
@@ -309,13 +344,14 @@ public final class WizardryEventHandler {
|
||||
Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell();
|
||||
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
|
||||
|
||||
if(spell != null && spell != Spells.none){
|
||||
if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check!
|
||||
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(event.getEntityLiving(), spell, modifiers,
|
||||
SpellCastEvent.Source.NPC, 0))){
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(),
|
||||
modifiers, 0))){
|
||||
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0,
|
||||
// TODO: This implementation of modifiers relies on them being accessible client-side.
|
||||
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future
|
||||
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
|
||||
}
|
||||
}
|
||||
@@ -323,7 +359,7 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST) // No siphoning if the event is cancelled, that could be exploited...
|
||||
public static void onLivingDeathEvent(LivingDeathEvent event){
|
||||
|
||||
if(event.getSource().getTrueSource() instanceof EntityPlayer){
|
||||
@@ -332,108 +368,83 @@ public final class WizardryEventHandler {
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
|
||||
|
||||
if(stack.getItem() instanceof ItemWand && stack.isItemDamaged()
|
||||
if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
|
||||
&& WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) > 0){
|
||||
int damage = stack.getItemDamage()
|
||||
- Constants.SIPHON_MANA_PER_LEVEL
|
||||
|
||||
int mana = Constants.SIPHON_MANA_PER_LEVEL
|
||||
* WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade)
|
||||
- player.world.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL);
|
||||
if(damage < 0) damage = 0;
|
||||
stack.setItemDamage(damage);
|
||||
break;
|
||||
+ player.world.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL);
|
||||
|
||||
if(ItemArtefact.isArtefactActive(player, WizardryItems.ring_siphoning)) mana *= 1.3f;
|
||||
|
||||
((IManaStoringItem)stack.getItem()).rechargeMana(stack, mana);
|
||||
|
||||
break; // Only recharge one item per kill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These two are lifted from EntityLivingBase#travel
|
||||
private static final double LIVING_ENTITY_GRAVITY = 0.08;
|
||||
private static final double LIVING_ENTITY_DRAG = 0.98;
|
||||
|
||||
private static final double LIVING_ENTITY_TERMINAL_VELOCITY = 3.92; // From Minecraft Wiki!
|
||||
|
||||
private static final double LOG_LIVING_ENTITY_DRAG = Math.log(LIVING_ENTITY_DRAG);
|
||||
private static final double FALL_TICKS_ERROR_CORRECTION = 0.500841776608447;
|
||||
|
||||
@SubscribeEvent // Priority doesn't matter here, we're only setting event fields so if it's cancelled it won't matter
|
||||
public static void onLivingFallEvent(LivingFallEvent event){
|
||||
// Why is fall damage based on distance fallen? Why? Who on earth came up with that? It makes no sense whatsoever!
|
||||
if(Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){
|
||||
// We want to keep the fall damage EXACTLY THE SAME for free, uninterrupted falls, but fix the weirdness
|
||||
// caused when something else changes the entity's velocity
|
||||
// All living entities have a gravity of 0.08b/t^2
|
||||
// Therefore it would be simple to say v^2 = u^2 + 2gs gives the equivalent fall distance as motionY^2 / 0.16
|
||||
// However, Minecraft also has a drag of 0.02 * the velocity, so we actually expect a slightly different value
|
||||
// Much maths later...
|
||||
|
||||
double v = event.getEntity().motionY;
|
||||
// Players are weird, their velocity somehow resets on the server just before this event fires so
|
||||
// instead we're storing the y velocity from the previous tick in WizardData and retrieving it here
|
||||
// Of course, if another mod screws things up and sets a player's velocity client-side only then this won't
|
||||
// work ...but it's better than having clients calculate their own fall damage
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
WizardData data = WizardData.get((EntityPlayer)event.getEntity());
|
||||
if(data != null){
|
||||
v = data.prevMotionY;
|
||||
}
|
||||
}
|
||||
|
||||
if(event.getEntityLiving() == player && event.getSource() instanceof IElementalDamage){
|
||||
WizardryAdvancementTriggers.self_destruct.triggerFor(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
// At terminal velocity, there's no way of finding fall distance from velocity, and the entity is probably dead anyway!
|
||||
if(v > -3.9){
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
// TODO: Really, this should be in a loot table (mob_additions), however I can't seem to find a way of
|
||||
// automatically adding it to all subclasses of IMob.
|
||||
// Evil wizards drop spell books themselves
|
||||
if(event.getEntityLiving() instanceof IMob && !(event.getEntityLiving() instanceof EntityEvilWizard)
|
||||
// TODO: Backport when you backport the new summoned creature system.
|
||||
&& !(event.getEntityLiving() instanceof ISummonedCreature)
|
||||
&& event.getSource().getTrueSource() instanceof EntityPlayer && Wizardry.settings.spellBookDropChance > 0){
|
||||
// Just to make the code more readable, java will replace them all with numbers anyway
|
||||
double g = LIVING_ENTITY_GRAVITY;
|
||||
double f = LIVING_ENTITY_DRAG;
|
||||
double lnf = LOG_LIVING_ENTITY_DRAG;
|
||||
double tv = LIVING_ENTITY_TERMINAL_VELOCITY;
|
||||
|
||||
// This does exactly what the entity drop method does, but with a different random number so that the
|
||||
// spell book doesn't always drop with other rare drops.
|
||||
int rareDropNumber = event.getEntity().world.rand.nextInt(200) - event.getLootingLevel();
|
||||
if(rareDropNumber < Wizardry.settings.spellBookDropChance){
|
||||
// Drops a spell book
|
||||
int id = WizardryUtilities.getStandardWeightedRandomSpellId(event.getEntity().world.rand);
|
||||
// Work backwards from y velocity to get fall time
|
||||
// logs are probably slow but it's not like this gets calculated every tick, and we only need one log
|
||||
double t = Math.log(((-v - tv) * lnf) / g) / lnf; // Log the number over log the base
|
||||
|
||||
event.getDrops()
|
||||
.add(new EntityItem(event.getEntityLiving().world, event.getEntityLiving().posX,
|
||||
event.getEntityLiving().posY, event.getEntityLiving().posZ,
|
||||
new ItemStack(WizardryItems.spell_book, 1, id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Because time is in discrete ticks, the above equation for t results in a constant error of
|
||||
// +0.500841776608447, so I guess we can just subtract it... if it works it works I guess!
|
||||
t -= FALL_TICKS_ERROR_CORRECTION; // Don't cast to int or perform any rounding
|
||||
|
||||
// Private helper methods
|
||||
// ================================================================================================================
|
||||
// Now work forwards from t to find the effective fall distance, i.e. the distance the entity would
|
||||
// have to freefall to reach the same velocity
|
||||
// Don't ask me where the 196 is from, again, it just works!
|
||||
double y = (g * Math.pow(f, t)) / (lnf*lnf) + tv * (t) - 196;
|
||||
|
||||
/**
|
||||
* Detects inconsistencies between player.getActiveItemStack and the actual itemstack and forces them to be equal.
|
||||
* Fixes issue #25.
|
||||
*
|
||||
* @param player
|
||||
*/
|
||||
private static void hackilyFixContinuousSpellCasting(EntityPlayer player){
|
||||
if(player.isHandActive() && player.getHeldItem(player.getActiveHand()).getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(player.getHeldItem(player.getActiveHand())).isContinuous){
|
||||
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())){
|
||||
player.setHeldItem(player.getActiveHand(), player.getActiveItemStack());
|
||||
}
|
||||
}
|
||||
}
|
||||
// DEBUG
|
||||
// if(event.getEntity() instanceof EntityPlayer){
|
||||
// Wizardry.logger.info("Replaced fall distance {} with effective distance {} based on entity velocity", event.getDistance(), y);
|
||||
// }
|
||||
|
||||
private static void processManaFlaskCrafting(IInventory craftMatrix, ItemStack output){
|
||||
|
||||
// Charges wand using mana flask. It is here rather than in the crafting handler so the result displays
|
||||
// the proper damage before it is actually crafted.
|
||||
|
||||
boolean flag = false;
|
||||
ItemStack wand = ItemStack.EMPTY;
|
||||
ItemStack armour = ItemStack.EMPTY;
|
||||
|
||||
for(int i = 0; i < craftMatrix.getSizeInventory(); i++){
|
||||
|
||||
ItemStack itemstack = craftMatrix.getStackInSlot(i);
|
||||
|
||||
if(itemstack.getItem() == WizardryItems.mana_flask){
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if(itemstack.getItem() instanceof ItemWand){
|
||||
wand = itemstack;
|
||||
}
|
||||
|
||||
if(itemstack.getItem() instanceof ItemWizardArmour){
|
||||
armour = itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
if(output.getItem() instanceof ItemWand && flag && !wand.isEmpty()){
|
||||
output.setTagCompound((wand.getTagCompound()));
|
||||
if(wand.getItemDamage() - Constants.MANA_PER_FLASK < 0){
|
||||
output.setItemDamage(0);
|
||||
}else{
|
||||
output.setItemDamage(wand.getItemDamage() - Constants.MANA_PER_FLASK);
|
||||
}
|
||||
}
|
||||
|
||||
if(output.getItem() instanceof ItemWizardArmour && flag && !armour.isEmpty()){
|
||||
output.setTagCompound((armour.getTagCompound()));
|
||||
if(armour.getItemDamage() - Constants.MANA_PER_FLASK < 0){
|
||||
output.setItemDamage(0);
|
||||
}else{
|
||||
output.setItemDamage(wand.getItemDamage() - Constants.MANA_PER_FLASK);
|
||||
event.setDistance((float)y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import electroblob.wizardry.client.gui.GuiConfigWizardry;
|
||||
import electroblob.wizardry.client.gui.config.GuiConfigWizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraftforge.fml.client.IModGuiFactory;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class WizardryGuiFactory implements IModGuiFactory {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import electroblob.wizardry.client.gui.handbook.GuiWizardHandbook;
|
||||
import electroblob.wizardry.item.ItemSpellBook;
|
||||
import electroblob.wizardry.item.ItemWizardHandbook;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
@@ -46,12 +45,12 @@ public class WizardryGuiHandler implements IGuiHandler {
|
||||
}
|
||||
}else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook
|
||||
|| player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){
|
||||
return new GuiWizardHandbook();
|
||||
return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook();
|
||||
}else if(id == SPELL_BOOK){
|
||||
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.byMetadata(player.getHeldItemMainhand().getItemDamage()));
|
||||
}else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){
|
||||
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.get(player.getHeldItemOffhand().getItemDamage()));
|
||||
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.byMetadata(player.getHeldItemOffhand().getItemDamage()));
|
||||
}
|
||||
}else if(id == PORTABLE_CRAFTING){
|
||||
return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
|
||||
|
||||
@@ -1,949 +0,0 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.BlockChest;
|
||||
import net.minecraft.block.BlockColored;
|
||||
import net.minecraft.block.BlockLeaves;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.BlockPlanks;
|
||||
import net.minecraft.block.BlockSlab;
|
||||
import net.minecraft.block.BlockSlab.EnumBlockHalf;
|
||||
import net.minecraft.block.BlockTorch;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
import net.minecraft.item.ItemDoor;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
import net.minecraft.world.gen.IChunkGenerator;
|
||||
import net.minecraft.world.gen.feature.WorldGenMinable;
|
||||
import net.minecraft.world.storage.loot.LootContext;
|
||||
import net.minecraft.world.storage.loot.LootTable;
|
||||
import net.minecraftforge.common.BiomeDictionary;
|
||||
import net.minecraftforge.common.IPlantable;
|
||||
import net.minecraftforge.fml.common.IWorldGenerator;
|
||||
|
||||
public class WizardryWorldGenerator implements IWorldGenerator {
|
||||
|
||||
/** The string identifier for wizard tower chests, used in ChestGenHooks. */
|
||||
public static final String WIZARD_TOWER = Wizardry.MODID + "wizardTower";
|
||||
|
||||
@Override
|
||||
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator,
|
||||
IChunkProvider chunkProvider){
|
||||
|
||||
for(int id : Wizardry.settings.oreDimensions){
|
||||
if(id == world.provider.getDimension()) this.addOreSpawn(WizardryBlocks.crystal_ore.getDefaultState(),
|
||||
world, random, chunkX * 16, chunkZ * 16, 16, 16, 5, 7, 5, 30);
|
||||
}
|
||||
|
||||
for(int id : Wizardry.settings.flowerDimensions){
|
||||
if(id == world.provider.getDimension()) this.generatePlant(WizardryBlocks.crystal_flower.getDefaultState(),
|
||||
world, random, chunkX * 16, chunkZ * 16, 2, 20);
|
||||
}
|
||||
|
||||
if(world.getWorldInfo().isMapFeaturesEnabled()){
|
||||
for(int id : Wizardry.settings.towerDimensions){
|
||||
if(id == world.provider.getDimension())
|
||||
this.generateWizardTower(world, random, chunkX * 16, chunkZ * 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Ore Spawn to Minecraft. Simply register all Ores to spawn with this method in your Generation method in
|
||||
* your IWorldGeneration extending Class
|
||||
*
|
||||
* @param The Block to spawn
|
||||
* @param The World to spawn in
|
||||
* @param A Random object for retrieving random positions within the world to spawn the Block
|
||||
* @param An int for passing the X-Coordinate for the Generation method
|
||||
* @param An int for passing the Z-Coordinate for the Generation method
|
||||
* @param An int for setting the maximum X-Coordinate values for spawning on the X-Axis on a Per-Chunk basis
|
||||
* @param An int for setting the maximum Z-Coordinate values for spawning on the Z-Axis on a Per-Chunk basis
|
||||
* @param An int for setting the maximum size of a vein
|
||||
* @param An int for the Number of chances available for the Block to spawn per-chunk
|
||||
* @param An int for the minimum Y-Coordinate height at which this block may spawn
|
||||
* @param An int for the maximum Y-Coordinate height at which this block may spawn
|
||||
**/
|
||||
public void addOreSpawn(IBlockState state, World world, Random random, int blockXPos, int blockZPos, int maxX,
|
||||
int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY){
|
||||
// int maxPossY = minY + (maxY - 1);
|
||||
assert maxY > minY : "The maximum Y must be greater than the Minimum Y";
|
||||
assert maxX > 0 && maxX <= 16 : "addOreSpawn: The Maximum X must be greater than 0 and less than 16";
|
||||
assert minY > 0 : "addOreSpawn: The Minimum Y must be greater than 0";
|
||||
assert maxY < 256 && maxY > 0 : "addOreSpawn: The Maximum Y must be less than 256 but greater than 0";
|
||||
assert maxZ > 0 && maxZ <= 16 : "addOreSpawn: The Maximum Z must be greater than 0 and less than 16";
|
||||
|
||||
int diffBtwnMinMaxY = maxY - minY;
|
||||
for(int x = 0; x < chancesToSpawn; x++){
|
||||
int posX = blockXPos + random.nextInt(maxX);
|
||||
int posY = minY + random.nextInt(diffBtwnMinMaxY);
|
||||
int posZ = blockZPos + random.nextInt(maxZ);
|
||||
(new WorldGenMinable(state, maxVeinSize)).generate(world, random, new BlockPos(posX, posY, posZ));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the specified plant randomly throughout the world.
|
||||
*
|
||||
* @param block The plant block
|
||||
* @param world The world
|
||||
* @param random A random instance
|
||||
* @param x The x coord of the first block in the chunk
|
||||
* @param z The y coord of the first block in the chunk
|
||||
* @param chancesToSpawn Number of chances to spawn a flower patch
|
||||
* @param groupSize The number of times to try generating a flower per flower patch spawn
|
||||
*/
|
||||
public void generatePlant(IBlockState state, World world, Random random, int x, int z, int chancesToSpawn,
|
||||
int groupSize){
|
||||
|
||||
for(int i = 0; i < chancesToSpawn; i++){
|
||||
int randPosX = x + random.nextInt(16);
|
||||
int randPosY = random.nextInt(256);
|
||||
int randPosZ = z + random.nextInt(16);
|
||||
for(int l = 0; l < groupSize; ++l){
|
||||
int i1 = randPosX + random.nextInt(8) - random.nextInt(8);
|
||||
int j1 = randPosY + random.nextInt(4) - random.nextInt(4);
|
||||
int k1 = randPosZ + random.nextInt(8) - random.nextInt(8);
|
||||
|
||||
BlockPos pos = new BlockPos(i1, j1, k1);
|
||||
|
||||
if(world.isBlockLoaded(pos) && world.isAirBlock(pos) && (!world.provider.isNether() || j1 < 127)
|
||||
&& state.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.UP)){
|
||||
|
||||
world.setBlockState(pos, state, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates wizard towers randomly throughout the world.
|
||||
*/
|
||||
public void generateWizardTower(World world, Random random, int chunkX, int chunkZ){
|
||||
|
||||
// Allows the config file to set the rarity value to 0 to disable tower generation completely.
|
||||
if(Wizardry.settings.towerRarity == 0) return;
|
||||
|
||||
// Compensates for the lack of space in forests. Math.max is required since treeless biomes have treesPerChunk =
|
||||
// -999
|
||||
// double treeFactor = 70 - Math.max((double)world.getBiomeGenForCoords(chunkX,
|
||||
// chunkZ).theBiomeDecorator.treesPerChunk, 0) * 1.5d;
|
||||
|
||||
// Multiplied by 70 to (roughly) retain the old rarity scale
|
||||
if(random.nextInt((int)(Wizardry.settings.towerRarity * 70)) == 0){
|
||||
|
||||
BlockPos origin = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16));
|
||||
|
||||
// Despite what its name suggests, this method does not return the position of a liquid. It is in fact
|
||||
// exactly what is needed here since it is used for placing villages and stuff, and doesn't include leaves
|
||||
// or other foliage.
|
||||
origin = origin.up(world.getTopSolidOrLiquidBlock(origin).getY() - 1);
|
||||
|
||||
int[][][] towerBlueprint = towerBlueprintSmall;
|
||||
|
||||
switch(random.nextInt(4)){
|
||||
case 0:
|
||||
towerBlueprint = towerBlueprintSmall;
|
||||
break;
|
||||
case 1:
|
||||
towerBlueprint = towerBlueprintMedium;
|
||||
break;
|
||||
case 2:
|
||||
towerBlueprint = towerBlueprintTall;
|
||||
break;
|
||||
case 3:
|
||||
towerBlueprint = towerBlueprintDouble;
|
||||
break;
|
||||
}
|
||||
|
||||
// 0 = West, 1 = North, 2 = East, 3 = South (The way you would face when walking out of the door)
|
||||
EnumFacing orientation = EnumFacing.byHorizontalIndex(random.nextInt(4));
|
||||
boolean flip = random.nextBoolean();
|
||||
|
||||
if(checkSpaceForTower(world, origin, towerBlueprint, orientation, flip)){
|
||||
|
||||
// == Setup ==
|
||||
|
||||
boolean evilWizard = random.nextInt(5) == 0;
|
||||
|
||||
IBlockState wallMaterial = Blocks.COBBLESTONE.getDefaultState();
|
||||
BlockPlanks.EnumType woodType = BlockPlanks.EnumType.OAK;
|
||||
|
||||
Biome biome = world.getBiome(origin);
|
||||
|
||||
// The order of these is somewhat important in that biomes can be many types, so the last of these
|
||||
// checks
|
||||
// that is true gets priority. Generally speaking, the later the check, the more specific it is.
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.DENSE))
|
||||
wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SWAMP))
|
||||
wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SANDY))
|
||||
wallMaterial = Blocks.SANDSTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.NETHER))
|
||||
wallMaterial = Blocks.NETHER_BRICK.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.MOUNTAIN))
|
||||
wallMaterial = Blocks.STONEBRICK.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.MESA))
|
||||
wallMaterial = Blocks.HARDENED_CLAY.getDefaultState();
|
||||
|
||||
// Unfortunately, I can't check all the wood types with the biome dictionary
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.CONIFEROUS))
|
||||
woodType = BlockPlanks.EnumType.SPRUCE;
|
||||
if(biome == Biomes.BIRCH_FOREST || biome == Biomes.BIRCH_FOREST_HILLS)
|
||||
woodType = BlockPlanks.EnumType.BIRCH;
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.JUNGLE)) woodType = BlockPlanks.EnumType.JUNGLE;
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SAVANNA)) woodType = BlockPlanks.EnumType.ACACIA;
|
||||
// Not technically a tree type, but I think it fits quite well anyway
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SPOOKY))
|
||||
woodType = BlockPlanks.EnumType.DARK_OAK;
|
||||
|
||||
IBlockState[] blockStateList = new IBlockState[]{null, Blocks.AIR.getDefaultState(),
|
||||
Blocks.PLANKS.getDefaultState().withProperty(BlockPlanks.VARIANT, woodType),
|
||||
Blocks.BOOKSHELF.getDefaultState(),
|
||||
Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(BlockColored.COLOR,
|
||||
EnumDyeColor.values()[random.nextInt(EnumDyeColor.values().length)]),
|
||||
Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.BOTTOM),
|
||||
Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.TOP),
|
||||
wallMaterial, Blocks.GLASS_PANE.getDefaultState(), Blocks.OAK_DOOR.getDefaultState(),
|
||||
WizardryBlocks.arcane_workbench.getDefaultState(), Blocks.TORCH.getDefaultState(),
|
||||
evilWizard ? Blocks.CHEST.getDefaultState() : Blocks.BOOKSHELF.getDefaultState()};
|
||||
|
||||
Set<BlockPos> blocksPlaced = new HashSet<BlockPos>();
|
||||
|
||||
// == Foundations ==
|
||||
|
||||
// Fills in foundations. This is done first so the door always has something to be placed on.
|
||||
boolean flag = true;
|
||||
|
||||
// BlockPos is immutable, so I'm not sure if simply saying pos1 = pos will be sufficient.
|
||||
BlockPos layerCentre = new BlockPos(origin);
|
||||
|
||||
// Stop when the bottom of the world is reached
|
||||
while(flag && layerCentre.getY() > 0){
|
||||
|
||||
flag = false;
|
||||
|
||||
for(BlockPos offset : foundationLayer){
|
||||
if(!world.isBlockNormalCube(layerCentre.add(offset), false)){
|
||||
world.setBlockState(layerCentre.add(offset), wallMaterial);
|
||||
blocksPlaced.add(layerCentre.add(offset));
|
||||
// Keeps going as long as something was filled in.
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
layerCentre = layerCentre.down();
|
||||
}
|
||||
|
||||
// == Main Structure ==
|
||||
|
||||
// It is assumed that the width of the blueprint is the same all the way up, and that the layers are
|
||||
// square.
|
||||
int width = towerBlueprint[0].length - 1;
|
||||
|
||||
// x, y and z are the position the block is being put in.
|
||||
// x1, y and z1 are the position in the blueprint which determines which block is being placed.
|
||||
|
||||
int x1 = 0, z1 = 0;
|
||||
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos = origin.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]] != null
|
||||
&& blockStateList[towerBlueprint[y][z1][x1]].getBlock() != Blocks.TORCH
|
||||
&& blockStateList[towerBlueprint[y][z1][x1]].getBlock() != Blocks.CHEST){
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.OAK_DOOR){
|
||||
// Rotates the door depending on whether flip is true.
|
||||
ItemDoor.placeDoor(
|
||||
world, pos, flip
|
||||
? EnumFacing.byHorizontalIndex(3 - orientation.getHorizontalIndex())
|
||||
.getOpposite()
|
||||
: orientation.rotateYCCW(),
|
||||
Blocks.OAK_DOOR, false);
|
||||
}else{
|
||||
world.setBlockState(pos, blockStateList[towerBlueprint[y][z1][x1]], 2);
|
||||
}
|
||||
|
||||
blocksPlaced.add(pos);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// == Extras ==
|
||||
|
||||
// Torches are done afterwards so they don't fall off
|
||||
// Chests are also done afterwards, because for some reason the chest decides which way round to face
|
||||
// itself, after it has been placed, based on the surrounding blocks... but if it was placed during
|
||||
// the rest of the tower generation, some of those blocks wouldn't exist, hence it must be done here.
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos = origin.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]] != null){
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.TORCH){
|
||||
if(placeTorch(world, pos, true)){
|
||||
blocksPlaced.add(pos);
|
||||
}else{
|
||||
Wizardry.logger.info("Attempted to generate a torch at " + pos + " in " + world
|
||||
+ ", but failed!");
|
||||
}
|
||||
}
|
||||
|
||||
// World should always be a WorldServer, but it's worth checking anyway.
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.CHEST
|
||||
&& world instanceof WorldServer){
|
||||
if(placeChest(world, pos)){
|
||||
blocksPlaced.add(pos);
|
||||
LootTable table = world.getLootTableManager().getLootTableFromLocation(
|
||||
new ResourceLocation(Wizardry.MODID, "chests/wizard_tower"));
|
||||
IInventory inventory = (IInventory)world.getTileEntity(pos);
|
||||
LootContext context = new LootContext.Builder((WorldServer)world).build();
|
||||
table.fillInventory(inventory, random, context);
|
||||
}else{
|
||||
Wizardry.logger.info("Attempted to generate a chest at " + pos + " in " + world
|
||||
+ ", but failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(evilWizard){
|
||||
|
||||
EntityEvilWizard wizard = new EntityEvilWizard(world);
|
||||
wizard.hasTower = true;
|
||||
wizard.setLocationAndAngles(origin.getX() + 1.5, origin.getY() + towerBlueprint.length - 9.5,
|
||||
origin.getZ() + 1.5, 0, 0);
|
||||
wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null);
|
||||
|
||||
world.spawnEntity(wizard);
|
||||
|
||||
}else{
|
||||
|
||||
EntityWizard wizard = new EntityWizard(world);
|
||||
wizard.setLocationAndAngles(origin.getX() + 1.5, origin.getY() + towerBlueprint.length - 9.5,
|
||||
origin.getZ() + 1.5, 0, 0);
|
||||
wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null);
|
||||
wizard.setTowerBlocks(blocksPlaced);
|
||||
|
||||
world.spawnEntity(wizard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a torch at the given position in the given world and automatically assigns an appropriate state. Order of
|
||||
* priority is U-S-W-N-E, or S-W-N-E-U if wallPriority is true.
|
||||
*
|
||||
* @param world The world to place the torch in.
|
||||
* @param pos The position to place the torch at.
|
||||
* @param wallPriority True to prioritise wall torches, false to prioritise floor torches.
|
||||
* @return True if the torch was placed, false if it is not possible.
|
||||
*/
|
||||
private static boolean placeTorch(World world, BlockPos pos, boolean wallPriority){
|
||||
|
||||
for(EnumFacing facing : ArrayUtils.add(EnumFacing.HORIZONTALS, wallPriority ? 4 : 0, EnumFacing.UP)){
|
||||
if(world.isSideSolid(pos.offset(facing.getOpposite()), facing)){
|
||||
world.setBlockState(pos, Blocks.TORCH.getDefaultState().withProperty(BlockTorch.FACING, facing));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a chest at the given position in the given world and automatically assigns an appropriate state. Order of
|
||||
* priority is S-W-N-E.
|
||||
*
|
||||
* @param world The world to chest the torch in.
|
||||
* @param pos The position to chest the torch at.
|
||||
* @return True if the chest was placed, false if it is not possible.
|
||||
*/
|
||||
private static boolean placeChest(World world, BlockPos pos){
|
||||
|
||||
for(EnumFacing facing : EnumFacing.HORIZONTALS){
|
||||
if(world.isAirBlock(pos.offset(facing))){
|
||||
world.setBlockState(pos, Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, facing));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a tower generated at the given coordinates will intersect any solid or liquid blocks. Only tests
|
||||
* for liquids (not solid blocks) for the first four layers to account for the floor and for sloping terrain.
|
||||
*
|
||||
* @return True if none of the blocks which the tower would replace are solid or liquid. Dirt, stone etc., water and
|
||||
* lava count, as do logs, but leaves and plants don't.
|
||||
*/
|
||||
private static boolean checkSpaceForTower(World world, BlockPos pos, int[][][] towerBlueprint,
|
||||
EnumFacing orientation, boolean flip){
|
||||
|
||||
// x, y and z are the position the block is being put in.
|
||||
// x1, y and z1 are the position in the blueprint which determines which block is being placed.
|
||||
|
||||
int x1 = 0, z1 = 0;
|
||||
|
||||
// It is assumed that the width of the blueprint is the same all the way up, and that the layers are square.
|
||||
int width = towerBlueprint[0].length - 1;
|
||||
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos1 = pos.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(towerBlueprint[y][z1][x1] != 0 && !WizardryUtilities.canBlockBeReplacedB(world, pos1)
|
||||
// TODO: Is this a better replacement for the subsequent two lines?
|
||||
// && !world.getBlockState(pos1).getBlock().isFoliage(world, pos1)
|
||||
&& !(world.getBlockState(pos1).getBlock() instanceof IPlantable)
|
||||
&& !(world.getBlockState(pos1).getBlock() instanceof BlockLeaves)
|
||||
&& (y > 3 || world.getBlockState(pos1).getBlock() instanceof BlockLiquid)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Array of relative positions of each block in a layer of foundations. */
|
||||
private static final BlockPos[] foundationLayer = new BlockPos[]{new BlockPos(-2, 0, -1), new BlockPos(-2, 0, 0),
|
||||
new BlockPos(-2, 0, 1), new BlockPos(2, 0, -1), new BlockPos(2, 0, 0), new BlockPos(2, 0, 1),
|
||||
new BlockPos(-1, 0, -2), new BlockPos(0, 0, -2), new BlockPos(1, 0, -2), new BlockPos(-1, 0, 2),
|
||||
new BlockPos(0, 0, 2), new BlockPos(1, 0, 2),};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintSmall = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintMedium = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintTall = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintDouble = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 9, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 6, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 11, 1, 11, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 6, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 6, 7, 7, 7, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 5, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 7, 7, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 1, 1, 1, 1, 7, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 7, 8, 7, 0},
|
||||
{0, 0, 0, 0, 7, 6, 1, 1, 1, 1, 1, 8, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 8, 7, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 4, 4, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 7, 4},
|
||||
{0, 0, 0, 0, 7, 1, 1, 11, 7, 1, 1, 7, 4}, {0, 0, 0, 0, 7, 5, 6, 1, 7, 7, 7, 7, 4},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 4, 4, 4, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 4, 4, 4, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 6, 7, 4, 1, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 5, 7, 4, 4, 4, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 4, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 1, 7, 4, 1, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 2, 7, 0, 0, 0},
|
||||
{0, 0, 0, 7, 5, 2, 2, 6, 2, 7, 4, 0, 0}, {0, 0, 0, 7, 2, 2, 2, 2, 2, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 3, 3, 3, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 10, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 8, 1, 1, 1, 1, 3, 7, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, {0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0},
|
||||
{0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0}, {0, 0, 4, 7, 1, 1, 1, 1, 12, 7, 4, 0, 0},
|
||||
{0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger for things done in the arcane workbench. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbenchTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, ArcaneWorkbenchTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public ArcaneWorkbenchTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new ArcaneWorkbenchTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public ArcaneWorkbenchTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new ArcaneWorkbenchTrigger.Instance(this.id, ItemPredicate.deserialize(json.get("item")));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, ItemStack stack){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final ItemPredicate item;
|
||||
|
||||
public Instance(ResourceLocation criterionIn, ItemPredicate item){
|
||||
super(criterionIn);
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean test(ItemStack stack){
|
||||
return this.item.test(stack);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<ArcaneWorkbenchTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(ItemStack stack){
|
||||
|
||||
List<Listener<ArcaneWorkbenchTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(stack)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
package electroblob.wizardry.util;
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.advancements.ICriterionInstance;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
@@ -34,7 +33,7 @@ public class CustomAdvancementTrigger implements ICriterionTrigger<CustomAdvance
|
||||
* advancement to happen.
|
||||
*/
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
public Instance(ResourceLocation triggerId) {
|
||||
public Instance(ResourceLocation triggerId) {
|
||||
super(triggerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger which is triggered when a spell is cast. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class SpellCastTrigger implements ICriterionTrigger<SpellCastTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, SpellCastTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public SpellCastTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<SpellCastTrigger.Instance> listener){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new SpellCastTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<SpellCastTrigger.Instance> listener){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public SpellCastTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new SpellCastTrigger.Instance(this.id, SpellPredicate.deserialize(json.get("spell")),
|
||||
ItemPredicate.deserialize(json.get("item")));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, Spell spell, ItemStack stack){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(spell, stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final SpellPredicate spell;
|
||||
private final ItemPredicate item;
|
||||
|
||||
public Instance(ResourceLocation criterion, SpellPredicate spell, ItemPredicate item){
|
||||
super(criterion);
|
||||
this.spell = spell;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell, ItemStack stack){
|
||||
return this.spell.test(spell) && item.test(stack);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<SpellCastTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<SpellCastTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<SpellCastTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(Spell spell, ItemStack stack){
|
||||
|
||||
List<Listener<SpellCastTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<SpellCastTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(spell, stack)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<SpellCastTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger that is triggered when a spell is discovered. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class SpellDiscoveryTrigger implements ICriterionTrigger<SpellDiscoveryTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, SpellDiscoveryTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public SpellDiscoveryTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new SpellDiscoveryTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public SpellDiscoveryTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
|
||||
String s = JsonUtils.getString(json, "source");
|
||||
DiscoverSpellEvent.Source source = DiscoverSpellEvent.Source.byName(s);
|
||||
if(source == null) throw new JsonSyntaxException("No such spell discovery source: " + s);
|
||||
return new SpellDiscoveryTrigger.Instance(this.id, SpellPredicate.deserialize(json.get("spell")), source);
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, Spell spell, DiscoverSpellEvent.Source source){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(spell, source);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final SpellPredicate spell;
|
||||
private final DiscoverSpellEvent.Source source;
|
||||
|
||||
public Instance(ResourceLocation criterion, SpellPredicate spell, DiscoverSpellEvent.Source source){
|
||||
super(criterion);
|
||||
this.spell = spell;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell, DiscoverSpellEvent.Source source){
|
||||
return this.spell.test(spell) && source == this.source;
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<SpellDiscoveryTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(Spell spell, DiscoverSpellEvent.Source source){
|
||||
|
||||
List<Listener<SpellDiscoveryTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<SpellDiscoveryTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(spell, source)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<SpellDiscoveryTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Predicate used by advancement triggers to match spells. */
|
||||
public class SpellPredicate {
|
||||
|
||||
public static final SpellPredicate ANY = new SpellPredicate();
|
||||
private final Spell spell;
|
||||
private final Tier[] tiers;
|
||||
private final Element[] elements;
|
||||
|
||||
public SpellPredicate(){
|
||||
this.spell = null;
|
||||
this.tiers = Tier.values();
|
||||
this.elements = Element.values();
|
||||
}
|
||||
|
||||
public SpellPredicate(@Nullable Spell spell, Tier[] tiers, Element[] elements){
|
||||
this.spell = spell;
|
||||
this.tiers = tiers;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell){
|
||||
|
||||
if(this.spell != null && spell != this.spell){
|
||||
return false;
|
||||
}else if(!Arrays.asList(this.tiers).contains(spell.getTier())){
|
||||
return false;
|
||||
}else if(!Arrays.asList(this.elements).contains(spell.getElement())){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static SpellPredicate deserialize(@Nullable JsonElement element){
|
||||
|
||||
if(element != null && !element.isJsonNull()){
|
||||
|
||||
JsonObject jsonobject = JsonUtils.getJsonObject(element, "spell");
|
||||
|
||||
Spell spell = null;
|
||||
|
||||
if(jsonobject.has("spell")){
|
||||
|
||||
String s = JsonUtils.getString(jsonobject, "spell");
|
||||
spell = Spell.get(s);
|
||||
|
||||
if(spell == null){
|
||||
throw new JsonSyntaxException("Unknown spell id '" + s + "'");
|
||||
}
|
||||
}
|
||||
|
||||
Tier[] tiers = Tier.values();
|
||||
|
||||
if(jsonobject.has("tiers")){
|
||||
try{
|
||||
JsonArray array = JsonUtils.getJsonArray(jsonobject, "tiers");
|
||||
tiers = Streams.stream(array)
|
||||
.map(je -> Tier.fromName(JsonUtils.getString(je, "element of array tiers")))
|
||||
.toArray(Tier[]::new);
|
||||
}catch(IllegalArgumentException e){
|
||||
throw new JsonSyntaxException("Incorrect spell predicate value", e);
|
||||
}
|
||||
}
|
||||
|
||||
Element[] elements = Element.values();
|
||||
|
||||
if(jsonobject.has("elements")){
|
||||
try{
|
||||
JsonArray array = JsonUtils.getJsonArray(jsonobject, "elements");
|
||||
elements = Streams.stream(array)
|
||||
.map(je -> Element.fromName(JsonUtils.getString(je, "element of array elements")))
|
||||
.toArray(Element[]::new);
|
||||
}catch(IllegalArgumentException e){
|
||||
throw new JsonSyntaxException("Incorrect spell predicate value", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new SpellPredicate(spell, tiers, elements);
|
||||
|
||||
}else{
|
||||
return ANY;
|
||||
}
|
||||
}
|
||||
|
||||
public static SpellPredicate[] deserializeArray(@Nullable JsonElement element){
|
||||
|
||||
if(element != null && !element.isJsonNull()){
|
||||
|
||||
JsonArray jsonarray = JsonUtils.getJsonArray(element, "spells");
|
||||
SpellPredicate[] predicates = new SpellPredicate[jsonarray.size()];
|
||||
|
||||
for(int i = 0; i < predicates.length; ++i){
|
||||
predicates[i] = deserialize(jsonarray.get(i));
|
||||
}
|
||||
|
||||
return predicates;
|
||||
|
||||
}else{
|
||||
return new SpellPredicate[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.worldgen.WorldGenSurfaceStructure;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.WorldServer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Copied from PositionTrigger and modified to work with wizardry's structures. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class StructureTrigger implements ICriterionTrigger<StructureTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, StructureTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public StructureTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<StructureTrigger.Instance> listener){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new StructureTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<StructureTrigger.Instance> listener){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public StructureTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new StructureTrigger.Instance(this.id, JsonUtils.getString(json, "structure_type"));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(player.getServerWorld(), player.posX, player.posY, player.posZ);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final WorldGenSurfaceStructure structureType;
|
||||
|
||||
public Instance(ResourceLocation criterionIn, String name){
|
||||
super(criterionIn);
|
||||
this.structureType = WorldGenSurfaceStructure.byName(name);
|
||||
}
|
||||
|
||||
public boolean test(WorldServer world, double x, double y, double z){
|
||||
return structureType.isInsideStructure(world, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<StructureTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<StructureTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<StructureTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(WorldServer world, double x, double y, double z){
|
||||
|
||||
List<Listener<StructureTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<StructureTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(world, x, y, z)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<StructureTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package electroblob.wizardry.api;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.SpellProperties;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraftforge.common.util.EnumHelper;
|
||||
|
||||
/**
|
||||
* This class contains methods similar to those in {@link EnumHelper} specific to wizardry's enum types.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
public final class WizardryEnumHelper {
|
||||
|
||||
// Make sure these are updated if the relevant constructors are updated!
|
||||
// Can't we do some kind of reflection to access them and generate these arrays?
|
||||
private static final Class[] TIER_ARGUMENTS = new Class[]{Integer.class, Integer.class, Integer.class, Style.class, String.class};
|
||||
private static final Class[] ELEMENT_ARGUMENTS = new Class[]{Style.class, String.class, String.class};
|
||||
private static final Class[] SPELL_TYPE_ARGUMENTS = new Class[]{String.class};
|
||||
private static final Class[] SPELL_CONTEXT_ARGUMENTS = new Class[]{String.class};
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new tiers. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code Tier}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has partial support for externally-added tiers; you'll need to do some of the
|
||||
* legwork yourself though.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param maxCharge The maximum charge for wands of this tier.
|
||||
* @param upgradeLimit The maximum total number of special upgrades that can be applied to wands of this tier.
|
||||
* @param weight The weight of this tier in the standard weighting.
|
||||
* @param colour The colour of text associated with this tier, as a style object.
|
||||
* @param name The unlocalised name of this tier, as used in translation keys.
|
||||
* @return The resulting {@code Tier} enum constant.
|
||||
*/
|
||||
public static Tier addTier(String codeName, int maxCharge, int upgradeLimit, int weight, Style colour, String name){
|
||||
return EnumHelper.addEnum(Tier.class, codeName, TIER_ARGUMENTS, maxCharge, upgradeLimit, weight, colour, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new elements. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code Element}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has full support for externally-added elements.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param colour The colour of text associated with this element, as a style object.
|
||||
* @param name The unlocalised name of this element, as used in translation keys.
|
||||
* @param modID The mod ID of the mod that added this element, for icon rendering purposes.
|
||||
* @return The resulting {@code Element} enum constant.
|
||||
*/
|
||||
// This is the only one that needs the mod ID argument because elements are the only ones that have icons
|
||||
// For some reason Minecraft doesn't seem to care about the resource domain for lang files, it just pools them
|
||||
public static Element addElement(String codeName, Style colour, String name, String modID){
|
||||
return EnumHelper.addEnum(Element.class, codeName, ELEMENT_ARGUMENTS, colour, name, modID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new spell types. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code SpellType}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has full support for externally-added spell types.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param name The unlocalised name of this spell type, as used in translation keys.
|
||||
* @return The resulting {@code SpellType} enum constant.
|
||||
*/
|
||||
public static SpellType addSpellType(String codeName, String name){
|
||||
return EnumHelper.addEnum(SpellType.class, codeName, SPELL_TYPE_ARGUMENTS, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new spell contexts (for use in spell property JSON files). Use this method in preference
|
||||
* to the generic one in case the constructor parameters change for {@code Context}.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param name The identifier for this spell context, as used in the JSON file.
|
||||
* @return The resulting {@code Context} enum constant.
|
||||
*/
|
||||
public static SpellProperties.Context addSpellContext(String codeName, String name){
|
||||
return EnumHelper.addEnum(SpellProperties.Context.class, codeName, SPELL_CONTEXT_ARGUMENTS, name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import electroblob.wizardry.WizardryGuiHandler;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.InventoryHelper;
|
||||
@@ -51,6 +52,16 @@ public class BlockArcaneWorkbench extends BlockContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube(IBlockState state){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
|
||||
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
|
||||
EnumFacing side, float hitX, float hitY, float hitZ){
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.MapColor;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import java.util.EnumMap;
|
||||
|
||||
public class BlockCrystal extends Block {
|
||||
|
||||
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class);
|
||||
|
||||
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
|
||||
|
||||
static {
|
||||
map_colours.put(Element.MAGIC, MapColor.PINK);
|
||||
map_colours.put(Element.FIRE, MapColor.ORANGE_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE);
|
||||
map_colours.put(Element.LIGHTNING, MapColor.CYAN);
|
||||
map_colours.put(Element.NECROMANCY, MapColor.PURPLE);
|
||||
map_colours.put(Element.EARTH, MapColor.GREEN);
|
||||
map_colours.put(Element.SORCERY, MapColor.LIME);
|
||||
map_colours.put(Element.HEALING, MapColor.YELLOW);
|
||||
}
|
||||
|
||||
public BlockCrystal(Material material){
|
||||
super(material);
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.MAGIC));
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
this.setHarvestLevel("pickaxe", 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damageDropped(IBlockState state){
|
||||
return (state.getValue(ELEMENT)).ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
|
||||
return map_colours.get(state.getProperties().get(ELEMENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
|
||||
if(this.getCreativeTab() == tab){
|
||||
for(Element element : Element.values()){
|
||||
items.add(new ItemStack(this, 1, element.ordinal()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int metadata){
|
||||
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return (state.getValue(ELEMENT)).ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, ELEMENT);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
@@ -19,6 +17,8 @@ import net.minecraftforge.event.entity.player.BonemealEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
// Extending BlockBush allows me to remove nearly everything from this class.
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockCrystalFlower extends BlockBush {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.SoundType;
|
||||
@@ -13,6 +11,8 @@ import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCrystalOre extends Block {
|
||||
|
||||
public BlockCrystalOre(Material material){
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import net.minecraft.block.BlockFrostedIce;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** Like {@link BlockFrostedIce}, but melting does not depend on light level or neighbouring blocks, and it just
|
||||
* disappears instead of turning to water. */
|
||||
public class BlockDryFrostedIce extends BlockFrostedIce {
|
||||
|
||||
@Override
|
||||
protected void turnIntoWater(World world, BlockPos pos){
|
||||
world.destroyBlock(pos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand){
|
||||
if(rand.nextInt(3) == 0){
|
||||
this.slightlyMelt(worldIn, pos, state, rand, true);
|
||||
}else{
|
||||
worldIn.scheduleUpdate(pos, this, MathHelper.getInt(rand, 20, 40));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,31 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.tileentity.TileEntityMagicLight;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumBlockRenderType;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockMagicLight extends BlockContainer {
|
||||
public class BlockMagicLight extends Block implements ITileEntityProvider {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
//private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
|
||||
public BlockMagicLight(Material par2Material){
|
||||
super(par2Material);
|
||||
public BlockMagicLight(Material material){
|
||||
super(material);
|
||||
this.setLightLevel(1.0f);
|
||||
this.setBlockUnbreakable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,13 +36,38 @@ public class BlockMagicLight extends BlockContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
return AABB;
|
||||
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
|
||||
// Let the player dispel any lights if they have the lantern charm, not just the permanent ones because that would be annoying!
|
||||
if(player.getHeldItem(hand).getItem() instanceof ISpellCastingItem && ItemArtefact.isArtefactActive(player, WizardryItems.charm_light)){
|
||||
|
||||
world.setBlockToAir(pos);
|
||||
return true;
|
||||
|
||||
}else{
|
||||
return super.onBlockActivated(world, pos, state, player, hand, facing, hitX, hitY, hitZ);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
// return AABB;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean isCollidable(){
|
||||
return false;
|
||||
// This method has nothing to do with entity movement, it's just for raytracing
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDestroyEffects(World world, BlockPos pos, net.minecraft.client.particle.ParticleManager manager){
|
||||
if(world.getBlockState(pos).getBlock() == this) return true; // No break particles!
|
||||
else return super.addDestroyEffects(world, pos, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockObsidian;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** Like {@link net.minecraft.block.BlockFrostedIce}, but for lava instead of water. */
|
||||
// This is mostly copied from that class, with a few changes
|
||||
public class BlockObsidianCrust extends BlockObsidian {
|
||||
|
||||
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 3);
|
||||
|
||||
public BlockObsidianCrust(){
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(AGE, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(AGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int meta){
|
||||
return this.getDefaultState().withProperty(AGE, MathHelper.clamp(meta, 0, 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(World world, BlockPos pos, IBlockState state, Random random){
|
||||
if((random.nextInt(3) == 0 || this.countNeighbors(world, pos) < 4) && world.getLightFromNeighbors(pos) > 11 - state.getValue(AGE) - state.getLightOpacity()){
|
||||
this.slightlyMelt(world, pos, state, random, true);
|
||||
}else{
|
||||
world.scheduleUpdate(pos, this, MathHelper.getInt(random, 20, 40));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos){
|
||||
if(block == this){
|
||||
int i = this.countNeighbors(world, pos);
|
||||
|
||||
if(i < 2){
|
||||
this.melt(world, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int countNeighbors(World world, BlockPos pos){
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(EnumFacing enumfacing : EnumFacing.values()){
|
||||
if(world.getBlockState(pos.offset(enumfacing)).getBlock() == this){
|
||||
++i;
|
||||
|
||||
if(i >= 4){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
protected void slightlyMelt(World world, BlockPos pos, IBlockState state, Random random, boolean meltNeighbours){
|
||||
|
||||
int i = state.getValue(AGE);
|
||||
|
||||
if(i < 3){
|
||||
|
||||
world.setBlockState(pos, state.withProperty(AGE, i + 1), 2);
|
||||
world.scheduleUpdate(pos, this, MathHelper.getInt(random, 20, 40));
|
||||
|
||||
}else{
|
||||
|
||||
this.melt(world, pos);
|
||||
|
||||
if(meltNeighbours){
|
||||
|
||||
for(EnumFacing enumfacing : EnumFacing.values()){
|
||||
|
||||
BlockPos blockpos = pos.offset(enumfacing);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
|
||||
if(iblockstate.getBlock() == this){
|
||||
this.slightlyMelt(world, blockpos, iblockstate, random, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void melt(World world, BlockPos pos){
|
||||
world.setBlockState(pos, Blocks.LAVA.getDefaultState());
|
||||
world.neighborChanged(pos, Blocks.LAVA, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, AGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(World world, BlockPos pos, IBlockState state){
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.tileentity.TileEntityShrineCore;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.material.MapColor;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyBool;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.Explosion;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
|
||||
public class BlockPedestal extends Block implements ITileEntityProvider {
|
||||
|
||||
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class,
|
||||
Arrays.copyOfRange(Element.values(), 1, Element.values().length)); // Everything except MAGIC
|
||||
|
||||
// A 'natural' pedestal is one that was generated as part of a structure, is unbreakable and has a tileentity
|
||||
public static final PropertyBool NATURAL = PropertyBool.create("natural");
|
||||
|
||||
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
|
||||
|
||||
static {
|
||||
map_colours.put(Element.FIRE, MapColor.RED_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.LIGHTNING, MapColor.CYAN_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.NECROMANCY, MapColor.PURPLE_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.EARTH, MapColor.BROWN_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.SORCERY, MapColor.GRAY);
|
||||
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
|
||||
}
|
||||
|
||||
public BlockPedestal(Material material){
|
||||
super(material);
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE).withProperty(NATURAL, false));
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
this.setHardness(1.5F);
|
||||
this.setResistance(10.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damageDropped(IBlockState state){
|
||||
return state.getValue(ELEMENT).ordinal(); // Ignore the NATURAL state here, it's unobtainable
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
|
||||
return map_colours.get(state.getProperties().get(ELEMENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
|
||||
// Ignore the NATURAL state here, it's unobtainable
|
||||
if(this.getCreativeTab() == tab){
|
||||
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
|
||||
items.add(new ItemStack(this, 1, element.ordinal()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBlockHardness(IBlockState state, World world, BlockPos pos){
|
||||
return state.getValue(NATURAL) ? -1 : super.getBlockHardness(state, world, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getExplosionResistance(World world, BlockPos pos, @Nullable Entity exploder, Explosion explosion){
|
||||
return world.getBlockState(pos).getValue(NATURAL) ? 6000000.0F : super.getExplosionResistance(world, pos, exploder, explosion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return state.getValue(NATURAL); // Only naturally-generated pedestals have a (shrine core) tile entity
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int meta){
|
||||
return new TileEntityShrineCore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int metadata){
|
||||
boolean natural = false;
|
||||
if(metadata > ELEMENT.getAllowedValues().size()){
|
||||
natural = true;
|
||||
metadata -= ELEMENT.getAllowedValues().size();
|
||||
}
|
||||
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]).withProperty(NATURAL, natural);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(ELEMENT).ordinal() + (state.getValue(NATURAL) ? ELEMENT.getAllowedValues().size() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, ELEMENT, NATURAL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.MapColor;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
|
||||
public class BlockRunestone extends Block {
|
||||
|
||||
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class,
|
||||
Arrays.copyOfRange(Element.values(), 1, Element.values().length)); // Everything except MAGIC
|
||||
|
||||
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
|
||||
|
||||
static {
|
||||
map_colours.put(Element.FIRE, MapColor.RED_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.LIGHTNING, MapColor.CYAN_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.NECROMANCY, MapColor.PURPLE_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.EARTH, MapColor.BROWN_STAINED_HARDENED_CLAY);
|
||||
map_colours.put(Element.SORCERY, MapColor.GRAY);
|
||||
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
|
||||
}
|
||||
|
||||
public BlockRunestone(Material material){
|
||||
super(material);
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE));
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
this.setHardness(1.5F);
|
||||
this.setResistance(10.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damageDropped(IBlockState state){
|
||||
return state.getValue(ELEMENT).ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
|
||||
return map_colours.get(state.getProperties().get(ELEMENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
|
||||
if(this.getCreativeTab() == tab){
|
||||
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
|
||||
items.add(new ItemStack(this, 1, element.ordinal()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int metadata){
|
||||
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(ELEMENT).ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, ELEMENT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -25,8 +25,9 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
// TODO: Apparently you shouldn't extend BlockContainer. I feel like BlockArcaneWorkbench should, but what about the rest?
|
||||
public class BlockSnare extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockSnare extends Block implements ITileEntityProvider {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f);
|
||||
|
||||
@@ -58,10 +59,14 @@ public class BlockSnare extends BlockContainer {
|
||||
|
||||
TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos);
|
||||
|
||||
if(WizardryUtilities.isValidTarget(tileentity.getCaster(), entity)){
|
||||
((EntityLivingBase)entity).attackEntityFrom(
|
||||
MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), 6);
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 100, 2));
|
||||
if(AllyDesignationSystem.isValidTarget(tileentity.getCaster(), entity)){
|
||||
|
||||
entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC),
|
||||
Spells.snare.getProperty(Spell.DAMAGE).floatValue());
|
||||
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS,
|
||||
Spells.snare.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.snare.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
|
||||
world.destroyBlock(pos, false);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -24,10 +22,10 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on
|
||||
// block break.
|
||||
import java.util.Random;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockSpectral extends BlockContainer {
|
||||
public class BlockSpectral extends Block implements ITileEntityProvider {
|
||||
|
||||
public BlockSpectral(Material material){
|
||||
super(material);
|
||||
@@ -76,6 +74,11 @@ public class BlockSpectral extends BlockContainer {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityTimer(1200);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.tileentity.TileEntityStatue;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -21,12 +19,16 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class BlockStatue extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockStatue extends Block implements ITileEntityProvider {
|
||||
|
||||
private boolean isIce;
|
||||
|
||||
|
||||
/** The NBT tag name for storing the petrified flag (used for rendering) in the target's tag compound. */
|
||||
public static final String NBT_KEY = "petrified";
|
||||
public static final String PETRIFIED_NBT_KEY = "petrified";
|
||||
/** The NBT tag name for storing the frozen flag (used for rendering) in the target's tag compound. */
|
||||
public static final String FROZEN_NBT_KEY = "frozen";
|
||||
|
||||
public BlockStatue(Material material){
|
||||
super(material);
|
||||
@@ -110,6 +112,11 @@ public class BlockStatue extends BlockContainer {
|
||||
return this.isIce ? EnumBlockRenderType.MODEL : EnumBlockRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityStatue(this.isIce);
|
||||
@@ -150,7 +157,7 @@ public class BlockStatue extends BlockContainer {
|
||||
|
||||
// This is only when position == 1 because world.destroyBlock calls this function for the other blocks.
|
||||
if(tileentity != null && tileentity.position == 1 && tileentity.creature != null){
|
||||
tileentity.creature.getEntityData().removeTag(BlockStatue.NBT_KEY);
|
||||
tileentity.creature.getEntityData().removeTag(BlockStatue.PETRIFIED_NBT_KEY);
|
||||
tileentity.creature.isDead = false;
|
||||
world.spawnEntity(tileentity.creature);
|
||||
}
|
||||
@@ -198,7 +205,7 @@ public class BlockStatue extends BlockContainer {
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
|
||||
}
|
||||
|
||||
if(!this.isIce) entity.getEntityData().setBoolean(NBT_KEY, true);
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
@@ -216,8 +223,8 @@ public class BlockStatue extends BlockContainer {
|
||||
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(entity, 2, 2);
|
||||
}
|
||||
|
||||
if(!this.isIce) entity.getEntityData().setBoolean(NBT_KEY, true);
|
||||
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
@@ -241,8 +248,8 @@ public class BlockStatue extends BlockContainer {
|
||||
if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(entity, 3, 3);
|
||||
}
|
||||
|
||||
if(!this.isIce) entity.getEntityData().setBoolean(NBT_KEY, true);
|
||||
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSaveTimed;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.BlockDoublePlant.EnumBlockHalf;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockThorns extends BlockBush implements ITileEntityProvider {
|
||||
|
||||
public static final int GROWTH_STAGES = 8;
|
||||
|
||||
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, GROWTH_STAGES-1);
|
||||
public static final PropertyEnum<EnumBlockHalf> HALF = PropertyEnum.create("half", EnumBlockHalf.class);
|
||||
|
||||
public BlockThorns(){
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(HALF, EnumBlockHalf.LOWER).withProperty(AGE, 7));
|
||||
this.setHardness(4);
|
||||
this.setSoundType(SoundType.PLANT);
|
||||
this.setCreativeTab(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
return FULL_BLOCK_AABB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int meta){
|
||||
return this.getDefaultState().withProperty(HALF, EnumBlockHalf.values()[meta / GROWTH_STAGES]).withProperty(AGE, meta % GROWTH_STAGES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(HALF).ordinal() * GROWTH_STAGES + state.getValue(AGE);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void updateTick(World world, BlockPos pos, IBlockState state, Random rand){
|
||||
//
|
||||
// super.updateTick(world, pos, state, rand);
|
||||
//
|
||||
// // Update the state, including on the client, but don't do a block update since it's only visual
|
||||
// if(state.getValue(AGE) < GROWTH_STAGES-1) world.setBlockState(pos, state.withProperty(AGE, state.getValue(AGE) + 1), 2);
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, HALF, AGE);
|
||||
}
|
||||
|
||||
public void placeAt(World world, BlockPos lowerPos, int flags){
|
||||
world.setBlockState(lowerPos, this.getDefaultState().withProperty(HALF, EnumBlockHalf.LOWER).withProperty(AGE, 0), flags);
|
||||
world.setBlockState(lowerPos.up(), this.getDefaultState().withProperty(HALF, EnumBlockHalf.UPPER).withProperty(AGE, 0), flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack){
|
||||
world.setBlockState(pos.up(), this.getDefaultState().withProperty(HALF, EnumBlockHalf.UPPER), 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakBlock(World world, BlockPos pos, IBlockState state){
|
||||
super.breakBlock(world, pos, state);
|
||||
if(state.getValue(HALF) == EnumBlockHalf.LOWER){
|
||||
if(world.getBlockState(pos.up()).getBlock() == this){
|
||||
world.destroyBlock(pos.up(), false);
|
||||
}
|
||||
}else{
|
||||
if(world.getBlockState(pos.down()).getBlock() == this){
|
||||
world.destroyBlock(pos.down(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canBlockStay(World worldIn, BlockPos pos, IBlockState state){
|
||||
if(state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER){
|
||||
return worldIn.getBlockState(pos.down()).getBlock() == this;
|
||||
}else{
|
||||
IBlockState iblockstate = worldIn.getBlockState(pos.up());
|
||||
return iblockstate.getBlock() == this && this.canSustainBush(worldIn.getBlockState(pos.down()));
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos){
|
||||
// // Copied from BlockFlowerPot on authority of the Forge docs, which says this check is necessary
|
||||
// SoundLoopSpellDispenser tileentity = world instanceof ChunkCache ? ((ChunkCache)world).getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK) : world.getTileEntity(pos);
|
||||
//
|
||||
// if(tileentity instanceof TileEntityPlayerSaveTimed){
|
||||
// state = state.withProperty(AGE, Math.min(7, ((TileEntityPlayerSaveTimed)tileentity).timer/2));
|
||||
// }else{
|
||||
// state = state.withProperty(AGE, 7);
|
||||
// }
|
||||
//
|
||||
// return state;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
if(!world.isRemote){
|
||||
if(applyThornDamage(world, pos, entity)){
|
||||
entity.setInWeb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean applyThornDamage(World world, BlockPos pos, Entity target){
|
||||
|
||||
DamageSource source = DamageSource.CACTUS;
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(pos);
|
||||
|
||||
if(tileentity instanceof TileEntityPlayerSaveTimed){
|
||||
if(AllyDesignationSystem.isValidTarget(((TileEntityPlayerSaveTimed)tileentity).getCaster(), target)){
|
||||
source = MagicDamage.causeDirectMagicDamage(((TileEntityPlayerSaveTimed)tileentity).getCaster(),
|
||||
MagicDamage.DamageType.MAGIC);
|
||||
}else{
|
||||
return false; // Don't attack or slow allies of the caster
|
||||
}
|
||||
}
|
||||
|
||||
if(world.getTotalWorldTime() % 20 == 0) target.attackEntityFrom(source, Spells.forest_of_thorns.getProperty(Spell.DAMAGE).floatValue());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block.EnumOffsetType getOffsetType(){
|
||||
return Block.EnumOffsetType.XZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityPlayerSaveTimed(600);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isReplaceable(IBlockAccess world, BlockPos pos){ return false; }
|
||||
@Override protected boolean canSustainBush(IBlockState state){ return state.isNormalCube(); }
|
||||
@Override public Item getItemDropped(IBlockState state, Random rand, int fortune){ return Items.AIR; }
|
||||
@Override public boolean canSilkHarvest(World world, BlockPos pos, IBlockState state, EntityPlayer player){ return false; }
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLeftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event){
|
||||
if(!event.getWorld().isRemote && event.getWorld().getTotalWorldTime() % 20 == 0
|
||||
&& event.getWorld().getBlockState(event.getPos()).getBlock() == WizardryBlocks.thorns){
|
||||
applyThornDamage(event.getWorld(), event.getPos(), event.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Transportation;
|
||||
import electroblob.wizardry.util.Location;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -20,6 +24,10 @@ import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockTransportationStone extends Block {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625f * 5, 0, 0.0625f * 5, 0.0625f * 11, 0.0625f * 6,
|
||||
@@ -103,7 +111,7 @@ public class BlockTransportationStone extends Block {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(stack.getItem() instanceof ItemWand){
|
||||
if(stack.getItem() instanceof ISpellCastingItem){
|
||||
if(WizardData.get(player) != null){
|
||||
|
||||
WizardData data = WizardData.get(player);
|
||||
@@ -112,17 +120,59 @@ public class BlockTransportationStone extends Block {
|
||||
for(int z = -1; z <= 1; z++){
|
||||
BlockPos pos1 = pos.add(x, 0, z);
|
||||
if(testForCircle(world, pos1)){
|
||||
data.setStoneCircleLocation(pos1, world.provider.getDimension());
|
||||
if(!world.isRemote) player.sendMessage(
|
||||
new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm",
|
||||
Spells.transportation.getNameForTranslationFormatted()));
|
||||
|
||||
Location here = new Location(pos1, player.dimension);
|
||||
|
||||
List<Location> locations = data.getVariable(Transportation.LOCATIONS_KEY);
|
||||
if(locations == null) data.setVariable(Transportation.LOCATIONS_KEY, locations = new ArrayList<>(Transportation.MAX_REMEMBERED_LOCATIONS));
|
||||
|
||||
if(ItemArtefact.isArtefactActive(player, WizardryItems.charm_transportation)){
|
||||
|
||||
if(locations.contains(here)){
|
||||
locations.remove(here);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.forget", here.pos.getX(), here.pos.getY(), here.pos.getZ(), here.dimension), true);
|
||||
|
||||
}else{
|
||||
|
||||
locations.add(here);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.remember", here.pos.getX(), here.pos.getY(), here.pos.getZ(), here.dimension), true);
|
||||
|
||||
if(locations.size() > Transportation.MAX_REMEMBERED_LOCATIONS){
|
||||
Location removed = locations.remove(0);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.forget", removed.pos.getX(), removed.pos.getY(), removed.pos.getZ(), removed.dimension), true);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
if(locations.isEmpty()) locations.add(here);
|
||||
else{
|
||||
locations.remove(here); // Prevents duplicates
|
||||
locations.set(locations.size() - 1, here);
|
||||
}
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted()), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!world.isRemote)
|
||||
player.sendMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.invalid"));
|
||||
if(!world.isRemote){
|
||||
player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.invalid"), true);
|
||||
}else{
|
||||
|
||||
BlockPos centre = findMostLikelyCircle(world, pos);
|
||||
// Displays particles in the required shape
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
ParticleBuilder.create(ParticleBuilder.Type.PATH)
|
||||
.pos(WizardryUtilities.getCentre(centre).add(x, -0.3125, z)).clr(0x86ff65)
|
||||
.time(200).scale(2).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -136,12 +186,47 @@ public class BlockTransportationStone extends Block {
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
if(world.getBlockState(pos.add(x, 0, z)).getBlock() != WizardryBlocks.transportation_stone){
|
||||
if(x != 0 || z != 0) return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static BlockPos findMostLikelyCircle(World world, BlockPos pos){
|
||||
|
||||
int bestSoFar = 0;
|
||||
BlockPos result = null;
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
BlockPos pos1 = pos.add(x, 0, z);
|
||||
int n = getCircleCompleteness(world, pos1);
|
||||
if(n > bestSoFar){
|
||||
bestSoFar = n;
|
||||
result = pos1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getCircleCompleteness(World world, BlockPos pos){
|
||||
|
||||
int n = 0;
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
if(world.getBlockState(pos.add(x, 0, z)).getBlock() == WizardryBlocks.transportation_stone) n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -17,15 +16,17 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break.
|
||||
public class BlockVanishingCobweb extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockVanishingCobweb extends Block implements ITileEntityProvider {
|
||||
|
||||
public BlockVanishingCobweb(Material material){
|
||||
super(material);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public BlockRenderLayer getBlockLayer(){
|
||||
@Override
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.CUTOUT;
|
||||
}
|
||||
|
||||
@@ -49,6 +50,11 @@ public class BlockVanishingCobweb extends BlockContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityTimer(400);
|
||||
|
||||
@@ -1,115 +1,54 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.CommonProxy;
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.audio.MovingSoundEntity;
|
||||
import electroblob.wizardry.client.audio.SoundLoop;
|
||||
import electroblob.wizardry.client.audio.SoundLoopSpell;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.client.gui.GuiWizardHandbook;
|
||||
import electroblob.wizardry.client.gui.config.NamedBooleanEntry;
|
||||
import electroblob.wizardry.client.gui.config.SpellHUDSkinChooserEntry;
|
||||
import electroblob.wizardry.client.gui.handbook.GuiWizardHandbook;
|
||||
import electroblob.wizardry.client.model.ModelWizardArmour;
|
||||
import electroblob.wizardry.client.particle.*;
|
||||
import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory;
|
||||
import electroblob.wizardry.client.renderer.LayerStone;
|
||||
import electroblob.wizardry.client.renderer.RenderArcaneWorkbench;
|
||||
import electroblob.wizardry.client.renderer.RenderBlackHole;
|
||||
import electroblob.wizardry.client.renderer.RenderBlank;
|
||||
import electroblob.wizardry.client.renderer.RenderBubble;
|
||||
import electroblob.wizardry.client.renderer.RenderDecay;
|
||||
import electroblob.wizardry.client.renderer.RenderDecoy;
|
||||
import electroblob.wizardry.client.renderer.RenderEvilWizard;
|
||||
import electroblob.wizardry.client.renderer.RenderFireRing;
|
||||
import electroblob.wizardry.client.renderer.RenderForceArrow;
|
||||
import electroblob.wizardry.client.renderer.RenderHammer;
|
||||
import electroblob.wizardry.client.renderer.RenderIceGiant;
|
||||
import electroblob.wizardry.client.renderer.RenderIceSpike;
|
||||
import electroblob.wizardry.client.renderer.RenderLightningDisc;
|
||||
import electroblob.wizardry.client.renderer.RenderMagicArrow;
|
||||
import electroblob.wizardry.client.renderer.RenderMagicLight;
|
||||
import electroblob.wizardry.client.renderer.RenderPhoenix;
|
||||
import electroblob.wizardry.client.renderer.RenderProjectile;
|
||||
import electroblob.wizardry.client.renderer.RenderSigil;
|
||||
import electroblob.wizardry.client.renderer.RenderSpiritHorse;
|
||||
import electroblob.wizardry.client.renderer.RenderSpiritWolf;
|
||||
import electroblob.wizardry.client.renderer.RenderStatue;
|
||||
import electroblob.wizardry.client.renderer.RenderWizard;
|
||||
import electroblob.wizardry.client.renderer.*;
|
||||
import electroblob.wizardry.command.SpellEmitter;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.EntityShield;
|
||||
import electroblob.wizardry.entity.construct.EntityArrowRain;
|
||||
import electroblob.wizardry.entity.construct.EntityBlackHole;
|
||||
import electroblob.wizardry.entity.construct.EntityBlizzard;
|
||||
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.EntityLightningSigil;
|
||||
import electroblob.wizardry.entity.construct.EntityTornado;
|
||||
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.EntityPhoenix;
|
||||
import electroblob.wizardry.entity.living.EntityShadowWraith;
|
||||
import electroblob.wizardry.entity.living.EntitySpiritHorse;
|
||||
import electroblob.wizardry.entity.living.EntitySpiritWolf;
|
||||
import electroblob.wizardry.entity.living.EntityStormElemental;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
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.entity.construct.*;
|
||||
import electroblob.wizardry.entity.living.*;
|
||||
import electroblob.wizardry.entity.projectile.*;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
|
||||
import electroblob.wizardry.item.ItemScroll;
|
||||
import electroblob.wizardry.item.ItemSpellBook;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.PacketNPCCastSpell;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync.Message;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.packet.*;
|
||||
import electroblob.wizardry.potion.PotionSlowTime;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Clairvoyance;
|
||||
import electroblob.wizardry.spell.None;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityMagicLight;
|
||||
import electroblob.wizardry.tileentity.TileEntityShrineCore;
|
||||
import electroblob.wizardry.tileentity.TileEntityStatue;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiMerchant;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.entity.RenderBlaze;
|
||||
import net.minecraft.client.renderer.entity.RenderHusk;
|
||||
import net.minecraft.client.renderer.entity.RenderSkeleton;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
@@ -119,19 +58,33 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityBlaze;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.ContainerMerchant;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.village.MerchantRecipeList;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The client proxy for wizardry.
|
||||
@@ -145,7 +98,7 @@ public class ClientProxy extends CommonProxy {
|
||||
public static MixedFontRenderer mixedFontRenderer;
|
||||
|
||||
/** Static particle factory map */
|
||||
private static Map<Type, IWizardryParticleFactory> factories;
|
||||
private static final Map<ResourceLocation, IWizardryParticleFactory> factories = new HashMap<>();
|
||||
|
||||
// Key Bindings
|
||||
public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", Keyboard.KEY_N, "key.categories." + Wizardry.MODID);
|
||||
@@ -154,6 +107,9 @@ public class ClientProxy extends CommonProxy {
|
||||
// Armour Model
|
||||
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
|
||||
|
||||
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style)}). */
|
||||
private static final int TOOLTIP_WRAP_WIDTH = 140;
|
||||
|
||||
// SECTION Registry
|
||||
// ===============================================================================================================
|
||||
|
||||
@@ -178,9 +134,18 @@ public class ClientProxy extends CommonProxy {
|
||||
public void registerResourceReloadListeners(){
|
||||
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
|
||||
if(manager instanceof IReloadableResourceManager){
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiSpellDisplay::loadSkins);
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiWizardHandbook::loadHandbookFile);
|
||||
}
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiSpellDisplay::loadSkins);
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiWizardHandbook::loadHandbookFile);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void registerSoundEventListener(){
|
||||
// Minecraft.getMinecraft().getSoundHandler().addListener(ContinuousSpellSoundEntity::soundPlayed);
|
||||
// }
|
||||
|
||||
public void registerAtlasMarkers(){
|
||||
WizardryAntiqueAtlasIntegration.registerMarkers();
|
||||
}
|
||||
|
||||
// SECTION Misc
|
||||
@@ -196,16 +161,35 @@ public class ClientProxy extends CommonProxy {
|
||||
property.setConfigEntryClass(SpellHUDSkinChooserEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToNamedBooleanEntry(Property property){
|
||||
property.setConfigEntryClass(NamedBooleanEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getTheWorld(){
|
||||
return Minecraft.getMinecraft().world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, sound, volume, pitch, repeat));
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity<>(entity, sound, category, volume, pitch, repeat));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellEntity(start, loop, end, spell, entity, volume, pitch));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch, int duration){
|
||||
if(duration == -1){
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellDispenser(start, loop, end, spell, world, x, y, z, volume, pitch));
|
||||
}else{
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellPosTimed(start, loop, end, spell, duration, x, y, z, volume, pitch));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSpellHUDSkins(){
|
||||
return GuiSpellDisplay.getSkinKeys();
|
||||
@@ -214,6 +198,40 @@ public class ClientProxy extends CommonProxy {
|
||||
// SECTION Items
|
||||
// ===============================================================================================================
|
||||
|
||||
@Override
|
||||
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
|
||||
|
||||
EntityPlayerSP player = Minecraft.getMinecraft().player;
|
||||
|
||||
if(player == null) return false;
|
||||
|
||||
// Displayed recipe
|
||||
if(Minecraft.getMinecraft().currentScreen instanceof GuiMerchant){
|
||||
// It doesn't actually matter if the recipe is selected or not, since the itemstack will only ever
|
||||
// match one of them anyway - and we'd have to reflect into GuiMerchant to get the selected recipe
|
||||
MerchantRecipeList recipes = ((GuiMerchant)Minecraft.getMinecraft().currentScreen).getMerchant().getRecipes(player);
|
||||
if(recipes != null && recipes.stream().anyMatch(r -> r.getItemToSell() == stack)){
|
||||
// Spell books are always discovered when wizards are selling them
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Recipe output slot
|
||||
// Required or players would be able to find out what the spell is without actually completing the trade
|
||||
if(player.openContainer instanceof ContainerMerchant){
|
||||
|
||||
if(((ContainerMerchant)player.openContainer).getMerchantInventory().getStackInSlot(2) == stack){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Wizardry.settings.discoveryMode) return true;
|
||||
if(player.isCreative()) return true;
|
||||
if(WizardData.get(player) != null && WizardData.get(player).hasSpellBeenDiscovered(spell)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontRenderer getFontRenderer(ItemStack stack){
|
||||
|
||||
@@ -222,12 +240,10 @@ public class ClientProxy extends CommonProxy {
|
||||
if(stack.getItem() instanceof ItemWand){
|
||||
spell = WandHelper.getCurrentSpell(stack);
|
||||
}else if(stack.getItem() instanceof ItemSpellBook || stack.getItem() instanceof ItemScroll){
|
||||
spell = Spell.get(stack.getItemDamage());
|
||||
spell = Spell.byMetadata(stack.getItemDamage());
|
||||
}
|
||||
|
||||
if(Minecraft.getMinecraft().player != null && Wizardry.settings.discoveryMode && WizardData.get(Minecraft.getMinecraft().player) != null
|
||||
&& !Minecraft.getMinecraft().player.capabilities.isCreativeMode
|
||||
&& !WizardData.get(Minecraft.getMinecraft().player).hasSpellBeenDiscovered(spell)){
|
||||
if(!shouldDisplayDiscovered(spell, stack)){
|
||||
return mixedFontRenderer;
|
||||
}
|
||||
|
||||
@@ -237,14 +253,14 @@ public class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public String getScrollDisplayName(ItemStack scroll){
|
||||
|
||||
Spell spell = Spell.get(scroll.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(scroll.getItemDamage());
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
boolean discovered = true;
|
||||
// It seems that this method is called when the world is loading, before thePlayer has been initialised.
|
||||
// If the player is null, the spell is assumed to be discovered.
|
||||
if(player != null && Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
if(player != null && Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
@@ -265,37 +281,50 @@ public class ClientProxy extends CommonProxy {
|
||||
return super.getConjuredBowDurability(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){
|
||||
String description = style.getFormattingCode() + I18n.format(key);
|
||||
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
|
||||
}
|
||||
|
||||
// SECTION Particles
|
||||
// ===============================================================================================================
|
||||
|
||||
/** Use {@link ParticleWizardry#registerParticle(ResourceLocation, IWizardryParticleFactory)}, this is internal. */
|
||||
// I mean, it does exactly the same thing but I might want to make it do something else in future...
|
||||
public static void addParticleFactory(ResourceLocation name, IWizardryParticleFactory factory){
|
||||
factories.put(name, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initParticleFactories(){
|
||||
|
||||
factories = new HashMap<>();
|
||||
|
||||
factories.put(Type.BEAM, ParticleBeam::new);
|
||||
factories.put(Type.BUFF, ParticleBuff::new);
|
||||
factories.put(Type.DARK_MAGIC, ParticleDarkMagic::new);
|
||||
factories.put(Type.DUST, ParticleDust::new);
|
||||
factories.put(Type.FLASH, ParticleFlash::new);
|
||||
factories.put(Type.ICE, ParticleIce::new);
|
||||
factories.put(Type.LEAF, ParticleLeaf::new);
|
||||
factories.put(Type.LIGHTNING, ParticleLightning::new);
|
||||
factories.put(Type.LIGHTNING_PULSE, ParticleLightningPulse::new);
|
||||
factories.put(Type.MAGIC_BUBBLE, ParticleMagicBubble::new);
|
||||
factories.put(Type.MAGIC_FIRE, ParticleMagicFlame::new);
|
||||
factories.put(Type.PATH, ParticlePath::new);
|
||||
factories.put(Type.SCORCH, ParticleScorch::new);
|
||||
factories.put(Type.SNOW, ParticleSnow::new);
|
||||
factories.put(Type.SPARK, ParticleSpark::new);
|
||||
factories.put(Type.SPARKLE, ParticleSparkle::new);
|
||||
public void registerParticles(){
|
||||
// I'll be a good programmer and use the API method rather than the one above. Lead by example, as they say...
|
||||
ParticleWizardry.registerParticle(Type.BEAM, ParticleBeam::new);
|
||||
ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new);
|
||||
ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new);
|
||||
ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new);
|
||||
ParticleWizardry.registerParticle(Type.FLASH, ParticleFlash::new);
|
||||
ParticleWizardry.registerParticle(Type.ICE, ParticleIce::new);
|
||||
ParticleWizardry.registerParticle(Type.LEAF, ParticleLeaf::new);
|
||||
ParticleWizardry.registerParticle(Type.LIGHTNING, ParticleLightning::new);
|
||||
ParticleWizardry.registerParticle(Type.LIGHTNING_PULSE, ParticleLightningPulse::new);
|
||||
ParticleWizardry.registerParticle(Type.MAGIC_BUBBLE, ParticleMagicBubble::new);
|
||||
ParticleWizardry.registerParticle(Type.MAGIC_FIRE, ParticleMagicFlame::new);
|
||||
ParticleWizardry.registerParticle(Type.PATH, ParticlePath::new);
|
||||
ParticleWizardry.registerParticle(Type.SCORCH, ParticleScorch::new);
|
||||
ParticleWizardry.registerParticle(Type.SNOW, ParticleSnow::new);
|
||||
ParticleWizardry.registerParticle(Type.SPARK, ParticleSpark::new);
|
||||
ParticleWizardry.registerParticle(Type.SPARKLE, ParticleSparkle::new);
|
||||
ParticleWizardry.registerParticle(Type.SPHERE, ParticleSphere::new);
|
||||
ParticleWizardry.registerParticle(Type.SUMMON, ParticleSummon::new);
|
||||
ParticleWizardry.registerParticle(Type.VINE, ParticleVine::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleWizardry createParticle(Type type, World world, double x, double y, double z){
|
||||
public ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){
|
||||
IWizardryParticleFactory factory = factories.get(type);
|
||||
if(factory == null){
|
||||
Wizardry.logger.warn("Unrecognised particle type %s ! Ensure the particle is properly registered.", type);
|
||||
Wizardry.logger.warn("Unrecognised particle type {} ! Ensure the particle is properly registered.", type);
|
||||
return null;
|
||||
}
|
||||
return factory.createParticle(world, x, y, z);
|
||||
@@ -315,14 +344,12 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
((EntityPlayer)caster).setActiveHand(message.hand);
|
||||
|
||||
// Duration isn't needed because it only ever affects things server-side, and anything that is
|
||||
// seen client-side gets synced elsewhere.
|
||||
spell.cast(world, (EntityPlayer)caster, message.hand, 0, message.modifiers);
|
||||
|
||||
Source source = Source.OTHER;
|
||||
@@ -337,19 +364,34 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
// No need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
// The handler for this event now deals with discovery.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityPlayer)caster, spell, message.modifiers, source));
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(source, spell, (EntityPlayer)caster, message.modifiers));
|
||||
|
||||
}else{
|
||||
Wizardry.logger.warn("Recieved a PacketCastSpell, but the caster ID was not the ID of a player");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCastSpellAtPosPacket(PacketCastSpellAtPos.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
|
||||
spell.cast(world, message.position.x, message.position.y, message.position.z, message.direction, 0, message.duration, message.modifiers);
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.COMMAND, spell, world, message.position.x, message.position.y, message.position.z, message.direction, message.modifiers));
|
||||
|
||||
if(spell.isContinuous){
|
||||
SpellEmitter.add(spell, world, message.position.x, message.position.y, message.position.z, message.direction, message.duration, message.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
@@ -359,7 +401,7 @@ public class ClientProxy extends CommonProxy {
|
||||
if(data.isCasting()){
|
||||
WizardData.get((EntityPlayer)caster).stopCastingContinuousSpell();
|
||||
}else{
|
||||
WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers);
|
||||
WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers, message.duration);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@@ -373,7 +415,7 @@ public class ClientProxy extends CommonProxy {
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Entity target = message.targetID == -1 ? null : world.getEntityByID(message.targetID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityLiving){
|
||||
|
||||
@@ -382,7 +424,7 @@ public class ClientProxy extends CommonProxy {
|
||||
spell.cast(world, (EntityLiving)caster, message.hand, 0, (EntityLivingBase)target, message.modifiers);
|
||||
// Again, no need to check if the spell succeeded, because the packet is only ever sent when it
|
||||
// succeeds.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityLiving)caster, spell, message.modifiers, Source.NPC));
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.NPC, spell, (EntityLiving)caster, message.modifiers));
|
||||
}
|
||||
|
||||
if(caster instanceof ISpellCaster){
|
||||
@@ -391,10 +433,41 @@ public class ClientProxy extends CommonProxy {
|
||||
((EntityLiving)caster).setAttackTarget((EntityLivingBase)target);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
}else if(caster != null){
|
||||
Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
|
||||
if(world.getTileEntity(message.pos) instanceof TileEntityDispenser){ // Should always be true
|
||||
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
|
||||
spell.cast(world, message.x, message.y, message.z, message.direction, 0, -1, message.modifiers);
|
||||
// No need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.DISPENSER, spell, world, message.x, message.y,
|
||||
message.z, message.direction, message.modifiers));
|
||||
|
||||
if(spell.isContinuous || spell instanceof None){
|
||||
|
||||
DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser)world.getTileEntity(message.pos));
|
||||
|
||||
if(spell.isContinuous){
|
||||
data.startCasting(spell, message.x, message.y, message.z, message.duration, message.modifiers);
|
||||
}else{
|
||||
data.stopCasting();
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
Wizardry.logger.warn("Recieved a PacketDispenserCastSpell, but no tileEntity was found at the supplied location.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){
|
||||
@@ -402,78 +475,150 @@ public class ClientProxy extends CommonProxy {
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
// Moved from when the packet is sent to when it is received; fixes the sound not playing in first person.
|
||||
caster.playSound(SoundEvents.BLOCK_PORTAL_TRAVEL, 1, 1);
|
||||
caster.playSound(WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, 1, 1);
|
||||
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.02, 0).clr(0.6f, 1, 0.6f)
|
||||
.time(80 + world.rand.nextInt(10)).spawn(world);
|
||||
}
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0);
|
||||
}
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePlayerSyncPacket(Message message){
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){
|
||||
|
||||
WizardData properties = WizardData.get(Minecraft.getMinecraft().player);
|
||||
WizardData data = WizardData.get(Minecraft.getMinecraft().player);
|
||||
|
||||
if(properties != null){
|
||||
if(data != null){
|
||||
|
||||
properties.spellsDiscovered = message.spellsDiscovered;
|
||||
data.synchronisedRandom.setSeed(message.seed);
|
||||
data.spellsDiscovered = message.spellsDiscovered;
|
||||
|
||||
message.spellData.forEach(data::setVariable);
|
||||
|
||||
if(message.selectedMinionID == -1){
|
||||
properties.selectedMinion = null;
|
||||
data.selectedMinion = null;
|
||||
}else{
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.selectedMinionID);
|
||||
|
||||
if(entity instanceof ISummonedCreature){
|
||||
properties.selectedMinion = new WeakReference<ISummonedCreature>((ISummonedCreature)entity);
|
||||
data.selectedMinion = new WeakReference<>((ISummonedCreature)entity);
|
||||
}else{
|
||||
properties.selectedMinion = null;
|
||||
data.selectedMinion = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleGlyphDataPacket(electroblob.wizardry.packet.PacketGlyphData.Message message){
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){
|
||||
|
||||
SpellGlyphData data = SpellGlyphData.get(Minecraft.getMinecraft().world);
|
||||
|
||||
data.randomNames = new HashMap<Spell, String>();
|
||||
data.randomDescriptions = new HashMap<Spell, String>();
|
||||
data.randomNames = new HashMap<>();
|
||||
data.randomDescriptions = new HashMap<>();
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
// -1 because the none spell isn't included
|
||||
data.randomNames.put(spell, message.names.get(spell.id() - 1));
|
||||
data.randomDescriptions.put(spell, message.descriptions.get(spell.id() - 1));
|
||||
// This is a case where we must use the network ID, not the metadata
|
||||
data.randomNames.put(spell, message.names.get(spell.networkID() - 1));
|
||||
data.randomDescriptions.put(spell, message.descriptions.get(spell.networkID() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClairvoyancePacket(electroblob.wizardry.packet.PacketClairvoyance.Message message){
|
||||
public void handleEmitterDataPacket(PacketEmitterData.Message message){
|
||||
message.emitters.forEach(e -> e.setWorld(Minecraft.getMinecraft().world)); // Do this as soon as possible!
|
||||
SpellEmitterData data = SpellEmitterData.get(Minecraft.getMinecraft().world);
|
||||
// We shouldn't need to clear the emitters because when a player logs in or changes dimension the client world
|
||||
// is wiped anyway, so the call to get() above should result in a fresh SpellEmitterData instance
|
||||
message.emitters.forEach(data::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){
|
||||
Clairvoyance.spawnPathPaticles(Minecraft.getMinecraft().world, message.path, message.durationMultiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){
|
||||
GuiWizardHandbook.updateUnlockStatus(message.showToasts, message.completedAdvancements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.hostID);
|
||||
if(entity instanceof EntityLivingBase) PotionSlowTime.unblockNearbyEntities((EntityLivingBase)entity);
|
||||
else Wizardry.logger.warn("Received a PacketEndSlowTime, but the entity ID did not match any living entity");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
|
||||
if(entity instanceof EntityPlayer){
|
||||
((Resurrection)Spells.resurrection).resurrect((EntityPlayer)entity);
|
||||
if(entity == Minecraft.getMinecraft().player){
|
||||
Minecraft.getMinecraft().world.spawnEntity(entity);
|
||||
Minecraft.getMinecraft().displayGuiScreen(null);
|
||||
}
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketResurrection, but the entity ID did not match any player");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePossessionPacket(PacketPossession.Message message){
|
||||
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
|
||||
|
||||
if(entity instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)entity;
|
||||
|
||||
if(message.targetID == -1){
|
||||
((Possession)Spells.possession).endPossession(player);
|
||||
}else{
|
||||
Entity target = Minecraft.getMinecraft().world.getEntityByID(message.targetID);
|
||||
if(target instanceof EntityLiving){
|
||||
((Possession)Spells.possession).possess(player, (EntityLiving)target, message.duration);
|
||||
player.sendStatusMessage(new TextComponentTranslation("spell." + Spells.possession.getRegistryName()
|
||||
+ ".success", Minecraft.getMinecraft().gameSettings.keyBindSneak.getDisplayName()), true);
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketPossession, but the target ID did not match any living entity");
|
||||
}
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketPossession, but the player ID did not match any player");
|
||||
}
|
||||
|
||||
public void handleConquerShrinePacket(PacketConquerShrine.Message message){
|
||||
|
||||
TileEntity tileEntity = Minecraft.getMinecraft().world.getTileEntity(new BlockPos(message.x, message.y, message.z));
|
||||
|
||||
if(tileEntity instanceof TileEntityShrineCore){
|
||||
((TileEntityShrineCore)tileEntity).conquer();
|
||||
|
||||
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
|
||||
}
|
||||
|
||||
// SECTION Rendering
|
||||
// ===============================================================================================================
|
||||
|
||||
@@ -486,6 +631,7 @@ public class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public void initialiseLayers(){
|
||||
LayerStone.initialiseLayers();
|
||||
LayerFrost.initialiseLayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -495,6 +641,12 @@ public class ClientProxy extends CommonProxy {
|
||||
// Yet another advantage to the new system: turns out you don't even need to register the renderer if you
|
||||
// just want the vanilla one for the mob you're extending.
|
||||
|
||||
// Luckily for us, the vanilla husk renderer is only parametrised to EntityZombie
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityHuskMinion.class, RenderHusk::new);
|
||||
// This now extends AbstractSkeleton so we need to bind the renderer ourselves
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySkeletonMinion.class, RenderSkeleton::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityStrayMinion.class, RenderStrayMinion::new);
|
||||
|
||||
// An anonymous class in a lambda expression! No point writing a separate class really, is there?
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityLightningWraith.class, manager -> new RenderBlaze(manager){
|
||||
@Override
|
||||
@@ -536,7 +688,7 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
// Throwables
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark_bomb.png"), false));
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/spark_bomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityFirebomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/firebomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityPoisonBomb.class,
|
||||
@@ -555,6 +707,14 @@ public class ClientProxy extends CommonProxy {
|
||||
manager -> new RenderLightningDisc(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySmokeBomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/smoke_bomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityEmber.class,
|
||||
manager -> new RenderProjectile(manager, 0.15f, new ResourceLocation(Wizardry.MODID, "textures/entity/ember.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityMagicFireball.class,
|
||||
manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/fireball.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityLargeMagicFireball.class,
|
||||
manager -> new RenderProjectile(manager, 1.5f, new ResourceLocation(Wizardry.MODID, "textures/entity/fireball.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityIceball.class,
|
||||
manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/iceball.png"), false));
|
||||
|
||||
// Effects and constructs
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBlackHole.class, RenderBlackHole::new);
|
||||
@@ -562,13 +722,14 @@ public class ClientProxy extends CommonProxy {
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBubble.class, RenderBubble::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new);
|
||||
//RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new);
|
||||
|
||||
// Stuff that doesn't render
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBlizzard.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityTornado.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityArrowRain.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityShadowWraith.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityThunderbolt.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new);
|
||||
@@ -586,6 +747,8 @@ public class ClientProxy extends CommonProxy {
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class,
|
||||
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, RenderDecay::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityCombustionRune.class,
|
||||
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/combustion_rune.png"), 2.0f, true));
|
||||
|
||||
// TESRs
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench());
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Utility class containing some useful static methods for drawing GUIs. Previously these were spread across the main
|
||||
@@ -23,7 +17,7 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
* @since Wizardry 4.2
|
||||
* @see MixedFontRenderer
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public final class DrawingUtils {
|
||||
|
||||
/**
|
||||
@@ -130,6 +124,31 @@ public final class DrawingUtils {
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes the two given opaque colours in the proportion specified.
|
||||
* @param colour1 The first colour to mix, as a 6-digit hexadecimal.
|
||||
* @param colour2 The second colour to mix, as a 6-digit hexadecimal.
|
||||
* @param proportion The proportion of the second colour; will be clamped to between 0 and 1.
|
||||
* @return The resulting colour, as a 6-digit hexadecimal.
|
||||
*/
|
||||
public static int mix(int colour1, int colour2, float proportion){
|
||||
|
||||
proportion = MathHelper.clamp(proportion, 0, 1);
|
||||
|
||||
int r1 = colour1 >> 16 & 255;
|
||||
int g1 = colour1 >> 8 & 255;
|
||||
int b1 = colour1 & 255;
|
||||
int r2 = colour2 >> 16 & 255;
|
||||
int g2 = colour2 >> 8 & 255;
|
||||
int b2 = colour2 & 255;
|
||||
|
||||
int r = (int)(r1 + (r2-r1) * proportion);
|
||||
int g = (int)(g1 + (g2-g1) * proportion);
|
||||
int b = (int)(b1 + (b2-b1) * proportion);
|
||||
|
||||
return (r << 16) + (g << 8) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given opaque colour translucent with the given opacity.
|
||||
* @param colour An integer colour code, should be a 6-digit hexadecimal (i.e. opaque).
|
||||
|
||||
@@ -5,15 +5,13 @@ import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Font renderer that renders parts of strings surrounded by '#' (without quotes) in the SGA instead of normal text.
|
||||
*
|
||||
* @since Wizardry 1.1
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class MixedFontRenderer extends FontRenderer {
|
||||
|
||||
public MixedFontRenderer(GameSettings p_i1035_1_, ResourceLocation p_i1035_2_, TextureManager p_i1035_3_,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import net.minecraft.client.audio.MovingSound;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that.
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class MovingSoundEntity extends MovingSound {
|
||||
private final Entity source;
|
||||
private float distance = 0.0F;
|
||||
|
||||
public MovingSoundEntity(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){
|
||||
// Uses BLOCKS because that's the closest thing to inanimate entities. Could use NEUTRAL like
|
||||
// MovingSoundMinecart.
|
||||
super(sound, SoundCategory.BLOCKS);
|
||||
this.source = entity;
|
||||
this.repeat = repeat;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.repeatDelay = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the JList with a new model.
|
||||
*/
|
||||
@Override
|
||||
public void update(){
|
||||
if(this.source.isDead && repeat){
|
||||
this.donePlaying = true;
|
||||
}else{
|
||||
this.xPosF = (float)this.source.posX;
|
||||
this.yPosF = (float)this.source.posY;
|
||||
this.zPosF = (float)this.source.posZ;
|
||||
float f = MathHelper.sqrt(this.source.motionX * this.source.motionX
|
||||
+ this.source.motionY * this.source.motionY + this.source.motionZ * this.source.motionZ);
|
||||
|
||||
// Is this something to do with the Doppler effect?
|
||||
if((double)f >= 0.01D){
|
||||
this.distance = MathHelper.clamp(this.distance + 0.0025F, 0.0F, 1.0F);
|
||||
this.volume = 0.0F + MathHelper.clamp(f, 0.0F, 0.5F) * 0.7F;
|
||||
}else{
|
||||
// this.pitch = 0.0F;
|
||||
// this.volume = 0.0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,52 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockMagicLight;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.item.ItemSpectralBow;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.spell.Flight;
|
||||
import electroblob.wizardry.spell.ShadowWard;
|
||||
import electroblob.wizardry.spell.Shield;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMerchant;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.item.EntityArmorStand;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.village.MerchantRecipe;
|
||||
import net.minecraftforge.client.event.FOVUpdateEvent;
|
||||
import net.minecraftforge.client.event.GuiContainerEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.client.event.RenderLivingEvent;
|
||||
import net.minecraftforge.client.event.RenderPlayerEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.*;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Event handler responsible for client-side only events, mostly rendering.
|
||||
@@ -53,13 +54,10 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryClientEventHandler {
|
||||
|
||||
private static final ResourceLocation shieldTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shield.png");
|
||||
private static final ResourceLocation wingTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/wing.png");
|
||||
private static final ResourceLocation shadowWardTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shadow_ward.png");
|
||||
private static final ResourceLocation sixthSenseTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/sixth_sense.png");
|
||||
private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png");
|
||||
private static final ResourceLocation frostOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png");
|
||||
@@ -73,6 +71,12 @@ public final class WizardryClientEventHandler {
|
||||
private static int blinkEffectTimer;
|
||||
/** The number of ticks the blink effect lasts for. */
|
||||
private static final int BLINK_EFFECT_DURATION = 8;
|
||||
|
||||
private static final Method unpressKey;
|
||||
|
||||
static {
|
||||
unpressKey = ObfuscationReflectionHelper.findMethod(KeyBinding.class, "func_74505_d", void.class);
|
||||
}
|
||||
|
||||
/** Starts the first person blink overlay effect. */
|
||||
public static void playBlinkEffect(){
|
||||
@@ -80,21 +84,97 @@ public final class WizardryClientEventHandler {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
if(event.getEntity() == Minecraft.getMinecraft().player){
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
|
||||
|
||||
if(event.player == Minecraft.getMinecraft().player){
|
||||
|
||||
if(blinkEffectTimer > 0) blinkEffectTimer--;
|
||||
|
||||
// Only seems to work here...
|
||||
// EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
|
||||
// if(victim != null && victim.getHeldItemMainhand().isEmpty()){
|
||||
// Minecraft.getMinecraft().player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
|
||||
// }
|
||||
|
||||
// Reset shaders if their respective potions aren't active
|
||||
// This is a player so the potion effects are synced by vanilla
|
||||
if(Minecraft.getMinecraft().entityRenderer.getShaderGroup() != null){
|
||||
|
||||
String activeShader = Minecraft.getMinecraft().entityRenderer.getShaderGroup().getShaderGroupName();
|
||||
|
||||
if((activeShader.equals(SlowTime.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.slow_time))
|
||||
|| (activeShader.equals(SixthSense.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense))
|
||||
|| (activeShader.equals(Transience.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.transience))){
|
||||
|
||||
if(activeShader.equals(SixthSense.SHADER.toString())
|
||||
|| activeShader.equals(Transience.SHADER.toString())) playBlinkEffect();
|
||||
|
||||
Minecraft.getMinecraft().entityRenderer.stopUseShader();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
public static void onRenderHandEvent(RenderHandEvent event){
|
||||
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL);
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE);
|
||||
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
|
||||
|
||||
// for(int i=0; i<7; i++){
|
||||
// event.getMap().registerSprite(new ResourceLocation(Wizardry.MODID, "particle/ice_" + i));
|
||||
// }
|
||||
if(victim != null){
|
||||
|
||||
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
|
||||
|
||||
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This event is called every tick, not just when a movement key is pressed
|
||||
@SubscribeEvent
|
||||
public static void onInputUpdateEvent(InputUpdateEvent event){
|
||||
// Prevents the player moving when paralysed
|
||||
if(event.getEntityPlayer().isPotionActive(WizardryPotions.paralysis)){
|
||||
event.getMovementInput().moveForward = 0;
|
||||
event.getMovementInput().moveStrafe = 0;
|
||||
event.getMovementInput().jump = false;
|
||||
event.getMovementInput().sneak = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
|
||||
|
||||
if(event.phase == TickEvent.Phase.END && !net.minecraft.client.Minecraft.getMinecraft().isGamePaused()){
|
||||
|
||||
World world = net.minecraft.client.Minecraft.getMinecraft().world;
|
||||
|
||||
if(world == null) return;
|
||||
|
||||
for(TileEntity tileentity : world.loadedTileEntityList){
|
||||
if(tileentity instanceof TileEntityDispenser){
|
||||
if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){
|
||||
DispenserCastingData.get((TileEntityDispenser)tileentity).update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpellEmitterData.update(world);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onMouseEvent(MouseEvent event){
|
||||
|
||||
// Prevents the player looking around when paralysed
|
||||
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.paralysis)
|
||||
&& Minecraft.getMinecraft().inGameHasFocus){
|
||||
event.setCanceled(true);
|
||||
Minecraft.getMinecraft().player.prevRotationYaw = 0;
|
||||
Minecraft.getMinecraft().player.prevRotationPitch = 0;
|
||||
Minecraft.getMinecraft().player.rotationYaw = 0;
|
||||
Minecraft.getMinecraft().player.rotationPitch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -121,11 +201,26 @@ public final class WizardryClientEventHandler {
|
||||
event.setNewfov(event.getFov() + f * f * 0.7f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onDrawBlockHighlightEvent(DrawBlockHighlightEvent event){
|
||||
// Hide the block outline for magic light blocks unless the player can dispel them
|
||||
if(event.getTarget().typeOfHit == RayTraceResult.Type.BLOCK
|
||||
&& event.getPlayer().world.getBlockState(event.getTarget().getBlockPos()).getBlock() instanceof BlockMagicLight){
|
||||
|
||||
if((!(event.getPlayer().getHeldItemMainhand().getItem() instanceof ISpellCastingItem)
|
||||
&& !(event.getPlayer().getHeldItemOffhand().getItem() instanceof ISpellCastingItem))
|
||||
|| !ItemArtefact.isArtefactActive(event.getPlayer(), WizardryItems.charm_light)){
|
||||
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Brute-force fix for crystals not showing up when a wizard is given a spell book in the trade GUI.
|
||||
@SubscribeEvent
|
||||
public static void onGuiDrawForegroundEvent(GuiContainerEvent.DrawForeground event){
|
||||
|
||||
|
||||
if(event.getGuiContainer() instanceof GuiMerchant){
|
||||
|
||||
GuiMerchant gui = (GuiMerchant)event.getGuiContainer();
|
||||
@@ -138,81 +233,35 @@ public final class WizardryClientEventHandler {
|
||||
for(MerchantRecipe trade : gui.getMerchant().getRecipes(Minecraft.getMinecraft().player)){
|
||||
if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){
|
||||
Slot slot = gui.inventorySlots.getSlot(2);
|
||||
// Uses reflection to draw the itemstack
|
||||
// It still doesn't look quite right because the slot highlight is behind the item, but it'll do
|
||||
// until/unless I find a better solution.
|
||||
renderItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
|
||||
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
|
||||
gui.getSlotUnderMouse() == slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderItemAndTooltip(GuiContainer gui, ItemStack stack, int x, int y, int mouseX, int mouseY, boolean tooltip){
|
||||
|
||||
RenderItem renderItem = Minecraft.getMinecraft().getRenderItem();
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
GlStateManager.enableLighting();
|
||||
renderItem.zLevel = 100.0F;
|
||||
|
||||
if(!stack.isEmpty()){
|
||||
renderItem.renderItemAndEffectIntoGUI(stack, x, y);
|
||||
renderItem.renderItemOverlays(Minecraft.getMinecraft().fontRenderer, stack, x, y);
|
||||
|
||||
if(tooltip){
|
||||
gui.drawHoveringText(gui.getItemToolTip(stack), mouseX + gui.getXSize()/2 - gui.width/2,
|
||||
mouseY + gui.getYSize()/2 - gui.height/2);
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.enableDepth();
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
}
|
||||
|
||||
// Third person
|
||||
@SubscribeEvent
|
||||
public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){
|
||||
renderShieldIfActive(event.getEntityPlayer());
|
||||
renderWingsIfActive(event.getEntityPlayer(), event.getPartialRenderTick());
|
||||
renderShadowWardIfActive(event.getEntityPlayer());
|
||||
}
|
||||
|
||||
// First person
|
||||
@SubscribeEvent
|
||||
public static void onRenderWorldLastEvent(RenderWorldLastEvent event){
|
||||
// Now only fires in first person.
|
||||
if(Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){
|
||||
renderShieldFirstPerson(Minecraft.getMinecraft().player);
|
||||
renderShadowWardFirstPerson(Minecraft.getMinecraft().player);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
WizardData properties = WizardData.get(mc.player);
|
||||
WizardData data = WizardData.get(mc.player);
|
||||
RenderManager renderManager = event.getRenderer().getRenderManager();
|
||||
|
||||
ItemStack wand = mc.player.getHeldItemMainhand();
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem)){
|
||||
wand = mc.player.getHeldItemOffhand();
|
||||
}
|
||||
|
||||
// Target selection pointer
|
||||
if(mc.player.isSneaking() && wand.getItem() instanceof ItemWand && WizardryUtilities.isLiving(event.getEntity())
|
||||
&& properties != null && properties.selectedMinion != null){
|
||||
if(mc.player.isSneaking() && wand.getItem() instanceof ISpellCastingItem && WizardryUtilities.isLiving(event.getEntity())
|
||||
&& data != null && data.selectedMinion != null){
|
||||
|
||||
// -> Moved this in here so it isn't called every tick
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(mc.world, mc.player, 16, false);
|
||||
RayTraceResult rayTrace = RayTracer.standardEntityRayTrace(mc.world, mc.player, 16, false);
|
||||
|
||||
if(rayTrace != null && rayTrace.entityHit == event.getEntity()){
|
||||
|
||||
@@ -256,7 +305,7 @@ public final class WizardryClientEventHandler {
|
||||
}
|
||||
|
||||
// Summoned creature selection pointer
|
||||
if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.getEntity()){
|
||||
if(data != null && data.selectedMinion != null && data.selectedMinion.get() == event.getEntity()){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
@@ -297,8 +346,9 @@ public final class WizardryClientEventHandler {
|
||||
}
|
||||
|
||||
// Sixth sense
|
||||
if(mc.player.isPotionActive(WizardryPotions.sixth_sense) && !(event.getEntity() instanceof EntityArmorStand) && event.getEntity() != mc.player
|
||||
&& mc.player.getActivePotionEffect(WizardryPotions.sixth_sense) != null && event.getEntity().getDistance(mc.player) < 20
|
||||
if(mc.player.isPotionActive(WizardryPotions.sixth_sense) && !(event.getEntity() instanceof EntityArmorStand)
|
||||
&& event.getEntity() != mc.player && mc.player.getActivePotionEffect(WizardryPotions.sixth_sense) != null
|
||||
&& event.getEntity().getDistance(mc.player) < Spells.sixth_sense.getProperty(Spell.EFFECT_RADIUS).floatValue()
|
||||
* (1 + mc.player.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier() * Constants.RANGE_INCREASE_PER_LEVEL)){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
@@ -413,322 +463,4 @@ public final class WizardryClientEventHandler {
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// FIXME: Something in here is making the first person shadow ward rather translucent.
|
||||
private static void renderShadowWardFirstPerson(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.disableAlpha();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.2, 0);
|
||||
GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0);
|
||||
GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(0, 0, 1.2);
|
||||
GlStateManager.rotate(entityplayer.world.getWorldTime() * -2, 0, 0, 1);
|
||||
GlStateManager.scale(1.1, 1.1, 1.1);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShadowWardIfActive(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.rotate(180, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.translate(0, 1.2, 0);
|
||||
GlStateManager.rotate(entityplayer.world.getWorldTime() * -2, 0, 0, 1);
|
||||
GlStateManager.scale(1.1, 1.1, 1.1);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderWingsIfActive(EntityPlayer entityplayer, float partialTickTime){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).currentlyCasting() instanceof Flight
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Flight)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
// GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
// GlStateManager.rotate(180, 1, 0, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(wingTexture);
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(0.1, 0.4, -0.15);
|
||||
GlStateManager.rotate(20 + 20 * (float)Math.sin(entityplayer.world.getWorldTime() * 0.3), 0, 1, 0);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(-0.1, 0.4, -0.15);
|
||||
GlStateManager.rotate(-200 - 20 * (float)Math.sin(entityplayer.world.getWorldTime() * 0.3), 0, 1, 0);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShieldFirstPerson(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).shield != null
|
||||
&& (WizardData.get(entityplayer).currentlyCasting() instanceof Shield
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Shield))){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.4, 0);
|
||||
|
||||
GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0);
|
||||
GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
GlStateManager.translate(0, 0, 0.8);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
|
||||
|
||||
renderShield(tessellator);
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableBlend();
|
||||
// RenderHelper.enableStandardItemLighting();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShieldIfActive(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Shield))){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.enableBlend();
|
||||
// For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the inner
|
||||
// edges to appear black, so I have changed it to this, which looks very slightly different.
|
||||
GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.3, 0);
|
||||
|
||||
// GlStateManager.rotate(180, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
// GlStateManager.rotate(-entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
GlStateManager.translate(0, 0, 0.8);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
|
||||
|
||||
renderShield(tessellator);
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableBlend();
|
||||
// RenderHelper.enableStandardItemLighting();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShield(Tessellator tessellator){
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
double widthOuter = 0.6d;
|
||||
double heightOuter = 0.7d;
|
||||
double widthInner = 0.3d;
|
||||
double heightInner = 0.4d;
|
||||
double depth = 0.2d;
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightOuter, -depth).tex(0.2, 0).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(widthInner, heightOuter, -depth).tex(0.8, 0).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthOuter, heightInner, -depth).tex(1, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(widthOuter, -heightInner, -depth).tex(1, 0.8).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightOuter, -depth).tex(0.8, 1).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(-widthInner, -heightOuter, -depth).tex(0.2, 1).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthOuter, -heightInner, -depth).tex(0, 0.8).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -16,72 +13,54 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/** Event handler class responsible for handling wizardry's controls. */
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class WizardryControlHandler {
|
||||
|
||||
static boolean NkeyPressed = false;
|
||||
static boolean BkeyPressed = false;
|
||||
static boolean NkeyAlreadyPressed = false;
|
||||
static boolean BkeyAlreadyPressed = false;
|
||||
|
||||
// Changed to a tick event to allow mouse button keybinds
|
||||
// The 'lag' that happened previously was actually because the code only fired when a keyboard key was pressed!
|
||||
@SubscribeEvent
|
||||
public static void onKeyInput(InputEvent.KeyInputEvent event){
|
||||
public static void onTickEvent(TickEvent.ClientTickEvent event){
|
||||
|
||||
// Key pressed
|
||||
if(Keyboard.getEventKeyState()){
|
||||
if(event.phase == TickEvent.Phase.END) return; // Only really needs to be once per tick
|
||||
|
||||
if(Wizardry.proxy instanceof ClientProxy){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
if(Wizardry.proxy instanceof ClientProxy){
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
wand = player.getHeldItemOffhand();
|
||||
// If the player isn't holding a wand, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
}
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(player != null){
|
||||
|
||||
ItemStack wand = getWandInUse(player);
|
||||
if(wand == null) return;
|
||||
|
||||
if(ClientProxy.NEXT_SPELL.isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!NkeyPressed){
|
||||
NkeyPressed = true;
|
||||
}else{
|
||||
NkeyAlreadyPressed = true;
|
||||
}
|
||||
if(!NkeyAlreadyPressed){
|
||||
selectNextSpell(wand);
|
||||
}
|
||||
}else{
|
||||
NkeyPressed = false;
|
||||
}
|
||||
|
||||
if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(ClientProxy.PREVIOUS_SPELL.isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!BkeyPressed){
|
||||
BkeyPressed = true;
|
||||
}else{
|
||||
BkeyAlreadyPressed = true;
|
||||
}
|
||||
if(!BkeyAlreadyPressed){
|
||||
// Packet building
|
||||
selectPreviousSpell(wand);
|
||||
}
|
||||
}else{
|
||||
BkeyPressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Key released
|
||||
else{
|
||||
if(NkeyPressed){
|
||||
NkeyPressed = false;
|
||||
NkeyAlreadyPressed = false;
|
||||
}else if(BkeyPressed){
|
||||
BkeyPressed = false;
|
||||
BkeyAlreadyPressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shift-scrolling to change spells
|
||||
@@ -89,16 +68,11 @@ public class WizardryControlHandler {
|
||||
public static void onMouseEvent(MouseEvent event){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
wand = player.getHeldItemOffhand();
|
||||
// If the player isn't holding a wand, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
}
|
||||
ItemStack wand = getWandInUse(player);
|
||||
if(wand == null) return;
|
||||
|
||||
if(Minecraft.getMinecraft().inGameHasFocus && !wand.isEmpty() && event.getDwheel() != 0 && player.isSneaking()
|
||||
&& Wizardry.settings.enableShiftScrolling){
|
||||
&& Wizardry.settings.shiftScrolling){
|
||||
|
||||
event.setCanceled(true);
|
||||
|
||||
@@ -111,15 +85,28 @@ public class WizardryControlHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemStack getWandInUse(EntityPlayer player){
|
||||
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
|
||||
// Only bother sending packets if the player is holding a spellcasting item with more than one spell slot
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem) || ((ISpellCastingItem)wand.getItem()).getSpells(wand).length < 2){
|
||||
wand = player.getHeldItemOffhand();
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem) || ((ISpellCastingItem)wand.getItem()).getSpells(wand).length < 2) return null;
|
||||
}
|
||||
|
||||
return wand;
|
||||
}
|
||||
|
||||
private static void selectNextSpell(ItemStack wand){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// GUI switch animation
|
||||
WandHelper.selectNextSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
((ISpellCastingItem)wand.getItem()).selectNextSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
GuiSpellDisplay.playSpellSwitchAnimation(true);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.SELECT_SPELL, 1));
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
|
||||
}
|
||||
|
||||
private static void selectPreviousSpell(ItemStack wand){
|
||||
@@ -127,8 +114,8 @@ public class WizardryControlHandler {
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// GUI switch animation
|
||||
WandHelper.selectPreviousSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
((ISpellCastingItem)wand.getItem()).selectPreviousSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
GuiSpellDisplay.playSpellSwitchAnimation(false);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.SELECT_SPELL, 1));
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import net.minecraft.client.audio.MovingSound;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
|
||||
// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that.
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class MovingSoundEntity<T extends Entity> extends MovingSound {
|
||||
|
||||
protected final T source;
|
||||
protected float distance = 0.0F;
|
||||
|
||||
public MovingSoundEntity(T entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
|
||||
super(sound, category);
|
||||
this.source = entity;
|
||||
this.repeat = repeat;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.repeatDelay = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
if(this.source.isDead){
|
||||
this.donePlaying = true;
|
||||
}else{
|
||||
this.xPosF = (float)this.source.posX;
|
||||
this.yPosF = (float)this.source.posY;
|
||||
this.zPosF = (float)this.source.posZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.ISound;
|
||||
import net.minecraft.util.ITickable;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Instances of this class represent a set of sounds which together form a looped sound: start, loop and end.
|
||||
* Currently this is only used for continuous spell sounds in wizardry itself, but feel free to make your own
|
||||
* implementations - this class will take care of the internals.
|
||||
*
|
||||
* @since Wizardry 4.2
|
||||
* @author Electroblob
|
||||
*/
|
||||
// See MusicTicker, this is the same idea of just storing the sounds statically client-side and ticking them
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public abstract class SoundLoop implements ITickable {
|
||||
|
||||
private static final Set<SoundLoop> activeLoops = new HashSet<>();
|
||||
|
||||
private final ISound start;
|
||||
private final ISound loop;
|
||||
private final ISound end;
|
||||
|
||||
private boolean looping = false;
|
||||
private boolean needsRemoving = false;
|
||||
|
||||
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, ISoundFactory factory){
|
||||
// The reason I've gone to the effort of having a factory for these is that we need SoundLoop to have control
|
||||
// over which sounds are repeated and which aren't whilst keeping them private.
|
||||
this.start = factory.create(start, category, false);
|
||||
this.loop = factory.create(loop, category, true);
|
||||
this.end = factory.create(end, category, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
// Check every tick if the start sound is done playing and if so, start the loop sound
|
||||
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(start)){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(loop);
|
||||
looping = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops the loop part of the sound immediately and starts playing the end part. This may be called from subclasses
|
||||
* or externally depending on the implementation. */
|
||||
// For continuous spell sounds it's internally
|
||||
public void endLoop(){
|
||||
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
|
||||
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(end);
|
||||
// Can't modify activeLoops directly since we'll probably be calling this method from update(), which is
|
||||
// during iteration of activeLoops so it could cause a ConcurrentModificationException
|
||||
this.markForRemoval();
|
||||
}
|
||||
|
||||
/** Marks this sound loop to be removed next tick. */
|
||||
protected void markForRemoval(){
|
||||
this.needsRemoving = true;
|
||||
}
|
||||
|
||||
// Static methods
|
||||
|
||||
public static void addLoop(SoundLoop loop){
|
||||
activeLoops.add(loop);
|
||||
// Do this here rather than in the constructor in case someone wants to play the loop later or reuse it
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(loop.start);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void tick(TickEvent.ClientTickEvent event){
|
||||
// Using the END phase means we can check for stopped sounds as soon as they are stopped (effectively),
|
||||
// meaning we don't get the 'cut' between the start and loop sounds
|
||||
// FIXME: Apparently this only works for dispensers. What the heck is the difference?!
|
||||
if(event.phase == TickEvent.Phase.END){
|
||||
activeLoops.forEach(SoundLoop::update);
|
||||
activeLoops.removeIf(s -> s.needsRemoving);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ISoundFactory {
|
||||
ISound create(SoundEvent sound, SoundCategory category, boolean repeat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.audio.PositionedSound;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/** Abstract base class for sound loops associated with spells; see subclasses below for implementations. */
|
||||
public abstract class SoundLoopSpell extends SoundLoop {
|
||||
|
||||
private final Spell spell;
|
||||
|
||||
public SoundLoopSpell(SoundEvent start, SoundEvent loop, SoundEvent end, ISoundFactory factory, Spell spell){
|
||||
super(start, loop, end, WizardrySounds.SPELLS, factory);
|
||||
this.spell = spell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
// This may be a bit overkill but I might as well put functionality in superclasses where possible
|
||||
if(stillCasting(spell)){
|
||||
// This can't called in the same tick as endLoop because otherwise, if the spell casting stops during the
|
||||
// same tick as the transition to the loop sound it will try and stop the loop immediately after it has
|
||||
// started - and for whatever reason, this causes the 'Channel null in method stop' error.
|
||||
super.update();
|
||||
}else{
|
||||
endLoop();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean stillCasting(Spell spell);
|
||||
|
||||
/** Implements a sound loop for continuous spells cast by entities. */
|
||||
public static class SoundLoopSpellEntity extends SoundLoopSpell {
|
||||
|
||||
private final EntityLivingBase source;
|
||||
|
||||
public SoundLoopSpellEntity(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, EntityLivingBase source, float volume, float pitch){
|
||||
super(start, loop, end, (sound, category, repeat) -> new MovingSoundEntity<>(source, sound, category, volume, pitch, repeat), spell);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return WizardryUtilities.isCasting(source, spell);
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class SoundLoopSpellPosition extends SoundLoopSpell {
|
||||
|
||||
public SoundLoopSpellPosition (SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
// Huh, I actually found a use for a non-static initialiser block - hence the double curly brackets...
|
||||
super(start, loop, end, (sound, category, r) -> new PositionedSound(sound, category){{
|
||||
// ...et voila, we can just set protected fields as we please using external variables
|
||||
this.xPosF = (float)x;
|
||||
this.yPosF = (float)y;
|
||||
this.zPosF = (float)z;
|
||||
this.repeat = r;
|
||||
this.volume = sndVolume;
|
||||
this.pitch = sndPitch;
|
||||
}}, spell);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements a sound loop for continuous spells cast by dispensers. */
|
||||
public static class SoundLoopSpellDispenser extends SoundLoopSpellPosition {
|
||||
|
||||
private final TileEntityDispenser source;
|
||||
|
||||
public SoundLoopSpellDispenser(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, World world,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
super(start, loop, end, spell, x, y, z, sndVolume, sndPitch);
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(new BlockPos(x, y, z));
|
||||
|
||||
if(tileentity instanceof TileEntityDispenser) this.source = (TileEntityDispenser)tileentity;
|
||||
else throw new NullPointerException(String.format("Playing continuous spell sound: no dispenser found at %s, %s, %s", x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return DispenserCastingData.get(source).currentlyCasting() == spell;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Implements a sound loop for continuous spells cast at a position (via commands). */
|
||||
public static class SoundLoopSpellPosTimed extends SoundLoopSpellPosition {
|
||||
|
||||
private int timeLeft;
|
||||
|
||||
public SoundLoopSpellPosTimed(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, int duration,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
super(start, loop, end, spell, x, y, z, sndVolume, sndPitch);
|
||||
this.timeLeft = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
timeLeft--;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return timeLeft > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,27 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.IManaStoringItem;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.IWorkbenchItem;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
@@ -25,131 +29,260 @@ import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiArcaneWorkbench extends GuiContainer {
|
||||
|
||||
private GuiButton applyBtn;
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
|
||||
public static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/gui/arcane_workbench.png");
|
||||
|
||||
private IInventory playerInventory;
|
||||
private IInventory arcaneWorkbenchInventory;
|
||||
|
||||
private final int tooltipWidth = 164;
|
||||
private static final int TOOLTIP_WIDTH = 164;
|
||||
|
||||
// We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it.
|
||||
// For calculations, we use the size without the tooltip.
|
||||
private final int xSizeNoTip = 176;
|
||||
/** We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it.
|
||||
* For calculations, we use the size without the tooltip, which is stored in this constant. */
|
||||
private static final int MAIN_GUI_WIDTH = 176;
|
||||
|
||||
private static final int RUNE_LEFT = 38;
|
||||
private static final int RUNE_TOP = 22;
|
||||
private static final int RUNE_WIDTH = 100;
|
||||
private static final int RUNE_HEIGHT = 100;
|
||||
|
||||
private static final int HALO_DIAMETER = 156;
|
||||
|
||||
private static final int TEXTURE_WIDTH = 512;
|
||||
private static final int TEXTURE_HEIGHT = 256;
|
||||
|
||||
private int animationTimer = 0;
|
||||
private static final int ANIMATION_DURATION = 20;
|
||||
|
||||
public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity){
|
||||
super(new ContainerArcaneWorkbench(invPlayer, entity));
|
||||
this.playerInventory = invPlayer;
|
||||
this.arcaneWorkbenchInventory = entity;
|
||||
xSize = xSizeNoTip;
|
||||
xSize = MAIN_GUI_WIDTH;
|
||||
ySize = 220;
|
||||
}
|
||||
|
||||
// Huh, didn't realise this method existed. Pretty neat.
|
||||
@Override
|
||||
public void drawScreen(int p_73863_1_, int p_73863_2_, float p_73863_3_){
|
||||
|
||||
this.drawDefaultBackground();
|
||||
|
||||
// Tests if there is a wand in the workbench and edits the positioning accordingly
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
xSize = xSizeNoTip + tooltipWidth;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = (this.width - tooltipWidth) / 2 + 48;
|
||||
}else{
|
||||
xSize = xSizeNoTip;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = this.width / 2 + 48;
|
||||
}
|
||||
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){
|
||||
this.applyBtn.enabled = true;
|
||||
}else{
|
||||
this.applyBtn.enabled = false;
|
||||
}
|
||||
|
||||
super.drawScreen(p_73863_1_, p_73863_2_, p_73863_3_);
|
||||
|
||||
// Required now, or item mouseover tooltips won't render.
|
||||
this.renderHoveredToolTip(p_73863_1_, p_73863_2_);
|
||||
public void updateScreen(){
|
||||
if(animationTimer > 0) animationTimer--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY){
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
GlStateManager.color(1F, 1F, 1F, 1F);
|
||||
this.drawDefaultBackground();
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
Slot slot = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT);
|
||||
|
||||
// Tests if there is a wand in the workbench and edits the positioning accordingly
|
||||
if(slot.getHasStack() && slot.getStack().getItem() instanceof IWorkbenchItem
|
||||
&& ((IWorkbenchItem)slot.getStack().getItem()).showTooltip(slot.getStack())){
|
||||
xSize = MAIN_GUI_WIDTH + TOOLTIP_WIDTH;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = (this.width - TOOLTIP_WIDTH) / 2 + 64;
|
||||
}else{
|
||||
xSize = MAIN_GUI_WIDTH;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = this.width / 2 + 64;
|
||||
}
|
||||
|
||||
this.applyBtn.enabled = slot.getHasStack();
|
||||
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
// Required now, or item mouseover tooltips won't render.
|
||||
this.renderHoveredToolTip(mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Animation
|
||||
|
||||
// Grey background
|
||||
DrawingUtils.drawTexturedRect(guiLeft + RUNE_LEFT, guiTop + RUNE_TOP, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, 0,
|
||||
RUNE_WIDTH, RUNE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// Yellow 'halo'
|
||||
if(animationTimer > 0){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
int x = guiLeft + RUNE_LEFT + RUNE_WIDTH/2;
|
||||
int y = guiTop + RUNE_TOP + RUNE_HEIGHT/2;
|
||||
|
||||
float scale = (animationTimer + partialTicks)/ANIMATION_DURATION;
|
||||
scale = (float)(1 - Math.pow(1-scale, 1.4f)); // Makes it slower at the start and speed up
|
||||
GlStateManager.scale(scale, scale, 1);
|
||||
GlStateManager.translate(x/scale, y/scale, 0);
|
||||
|
||||
DrawingUtils.drawTexturedRect(-HALO_DIAMETER /2, -HALO_DIAMETER /2, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, RUNE_HEIGHT,
|
||||
HALO_DIAMETER, HALO_DIAMETER, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Main inventory
|
||||
DrawingUtils.drawTexturedRect(guiLeft, guiTop, 0, 0, MAIN_GUI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
float opacity = (animationTimer + partialTicks)/ANIMATION_DURATION;
|
||||
|
||||
// Changing slots
|
||||
for(int i = 0; i < ContainerArcaneWorkbench.CRYSTAL_SLOT; i++){
|
||||
|
||||
Slot slot = this.inventorySlots.getSlot(i);
|
||||
if(slot.xPos >= 0 && slot.yPos >= 0)
|
||||
this.drawTexturedModalRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 0, 220, 36, 36);
|
||||
|
||||
if(slot.xPos >= 0 && slot.yPos >= 0){
|
||||
// Slot background
|
||||
DrawingUtils.drawTexturedRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 0, 220, 36, 36, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// Slot animation
|
||||
// IDEA: Somehow replace with intelligent check for whether the spell actually got applied
|
||||
if(animationTimer > 0 && slot.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 36, 220, 36, 36, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crystal + upgrade slot animations
|
||||
if(animationTimer > 0){
|
||||
|
||||
Slot crystals = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CRYSTAL_SLOT);
|
||||
Slot upgrades = this.inventorySlots.getSlot(ContainerArcaneWorkbench.UPGRADE_SLOT);
|
||||
|
||||
if(crystals.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + crystals.xPos - 8, guiTop + crystals.yPos - 8,
|
||||
MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
if(upgrades.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + upgrades.xPos - 8, guiTop + upgrades.yPos - 8,
|
||||
MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip only drawn if there is a wand
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){
|
||||
|
||||
// Tooltip box
|
||||
drawTexturedModalRect(guiLeft + xSizeNoTip, guiTop, xSizeNoTip, 0, 256 - xSizeNoTip - 4, ySize);
|
||||
drawTexturedModalRect(guiLeft + 252, guiTop, xSizeNoTip + 4, 0, tooltipWidth - 2 * (256 - xSizeNoTip - 4), ySize);
|
||||
drawTexturedModalRect(guiLeft + xSize - (256 - xSizeNoTip - 4), guiTop, xSizeNoTip + 4, 0,
|
||||
256 - xSizeNoTip - 4, ySize);
|
||||
ItemStack stack = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
|
||||
ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.capabilities.isCreativeMode && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
// As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on
|
||||
// mods to add their own.
|
||||
Minecraft.getMinecraft().renderEngine
|
||||
.bindTexture(discovered ? spell.element.getIcon() : Element.MAGIC.getIcon());
|
||||
|
||||
// Renders the little element icon
|
||||
DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + 5, guiTop + 34 + 10 * i++, 8, 8);
|
||||
if(!(stack.getItem() instanceof IWorkbenchItem)){
|
||||
Wizardry.logger.warn("Invalid item in central slot of arcane workbench, how did that get there?!");
|
||||
return;
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = guiTop + 50 + spells.length * 10;
|
||||
if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){
|
||||
|
||||
// Look how much shorter this is with the WandHelper class!
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
// Tooltip box
|
||||
DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH, guiTop, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(wand, item);
|
||||
int y = guiTop + 20;
|
||||
|
||||
if(level > 0){
|
||||
ItemStack stack = new ItemStack(item, level);
|
||||
GlStateManager.enableDepth();
|
||||
this.itemRender.renderItemAndEffectIntoGUI(stack, guiLeft + xSizeNoTip + 6 + x, y);
|
||||
this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack, guiLeft + xSizeNoTip + 6 + x, y,
|
||||
null);
|
||||
x += 18;
|
||||
GlStateManager.disableDepth();
|
||||
if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){
|
||||
y += 14;
|
||||
}
|
||||
|
||||
if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){
|
||||
|
||||
Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack);
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
// As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on
|
||||
// mods to add their own.
|
||||
Minecraft.getMinecraft().renderEngine
|
||||
.bindTexture(discovered ? spell.getElement().getIcon() : Element.MAGIC.getIcon());
|
||||
|
||||
// Renders the little element icon
|
||||
DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + 5, y, 8, 8);
|
||||
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
int x = 0;
|
||||
y += 16;
|
||||
|
||||
// Look how much shorter this is with the WandHelper class!
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(stack, item);
|
||||
|
||||
if(level > 0){
|
||||
ItemStack stack1 = new ItemStack(item, level);
|
||||
GlStateManager.enableDepth();
|
||||
this.itemRender.renderItemAndEffectIntoGUI(stack1, guiLeft + MAIN_GUI_WIDTH + 6 + x, y);
|
||||
this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, guiLeft + MAIN_GUI_WIDTH + 6 + x, y,
|
||||
null);
|
||||
x += 18;
|
||||
GlStateManager.disableDepth();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Fixes the bug that caused the slot hightlight to render opaque. I don't know why it works, it just works!
|
||||
// Fixes the bug that caused the slot highlight to render opaque. I don't know why it works, it just works!
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableAlpha();
|
||||
}
|
||||
@@ -157,64 +290,86 @@ public class GuiArcaneWorkbench extends GuiContainer {
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
this.fontRenderer
|
||||
.drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName()
|
||||
: I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752);
|
||||
this.fontRenderer.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName()
|
||||
: I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752);
|
||||
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){
|
||||
|
||||
ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
ItemStack stack = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSizeNoTip + 6, 6, 0);
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana") + " "
|
||||
+ (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(),
|
||||
xSizeNoTip + 6, 20, 0);
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
|
||||
int y = 34;
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.capabilities.isCreativeMode && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSizeNoTip + 16, y, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawStringWithShadow(
|
||||
"\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), xSizeNoTip + 16, y, 0);
|
||||
}
|
||||
y += 10;
|
||||
if(!(stack.getItem() instanceof IWorkbenchItem)){
|
||||
Wizardry.logger.warn("Invalid item in central slot of arcane workbench, how did that get there?!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) > 0){
|
||||
if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){
|
||||
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A7f" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.upgrades"), xSizeNoTip + 6, y + 6, 0);
|
||||
int y = 6;
|
||||
|
||||
int x = 0;
|
||||
y = 50 + spells.length * 10;
|
||||
// Wand upgrade tooltips
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + stack.getDisplayName(), MAIN_GUI_WIDTH + 6, y, 0);
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(wand, item);
|
||||
if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){
|
||||
y += 14;
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana")
|
||||
+ " " + ((IManaStoringItem)stack.getItem()).getMana(stack) + "/"
|
||||
+ ((IManaStoringItem)stack.getItem()).getManaCapacity(stack),
|
||||
MAIN_GUI_WIDTH + 6, y, 0);
|
||||
}
|
||||
|
||||
if(level > 0){
|
||||
// The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is
|
||||
// relative to the GUI but the POINT isn't.
|
||||
if(isPointInRegion(xSizeNoTip + 6 + x, y, 16, 16, mouseX, mouseY)){
|
||||
ItemStack stack = new ItemStack(item, level);
|
||||
this.renderToolTip(stack, mouseX - guiLeft, mouseY - guiTop);
|
||||
y += 14;
|
||||
|
||||
if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){
|
||||
|
||||
Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack);
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), MAIN_GUI_WIDTH + 16, y, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawStringWithShadow(
|
||||
"\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), MAIN_GUI_WIDTH + 16, y, 0);
|
||||
}
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
if(WandHelper.getTotalUpgrades(stack) > 0){
|
||||
|
||||
y += 6;
|
||||
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + I18n.format("container."
|
||||
+ Wizardry.MODID + ":arcane_workbench.upgrades"), MAIN_GUI_WIDTH + 6, y, 0);
|
||||
|
||||
int x = 0;
|
||||
y += 10;
|
||||
|
||||
// Wand upgrade tooltips
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(stack, item);
|
||||
|
||||
if(level > 0){
|
||||
// The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is
|
||||
// relative to the GUI but the POINT isn't.
|
||||
if(isPointInRegion(MAIN_GUI_WIDTH + 6 + x, y, 16, 16, mouseX, mouseY)){
|
||||
ItemStack stack1 = new ItemStack(item, level);
|
||||
this.renderToolTip(stack1, mouseX - guiLeft, mouseY - guiTop);
|
||||
}
|
||||
x += 18;
|
||||
}
|
||||
x += 18;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,7 +383,7 @@ public class GuiArcaneWorkbench extends GuiContainer {
|
||||
this.guiTop = (this.height - this.ySize) / 2;
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 48, this.height / 2 + 3));
|
||||
this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -244,8 +399,51 @@ public class GuiArcaneWorkbench extends GuiContainer {
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// Sound
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(
|
||||
WizardrySounds.BLOCK_ARCANE_WORKBENCH_SPELLBIND, 1));
|
||||
// Animation
|
||||
animationTimer = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GuiButtonApply extends GuiButton {
|
||||
|
||||
public GuiButtonApply(int id, int x, int y){
|
||||
super(id, x, y, 16, 16, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.apply"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
// Whether the button is highlighted
|
||||
this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
|
||||
|
||||
int k = 72;
|
||||
int l = 220;
|
||||
//int colour = 14737632;
|
||||
|
||||
if(this.enabled){
|
||||
if(this.hovered){
|
||||
k += this.width * 2;
|
||||
//colour = 16777120;
|
||||
}
|
||||
}else{
|
||||
k += this.width;
|
||||
//colour = 10526880;
|
||||
}
|
||||
|
||||
DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, 512, 256);
|
||||
//this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.x + this.width / 2,
|
||||
// this.y + (this.height - 8) / 2, colour);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL);
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,8 @@ package electroblob.wizardry.client.gui;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class GuiButtonInvisible extends GuiButton {
|
||||
|
||||
public GuiButtonInvisible(int id, int x, int y, int width, int height){
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Resurrection;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiGameOver;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiButtonResurrect extends GuiButton {
|
||||
|
||||
private static int timeSinceDeath = -1;
|
||||
|
||||
private final String translationKey;
|
||||
|
||||
public GuiButtonResurrect(int id, int x, int y, String translationKey){
|
||||
super(id, x, y, I18n.format(translationKey + "_wait", Resurrection.getRemainingWaitTime(timeSinceDeath)));
|
||||
this.translationKey = translationKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks){
|
||||
int waitTime = Resurrection.getRemainingWaitTime(timeSinceDeath);
|
||||
this.enabled = waitTime == 0;
|
||||
this.displayString = I18n.format(translationKey + (waitTime == 0 ? "_ready" : "_wait"), waitTime);
|
||||
super.drawButton(mc, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
|
||||
if(event.phase == TickEvent.Phase.START && timeSinceDeath >= 0) timeSinceDeath++;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onGuiScreenInitEvent(GuiScreenEvent.InitGuiEvent event){
|
||||
|
||||
if(event.getGui() instanceof GuiGameOver && ItemArtefact.isArtefactActive(Minecraft.getMinecraft().player, WizardryItems.amulet_resurrection)
|
||||
&& WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream().anyMatch(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player))){
|
||||
|
||||
event.getButtonList().add(new GuiButtonResurrect(event.getButtonList().size(), event.getGui().width / 2 - 100,
|
||||
event.getGui().height / 4 + 120, "spell." + Spells.resurrection.getRegistryName() + ".button"));
|
||||
timeSinceDeath = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onGuiScreenActionPerformedEvent(GuiScreenEvent.ActionPerformedEvent event){
|
||||
|
||||
if(event.getGui() instanceof GuiGameOver){
|
||||
|
||||
ItemStack stack = WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream()
|
||||
.filter(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player)).findFirst().orElse(null);
|
||||
|
||||
if(stack != null){
|
||||
|
||||
if(event.getButton() instanceof GuiButtonResurrect && timeSinceDeath >= 0){
|
||||
// Cast resurrection on the client player and notify the server to do the same
|
||||
// ISpellCastingItem#canCast already checked in Resurrection#canStackResurrect
|
||||
((ISpellCastingItem)stack.getItem()).cast(stack, Spells.resurrection, Minecraft.getMinecraft().player, EnumHand.MAIN_HAND, 0, new SpellModifiers());
|
||||
WizardryPacketHandler.net.sendToServer(new PacketControlInput.Message(PacketControlInput.ControlType.RESURRECT_BUTTON));
|
||||
|
||||
}else if(!Minecraft.getMinecraft().world.getGameRules().getBoolean("keepInventory")){
|
||||
// Any other button drops the wand (N.B. this should be inside the stack != null check or it'll send
|
||||
// packets unnecessarily and generate incorrect warnings
|
||||
WizardryPacketHandler.net.sendToServer(new PacketControlInput.Message(PacketControlInput.ControlType.CANCEL_RESURRECT));
|
||||
}
|
||||
|
||||
timeSinceDeath = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,34 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GuiSpellBook extends GuiScreen {
|
||||
|
||||
private int xSize, ySize;
|
||||
private Spell spell;
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/spellbook.png");
|
||||
private static final Map<Tier, ResourceLocation> textures = ImmutableMap.of(
|
||||
Tier.NOVICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_novice.png"),
|
||||
Tier.APPRENTICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_apprentice.png"),
|
||||
Tier.ADVANCED, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_advanced.png"),
|
||||
Tier.MASTER, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_master.png"));
|
||||
|
||||
public GuiSpellBook(Spell spell){
|
||||
super();
|
||||
@@ -39,45 +48,47 @@ public class GuiSpellBook extends GuiScreen {
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
boolean discovered = true;
|
||||
if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
if(Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
// Draws spell illustration on opposite page, underneath the book so it shows through the hole.
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
|
||||
DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(textures.get(spell.tier));
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(textures.get(spell.getTier()));
|
||||
DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
|
||||
|
||||
super.drawScreen(par1, par2, par3);
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawString(spell.getDisplayName(), xPos + 17, yPos + 15, 0);
|
||||
this.fontRenderer.drawString(spell.type.getDisplayName(), xPos + 17, yPos + 26, 0x777777);
|
||||
this.fontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26, 0x777777);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.world), xPos + 17,
|
||||
yPos + 15, 0);
|
||||
this.mc.standardGalacticFontRenderer.drawString(spell.type.getDisplayName(), xPos + 17, yPos + 26,
|
||||
this.mc.standardGalacticFontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26,
|
||||
0x777777);
|
||||
}
|
||||
|
||||
this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0);
|
||||
//this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0);
|
||||
|
||||
if(spell.tier == Tier.BASIC){
|
||||
if(spell.getTier() == Tier.NOVICE){
|
||||
// Basic is usually white but this doesn't show up.
|
||||
this.fontRenderer.drawString("Tier: \u00A77" + Tier.BASIC.getDisplayName(), xPos + 17, yPos + 45, 0);
|
||||
this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0);
|
||||
}else{
|
||||
this.fontRenderer.drawString("Tier: " + spell.tier.getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
|
||||
this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
|
||||
}
|
||||
|
||||
String element = "Element: " + spell.element.getFormattingCode() + spell.element.getDisplayName();
|
||||
String element = "Element: " + spell.getElement().getFormattingCode() + spell.getElement().getDisplayName();
|
||||
if(!discovered) element = "Element: ?";
|
||||
this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0);
|
||||
|
||||
String manaCost = "Mana Cost: " + spell.cost;
|
||||
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.cost + "/second";
|
||||
String manaCost = "Mana Cost: " + spell.getCost();
|
||||
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.getCost() + "/second";
|
||||
if(!discovered) manaCost = "Mana Cost: ?";
|
||||
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0);
|
||||
|
||||
@@ -93,10 +104,18 @@ public class GuiSpellBook extends GuiScreen {
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
|
||||
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
|
||||
}
|
||||
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesGuiPauseGame(){
|
||||
return Wizardry.settings.booksPauseGame;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +1,17 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import electroblob.wizardry.Settings;
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.client.MixedFontRenderer;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
@@ -37,14 +24,22 @@ import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiSpellDisplay {
|
||||
|
||||
@@ -53,8 +48,9 @@ public class GuiSpellDisplay {
|
||||
/** A map which stores all loaded HUD skin objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by {@code _index.json} (these stack between resource packs). The keys in the map correspond
|
||||
* to the keys in {@code _index.json}, and are sorted in that order, with skins belonging to resource packs sorted
|
||||
* from lowest to highest priority. The skins in the base mod will therefore always be first. */
|
||||
private static final Map<String, Skin> skins = new LinkedHashMap<>(12); // 12 is the number of skins packaged with the mod
|
||||
* from lowest to highest priority. The skins in the base mod will therefore always be first. (It should be noted,
|
||||
* however, that in the gui itself the skins are always sorted in alphabetical order for some reason.) */
|
||||
private static final Map<String, Skin> skins = new LinkedHashMap<>(14); // 14 is the number of skins packaged with the mod
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
@@ -66,6 +62,9 @@ public class GuiSpellDisplay {
|
||||
private static final float SPELL_NAME_SCALE = 0.5f;
|
||||
/** Opacity of the next/previous spell names, as a fraction. */
|
||||
private static final float SPELL_NAME_OPACITY = 0.3f;
|
||||
|
||||
private static final int HALF_HOTBAR_WIDTH = 97; // Half the width of the hotbar, plus a bit for clearance
|
||||
private static final int OFFHAND_SLOT_WIDTH = 29; // Width of the offhand slot plus the gap between it and the hotbar
|
||||
|
||||
/** Controls the spell switching animation. Positive when switching to the next spell, negative when switching to
|
||||
* the previous spell. Decremented in magnitude by 1 each tick until it reaches 0 again. */
|
||||
@@ -103,14 +102,18 @@ public class GuiSpellDisplay {
|
||||
|
||||
EntityPlayer player = mc.player;
|
||||
|
||||
if(player.isSpectator()) return; // Spectators shouldn't have the spell HUD!
|
||||
|
||||
// If the player has a wand in each hand, only displays for the one in the main hand.
|
||||
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
boolean mainHand = true;
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand))){
|
||||
wand = player.getHeldItemOffhand();
|
||||
// If the player isn't holding a wand, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
mainHand = false;
|
||||
// If the player isn't holding a spellcasting item that shows the HUD, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand))) return;
|
||||
}
|
||||
|
||||
int width = event.getResolution().getScaledWidth();
|
||||
@@ -118,6 +121,11 @@ public class GuiSpellDisplay {
|
||||
|
||||
boolean flipX = Wizardry.settings.spellHUDPosition.flipX;
|
||||
boolean flipY = Wizardry.settings.spellHUDPosition.flipY;
|
||||
|
||||
if(Wizardry.settings.spellHUDPosition.dynamic){
|
||||
// ............. | This bit is true if the wand is on the left, false if it is on the right
|
||||
flipX = flipX == ((mainHand ? player.getPrimaryHand() : player.getPrimaryHand().opposite()) == EnumHandSide.LEFT);
|
||||
}
|
||||
|
||||
Skin skin = skins.get(Wizardry.settings.spellHUDSkin);
|
||||
|
||||
@@ -134,13 +142,32 @@ public class GuiSpellDisplay {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
// 'Origin' of the spell hud (bottom left corner of the actual texture, always in the corner of the screen)
|
||||
int x = flipX ? width : 0;
|
||||
int y = flipY ? 0: height;
|
||||
|
||||
// The space available to render the spell HUD
|
||||
float xSpace = (float)(width/2 - HALF_HOTBAR_WIDTH);
|
||||
if(!player.getHeldItemOffhand().isEmpty()
|
||||
// Tests whether the offhand slot is rendered on the same side of the hotbar as the spell HUD
|
||||
&& (player.getPrimaryHand() == EnumHandSide.LEFT) == flipX){
|
||||
xSpace -= OFFHAND_SLOT_WIDTH;
|
||||
}
|
||||
|
||||
// If the skin is at the bottom and the screen width is too small, scale it to avoid the hotbar and offhand
|
||||
if(!flipY && skin.getWidth() > xSpace){ // width/2 - 91 - 29 taken from GuiInGame line 547
|
||||
float scale = xSpace / skin.getWidth();
|
||||
GlStateManager.scale(scale, scale, 1);
|
||||
x = MathHelper.ceil(x/scale);
|
||||
y = MathHelper.ceil(y/scale);
|
||||
}
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(wand);
|
||||
int cooldown = WandHelper.getCurrentCooldown(wand);
|
||||
int maxCooldown = WandHelper.getCurrentMaxCooldown(wand);
|
||||
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){
|
||||
|
||||
@@ -155,16 +182,9 @@ public class GuiSpellDisplay {
|
||||
|
||||
}else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){
|
||||
|
||||
float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade) * Constants.COOLDOWN_REDUCTION_PER_LEVEL;
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.font_of_mana)){
|
||||
// Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously
|
||||
cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier();
|
||||
}
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){
|
||||
if(!player.isCreative() && WizardData.get(player) != null){
|
||||
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
@@ -172,31 +192,23 @@ public class GuiSpellDisplay {
|
||||
|
||||
float progress = 1;
|
||||
// Doesn't really matter what progress is when in creative, but we might as well avoid the calculation.
|
||||
if(!player.capabilities.isCreativeMode && !spell.isContinuous){
|
||||
if(!player.isCreative() && !spell.isContinuous){
|
||||
// Subtracted partial tick time to make it smoother
|
||||
progress = (spell.cooldown * cooldownMultiplier - (float)cooldown + event.getPartialTicks())
|
||||
/(spell.cooldown * cooldownMultiplier);
|
||||
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + event.getPartialTicks()) / maxCooldown;
|
||||
}
|
||||
|
||||
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.capabilities.isCreativeMode);
|
||||
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative());
|
||||
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given opaque colour translucent with the given opacity.
|
||||
* @param colour An integer colour code, should be a 6-digit hexadecimal (i.e. opaque).
|
||||
* @param opacity The opacity to apply to the given colour, as a fraction between 0 and 1.
|
||||
* @return The resulting integer colour code, which will be an 8-digit hexadecimal.
|
||||
*/
|
||||
private static int makeTranslucent(int colour, float opacity){
|
||||
return colour + ((int)(0xff * opacity * 0x01000000));
|
||||
}
|
||||
|
||||
/** Gets the name of the given spell, with formatted added according to its cooldown and whether the given player
|
||||
* Gets the name of the given spell, with formatting added according to its cooldown and whether the given player
|
||||
* has discovered it.
|
||||
* @param spell The spell to get the name of.
|
||||
* @param The player to test for having discovered the given spell.
|
||||
* @param player The player to test for having discovered the given spell.
|
||||
* @param cooldown The spell's current cooldown.
|
||||
* @return The spell name, with relevant formatting added, for use with the {@link MixedFontRenderer}.
|
||||
*/
|
||||
@@ -204,12 +216,12 @@ public class GuiSpellDisplay {
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){
|
||||
if(!player.isCreative() && WizardData.get(player) != null){
|
||||
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
// Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect
|
||||
String format = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode();
|
||||
String format = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.getElement().getFormattingCode();
|
||||
if(!discovered) format = "\u00A79";
|
||||
|
||||
String name = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world);
|
||||
@@ -274,10 +286,15 @@ public class GuiSpellDisplay {
|
||||
}
|
||||
}
|
||||
|
||||
/** Instances of this class represent individual HUD skins, complete with texture and all necessary metadata. This
|
||||
/**
|
||||
* Instances of this class represent individual HUD skins, complete with texture and all necessary metadata. This
|
||||
* class serves to separate the logic behind the spell HUD from its actual rendering.
|
||||
* All information and processing done within this class relates only to the actual drawing; spells and such like
|
||||
* must be queried outside of this class and fed into the methods as appropriate. */
|
||||
* must be queried outside of this class and fed into the methods as appropriate.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
public static class Skin {
|
||||
|
||||
/** The texture file for this skin. */
|
||||
|
||||
+6
-10
@@ -1,21 +1,17 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.client.config.GuiButtonExt;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArray;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArrayEntries;
|
||||
import net.minecraftforge.fml.client.config.*;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArrayEntries.StringEntry;
|
||||
import net.minecraftforge.fml.client.config.GuiSelectString;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
import net.minecraftforge.fml.common.registry.EntityEntry;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* [NYI] Intended as a way of choosing entities by name from all those currently registered, within the config file, so
|
||||
* that users don't have to look up the entity IDs. I can't get this to work correctly at the moment.
|
||||
+43
-32
@@ -1,7 +1,4 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import electroblob.wizardry.Settings;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
@@ -15,6 +12,9 @@ import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
public GuiConfigWizardry(GuiScreen parent){
|
||||
@@ -33,7 +33,8 @@ public class GuiConfigWizardry extends GuiConfig {
|
||||
configList.add(new DummyCategoryElement("clientConfig", "config." + Wizardry.MODID + ".category." + Settings.CLIENT_CATEGORY, ClientCategory.class));
|
||||
configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY, SpellsCategory.class));
|
||||
configList.add(new DummyCategoryElement("resistancesConfig", "config." + Wizardry.MODID + ".category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class));
|
||||
|
||||
configList.add(new DummyCategoryElement("compatibilityConfig", "config." + Wizardry.MODID + ".category." + Settings.COMPATIBILITY_CATEGORY, CompatibilityCategory.class));
|
||||
|
||||
configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL)).getChildElements());
|
||||
|
||||
return configList;
|
||||
@@ -41,7 +42,7 @@ public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
// The reason this system is so convoluted is that it's designed for use with the @Config annotation. The problem is,
|
||||
// I'm not sure whether that will play well with the load phases. Hmmm...
|
||||
|
||||
|
||||
public static abstract class CategoryBase extends CategoryEntry {
|
||||
|
||||
public CategoryBase(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
@@ -77,6 +78,36 @@ public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
@Override protected String getCategory() { return Settings.GAMEPLAY_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Worldgen category of the config gui. */
|
||||
public static class WorldgenCategory extends CategoryBase {
|
||||
|
||||
public WorldgenCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Commands category of the config gui. */
|
||||
public static class CommandsCategory extends CategoryBase {
|
||||
|
||||
public CommandsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.COMMANDS_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Client category of the config gui. */
|
||||
public static class ClientCategory extends CategoryBase {
|
||||
|
||||
public ClientCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.CLIENT_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Spells category of the config gui. */
|
||||
public static class SpellsCategory extends CategoryBase {
|
||||
@@ -97,34 +128,14 @@ public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
@Override protected String getCategory() { return Settings.RESISTANCES_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Worldgen category of the config gui. */
|
||||
public static class WorldgenCategory extends CategoryBase {
|
||||
|
||||
public WorldgenCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Client category of the config gui. */
|
||||
public static class ClientCategory extends CategoryBase {
|
||||
|
||||
public ClientCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.CLIENT_CATEGORY; }
|
||||
}
|
||||
|
||||
|
||||
/** Commands category of the config gui. */
|
||||
public static class CommandsCategory extends CategoryBase {
|
||||
|
||||
public CommandsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
public static class CompatibilityCategory extends CategoryBase {
|
||||
|
||||
public CompatibilityCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.COMMANDS_CATEGORY; }
|
||||
|
||||
@Override protected String getCategory() { return Settings.COMPATIBILITY_CATEGORY; }
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -1,12 +1,8 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay.Skin;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
@@ -16,6 +12,9 @@ import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.client.config.GuiSelectString;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
public class GuiSelectHUDSkin extends GuiSelectString {
|
||||
|
||||
public GuiSelectHUDSkin(GuiScreen parentScreen, IConfigElement configElement, int slotIndex, Map<Object, String> selectableValues, Object currentValue, boolean enabled){
|
||||
@@ -50,7 +49,7 @@ public class GuiSelectHUDSkin extends GuiSelectString {
|
||||
|
||||
if(this.currentValue instanceof String){
|
||||
|
||||
this.drawString(this.fontRenderer, I18n.format("config." + Wizardry.MODID + ":spell_hud_skin.preview"), 170, 44, 0xffffff);
|
||||
this.drawString(this.fontRenderer, I18n.format("config." + Wizardry.MODID + ".spell_hud_skin.preview"), 170, 44, 0xffffff);
|
||||
|
||||
int previewLeft = 170;
|
||||
int previewRight = width-10;
|
||||
@@ -0,0 +1,93 @@
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiUtils;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
/**
|
||||
* Same as {@link net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry}, but instead of simply
|
||||
* displaying 'true' or 'false', allows the two display strings to be specified in the lang file.
|
||||
*/
|
||||
// BooleanEntry's constructors are private, so I had to copy the whole goddamn class to change one method. Thanks Forge.
|
||||
public class NamedBooleanEntry extends GuiConfigEntries.ButtonEntry {
|
||||
|
||||
protected final boolean beforeValue;
|
||||
protected boolean currentValue;
|
||||
|
||||
private static final String DEFAULT_KEY = "config.ebwizardry.generic";
|
||||
|
||||
public NamedBooleanEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement configElement){
|
||||
super(owningScreen, owningEntryList, configElement);
|
||||
this.beforeValue = Boolean.valueOf(configElement.get().toString());
|
||||
this.currentValue = beforeValue;
|
||||
this.btnValue.enabled = enabled();
|
||||
updateValueButtonText();
|
||||
}
|
||||
|
||||
// This is the only method that's any different
|
||||
@Override
|
||||
public void updateValueButtonText(){
|
||||
|
||||
String langKey = configElement.getLanguageKey() + "." + currentValue;
|
||||
this.btnValue.displayString = I18n.format(langKey);
|
||||
// If the key is unspecified, it defaults to the generic 'Enabled'/'Disabled' keys and adds a red/green colour
|
||||
if(this.btnValue.displayString.equals(langKey)){
|
||||
this.btnValue.displayString = I18n.format(DEFAULT_KEY + "." + currentValue);
|
||||
btnValue.packedFGColour = currentValue ? GuiUtils.getColorCode('a', true) : GuiUtils.getColorCode('c', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Everything from here down is the same as BooleanEntry
|
||||
|
||||
@Override
|
||||
public void valueButtonPressed(int slotIndex){
|
||||
if(enabled()) currentValue = !currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(){
|
||||
return currentValue == Boolean.valueOf(configElement.getDefault().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(){
|
||||
if(enabled()){
|
||||
currentValue = Boolean.valueOf(configElement.getDefault().toString());
|
||||
updateValueButtonText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged(){
|
||||
return currentValue != beforeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoChanges(){
|
||||
if(enabled()){
|
||||
currentValue = beforeValue;
|
||||
updateValueButtonText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveConfigElement(){
|
||||
if(enabled() && isChanged()){
|
||||
configElement.set(currentValue);
|
||||
return configElement.requiresMcRestart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getCurrentValue(){
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean[] getCurrentValues(){
|
||||
return new Boolean[]{getCurrentValue()};
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -1,10 +1,5 @@
|
||||
package electroblob.wizardry.client;
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import electroblob.wizardry.client.gui.GuiSelectHUDSkin;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import net.minecraftforge.client.gui.ForgeGuiFactory.ForgeConfigGui.ModIDEntry;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
@@ -12,17 +7,19 @@ import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Custom config GUI for spell HUD skin selection; displays a list of all the loaded skins and a preview of the currently
|
||||
* selected skin. based off of {@link ModIDEntry} from Forge.
|
||||
*/
|
||||
public class SpellHUDSkinChooserEntry extends SelectValueEntry {
|
||||
|
||||
public SpellHUDSkinChooserEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
|
||||
{
|
||||
public SpellHUDSkinChooserEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop, getSelectableValues());
|
||||
if (this.selectableValues.size() == 0)
|
||||
this.btnValue.enabled = false;
|
||||
if(this.selectableValues.size() == 0) this.btnValue.enabled = false;
|
||||
}
|
||||
|
||||
private static Map<Object, String> getSelectableValues(){
|
||||
@@ -27,6 +27,7 @@ class Contents {
|
||||
|
||||
// Final fields are mandatory, the rest are optional
|
||||
final String id;
|
||||
final Section section;
|
||||
private boolean hyperlinks = true;
|
||||
private boolean pageNumbers = true;
|
||||
private String separator = ".";
|
||||
@@ -37,10 +38,14 @@ class Contents {
|
||||
|
||||
private final List<Section> entries;
|
||||
|
||||
private Contents(String id){
|
||||
private List<Section> visibleEntries;
|
||||
|
||||
private Contents(String id, Section section){
|
||||
this.id = id;
|
||||
this.section = section;
|
||||
this.entries = new ArrayList<>();
|
||||
this.buttons = new ArrayList<>();
|
||||
this.visibleEntries = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable, flattened collection of all the buttons in this contents. */
|
||||
@@ -83,26 +88,29 @@ class Contents {
|
||||
|
||||
for(int page : visiblePages){
|
||||
|
||||
if(page >= 0 && page < entries.size() / maxLineNumber + 1){
|
||||
if(page >= 0 && page < visibleEntries.size() / maxLineNumber + 1){
|
||||
|
||||
int x = left + (GuiWizardHandbook.isRightPage(startPage + page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X);
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT;
|
||||
|
||||
for(Section entry : this.entries){
|
||||
for(Section entry : this.visibleEntries){
|
||||
|
||||
int nameWidth = font.getStringWidth(entry.title);
|
||||
if(entry.isUnlocked()){
|
||||
|
||||
String dotsAndNumber = " " + entry.startPage;
|
||||
int nameWidth = font.getStringWidth(entry.title);
|
||||
|
||||
while(font.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH - nameWidth - 2){
|
||||
dotsAndNumber = separator + dotsAndNumber;
|
||||
String dotsAndNumber = " " + entry.startPage;
|
||||
|
||||
while(font.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH - nameWidth - 2){
|
||||
dotsAndNumber = separator + dotsAndNumber;
|
||||
}
|
||||
|
||||
font.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH - font.getStringWidth(dotsAndNumber), y, DrawingUtils.BLACK, false);
|
||||
|
||||
if(!hyperlinks) font.drawString(entry.title, x, y, DrawingUtils.BLACK, false);
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
font.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH - font.getStringWidth(dotsAndNumber), y, DrawingUtils.BLACK, false);
|
||||
|
||||
if(!hyperlinks) font.drawString(entry.title, x, y, DrawingUtils.BLACK, false);
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +132,10 @@ class Contents {
|
||||
|
||||
this.buttons.clear();
|
||||
|
||||
this.visibleEntries = new ArrayList<>(entries); // Need to copy the collection first!
|
||||
|
||||
this.visibleEntries.removeIf(s -> !s.isUnlocked());
|
||||
|
||||
if(hyperlinks){
|
||||
|
||||
// FONT_HEIGHT may change between fonts, so this is calculated here. With the default font it's 14.
|
||||
@@ -134,12 +146,12 @@ class Contents {
|
||||
|
||||
List<GuiButton> list = new ArrayList<>(maxLineNumber);
|
||||
|
||||
for(Section entry : this.entries){
|
||||
for(Section entry : this.visibleEntries){
|
||||
|
||||
int x = GuiWizardHandbook.isRightPage(startPage) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT;
|
||||
|
||||
list.add(new GuiButtonHyperlink.Internal(0, x, y, font, entry.title, entry, 0, ""));
|
||||
list.add(new GuiButtonHyperlink.Internal(0, x, y, font, entry.title, entry, 0, "", maxLineNumber-startLine, GuiWizardHandbook.isRightPage(startPage)));
|
||||
|
||||
startLine++;
|
||||
|
||||
@@ -156,21 +168,22 @@ class Contents {
|
||||
|
||||
// Returning this is kind of trivial at the moment but if we ever wanted to add a header or something,
|
||||
// it would be more useful.
|
||||
return entries.size();
|
||||
return visibleEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given JSON object and constructs a new {@code Contents} from it, setting all the relevant fields
|
||||
* and references.
|
||||
*
|
||||
* @param parent The parent section for this contents.
|
||||
* @param json A JSON object representing the contents to be constructed. This must contain at least an "id"
|
||||
* string.
|
||||
* @return The resulting {@code Contents} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static Contents fromJson(JsonObject json){
|
||||
static Contents fromJson(Section parent, JsonObject json){
|
||||
|
||||
Contents contents = new Contents(JsonUtils.getString(json, "id"));
|
||||
Contents contents = new Contents(JsonUtils.getString(json, "id"), parent);
|
||||
|
||||
contents.hyperlinks = JsonUtils.getBoolean(json, "hyperlinks", true);
|
||||
contents.pageNumbers = JsonUtils.getBoolean(json, "page_numbers", true);
|
||||
|
||||
@@ -1,43 +1,38 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.spell.Mine;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.recipebook.GuiRecipeBook;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.stats.RecipeBook;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
class CraftingRecipe {
|
||||
|
||||
static final int WIDTH = 111, HEIGHT = 56;
|
||||
static final int BORDER = 7;
|
||||
static final int TEXTURE_INSET_X = 40, TEXTURE_INSET_Y = 190;
|
||||
static final int WIDTH = 121, HEIGHT = 66;
|
||||
|
||||
// Final fields are mandatory, the rest are optional
|
||||
private final ResourceLocation location;
|
||||
private final ResourceLocation[] locations;
|
||||
// Derived fields, not specifically defined in JSON
|
||||
private IRecipe recipe;
|
||||
private List<IRecipe> recipes;
|
||||
private final Set<int[]> instances = new HashSet<>();
|
||||
|
||||
private CraftingRecipe(ResourceLocation location){
|
||||
this.location = location;
|
||||
private CraftingRecipe(ResourceLocation[] locations){
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,9 +55,14 @@ class CraftingRecipe {
|
||||
* the recipes aren't necessarily loaded at that point. */
|
||||
void load(){
|
||||
|
||||
this.recipe = CraftingManager.getRecipe(location);
|
||||
recipes = new ArrayList<>(locations.length);
|
||||
|
||||
if(recipe == null) throw new JsonSyntaxException("No such recipe: " + location);
|
||||
for(ResourceLocation location : locations){
|
||||
|
||||
IRecipe recipe = CraftingManager.getRecipe(location);
|
||||
if(recipe == null) throw new JsonSyntaxException("No such recipe: " + location);
|
||||
recipes.add(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +75,12 @@ class CraftingRecipe {
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void draw(FontRenderer font, RenderItem itemRenderer, int doublePage, int left, int top){
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
renderCraftingRecipe(font, itemRenderer, left + instance[1], top + instance[2], this.recipe);
|
||||
renderCraftingRecipe(font, itemRenderer, left + instance[1], top + instance[2], recipes.get(index % recipes.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,9 +95,12 @@ class CraftingRecipe {
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void drawTooltips(GuiWizardHandbook gui, FontRenderer font, RenderItem itemRenderer, int doublePage, int left, int top, int mouseX, int mouseY){
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
renderCraftingTooltips(gui, itemRenderer, left + instance[1], top + instance[2], mouseX, mouseY, this.recipe);
|
||||
renderCraftingTooltips(gui, itemRenderer, left + instance[1], top + instance[2], mouseX, mouseY, recipes.get(index % recipes.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,15 +109,16 @@ class CraftingRecipe {
|
||||
* Parses the given JSON object and constructs a new {@code Image} from it, setting all the relevant fields
|
||||
* and references.
|
||||
*
|
||||
* @param json A JSON object representing the image to be constructed. This must contain at least a "location"
|
||||
* @param json A JSON object representing the image to be constructed. This must contain at least a "locations"
|
||||
* string.
|
||||
* @return The resulting {@code Image} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static CraftingRecipe fromJson(JsonObject json){
|
||||
|
||||
ResourceLocation location = new ResourceLocation(JsonUtils.getString(json, "location"));
|
||||
return new CraftingRecipe(location);
|
||||
ResourceLocation[] locations = Streams.stream(JsonUtils.getJsonArray(json, "locations"))
|
||||
.map(je -> new ResourceLocation(je.getAsString())).toArray(ResourceLocation[]::new);
|
||||
return new CraftingRecipe(locations);
|
||||
}
|
||||
|
||||
static void populate(Map<String, CraftingRecipe> map, JsonObject json){
|
||||
@@ -135,17 +142,15 @@ class CraftingRecipe {
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
|
||||
DrawingUtils.drawTexturedRect(x - 2, y - 2, 60, 190, WIDTH, HEIGHT, 512, 256);
|
||||
DrawingUtils.drawTexturedRect(x, y, TEXTURE_INSET_X, TEXTURE_INSET_Y, WIDTH, HEIGHT, GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
GlStateManager.enableLighting();
|
||||
itemRenderer.zLevel = 100.0F;
|
||||
|
||||
// TODO: There may be a better way of doing this
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
int i = 0;
|
||||
@@ -155,8 +160,8 @@ class CraftingRecipe {
|
||||
if(ingredient != Ingredient.EMPTY){
|
||||
ItemStack stack = ingredient.getMatchingStacks()[index % ingredient.getMatchingStacks().length];
|
||||
if(!stack.isEmpty()){
|
||||
itemRenderer.renderItemAndEffectIntoGUI(stack, x + 18 * (i%3), y + 18 * (i/3));
|
||||
itemRenderer.renderItemOverlays(font, stack, x + 18 * (i%3), y + 18 * (i/3));
|
||||
itemRenderer.renderItemAndEffectIntoGUI(stack, x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3));
|
||||
itemRenderer.renderItemOverlays(font, stack, x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,16 +169,15 @@ class CraftingRecipe {
|
||||
}
|
||||
|
||||
if(!result.isEmpty()){
|
||||
itemRenderer.renderItemAndEffectIntoGUI(result, x + 86, y + 18);
|
||||
itemRenderer.renderItemOverlays(font, result, x + 86, y + 18);
|
||||
itemRenderer.renderItemAndEffectIntoGUI(result, x + BORDER + 86, y + BORDER + 18);
|
||||
itemRenderer.renderItemOverlays(font, result, x + BORDER + 86, y + BORDER + 18);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.disableColorMaterial();
|
||||
itemRenderer.zLevel = 0.0F;
|
||||
//RenderHelper.enableStandardItemLighting();
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
@@ -187,9 +191,7 @@ class CraftingRecipe {
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
itemRenderer.zLevel = 0.0F;
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
// TODO: There may be a better way of doing this
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
int i = 0;
|
||||
@@ -198,7 +200,7 @@ class CraftingRecipe {
|
||||
|
||||
if(ingredient != Ingredient.EMPTY){
|
||||
ItemStack stack = ingredient.getMatchingStacks()[index % ingredient.getMatchingStacks().length];
|
||||
if(!stack.isEmpty() && isPointInRegion(x + 18 * (i%3), y + 18 * (i/3), 16, 16, mouseX, mouseY)){
|
||||
if(!stack.isEmpty() && isPointInRegion(x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3), 16, 16, mouseX, mouseY)){
|
||||
gui.renderToolTip(stack, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
@@ -206,15 +208,14 @@ class CraftingRecipe {
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!result.isEmpty() && isPointInRegion(x + 86, y + 18, 16, 16, mouseX, mouseY)){
|
||||
if(!result.isEmpty() && isPointInRegion(x + BORDER + 86, y + BORDER + 18, 16, 16, mouseX, mouseY)){
|
||||
gui.renderToolTip(result, mouseX, mouseY);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.disableColorMaterial();
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
@@ -19,10 +21,14 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
|
||||
public static final String URL_REGEX = "^((https?|ftp)://|(www|ftp)\\.)?[a-z0-9-]+(\\.[a-z0-9-]+)+([/?].*)?$";
|
||||
|
||||
/** Pulse period of links to new sections, in milliseconds. */
|
||||
private static final float PULSATION_PERIOD = 1500;
|
||||
|
||||
final int indent;
|
||||
final List<String> lines;
|
||||
final int linesLeft;
|
||||
|
||||
GuiButtonHyperlink(int id, int x, int y, FontRenderer font, String text, int indent, String suffix){
|
||||
GuiButtonHyperlink(int id, int x, int y, FontRenderer font, String text, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
|
||||
super(id, x, y, font.getStringWidth(text), font.FONT_HEIGHT, text);
|
||||
|
||||
@@ -36,6 +42,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
}
|
||||
|
||||
this.indent = indent; // Assigned here in case it was corrected above
|
||||
this.linesLeft = linesLeft;
|
||||
|
||||
String line1 = font.listFormattedStringToWidth(linkWithSuffix, GuiWizardHandbook.PAGE_WIDTH - indent).get(0);
|
||||
// Without trim(), there will be at least 1 leading space due to the custom wrapping
|
||||
@@ -59,6 +66,11 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any lines that overflowed onto the next double-page
|
||||
if(rightPage){
|
||||
while(lines.size() > linesLeft) lines.remove(lines.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHovered(net.minecraft.client.gui.FontRenderer font, int mouseX, int mouseY){
|
||||
@@ -72,6 +84,11 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
|
||||
int t = y + font.FONT_HEIGHT * i;
|
||||
|
||||
if(i > linesLeft){
|
||||
l = l + GuiWizardHandbook.GUI_WIDTH - 2 * GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH;
|
||||
t -= GuiWizardHandbook.PAGE_HEIGHT - (GuiWizardHandbook.PAGE_HEIGHT % font.FONT_HEIGHT);
|
||||
}
|
||||
|
||||
if(mouseX >= l && mouseY >= t && mouseX < l + font.getStringWidth(line) && mouseY < t + font.FONT_HEIGHT){
|
||||
return true;
|
||||
}
|
||||
@@ -93,7 +110,6 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
if(this.visible){
|
||||
|
||||
this.hovered = isHovered(minecraft.fontRenderer, mouseX, mouseY);
|
||||
int colour = hovered ? GuiWizardHandbook.colours.get("highlight") : GuiWizardHandbook.colours.get("hyperlink");
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -104,13 +120,22 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
|
||||
int t = y + minecraft.fontRenderer.FONT_HEIGHT * i;
|
||||
|
||||
minecraft.fontRenderer.drawString(line, l, t, colour);
|
||||
if(i > linesLeft){
|
||||
l = l + GuiWizardHandbook.GUI_WIDTH - 2 * GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH;
|
||||
t -= GuiWizardHandbook.PAGE_HEIGHT - (GuiWizardHandbook.PAGE_HEIGHT % minecraft.fontRenderer.FONT_HEIGHT);
|
||||
}
|
||||
|
||||
minecraft.fontRenderer.drawString(line, l, t, getColour());
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getColour(){
|
||||
return hovered ? GuiWizardHandbook.colours.get("highlight") : GuiWizardHandbook.colours.get("hyperlink");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new hyperlink button from the given arguments, automatically differentiating between URLs and sections.
|
||||
* @param x The x position of the button
|
||||
@@ -124,7 +149,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
* @throws IllegalArgumentException if the given argument array is empty or contains more than 2 arguments
|
||||
* @throws JsonSyntaxException if the specified link target is not a URL or a valid section ID
|
||||
*/
|
||||
public static GuiButtonHyperlink create(int x, int y, FontRenderer font, List<String> upToLink, String[] arguments, String suffix){
|
||||
public static GuiButtonHyperlink create(int x, int y, FontRenderer font, List<String> upToLink, String[] arguments, String suffix, int linesLeft, boolean rightPage){
|
||||
|
||||
if(arguments.length == 0 || arguments.length > 2) throw new IllegalArgumentException("Incorrect array length!");
|
||||
|
||||
@@ -133,7 +158,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
if(arguments[0].matches(URL_REGEX)){
|
||||
|
||||
button = new GuiButtonHyperlink.External(0, x, y, font, arguments[arguments.length - 1], arguments[0],
|
||||
font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix);
|
||||
font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
|
||||
|
||||
}else{
|
||||
|
||||
@@ -142,7 +167,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
if(target == null) throw new JsonSyntaxException("Hyperlink points to nonexistent section id " + arguments[0]);
|
||||
|
||||
button = new GuiButtonHyperlink.Internal(0, x, y, font, arguments[arguments.length - 1],
|
||||
target, font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix);
|
||||
target, font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
|
||||
}
|
||||
|
||||
return button;
|
||||
@@ -152,26 +177,49 @@ public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
|
||||
final Section target;
|
||||
|
||||
Internal(int id, int x, int y, FontRenderer font, String text, Section target, int indent, String suffix){
|
||||
super(id, x, y, font, text, indent, suffix);
|
||||
Internal(int id, int x, int y, FontRenderer font, String text, Section target, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY){
|
||||
if(!target.isUnlocked()) return false;
|
||||
return super.mousePressed(minecraft, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playPressSound(SoundHandler soundHandler){
|
||||
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getColour(){
|
||||
|
||||
if(!target.isUnlocked()) return GuiWizardHandbook.colours.get("text");
|
||||
|
||||
if(!hovered && target.isNew() && !Minecraft.getMinecraft().player.isCreative()){
|
||||
|
||||
int c = GuiWizardHandbook.colours.get("new_section");
|
||||
int d = GuiWizardHandbook.colours.get("hyperlink");
|
||||
float f = (MathHelper.sin((Minecraft.getSystemTime() % PULSATION_PERIOD) / PULSATION_PERIOD * 2 * (float)Math.PI) + 1) / 2f;
|
||||
|
||||
return DrawingUtils.mix(c, d, f);
|
||||
}
|
||||
|
||||
return super.getColour();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class External extends GuiButtonHyperlink {
|
||||
|
||||
final ITextComponent link;
|
||||
|
||||
External(int id, int x, int y, FontRenderer font, String text, String url, int indent, String suffix){
|
||||
super(id, x, y, font, text, indent, suffix);
|
||||
External(int id, int x, int y, FontRenderer font, String text, String url, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
|
||||
this.link = new TextComponentString(text);
|
||||
link.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url));
|
||||
link.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)).setColor(TextFormatting.DARK_BLUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,8 @@ import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
class GuiButtonTurnPage extends GuiButton {
|
||||
|
||||
static final int WIDTH = 20;
|
||||
|
||||
@@ -3,6 +3,7 @@ package electroblob.wizardry.client.gui.handbook;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
@@ -11,34 +12,28 @@ import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.packet.PacketRequestAdvancementSync;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.multiplayer.ClientAdvancementManager;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.ReflectionHelper;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* GUI class for the wizard's handbook. Like any GUI class, this is instantiated each time the book is opened. As of
|
||||
@@ -54,7 +49,6 @@ import java.util.List;
|
||||
* @see Image
|
||||
* @see CraftingRecipe
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
|
||||
@@ -71,6 +65,7 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
static final String IMAGE_TAG = "image";
|
||||
static final String RECIPE_TAG = "recipe";
|
||||
static final String RULER_TAG = "ruler";
|
||||
|
||||
static final Map<String, String> FORMAT_TAGS = new HashMap<>();
|
||||
|
||||
@@ -79,6 +74,8 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
/** The dimensions of the rendered GUI area. */
|
||||
static final int GUI_WIDTH = 288, GUI_HEIGHT = 180;
|
||||
/** The dimensions of the GUI texture itself. */
|
||||
static final int TEXTURE_WIDTH = 512, TEXTURE_HEIGHT = 256;
|
||||
/** The dimensions of the area of a single page in which text can be drawn. */
|
||||
static final int PAGE_WIDTH = 120, PAGE_HEIGHT = 140;
|
||||
/** The distance of the text from the top outside corner of each page. */
|
||||
@@ -107,12 +104,12 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
* The <b>double-page</b> number where the bookmark is currently set, <b>relative to the section stored in
|
||||
* {@link GuiWizardHandbook#bookmarkSection}</b>. Static because it persists when the book is closed.
|
||||
*/
|
||||
private static int bookmarkPage = 1;
|
||||
private static int bookmarkPage = 0;
|
||||
/**
|
||||
* A reference to the section in which the bookmark is currently set. Static because it persists when the book is
|
||||
* closed. Storing a section means the bookmark doesn't change location when new sections are unlocked.
|
||||
* The key corresponding to the section in which the bookmark is currently set. Static because it persists when the
|
||||
* book is closed. Storing a section means the bookmark doesn't change location when new sections are unlocked.
|
||||
*/
|
||||
private static Section bookmarkSection;
|
||||
private static String bookmarkSection;
|
||||
|
||||
// Buttons
|
||||
private GuiButton bookmark, next, previous, nextSection, previousSection, menu;
|
||||
@@ -164,19 +161,6 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
*/
|
||||
static final Map<String, CraftingRecipe> recipes = new HashMap<>();
|
||||
|
||||
static String rulerText = "--------------------"; // Assigned here as a fallback
|
||||
|
||||
private static final List<Pair<ItemStack, NonNullList<NonNullList<ItemStack>>>> RECIPES = new ArrayList<>();
|
||||
|
||||
// Reflection
|
||||
|
||||
static final Field ADVANCEMENT_TO_PROGRESS;
|
||||
|
||||
static {
|
||||
// TODO: Obf field name
|
||||
ADVANCEMENT_TO_PROGRESS = ReflectionHelper.findField(ClientAdvancementManager.class, "advancementToProgress");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a format tag to the handbook. All occurrences of the given tag string preceded by a # will be replaced with
|
||||
* the result of the given value string on GUI load. The value string, therefore, can be anything that should be
|
||||
@@ -193,9 +177,9 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
addFormatTag("next_spell_key", ClientProxy.NEXT_SPELL.getDisplayName());
|
||||
addFormatTag("previous_spell_key", ClientProxy.PREVIOUS_SPELL.getDisplayName());
|
||||
addFormatTag("mana_per_crystal_minus_30", "" + (Constants.MANA_PER_CRYSTAL - 30));
|
||||
addFormatTag("example_charging_loss", "" + (Constants.MANA_PER_CRYSTAL - 30));
|
||||
addFormatTag("mana_per_crystal", "" + Constants.MANA_PER_CRYSTAL);
|
||||
addFormatTag("novice_max_charge", "" + Tier.BASIC.maxCharge);
|
||||
addFormatTag("novice_max_charge", "" + Tier.NOVICE.maxCharge);
|
||||
addFormatTag("apprentice_max_charge", "" + Tier.APPRENTICE.maxCharge);
|
||||
addFormatTag("advanced_max_charge", "" + Tier.ADVANCED.maxCharge);
|
||||
addFormatTag("master_max_charge", "" + Tier.MASTER.maxCharge);
|
||||
@@ -264,12 +248,14 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
// Main background
|
||||
DrawingUtils.drawTexturedRect(left, top, 0, 0, GUI_WIDTH, GUI_HEIGHT, 512, 256);
|
||||
DrawingUtils.drawTexturedRect(left, top, 0, 0, GUI_WIDTH, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// First page background
|
||||
if(currentPage == 0){
|
||||
DrawingUtils.drawTexturedRect(left, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, 512, 256);
|
||||
DrawingUtils.drawTexturedRect(left, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
previous.visible = false;
|
||||
previousSection.visible = false; // Not worth testing if we're in the first section every frame
|
||||
menu.visible = false;
|
||||
@@ -281,7 +267,7 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
// Last page background
|
||||
if(currentPage == singleToDoublePage(pageCount)){
|
||||
DrawingUtils.drawTexturedFlippedRect(left + GUI_WIDTH / 2, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, 512, 256, true, false);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + GUI_WIDTH / 2, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT, true, false);
|
||||
next.visible = false;
|
||||
nextSection.visible = false;
|
||||
}else{
|
||||
@@ -302,9 +288,10 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
}
|
||||
|
||||
// Main content
|
||||
contentsList.values().forEach(c -> { if(c.section.isUnlocked()) c.draw(fontRenderer, currentPage, left, top); } );
|
||||
sections.values().forEach(s -> { if(s.isUnlocked()) s.draw(fontRenderer, currentPage, left, top); } );
|
||||
// These only get populated if the sections are unlocked so no checks are necessary
|
||||
images.values().forEach(i -> i.draw(fontRenderer, currentPage, left, top));
|
||||
contentsList.values().forEach(c -> c.draw(fontRenderer, currentPage, left, top));
|
||||
sections.values().forEach(s -> s.draw(fontRenderer, currentPage, left, top));
|
||||
recipes.values().forEach(r -> r.draw(fontRenderer, itemRender, currentPage, left, top));
|
||||
|
||||
// Buttons
|
||||
@@ -314,14 +301,15 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
if(currentPage == singleToDoublePage(bookmarkSection.startPage) + bookmarkPage){
|
||||
if(currentPage == singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage){
|
||||
// If the current page is the bookmarked page, the (invisible) bookmark button is disabled
|
||||
bookmark.visible = false;
|
||||
DrawingUtils.drawTexturedRect(left + 138, top, 299, 0, 11, 191, 512, 256);
|
||||
DrawingUtils.drawTexturedRect(left + 138, top, 299, 0, 11, 191, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}else{
|
||||
bookmark.visible = true;
|
||||
bookmark.x = left + (currentPage > singleToDoublePage(bookmarkSection.startPage) + bookmarkPage ? 130 : 147);
|
||||
bookmark.x = left + (currentPage > singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage ? 130 : 147);
|
||||
DrawingUtils.drawTexturedRect(bookmark.x, top,
|
||||
bookmark.isMouseOver() ? 310 : 288, 0, 11, 191, 512, 256);
|
||||
bookmark.isMouseOver() ? 310 : 288, 0, 11, 191, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
// Recipe tooltips
|
||||
@@ -426,8 +414,6 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
JsonElement je = gson.fromJson(reader, JsonElement.class);
|
||||
JsonObject json = je.getAsJsonObject();
|
||||
|
||||
rulerText = JsonUtils.getString(json, "ruler_text");
|
||||
|
||||
JsonUtils.getJsonObject(json, "colours").entrySet().forEach(e -> colours.put(e.getKey(),
|
||||
Color.decode(e.getValue().getAsString()).getRGB()));
|
||||
|
||||
@@ -443,10 +429,12 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempts to find a suitable place to put the bookmark to start with
|
||||
bookmarkSection = sections.get("introduction");
|
||||
if(bookmarkSection == null) bookmarkSection = sectionList.get(0);
|
||||
bookmarkSection = JsonUtils.getString(json, "bookmark_start_section");
|
||||
if(!sections.containsKey(bookmarkSection)) throw new JsonSyntaxException("Section with id " + bookmarkSection + " is undefined");
|
||||
}
|
||||
|
||||
// The first resource load on startup is done before the packet handler is loaded
|
||||
if(WizardryPacketHandler.net != null) WizardryPacketHandler.net.sendToServer(new PacketRequestAdvancementSync.Message());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,6 +448,8 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
*/
|
||||
private static IResource getHandbookResource(IResourceManager manager){
|
||||
|
||||
// TODO: Implement resource pack stacking to allow addon mods and texture packs to add/overwrite content
|
||||
|
||||
IResource handbookFile = null;
|
||||
|
||||
try{
|
||||
@@ -507,12 +497,15 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
if(currentSection != null){
|
||||
|
||||
int index = sectionList.indexOf(currentSection);
|
||||
List<Section> visibleSections = new ArrayList<>(sectionList);
|
||||
visibleSections.removeIf(s -> !s.isUnlocked());
|
||||
|
||||
if(button == nextSection && index + 1 < sections.size()){
|
||||
currentPage = singleToDoublePage(sectionList.get(index + 1).startPage);
|
||||
int index = visibleSections.indexOf(currentSection);
|
||||
|
||||
if(button == nextSection && index + 1 < visibleSections.size()){
|
||||
currentPage = singleToDoublePage(visibleSections.get(index + 1).startPage);
|
||||
}else if(index > 0){
|
||||
currentPage = singleToDoublePage(sectionList.get(index - 1).startPage);
|
||||
currentPage = singleToDoublePage(visibleSections.get(index - 1).startPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +513,7 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
currentPage = singleToDoublePage(sections.get("main_contents").startPage);
|
||||
|
||||
}else if(button == bookmark && bookmarkSection != null){
|
||||
currentPage = singleToDoublePage(bookmarkSection.startPage) + bookmarkPage;
|
||||
currentPage = singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage;
|
||||
|
||||
}else{
|
||||
if(button instanceof GuiButtonHyperlink.Internal){
|
||||
@@ -540,12 +533,12 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
this.selectedButton = bookmark;
|
||||
|
||||
for(Section section : sections.values()){
|
||||
for(String key : sections.keySet()){
|
||||
// The bookmark is assumed to bookmark the left-hand page
|
||||
if(section.containsPage(doubleToSinglePage(bookmarkPage, false))) bookmarkSection = section;
|
||||
if(sections.get(key).containsPage(doubleToSinglePage(currentPage, false))) bookmarkSection = key;
|
||||
}
|
||||
|
||||
bookmarkPage = currentPage - singleToDoublePage(bookmarkSection.startPage);
|
||||
bookmarkPage = currentPage - singleToDoublePage(sections.get(bookmarkSection).startPage);
|
||||
}
|
||||
}else{
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
@@ -558,9 +551,14 @@ public class GuiWizardHandbook extends GuiScreen {
|
||||
super.renderToolTip(stack, x, y);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAdvancementEvent(AdvancementEvent event){
|
||||
sections.values().forEach(s -> s.onAdvancement(event.getEntityPlayer(), event.getAdvancement()));
|
||||
@Override
|
||||
public boolean doesGuiPauseGame(){
|
||||
return Wizardry.settings.booksPauseGame;
|
||||
}
|
||||
|
||||
|
||||
public static void updateUnlockStatus(boolean showToasts, ResourceLocation... completedAdvancements){
|
||||
sections.values().forEach(s -> s.updateUnlockStatus(showToasts, completedAdvancements));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.gui.toasts.GuiToast;
|
||||
import net.minecraft.client.gui.toasts.IToast;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class HandbookToast implements IToast {
|
||||
|
||||
private final Section section;
|
||||
|
||||
public HandbookToast(Section section){
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
public IToast.Visibility draw(GuiToast toastGui, long delta){
|
||||
|
||||
toastGui.getMinecraft().getTextureManager().bindTexture(TEXTURE_TOASTS);
|
||||
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F);
|
||||
toastGui.drawTexturedModalRect(0, 0, 0, 32, 160, 32);
|
||||
|
||||
boolean firstPart = delta < 1500L;
|
||||
|
||||
int a = firstPart ? MathHelper.floor(MathHelper.clamp((float)(1500L - delta) / 300.0F, 0.0F, 1.0F) * 255.0F) << 24 | 67108864
|
||||
: MathHelper.floor(MathHelper.clamp((float)(delta - 1500L) / 300.0F, 0.0F, 1.0F) * 252.0F) << 24 | 67108864;
|
||||
|
||||
String s = firstPart ? I18n.format("handbook.toast.title") : section.title;
|
||||
|
||||
int c = firstPart ? -11534256 : -16777216;
|
||||
|
||||
List<String> list = toastGui.getMinecraft().fontRenderer.listFormattedStringToWidth(s, 125);
|
||||
|
||||
int h = 16 - list.size() * toastGui.getMinecraft().fontRenderer.FONT_HEIGHT / 2;
|
||||
|
||||
for(String line : list){
|
||||
toastGui.getMinecraft().fontRenderer.drawString(line, 30, h, c | a);
|
||||
h += toastGui.getMinecraft().fontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
toastGui.getMinecraft().getRenderItem().renderItemAndEffectIntoGUI(null, new ItemStack(WizardryItems.wizard_handbook), 8, 8);
|
||||
|
||||
return delta >= 5000L ? IToast.Visibility.HIDE : IToast.Visibility.SHOW;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -22,11 +23,15 @@ class Image {
|
||||
private int textureWidth, textureHeight;
|
||||
private int u = 0, v = 0;
|
||||
private String caption = "";
|
||||
private boolean border = true;
|
||||
// Derived fields, not specifically defined in JSON
|
||||
private final Set<int[]> instances = new HashSet<>();
|
||||
|
||||
private static final int CAPTION_OFFSET = 4;
|
||||
|
||||
private static final int TEXTURE_INSET_X = 180;
|
||||
private static final int BORDER = 1;
|
||||
|
||||
private Image(ResourceLocation location, int width, int height){
|
||||
this.location = location;
|
||||
this.width = width;
|
||||
@@ -68,6 +73,7 @@ class Image {
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void draw(FontRenderer font, int doublePage, int left, int top){
|
||||
// Images
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(location);
|
||||
@@ -77,6 +83,29 @@ class Image {
|
||||
top + instance[2] + height + CAPTION_OFFSET, GuiWizardHandbook.colours.get("caption"));
|
||||
}
|
||||
}
|
||||
|
||||
if(border){
|
||||
// Borders - do this after all the images are drawn so we only have to bind the handbook texture again once
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
// Math.ceil accounts for odd-numbered image dimensions
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] - BORDER, top + instance[2] - BORDER,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, width / 2 + BORDER, height / 2 + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, false, false);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] + width / 2, top + instance[2] - BORDER,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, MathHelper.ceil(width / 2f) + BORDER, height / 2 + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, true, false);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] - BORDER, top + instance[2] + height / 2,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, width / 2 + BORDER, MathHelper.ceil(height / 2f) + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, false, true);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] + width / 2, top + instance[2] + height / 2,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, MathHelper.ceil(width / 2f) + BORDER, MathHelper.ceil(height / 2f) + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +127,7 @@ class Image {
|
||||
image.textureWidth = JsonUtils.getInt(json, "texture_width", image.width);
|
||||
image.textureHeight = JsonUtils.getInt(json, "texture_height", image.height);
|
||||
image.caption = JsonUtils.getString(json, "caption", "");
|
||||
|
||||
image.border = JsonUtils.getBoolean(json, "border", true);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@ import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
@@ -23,7 +22,7 @@ import java.util.*;
|
||||
* everything within the section itself, including JSON parsing, unlock triggers and drawing the actual rawText.
|
||||
* Sections may now also be nested and have other elements within them, such as images and a table of contents, a
|
||||
* behaviour which is also handled within this class.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* The formatting of the book is now done 'dynamically' - that is, the exact positions and page numbers of
|
||||
* sections, images and so on are determined on GUI load and depend on which of the previous sections have been
|
||||
* unlocked, amongst other factors. This means that all of the unlocked sections must be formatted in order on GUI
|
||||
@@ -32,15 +31,15 @@ import java.util.*;
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
// Because these are now generated on resource pack reload (not on handbook open, as before), this class must now
|
||||
// be static
|
||||
// Because these are now generated on resource pack reload (not on handbook open, as before), this class can no longer
|
||||
// be a non-static inner class
|
||||
class Section {
|
||||
|
||||
// Final fields are mandatory (none here though), the rest are optional
|
||||
String title;
|
||||
private String[] rawText;
|
||||
private Contents contents;
|
||||
private Advancement[] triggers;
|
||||
private ResourceLocation[] triggers;
|
||||
private Map<String, Section> subsections;
|
||||
private boolean centreX, centreY;
|
||||
|
||||
@@ -54,6 +53,9 @@ class Section {
|
||||
*/
|
||||
private final List<List<String>> pages;
|
||||
|
||||
private boolean unlocked = false;
|
||||
private boolean isNew = false;
|
||||
|
||||
private Section(){
|
||||
this.buttons = new ArrayList<>();
|
||||
this.pages = new ArrayList<>();
|
||||
@@ -65,10 +67,10 @@ class Section {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given page is within this section, false if not.
|
||||
* Returns true if the given page is within this section, false if not (or if the section is locked).
|
||||
*/
|
||||
boolean containsPage(int page){
|
||||
return startPage <= page && startPage + pages.size() > page;
|
||||
return this.isUnlocked() && startPage <= page && startPage + pages.size() > page;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,32 +79,24 @@ class Section {
|
||||
*/
|
||||
boolean isUnlocked(){
|
||||
|
||||
if(Minecraft.getMinecraft().player.isCreative()) return true;
|
||||
if(!Wizardry.settings.handbookProgression) return true; // Always unlocked if handbook progression is off
|
||||
if(triggers == null) return true; // If no triggers were defined, the section is unlocked from the start
|
||||
|
||||
// A section is automatically unlocked if one of its subsections is unlocked
|
||||
for(Section subsection : subsections.values()){
|
||||
if(subsection.isUnlocked()) return true;
|
||||
}
|
||||
|
||||
if(triggers == null) return true; // If no triggers were defined, the section is unlocked from the start
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
for(Advancement trigger : triggers){
|
||||
|
||||
try{
|
||||
|
||||
// TESTME: Is this properly synced or do we need to do that as well?
|
||||
Map<Advancement, AdvancementProgress> advancements = (Map)GuiWizardHandbook.ADVANCEMENT_TO_PROGRESS.get(Minecraft.getMinecraft().player.connection.getAdvancementManager());
|
||||
|
||||
if(advancements.get(trigger).isDone()){
|
||||
return true;
|
||||
}
|
||||
|
||||
}catch(IllegalAccessException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Returns true if this section has been unlocked and not read yet. Also returns true if any subsections are new.
|
||||
*/
|
||||
boolean isNew(){
|
||||
if(!Wizardry.settings.handbookProgression) return false;
|
||||
return isNew || this.subsections.values().stream().anyMatch(Section::isNew);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,12 +137,19 @@ class Section {
|
||||
|
||||
for(String line : lines){
|
||||
|
||||
int lx = centreX ? x + GuiWizardHandbook.PAGE_WIDTH / 2 - font.getStringWidth(line) / 2 : x;
|
||||
|
||||
font.drawString(line, lx, y, DrawingUtils.BLACK, false);
|
||||
if(line.startsWith(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG)){
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
DrawingUtils.drawTexturedRect(x-1, y-1, 0, GuiWizardHandbook.GUI_HEIGHT, GuiWizardHandbook.PAGE_WIDTH + 2, 9, GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT);
|
||||
}else{
|
||||
int lx = centreX ? x + GuiWizardHandbook.PAGE_WIDTH / 2 - font.getStringWidth(line) / 2 : x;
|
||||
font.drawString(line, lx, y, DrawingUtils.BLACK, false);
|
||||
}
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
isNew = false; // Now a page has been drawn, the player must have seen it so it's not new any more
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +181,7 @@ class Section {
|
||||
// Adds the header if present
|
||||
if(!this.title.isEmpty()){
|
||||
lines.add(this.title);
|
||||
lines.add(GuiWizardHandbook.rulerText);
|
||||
lines.add(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG);
|
||||
}
|
||||
|
||||
// Adds space for the contents if it exists
|
||||
@@ -212,7 +213,7 @@ class Section {
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
Image image = GuiWizardHandbook.images.get(arguments[1]);
|
||||
if(image == null) throw new JsonSyntaxException("Image with id " + arguments[1] + "is undefined");
|
||||
if(image == null) throw new JsonSyntaxException("Image with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the image will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + image.getHeight(font) > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
@@ -248,27 +249,35 @@ class Section {
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
CraftingRecipe recipe = GuiWizardHandbook.recipes.get(arguments[1]);
|
||||
if(recipe == null) throw new JsonSyntaxException("Recipe with id " + arguments[1] + "is undefined");
|
||||
if(recipe == null) throw new JsonSyntaxException("Recipe with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the recipe will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + CraftingRecipe.HEIGHT > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
// Remaining number of lines on the page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), ""));
|
||||
// Remaining number of lines on the page, plus the first blank one on the new page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), " "));
|
||||
}
|
||||
|
||||
int page = startPage + (lines.size() / maxLineNumber);
|
||||
|
||||
if(lines.size() % maxLineNumber == 0) lines.add(" ");
|
||||
int startLine = lines.size() % maxLineNumber - 1;
|
||||
|
||||
recipe.addInstance(page, GuiWizardHandbook.PAGE_WIDTH / 2 - CraftingRecipe.WIDTH / 2
|
||||
+ (GuiWizardHandbook.isRightPage(page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X),
|
||||
GuiWizardHandbook.TEXT_INSET_Y + (lines.size() % maxLineNumber) * font.FONT_HEIGHT);
|
||||
GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT);
|
||||
|
||||
// Height of the recipe in lines, rounded up
|
||||
// Uses a single space instead of an empty string so that the page trimming doesn't remove them
|
||||
lines.addAll(Collections.nCopies(CraftingRecipe.HEIGHT / font.FONT_HEIGHT, " "));
|
||||
lines.addAll(Collections.nCopies(CraftingRecipe.HEIGHT / font.FONT_HEIGHT - 1, " "));
|
||||
// This time we're not adding an extra space because it's not really needed
|
||||
|
||||
}else{ // All other paragraphs
|
||||
|
||||
// Formatting
|
||||
for(Map.Entry<String, String> entry : GuiWizardHandbook.FORMAT_TAGS.entrySet()){
|
||||
paragraph = paragraph.replace(GuiWizardHandbook.FORMAT_MARKER + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// Hyperlinks
|
||||
|
||||
int linkStart;
|
||||
@@ -284,15 +293,17 @@ class Section {
|
||||
|
||||
String linkRaw = paragraph.substring(linkStart, linkEnd + 1);
|
||||
String[] arguments = paragraph.substring(linkStart + 1, linkEnd).split("\\s", 2);
|
||||
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0];
|
||||
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0].substring(1); // substring(1) to remove the @
|
||||
|
||||
// The index of the single page currently being formatted, relative to the section
|
||||
int pageRelative = (lines.size() + upToLink.size() - 1) / maxLineNumber;
|
||||
// The overall index of the single page currently being formatted
|
||||
int page = startPage + pageRelative;
|
||||
// The line number on this page
|
||||
int lineNumber = (lines.size() + upToLink.size() - 1) % maxLineNumber;
|
||||
|
||||
int x = GuiWizardHandbook.isRightPage(page) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + (((lines.size() + upToLink.size() - 1) % maxLineNumber)) * font.FONT_HEIGHT;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + lineNumber * font.FONT_HEIGHT;
|
||||
|
||||
// Adds any missing sub-lists
|
||||
while(this.buttons.size() <= pageRelative){
|
||||
@@ -300,18 +311,13 @@ class Section {
|
||||
}
|
||||
|
||||
// The button id only does what you use it for, so we're just not using it at all.
|
||||
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix));
|
||||
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix, maxLineNumber - lineNumber - 1, GuiWizardHandbook.isRightPage(page)));
|
||||
|
||||
// The link button should exactly overlay the display rawText in the main string
|
||||
// If the link has no display rawText specified, it displays the unformatted target string
|
||||
paragraph = paragraph.replace(linkRaw, arguments[arguments.length - 1]);
|
||||
}
|
||||
|
||||
// Formatting
|
||||
for(Map.Entry<String, String> entry : GuiWizardHandbook.FORMAT_TAGS.entrySet()){
|
||||
paragraph = paragraph.replace(GuiWizardHandbook.FORMAT_MARKER + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
lines.addAll(font.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH));
|
||||
}
|
||||
|
||||
@@ -373,18 +379,22 @@ class Section {
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "contents")){
|
||||
section.contents = Contents.fromJson(JsonUtils.getJsonObject(json, "contents"));
|
||||
section.contents = Contents.fromJson(section, JsonUtils.getJsonObject(json, "contents"));
|
||||
GuiWizardHandbook.contentsList.put(section.contents.id, section.contents);
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "text")){
|
||||
// TESTME: Does this always preserve order?
|
||||
section.rawText = Streams.stream(JsonUtils.getJsonArray(json, "text"))
|
||||
.map(e -> JsonUtils.getString(e, "element of array rawText"))
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
// TODO: Triggers
|
||||
if(JsonUtils.hasField(json, "triggers")){
|
||||
section.triggers = Streams.stream(JsonUtils.getJsonArray(json, "triggers"))
|
||||
.map(e -> new ResourceLocation(JsonUtils.getString(e, "element of array triggers")))
|
||||
.toArray(ResourceLocation[]::new);
|
||||
// TODO: Can we validate this and throw a JSON exception if no such advancement exists?
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "centre")){
|
||||
JsonObject centre = JsonUtils.getJsonObject(json,"centre");
|
||||
@@ -416,14 +426,26 @@ class Section {
|
||||
}
|
||||
}
|
||||
|
||||
public void onAdvancement(EntityPlayer player, Advancement advancement){
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
//System.out.println(title);
|
||||
if(triggers != null && Arrays.asList(triggers).contains(advancement) && player == minecraft.player){
|
||||
//minecraft.getToastGui().drawToast(IToast);
|
||||
/**
|
||||
* Called on login and advancement completion to update this section's unlock status and display toast
|
||||
* notifications if applicable.
|
||||
*/
|
||||
public void updateUnlockStatus(boolean showToasts, ResourceLocation... completedAdvancements){
|
||||
|
||||
// Debug
|
||||
//System.out.println(title);
|
||||
if(triggers == null) return;
|
||||
|
||||
List<ResourceLocation> completed = new ArrayList<>(Arrays.asList(completedAdvancements));
|
||||
completed.retainAll(Arrays.asList(triggers));
|
||||
|
||||
// Only shows the toast when the section was locked before and is now unlocked
|
||||
if(!this.unlocked && !completed.isEmpty() && showToasts && Wizardry.settings.handbookProgression){
|
||||
// Mmmmm toast...
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
minecraft.getToastGui().add(new HandbookToast(this));
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
// Currently, this will not take subsections into account
|
||||
this.unlocked = !completed.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
|
||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Custom baked model that stores a list of texture names and sets the lighting to full brightness for any quads
|
||||
* with one of those textures. Most of this code was copied and adapted from refined storage, which is licensed under
|
||||
* the MIT license. https://github.com/raoulvdberge/refinedstorage<br>
|
||||
* <br>
|
||||
* <i>N.B. This doesn't cover item models, and all of the code/model solutions to this that I have tried haven't worked.
|
||||
* However, I noticed that the shade tag seems to work perfectly for items, so instead I've simply duplicated the block
|
||||
* models into the item models folder and add {@code "shade":false} where appropriate. (If it works, it works, right?)</i>
|
||||
*
|
||||
* @author Electroblob
|
||||
* @author raoulvdberge
|
||||
*/
|
||||
public class BakedModelGlowingOverlay implements IBakedModel {
|
||||
|
||||
// Something something something cache, says Forge
|
||||
// This breaks randomised block models (because it's cached, duh...) but simply including the rand parameter will
|
||||
// completely defeat the point of the cache so I need some way of storing the randomised model variants... hmmm...
|
||||
// See WeightedBakedModel for more on randomisation (it's pretty similar to my 1.7 implementation from years ago)
|
||||
|
||||
// private class CacheKey {
|
||||
//
|
||||
// private IBakedModel base;
|
||||
// private String suffix;
|
||||
// private IBlockState state;
|
||||
// private EnumFacing side;
|
||||
//
|
||||
// public CacheKey(IBakedModel base, String suffix, IBlockState state, EnumFacing side){
|
||||
// this.base = base;
|
||||
// this.suffix = suffix;
|
||||
// this.state = state;
|
||||
// this.side = side;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean equals(Object o){
|
||||
//
|
||||
// if(this == o) return true;
|
||||
// if(o == null || getClass() != o.getClass()) return false;
|
||||
//
|
||||
// CacheKey cacheKey = (CacheKey)o;
|
||||
//
|
||||
// if(cacheKey.side != side) return false;
|
||||
// if(!state.equals(cacheKey.state)) return false;
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int hashCode() {
|
||||
// return state.hashCode() + (31 * (side != null ? side.hashCode() : 0));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static final LoadingCache<CacheKey, List<BakedQuad>> CACHE = CacheBuilder.newBuilder().build(new CacheLoader<CacheKey, List<BakedQuad>>() {
|
||||
// @Override
|
||||
// public List<BakedQuad> load(CacheKey key) {
|
||||
// return transformQuads(key.base.getQuads(key.state, key.side, 0), key.suffix);
|
||||
// }
|
||||
// });
|
||||
|
||||
private final IBakedModel delegate;
|
||||
private String suffix;
|
||||
|
||||
public BakedModelGlowingOverlay(IBakedModel delegate, String suffix){
|
||||
this.delegate = delegate;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
|
||||
if(state == null) return delegate.getQuads(state, side, rand);
|
||||
return transformQuads(delegate.getQuads(state, side, rand), suffix);
|
||||
//return CACHE.getUnchecked(new CacheKey(delegate, suffix, state instanceof IExtendedBlockState ? ((IExtendedBlockState) state).getClean() : state, side));
|
||||
}
|
||||
|
||||
// I would write these myself but I'd end up with almost the exact same thing anyway
|
||||
// They replace the quads from the original (delegate) model with full-brightness quads if their texture has the given suffix
|
||||
|
||||
private static List<BakedQuad> transformQuads(List<BakedQuad> oldQuads, String suffix){
|
||||
|
||||
List<BakedQuad> quads = new ArrayList<>(oldQuads);
|
||||
|
||||
for(int i = 0; i < quads.size(); ++i){
|
||||
BakedQuad quad = quads.get(i);
|
||||
|
||||
if(quad.getSprite().getIconName().endsWith(suffix)){
|
||||
quads.set(i, transformQuad(quad, 0.007F)); // What's the significance of 0.007?
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
private static BakedQuad transformQuad(BakedQuad quad, float light){
|
||||
|
||||
if(isLightMapDisabled()){
|
||||
return quad;
|
||||
}
|
||||
|
||||
VertexFormat newFormat = getFormatWithLightMap(quad.getFormat());
|
||||
|
||||
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(newFormat);
|
||||
|
||||
VertexLighterFlat trans = new VertexLighterFlat(Minecraft.getMinecraft().getBlockColors()) {
|
||||
@Override
|
||||
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z){
|
||||
lightmap[0] = light;
|
||||
lightmap[1] = light;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadTint(int tint){
|
||||
// NO OP
|
||||
}
|
||||
};
|
||||
|
||||
trans.setParent(builder);
|
||||
|
||||
quad.pipe(trans);
|
||||
|
||||
builder.setQuadTint(quad.getTintIndex());
|
||||
builder.setQuadOrientation(quad.getFace());
|
||||
builder.setTexture(quad.getSprite());
|
||||
builder.setApplyDiffuseLighting(false);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion(){
|
||||
return delegate.isAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d(){
|
||||
return delegate.isGui3d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltInRenderer(){
|
||||
return delegate.isBuiltInRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleTexture(){
|
||||
return delegate.getParticleTexture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemCameraTransforms getItemCameraTransforms(){
|
||||
return delegate.getItemCameraTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides(){
|
||||
return delegate.getOverrides();//BakedModelItemOverride.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion(IBlockState state){
|
||||
return delegate.isAmbientOcclusion(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType){
|
||||
return delegate.handlePerspective(cameraTransformType);
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
private static boolean isLightMapDisabled(){
|
||||
return FMLClientHandler.instance().hasOptifine() || !ForgeModContainer.forgeLightPipelineEnabled;
|
||||
}
|
||||
|
||||
private static final VertexFormat ITEM_FORMAT_WITH_LIGHTMAP = new VertexFormat(DefaultVertexFormats.ITEM).addElement(DefaultVertexFormats.TEX_2S);
|
||||
|
||||
private static VertexFormat getFormatWithLightMap(VertexFormat format){
|
||||
|
||||
if(isLightMapDisabled()){
|
||||
return format;
|
||||
}
|
||||
|
||||
if(format == DefaultVertexFormats.BLOCK){
|
||||
return DefaultVertexFormats.BLOCK;
|
||||
}else if(format == DefaultVertexFormats.ITEM){
|
||||
return ITEM_FORMAT_WITH_LIGHTMAP;
|
||||
}else if(!format.hasUvOffset(1)){
|
||||
VertexFormat result = new VertexFormat(format);
|
||||
result.addElement(DefaultVertexFormats.TEX_2S);
|
||||
return result;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
// Bit of a weird way of doing things if you ask me, but as far as I can tell it's how you're supposed to do it
|
||||
// public static final class BakedModelItemOverride extends ItemOverrideList {
|
||||
//
|
||||
// // This class doesn't contain any data of its own so it can be a singleton
|
||||
// public static final BakedModelItemOverride instance = new BakedModelItemOverride();
|
||||
//
|
||||
// private BakedModelItemOverride(){
|
||||
// super(ImmutableList.of()); // We're not using the list functionality
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
|
||||
// return new BakedModelGlowingOverlay(originalModel, "overlay"); // Bish bash bosh
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
|
||||
import electroblob.wizardry.entity.living.EntityIceGiant;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ModelIceGiant extends ModelBase {
|
||||
/** The head model for the iron golem. */
|
||||
public ModelRenderer iceGiantHead;
|
||||
|
||||
@@ -97,9 +97,9 @@ public class ModelWizard extends ModelBiped {
|
||||
setRotation(Shape13, 0F, 0F, 0F);
|
||||
|
||||
// Makes head bits move with head
|
||||
// bipedHead.addChild(Shape5);
|
||||
// bipedHead.addChild(hatSegment4);
|
||||
bipedHead.addChild(beard);
|
||||
// bipedHead.addChild(Shape7);
|
||||
// bipedHead.addChild(hatSegment6);
|
||||
// bipedHead.addChild(Shape8);
|
||||
// bipedHead.addChild(Shape9);
|
||||
// bipedHead.addChild(Shape10);
|
||||
@@ -116,8 +116,8 @@ public class ModelWizard extends ModelBiped {
|
||||
/* 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); setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
* bipedRightLeg.render(f5); bipedLeftLeg.render(f5); bipedBody.render(f5); bipedLeftArm.render(f5);
|
||||
* bipedRightArm.render(f5); bipedHead.render(f5); Shape5.render(f5); Shape8.render(f5); Shape9.render(f5);
|
||||
* Shape10.render(f5); Shape7.render(f5); Shape11.render(f5); Shape12.render(f5); Shape6.render(f5);
|
||||
* bipedRightArm.render(f5); bipedHead.render(f5); hatSegment4.render(f5); Shape8.render(f5); Shape9.render(f5);
|
||||
* Shape10.render(f5); hatSegment6.render(f5); Shape11.render(f5); Shape12.render(f5); hatSegment5.render(f5);
|
||||
* Shape13.render(f5); } */
|
||||
private void setRotation(ModelRenderer model, float x, float y, float z){
|
||||
model.rotateAngleX = x;
|
||||
|
||||
@@ -1,99 +1,110 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
public class ModelWizardArmour extends ModelArmourFixer {
|
||||
ModelRenderer Shape1;
|
||||
ModelRenderer Shape2;
|
||||
ModelRenderer Shape3;
|
||||
ModelRenderer Shape4;
|
||||
ModelRenderer Shape5;
|
||||
ModelRenderer Shape6;
|
||||
ModelRenderer Shape7;
|
||||
|
||||
ModelRenderer hatBrim;
|
||||
ModelRenderer hatSegment1;
|
||||
ModelRenderer hatSegment2;
|
||||
ModelRenderer hatSegment3;
|
||||
ModelRenderer hatSegment4;
|
||||
ModelRenderer hatSegment5;
|
||||
ModelRenderer hatSegment6;
|
||||
ModelRenderer robe;
|
||||
|
||||
public ModelWizardArmour(float scale){
|
||||
public ModelWizardArmour(float delta){
|
||||
|
||||
super(scale, 0, 64, 64);
|
||||
super(delta, 0, 64, 64);
|
||||
|
||||
// This is necessary to stop the head from scaling.
|
||||
this.bipedHead = new ModelRenderer(this, 0, 0);
|
||||
this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.1f);
|
||||
// The hat layer has an offset of 0.5, so 0.6 is about the smallest we can get away with
|
||||
this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.6f);
|
||||
this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F);
|
||||
|
||||
Shape1 = new ModelRenderer(this, -16, 32);
|
||||
Shape1.addBox(-8F, -7F, -8F, 16, 0, 16);
|
||||
Shape1.setRotationPoint(0F, 0F, 0F);
|
||||
Shape1.setTextureSize(64, 64);
|
||||
Shape1.mirror = true;
|
||||
setRotation(Shape1, 0F, 0F, 0F);
|
||||
hatBrim = new ModelRenderer(this, 0, 47);
|
||||
// Making the height 1 stops the top and bottom z-fighting when the hat is enchanted
|
||||
hatBrim.addBox(-8F, -6.85F, -8F, 16, 1, 16, 0.6f);
|
||||
hatBrim.setRotationPoint(0F, 0F, 0F);
|
||||
hatBrim.setTextureSize(64, 64);
|
||||
hatBrim.mirror = true;
|
||||
setRotation(hatBrim, 0F, 0F, 0F);
|
||||
|
||||
Shape2 = new ModelRenderer(this, 0, 48);
|
||||
Shape2.addBox(0F, 0F, 0F, 6, 2, 6);
|
||||
Shape2.setRotationPoint(-3F, -10F, -3F);
|
||||
Shape2.setTextureSize(64, 64);
|
||||
Shape2.mirror = true;
|
||||
setRotation(Shape2, -0.1396263F, 0F, 0F);
|
||||
hatSegment1 = new ModelRenderer(this, 0, 32);
|
||||
hatSegment1.addBox(0F, 0F, 0F, 6, 2, 6, 0.2f);
|
||||
hatSegment1.setRotationPoint(-3F, -10.6F, -3F);
|
||||
hatSegment1.setTextureSize(64, 64);
|
||||
hatSegment1.mirror = true;
|
||||
setRotation(hatSegment1, -0.1396263F, 0F, 0F);
|
||||
|
||||
Shape3 = new ModelRenderer(this, 0, 56);
|
||||
Shape3.addBox(0F, 0F, 0F, 5, 2, 5);
|
||||
Shape3.setRotationPoint(-2.5F, -11.53333F, -2F);
|
||||
Shape3.setTextureSize(64, 64);
|
||||
Shape3.mirror = true;
|
||||
setRotation(Shape3, -0.2443461F, 0F, 0F);
|
||||
hatSegment2 = new ModelRenderer(this, 0, 40);
|
||||
hatSegment2.addBox(0F, 0F, 0F, 5, 2, 5, 0.1f);
|
||||
hatSegment2.setRotationPoint(-2.5F, -12.13333F, -2F);
|
||||
hatSegment2.setTextureSize(64, 64);
|
||||
hatSegment2.mirror = true;
|
||||
setRotation(hatSegment2, -0.2443461F, 0F, 0F);
|
||||
|
||||
Shape4 = new ModelRenderer(this, 24, 48);
|
||||
Shape4.addBox(0F, 0F, 0F, 4, 2, 4);
|
||||
Shape4.setRotationPoint(-2F, -13F, -1F);
|
||||
Shape4.setTextureSize(64, 64);
|
||||
Shape4.mirror = true;
|
||||
setRotation(Shape4, -0.4014257F, 0F, 0F);
|
||||
hatSegment3 = new ModelRenderer(this, 24, 32);
|
||||
hatSegment3.addBox(0F, 0F, 0F, 4, 2, 4);
|
||||
hatSegment3.setRotationPoint(-2F, -13.6F, -1F);
|
||||
hatSegment3.setTextureSize(64, 64);
|
||||
hatSegment3.mirror = true;
|
||||
setRotation(hatSegment3, -0.4014257F, 0F, 0F);
|
||||
|
||||
Shape5 = new ModelRenderer(this, 24, 54);
|
||||
Shape5.addBox(0F, 0F, 0F, 3, 2, 3);
|
||||
Shape5.setRotationPoint(-1.5F, -14F, 0F);
|
||||
Shape5.setTextureSize(64, 64);
|
||||
Shape5.mirror = true;
|
||||
setRotation(Shape5, -0.5759587F, 0F, 0F);
|
||||
hatSegment4 = new ModelRenderer(this, 24, 38);
|
||||
hatSegment4.addBox(0F, 0F, 0F, 3, 2, 3);
|
||||
hatSegment4.setRotationPoint(-1.5F, -14.6F, 0F);
|
||||
hatSegment4.setTextureSize(64, 64);
|
||||
hatSegment4.mirror = true;
|
||||
setRotation(hatSegment4, -0.5759587F, 0F, 0F);
|
||||
|
||||
Shape6 = new ModelRenderer(this, 20, 59);
|
||||
Shape6.addBox(0F, 0F, 0F, 2, 2, 2);
|
||||
Shape6.setRotationPoint(-1F, -14F, 0F);
|
||||
Shape6.setTextureSize(64, 64);
|
||||
Shape6.mirror = true;
|
||||
setRotation(Shape6, 0.3316126F, 0F, 0F);
|
||||
hatSegment5 = new ModelRenderer(this, 20, 43);
|
||||
hatSegment5.addBox(0F, 0F, 0F, 2, 2, 2);
|
||||
hatSegment5.setRotationPoint(-1F, -14.6F, 0F);
|
||||
hatSegment5.setTextureSize(64, 64);
|
||||
hatSegment5.mirror = true;
|
||||
setRotation(hatSegment5, 0.3316126F, 0F, 0F);
|
||||
|
||||
Shape7 = new ModelRenderer(this, 28, 59);
|
||||
Shape7.addBox(0F, 0F, 0F, 1, 1, 3);
|
||||
Shape7.setRotationPoint(-0.5F, -14.5F, 2F);
|
||||
Shape7.setTextureSize(64, 64);
|
||||
Shape7.mirror = true;
|
||||
setRotation(Shape7, -0.5585054F, 0F, 0F);
|
||||
hatSegment6 = new ModelRenderer(this, 28, 43);
|
||||
hatSegment6.addBox(0F, 0F, 0F, 1, 1, 3);
|
||||
hatSegment6.setRotationPoint(-0.5F, -15.1F, 2F);
|
||||
hatSegment6.setTextureSize(64, 64);
|
||||
hatSegment6.mirror = true;
|
||||
setRotation(hatSegment6, -0.5585054F, 0F, 0F);
|
||||
|
||||
// The robe is now the body
|
||||
bipedBody = new ModelRenderer(this, 40, 42);
|
||||
bipedBody.addBox(-4F, 0F, -2F, 8, 18, 4, scale);
|
||||
bipedBody = new ModelRenderer(this, 16, 16);
|
||||
bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta);
|
||||
bipedBody.setRotationPoint(0F, 0F, 0F);
|
||||
bipedBody.setTextureSize(64, 64);
|
||||
bipedBody.mirror = true;
|
||||
setRotation(bipedBody, 0F, 0F, 0F);
|
||||
|
||||
robe = new ModelRenderer(this, 40, 32);
|
||||
robe.addBox(-4F, 0F, -2F, 8, 7, 4, delta);
|
||||
robe.setRotationPoint(0F, 12, 0F); // 12.5 accounts for the expansion of each box
|
||||
robe.setTextureSize(64, 64);
|
||||
robe.mirror = true;
|
||||
setRotation(robe, 0F, 0F, 0F);
|
||||
|
||||
// Makes the hat rotate with the head.
|
||||
bipedHead.addChild(Shape1);
|
||||
bipedHead.addChild(Shape2);
|
||||
bipedHead.addChild(Shape3);
|
||||
bipedHead.addChild(Shape4);
|
||||
bipedHead.addChild(Shape5);
|
||||
bipedHead.addChild(Shape6);
|
||||
bipedHead.addChild(Shape7);
|
||||
// Makes the robe move with the body
|
||||
// bipedBody.addChild(robe);
|
||||
bipedHead.addChild(hatBrim);
|
||||
bipedHead.addChild(hatSegment1);
|
||||
bipedHead.addChild(hatSegment2);
|
||||
bipedHead.addChild(hatSegment3);
|
||||
bipedHead.addChild(hatSegment4);
|
||||
bipedHead.addChild(hatSegment5);
|
||||
bipedHead.addChild(hatSegment6);
|
||||
}
|
||||
|
||||
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
|
||||
if(entity.isInvisible() && !entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)
|
||||
&& !entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)) return;
|
||||
super.render(entity, f, f1, f2, f3, f4, f5);
|
||||
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
this.robe.render(f5);
|
||||
}
|
||||
|
||||
private void setRotation(ModelRenderer model, float x, float y, float z){
|
||||
@@ -104,6 +115,20 @@ public class ModelWizardArmour extends ModelArmourFixer {
|
||||
|
||||
public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity){
|
||||
super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
this.robe.showModel = this.bipedBody.showModel;
|
||||
if(this.isSneak){
|
||||
//this.robe.rotationPointY = 10.5f;
|
||||
this.robe.rotationPointZ = 4;
|
||||
}else{
|
||||
//this.robe.rotationPointY = 12.5f;
|
||||
this.robe.rotationPointZ = 0;
|
||||
}
|
||||
|
||||
// The bottom part of the robe takes the y rotation from the rest of the robe but the x/z rotation
|
||||
// from the average of the two legs
|
||||
this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f;
|
||||
this.robe.rotateAngleY = this.bipedBody.rotateAngleY;
|
||||
this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
/**
|
||||
* Class responsible for registering all of wizardry's item (and itemblock) models.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryItemModels {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(ModelRegistryEvent event){
|
||||
|
||||
// ItemBlocks
|
||||
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_block));
|
||||
|
||||
// Items
|
||||
|
||||
registerItemModel(WizardryItems.magic_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.magic_wand);
|
||||
registerItemModel(WizardryItems.apprentice_wand);
|
||||
registerItemModel(WizardryItems.advanced_wand);
|
||||
registerItemModel(WizardryItems.master_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spell_book);
|
||||
// Wildcard registered for wizard trades.
|
||||
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
|
||||
registerItemModel(WizardryItems.arcane_tome);
|
||||
registerItemModel(WizardryItems.wizard_handbook);
|
||||
|
||||
registerItemModel(WizardryItems.basic_fire_wand);
|
||||
registerItemModel(WizardryItems.basic_ice_wand);
|
||||
registerItemModel(WizardryItems.basic_lightning_wand);
|
||||
registerItemModel(WizardryItems.basic_necromancy_wand);
|
||||
registerItemModel(WizardryItems.basic_earth_wand);
|
||||
registerItemModel(WizardryItems.basic_sorcery_wand);
|
||||
registerItemModel(WizardryItems.basic_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.apprentice_fire_wand);
|
||||
registerItemModel(WizardryItems.apprentice_ice_wand);
|
||||
registerItemModel(WizardryItems.apprentice_lightning_wand);
|
||||
registerItemModel(WizardryItems.apprentice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.apprentice_earth_wand);
|
||||
registerItemModel(WizardryItems.apprentice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.apprentice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.advanced_fire_wand);
|
||||
registerItemModel(WizardryItems.advanced_ice_wand);
|
||||
registerItemModel(WizardryItems.advanced_lightning_wand);
|
||||
registerItemModel(WizardryItems.advanced_necromancy_wand);
|
||||
registerItemModel(WizardryItems.advanced_earth_wand);
|
||||
registerItemModel(WizardryItems.advanced_sorcery_wand);
|
||||
registerItemModel(WizardryItems.advanced_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.master_fire_wand);
|
||||
registerItemModel(WizardryItems.master_ice_wand);
|
||||
registerItemModel(WizardryItems.master_lightning_wand);
|
||||
registerItemModel(WizardryItems.master_necromancy_wand);
|
||||
registerItemModel(WizardryItems.master_earth_wand);
|
||||
registerItemModel(WizardryItems.master_sorcery_wand);
|
||||
registerItemModel(WizardryItems.master_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_sword);
|
||||
registerItemModel(WizardryItems.spectral_pickaxe);
|
||||
registerItemModel(WizardryItems.spectral_bow);
|
||||
|
||||
registerItemModel(WizardryItems.mana_flask);
|
||||
|
||||
registerItemModel(WizardryItems.storage_upgrade);
|
||||
registerItemModel(WizardryItems.siphon_upgrade);
|
||||
registerItemModel(WizardryItems.condenser_upgrade);
|
||||
registerItemModel(WizardryItems.range_upgrade);
|
||||
registerItemModel(WizardryItems.duration_upgrade);
|
||||
registerItemModel(WizardryItems.cooldown_upgrade);
|
||||
registerItemModel(WizardryItems.blast_upgrade);
|
||||
registerItemModel(WizardryItems.attunement_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.flaming_axe);
|
||||
registerItemModel(WizardryItems.frost_axe);
|
||||
|
||||
registerItemModel(WizardryItems.firebomb);
|
||||
registerItemModel(WizardryItems.poison_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.blank_scroll);
|
||||
registerItemModel(WizardryItems.scroll);
|
||||
|
||||
registerItemModel(WizardryItems.armour_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.magic_silk);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat);
|
||||
registerItemModel(WizardryItems.wizard_robe);
|
||||
registerItemModel(WizardryItems.wizard_leggings);
|
||||
registerItemModel(WizardryItems.wizard_boots);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_fire);
|
||||
registerItemModel(WizardryItems.wizard_robe_fire);
|
||||
registerItemModel(WizardryItems.wizard_leggings_fire);
|
||||
registerItemModel(WizardryItems.wizard_boots_fire);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_ice);
|
||||
registerItemModel(WizardryItems.wizard_robe_ice);
|
||||
registerItemModel(WizardryItems.wizard_leggings_ice);
|
||||
registerItemModel(WizardryItems.wizard_boots_ice);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_lightning);
|
||||
registerItemModel(WizardryItems.wizard_robe_lightning);
|
||||
registerItemModel(WizardryItems.wizard_leggings_lightning);
|
||||
registerItemModel(WizardryItems.wizard_boots_lightning);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_robe_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_leggings_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_boots_necromancy);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_earth);
|
||||
registerItemModel(WizardryItems.wizard_robe_earth);
|
||||
registerItemModel(WizardryItems.wizard_leggings_earth);
|
||||
registerItemModel(WizardryItems.wizard_boots_earth);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_robe_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_leggings_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_boots_sorcery);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_healing);
|
||||
registerItemModel(WizardryItems.wizard_robe_healing);
|
||||
registerItemModel(WizardryItems.wizard_leggings_healing);
|
||||
registerItemModel(WizardryItems.wizard_boots_healing);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_helmet);
|
||||
registerItemModel(WizardryItems.spectral_chestplate);
|
||||
registerItemModel(WizardryItems.spectral_leggings);
|
||||
registerItemModel(WizardryItems.spectral_boots);
|
||||
|
||||
registerItemModel(WizardryItems.smoke_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.identification_scroll);
|
||||
}
|
||||
|
||||
// Moved from the proxies
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 creative tab supplied
|
||||
* to the aforementioned method will be whichever one the item is in.
|
||||
*/
|
||||
private static void registerItemModel(Item item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items); // Client-only method, but we're client-side so this is OK.
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
}
|
||||
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
|
||||
ModelLoader.setCustomModelResourceLocation(item, 0,
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model for the given metadata, using the item's registry name as the model name (this convention
|
||||
* makes it easier to keep track of everything). This is intended for registering additional metadata values which
|
||||
* aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades.
|
||||
*/
|
||||
private static void registerItemModel(Item item, int metadata, String variant){
|
||||
ModelLoader.setCustomModelResourceLocation(item, metadata,
|
||||
new ModelResourceLocation(item.getRegistryName(), variant));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockCrystal;
|
||||
import electroblob.wizardry.block.BlockPedestal;
|
||||
import electroblob.wizardry.block.BlockRunestone;
|
||||
import electroblob.wizardry.item.IMultiTexturedItem;
|
||||
import electroblob.wizardry.item.ItemBlockMultiTexturedElemental;
|
||||
import electroblob.wizardry.item.ItemCrystal;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.client.renderer.block.statemap.StateMap;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraftforge.client.event.ModelBakeEvent;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
/**
|
||||
* Class responsible for registering all of wizardry's item and block models.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryModels {
|
||||
|
||||
private WizardryModels(){} // No instances!
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(ModelRegistryEvent event){
|
||||
|
||||
// ItemBlocks
|
||||
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone));
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.crystal_block, new StateMap.Builder()
|
||||
.withName(BlockCrystal.ELEMENT).withSuffix("_crystal_block").build());
|
||||
// Yay unchecked casting! But we know it's always ok here, and it makes everything much neater.
|
||||
ItemBlockMultiTexturedElemental crystalBlockItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.crystal_block);
|
||||
registerMultiTexturedModel(crystalBlockItem);
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone, new StateMap.Builder()
|
||||
.withName(BlockRunestone.ELEMENT).withSuffix("_runestone").build());
|
||||
ItemBlockMultiTexturedElemental runestoneItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone);
|
||||
registerMultiTexturedModel(runestoneItem);
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone_pedestal, new StateMap.Builder()
|
||||
.withName(BlockPedestal.ELEMENT).ignore(BlockPedestal.NATURAL).withSuffix("_runestone_pedestal").build()); // Don't care about NATURAL property
|
||||
ItemBlockMultiTexturedElemental pedestalItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
|
||||
registerMultiTexturedModel(pedestalItem);
|
||||
|
||||
// Items
|
||||
|
||||
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.magic_wand);
|
||||
registerItemModel(WizardryItems.apprentice_wand);
|
||||
registerItemModel(WizardryItems.advanced_wand);
|
||||
registerItemModel(WizardryItems.master_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spell_book);
|
||||
// Wildcard registered for wizard trades.
|
||||
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
|
||||
registerItemModel(WizardryItems.arcane_tome);
|
||||
registerItemModel(WizardryItems.wizard_handbook);
|
||||
|
||||
registerItemModel(WizardryItems.novice_fire_wand);
|
||||
registerItemModel(WizardryItems.novice_ice_wand);
|
||||
registerItemModel(WizardryItems.novice_lightning_wand);
|
||||
registerItemModel(WizardryItems.novice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.novice_earth_wand);
|
||||
registerItemModel(WizardryItems.novice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.novice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.apprentice_fire_wand);
|
||||
registerItemModel(WizardryItems.apprentice_ice_wand);
|
||||
registerItemModel(WizardryItems.apprentice_lightning_wand);
|
||||
registerItemModel(WizardryItems.apprentice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.apprentice_earth_wand);
|
||||
registerItemModel(WizardryItems.apprentice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.apprentice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.advanced_fire_wand);
|
||||
registerItemModel(WizardryItems.advanced_ice_wand);
|
||||
registerItemModel(WizardryItems.advanced_lightning_wand);
|
||||
registerItemModel(WizardryItems.advanced_necromancy_wand);
|
||||
registerItemModel(WizardryItems.advanced_earth_wand);
|
||||
registerItemModel(WizardryItems.advanced_sorcery_wand);
|
||||
registerItemModel(WizardryItems.advanced_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.master_fire_wand);
|
||||
registerItemModel(WizardryItems.master_ice_wand);
|
||||
registerItemModel(WizardryItems.master_lightning_wand);
|
||||
registerItemModel(WizardryItems.master_necromancy_wand);
|
||||
registerItemModel(WizardryItems.master_earth_wand);
|
||||
registerItemModel(WizardryItems.master_sorcery_wand);
|
||||
registerItemModel(WizardryItems.master_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_sword);
|
||||
registerItemModel(WizardryItems.spectral_pickaxe);
|
||||
registerItemModel(WizardryItems.spectral_bow);
|
||||
|
||||
registerItemModel(WizardryItems.small_mana_flask);
|
||||
registerItemModel(WizardryItems.medium_mana_flask);
|
||||
registerItemModel(WizardryItems.large_mana_flask);
|
||||
|
||||
registerItemModel(WizardryItems.crystal_shard);
|
||||
registerItemModel(WizardryItems.grand_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.astral_diamond);
|
||||
|
||||
registerItemModel(WizardryItems.purifying_elixir);
|
||||
|
||||
registerItemModel(WizardryItems.storage_upgrade);
|
||||
registerItemModel(WizardryItems.siphon_upgrade);
|
||||
registerItemModel(WizardryItems.condenser_upgrade);
|
||||
registerItemModel(WizardryItems.range_upgrade);
|
||||
registerItemModel(WizardryItems.duration_upgrade);
|
||||
registerItemModel(WizardryItems.cooldown_upgrade);
|
||||
registerItemModel(WizardryItems.blast_upgrade);
|
||||
registerItemModel(WizardryItems.attunement_upgrade);
|
||||
registerItemModel(WizardryItems.melee_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.flaming_axe);
|
||||
registerItemModel(WizardryItems.frost_axe);
|
||||
|
||||
registerItemModel(WizardryItems.firebomb);
|
||||
registerItemModel(WizardryItems.poison_bomb);
|
||||
registerItemModel(WizardryItems.smoke_bomb);
|
||||
registerItemModel(WizardryItems.spark_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.blank_scroll);
|
||||
registerItemModel(WizardryItems.scroll);
|
||||
registerItemModel(WizardryItems.identification_scroll);
|
||||
|
||||
registerItemModel(WizardryItems.armour_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.magic_silk);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat);
|
||||
registerItemModel(WizardryItems.wizard_robe);
|
||||
registerItemModel(WizardryItems.wizard_leggings);
|
||||
registerItemModel(WizardryItems.wizard_boots);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_fire);
|
||||
registerItemModel(WizardryItems.wizard_robe_fire);
|
||||
registerItemModel(WizardryItems.wizard_leggings_fire);
|
||||
registerItemModel(WizardryItems.wizard_boots_fire);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_ice);
|
||||
registerItemModel(WizardryItems.wizard_robe_ice);
|
||||
registerItemModel(WizardryItems.wizard_leggings_ice);
|
||||
registerItemModel(WizardryItems.wizard_boots_ice);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_lightning);
|
||||
registerItemModel(WizardryItems.wizard_robe_lightning);
|
||||
registerItemModel(WizardryItems.wizard_leggings_lightning);
|
||||
registerItemModel(WizardryItems.wizard_boots_lightning);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_robe_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_leggings_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_boots_necromancy);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_earth);
|
||||
registerItemModel(WizardryItems.wizard_robe_earth);
|
||||
registerItemModel(WizardryItems.wizard_leggings_earth);
|
||||
registerItemModel(WizardryItems.wizard_boots_earth);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_robe_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_leggings_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_boots_sorcery);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_healing);
|
||||
registerItemModel(WizardryItems.wizard_robe_healing);
|
||||
registerItemModel(WizardryItems.wizard_leggings_healing);
|
||||
registerItemModel(WizardryItems.wizard_boots_healing);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_helmet);
|
||||
registerItemModel(WizardryItems.spectral_chestplate);
|
||||
registerItemModel(WizardryItems.spectral_leggings);
|
||||
registerItemModel(WizardryItems.spectral_boots);
|
||||
|
||||
registerItemModel(WizardryItems.lightning_hammer);
|
||||
|
||||
registerItemModel(WizardryItems.ring_condensing);
|
||||
registerItemModel(WizardryItems.ring_siphoning);
|
||||
registerItemModel(WizardryItems.ring_battlemage);
|
||||
registerItemModel(WizardryItems.ring_combustion);
|
||||
registerItemModel(WizardryItems.ring_fire_melee);
|
||||
registerItemModel(WizardryItems.ring_fire_biome);
|
||||
registerItemModel(WizardryItems.ring_disintegration);
|
||||
registerItemModel(WizardryItems.ring_ice_melee);
|
||||
registerItemModel(WizardryItems.ring_ice_biome);
|
||||
registerItemModel(WizardryItems.ring_arcane_frost);
|
||||
registerItemModel(WizardryItems.ring_shattering);
|
||||
registerItemModel(WizardryItems.ring_lightning_melee);
|
||||
registerItemModel(WizardryItems.ring_storm);
|
||||
registerItemModel(WizardryItems.ring_seeking);
|
||||
registerItemModel(WizardryItems.ring_hammer);
|
||||
registerItemModel(WizardryItems.ring_soulbinding);
|
||||
registerItemModel(WizardryItems.ring_leeching);
|
||||
registerItemModel(WizardryItems.ring_necromancy_melee);
|
||||
registerItemModel(WizardryItems.ring_mind_control);
|
||||
registerItemModel(WizardryItems.ring_poison);
|
||||
registerItemModel(WizardryItems.ring_earth_melee);
|
||||
registerItemModel(WizardryItems.ring_earth_biome);
|
||||
registerItemModel(WizardryItems.ring_full_moon);
|
||||
registerItemModel(WizardryItems.ring_extraction);
|
||||
registerItemModel(WizardryItems.ring_mana_return);
|
||||
registerItemModel(WizardryItems.ring_blockwrangler);
|
||||
registerItemModel(WizardryItems.ring_conjurer);
|
||||
registerItemModel(WizardryItems.ring_defender);
|
||||
registerItemModel(WizardryItems.ring_paladin);
|
||||
registerItemModel(WizardryItems.ring_interdiction);
|
||||
|
||||
registerItemModel(WizardryItems.amulet_arcane_defence);
|
||||
registerItemModel(WizardryItems.amulet_warding);
|
||||
registerItemModel(WizardryItems.amulet_wisdom);
|
||||
registerItemModel(WizardryItems.amulet_fire_protection);
|
||||
registerItemModel(WizardryItems.amulet_fire_cloaking);
|
||||
registerItemModel(WizardryItems.amulet_ice_immunity);
|
||||
registerItemModel(WizardryItems.amulet_ice_protection);
|
||||
registerItemModel(WizardryItems.amulet_potential);
|
||||
registerItemModel(WizardryItems.amulet_channeling);
|
||||
registerItemModel(WizardryItems.amulet_lich);
|
||||
registerItemModel(WizardryItems.amulet_wither_immunity);
|
||||
registerItemModel(WizardryItems.amulet_glide);
|
||||
registerItemModel(WizardryItems.amulet_banishing);
|
||||
registerItemModel(WizardryItems.amulet_anchoring);
|
||||
registerItemModel(WizardryItems.amulet_recovery);
|
||||
registerItemModel(WizardryItems.amulet_transience);
|
||||
registerItemModel(WizardryItems.amulet_resurrection);
|
||||
registerItemModel(WizardryItems.amulet_auto_shield);
|
||||
|
||||
registerItemModel(WizardryItems.charm_haggler);
|
||||
registerItemModel(WizardryItems.charm_experience_tome);
|
||||
registerItemModel(WizardryItems.charm_auto_smelt);
|
||||
registerItemModel(WizardryItems.charm_lava_walking);
|
||||
registerItemModel(WizardryItems.charm_storm);
|
||||
registerItemModel(WizardryItems.charm_minion_health);
|
||||
registerItemModel(WizardryItems.charm_minion_variants);
|
||||
registerItemModel(WizardryItems.charm_flight);
|
||||
registerItemModel(WizardryItems.charm_growth);
|
||||
registerItemModel(WizardryItems.charm_abseiling);
|
||||
registerItemModel(WizardryItems.charm_silk_touch);
|
||||
registerItemModel(WizardryItems.charm_stop_time);
|
||||
registerItemModel(WizardryItems.charm_light);
|
||||
registerItemModel(WizardryItems.charm_transportation);
|
||||
registerItemModel(WizardryItems.charm_feeding);
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void bake(ModelBakeEvent event){
|
||||
// MMMmmmm I love the smell of freshly-baked models...
|
||||
// This stuff is the boilerplate for making runestone overlay render with full brightness
|
||||
// See https://www.minecraftforge.net/forum/topic/66005-how-do-i-make-a-tileentityspecialrenderer-solved-with-ibakedmodel/
|
||||
// As usual the Forge documentation is just a description of each class and not an explanation of how to use them
|
||||
// I had to work out where this goes from the refined storage repo linked in the above thread, which is mixed in
|
||||
// with a more extensive registration system (which is super neat, but it's overkill for our purposes)
|
||||
// https://github.com/raoulvdberge/refinedstorage/blob/13d6e7f2b92f41b5009187aa2cbde50dbc72082f/src/main/java/com/raoulvdberge/refinedstorage/proxy/ProxyClient.java#L59
|
||||
|
||||
for(ModelResourceLocation location : event.getModelRegistry().getKeys()){
|
||||
|
||||
if(location.getNamespace().equals(Wizardry.MODID)){
|
||||
|
||||
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){
|
||||
IBakedModel original = event.getModelRegistry().getObject(location);
|
||||
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Moved from the proxies
|
||||
|
||||
/**
|
||||
* 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
|
||||
* all the other metadata values that the item can take, as defined in
|
||||
* {@link Item#getSubItems(CreativeTabs, NonNullList)}. The creative tab supplied
|
||||
* to the aforementioned method will be whichever one the item is in.
|
||||
*/
|
||||
private static void registerItemModel(Item item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items); // Client-only method, but we're client-side so this is OK.
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
}
|
||||
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
|
||||
ModelLoader.setCustomModelResourceLocation(item, 0,
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model, using the itemstack-sensitive {@link IMultiTexturedItem#getModelName(ItemStack)} as the
|
||||
* model name. This allows items to change their texture based on metadata/NBT. 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
|
||||
* {@link Item#getSubItems(CreativeTabs, NonNullList)}. The creative tab supplied
|
||||
* to the aforementioned method will be whichever one the item is in.
|
||||
*/
|
||||
private static <T extends Item & IMultiTexturedItem> void registerMultiTexturedModel(T item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items);
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getModelName(stack), "inventory"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model for the given metadata, using the item's registry name as the model name (this convention
|
||||
* makes it easier to keep track of everything). This is intended for registering additional metadata values which
|
||||
* aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades.
|
||||
*/
|
||||
private static void registerItemModel(Item item, int metadata, String variant){
|
||||
ModelLoader.setCustomModelResourceLocation(item, metadata,
|
||||
new ModelResourceLocation(item.getRegistryName(), variant));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ParticleBeam extends ParticleTargeted {
|
||||
|
||||
@@ -17,10 +16,15 @@ public class ParticleBeam extends ParticleTargeted {
|
||||
public ParticleBeam(World world, double x, double y, double z){
|
||||
super(world, x, y, z); // Does not have a texture!
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.setMaxAge(1);
|
||||
this.setMaxAge(0);
|
||||
this.particleScale = 1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
return 3;
|
||||
@@ -31,7 +35,7 @@ public class ParticleBeam extends ParticleTargeted {
|
||||
|
||||
float scale = this.particleScale;
|
||||
|
||||
if(this.particleMaxAge > 1){
|
||||
if(this.particleMaxAge > 0){
|
||||
float ageFraction = (particleAge + partialTicks - 1)/particleMaxAge;
|
||||
// Squaring this makes it look smoother than a linear shrinking effect
|
||||
scale = this.particleScale * (1 - ageFraction*ageFraction);
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleBuff extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/buff.png");
|
||||
@@ -28,13 +20,19 @@ public class ParticleBuff extends ParticleWizardry {
|
||||
|
||||
public ParticleBuff(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.setVelocity(0, 0.27, 0); // Approximately what it was before
|
||||
this.mirror = world.rand.nextBoolean();
|
||||
this.setVelocity(0, 0.162, 0); // Approximately what it was before
|
||||
this.mirror = random.nextBoolean();
|
||||
this.setMaxAge(15);
|
||||
this.setGravity(false);
|
||||
this.canCollide = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
super.onUpdate();
|
||||
@@ -59,14 +57,13 @@ public class ParticleBuff extends ParticleWizardry {
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
|
||||
// Copied from ParticleWizardry, needs to be here since we're not calling super
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
float scale = 0.6f;
|
||||
GlStateManager.scale(scale, scale, scale);
|
||||
if(mirror) GlStateManager.scale(-1, 1, 1);
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.disableCull();
|
||||
@@ -94,31 +91,29 @@ public class ParticleBuff extends ParticleWizardry {
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
// I'm pretty sure these were always static.
|
||||
Particle.interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks;
|
||||
Particle.interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks;
|
||||
Particle.interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks;
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
|
||||
|
||||
// Increases from 0 to 1 in steps of 0.125 evenly throughout the particle's lifetime
|
||||
float f = 0.875f - 0.125f * MathHelper.floor((float)this.particleAge/(float)this.particleMaxAge * 8 - 0.000001f);
|
||||
float g = f + 0.125f;
|
||||
float hrepeat = 1;
|
||||
float yScale = 0.7f;
|
||||
float scale = 0.6f;
|
||||
float yScale = 0.7f * scale;
|
||||
float dx = mirror ? -scale : scale;
|
||||
float dz = scale;
|
||||
|
||||
buffer.pos(x-1, y-yScale, z-1).tex(0, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z-1).tex(0, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y-yScale, z-1).tex(0.25*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y+yScale, z-1).tex(0.25*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y-yScale, z+1).tex(0.5*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y+yScale, z+1).tex(0.5*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y-yScale, z+1).tex(0.75*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z+1).tex(0.75*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y-yScale, z-1).tex(hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z-1).tex(hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y-yScale, z-dz).tex(0, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z-dz).tex(0, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y-yScale, z-dz).tex(0.25*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y+yScale, z-dz).tex(0.25*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y-yScale, z+dz).tex(0.5*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y+yScale, z+dz).tex(0.5*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y-yScale, z+dz).tex(0.75*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z+dz).tex(0.75*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y-yScale, z-dz).tex(hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z-dz).tex(hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
|
||||
Tessellator.getInstance().draw();
|
||||
|
||||
|
||||
@@ -3,10 +3,8 @@ package electroblob.wizardry.client.particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleDarkMagic extends ParticleWizardry {
|
||||
|
||||
/** Base spell texture index */
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleDust extends ParticleWizardry {
|
||||
|
||||
public ParticleDust(World world, double x, double y, double z){
|
||||
@@ -24,7 +22,7 @@ public class ParticleDust extends ParticleWizardry {
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
// this.moveEntity(this.motionX, this.motionY, this.motionZ);
|
||||
this.move(this.motionX, this.motionY, this.motionZ);
|
||||
|
||||
if(this.particleMaxAge-- <= 0){
|
||||
this.setExpired();
|
||||
|
||||
@@ -19,10 +19,15 @@ public class ParticleFlash extends ParticleWizardry {
|
||||
this.particleScale = 0.6f; // 7.1f is the value used in fireworks
|
||||
this.particleMaxAge = 6;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){
|
||||
float f4 = particleScale * MathHelper.sin(((float)this.particleAge + partialTicks - 1.0F)/particleMaxAge * (float)Math.PI);
|
||||
public boolean shouldDisableDepth(){
|
||||
return true; // Well this fixes everything... let's hope it doesn't cause any side-effects!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawParticle(BufferBuilder buffer, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){
|
||||
float f4 = particleScale * MathHelper.sin(((float)this.particleAge + partialTicks - 1.0F)/particleMaxAge * (float)Math.PI);
|
||||
this.setAlphaF(0.6F - ((float)this.particleAge + partialTicks - 1.0F)/particleMaxAge * 0.5F);
|
||||
float f5 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float f6 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
@@ -34,7 +39,7 @@ public class ParticleFlash extends ParticleWizardry {
|
||||
buffer.pos((double)(f5 - rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 - rotationYZ * f4 + rotationXZ * f4)).tex(0.5D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
buffer.pos((double)(f5 + rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 + rotationYZ * f4 + rotationXZ * f4)).tex(0.25D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
buffer.pos((double)(f5 + rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 + rotationYZ * f4 - rotationXZ * f4)).tex(0.25D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float partialTicks){
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleIce extends ParticleWizardry {
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleLeaf extends ParticleWizardry {
|
||||
|
||||
@@ -24,7 +23,7 @@ public class ParticleLeaf extends ParticleWizardry {
|
||||
this.particleGravity = 0;
|
||||
this.canCollide = true;
|
||||
// Produces a variety of browns and greens
|
||||
this.setRBGColorF(0.1f + 0.3f * world.rand.nextFloat(), 0.5f + 0.3f * world.rand.nextFloat(), 0.1f);
|
||||
this.setRBGColorF(0.1f + 0.3f * random.nextFloat(), 0.5f + 0.3f * random.nextFloat(), 0.1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ParticleLightning extends ParticleTargeted {
|
||||
|
||||
@@ -28,10 +25,6 @@ public class ParticleLightning extends ParticleTargeted {
|
||||
/** Number of ticks to wait before the arc changes shape again. */
|
||||
private static final int UPDATE_PERIOD = 1;
|
||||
|
||||
/** A random long value used by the renderer as a seed to generate its vertices from, ensuring they remain the same
|
||||
* across multiple frames. Not synced. */
|
||||
public final long seed;
|
||||
|
||||
public ParticleLightning(World world, double x, double y, double z){
|
||||
super(world, x, y, z); // Does not have a texture!
|
||||
seed = this.rand.nextLong();
|
||||
@@ -39,7 +32,12 @@ public class ParticleLightning extends ParticleTargeted {
|
||||
this.setMaxAge(3);
|
||||
this.particleScale = 1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
return 3;
|
||||
@@ -67,7 +65,7 @@ public class ParticleLightning extends ParticleTargeted {
|
||||
// Creates a random from the arc's seed field + the number of ticks it has existed/the update period.
|
||||
// By using a seed, we can ensure the vertex positions and forks are identical a) for each layer, even
|
||||
// though they are rendered sequentially, and b) across many frames (and ticks, if updateTime > 1).
|
||||
Random random = new Random(this.seed + this.particleAge/UPDATE_PERIOD);
|
||||
random.setSeed(this.seed + this.particleAge/UPDATE_PERIOD);
|
||||
|
||||
// numberOfSegments-1 because the last segment is handled separately.
|
||||
for(int i=0; i<numberOfSegments-1; i++){
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleLightningPulse extends ParticleWizardry {
|
||||
|
||||
@@ -27,7 +26,12 @@ public class ParticleLightningPulse extends ParticleWizardry {
|
||||
this.pitch = 90;
|
||||
this.yaw = 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation texture : TEXTURES){
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleMagicBubble extends ParticleWizardry {
|
||||
|
||||
public ParticleMagicBubble(World world, double x, double y, double z){
|
||||
|
||||
@@ -1,46 +1,36 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleMagicFlame extends ParticleWizardry {
|
||||
|
||||
/** The scale of the flame particle */
|
||||
private float flameScale;
|
||||
// 4 different animation strips, 8 frames in each strip
|
||||
private static final ResourceLocation[][] TEXTURES = generateTextures("flame", 4, 8);
|
||||
|
||||
public ParticleMagicFlame(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.flameScale = 1 + world.rand.nextFloat();
|
||||
|
||||
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
|
||||
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleAlpha = 1;
|
||||
this.particleMaxAge = (int)(2.0D / (Math.random() * 0.8D + 0.2D));
|
||||
this.particleMaxAge = 12 + rand.nextInt(4);
|
||||
this.shaded = false;
|
||||
// IDEA: Make the particles for ray spells collide properly and not spawn on the other side of stuff.
|
||||
this.canCollide = false;
|
||||
this.setParticleTextureIndex(48);
|
||||
this.canCollide = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity entity, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge;
|
||||
this.particleScale = this.flameScale * (1.0F - f6 * f6 * 0.5F);
|
||||
super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float par1){
|
||||
return 256;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Particle multipleParticleScaleBy(float scale){
|
||||
this.flameScale *= scale;
|
||||
return super.multipleParticleScaleBy(scale);
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation[] array : TEXTURES){
|
||||
for(ResourceLocation texture : array){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticlePath extends ParticleWizardry {
|
||||
|
||||
@@ -34,6 +33,11 @@ public class ParticlePath extends ParticleWizardry {
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleScorch extends ParticleWizardry {
|
||||
|
||||
@@ -27,7 +26,12 @@ public class ParticleScorch extends ParticleWizardry {
|
||||
this.setRBGColorF(0, 0, 0);
|
||||
this.shaded = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRBGColorF(float r, float g, float b){
|
||||
super.setRBGColorF(r, g, b);
|
||||
@@ -48,7 +52,7 @@ public class ParticleScorch extends ParticleWizardry {
|
||||
|
||||
// Fading
|
||||
if(this.particleAge > this.particleMaxAge/2){
|
||||
this.setAlphaF(1 - ((float)this.particleAge - (float)(this.particleMaxAge/2)) / (float)this.particleMaxAge);
|
||||
this.setAlphaF(1 - ((float)this.particleAge - this.particleMaxAge/2f) / (this.particleMaxAge/2f));
|
||||
}
|
||||
|
||||
EnumFacing facing = EnumFacing.fromAngle(yaw);
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleSnow extends ParticleWizardry {
|
||||
|
||||
@@ -24,7 +23,7 @@ public class ParticleSnow extends ParticleWizardry {
|
||||
this.canCollide = true;
|
||||
this.setMaxAge(40 + rand.nextInt(10));
|
||||
// Produces a variety of light blues and whites
|
||||
this.setRBGColorF(0.9f + 0.1f * world.rand.nextFloat(), 0.95f + 0.05f * world.rand.nextFloat(), 1);
|
||||
this.setRBGColorF(0.9f + 0.1f * random.nextFloat(), 0.95f + 0.05f * random.nextFloat(), 1);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleSpark extends ParticleWizardry {
|
||||
|
||||
@@ -25,14 +24,18 @@ public class ParticleSpark extends ParticleWizardry {
|
||||
this.canCollide = false;
|
||||
this.setMaxAge(3); // Lifetime defaults to 3 (and is very unlikely to be changed)
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
// May no longer be necessary, ParticleManager seems to enable blending now
|
||||
|
||||
// @Override
|
||||
// public void applyGLStateChanges(){
|
||||
// GlStateManager.enableBlend();
|
||||
// GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
// // TESTME: Are these two actually necessary?
|
||||
// GlStateManager.disableLighting();
|
||||
// OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
|
||||
// }
|
||||
|
||||
@@ -6,9 +6,8 @@ import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleSparkle extends ParticleWizardry {
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ParticleSphere extends ParticleWizardry {
|
||||
|
||||
public ParticleSphere(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleMaxAge = 5;
|
||||
this.particleAlpha = 0.8f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ,
|
||||
float rotationXY, float rotationXZ){
|
||||
|
||||
// Copied from ParticleWizardry, needs to be here since we're not calling super
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(x - interpPosX, y - interpPosY, z - interpPosZ);
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE);
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
float latStep = (float)Math.PI/20;
|
||||
float longStep = (float)Math.PI/20;
|
||||
|
||||
float sphereRadius = this.particleScale * (this.particleAge + partialTicks - 1) / this.particleMaxAge;
|
||||
float alpha = this.particleAlpha * (1 - (this.particleAge + partialTicks - 1) / this.particleMaxAge);
|
||||
|
||||
drawSphere(Tessellator.getInstance(), buffer, sphereRadius, latStep, longStep, true, particleRed, particleGreen, particleBlue, alpha);
|
||||
drawSphere(Tessellator.getInstance(), buffer, sphereRadius, latStep, longStep, false, particleRed, particleGreen, particleBlue, alpha);
|
||||
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float partialTicks){
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a sphere (using lat/long triangles) with the given parameters.
|
||||
* @param radius The radius of the sphere.
|
||||
* @param latStep The latitude step; smaller is smoother but increases performance cost.
|
||||
* @param longStep The longitude step; smaller is smoother but increases performance cost.
|
||||
* @param inside Whether to draw the outside or the inside of the sphere.
|
||||
* @param r The red component of the sphere colour.
|
||||
* @param g The green component of the sphere colour.
|
||||
* @param b The blue component of the sphere colour.
|
||||
* @param a The alpha component of the sphere colour.
|
||||
*/
|
||||
private static void drawSphere(Tessellator tessellator, BufferBuilder buffer, float radius, float latStep, float longStep, boolean inside, float r, float g, float b, float a){
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
|
||||
|
||||
boolean goingUp = inside;
|
||||
|
||||
buffer.pos(0, goingUp ? -radius : radius, 0).color(r, g, b, a).endVertex(); // Start at the north pole
|
||||
|
||||
for(float longitude = -(float)Math.PI; longitude <= (float)Math.PI; longitude += longStep){
|
||||
|
||||
// Leave the poles out since they only have a single point per stack instead of two
|
||||
for(float theta = (float)Math.PI/2 - latStep; theta >= -(float)Math.PI/2 + latStep; theta -= latStep){
|
||||
|
||||
float latitude = goingUp ? -theta : theta;
|
||||
|
||||
float hRadius = radius * MathHelper.cos(latitude);
|
||||
float vy = radius * MathHelper.sin(latitude);
|
||||
float vx = hRadius * MathHelper.sin(longitude);
|
||||
float vz = hRadius * MathHelper.cos(longitude);
|
||||
|
||||
buffer.pos(vx, vy, vz).color(r, g, b, a).endVertex();
|
||||
|
||||
vx = hRadius * MathHelper.sin(longitude + longStep);
|
||||
vz = hRadius * MathHelper.cos(longitude + longStep);
|
||||
|
||||
buffer.pos(vx, vy, vz).color(r, g, b, a).endVertex();
|
||||
}
|
||||
|
||||
// The next pole
|
||||
buffer.pos(0, goingUp ? radius : -radius, 0).color(r, g, b, a).endVertex();
|
||||
|
||||
goingUp = !goingUp;
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleSummon extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/summon.png");
|
||||
private final boolean mirror;
|
||||
|
||||
public ParticleSummon(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.mirror = random.nextBoolean();
|
||||
this.setMaxAge(10);
|
||||
this.setGravity(false);
|
||||
this.canCollide = false;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onUpdate(){
|
||||
// super.onUpdate();
|
||||
// if(this.particleAge > this.particleMaxAge/2) this.particleAlpha = 2f - 2f*(float)this.particleAge/(float)this.particleMaxAge;
|
||||
// }
|
||||
|
||||
/* There are 4 layers of particles, specified as 0-3 by the method below. - Layer 0 causes the normal particles.png
|
||||
* to be bound to the render engine for normal particles. - Layer 1 causes the block textures to be bound to the
|
||||
* render engine for digging fx and falling fx. - Layer 2 causes the item textures to be bound to the render engine
|
||||
* for tool breaking fx, snowballpoofs, slime particles, etc. - Layer 3 is not used in vanilla minecraft and was
|
||||
* presumably added by forge for exactly this reason. This means no texture is bound by vanilla minecraft, meaning
|
||||
* you are free to do as you wish without possibly overwriting vanilla particles. Mod particles won't be overwritten
|
||||
* anyway since they bind their own textures. It is of course important to bind the texture every time you render a
|
||||
* custom particle, but I don't see how you could do it any other way, since you don't have access to
|
||||
* EffectRenderer. */
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
// This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer.
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
|
||||
// Copied from ParticleWizardry, needs to be here since we're not calling super
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
float scale = 0.6f;
|
||||
GlStateManager.scale(scale, scale, scale);
|
||||
if(mirror) GlStateManager.scale(-1, 1, 1);
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE);
|
||||
// Makes the particle colour add to the colour of the texture pixels, rather than the default multiplying
|
||||
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
|
||||
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
|
||||
// Does the texture translation wrapping thing (the cool stuff)
|
||||
// GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
// GlStateManager.loadIdentity();
|
||||
//
|
||||
// GlStateManager.translate((this.particleAge + partialTicks)/(float)this.particleMaxAge * -2, 0, 0);
|
||||
//
|
||||
// GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
|
||||
// Increases from 0 to 1 in steps of 0.125 evenly throughout the particle's lifetime
|
||||
float f = 0.125f * MathHelper.floor((float)this.particleAge/(float)this.particleMaxAge * 8 - 0.000001f);
|
||||
float g = f + 0.125f;
|
||||
float hrepeat = 1;
|
||||
float yScale = 3f;
|
||||
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
|
||||
buffer.pos(x-1, y, z-1).tex(0, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z-1).tex(0, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y, z-1).tex(0.25*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y+yScale, z-1).tex(0.25*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y, z+1).tex(0.5*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+1, y+yScale, z+1).tex(0.5*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y, z+1).tex(0.75*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z+1).tex(0.75*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y, z-1).tex(hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-1, y+yScale, z-1).tex(hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
|
||||
Tessellator.getInstance().draw();
|
||||
|
||||
// Undoes the texture transformations
|
||||
// GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
// GlStateManager.loadIdentity();
|
||||
// GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.enableLighting();
|
||||
// Reverses the colour addition change from before
|
||||
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
|
||||
GlStateManager.popAttrib();
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Superclass for particles with a second target entity or target position. */
|
||||
public abstract class ParticleTargeted extends ParticleWizardry {
|
||||
|
||||
|
||||
protected double targetX;
|
||||
protected double targetY;
|
||||
protected double targetZ;
|
||||
protected double targetVelX;
|
||||
protected double targetVelY;
|
||||
protected double targetVelZ;
|
||||
|
||||
protected double length;
|
||||
|
||||
/** The target this particle is linked to. The particle will stretch to touch this entity. */
|
||||
@Nullable
|
||||
@@ -33,43 +38,83 @@ public abstract class ParticleTargeted extends ParticleWizardry {
|
||||
this.targetY = y;
|
||||
this.targetZ = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTargetVelocity(double vx, double vy, double vz){
|
||||
this.targetVelX = vx;
|
||||
this.targetVelY = vy;
|
||||
this.targetVelZ = vz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTargetEntity(Entity target){
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setLength(double length){
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
if(!Double.isNaN(targetVelX) && !Double.isNaN(targetVelY) && !Double.isNaN(targetVelZ)){
|
||||
this.targetX += this.targetVelX;
|
||||
this.targetY += this.targetVelY;
|
||||
this.targetZ += this.targetVelZ;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ,
|
||||
float rotationXY, float rotationXZ){
|
||||
|
||||
|
||||
// Copied from ParticleWizardry, needs to be here since we're not calling super
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks);
|
||||
|
||||
if(this.target != null){
|
||||
this.targetX = this.target.posX;
|
||||
this.targetY = this.target.getEntityBoundingBox().minY + target.height/2;
|
||||
this.targetZ = this.target.posZ;
|
||||
|
||||
this.targetX = this.target.prevPosX + (this.target.posX - this.target.prevPosX) * partialTicks;
|
||||
double correction = this.target.getEntityBoundingBox().minY - this.target.posY;
|
||||
this.targetY = this.target.prevPosY + (this.target.posY - this.target.prevPosY) * partialTicks
|
||||
+ target.height/2 + correction;
|
||||
this.targetZ = this.target.prevPosZ + (this.target.posZ - this.target.prevPosZ) * partialTicks;
|
||||
|
||||
}else if(this.entity != null && this.length > 0){
|
||||
|
||||
Vec3d look = entity.getLook(partialTicks).scale(length);
|
||||
this.targetX = x + look.x;
|
||||
this.targetY = y + look.y;
|
||||
this.targetZ = z + look.z;
|
||||
}
|
||||
|
||||
if(Double.isNaN(targetX) || Double.isNaN(targetY) || Double.isNaN(targetZ)){
|
||||
Wizardry.logger.warn("Attempted to render a targeted particle, but neither its target entity nor target"
|
||||
+ "position was set!");
|
||||
+ "position was set, and it either had no length assigned or was not linked to an entity!");
|
||||
return;
|
||||
}
|
||||
|
||||
// I'm pretty sure these were always static.
|
||||
interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks;
|
||||
interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks;
|
||||
interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks;
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(x, y, z);
|
||||
GlStateManager.translate(x - interpPosX, y - interpPosY, z - interpPosZ);
|
||||
|
||||
double dx = this.targetX - this.posX;
|
||||
double dy = this.targetY - this.posY;
|
||||
double dz = this.targetZ - this.posZ;
|
||||
double dx = this.targetX - x;
|
||||
double dy = this.targetY - y;
|
||||
double dz = this.targetZ - z;
|
||||
|
||||
// No need for previous tick target positions and all that stuff since this is the only place they're used
|
||||
// and interpolating like this works just as well
|
||||
if(!Double.isNaN(targetVelX) && !Double.isNaN(targetVelY) && !Double.isNaN(targetVelZ)){
|
||||
dx += partialTicks * this.targetVelX;
|
||||
dy += partialTicks * this.targetVelY;
|
||||
dz += partialTicks * this.targetVelZ;
|
||||
}
|
||||
|
||||
// The distance from origin to endpoint
|
||||
double length = Math.sqrt(dx*dx+dy*dy+dz*dz);
|
||||
@@ -87,12 +132,12 @@ public abstract class ParticleTargeted extends ParticleWizardry {
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
|
||||
/** Called from {@link ParticleTargeted#renderParticle(BufferBuilder, Entity, float, float, float, float, float, float)},
|
||||
* once the appropriate calculations and transformations have been applied, to actually render the particle. Subclasses
|
||||
* override this <i>instead</i> of overriding {@code renderParticle} directly, and inside render the particle <b>along
|
||||
* the z-axis, starting at (0, 0, 0)</b> - it will be translated and rotated automatically.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* <i>N.B. Other than transformations, no GL state changes are applied; these should be done within this method.</i>
|
||||
*
|
||||
* @param tessellator A reference to the tessellator, for convenience.
|
||||
|
||||
@@ -3,14 +3,13 @@ package electroblob.wizardry.client.particle;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.particle.ParticleDigging;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleTornado extends ParticleDigging {
|
||||
|
||||
private double angle;
|
||||
private float angle;
|
||||
private double radius;
|
||||
private double speed;
|
||||
/** Velocity of the tornado itself; in other words the velocity of the point the particle circles around. */
|
||||
@@ -20,9 +19,9 @@ public class ParticleTornado extends ParticleDigging {
|
||||
public ParticleTornado(World world, int maxAge, double originX, double originZ, double radius, double yPos,
|
||||
double velX, double velZ, IBlockState block){
|
||||
super(world, 0, 0, 0, 0, 0, 0, block);
|
||||
this.angle = this.rand.nextDouble() * Math.PI * 2;
|
||||
double x = originX - Math.cos(angle) * radius;
|
||||
double z = originZ + radius * Math.sin(angle);
|
||||
float angle = this.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = originX - MathHelper.cos(angle) * radius;
|
||||
double z = originZ + radius * MathHelper.sin(angle);
|
||||
this.radius = radius;
|
||||
this.setPosition(x, yPos, z);
|
||||
this.prevPosX = x;
|
||||
@@ -66,8 +65,8 @@ public class ParticleTornado extends ParticleDigging {
|
||||
// v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi.
|
||||
this.angle += omega;
|
||||
|
||||
this.motionZ = radius * omega * Math.cos(angle);
|
||||
this.motionX = radius * omega * Math.sin(angle);
|
||||
this.motionZ = radius * omega * MathHelper.cos(angle);
|
||||
this.motionX = radius * omega * MathHelper.sin(angle);
|
||||
this.move(motionX + velX, 0, motionZ + velZ);
|
||||
|
||||
if(this.particleAge > this.particleMaxAge / 2){
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleVine extends ParticleTargeted {
|
||||
|
||||
/** Half the width of the vine. */
|
||||
private static final float THICKNESS = 0.02f;
|
||||
private static final float LEAF_SPACING = 0.5f;
|
||||
private static final float SEGMENT_LENGTH = 1;
|
||||
|
||||
private static final ResourceLocation STEM_TEXTURE = new ResourceLocation(Wizardry.MODID, "particle/vine");
|
||||
private static final ResourceLocation[] LEAF_TEXTURES = generateTextures("vine_leaf", 5);
|
||||
|
||||
public ParticleVine(World world, double x, double y, double z){
|
||||
super(world, x, y, z, STEM_TEXTURE);
|
||||
//this.setRBGColorF(1, 1, 1);
|
||||
this.setMaxAge(0);
|
||||
this.particleScale = 1;
|
||||
this.setRBGColorF(0.2f, 0.65f, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){
|
||||
// When using FX layer 1 the BufferBuilder is already drawing... but in the wrong mode :/
|
||||
Tessellator.getInstance().draw();
|
||||
super.renderParticle(buffer, viewer, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw(Tessellator tessellator, double length, float partialTicks){
|
||||
|
||||
random.setSeed(seed); // Reset the random so we get the same sequence of numbers each frame
|
||||
|
||||
float scale = this.particleScale;
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
// Hmmmm we can't get the texture to tile using OpenGL texture space because it's on a sprite sheet...
|
||||
// Solution: Draw loads of boxes. Simple!
|
||||
// (Since there aren't going to be that many of these particles around we could have not used sprite sheets
|
||||
// and done the OpenGL texture space thing, but this is kinda easier)
|
||||
// Everything is drawn back-to-front so it looks like the vine is growing from the origin, not the endpoint
|
||||
int i = 0;
|
||||
while(i + SEGMENT_LENGTH < length){
|
||||
drawShearedBox(tessellator, 0, 0, length-i, 0, 0, length-i-SEGMENT_LENGTH, THICKNESS * scale,
|
||||
particleRed, particleGreen, particleBlue, particleAlpha);
|
||||
i += SEGMENT_LENGTH;
|
||||
}
|
||||
|
||||
drawShearedBox(tessellator, 0, 0, length-i, 0, 0, 0, THICKNESS * scale,
|
||||
particleRed, particleGreen, particleBlue, particleAlpha);
|
||||
|
||||
for(double l=length; l>0; l-=LEAF_SPACING){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.rotate(random.nextInt(4) * 90, 0, 0, 1);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
TextureAtlasSprite leaf = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(
|
||||
LEAF_TEXTURES[random.nextInt(LEAF_TEXTURES.length)].toString());
|
||||
|
||||
float w = 16 * THICKNESS * scale;
|
||||
float u1 = leaf.getMinU();
|
||||
float u2 = leaf.getMaxU();
|
||||
float v1 = leaf.getMinV();
|
||||
float v2 = leaf.getMaxV();
|
||||
|
||||
float colourVariation = 0.3f;
|
||||
|
||||
float r = MathHelper.clamp(particleRed + (random.nextFloat() - 0.5f) * colourVariation, 0, 1);
|
||||
float g = MathHelper.clamp(particleGreen + (random.nextFloat() - 0.5f) * colourVariation, 0, 1);
|
||||
float b = MathHelper.clamp(particleBlue + (random.nextFloat() - 0.5f) * colourVariation, 0, 1);
|
||||
|
||||
buffer.pos(0, 0, l).tex(u1, v1).color(r, g, b, particleAlpha).endVertex();
|
||||
buffer.pos(w, 0, l).tex(u2, v1).color(r, g, b, particleAlpha).endVertex();
|
||||
buffer.pos(w, w, l).tex(u2, v2).color(r, g, b, particleAlpha).endVertex();
|
||||
buffer.pos(0, w, l).tex(u1, v2).color(r, g, b, particleAlpha).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Makes the rain go weird
|
||||
//GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
/** Draws a single box for one segment of the arc, from the point (x1, y1, z1) to the point (x2, y2, z2), with given width and colour. */
|
||||
private void drawShearedBox(Tessellator tessellator, double x1, double y1, double z1, double x2, double y2, double z2, float width, float r, float g, float b, float a){
|
||||
|
||||
float u1 = particleTexture.getMinU();
|
||||
float u2 = u1 + (particleTexture.getMaxU() - u1) * (float)(z1-z2)/SEGMENT_LENGTH;
|
||||
float v1 = particleTexture.getMinV();
|
||||
float dv = particleTexture.getMaxV() - v1;
|
||||
// width * 8 gives the total 'circumference' of the box
|
||||
float v2 = v1 + dv * 0.0625f;
|
||||
float v3 = v1 + dv * 0.125f;
|
||||
float v4 = v1 + dv * 0.1875f;
|
||||
float v5 = v1 + dv * 0.25f;
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
buffer.pos(x1-width, y1-width, z1).tex(u1, v1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).tex(u2, v1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1+width, z1).tex(u1, v2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2+width, z2).tex(u2, v2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1+width, z1).tex(u1, v3).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2+width, z2).tex(u2, v3).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1-width, z1).tex(u1, v4).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2-width, z2).tex(u2, v4).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1-width, z1).tex(u1, v5).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).tex(u2, v5).color(r, g, b, a).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
|
||||
event.getMap().registerSprite(STEM_TEXTURE);
|
||||
|
||||
for(ResourceLocation texture : LEAF_TEXTURES){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +1,41 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.entity.ICustomHitbox;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Abstract superclass for all of wizardry's particles. This replaces {@code ParticleCustomTexture} (the functionality of
|
||||
* which is no longer necessary since wizardry now uses {@code TextureAtlasSprite}s to do the rendering), and fits into
|
||||
* {@code ParticleBuilder} by exposing all the necessary variables through getters, allowing them to be set on the fly
|
||||
* rather than needing to be passed into the constructor.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* The new system is as follows:
|
||||
* <p>
|
||||
* <p></p>
|
||||
* - All particle classes have a single constructor which takes a world and a position only.<br>
|
||||
* - Each particle class defines any relevant default values in its constructor, including velocity.<br>
|
||||
* - The particle builder then overwrites any other values that were set during building.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* This beauty of this system is that there are never any redundant parameters when spawning particles, since you can set
|
||||
* as many or as few parameters as necessary - and in addition, common defaults don't need setting at all. For example,
|
||||
* snow particles nearly always fall at the same speed, which can now be defined in the particle class and no longer
|
||||
@@ -39,12 +45,24 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
* @since Wizardry 4.2.0
|
||||
* @see electroblob.wizardry.util.ParticleBuilder ParticleBuilder
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public abstract class ParticleWizardry extends Particle {
|
||||
|
||||
/** Implementation of animated particles using the TextureAtlasSprite system. Why vanilla doesn't support this I
|
||||
* don't know, considering it too has animated particles. */
|
||||
protected final TextureAtlasSprite[] sprites;
|
||||
|
||||
/** A long value used by the renderer as a random number seed, ensuring anything that is randomised remains the
|
||||
* same across multiple frames. For example, lightning particles use this to keep their shape across ticks.
|
||||
* This value can also be set during particle creation, allowing users to keep randomised properties the same
|
||||
* even across multiple particles. If unspecified, the seed is chosen at random. */
|
||||
protected long seed;
|
||||
/** This particle's random number generator. All particles should use this in preference to any other random
|
||||
* instance (like random), even if it isn't actually necessary to keep properties across frames. Note that
|
||||
* if you <b>do</b> need to generate the same sequence of random numbers each frame, you must call
|
||||
* {@code random.setSeed(seed)} from the {@link ParticleWizardry#renderParticle(BufferBuilder, Entity, float, float, float, float, float, float)}
|
||||
* method - this is not done automatically. */
|
||||
protected Random random = new Random(); // If we're not using a seed, this defaults to any old seed
|
||||
|
||||
/** True if the particle is shaded, false if the particle always renders at full brightness. Defaults to false. */
|
||||
protected boolean shaded = false;
|
||||
@@ -64,12 +82,12 @@ public abstract class ParticleWizardry extends Particle {
|
||||
/** The entity this particle is linked to. The particle will move with this entity. */
|
||||
@Nullable
|
||||
protected Entity entity = null;
|
||||
/** Coordinates of this particle relative to the linked entity. If the linked entity is null, these are not used. */
|
||||
/** Coordinates of this particle relative to the linked entity. If the linked entity is null, these are used as
|
||||
* the absolute coordinates of the centre of rotation for particles with spin. If the particle has neither a
|
||||
* linked entity nor spin, these are not used. */
|
||||
protected double relativeX, relativeY, relativeZ;
|
||||
/** Velocity of this particle relative to the linked entity. The relative x and z velocities are also used when
|
||||
* the particle has spin to move the centre of rotation. If the linked entity is null and the particle is not
|
||||
* spinning, these are not used and will be {@code NaN}. */
|
||||
protected double relativeMotionX = Double.NaN, relativeMotionY = Double.NaN, relativeMotionZ = Double.NaN;
|
||||
/** Velocity of this particle relative to the linked entity. If the linked entity is null, these are not used. */
|
||||
protected double relativeMotionX, relativeMotionY, relativeMotionZ;
|
||||
// Note that roll (equivalent to rotating the texture) is effectively handled by particleAngle - although that is
|
||||
// actually the rotation speed and not the angle itself.
|
||||
/** The yaw angle this particle is facing, or {@code NaN} if this particle always faces the viewer (default behaviour). */
|
||||
@@ -77,6 +95,14 @@ public abstract class ParticleWizardry extends Particle {
|
||||
/** The pitch angle this particle is facing, or {@code NaN} if this particle always faces the viewer (default behaviour). */
|
||||
protected float pitch = Float.NaN;
|
||||
|
||||
/** The fraction of the impact velocity that should be the maximum spread speed added on impact. */
|
||||
private static final double SPREAD_FACTOR = 0.2;
|
||||
/** Lateral velocity is reduced by this factor on impact, before adding random spread velocity. */
|
||||
private static final double IMPACT_FRICTION = 0.2;
|
||||
|
||||
/** Previous-tick velocity, used in collision detection. */
|
||||
private double prevVelX, prevVelY, prevVelZ;
|
||||
|
||||
/**
|
||||
* Creates a new particle in the given world at the given position. All other parameters are set via the various
|
||||
* setter methods ({@link electroblob.wizardry.util.ParticleBuilder ParticleBuilder} deals with all of that anyway).
|
||||
@@ -108,7 +134,6 @@ public abstract class ParticleWizardry extends Particle {
|
||||
this.setParticleTexture(sprites[0]);
|
||||
|
||||
}else{
|
||||
|
||||
sprites = new TextureAtlasSprite[0];
|
||||
}
|
||||
}
|
||||
@@ -117,6 +142,15 @@ public abstract class ParticleWizardry extends Particle {
|
||||
|
||||
// Setters for parameters that affect all particles - these are implemented in this class (although they may be
|
||||
// reimplemented in subclasses)
|
||||
|
||||
/** Sets the seed for this particle's randomly generated values and resets {@link ParticleWizardry#random} to use
|
||||
* that seed. Implementations will differ between particle types; for example, ParticleLightning has an update
|
||||
* period which changes the seed every few ticks, whereas ParticleVine simply retains the same seed for its entire
|
||||
* lifetime. */
|
||||
public void setSeed(long seed){
|
||||
this.seed = seed;
|
||||
this.random = new Random(seed);
|
||||
}
|
||||
|
||||
/** Sets whether the particle should render at full brightness or not. True if the particle is shaded, false if
|
||||
* the particle always renders at full brightness. Defaults to false.*/
|
||||
@@ -154,13 +188,10 @@ public abstract class ParticleWizardry extends Particle {
|
||||
public void setSpin(double radius, double speed){
|
||||
this.radius = radius;
|
||||
this.speed = speed * 2 * Math.PI; // Converts rotations per tick into radians per tick for the trig functions
|
||||
this.angle = this.rand.nextFloat() * (float)Math.PI * 2; // Random start angle TODO: Perhaps this should be specified?
|
||||
this.angle = this.rand.nextFloat() * (float)Math.PI * 2; // Random start angle
|
||||
// Need to set the start position or the circle won't be centred on the correct position
|
||||
this.relativeX = radius * -MathHelper.cos(angle);
|
||||
this.relativeZ = radius * MathHelper.sin(angle);
|
||||
this.setPosition(posX + relativeX, posY, posZ + relativeZ);
|
||||
this.prevPosX = posX;
|
||||
this.prevPosZ = posZ;
|
||||
this.posX = relativeX - radius * MathHelper.cos(angle);
|
||||
this.posZ = relativeZ + radius * MathHelper.sin(angle);
|
||||
// Set these to the correct values
|
||||
this.relativeMotionX = motionX;
|
||||
this.relativeMotionY = motionY;
|
||||
@@ -180,6 +211,7 @@ public abstract class ParticleWizardry extends Particle {
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
// Set these to the correct values
|
||||
this.relativeMotionX = motionX;
|
||||
this.relativeMotionY = motionY;
|
||||
this.relativeMotionZ = motionZ;
|
||||
@@ -218,7 +250,7 @@ public abstract class ParticleWizardry extends Particle {
|
||||
|
||||
/**
|
||||
* Sets the direction this particle faces. This will cause the particle to render facing the given direction.
|
||||
* @param yaw The yaw angle of this particle in degrees, where 0 is [TODO south?].
|
||||
* @param yaw The yaw angle of this particle in degrees, where 0 is south.
|
||||
* @param pitch The pitch angle of this particle in degrees, where 0 is horizontal.
|
||||
*/
|
||||
public void setFacing(float yaw, float pitch){
|
||||
@@ -239,6 +271,18 @@ public abstract class ParticleWizardry extends Particle {
|
||||
public void setTargetPosition(double x, double y, double z){
|
||||
// Does nothing for normal particles since normal particles always render at a single point
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target point velocity for this particle. This will cause the position it stretches to touch to move
|
||||
* at the given velocity. Has no effect unless {@link ParticleWizardry#setTargetVelocity(double, double, double)}
|
||||
* is also used.
|
||||
* @param vx The x velocity of the target point.
|
||||
* @param vy The y velocity of the target point.
|
||||
* @param vz The z velocity of the target point.
|
||||
*/
|
||||
public void setTargetVelocity(double vx, double vy, double vz){
|
||||
// Does nothing for normal particles since normal particles always render at a single point
|
||||
}
|
||||
|
||||
/**
|
||||
* Links this particle to the given target. This will cause it to stretch to touch the target, if supported.
|
||||
@@ -247,6 +291,15 @@ public abstract class ParticleWizardry extends Particle {
|
||||
public void setTargetEntity(Entity target){
|
||||
// Does nothing for normal particles since normal particles always render at a single point
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of this particle. This will cause it to stretch to touch a point this distance along its
|
||||
* linked entity's line of sight.
|
||||
* @param length The length to set.
|
||||
*/
|
||||
public void setLength(double length){
|
||||
// Does nothing for normal particles since normal particles always render at a single point
|
||||
}
|
||||
|
||||
// ============================================== Method Overrides ==============================================
|
||||
|
||||
@@ -278,15 +331,16 @@ public abstract class ParticleWizardry extends Particle {
|
||||
* @param lookYZ Equal to {@code -lookZ} times the sine of {@code viewer.rotationPitch}. Will be 0 when facing directly horizontal.
|
||||
* When facing directly up, will be equal to {@code -lookZ}. When facing directly down, will be equal to {@code lookZ}.
|
||||
*/
|
||||
// Fun fact: unlike entities, particles don't seem to bother checking the camera frustum...
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float lookZ, float lookY,
|
||||
float lookX, float lookXY, float lookYZ){
|
||||
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
if(Float.isNaN(this.yaw) || Float.isNaN(this.pitch)){
|
||||
|
||||
// Normal behaviour (rotates to face the viewer)
|
||||
super.renderParticle(buffer, viewer, partialTicks, lookZ, lookY, lookX, lookXY, lookYZ);
|
||||
|
||||
drawParticle(buffer, viewer, partialTicks, lookZ, lookY, lookX, lookXY, lookYZ);
|
||||
}else{
|
||||
|
||||
// Specific rotation
|
||||
@@ -302,7 +356,25 @@ public abstract class ParticleWizardry extends Particle {
|
||||
float rotationYZ = -rotationZ * MathHelper.sin(pitch * degToRadFactor);
|
||||
float rotationXY = rotationX * MathHelper.sin(pitch * degToRadFactor);
|
||||
|
||||
super.renderParticle(buffer, viewer, partialTicks, rotationX, rotationY, rotationZ, rotationYZ, rotationXY);
|
||||
drawParticle(buffer, viewer, partialTicks, rotationX, rotationY, rotationZ, rotationYZ, rotationXY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate function for {@link ParticleWizardry#renderParticle(BufferBuilder, Entity, float, float, float, float, float, float)};
|
||||
* does the actual rendering. Subclasses should override this method instead of renderParticle. By default, this
|
||||
* method simply calls super.renderParticle.
|
||||
*/
|
||||
protected void drawParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationY, float rotationZ, float rotationYZ, float rotationXY){
|
||||
super.renderParticle(buffer, viewer, partialTicks, rotationX, rotationY, rotationZ, rotationYZ, rotationXY);
|
||||
}
|
||||
|
||||
protected void updateEntityLinking(float partialTicks){
|
||||
if(this.entity != null){
|
||||
// This is kind of cheating but we know it's always a constant velocity so it works fine
|
||||
prevPosX = posX + entity.prevPosX - entity.posX - relativeMotionX * (1-partialTicks);
|
||||
prevPosY = posY + entity.prevPosY - entity.posY - relativeMotionY * (1-partialTicks);
|
||||
prevPosZ = posZ + entity.prevPosZ - entity.posZ - relativeMotionZ * (1-partialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,38 +382,43 @@ public abstract class ParticleWizardry extends Particle {
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
// If any of these values is NaN, the particle has neither an entity nor a spin
|
||||
if(!Double.isNaN(relativeMotionX) && !Double.isNaN(relativeMotionY) && !Double.isNaN(relativeMotionZ)){
|
||||
|
||||
// This allows velocity changes from entity linking and spin to stack
|
||||
double vx = relativeMotionX;
|
||||
double vy = relativeMotionY;
|
||||
double vz = relativeMotionZ;
|
||||
|
||||
if(this.canCollide && this.onGround){
|
||||
// I reject your friction and substitute my own!
|
||||
this.motionX /= 0.699999988079071D;
|
||||
this.motionZ /= 0.699999988079071D;
|
||||
}
|
||||
|
||||
if(entity != null || radius > 0){
|
||||
|
||||
double x = relativeX;
|
||||
double y = relativeY;
|
||||
double z = relativeZ;
|
||||
|
||||
// Entity linking
|
||||
if(this.entity != null){
|
||||
|
||||
if(this.entity.isDead) this.setExpired();
|
||||
|
||||
this.setPosition(this.entity.posX + relativeX, this.entity.getEntityBoundingBox().minY + relativeY,
|
||||
this.entity.posZ + relativeZ);
|
||||
// Velocity is set so that the renderer will interpolate correctly between ticks
|
||||
vx += this.entity.motionX;
|
||||
vy += this.entity.motionY;
|
||||
vz += this.entity.motionZ;
|
||||
if(this.entity.isDead){
|
||||
this.setExpired();
|
||||
}else{
|
||||
x += this.entity.posX;
|
||||
y += this.entity.posY;
|
||||
z += this.entity.posZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Spin
|
||||
if(radius > 0){
|
||||
angle += speed;
|
||||
vx += radius * speed * MathHelper.sin(angle);
|
||||
vz += radius * speed * MathHelper.cos(angle);
|
||||
// If the particle has spin, x/z relative position is used as centre and coords are changed each tick
|
||||
x += radius * -MathHelper.cos(angle);
|
||||
z += radius * MathHelper.sin(angle);
|
||||
}
|
||||
|
||||
this.relativeX += vx;
|
||||
this.relativeY += vy;
|
||||
this.relativeZ += vz;
|
||||
|
||||
this.setPosition(x, y, z);
|
||||
|
||||
this.relativeX += relativeMotionX;
|
||||
this.relativeY += relativeMotionY;
|
||||
this.relativeZ += relativeMotionZ;
|
||||
}
|
||||
|
||||
// Colour fading
|
||||
@@ -357,8 +434,96 @@ public abstract class ParticleWizardry extends Particle {
|
||||
// (which would probably otherwise happen if particleAge == particleMaxAge)
|
||||
this.setParticleTexture(sprites[Math.min((int)(ageFraction * sprites.length), sprites.length - 1)]);
|
||||
}
|
||||
|
||||
// Collision spreading
|
||||
if(canCollide){
|
||||
|
||||
if(this.motionX == 0 && this.prevVelX != 0){ // If the particle just collided in x
|
||||
// Reduce lateral velocity so the added spread speed actually has an effect
|
||||
this.motionY *= IMPACT_FRICTION;
|
||||
this.motionZ *= IMPACT_FRICTION;
|
||||
// Add random velocity in y and z proportional to the impact velocity
|
||||
this.motionY += (rand.nextDouble()*2 - 1) * this.prevVelX * SPREAD_FACTOR;
|
||||
this.motionZ += (rand.nextDouble()*2 - 1) * this.prevVelX * SPREAD_FACTOR;
|
||||
}
|
||||
|
||||
if(this.motionY == 0 && this.prevVelY != 0){ // If the particle just collided in y
|
||||
// Reduce lateral velocity so the added spread speed actually has an effect
|
||||
this.motionX *= IMPACT_FRICTION;
|
||||
this.motionZ *= IMPACT_FRICTION;
|
||||
// Add random velocity in x and z proportional to the impact velocity
|
||||
this.motionX += (rand.nextDouble()*2 - 1) * this.prevVelY * SPREAD_FACTOR;
|
||||
this.motionZ += (rand.nextDouble()*2 - 1) * this.prevVelY * SPREAD_FACTOR;
|
||||
}
|
||||
|
||||
if(this.motionZ == 0 && this.prevVelZ != 0){ // If the particle just collided in z
|
||||
// Reduce lateral velocity so the added spread speed actually has an effect
|
||||
this.motionX *= IMPACT_FRICTION;
|
||||
this.motionY *= IMPACT_FRICTION;
|
||||
// Add random velocity in x and y proportional to the impact velocity
|
||||
this.motionX += (rand.nextDouble()*2 - 1) * this.prevVelZ * SPREAD_FACTOR;
|
||||
this.motionY += (rand.nextDouble()*2 - 1) * this.prevVelZ * SPREAD_FACTOR;
|
||||
}
|
||||
|
||||
double searchRadius = 20;
|
||||
|
||||
List<Entity> nearbyEntities = WizardryUtilities.getEntitiesWithinRadius(searchRadius, this.posX,
|
||||
this.posY, this.posZ, world, Entity.class);
|
||||
|
||||
nearbyEntities.removeIf(e -> !(e instanceof ICustomHitbox && ((ICustomHitbox)e).contains(new Vec3d(this.posX, this.posY, this.posZ))));
|
||||
|
||||
if(nearbyEntities.size() > 0) this.setExpired();
|
||||
|
||||
}
|
||||
|
||||
this.prevVelX = motionX;
|
||||
this.prevVelY = motionY;
|
||||
this.prevVelZ = motionZ;
|
||||
}
|
||||
|
||||
|
||||
// Overridden and copied to fix the collision behaviour
|
||||
@Override
|
||||
public void move(double x, double y, double z){
|
||||
|
||||
double origY = y;
|
||||
double origX = x;
|
||||
double origZ = z;
|
||||
|
||||
if(this.canCollide){
|
||||
|
||||
List<AxisAlignedBB> list = this.world.getCollisionBoxes(null, this.getBoundingBox().expand(x, y, z));
|
||||
|
||||
for(AxisAlignedBB axisalignedbb : list){
|
||||
y = axisalignedbb.calculateYOffset(this.getBoundingBox(), y);
|
||||
}
|
||||
|
||||
this.setBoundingBox(this.getBoundingBox().offset(0.0D, y, 0.0D));
|
||||
|
||||
for(AxisAlignedBB axisalignedbb1 : list){
|
||||
x = axisalignedbb1.calculateXOffset(this.getBoundingBox(), x);
|
||||
}
|
||||
|
||||
this.setBoundingBox(this.getBoundingBox().offset(x, 0.0D, 0.0D));
|
||||
|
||||
for(AxisAlignedBB axisalignedbb2 : list){
|
||||
z = axisalignedbb2.calculateZOffset(this.getBoundingBox(), z);
|
||||
}
|
||||
|
||||
this.setBoundingBox(this.getBoundingBox().offset(0.0D, 0.0D, z));
|
||||
|
||||
}else{
|
||||
this.setBoundingBox(this.getBoundingBox().offset(x, y, z));
|
||||
}
|
||||
|
||||
this.resetPositionToBB();
|
||||
this.onGround = origY != y && origY < 0.0D;
|
||||
|
||||
if(origX != x) this.motionX = 0.0D;
|
||||
if(origY != y) this.motionY = 0.0D; // Why doesn't Particle do this for y?
|
||||
if(origZ != z) this.motionZ = 0.0D;
|
||||
}
|
||||
|
||||
|
||||
// =============================================== Helper Methods ===============================================
|
||||
|
||||
/** Static helper method that generates an array of n ResourceLocations using the particle file naming convention,
|
||||
@@ -390,6 +555,21 @@ public abstract class ParticleWizardry extends Particle {
|
||||
return textures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the given {@link ResourceLocation} with the given {@link IWizardryParticleFactory}, allowing it to
|
||||
* be used in the {@link electroblob.wizardry.util.ParticleBuilder ParticleBuilder}. This is a similar concept to
|
||||
* registering entity renderers, in that it associates the client-only bit with its common-code counterpart - but
|
||||
* of course, particles are client-side only so a simple identifier is all that is necessary. As with entity
|
||||
* renderers, <b>this method may only be called from the client side</b>, probably a client proxy.
|
||||
* @param name The {@link ResourceLocation} to use for the particle. This effectively replaces the particle type
|
||||
* enum from previous versions. Keep a reference to this somewhere in <b>common</b> code for use later.
|
||||
* @param factory A {@link IWizardryParticleFactory} that produces your particle. A constructor reference is usually
|
||||
* sufficient.
|
||||
*/
|
||||
public static void registerParticle(ResourceLocation name, IWizardryParticleFactory factory){
|
||||
ClientProxy.addParticleFactory(name, factory);
|
||||
}
|
||||
|
||||
/** Simple particle factory interface which takes a world and a position and returns a particle. Used (via method
|
||||
* references) in the client proxy to link particle enum types to actual particle classes. */
|
||||
@SideOnly(Side.CLIENT)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.model.ModelPlayer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||
import net.minecraft.client.renderer.entity.RenderPlayer;
|
||||
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Layer used to render the frost texture on a creature with the frostbite effect. Handles dynamic tiling of the texture.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
public class LayerFrost implements LayerRenderer<EntityLivingBase> {
|
||||
|
||||
protected ModelBase model;
|
||||
private final RenderLivingBase<?> renderer;
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/frost_overlay.png");
|
||||
|
||||
public static void initialiseLayers(){
|
||||
|
||||
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
|
||||
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
|
||||
if(renderer instanceof RenderLivingBase){
|
||||
// Adds a frost layer to all the living entity renderers in the game. Whether it is actually rendered
|
||||
// is decided in doRenderLayer below on a per-entity basis.
|
||||
((RenderLivingBase<?>)renderer).addLayer(new LayerFrost((RenderLivingBase<?>)renderer));
|
||||
}
|
||||
}
|
||||
|
||||
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
|
||||
renderer.addLayer(new LayerFrost(renderer));
|
||||
}
|
||||
}
|
||||
|
||||
public LayerFrost(RenderLivingBase<?> renderer){
|
||||
this.renderer = renderer;
|
||||
this.model = renderer.getMainModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||
|
||||
if(entity.isPotionActive(WizardryPotions.frost) || entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)){
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
int i = this.getBlockBrightnessForEntity(entity, partialTicks);
|
||||
|
||||
int j = i % 65536;
|
||||
int k = i / 65536;
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F);
|
||||
|
||||
// Frost texture
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
this.renderer.bindTexture(texture);
|
||||
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
|
||||
headPitch, scale);
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private int getBlockBrightnessForEntity(Entity entity, float partialTicks){
|
||||
|
||||
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor(entity.posX), 0,
|
||||
MathHelper.floor(entity.posZ));
|
||||
|
||||
if(entity.world.isBlockLoaded(pos)){
|
||||
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
|
||||
return entity.world.getCombinedLight(pos, 0);
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderEntityModel(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
// Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam)
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
|
||||
GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth)
|
||||
// In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason.
|
||||
|
||||
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
|
||||
// but with translation instead of scaling.
|
||||
// NOTE: You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
double scaleX = 1, scaleY = 1;
|
||||
// It's more logical to use the model's texture size, but some classes don't bother setting it properly
|
||||
// (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead.
|
||||
if(model.boxList != null && model.boxList.get(0) != null){
|
||||
scaleX = (double)model.boxList.get(0).textureWidth / 16d;
|
||||
scaleY = (double)model.boxList.get(0).textureHeight / 16d;
|
||||
}else{ // Fallback to model fields; should never be needed
|
||||
scaleX = (double)model.textureWidth / 16d;
|
||||
scaleY = (double)model.textureHeight / 16d;
|
||||
}
|
||||
GlStateManager.scale(scaleX, scaleY, 1);
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
// Hides the hat layer for bipeds
|
||||
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = true;
|
||||
|
||||
if(this.model instanceof ModelPlayer){
|
||||
((ModelPlayer)this.model).bipedBodyWear.isHidden = true;
|
||||
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = true;
|
||||
((ModelPlayer)this.model).bipedRightArmwear.isHidden = true;
|
||||
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = true;
|
||||
((ModelPlayer)this.model).bipedRightLegwear.isHidden = true;
|
||||
}
|
||||
|
||||
this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
|
||||
this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
|
||||
|
||||
if(this.model instanceof ModelBiped) ((ModelBiped)this.model).bipedHeadwear.isHidden = false;
|
||||
|
||||
// Undoes the texture scaling
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCombineTextures(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -21,6 +17,7 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Layer used to render the stone texture on a petrified creature. Handles dynamic tiling of the stone texture.
|
||||
@@ -36,16 +33,14 @@ public class LayerStone implements LayerRenderer<EntityLivingBase> {
|
||||
private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.png");
|
||||
|
||||
public static void initialiseLayers(){
|
||||
for(Entry<Class<? extends Entity>, Render<? extends Entity>> entry : Minecraft.getMinecraft()
|
||||
.getRenderManager().entityRenderMap.entrySet()){
|
||||
|
||||
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
|
||||
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
|
||||
if(entry.getValue() instanceof RenderLivingBase){
|
||||
if(renderer instanceof RenderLivingBase){
|
||||
// Adds a stone layer to all the living entity renderers in the game. Whether it is actually rendered
|
||||
// is decided in doRenderLayer below on a per-entity basis.
|
||||
((RenderLivingBase<?>)entry.getValue()).addLayer(new LayerStone((RenderLivingBase<?>)entry.getValue()));
|
||||
((RenderLivingBase<?>)renderer).addLayer(new LayerStone((RenderLivingBase<?>)renderer));
|
||||
}
|
||||
// NOTE: May have to do some special stuff for players if they are to be added; see
|
||||
// Minecraft.getMinecraft().getRenderManager().getSkinMap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,12 +50,13 @@ public class LayerStone implements LayerRenderer<EntityLivingBase> {
|
||||
}
|
||||
|
||||
// FIXME: Does not work with zombie pigmen, I have no idea why.
|
||||
// I believe the issue is with the TESR actually, since LayerFrost works fine
|
||||
|
||||
@Override
|
||||
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
|
||||
float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||
|
||||
if(entity.getEntityData().getBoolean(BlockStatue.NBT_KEY)){
|
||||
if(entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)){
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
int i = this.getBlockBrightnessForEntity(entity, partialTicks);
|
||||
@@ -108,7 +104,6 @@ public class LayerStone implements LayerRenderer<EntityLivingBase> {
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
// Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam)
|
||||
// TODO: Backport this improvement
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.entity.living.EntityStrayMinion;
|
||||
import net.minecraft.client.model.ModelSkeleton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.entity.RenderLivingBase;
|
||||
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/** Had to copy this entire class just because of one unnecessarily specific type parameter. The type parameter has been
|
||||
* changed to {@code EntityStrayMinion} and parameter types updated accordingly. Everything else is identical. */
|
||||
public class LayerStrayMinionClothing implements LayerRenderer<EntityStrayMinion> {
|
||||
|
||||
private static final ResourceLocation STRAY_CLOTHES_TEXTURES = new ResourceLocation("textures/entity/skeleton/stray_overlay.png");
|
||||
private final RenderLivingBase<?> renderer;
|
||||
private final ModelSkeleton layerModel = new ModelSkeleton(0.25F, true);
|
||||
|
||||
public LayerStrayMinionClothing(RenderLivingBase<?> renderer){
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
public void doRenderLayer(EntityStrayMinion entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale){
|
||||
this.layerModel.setModelAttributes(this.renderer.getMainModel());
|
||||
this.layerModel.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.renderer.bindTexture(STRAY_CLOTHES_TEXTURES);
|
||||
this.layerModel.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
|
||||
}
|
||||
|
||||
public boolean shouldCombineTextures(){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.spell.ArcaneLock;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class RenderArcaneLock {
|
||||
|
||||
private static final ResourceLocation[] textures = new ResourceLocation[8];
|
||||
|
||||
static {
|
||||
for(int i=0; i<textures.length; i++){
|
||||
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/arcane_lock_" + i + ".png");
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderWorldLastEvent(RenderWorldLastEvent event){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Vec3d origin = player.getPositionEyes(event.getPartialTicks());
|
||||
GlStateManager.translate(-origin.x, -origin.y + player.getEyeHeight(), -origin.z);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(textures[(player.ticksExisted % (textures.length * 2))/2]);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
for(TileEntity tileentity : world.loadedTileEntityList){
|
||||
|
||||
if(tileentity.getDistanceSq(origin.x, origin.y, origin.z) <= 64*64 && tileentity.getTileData().hasUniqueId(ArcaneLock.NBT_KEY)){
|
||||
|
||||
Vec3d[] vertices = WizardryUtilities.getVertices(world.getBlockState(tileentity.getPos()).getBoundingBox(world, tileentity.getPos()).grow(0.05).offset(tileentity.getPos()));
|
||||
|
||||
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0, 0, 1, 1); // Bottom
|
||||
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0, 0, 1, 1); // South
|
||||
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0, 1, 1); // East
|
||||
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0, 0, 1, 1); // North
|
||||
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0, 0, 1, 1); // West
|
||||
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0, 0, 1, 1); // Top
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableRescaleNormal();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
private static void drawFace(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float u1, float v1, float u2, float v2){
|
||||
buffer.pos(topLeft.x, topLeft.y, topLeft.z).tex(u1, v1).endVertex();
|
||||
buffer.pos(topRight.x, topRight.y, topRight.z).tex(u2, v1).endVertex();
|
||||
buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).tex(u2, v2).endVertex();
|
||||
buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).tex(u1, v2).endVertex();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
@@ -15,6 +13,8 @@ import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderArcaneWorkbench extends TileEntitySpecialRenderer<TileEntityArcaneWorkbench> {
|
||||
|
||||
@@ -30,19 +30,19 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer<TileEntityA
|
||||
GlStateManager.translate((float)x + 0.5F, (float)y + 1.5F, (float)z + 0.5F);
|
||||
GlStateManager.rotate(180, 0F, 0F, 1F);
|
||||
GlStateManager.pushMatrix();
|
||||
double angle = 0.0d;
|
||||
double angle;
|
||||
if(x < -0.5){
|
||||
angle = Math.toDegrees(Math.atan((z + 0.5) / (x + 0.5))) + 180;
|
||||
}else{
|
||||
angle = Math.toDegrees(Math.atan((z + 0.5) / (x + 0.5)));
|
||||
}
|
||||
this.renderEffect(tileentity);
|
||||
this.renderWand(tileentity, angle);
|
||||
this.renderEffect(tileentity, partialTicks);
|
||||
this.renderWand(tileentity, angle, partialTicks);
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
private void renderEffect(TileEntityArcaneWorkbench tileentity){
|
||||
private void renderEffect(TileEntityArcaneWorkbench tileentity, float partialTicks){
|
||||
|
||||
ItemStack itemstack = tileentity.getStackInSlot(ContainerArcaneWorkbench.CENTRE_SLOT);
|
||||
|
||||
@@ -52,7 +52,7 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer<TileEntityA
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); // This line fixes the weird brightness bug.
|
||||
GlStateManager.rotate(tileentity.timer, 0.0f, 1.0f, 0.0f);
|
||||
GlStateManager.rotate(tileentity.timer + partialTicks, 0.0f, 1.0f, 0.0f);
|
||||
GlStateManager.translate(0.0f, 0.65f, 0.0f);
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
@@ -76,7 +76,8 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer<TileEntityA
|
||||
*
|
||||
* @param tileentity The instance of the workbench tile entity
|
||||
*/
|
||||
private void renderWand(TileEntityArcaneWorkbench tileentity, double viewAngle){
|
||||
private void renderWand(TileEntityArcaneWorkbench tileentity, double viewAngle, float partialTicks){
|
||||
|
||||
ItemStack stack = tileentity.getStackInSlot(ContainerArcaneWorkbench.CENTRE_SLOT);
|
||||
|
||||
if(!stack.isEmpty()){
|
||||
@@ -87,8 +88,8 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer<TileEntityA
|
||||
|
||||
GlStateManager.rotate(180, 0, 1, 0);
|
||||
GlStateManager.rotate((float)(viewAngle - 90f), 0, 0, 1);
|
||||
// Does the floaty thing
|
||||
GlStateManager.translate(0.0F, 0.0F, (float)tileentity.yOffset / 5000.0F + 0.55f);
|
||||
// Does the floaty thing <- #bestcommentever
|
||||
GlStateManager.translate(0.0F, 0.0F, 0.56f + 0.05f * MathHelper.sin((tileentity.timer + partialTicks)/15));//(float)tileentity.yOffset / 5000.0F + 0.55f);
|
||||
GlStateManager.scale(0.75F, 0.75F, 0.75F);
|
||||
// This is what the item frame uses so it's definitely what we want.
|
||||
Minecraft.getMinecraft().getRenderItem().renderItem(stack, TransformType.FIXED);
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.construct.EntityBlackHole;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class RenderBlackHole extends Render<EntityBlackHole> {
|
||||
|
||||
@@ -81,21 +77,21 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
|
||||
|
||||
int sliceAngle = 20 + a;
|
||||
|
||||
double x1 = scale * Math.sin((blackhole.ticksExisted + 40 * j) * (Math.PI / 180));
|
||||
// double y1 = 0.7*Math.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
|
||||
double z1 = scale * Math.cos((blackhole.ticksExisted + 40 * j) * (Math.PI / 180));
|
||||
double x1 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j) * ((float)Math.PI / 180f));
|
||||
// double y1 = 0.7*MathHelper.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
|
||||
double z1 = scale * MathHelper.cos((blackhole.ticksExisted + 40 * j) * ((float)Math.PI / 180));
|
||||
|
||||
double x2 = scale * Math.sin((blackhole.ticksExisted + 40 * j - sliceAngle) * (Math.PI / 180));
|
||||
// double y2 = 0.7*Math.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
|
||||
double z2 = scale * Math.cos((blackhole.ticksExisted + 40 * j - sliceAngle) * (Math.PI / 180));
|
||||
double x2 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
|
||||
// double y2 = 0.7*MathHelper.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
|
||||
double z2 = scale * MathHelper.cos((blackhole.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
|
||||
|
||||
double absoluteX = x1 * Math.cos(31 * b);
|
||||
double absoluteY = z1 * Math.sin(31 * a) + x1 * Math.cos(31 * a) * Math.sin(31 * b);
|
||||
double absoluteZ = z1 * Math.cos(31 * a);
|
||||
double absoluteX = x1 * MathHelper.cos(31 * b);
|
||||
double absoluteY = z1 * MathHelper.sin(31 * a) + x1 * MathHelper.cos(31 * a) * MathHelper.sin(31 * b);
|
||||
double absoluteZ = z1 * MathHelper.cos(31 * a);
|
||||
|
||||
double absoluteX2 = x2 * Math.cos(31 * b);
|
||||
double absoluteY2 = z2 * Math.sin(31 * a) + x2 * Math.cos(31 * a) * Math.sin(31 * b);
|
||||
double absoluteZ2 = z2 * Math.cos(31 * a);
|
||||
double absoluteX2 = x2 * MathHelper.cos(31 * b);
|
||||
double absoluteY2 = z2 * MathHelper.sin(31 * a) + x2 * MathHelper.cos(31 * a) * MathHelper.sin(31 * b);
|
||||
double absoluteZ2 = z2 * MathHelper.cos(31 * a);
|
||||
/* buffer.begin(0, DefaultVertexFormats.POSITION_TEX);
|
||||
*
|
||||
* tessellator.setColorOpaque(255, 255, 255); GL11.glPointSize(5);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.construct.EntityBubble;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
@@ -14,6 +12,7 @@ import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderBubble extends Render<EntityBubble> {
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.potion.PotionContainment;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class RenderContainmentField {
|
||||
|
||||
private static final ResourceLocation[] textures = new ResourceLocation[8];
|
||||
|
||||
private static final float ANIMATION_SPEED = 0.004f;
|
||||
private static final float FADE_DISTANCE_SQUARED = 15;
|
||||
|
||||
static {
|
||||
for(int i=0; i<textures.length; i++){
|
||||
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/containment_field_" + i + ".png");
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderWorldLastEvent(RenderWorldLastEvent event){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.containment)){
|
||||
|
||||
Vec3d centre = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(player.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)));
|
||||
float r = PotionContainment.getContainmentDistance(player.getActivePotionEffect(WizardryPotions.containment).getAmplifier());
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH); // Allows the vertex colours to produce a gradient
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam)
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
|
||||
// Translate the texture in the 2D space (like the creeper charge layer)
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
float distance = (player.ticksExisted + event.getPartialTicks()) * ANIMATION_SPEED;
|
||||
GlStateManager.translate(distance, distance, 0);
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(textures[(player.ticksExisted % (textures.length * 2))/2]);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
|
||||
// The aim here is to have only the bits of the containment field near the player be visible (if any).
|
||||
// There's probably a really neat, advanced way of doing this with OpenGL (stencil buffer...?) but in the
|
||||
// absence of OpenGL skills, I've devised a cheat which kinda achieves the effect we want:
|
||||
|
||||
// - Split each face of the cube into four, where the split point is taken orthogonal to the player (the
|
||||
// player is taken as (0, 0, 0) for simplicity, unlike most render classes where the entire thing is
|
||||
// translated first)
|
||||
// - Then have the alpha value for each vertex change based on its distance from the player
|
||||
// - N.B. OpenGL renders quads as two triangles, with the 1st and 3rd vertices forming the line between them.
|
||||
// This causes weirdness in the interpolation since the interpolation isn't taking the whole square into
|
||||
// account. The order of the vertices below ensures that the seams between the triangles form an X shape
|
||||
// on each of the faces, meaning that you always see a square shape appear rather than a random polygon.
|
||||
|
||||
Vec3d relative = centre.subtract(player.getPositionEyes(event.getPartialTicks())).add(0, player.getEyeHeight(), 0);
|
||||
|
||||
double x1 = relative.x - r;
|
||||
double y1 = relative.y - r;
|
||||
double z1 = relative.z - r;
|
||||
double x2 = relative.x + r;
|
||||
double y2 = relative.y + r;
|
||||
double z2 = relative.z + r;
|
||||
|
||||
float alpha = Math.min(1, player.getActivePotionEffect(WizardryPotions.containment).getDuration() / 40f);
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
// The following code is mostly an incomprehensible mess of numbers, but unfortunately without a lot of
|
||||
// fancy vector stuff it has to be like this, and besides, this is faster!
|
||||
|
||||
// Bottom
|
||||
drawVertex(buffer, x1, y1, z1, 0, 0, alpha);
|
||||
drawVertex(buffer, x1, y1, 0, 0, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y1, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y1, z1, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, 0, y1, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, x2, y1, 0, 2*r, -(float)z1, alpha);
|
||||
drawVertex(buffer, x2, y1, z1, 2*r, 0, alpha);
|
||||
drawVertex(buffer, 0, y1, z1, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, x1, y1, z2, 0, 2*r, alpha);
|
||||
drawVertex(buffer, 0, y1, z2, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, 0, y1, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, x1, y1, 0, 0, -(float)z1, alpha);
|
||||
|
||||
drawVertex(buffer, 0, y1, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y1, z2, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, x2, y1, z2, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, x2, y1, 0, 2*r, -(float)z1, alpha);
|
||||
|
||||
// Top
|
||||
drawVertex(buffer, x1, y2, z1, 0, 0, alpha);
|
||||
drawVertex(buffer, 0, y2, z1, -(float)x1, 0, alpha);
|
||||
drawVertex(buffer, 0, y2, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, x1, y2, 0, 0, -(float)z1, alpha);
|
||||
|
||||
drawVertex(buffer, x2, y2, z1, 2*r, 0, alpha);
|
||||
drawVertex(buffer, x2, y2, 0, 2*r, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y2, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y2, z1, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, 0, y2, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, 0, y2, z2, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, x1, y2, z2, 0, 2*r, alpha);
|
||||
drawVertex(buffer, x1, y2, 0, 0, -(float)z1, alpha);
|
||||
|
||||
drawVertex(buffer, 0, y2, 0, -(float)x1, -(float)z1, alpha);
|
||||
drawVertex(buffer, x2, y2, 0, 2*r, -(float)z1, alpha);
|
||||
drawVertex(buffer, x2, y2, z2, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, 0, y2, z2, -(float)x1, 2*r, alpha);
|
||||
|
||||
// North
|
||||
drawVertex(buffer, x1, y1, z1, 0, 0, alpha);
|
||||
drawVertex(buffer, 0, y1, z1, -(float)x1, 0, alpha);
|
||||
drawVertex(buffer, 0, 0, z1, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, 0, z1, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, x2, y1, z1, 2*r, 0, alpha);
|
||||
drawVertex(buffer, x2, 0, z1, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, 0, z1, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, y1, z1, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, 0, 0, z1, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, y2, z1, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, x1, y2, z1, 0, 2*r, alpha);
|
||||
drawVertex(buffer, x1, 0, z1, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, 0, 0, z1, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, 0, z1, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, y2, z1, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, 0, y2, z1, -(float)x1, 2*r, alpha);
|
||||
|
||||
// South
|
||||
drawVertex(buffer, x1, y1, z2, 0, 0, alpha);
|
||||
drawVertex(buffer, x1, 0, z2, 0, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, 0, z2, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, y1, z2, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, 0, 0, z2, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, 0, z2, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, y1, z2, 2*r, 0, alpha);
|
||||
drawVertex(buffer, 0, y1, z2, -(float)x1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, x1, y2, z2, 0, 2*r, alpha);
|
||||
drawVertex(buffer, 0, y2, z2, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, 0, 0, z2, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, 0, z2, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, 0, 0, z2, -(float)x1, -(float)y1, alpha);
|
||||
drawVertex(buffer, 0, y2, z2, -(float)x1, 2*r, alpha);
|
||||
drawVertex(buffer, x2, y2, z2, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, x2, 0, z2, 2*r, -(float)y1, alpha);
|
||||
|
||||
// West
|
||||
drawVertex(buffer, x1, y1, z1, 0, 0, alpha);
|
||||
drawVertex(buffer, x1, 0, z1, 0, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, y1, 0, -(float)z1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, x1, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, 0, z2, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, y1, z2, 2*r, 0, alpha);
|
||||
drawVertex(buffer, x1, y1, 0, -(float)z1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, x1, y2, z1, 0, 2*r, alpha);
|
||||
drawVertex(buffer, x1, y2, 0, -(float)z1, 2*r, alpha);
|
||||
drawVertex(buffer, x1, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, 0, z1, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, x1, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x1, y2, 0, -(float)z1, 2*r, alpha);
|
||||
drawVertex(buffer, x1, y2, z2, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, x1, 0, z2, 2*r, -(float)y1, alpha);
|
||||
|
||||
// East
|
||||
drawVertex(buffer, x2, y1, z1, 0, 0, alpha);
|
||||
drawVertex(buffer, x2, y1, 0, -(float)z1, 0, alpha);
|
||||
drawVertex(buffer, x2, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, 0, z1, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, x2, y1, z2, 2*r, 0, alpha);
|
||||
drawVertex(buffer, x2, 0, z2, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, y1, 0, -(float)z1, 0, alpha);
|
||||
|
||||
drawVertex(buffer, x2, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, y2, 0, -(float)z1, 2*r, alpha);
|
||||
drawVertex(buffer, x2, y2, z1, 0, 2*r, alpha);
|
||||
drawVertex(buffer, x2, 0, z1, 0, -(float)y1, alpha);
|
||||
|
||||
drawVertex(buffer, x2, 0, 0, -(float)z1, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, 0, z2, 2*r, -(float)y1, alpha);
|
||||
drawVertex(buffer, x2, y2, z2, 2*r, 2*r, alpha);
|
||||
drawVertex(buffer, x2, y2, 0, -(float)z1, 2*r, alpha);
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
// Undoes the texture translation
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableRescaleNormal();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawVertex(BufferBuilder buffer, double x, double y, double z, float u, float v, float alpha){
|
||||
float f = MathHelper.clamp(1 - (float)(x*x + y*y + z*z) / FADE_DISTANCE_SQUARED, 0, 1);
|
||||
buffer.pos(x, y, z).tex(u, v).color(1, 1, 1, f * alpha).endVertex();
|
||||
}
|
||||
|
||||
// Useful to keep this mapping in case I ever want to draw cubes again
|
||||
|
||||
// Vec3d[] vertices = WizardryUtilities.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector().scale(-1)));
|
||||
//
|
||||
// // Outside
|
||||
// drawFace(tessellator, vertices[0], vertices[1], vertices[3], vertices[2], u1, v1, u2, v2); // Bottom
|
||||
// drawFace(tessellator, vertices[6], vertices[7], vertices[2], vertices[3], u1, v1, u2, v2); // South
|
||||
// drawFace(tessellator, vertices[5], vertices[6], vertices[1], vertices[2], u1, v1, u2, v2); // East
|
||||
// drawFace(tessellator, vertices[4], vertices[5], vertices[0], vertices[1], u1, v1, u2, v2); // North
|
||||
// drawFace(tessellator, vertices[7], vertices[4], vertices[3], vertices[0], u1, v1, u2, v2); // West
|
||||
// drawFace(tessellator, vertices[5], vertices[4], vertices[6], vertices[7], u1, v1, u2, v2); // Top
|
||||
//
|
||||
// // Inside
|
||||
// drawFace(tessellator, vertices[1], vertices[0], vertices[2], vertices[3], u1, v1, u2, v2); // Bottom
|
||||
// drawFace(tessellator, vertices[7], vertices[6], vertices[3], vertices[2], u1, v1, u2, v2); // South
|
||||
// drawFace(tessellator, vertices[6], vertices[5], vertices[2], vertices[1], u1, v1, u2, v2); // East
|
||||
// drawFace(tessellator, vertices[5], vertices[4], vertices[1], vertices[0], u1, v1, u2, v2); // North
|
||||
// drawFace(tessellator, vertices[4], vertices[7], vertices[0], vertices[3], u1, v1, u2, v2); // West
|
||||
// drawFace(tessellator, vertices[4], vertices[5], vertices[7], vertices[6], u1, v1, u2, v2); // Top
|
||||
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.construct.EntityDecay;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
@@ -12,6 +10,7 @@ import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderDecay extends Render<EntityDecay> {
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.living.EntityDecoy;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
@@ -12,10 +9,11 @@ import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.ReflectionHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class RenderDecoy extends RenderBiped<EntityDecoy> {
|
||||
|
||||
private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png");
|
||||
|
||||
@@ -7,10 +7,8 @@ import net.minecraft.client.renderer.entity.RenderBiped;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.entity.layers.LayerBipedArmor;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class RenderEvilWizard extends RenderBiped<EntityEvilWizard> {
|
||||
static final ResourceLocation[] textures = new ResourceLocation[6];
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.entity.construct.EntityFireRing;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
@@ -15,6 +13,7 @@ import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderFireRing extends Render<EntityFireRing> {
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.projectile.EntityForceArrow;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
@@ -13,10 +11,9 @@ import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class RenderForceArrow extends Render<EntityForceArrow> {
|
||||
|
||||
private static final ResourceLocation arrowTextures = new ResourceLocation(Wizardry.MODID,
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package electroblob.wizardry.client.renderer;
|
||||
|
||||
import electroblob.wizardry.entity.construct.EntityForcefield;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class RenderForcefield extends Render<EntityForcefield> {
|
||||
|
||||
private static final float EXPANSION_TIME = 3;
|
||||
|
||||
public RenderForcefield(RenderManager renderManager){
|
||||
super(renderManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRender(EntityForcefield entity, double x, double y, double z, float yaw, float partialTicks){
|
||||
|
||||
// For now we're just using a UV sphere
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(x, y, z);
|
||||
|
||||
float latStep = (float)Math.PI/20;
|
||||
float longStep = (float)Math.PI/20;
|
||||
|
||||
float pulse = MathHelper.sin((entity.ticksExisted + partialTicks)/10f);
|
||||
|
||||
float r = 0.35f, g = 0.55f + 0.05f * pulse, b = 1;
|
||||
|
||||
float radius = entity.getRadius();
|
||||
float a = 0.5f;
|
||||
|
||||
if(entity.ticksExisted > entity.lifetime - EXPANSION_TIME){
|
||||
radius *= 1 + 0.2f * (entity.ticksExisted + partialTicks - (entity.lifetime - EXPANSION_TIME))/EXPANSION_TIME;
|
||||
a *= Math.max(0, 1 - (entity.ticksExisted + partialTicks - (entity.lifetime - EXPANSION_TIME))/EXPANSION_TIME);
|
||||
}else if(entity.ticksExisted < EXPANSION_TIME){
|
||||
radius *= 1 - (EXPANSION_TIME - entity.ticksExisted - partialTicks)/EXPANSION_TIME;
|
||||
a *= 1 - (EXPANSION_TIME - entity.ticksExisted - partialTicks)/EXPANSION_TIME;
|
||||
}
|
||||
|
||||
// Draw the inside first
|
||||
drawSphere(radius - 0.1f - 0.025f * pulse, latStep, longStep, true, r, g, b, a);
|
||||
drawSphere(radius - 0.1f - 0.025f * pulse, latStep, longStep, false, 1, 1, 1, a);
|
||||
drawSphere(radius, latStep, longStep, false, r, g, b, 0.7f * a);
|
||||
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceLocation getEntityTexture(EntityForcefield entity){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a sphere (using lat/long triangles) with the given parameters.
|
||||
* @param radius The radius of the sphere.
|
||||
* @param latStep The latitude step; smaller is smoother but increases performance cost.
|
||||
* @param longStep The longitude step; smaller is smoother but increases performance cost.
|
||||
* @param inside Whether to draw the outside or the inside of the sphere.
|
||||
* @param r The red component of the sphere colour.
|
||||
* @param g The green component of the sphere colour.
|
||||
* @param b The blue component of the sphere colour.
|
||||
* @param a The alpha component of the sphere colour.
|
||||
*/
|
||||
private static void drawSphere(float radius, float latStep, float longStep, boolean inside, float r, float g, float b, float a){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
|
||||
|
||||
boolean goingUp = inside;
|
||||
|
||||
buffer.pos(0, goingUp ? -radius : radius, 0).color(r, g, b, a).endVertex(); // Start at the north pole
|
||||
|
||||
for(float longitude = -(float)Math.PI; longitude <= (float)Math.PI; longitude += longStep){
|
||||
|
||||
// Leave the poles out since they only have a single point per stack instead of two
|
||||
for(float theta = (float)Math.PI/2 - latStep; theta >= -(float)Math.PI/2 + latStep; theta -= latStep){
|
||||
|
||||
float latitude = goingUp ? -theta : theta;
|
||||
|
||||
float hRadius = radius * MathHelper.cos(latitude);
|
||||
float vy = radius * MathHelper.sin(latitude);
|
||||
float vx = hRadius * MathHelper.sin(longitude);
|
||||
float vz = hRadius * MathHelper.cos(longitude);
|
||||
|
||||
buffer.pos(vx, vy, vz).color(r, g, b, a).endVertex();
|
||||
|
||||
vx = hRadius * MathHelper.sin(longitude + longStep);
|
||||
vz = hRadius * MathHelper.cos(longitude + longStep);
|
||||
|
||||
buffer.pos(vx, vy, vz).color(r, g, b, a).endVertex();
|
||||
}
|
||||
|
||||
// The next pole
|
||||
buffer.pos(0, goingUp ? radius : -radius, 0).color(r, g, b, a).endVertex();
|
||||
|
||||
goingUp = !goingUp;
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,11 +19,13 @@ public class RenderHammer extends Render<EntityHammer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRender(EntityHammer entity, double x, double y, double z, float f, float f1){
|
||||
public void doRender(EntityHammer entity, double x, double y, double z, float yaw, float partialTicks){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.translate(x, y + 1.5, z);
|
||||
GlStateManager.rotate(180, 0F, 0F, 1F);
|
||||
GlStateManager.rotate(yaw, 0, 1, 0);
|
||||
GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks, 0, 0, 1);
|
||||
|
||||
this.bindTexture(texture);
|
||||
|
||||
|
||||
@@ -7,10 +7,8 @@ import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.entity.RenderLiving;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class RenderIceGiant extends RenderLiving<EntityIceGiant> {
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user