Merge branch '4.3-dev' into 1.12.2

This commit is contained in:
Electroblob77
2020-10-23 15:14:53 +01:00
1141 changed files with 29925 additions and 6978 deletions
@@ -2,9 +2,14 @@ package electroblob.wizardry.client;
import electroblob.wizardry.CommonProxy;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.client.animation.ActionAnimation;
import electroblob.wizardry.client.animation.PlayerAnimator;
import electroblob.wizardry.client.audio.MovingSoundEntity;
import electroblob.wizardry.client.audio.MovingSoundSpellCharge;
import electroblob.wizardry.client.audio.SoundLoop;
import electroblob.wizardry.client.audio.SoundLoopSpell;
import electroblob.wizardry.client.gui.GuiLectern;
import electroblob.wizardry.client.gui.GuiSpellDisplay;
import electroblob.wizardry.client.gui.config.NamedBooleanEntry;
import electroblob.wizardry.client.gui.config.SpellHUDSkinChooserEntry;
@@ -12,8 +17,12 @@ 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.*;
import electroblob.wizardry.client.renderer.entity.*;
import electroblob.wizardry.client.renderer.entity.layers.*;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.client.renderer.tileentity.*;
import electroblob.wizardry.command.SpellEmitter;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.data.SpellGlyphData;
@@ -32,10 +41,7 @@ import electroblob.wizardry.packet.*;
import electroblob.wizardry.registry.Spells;
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.tileentity.*;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.WandHelper;
@@ -72,6 +78,8 @@ 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.client.settings.KeyConflictContext;
import net.minecraftforge.client.settings.KeyModifier;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry;
@@ -79,6 +87,7 @@ import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import org.lwjgl.input.Keyboard;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
@@ -100,8 +109,16 @@ public class ClientProxy extends CommonProxy {
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);
public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", Keyboard.KEY_B, "key.categories." + Wizardry.MODID);
public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_N, "key.categories." + Wizardry.MODID);
public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_B, "key.categories." + Wizardry.MODID);
public static final KeyBinding[] SPELL_QUICK_ACCESS = new KeyBinding[ItemWand.BASE_SPELL_SLOTS + Constants.UPGRADE_STACK_LIMIT];
static {
for(int i = 0; i < SPELL_QUICK_ACCESS.length; i++){
SPELL_QUICK_ACCESS[i] = new KeyBinding("key." + Wizardry.MODID + ".spell_" + (i+1),
KeyConflictContext.IN_GAME, KeyModifier.ALT, Keyboard.KEY_1 + i, "key.categories." + Wizardry.MODID);
}
}
// Armour Model
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
@@ -109,7 +126,7 @@ public class ClientProxy extends CommonProxy {
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style, Object...)}). */
private static final int TOOLTIP_WRAP_WIDTH = 140;
// SECTION Registry
// Registry
// ===============================================================================================================
@Override
@@ -121,6 +138,7 @@ public class ClientProxy extends CommonProxy {
public void registerKeyBindings(){
ClientRegistry.registerKeyBinding(NEXT_SPELL);
ClientRegistry.registerKeyBinding(PREVIOUS_SPELL);
for(KeyBinding key : SPELL_QUICK_ACCESS) ClientRegistry.registerKeyBinding(key);
}
@Override
@@ -128,7 +146,7 @@ public class ClientProxy extends CommonProxy {
mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, new ResourceLocation("textures/font/ascii.png"),
Minecraft.getMinecraft().renderEngine, false);
}
@Override
public void registerResourceReloadListeners(){
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
@@ -147,14 +165,14 @@ public class ClientProxy extends CommonProxy {
WizardryAntiqueAtlasIntegration.registerMarkers();
}
// SECTION Misc
// Misc
// ===============================================================================================================
@Override
public void setToNumberSliderEntry(Property property){
property.setConfigEntryClass(NumberSliderEntry.class);
}
@Override
public void setToHUDChooserEntry(Property property){
property.setConfigEntryClass(SpellHUDSkinChooserEntry.class);
@@ -171,32 +189,23 @@ public class ClientProxy extends CommonProxy {
}
@Override
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));
public EntityPlayer getThePlayer(){
return Minecraft.getMinecraft().player;
}
@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));
}
public boolean isFirstPerson(Entity entity){
return entity == Minecraft.getMinecraft().getRenderViewEntity() && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0;
}
@Override
public void playBlinkEffect(EntityPlayer player){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.playBlinkEffect();
if(Minecraft.getMinecraft().player == player) RenderBlinkEffect.playBlinkEffect();
}
@Override
public void shakeScreen(EntityPlayer player, float intensity){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.shakeScreen(intensity);
if(Minecraft.getMinecraft().player == player) ScreenShakeHandler.shakeScreen(intensity);
}
@Override
@@ -211,17 +220,61 @@ public class ClientProxy extends CommonProxy {
return GuiSpellDisplay.getSkinKeys();
}
// SECTION Items
@Override
public void notifyBookshelfChange(World world, BlockPos pos){
super.notifyBookshelfChange(world, pos);
EntityPlayer player = Minecraft.getMinecraft().player;
if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){
if(Minecraft.getMinecraft().currentScreen instanceof GuiLectern){
((GuiLectern)Minecraft.getMinecraft().currentScreen).refreshAvailableSpells();
}
}
}
// Sound
// ===============================================================================================================
@Override
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
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 playChargeupSound(EntityLivingBase entity){
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundSpellCharge(entity, WizardrySounds.ITEM_WAND_CHARGEUP, WizardrySounds.SPELLS, 2.5f, 1.4f, false));
}
@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));
}
}
// Items
// ===============================================================================================================
@Override
public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){
EntityPlayerSP player = Minecraft.getMinecraft().player;
if(player == null) return false;
// Displayed recipe
// Weirdly, the gui is actually the only way of accessing the current IMerchant or their recipes, other than a
// brute-force search through all the entities in the world to find the merchant interacting with the player
// Since we only need this client-side anyway, we might as well go via the gui
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
@@ -298,12 +351,16 @@ public class ClientProxy extends CommonProxy {
}
@Override
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
String description = style.getFormattingCode() + I18n.format(key, args);
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
public String translate(String key, Style style, Object... args){
return style.getFormattingCode() + I18n.format(key, args);
}
// SECTION Particles
@Override
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(translate(key, style, args), TOOLTIP_WRAP_WIDTH));
}
// Particles
// ===============================================================================================================
/** Use {@link ParticleWizardry#registerParticle(ResourceLocation, IWizardryParticleFactory)}, this is internal. */
@@ -316,10 +373,13 @@ public class ClientProxy extends CommonProxy {
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.BLOCK_HIGHLIGHT, ParticleBlockHighlight::new);
ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new);
ParticleWizardry.registerParticle(Type.CLOUD, ParticleCloud::new);
ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new);
ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new);
ParticleWizardry.registerParticle(Type.FLASH, ParticleFlash::new);
ParticleWizardry.registerParticle(Type.GUARDIAN_BEAM, ParticleGuardianBeam::new);
ParticleWizardry.registerParticle(Type.ICE, ParticleIce::new);
ParticleWizardry.registerParticle(Type.LEAF, ParticleLeaf::new);
ParticleWizardry.registerParticle(Type.LIGHTNING, ParticleLightning::new);
@@ -332,10 +392,9 @@ public class ClientProxy extends CommonProxy {
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(ResourceLocation type, World world, double x, double y, double z){
IWizardryParticleFactory factory = factories.get(type);
@@ -352,7 +411,7 @@ public class ClientProxy extends CommonProxy {
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));// , world.rand.nextInt(6)));
}
// SECTION Packet Handlers
// Packet Handlers
// ===============================================================================================================
@Override
@@ -455,72 +514,74 @@ public class ClientProxy extends CommonProxy {
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){
World world = Minecraft.getMinecraft().world;
Entity caster = world.getEntityByID(message.casterID);
BlockPos pos = message.destination;
if(caster == null) return; // Shouldn't happen
Entity entity = world.getEntityByID(message.dismountEntityID);
if(message.dismountEntityID != -1 && entity != null) entity.dismountRidingEntity();
// Moved from when the packet is sent to when it is received; fixes the sound not playing in first person.
caster.playSound(WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, 1, 1);
// Changed to a position to avoid syncing issues
world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, WizardrySounds.SPELLS, 1, 1, false);
for(int i = 0; i < 20; i++){
double radius = 1;
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 * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + 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;
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 * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + 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;
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 * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle);
world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0);
}
}
@@ -639,7 +700,7 @@ public class ClientProxy extends CommonProxy {
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
}
// SECTION Rendering
// Rendering
// ===============================================================================================================
private static final ResourceLocation ICE_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_wraith.png");
@@ -650,8 +711,17 @@ public class ClientProxy extends CommonProxy {
@Override
public void initialiseLayers(){
LayerStone.initialiseLayers();
LayerFrost.initialiseLayers();
LayerTiledOverlay.initialiseLayers(LayerStone::new);
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
LayerTiledOverlay.initialiseLayers(LayerMindControl::new);
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
LayerTiledOverlay.initialiseLayers(LayerDisintegrateAnimation::new);
}
@Override
public void initialiseAnimations(){
PlayerAnimator.init();
ActionAnimation.register();
}
@Override
@@ -695,7 +765,9 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityDart.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/dart.png"), false, 8.0, 2.0, 16, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityIceLance.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, true));
new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, false));
RenderingRegistry.registerEntityRenderingHandler(EntityFlamecatcherArrow.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/flamecatcher_arrow.png"), false, 8, 2.0, 16, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
@@ -705,6 +777,7 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new);
RenderingRegistry.registerEntityRenderingHandler(EntityRemnant.class, RenderRemnant::new);
// Throwables
RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class,
@@ -743,6 +816,11 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new);
RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new);
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new);
RenderingRegistry.registerEntityRenderingHandler(EntityZombieSpawner.class, RenderZombieSpawner::new);
RenderingRegistry.registerEntityRenderingHandler(EntityRadiantTotem.class, RenderRadiantTotem::new);
RenderingRegistry.registerEntityRenderingHandler(EntityBoulder.class, RenderBoulder::new);
RenderingRegistry.registerEntityRenderingHandler(EntityWitheringTotem.class, RenderWitheringTotem::new);
RenderingRegistry.registerEntityRenderingHandler(EntityIceBarrier.class, RenderIceBarrier::new);
//RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new);
// Stuff that doesn't render
@@ -754,26 +832,29 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityStormcloud.class, RenderBlank::new);
// Runes on ground
RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 5.0f, false));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 0.3f, false));
RenderingRegistry.registerEntityRenderingHandler(EntityFireSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityFrostSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityLightningSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class,
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f));
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png")));
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));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/combustion_rune.png"), 0, true));
// TESRs
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityLectern.class, new RenderLectern());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar());
}
}
@@ -9,9 +9,10 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
import java.util.Random;
/**
* Utility class containing some useful static methods for drawing GUIs. Previously these were spread across the main
* {@code WizardryUtilities} class and various individual GUI classes.
* Utility class containing some useful static methods for drawing GUIs, as well as general rendering.
*
* @author Electroblob
* @since Wizardry 4.2
@@ -124,6 +125,29 @@ public final class DrawingUtils {
tessellator.draw();
}
/**
* Draws a 'glitch' rectangle, with some rows of pixels shifted randomly to give a broken effect.
*
* @param random A random number generator to use
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
* @param flipX Whether to flip the texture in the x direction.
* @param flipY Whether to flip the texture in the y direction.
*/
public static void drawGlitchRect(Random random, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight, boolean flipX, boolean flipY){
for(int i=0; i<height; i++){
if(flipY) i = height - i - 1;
int offset = random.nextInt(4) == 0 ? random.nextInt(6) - 3 : 0;
drawTexturedFlippedRect(x + offset, y + i, u, v + i, width, 1, textureWidth, textureHeight, flipX, flipY);
}
}
/**
* Mixes the two given opaque colours in the proportion specified.
* @param colour1 The first colour to mix, as a 6-digit hexadecimal.
@@ -156,7 +180,7 @@ public final class DrawingUtils {
* @return The resulting integer colour code, which will be an 8-digit hexadecimal.
*/
public static int makeTranslucent(int colour, float opacity){
return colour + ((int)(0xff * opacity * 0x01000000));
return colour + ((int)(opacity * 0xff) << 24);
}
/**
@@ -244,4 +268,37 @@ public final class DrawingUtils {
RenderHelper.enableStandardItemLighting();
}
/**
* Calculates a factor between 0 and 1 that results in a smooth, aesthetically-pleasing animation when used to scale
* things. Mainly for rendering, but can be used anywhere since it's just a mathematical formula.
* @param lifetime The lifetime of the thing being animated, in ticks (if this is negative, disappearing is ignored)
* @param ticksExisted The current age of the thing being animated, in ticks
* @param partialTicks The current partial tick time
* @param startLength The length of the appearing animation, in ticks
* @param endLength The length of the disappearing animation, in ticks
* @return A fraction between 0 and 1, with the value at the very start and end being 0 and the constant middle
* section having a value of 1.
*/
public static float smoothScaleFactor(int lifetime, int ticksExisted, float partialTicks, int startLength, int endLength){
float age = ticksExisted + partialTicks;
float s = MathHelper.clamp(age < startLength || lifetime < 0 ? age/startLength : (lifetime - age) / endLength, 0, 1);
s = (float)Math.pow(s, 0.4); // Smooths the animation
return s;
}
/**
* Tests if the point with the given coordinates lies within the rectangle specified. The check is identical to
* {@link GuiContainer#isPointInRegion(int, int, int, int, int, int)}, but without the adjustment relative to the GUI.
* @param left The minimum x coordinate of the rectangle
* @param top The minimum y coordinate of the rectangle
* @param width The width of the rectangle
* @param height The height of the rectangle
* @param x The x coordinate of the point to test
* @param y The y coordinate of the point to test
* @return True if the given point is in the rectangle, false otherwise
*/
public static boolean isPointInRegion(int left, int top, int width, int height, int x, int y){
return x >= left - 1 && x < left + width + 1 && y >= top - 1 && y < top + height + 1;
}
}
@@ -0,0 +1,42 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
@EventBusSubscriber(Side.CLIENT)
public class ScreenShakeHandler {
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
if(Wizardry.settings.screenShake){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(Wizardry.settings.screenShake){
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
}else{
screenShakeCounter = 0;
}
}
}
}
@@ -1,41 +1,35 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockMagicLight;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
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.ItemFlamecatcher;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.*;
import electroblob.wizardry.util.RayTracer;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.spell.Possession;
import electroblob.wizardry.spell.SixthSense;
import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMerchant;
import net.minecraft.client.gui.ScaledResolution;
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.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.world.World;
import net.minecraftforge.client.event.*;
import net.minecraftforge.client.event.FOVUpdateEvent;
import net.minecraftforge.client.event.InputUpdateEvent;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -46,8 +40,9 @@ import java.util.ArrayList;
import java.util.List;
/**
* Event handler responsible for client-side only events, mostly rendering.
*
* General-purpose client-side event handler for things that don't fit anywhere else or groups of related behaviours
* that are better kept together.
*
* @author Electroblob
* @since Wizardry 1.0
*/
@@ -55,67 +50,19 @@ import java.util.List;
@Mod.EventBusSubscriber(Side.CLIENT)
public final class WizardryClientEventHandler {
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");
private static final ResourceLocation blinkOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_overlay.png");
private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png");
private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png");
private WizardryClientEventHandler(){} // No instances!
/** The remaining time for which the blink screen overlay effect will be displayed in first-person. Since this is
* only for the first-person player (the instance of which is itself stored in a static variable), this can simply
* be stored statically here, rather than needing to be in {@code WizardData}. */
private static int blinkEffectTimer;
/** The number of ticks the blink effect lasts for. */
private static final int BLINK_EFFECT_DURATION = 8;
// Tick Events
// ===============================================================================================================
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the first-person blink overlay effect. */
public static void playBlinkEffect(){
if(Wizardry.settings.blinkEffect) blinkEffectTimer = BLINK_EFFECT_DURATION;
}
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
if(Wizardry.settings.screenShake){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(Wizardry.settings.blinkEffect){
if(blinkEffectTimer > 0) blinkEffectTimer--;
}else{
blinkEffectTimer = 0;
}
if(Wizardry.settings.screenShake){
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
}else{
screenShakeCounter = 0;
}
// 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){
if(Minecraft.getMinecraft().entityRenderer.getShaderGroup() != null){ // IntelliJ is wrong, this can be null
String activeShader = Minecraft.getMinecraft().entityRenderer.getShaderGroup().getShaderGroupName();
@@ -124,41 +71,16 @@ public final class WizardryClientEventHandler {
|| (activeShader.equals(Transience.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.transience))){
if(activeShader.equals(SixthSense.SHADER.toString())
|| activeShader.equals(Transience.SHADER.toString())) playBlinkEffect();
|| activeShader.equals(Transience.SHADER.toString())) RenderBlinkEffect.playBlinkEffect();
Minecraft.getMinecraft().entityRenderer.stopUseShader();
}
}
}
}
@SubscribeEvent
public static void onRenderHandEvent(RenderHandEvent event){
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
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;
}
}
// The classes referenced here used to handle their own client tick events, but since they're not client-only
// classes, that crashed the dedicated server...
@SubscribeEvent
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
@@ -183,10 +105,13 @@ public final class WizardryClientEventHandler {
PotionSlowTime.cleanUpEntities(world);
}
}
// Input and Controls
// ===============================================================================================================
@SubscribeEvent
public static void onMouseEvent(MouseEvent event){
// Prevents the player looking around when paralysed
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.paralysis)
&& Minecraft.getMinecraft().inGameHasFocus){
@@ -198,267 +123,76 @@ public final class WizardryClientEventHandler {
}
}
// This event is called every tick, not just when a movement key is pressed
@SubscribeEvent
public static void onFOVUpdateEvent(FOVUpdateEvent event){
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive() && event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow){
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
float maxUseSeconds = (float)maxUseTicks / 20.0F;
if(maxUseSeconds > 1.0F){
maxUseSeconds = 1.0F;
}else{
maxUseSeconds = maxUseSeconds * maxUseSeconds;
}
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
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;
}
if(blinkEffectTimer > 0){
float f = ((float)Math.max(blinkEffectTimer - 2, 0))/BLINK_EFFECT_DURATION;
event.setNewfov(event.getFov() + f * f * 0.7f);
if(ItemArtefact.isArtefactActive(event.getEntityPlayer(), WizardryItems.charm_move_speed)
&& event.getEntityPlayer().isHandActive()
&& event.getEntityPlayer().getActiveItemStack().getItem() instanceof ItemWand){
// Normally speed is set to 20% when using items, this makes it 80%
event.getMovementInput().moveStrafe *= 4;
event.getMovementInput().moveForward *= 4;
}
}
// Rendering
// ===============================================================================================================
@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){
public static void onRenderHandEvent(RenderHandEvent event){
if((!(event.getPlayer().getHeldItemMainhand().getItem() instanceof ISpellCastingItem)
&& !(event.getPlayer().getHeldItemOffhand().getItem() instanceof ISpellCastingItem))
|| !ItemArtefact.isArtefactActive(event.getPlayer(), WizardryItems.charm_light)){
// Hide the player's empty hand in first-person when possessing
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
if(victim != null){
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
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){
public static void onFOVUpdateEvent(FOVUpdateEvent event){
if(event.getGuiContainer() instanceof GuiMerchant){
GuiMerchant gui = (GuiMerchant)event.getGuiContainer();
// Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard.
// Using == the specific item rather than instanceof because that's how trades do it.
if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book
|| gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){
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);
// 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.
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
gui.getSlotUnderMouse() == slot);
}
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive()){
Item item = event.getEntity().getActiveItemStack().getItem();
if(item instanceof ItemSpectralBow || item instanceof ItemFlamecatcher){
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
float maxUseSeconds = (float)maxUseTicks / (item instanceof ItemFlamecatcher ? ItemFlamecatcher.DRAW_TIME : 20);
if(maxUseSeconds > 1.0F){
maxUseSeconds = 1.0F;
}else{
maxUseSeconds = maxUseSeconds * maxUseSeconds;
}
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
}
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
Minecraft mc = Minecraft.getMinecraft();
WizardData data = WizardData.get(mc.player);
RenderManager renderManager = event.getRenderer().getRenderManager();
ItemStack wand = mc.player.getHeldItemMainhand();
if(!(wand.getItem() instanceof ISpellCastingItem)){
wand = mc.player.getHeldItemOffhand();
}
// Target selection pointer
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 = RayTracer.standardEntityRayTrace(mc.world, mc.player, 16, false);
if(rayTrace != null && rayTrace.entityHit == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(targetPointerTexture);
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
// Summoned creature selection pointer
if(data != null && data.selectedMinion != null && data.selectedMinion.get() == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(pointerTexture);
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
// 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) < 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();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(sixthSenseTexture);
buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex();
buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex();
buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex();
buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
@SubscribeEvent
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
renderScreenOverlay(event, sixthSenseOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.frost)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
renderScreenOverlay(event, frostOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
if(blinkEffectTimer > 0){
float alpha = ((float)blinkEffectTimer)/BLINK_EFFECT_DURATION;
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, alpha);
GlStateManager.disableAlpha();
renderScreenOverlay(event, blinkOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
}
}
private static void renderScreenOverlay(RenderGameOverlayEvent.Post event, ResourceLocation texture){
/**
* Renders an overlay across the entire screen.
* @param resolution The screen resolution
* @param texture The overlay texture to display
*/
public static void renderScreenOverlay(ScaledResolution resolution, ResourceLocation texture){
GlStateManager.pushMatrix();
@@ -471,11 +205,12 @@ public final class WizardryClientEventHandler {
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex();
buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D)
.endVertex();
buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex();
buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex();
buffer.pos(0, resolution.getScaledHeight(), -90).tex(0, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), resolution.getScaledHeight(), -90).tex(1, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), 0, -90).tex(1, 0).endVertex();
buffer.pos(0, 0, -90).tex(0, 0).endVertex();
tessellator.draw();
GlStateManager.depthMask(true);
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.gui.GuiSpellDisplay;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.packet.PacketControlInput;
import electroblob.wizardry.packet.PacketSpellQuickAccess;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.WizardrySounds;
import net.minecraft.client.Minecraft;
@@ -24,6 +25,7 @@ public class WizardryControlHandler {
static boolean NkeyPressed = false;
static boolean BkeyPressed = false;
static boolean[] quickAccessKeyPressed = new boolean[ClientProxy.SPELL_QUICK_ACCESS.length];
// 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!
@@ -59,6 +61,19 @@ public class WizardryControlHandler {
}else{
BkeyPressed = false;
}
for(int i = 0; i < ClientProxy.SPELL_QUICK_ACCESS.length; i++){
if(ClientProxy.SPELL_QUICK_ACCESS[i].isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
if(!quickAccessKeyPressed[i]){
quickAccessKeyPressed[i] = true;
// Packet building
selectSpell(wand, i);
}
}else{
quickAccessKeyPressed[i] = false;
}
}
}
}
}
@@ -118,4 +133,17 @@ public class WizardryControlHandler {
GuiSpellDisplay.playSpellSwitchAnimation(false);
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
}
private static void selectSpell(ItemStack wand, int index){
// GUI switch animation
if(((ISpellCastingItem)wand.getItem()).selectSpell(wand, index)){ // Makes sure the spell is set immediately for the client
// Packet building (no point sending it unless the client-side spell selection succeeded
IMessage msg = new PacketSpellQuickAccess.Message(index);
WizardryPacketHandler.net.sendToServer(msg);
GuiSpellDisplay.playSpellSwitchAnimation(true); // This will do, it's only an animation
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
}
}
}
@@ -0,0 +1,155 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.spell.Grapple;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.InventoryUtils;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
/**
* An animation that is associated with a particular {@link net.minecraft.item.EnumAction}. This animation will display
* when a player is using an item with that action.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions
*/
public abstract class ActionAnimation extends Animation {
private final EnumAction action;
public ActionAnimation(EnumAction action){
super(action.name());
this.action = action;
}
@Override
public boolean shouldDisplay(EntityPlayer player, boolean firstPerson){
return !firstPerson && player.getItemInUseMaxCount() > 0 && player.getActiveItemStack().getItemUseAction() == action;
}
/** Called from the client proxy to register all of wizardry's action animations. */
public static void register(){
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
// Something (probably some sort of race condition) causes getActiveItemStack to sometimes be wrong
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
float pitch = (float)Math.toRadians(player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks);
float yaw = (float)Math.toRadians(player.prevRotationYawHead + (player.rotationYawHead - player.prevRotationYawHead) * partialTicks)
- (float)Math.toRadians(player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks);
float x = -((float)Math.PI / 2) + pitch + 0.2f;
float y = (side == EnumHandSide.RIGHT ? -0.25f : 0.25f) + yaw;
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_UP){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-2.2f, side == EnumHandSide.RIGHT ? 0.2f : -0.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_DOWN){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float y = Math.min(0.4f + tick * 0.05f, 0.7f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-0.2f, side == EnumHandSide.RIGHT ? y : -y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.SUMMON){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float x = -Math.min(0.4f + tick * 0.2f, 2f);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.RIGHT)).addRotation(x, 1.2f, 0);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.LEFT)).addRotation(x, -1.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.GRAPPLE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
WizardData data = WizardData.get(player);
RayTraceResult hit = data.getVariable(Grapple.TARGET_KEY);
if(hit == null || hit.typeOfHit == RayTraceResult.Type.MISS) return;
Vec3d target = hit.hitVec;
if(hit.entityHit instanceof EntityLivingBase){
// If the target is an entity, we need to use the entity's centre rather than the original hit position
// because the entity will have moved!
target = GeometryUtils.getCentre(hit.entityHit);
}
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
Vec3d direction = target.subtract(player.getPositionEyes(partialTicks));
float yaw = player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks;
float pitch = (float)MathHelper.atan2(MathHelper.sqrt(direction.x*direction.x + direction.z*direction.z), direction.y);
float x = pitch - (float)Math.PI * 0.9f;
float y = -(float)Math.toRadians(yaw) - (float)MathHelper.atan2(direction.x, direction.z);
y += (side == EnumHandSide.RIGHT ? -0.25f : 0.25f);
if(Math.abs(pitch) < 0.2f) y = arm.rotateAngleY;
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.IMBUE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float z = Math.max(1.5f - tick * 0.1f, 0.8f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-1.2f, side == EnumHandSide.RIGHT ? -0.2f : 0.2f, side == EnumHandSide.RIGHT ? z : -z);
if(!player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
((ModelRendererExtended)getArmForSide(model, side.opposite())).addRotation(-0.8f, side == EnumHandSide.LEFT ? 0.3f : -0.3f, 0);
}
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.THRUST){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
float y = side == EnumHandSide.RIGHT ? -0.6f : 0.6f;
arm.addRotation(-1.2f, y, 0);
if(player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
ModelRendererExtended otherArm = (ModelRendererExtended)getArmForSide(model, side.opposite());
otherArm.setRotation(arm.rotateAngleX - 1.2f, -arm.rotateAngleY - y, otherArm.rotateAngleZ);
}
}
});
}
}
@@ -0,0 +1,77 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumHandSide;
/**
* Represents a player animation. Use one of the predefined subclasses below for common animation types, or extend this
* class directly to define a completely custom animation. Register instances of this class using
* {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see ActionAnimation
*/
public abstract class Animation {
private final String name;
public Animation(String name){
this.name = name;
}
/** Returns the name of this animation, currently only used for warning messages / debugging. */
public String getName(){
return name;
}
/**
* Returns whether this animation should be displayed for the given player. <i>Note that if two registered
* animations have overlapping conditions, both will attempt to display simultaneously, so if they both animate the
* same model part, that part may not behave as expected.</i>
* @param player The player being animated
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True if the animation should be displayed, false if not.
*/
public abstract boolean shouldDisplay(EntityPlayer player, boolean firstPerson);
/**
* Sets the rotation of the model parts for this animation. This method is called every time the player is rendered
* when {@link Animation#shouldDisplay(EntityPlayer, boolean)} returns true.
* @param player The player being animated
* @param model The model to animate. All of the standard {@link ModelBiped} parts will already be wrapped and may
* be safely cast to {@code ModelRendererExtended} in order to override the rotations set by the model
* itself - see {@link ModelRendererExtended ModelRendererExtended} for details.
* @param partialTicks The current partial tick time
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
*/
public abstract void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson);
/**
* Returns whether the boxes that form the second layer of the player's skin (for models that are instances of
* {@link net.minecraft.client.model.ModelPlayer ModelPlayer}) should automatically be set to the same angles as
* their corresponding first-layer parts.
* @param player The player being rendered, for reference
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True to let {@link PlayerAnimator} auto-rotate the second skin layer, false to rotate them manually.
*/
public boolean autoRotateSecondLayer(EntityPlayer player, boolean firstPerson){
return true;
}
/**
* Returns the arm of the given model corresponding to the given side. Function is identical to the method of the
* same name in {@link ModelBiped}.
* @param model The model to get the arm of
* @param side The {@link EnumHandSide} to return the arm for
* @return The {@link ModelRenderer} corresponding to the given arm
*/
public static ModelRenderer getArmForSide(ModelBiped model, EnumHandSide side){
return side == EnumHandSide.LEFT ? model.bipedLeftArm : model.bipedRightArm;
}
}
@@ -0,0 +1,176 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
/**
* A wrapper around {@link ModelRenderer} which can be swapped into models to allow render pre/post events to
* circumvent {@code ModelBase.setRotationAngles(...)} and set model part rotations themselves. Instances of this class
* keep a reference to the original {@code ModelRenderer} and delegate all the actual rendering to it.
* <p></p>
* {@link PlayerAnimator PlayerAnimator} does all the necessary setup with this
* class for player animations. It is also possible to set up custom animations for non-player entities with this class,
* see {@link ModelRendererExtended#wrap(ModelBiped)} and {@link ModelRendererExtended#wrap(ModelBase, ModelRenderer)}.
* @author Electroblob
* @since Wizardry 4.3
*/
public class ModelRendererExtended extends ModelRenderer {
// We probably could have copied all the fields over instead, but this way if anyone else modifies the boxes we
// won't overwrite their changes
// This way also avoids reflection because we don't need to copy private fields, which is better for performance
private ModelRenderer delegate;
private float actualRotationX;
private float actualRotationY;
private float actualRotationZ;
private float extraRotationX;
private float extraRotationY;
private float extraRotationZ;
private ModelRendererExtended(ModelBase baseModel, ModelRenderer delegate){
super(baseModel, delegate.boxName);
this.delegate = delegate;
// Copy over any public fields that might get changed
this.textureWidth = delegate.textureWidth;
this.textureHeight = delegate.textureHeight;
this.showModel = delegate.showModel;
this.isHidden = delegate.isHidden;
this.rotationPointX = delegate.rotationPointX;
this.rotationPointY = delegate.rotationPointY;
this.rotationPointZ = delegate.rotationPointZ;
this.resetRotation();
}
// Static initialisers
/**
* Replaces the given part of the given {@link ModelBase} with a new {@code ModelRendererExtended} that wraps the
* original part. This method takes care of modifying the model's box list, but <b>is unable to change any
* individual {@link ModelRenderer} fields</b> - this method instead returns the resulting
* {@code ModelRendererExtended} to be assigned to the appropriate field.
* @param model The model whose part is to be wrapped
* @param box The part of the model to wrap, must belong to the above model
* @return The resulting wrapped model part, this should be assigned to the appropriate part field in the model. If
* the given model part was already wrapped, this method simply returns it unchanged.
* @throws IllegalArgumentException if the given box does not belong to the given model
*/
public static ModelRendererExtended wrap(ModelBase model, ModelRenderer box){
if(box instanceof ModelRendererExtended) return (ModelRendererExtended)box; // Ignore already-wrapped parts
ModelRendererExtended wrapper = new ModelRendererExtended(model, box);
int index = model.boxList.indexOf(box);
if(index < 0) throw new IllegalArgumentException(String.format("The given ModelRenderer %s does not belong to the given model %s", box, model));
model.boxList.set(index, wrapper); // I doubt the order matters but we may as well put it at the same index
return wrapper;
}
/**
* Replaces the given {@link ModelBiped}'s parts with new {@code ModelRendererExtended} versions that wrap the
* original parts. If the given model is a {@link ModelPlayer}, the extra boxes for player skin overlays will also
* be wrapped.
*/
public static void wrap(ModelBiped model){
// MMmmmmmm wraps
model.bipedHead = wrap(model, model.bipedHead);
model.bipedBody = wrap(model, model.bipedBody);
model.bipedRightArm = wrap(model, model.bipedRightArm);
model.bipedLeftArm = wrap(model, model.bipedLeftArm);
model.bipedRightLeg = wrap(model, model.bipedRightLeg);
model.bipedLeftLeg = wrap(model, model.bipedLeftLeg);
model.bipedHeadwear = wrap(model, model.bipedHeadwear);
if(model instanceof ModelPlayer){
((ModelPlayer)model).bipedBodyWear = wrap(model, ((ModelPlayer)model).bipedBodyWear);
((ModelPlayer)model).bipedRightArmwear = wrap(model, ((ModelPlayer)model).bipedRightArmwear);
((ModelPlayer)model).bipedLeftArmwear = wrap(model, ((ModelPlayer)model).bipedLeftArmwear);
((ModelPlayer)model).bipedRightLegwear = wrap(model, ((ModelPlayer)model).bipedRightLegwear);
((ModelPlayer)model).bipedLeftLegwear = wrap(model, ((ModelPlayer)model).bipedLeftLegwear);
}
}
/** Resets the rotation of this model part to the angle set by the parent model. */
public void resetRotation(){
this.actualRotationX = Float.NaN;
this.actualRotationY = Float.NaN;
this.actualRotationZ = Float.NaN;
this.extraRotationX = 0;
this.extraRotationY = 0;
this.extraRotationZ = 0;
}
/** Sets the rotation of this model part, which will overwrite the angle set by the parent model. */
public void setRotation(float x, float y, float z){
this.actualRotationX = x;
this.actualRotationY = y;
this.actualRotationZ = z;
}
/** Sets the extra rotation of this model part, which will be added onto the angle set by the parent model. */
public void addRotation(float x, float y, float z){
this.extraRotationX = x;
this.extraRotationY = y;
this.extraRotationZ = z;
}
/** Sets the rotation of this model part to the same values as the given box. */
public void setRotationTo(ModelRendererExtended box){
this.actualRotationX = box.actualRotationX;
this.actualRotationY = box.actualRotationY;
this.actualRotationZ = box.actualRotationZ;
this.extraRotationX = box.extraRotationX;
this.extraRotationY = box.extraRotationY;
this.extraRotationZ = box.extraRotationZ;
}
// Delegate rendering, but fiddle with the angles first
@Override
public void render(float scale){
// Need to copy these over each time in case they were changed
delegate.showModel = this.showModel;
delegate.isHidden = this.isHidden;
delegate.rotationPointX = this.rotationPointX;
delegate.rotationPointY = this.rotationPointY;
delegate.rotationPointZ = this.rotationPointZ;
if(!Float.isNaN(actualRotationX) && !Float.isNaN(actualRotationY) && !Float.isNaN(actualRotationZ)){
delegate.rotateAngleX = actualRotationX;
delegate.rotateAngleY = actualRotationY;
delegate.rotateAngleZ = actualRotationZ;
}else{
delegate.rotateAngleX = this.rotateAngleX + extraRotationX;
delegate.rotateAngleY = this.rotateAngleY + extraRotationY;
delegate.rotateAngleZ = this.rotateAngleZ + extraRotationZ;
}
delegate.render(scale);
}
// Delegate all other methods
@Override
public void addChild(ModelRenderer renderer){
delegate.addChild(renderer);
}
@Override
public void renderWithRotation(float scale){
delegate.renderWithRotation(scale);
}
@Override
public void postRender(float scale){
// Exactly the same setup as above, just add item rotation/translation fields and setters
// float angle = 1;
// float radius = 10;
// delegate.rotationPointY -= radius * MathHelper.cos(angle);
// delegate.rotationPointZ -= radius * MathHelper.sin(angle);
// delegate.rotateAngleX += angle;
delegate.postRender(scale);
}
}
@@ -0,0 +1,220 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.util.InventoryUtils;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GlStateManager;
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.LayerBipedArmor;
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Handles the setup and rendering events for custom player animations, as well as registering of animations. Addons
* can also register their own animations here, see {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions SpellActions
*/
@Mod.EventBusSubscriber(Side.CLIENT)
public class PlayerAnimator {
// TODO: Attempt to extend this to all bipeds
private static final List<Animation> animations = new ArrayList<>();
/** Stores all registered layer renderers for lazy-loading of armour models later. */
private static final Map<RenderPlayer, List<LayerRenderer<? extends EntityLivingBase>>> playerLayers = new HashMap<>();
/** Stores the main model for each renderer plus all models for registered layer renderers. */
private static final Map<RenderPlayer, List<ModelBiped>> playerLayerModels = new HashMap<>();
/** Reflected into {@link RenderLivingBase}{@code #layerRenderers}. */
private static final Field layerRenderers;
static {
layerRenderers = ObfuscationReflectionHelper.findField(RenderLivingBase.class, "field_177097_h");
}
/**
* Registers a new player animation. This method should be called from the {@code init()} phase via a client proxy.
* @param animation The animation to register
*/
// N.B. This can (and will) be called before PlayerAnimator#init() is called
public static void registerAnimation(Animation animation){
if(animations.contains(animation)){
Wizardry.logger.warn("Animation {} is already registered!", animation.getName());
}else{
animations.add(animation);
}
}
// TODO: Figure out if calling this from postInit ensures we catch all the layers, and lazy-load it if not
@SuppressWarnings("unchecked")
public static void init(){
if(!Wizardry.settings.spellcastingAnimations) return;
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
List<ModelBiped> models = new ArrayList<>();
ModelRendererExtended.wrap(renderer.getMainModel());
models.add(renderer.getMainModel());
try {
List<LayerRenderer<? extends EntityLivingBase>> layers = (List<LayerRenderer<? extends EntityLivingBase>>)layerRenderers.get(renderer);
// Save armour layers for lazy-loading later (ignore other layers so iteration is faster)
playerLayers.put(renderer, layers.stream().filter(l -> l instanceof LayerBipedArmor).collect(Collectors.toList()));
for(LayerRenderer<?> layer : layers){
for(Field field : JavaUtils.getAllFields(layer.getClass())){
field.setAccessible(true);
// If your layer model doesn't extend ModelBiped, you DESERVE to be incompatible!
// (Just kidding... there's nothing I can do about it anyway)
if(field.get(layer) instanceof ModelBiped){
ModelBiped model = (ModelBiped)field.get(layer);
ModelRendererExtended.wrap(model);
models.add(model);
}
}
}
}catch(IllegalAccessException e){
Wizardry.logger.error("Error during reflective access of render layers: ", e);
}
playerLayerModels.put(renderer, models);
}
}
private static void updateModels(EntityPlayer player, Render<?> renderer, float partialTicks, boolean firstPerson){
// Biped armour is special because of Forge's armour item render hook
// This needs to be lazy-loaded because we need access to an actual item
for(LayerRenderer<? extends EntityLivingBase> layer : playerLayers.get(renderer)){
if(layer instanceof LayerBipedArmor){
for(EntityEquipmentSlot slot : InventoryUtils.ARMOUR_SLOTS){
ItemStack armour = player.getItemStackFromSlot(slot);
// This method could behave dynamically depending on stuff like NBT so there's not really any way to
// optimise it further, we *have* to retrieve the model every time (shouldn't be too bad though)
ModelBiped model = ForgeHooksClient.getArmorModel(player, armour, slot, ((LayerBipedArmor)layer).getModelFromSlot(slot));
List<ModelBiped> models = playerLayerModels.get(renderer);
if(!models.contains(model)){ // Ignore already-wrapped models
models.add(model);
ModelRendererExtended.wrap(model);
}
}
}
}
for(Animation animation : animations){
if(animation.shouldDisplay(player, firstPerson)){
boolean autoRotateSecondLayer = animation.autoRotateSecondLayer(player, firstPerson);
for(ModelBiped model : playerLayerModels.get(renderer)){
animation.setRotationAngles(player, model, partialTicks, firstPerson);
if(autoRotateSecondLayer && model instanceof ModelPlayer){
alignSecondLayer((ModelPlayer)model);
}
}
}
}
}
/** Rotates the second layer boxes of the given (wrapped) player model to match the first layer. */
public static void alignSecondLayer(ModelPlayer model){
((ModelRendererExtended)model.bipedBodyWear) .setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedRightArmwear).setRotationTo((ModelRendererExtended)model.bipedRightArm);
((ModelRendererExtended)model.bipedLeftArmwear) .setRotationTo((ModelRendererExtended)model.bipedLeftArm);
((ModelRendererExtended)model.bipedRightLegwear).setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedLeftLegwear) .setRotationTo((ModelRendererExtended)model.bipedBody);
}
@SubscribeEvent
public static void onRenderHandEvent(RenderSpecificHandEvent event){
if(!Wizardry.settings.spellcastingAnimations) return;
AbstractClientPlayer player = Minecraft.getMinecraft().player;
EnumAction action = event.getItemStack().getItemUseAction();
if(player.isHandActive() && player.getActiveHand() == event.getHand() && SpellActions.getSpellActions().contains(action)){
// Minecraft's item renderer helpfully doesn't have a default case for the usage action so it doesn't do any
// transformations at all (which is VERY ANNOYING!) - the following lines are from ItemRenderer#transformSideFirstPerson
int i = (player.getPrimaryHand() == EnumHandSide.RIGHT) == (event.getHand() == EnumHand.MAIN_HAND) ? 1 : -1;
GlStateManager.translate((float)i * 0.56F, -0.52F + event.getEquipProgress() * -0.6F, -0.72F);
}
updateModels(player, Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(player), event.getPartialTicks(), true);
}
@SubscribeEvent
public static void onRenderPlayerPreEvent(RenderPlayerEvent.Pre event){
if(!Wizardry.settings.spellcastingAnimations) return;
// boolean firstPerson = event.getEntityPlayer() == Minecraft.getMinecraft().player
// && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0;
updateModels(event.getEntityPlayer(), event.getRenderer(), event.getPartialRenderTick(), false);
}
@SubscribeEvent
public static void onRenderPlayerPostEvent(RenderPlayerEvent.Post event){
if(!Wizardry.settings.spellcastingAnimations) return;
for(ModelBiped model : playerLayerModels.get(event.getRenderer())){
for(ModelRenderer box : model.boxList){ // For ModelPlayer, this will include the second layer
// Some models have extra boxes, they (probably) don't need wrapping but we need this check!
if(box instanceof ModelRendererExtended){
((ModelRendererExtended)box).resetRotation();
}
}
}
}
}
@@ -0,0 +1,36 @@
package electroblob.wizardry.client.audio;
import electroblob.wizardry.item.ISpellCastingItem;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
/** A special type of moving sound that stops when the player stops charging up a spell, either by releasing the mouse
* button or when the charge-up time has elapsed. */
public class MovingSoundSpellCharge extends MovingSoundEntity<EntityLivingBase> {
public MovingSoundSpellCharge(EntityLivingBase entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
super(entity, sound, category, volume, pitch, repeat);
}
@Override
public void update(){
if(source.isHandActive()){
ItemStack stack = source.getActiveItemStack();
if(stack.getItem() instanceof ISpellCastingItem){
if(source.getItemInUseMaxCount() < ((ISpellCastingItem)stack.getItem()).getCurrentSpell(stack).getChargeup()){
super.update();
return;
}
}
}
this.donePlaying = true;
}
}
@@ -27,6 +27,16 @@ public abstract class SoundLoop implements ITickable {
private static final Set<SoundLoop> activeLoops = new HashSet<>();
// The issue we had here was that there is no way of hooking into the client tick loop between the sound manager
// update and the world tick (other than via the sounds themselves, but playing other sounds from there results in
// CMEs, which is why we made this class in the first place). This meant there was always a gap between consecutive
// sounds, which could be heard in-game as a weird audio artefact. The solution is simply to overlap the sounds by
// 1 tick, which is fairly straightforward for the end sound, but because the start sound has to finish for us to
// know when to play the others, this doubling-up solution is required (bit of a waste of resources but meh).
/** A dummy sound with effectively zero volume, played one tick in advance of the actual start sound so the loop
* sound can start one tick before the actual start sound ends. */
private final ISound dummyStart; // It's the stupidest system ever but it works!
private final ISound start;
private final ISound loop;
private final ISound end;
@@ -34,29 +44,28 @@ public abstract class SoundLoop implements ITickable {
private boolean looping = false;
private boolean needsRemoving = false;
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, ISoundFactory factory){
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, 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);
this.dummyStart = factory.create(start, category, 0.00001f, false); // Inaudible but not actually 0
this.start = factory.create(start, category, volume, false);
this.loop = factory.create(loop, category, volume, true);
this.end = factory.create(end, category, volume, 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)){
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(dummyStart)){
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. */
/** Starts playing the end part immediately and marks the loop part to stop next tick. 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
@@ -68,12 +77,20 @@ public abstract class SoundLoop implements ITickable {
this.needsRemoving = true;
}
/** Stops the start and loop sounds immediately. */
protected void stopStartAndLoop(){
Minecraft.getMinecraft().getSoundHandler().stopSound(dummyStart);
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
}
// 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);
Minecraft.getMinecraft().getSoundHandler().playSound(loop.dummyStart);
Minecraft.getMinecraft().getSoundHandler().playDelayedSound(loop.start, 2); // 1 seems to be insufficient
}
@SubscribeEvent
@@ -82,13 +99,14 @@ public abstract class SoundLoop implements ITickable {
// 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.stream().filter(s -> s.needsRemoving).forEach(SoundLoop::stopStartAndLoop);
activeLoops.removeIf(s -> s.needsRemoving);
activeLoops.forEach(SoundLoop::update); // Do this last to allow a 1-tick overlap
}
}
@FunctionalInterface
public interface ISoundFactory {
ISound create(SoundEvent sound, SoundCategory category, boolean repeat);
ISound create(SoundEvent sound, SoundCategory category, float volume, boolean repeat);
}
}
@@ -3,7 +3,7 @@ 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 electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.audio.PositionedSound;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntity;
@@ -17,8 +17,8 @@ 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);
public SoundLoopSpell(SoundEvent start, SoundEvent loop, SoundEvent end, float volume, ISoundFactory factory, Spell spell){
super(start, loop, end, WizardrySounds.SPELLS, volume, factory);
this.spell = spell;
}
@@ -43,13 +43,13 @@ public abstract class SoundLoopSpell extends SoundLoop {
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);
super(start, loop, end, volume, (sound, category, v, repeat) -> new MovingSoundEntity<>(source, sound, category, v, pitch, repeat), spell);
this.source = source;
}
@Override
protected boolean stillCasting(Spell spell){
return WizardryUtilities.isCasting(source, spell);
return EntityUtils.isCasting(source, spell);
}
}
@@ -58,13 +58,13 @@ public abstract class SoundLoopSpell extends SoundLoop {
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){{
super(start, loop, end, sndVolume, (sound, category, v, 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.volume = v;
this.pitch = sndPitch;
}}, spell);
}
@@ -0,0 +1,109 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.client.resources.I18n;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.Locale;
@EventBusSubscriber(Side.CLIENT)
public class CustomCreativeSearchHandler {
private static final int SEARCH_TOOLTIP_HOVER_TIME = 20;
private static final Style TOOLTIP_SYNTAX = new Style().setColor(TextFormatting.YELLOW);
private static final Style TOOLTIP_BODY = new Style().setColor(TextFormatting.WHITE);
/** Reflected into {@code GuiContainerCreative#searchField} */
private static final Field searchField;
private static GuiTextField currentSearchField = null;
private static int searchBarHoverTime;
static {
searchField = ObfuscationReflectionHelper.findField(GuiContainerCreative.class, "field_147062_A");
}
@SubscribeEvent
public static void onInitGuiEvent(GuiScreenEvent.InitGuiEvent.Post event){
// Reduces reflection as much as possible - searchField is created on GUI init and never reassigned so we need
// not (and should not!) use reflection to retrieve it every time a key is typed
if(event.getGui() instanceof GuiContainerCreative){
try{
currentSearchField = (GuiTextField)searchField.get(event.getGui());
}catch(IllegalAccessException e){
e.printStackTrace();
}
}
}
@SubscribeEvent
public static void onKeyboardInputEvent(GuiScreenEvent.KeyboardInputEvent.Post event){
// Custom creative tab search behaviour
if(event.getGui() instanceof GuiContainerCreative){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS){
GuiContainerCreative.ContainerCreative container = (GuiContainerCreative.ContainerCreative)gui.inventorySlots;
container.itemList.clear(); // Required or duplicates will appear!
String searchText = currentSearchField.getText().toLowerCase(Locale.ROOT);
tab.displayAllRelevantItems(container.itemList);
if(!searchText.isEmpty()){
container.itemList.removeIf(s -> !Spell.byMetadata(s.getMetadata()).matches(searchText));
container.scrollTo(0); // Seems to refresh the GUI somehow so it displays correctly
}
}
}
}
@SubscribeEvent
public static void onClientTickEvent(ClientTickEvent event){
if(event.phase == Phase.END && searchBarHoverTime > 0 && searchBarHoverTime < SEARCH_TOOLTIP_HOVER_TIME){
searchBarHoverTime++;
}
}
@SubscribeEvent
public static void onDrawScreenPostEvent(GuiScreenEvent.DrawScreenEvent.Post event){
if(event.getGui() instanceof GuiContainerCreative && currentSearchField != null){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS && DrawingUtils.isPointInRegion(currentSearchField.x, currentSearchField.y, currentSearchField.width, currentSearchField.height, event.getMouseX(), event.getMouseY())){
if(searchBarHoverTime == 0){
searchBarHoverTime++;
}else if(searchBarHoverTime == SEARCH_TOOLTIP_HOVER_TIME){
event.getGui().drawHoveringText(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.search_tooltip",
TOOLTIP_SYNTAX.getFormattingCode(), TOOLTIP_BODY.getFormattingCode()), event.getMouseX(), event.getMouseY());
}
}
}else{
searchBarHoverTime = 0;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class GuiBookshelf extends GuiContainer {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/bookshelf.png");
/** The player inventory bound to this GUI. */
private final InventoryPlayer playerInventory;
/** The inventory contained within the corresponding bookshelf. */
public IInventory bookshelfInventory;
public GuiBookshelf(InventoryPlayer playerInv, TileEntityBookshelf bookshelfInv){
super(new ContainerBookshelf(playerInv, bookshelfInv));
this.playerInventory = playerInv;
this.bookshelfInventory = bookshelfInv;
this.allowUserInput = false;
this.ySize = 148;
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
this.drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
this.renderHoveredToolTip(mouseX, mouseY);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
String s = this.bookshelfInventory.getDisplayName().getUnformattedText();
this.fontRenderer.drawString(s, 8, 6, 4210752);
this.fontRenderer.drawString(this.playerInventory.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(TEXTURE);
int i = (this.width - this.xSize) / 2;
int j = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
}
}
@@ -7,8 +7,8 @@ import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Resurrection;
import electroblob.wizardry.util.InventoryUtils;
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;
@@ -52,7 +52,7 @@ public class GuiButtonResurrect extends GuiButton {
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))){
&& InventoryUtils.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"));
@@ -65,7 +65,7 @@ public class GuiButtonResurrect extends GuiButton {
if(event.getGui() instanceof GuiGameOver){
ItemStack stack = WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream()
ItemStack stack = InventoryUtils.getHotbar(Minecraft.getMinecraft().player).stream()
.filter(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player)).findFirst().orElse(null);
if(stack != null){
@@ -0,0 +1,57 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.ISpellSortable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
public class GuiButtonSpellSort extends GuiButton {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/spell_sort_buttons.png");
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_HEIGHT = 32;
public final ISpellSortable.SortType sortType;
private final ISpellSortable sortable;
private final GuiScreen parent;
public GuiButtonSpellSort(int id, int x, int y, ISpellSortable.SortType sortType, ISpellSortable sortable, GuiScreen parent){
super(id, x, y, 10, 10, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.sort_" + sortType.name));
this.sortType = sortType;
this.sortable = sortable;
this.parent = parent;
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
// 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 = 0;
int l = this.sortType.ordinal() * this.height;
if(sortType == sortable.getSortType()){
k += this.width;
if(sortable.isSortDescending()) k += this.width;
}
parent.mc.getTextureManager().bindTexture(TEXTURE);
DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
}
@Override
public void drawButtonForegroundLayer(int mouseX, int mouseY){
if(hovered) parent.drawHoveringText(this.displayString, mouseX, mouseY);
}
}
@@ -1,6 +1,5 @@
package electroblob.wizardry.client.gui.handbook;
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.registry.WizardrySounds;
import net.minecraft.client.Minecraft;
@@ -11,12 +10,12 @@ import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
//@SideOnly(Side.CLIENT)
class GuiButtonTurnPage extends GuiButton {
public class GuiButtonTurnPage extends GuiButton {
static final int WIDTH = 20;
static final int HEIGHT = 12;
public static final int WIDTH = 20;
public static final int HEIGHT = 12;
enum Type {
public enum Type {
NEXT_PAGE(0, 196),
PREVIOUS_PAGE(0, 208),
@@ -34,11 +33,15 @@ class GuiButtonTurnPage extends GuiButton {
public final Type type;
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
private final ResourceLocation texture;
private final int textureWidth, textureHeight;
public GuiButtonTurnPage(int id, int x, int y, Type type){
public GuiButtonTurnPage(int id, int x, int y, Type type, ResourceLocation texture, int textureWidth, int textureHeight){
super(id, x, y, WIDTH, HEIGHT, "");
this.type = type;
this.texture = texture;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
}
@Override
@@ -50,12 +53,13 @@ class GuiButtonTurnPage extends GuiButton {
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
minecraft.getTextureManager().bindTexture(texture);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, 512, 256);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, textureWidth, textureHeight);
}
}
}
@@ -0,0 +1,499 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.packet.PacketLectern;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.TileEntityLectern;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.ISpellSortable;
import electroblob.wizardry.util.ParticleBuilder;
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.GuiTextField;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.client.CPacketCloseWindow;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class GuiLectern extends GuiSpellInfo implements ISpellSortable {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/lectern.png");
/** The distance of the page buttons from the bottom outside corners of the GUI. */
private static final int PAGE_BUTTON_INSET_X = 22, PAGE_BUTTON_INSET_Y = 13;
/** The distance between adjacent page turn buttons. */
private static final int PAGE_BUTTON_SPACING = 20;
/** The distance of the sort buttons from the top left corner of the GUI. */
private static final int SORT_BUTTON_INSET_X = 96, SORT_BUTTON_INSET_Y = 20;
/** The distance between adjacent sort buttons. */
private static final int SORT_BUTTON_SPACING = 13;
/** The distance of the spell buttons from the top outside corners of the GUI. */
private static final int SPELL_BUTTON_INSET_X = 23, SPELL_BUTTON_INSET_Y = 44;
/** The distance between adjacent spell buttons (in both x and y). */
private static final int SPELL_BUTTON_SPACING = 38;
private static final int SPELL_ROWS = 3, SPELL_COLUMNS = 3;
public static final int SPELL_BUTTON_COUNT = SPELL_ROWS * SPELL_COLUMNS * 2; // x2 because there are 2 pages
private static final int SEARCH_TOOLTIP_HOVER_TIME = 20;
private static final Style TOOLTIP_SYNTAX = new Style().setColor(TextFormatting.YELLOW);
private static final Style TOOLTIP_BODY = new Style().setColor(TextFormatting.WHITE);
private final TileEntityLectern lectern;
private GuiButton nextPageButton;
private GuiButton prevPageButton;
private GuiButton lastPageButton;
private GuiButton firstPageButton;
private GuiButton indexButton;
private GuiButton locateButton;
private GuiButton[] sortButtons = new GuiButton[3];
private GuiButtonSpell[] spellButtons = new GuiButtonSpell[SPELL_BUTTON_COUNT];
/** The spell currently being viewed, or null if the index is being viewed. */
private Spell currentSpell;
/** The available spells in nearby bookshelves; should not contain duplicates. */
private List<Spell> availableSpells = new ArrayList<>();
/** The spells matching the current search criteria, sorted according to the current sort settings. Will be a subset
* of {@link GuiLectern#availableSpells}. */
private List<Spell> matchingSpells;
private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER;
private boolean sortDescending = false;
private GuiTextField searchField;
private boolean searchNeedsClearing;
private int searchBarHoverTime;
private int currentPage = 0;
public GuiLectern(TileEntityLectern lectern){
super(288, 180);
this.lectern = lectern;
this.currentSpell = lectern.currentSpell;
this.setTextureSize(512, 512);
}
@Override
public Spell getSpell(){
return currentSpell;
}
@Override
public ResourceLocation getTexture(){
return TEXTURE;
}
@Override
public SortType getSortType(){
return sortType;
}
@Override
public boolean isSortDescending(){
return sortDescending;
}
private int getPageCount(){
return MathHelper.ceil((float)matchingSpells.size() / SPELL_BUTTON_COUNT);
}
private Spell getSpellForButton(GuiButtonSpell button){
return matchingSpells.get(currentPage * SPELL_BUTTON_COUNT + button.index);
}
@Override
public void initGui(){
super.initGui();
final int left = this.width / 2 - this.xSize / 2;
final int top = this.height / 2 - this.ySize / 2;
int buttonID = 0;
// Page buttons
this.buttonList.add(nextPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(prevPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(lastPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - PAGE_BUTTON_SPACING,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(firstPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X + PAGE_BUTTON_SPACING,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(indexButton = new GuiButtonTurnPage(buttonID++, left + xSize/2 - 23,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(locateButton = new GuiButtonLocateBook(buttonID++, left + xSize/2 - 34,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT));
// Sort buttons
for(SortType sortType : SortType.values()){
this.buttonList.add(sortButtons[sortType.ordinal()] = new GuiButtonSpellSort(buttonID++,
left + SORT_BUTTON_INSET_X + SORT_BUTTON_SPACING * sortType.ordinal(),
top + SORT_BUTTON_INSET_Y, sortType, this, this));
}
// Spell buttons
for(int i = 0; i < SPELL_BUTTON_COUNT; i++){
int row = i % SPELL_COLUMNS;
int column = (i / SPELL_COLUMNS) % SPELL_ROWS;
int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING
: xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING;
int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING;
this.buttonList.add(spellButtons[i] = new GuiButtonSpell(buttonID++, left + x, top + y, i));
}
this.searchField = new GuiTextField(0, this.fontRenderer, left + 157, top + 21, 106, this.fontRenderer.FONT_HEIGHT);
this.searchField.setMaxStringLength(50);
this.searchField.setEnableBackgroundDrawing(false);
this.searchField.setVisible(true);
this.searchField.setTextColor(16777215);
this.searchField.setCanLoseFocus(false);
this.searchField.setFocused(true);
refreshAvailableSpells(); // Must be done last
}
@Override
public void updateScreen(){
super.updateScreen();
if(searchBarHoverTime > 0 && searchBarHoverTime < SEARCH_TOOLTIP_HOVER_TIME) searchBarHoverTime++;
}
@Override
public void onGuiClosed(){
WizardryPacketHandler.net.sendToServer(new PacketLectern.Message(lectern.getPos(), currentSpell));
super.onGuiClosed();
}
@Override
protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){
if(currentSpell == Spells.none){
drawIndexPage(left, top);
}else{
super.drawBackgroundLayer(left, top, mouseX, mouseY);
}
}
@Override
protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){
if(currentSpell == Spells.none){
this.fontRenderer.drawString(I18n.format("gui." + Wizardry.MODID + ":lectern.title"),
left + 20, top + SORT_BUTTON_INSET_Y, 0);
}else{
super.drawForegroundLayer(left, top, mouseX, mouseY);
}
this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY));
// Search tooltip
if(DrawingUtils.isPointInRegion(searchField.x, searchField.y, searchField.width, searchField.height, mouseX, mouseY)){
if(searchBarHoverTime == 0){
searchBarHoverTime++;
}else if(searchBarHoverTime == SEARCH_TOOLTIP_HOVER_TIME){
drawHoveringText(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.search_tooltip",
TOOLTIP_SYNTAX.getFormattingCode(), TOOLTIP_BODY.getFormattingCode()), mouseX, mouseY);
}
}else{
searchBarHoverTime = 0;
}
}
private void drawIndexPage(int left, int top){
for(int i = 0; i < SPELL_BUTTON_COUNT; i++){
int index = currentPage * SPELL_BUTTON_COUNT + i;
Spell spell = index < matchingSpells.size() ? matchingSpells.get(index) : Spells.none;
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(spell, null);
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
int row = i % SPELL_COLUMNS;
int column = (i / SPELL_COLUMNS) % SPELL_ROWS;
int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING
: xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING;
int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING;
DrawingUtils.drawTexturedRect(left + x + 1, top + y + 1, 0, 0, 32, 32, 32, 32);
}
mc.renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(left, top, 0, 256, xSize, ySize, textureWidth, textureHeight);
this.searchField.drawTextBox();
GlStateManager.color(1, 1, 1, 1);
mc.renderEngine.bindTexture(getTexture());
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
super.mouseClicked(mouseX, mouseY, mouseButton);
searchNeedsClearing = true;
}
@Override
protected void actionPerformed(GuiButton button){
int lastPage = getPageCount() - 1;
if(button == indexButton){
currentSpell = Spells.none;
// currentPage = 0;
}else if(button == locateButton){
// Close the GUI
this.mc.player.connection.sendPacket(new CPacketCloseWindow(this.mc.player.openContainer.windowId));
this.mc.displayGuiScreen(null);
// Find the location(s) of the current spell's book(s) and highlight them
for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){
for(int i = 0; i < bookshelf.getSizeInventory(); i++){
ItemStack stack = bookshelf.getStackInSlot(i);
if(stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getMetadata());
if(spell == this.currentSpell){
BlockPos pos = (((TileEntity)bookshelf).getPos());
for(EnumFacing side : EnumFacing.VALUES){
ParticleBuilder.create(ParticleBuilder.Type.BLOCK_HIGHLIGHT).pos(
GeometryUtils.getFaceCentre(pos, side)
.add(new Vec3d(side.getDirectionVec())
.scale(GeometryUtils.ANTI_Z_FIGHTING_OFFSET)))
.face(side).clr(0.9f, 0.5f, 0.8f).fade(0.7f, 0, 1).spawn(mc.world);
}
mc.world.playSound(pos, WizardrySounds.BLOCK_LECTERN_LOCATE_SPELL, SoundCategory.BLOCKS, 1, 0.7f, false);
break; // This bookshelf has the spell, skip to the next bookshelf
}
}
}
}
}else if(button == nextPageButton){
if(currentPage < lastPage) currentPage++;
}else if(button == prevPageButton){
if(currentPage > 0) currentPage--;
}else if(button == lastPageButton){
currentPage = lastPage;
}else if(button == firstPageButton){
currentPage = 0;
}else if(button instanceof GuiButtonSpell){
currentSpell = getSpellForButton((GuiButtonSpell)button);
}else if(button instanceof GuiButtonSpellSort){
SortType sortType = (((GuiButtonSpellSort)button).sortType);
if(this.sortType == sortType){
this.sortDescending = !this.sortDescending;
}else{
this.sortType = sortType;
this.sortDescending = false;
}
updateMatchingSpells();
}
updateButtonVisiblity();
}
@Override
protected void keyTyped(char typedChar, int keyCode) throws IOException {
if(this.searchNeedsClearing){
this.searchNeedsClearing = false;
this.searchField.setText("");
this.currentPage = 0;
}
if(this.searchField.textboxKeyTyped(typedChar, keyCode)){
this.currentPage = 0;
updateMatchingSpells();
updateButtonVisiblity();
}else{
super.keyTyped(typedChar, keyCode);
}
}
private void updateButtonVisiblity(){
if(currentSpell == Spells.none){
this.searchField.setVisible(true);
int lastPage = getPageCount() - 1;
prevPageButton.visible = currentPage > 0;
firstPageButton.visible = currentPage > 0;
nextPageButton.visible = currentPage < lastPage;
lastPageButton.visible = currentPage < lastPage;
indexButton.visible = false;
locateButton.visible = false;
for(GuiButton button : sortButtons) button.visible = true;
for(GuiButtonSpell button : spellButtons){
button.visible = currentPage * SPELL_BUTTON_COUNT + button.index < matchingSpells.size();
}
}else{
this.searchField.setVisible(false);
this.buttonList.forEach(b -> b.visible = false); // Hide all buttons...
indexButton.visible = true; // ... except the index button and locate button
locateButton.visible = true;
}
}
private void updateMatchingSpells(){
matchingSpells = availableSpells.stream()
.filter(s -> s.matches(searchField.getText().toLowerCase(Locale.ROOT)))
.sorted(sortDescending ? sortType.comparator.reversed() : sortType.comparator)
.collect(Collectors.toList());
}
// TODO: Config option to always display all spells when in creative
/** Called on initialisation and whenever a bookshelf is added or removed, to update the list of spells. */
public void refreshAvailableSpells(){
availableSpells.clear();
// No need to exclude the lectern TE because it's not an IInventory!
for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){
for(int i=0; i<bookshelf.getSizeInventory(); i++){
ItemStack stack = bookshelf.getStackInSlot(i);
if(stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getMetadata());
if(spell != Spells.none && !availableSpells.contains(spell)) availableSpells.add(spell);
}
}
}
if(!availableSpells.contains(currentSpell)) currentSpell = Spells.none;
updateMatchingSpells();
updateButtonVisiblity();
}
private class GuiButtonLocateBook extends GuiButton {
public GuiButtonLocateBook(int id, int x, int y){
super(id, x, y, 12, 12, "");
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
GlStateManager.color(1, 1, 1, 1);
minecraft.getTextureManager().bindTexture(TEXTURE);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? width : 0, 184, width, height, textureWidth, textureHeight);
}
}
}
private class GuiButtonSpell extends GuiButtonInvisible {
private static final int WIDTH = 34, HEIGHT = 34;
private final int index;
public GuiButtonSpell(int id, int x, int y, int index){
super(id, x, y, WIDTH, HEIGHT);
this.index = index;
}
@Override
public void playPressSound(SoundHandler soundHandler){
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(visible){
super.drawButton(minecraft, mouseX, mouseY, partialTicks);
if(hovered){
mc.renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(x, y, 40, 180, width, height, textureWidth, textureHeight);
}
}
}
@Override
public void drawButtonForegroundLayer(int mouseX, int mouseY){
if(visible && hovered){
Spell spell = getSpellForButton(this);
if(Wizardry.proxy.shouldDisplayDiscovered(spell, null)){
drawHoveringText(Collections.singletonList(spell.getDisplayName()), mouseX, mouseY, fontRenderer);
}else{
drawHoveringText(Collections.singletonList(SpellGlyphData.getGlyphName(spell, mc.world)),
mouseX, mouseY, mc.standardGalacticFontRenderer);
}
}
}
}
}
@@ -1,113 +1,35 @@
package electroblob.wizardry.client.gui;
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.item.ItemSpellBook;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
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.client.resources.I18n;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard;
import net.minecraft.util.ResourceLocation;
public class GuiSpellBook extends GuiScreen {
public class GuiSpellBook extends GuiSpellInfo {
private int xSize, ySize;
private ItemSpellBook book;
private Spell spell;
private final ItemSpellBook book;
private final Spell spell;
public GuiSpellBook(ItemStack stack){
super();
xSize = 288;
ySize = 180;
if(!(stack.getItem() instanceof ItemSpellBook)) throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!");
super(288, 180);
if(!(stack.getItem() instanceof ItemSpellBook)){
throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!");
}
this.book = (ItemSpellBook)stack.getItem();
this.spell = Spell.byMetadata(stack.getItemDamage());
this.spell = (Spell.byMetadata(stack.getItemDamage()));
}
@Override
public void drawScreen(int par1, int par2, float par3){
int xPos = this.width / 2 - xSize / 2;
int yPos = this.height / 2 - this.ySize / 2;
EntityPlayer player = Minecraft.getMinecraft().player;
boolean discovered = true;
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(book.getGuiTexture(spell));
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.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.getType().getDisplayName(), xPos + 17, yPos + 26,
0x777777);
}
// Novice is usually white but this doesn't show up
String tier = I18n.format("gui.ebwizardry:spell_book.tier", spell.getTier() == Tier.NOVICE ?
"\u00A77" + spell.getTier().getDisplayName() : spell.getTier().getDisplayNameWithFormatting());
this.fontRenderer.drawString(tier, xPos + 17, yPos + 45, 0);
String element = I18n.format("gui.ebwizardry:spell_book.element", spell.getElement().getFormattingCode() + spell.getElement().getDisplayName());
if(!discovered) element = I18n.format("gui.ebwizardry:spell_book.element_undiscovered");
this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0);
String manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost", spell.getCost());
if(spell.isContinuous) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_continuous", spell.getCost());
if(!discovered) manaCost = I18n.format("gui.ebwizardry:spell_book.mana_cost_undiscovered");
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0);
if(discovered){
this.fontRenderer.drawSplitString(spell.getDescription(), xPos + 17, yPos + 83, 118, 0);
}else{
this.mc.standardGalacticFontRenderer.drawSplitString(
SpellGlyphData.getGlyphDescription(spell, player.world), xPos + 17, yPos + 83, 118, 0);
}
public Spell getSpell(){
return spell;
}
@Override
public void initGui(){
super.initGui();
Keyboard.enableRepeatEvents(true);
this.buttonList.clear();
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
}
@Override
public void onGuiClosed(){
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
}
@Override
public boolean doesGuiPauseGame(){
return Wizardry.settings.booksPauseGame;
public ResourceLocation getTexture(){
return book.getGuiTexture(spell);
}
}
@@ -14,7 +14,7 @@ import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.GlStateManager;
@@ -45,7 +45,8 @@ import java.nio.charset.StandardCharsets;
public class GuiSpellDisplay {
private static final ResourceLocation INDEX = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud/_index.json");
private static final ResourceLocation CHARGE_METER = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_charge_meter.png");
/** 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
@@ -54,6 +55,12 @@ public class GuiSpellDisplay {
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();
private static final Random random = new Random();
/** Width of the charge meter. */
private static final int CHARGE_METER_WIDTH = 25;
/** Height of the charge meter. */
private static final int CHARGE_METER_HEIGHT = 9;
/** Width and height of the spell icon (very unlikely to change!) */
private static final int SPELL_ICON_SIZE = 32;
@@ -99,12 +106,9 @@ public class GuiSpellDisplay {
@SubscribeEvent
public static void draw(RenderGameOverlayEvent.Post event){
if(event.getType() != RenderGameOverlayEvent.ElementType.TEXT
&& event.getType() != RenderGameOverlayEvent.ElementType.HOTBAR) return;
Minecraft mc = Minecraft.getMinecraft();
if(!Wizardry.settings.showSpellHUD && !Wizardry.settings.showChargeMeter) return; // Optimisation
EntityPlayer player = mc.player;
EntityPlayer player = Minecraft.getMinecraft().player;
if(player.isSpectator()) return; // Spectators shouldn't have the spell HUD!
@@ -117,12 +121,89 @@ public class GuiSpellDisplay {
wand = player.getHeldItemOffhand();
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;
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand)))
return;
}
int width = event.getResolution().getScaledWidth();
int height = event.getResolution().getScaledHeight();
switch(event.getType()){
case CROSSHAIRS:
renderChargeMeter(player, wand, width, height, event.getPartialTicks());
break;
case HOTBAR:
renderSpellHUD(player, wand, mainHand, width, height, event.getPartialTicks(), false);
break;
case TEXT:
renderSpellHUD(player, wand, mainHand, width, height, event.getPartialTicks(), true);
break;
}
}
/**
* Renders the spell charge meter around the crosshairs.
* @param player A reference to the client player
* @param wand The wand the HUD is for
* @param width The width of the screen
* @param height The height of the screen
* @param partialTicks The current partial tick time
*/
private static void renderChargeMeter(EntityPlayer player, ItemStack wand, int width, int height, float partialTicks){
if(!Wizardry.settings.showChargeMeter) return;
if(Minecraft.getMinecraft().gameSettings.showDebugInfo) return; // Don't show charge meter in the debug screen
if(Minecraft.getMinecraft().gameSettings.thirdPersonView != 0) return; // Don't show in third person
if(wand != player.getActiveItemStack()) return; // Don't show when using the other held item
if(!(wand.getItem() instanceof ISpellCastingItem)) throw new IllegalArgumentException("The given stack must contain an ISpellCastingItem!");
Spell spell = ((ISpellCastingItem)wand.getItem()).getCurrentSpell(wand);
int chargeup = spell.getChargeup();
if(WizardData.get(player) != null){
// Pretty sure this is accessible client-side since it's only assigned from common code
chargeup = (int)(chargeup * WizardData.get(player).itemCastingModifiers.get(SpellModifiers.CHARGEUP));
}
if(chargeup <= 0) return;
// WHY WHY WHY are these methods named so misleadingly?! Sort yourselves out MCP!
// (getItemInUseCount returns the max count MINUS the use count, and getItemInUseMaxCount returns the use count)
if(player.getItemInUseMaxCount() == 0) return; // Not charging
float charge = (player.getItemInUseMaxCount() + partialTicks) / chargeup;
if(charge > 1) return; // Done charging
Minecraft.getMinecraft().renderEngine.bindTexture(CHARGE_METER);
int x1 = width/2 - CHARGE_METER_WIDTH/2;
int y = height/2 - CHARGE_METER_HEIGHT/2;
int w = (int)(CHARGE_METER_WIDTH/2 * charge);
int u = CHARGE_METER_WIDTH - w;
DrawingUtils.drawTexturedRect(x1, y, 0, 0, w, CHARGE_METER_HEIGHT, 32, 32);
DrawingUtils.drawTexturedRect(x1 + u, y, u, 0, w, CHARGE_METER_HEIGHT, 32, 32);
}
/**
* Renders the main spell HUD in the corner of the screen.
* @param player A reference to the client player
* @param wand The wand the HUD is for
* @param mainHand True if the wand is in the player's main hand, false if it is in their offhand
* @param width The width of the screen
* @param height The height of the screen
* @param partialTicks The current partial tick time
* @param textLayer True to render the text layer, false to render the background (hotbar layer)
*/
private static void renderSpellHUD(EntityPlayer player, ItemStack wand, boolean mainHand, int width, int height, float partialTicks, boolean textLayer){
if(!Wizardry.settings.showSpellHUD) return;
if(!(wand.getItem() instanceof ISpellCastingItem)) throw new IllegalArgumentException("The given stack must contain an ISpellCastingItem!");
boolean flipX = Wizardry.settings.spellHUDPosition.flipX;
boolean flipY = Wizardry.settings.spellHUDPosition.flipY;
@@ -130,16 +211,16 @@ public class GuiSpellDisplay {
// ............. | 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);
if(skin == null){
Wizardry.logger.info("The spell HUD skin '" + Wizardry.settings.spellHUDSkin + "' specified in the config"
+ " did not match any of the loaded skins; using the default skin as a fallback.");
skin = skins.get(Settings.DEFAULT_HUD_SKIN_KEY);
if(skin == null){
Wizardry.logger.warn("The default spell HUD skin is missing! A resource pack must have overridden it"
+ " with an invalid JSON file (default.json), please try again without any resource packs.");
@@ -148,7 +229,7 @@ public class GuiSpellDisplay {
}
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;
@@ -169,45 +250,45 @@ public class GuiSpellDisplay {
y = MathHelper.ceil(y/scale);
}
Spell spell = WandHelper.getCurrentSpell(wand);
int cooldown = WandHelper.getCurrentCooldown(wand);
int maxCooldown = WandHelper.getCurrentMaxCooldown(wand);
Spell spell = ((ISpellCastingItem)wand.getItem()).getCurrentSpell(wand);
int cooldown = ((ISpellCastingItem)wand.getItem()).getCurrentCooldown(wand);
int maxCooldown = ((ISpellCastingItem)wand.getItem()).getCurrentMaxCooldown(wand);
if(textLayer){
if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){
float animationProgress = Math.signum(switchTimer) * ((SPELL_SWITCH_TIME - Math.abs(switchTimer) +
event.getPartialTicks()) / SPELL_SWITCH_TIME);
String prevSpellName = getFormattedSpellName(WandHelper.getPreviousSpell(wand), player, WandHelper.getPreviousCooldown(wand));
partialTicks) / SPELL_SWITCH_TIME);
String prevSpellName = getFormattedSpellName(((ISpellCastingItem)wand.getItem()).getPreviousSpell(wand), player, 0);
String spellName = getFormattedSpellName(spell, player, cooldown);
String nextSpellName = getFormattedSpellName(WandHelper.getNextSpell(wand), player, WandHelper.getNextCooldown(wand));
String nextSpellName = getFormattedSpellName(((ISpellCastingItem)wand.getItem()).getNextSpell(wand), player, 0);
skin.drawText(x, y, flipX, flipY, prevSpellName, spellName, nextSpellName, animationProgress);
}else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){
}else{
boolean discovered = true;
if(!player.isCreative() && WizardData.get(player) != null){
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
}
ResourceLocation icon = discovered ? spell.getIcon() : Spells.none.getIcon();
float progress = 1;
// Doesn't really matter what progress is when in creative, but we might as well avoid the calculation.
if(!player.isCreative() && !spell.isContinuous){
if(!player.isCreative()){
// Subtracted partial tick time to make it smoother
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + event.getPartialTicks()) / maxCooldown;
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + partialTicks) / maxCooldown;
}
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative());
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative(), player.isPotionActive(WizardryPotions.arcane_jammer));
}
GlStateManager.popMatrix();
}
/**
* Gets the name of the given spell, with formatting added according to its cooldown and whether the given player
* has discovered it.
@@ -224,9 +305,11 @@ public class GuiSpellDisplay {
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.getElement().getFormattingCode();
// Makes spells greyed out if they are in cooldown
String format = cooldown > 0 ? "\u00A78" : spell.getElement().getFormattingCode();
if(!discovered) format = "\u00A79";
// Obfuscates the spell name if the player has the arcane jammer effect
if(player.isPotionActive(WizardryPotions.arcane_jammer)) format = format + "\u00A7k";
String name = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world);
name = format + name;
@@ -458,8 +541,9 @@ public class GuiSpellDisplay {
* @param icon A {@code ResourceLocation} corresponding to the icon of the selected spell.
* @param cooldownBarProgress The fraction of the cooldown bar to draw; must be between 0 and 1 (inclusive).
* @param creativeMode True to draw the creative mode HUD, false for the survival mode version.
* @param jammed True to show the 'glitch' effect (user for arcane jammer), false to draw normally.
*/
public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode){
public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode, boolean jammed){
// Moves the origin if the HUD does not mirror; neatens the rest of the code.
if(flipX && !mirrorX) x -= width;
@@ -476,8 +560,13 @@ public class GuiSpellDisplay {
int x1 = flipX && mirrorX ? x - spellIconInsetX - SPELL_ICON_SIZE : x + spellIconInsetX;
// y is upside-down so this is the other way round
int y1 = flipY && mirrorY ? y + spellIconInsetY : y - spellIconInsetY - SPELL_ICON_SIZE;
DrawingUtils.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE);
if(jammed){
random.setSeed(Minecraft.getMinecraft().world.getTotalWorldTime() / 2);
DrawingUtils.drawGlitchRect(random, x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, false, false);
}else{
DrawingUtils.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE);
}
// Background of spell hud
mc.renderEngine.bindTexture(texture);
@@ -486,7 +575,11 @@ public class GuiSpellDisplay {
y1 = flipY && mirrorY ? y : y - height;
// The 128 here is a uv value, not a dimension, and hence is left as a hardcoded number.
// TODO: Since the HUD is wider than it is tall, perhaps the creative mode texture should be in the bottom half instead of the right half?
DrawingUtils.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
if(jammed){
DrawingUtils.drawGlitchRect(random, x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
}else{
DrawingUtils.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
}
// Cooldown bar
if(!creativeMode && cooldownBarProgress > 0 && (showCooldownWhenFull || cooldownBarProgress < 1)){
@@ -498,8 +591,12 @@ public class GuiSpellDisplay {
int u = cooldownBarX; // This doesn't change, even when cooldownBarMirrorX is true, because it should
int v = height; // always start with the left-hand in the actual texture file
DrawingUtils.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
if(jammed){
DrawingUtils.drawGlitchRect(random, x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
}else{
DrawingUtils.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
}
}
GlStateManager.popMatrix();
@@ -0,0 +1,138 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
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.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Keyboard;
/** Abstract base class for both {@link GuiSpellBook} and {@link GuiLectern}. Centralises code common to both
* those classes. */
public abstract class GuiSpellInfo extends GuiScreen {
protected static final String TRANSLATION_KEY_PREFIX = "gui." + Wizardry.MODID + ":spell_book";
protected final int xSize, ySize;
protected int textureWidth = 512;
protected int textureHeight = 256;
public GuiSpellInfo(int xSize, int ySize){
super();
this.xSize = xSize;
this.ySize = ySize;
}
protected void setTextureSize(int width, int height){
this.textureWidth = width;
this.textureHeight = height;
}
/** Returns the spell to be displayed by this GUI. */
public abstract Spell getSpell();
/** Returns the main texture for the background of this GUI. */
public abstract ResourceLocation getTexture();
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
int left = this.width/2 - xSize/2;
int top = this.height/2 - this.ySize/2;
this.drawDefaultBackground();
this.drawBackgroundLayer(left, top, mouseX, mouseY);
super.drawScreen(mouseX, mouseY, partialTicks); // Just draws the buttons
this.drawForegroundLayer(left, top, mouseX, mouseY);
}
/**
* Draws the background of the spell info GUI. This is called before buttons are drawn.
* @param left The x-coordinate of the left-hand edge of the GUI
* @param top The y-coordinate of the top edge of the GUI
* @param mouseX The current x position of the mouse pointer
* @param mouseY The current y position of the mouse pointer
*/
protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null);
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 ? getSpell().getIcon() : Spells.none.getIcon());
DrawingUtils.drawTexturedRect(left + 146, top + 20, 0, 0, 128, 128, 128, 128);
Minecraft.getMinecraft().renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(left, top, 0, 0, xSize, ySize, textureWidth, textureHeight);
}
/**
* Draws the foreground of the spell info GUI. This is called after buttons are drawn.
* @param left The x-coordinate of the left-hand edge of the GUI
* @param top The y-coordinate of the top edge of the GUI
* @param mouseX The current x position of the mouse pointer
* @param mouseY The current y position of the mouse pointer
*/
protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null);
if(discovered){
this.fontRenderer.drawString(getSpell().getDisplayName(), left + 17, top + 15, 0);
this.fontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26, 0x777777);
}else{
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(getSpell(), mc.world), left + 17,
top + 15, 0);
this.mc.standardGalacticFontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26,
0x777777);
}
// Novice is usually white but this doesn't show up
String tier = I18n.format(TRANSLATION_KEY_PREFIX + ".tier", getSpell().getTier() == Tier.NOVICE ?
"\u00A77" + getSpell().getTier().getDisplayName() : getSpell().getTier().getDisplayNameWithFormatting());
this.fontRenderer.drawString(tier, left + 17, top + 45, 0);
String element = I18n.format(TRANSLATION_KEY_PREFIX + ".element", getSpell().getElement().getFormattingCode() + getSpell().getElement().getDisplayName());
if(!discovered) element = I18n.format(TRANSLATION_KEY_PREFIX + ".element_undiscovered");
this.fontRenderer.drawString(element, left + 17, top + 57, 0);
String manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost", getSpell().getCost());
if(getSpell().isContinuous) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_continuous", getSpell().getCost());
if(!discovered) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_undiscovered");
this.fontRenderer.drawString(manaCost, left + 17, top + 69, 0);
if(discovered){
this.fontRenderer.drawSplitString(getSpell().getDescription(), left + 17, top + 83, 118, 0);
}else{
this.mc.standardGalacticFontRenderer.drawSplitString(
SpellGlyphData.getGlyphDescription(getSpell(), mc.world), left + 17, top + 83, 118, 0);
}
}
@Override
public void initGui(){
super.initGui();
Keyboard.enableRepeatEvents(true);
this.buttonList.clear();
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
}
@Override
public void onGuiClosed(){
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
}
@Override
public boolean doesGuiPauseGame(){
return Wizardry.settings.booksPauseGame;
}
}
@@ -0,0 +1,129 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMerchant;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ContainerMerchant;
import net.minecraft.inventory.Slot;
import net.minecraft.util.ResourceLocation;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList;
import net.minecraftforge.client.event.GuiContainerEvent;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
@EventBusSubscriber(Side.CLIENT)
public class WizardTradeTweaksHandler {
private static final ResourceLocation NEW_SPELL_ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/container/new_spell_indicator.png");
private static final int ICON_WIDTH = 8;
private static final int ICON_HEIGHT = 8;
private static final int ANIMATION_FRAMES = 4;
private static final int ANIMATION_FRAME_TIME = 2; // In ticks
private static final int ANIMATION_PERIOD = 40; // In frame durations
private static int tradeIndex; // Mirrors GuiMerchant#selectedMerchantRecipe (don't want to reflect into it every frame)
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void onGuiOpenEvent(GuiOpenEvent event){
if(event.getGui() instanceof GuiMerchant) tradeIndex = 0;
}
@SubscribeEvent
public static void onActionPerformedPostEvent(ActionPerformedEvent.Post event){
if(event.getGui() instanceof GuiMerchant){
MerchantRecipeList recipes = ((GuiMerchant)event.getGui()).getMerchant().getRecipes(Minecraft.getMinecraft().player);
if(recipes == null) return;
if(event.getButton().id == 1){ // Next
tradeIndex = Math.min(tradeIndex + 1, recipes.size());
}else if(event.getButton().id == 2){ // Previous
tradeIndex = Math.max(tradeIndex - 1, 0);
}
}
}
// 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();
// Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard.
MerchantRecipeList trades = gui.getMerchant().getRecipes(Minecraft.getMinecraft().player);
if(trades == null) return;
// Using == the specific item rather than instanceof because that's how trades do it.
if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book
|| gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){
for(MerchantRecipe trade : trades){
if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){
Slot slot = gui.inventorySlots.getSlot(2);
// 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.
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
gui.getSlotUnderMouse() == slot);
}
}
}
// New spell indicator
if(gui.inventorySlots instanceof ContainerMerchant){
// Can't use getCurrentRecipe because that only gets updated when the correct items are given
MerchantRecipe recipe = trades.get(tradeIndex);
if(recipe != null && recipe.getItemToSell().getItem() instanceof ItemSpellBook){
EntityPlayer player = Minecraft.getMinecraft().player;
Spell spell = Spell.byMetadata(recipe.getItemToSell().getMetadata());
if(Wizardry.settings.discoveryMode && !player.isCreative() && Wizardry.proxy.shouldDisplayDiscovered(spell, recipe.getItemToSell())
&& WizardData.get(player) != null && !WizardData.get(player).hasSpellBeenDiscovered(spell)){
int x = gui.inventorySlots.getSlot(2).xPos + 14;
int y = gui.inventorySlots.getSlot(2).yPos - 17;
RenderHelper.enableGUIStandardItemLighting();
GlStateManager.color(1, 1, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(NEW_SPELL_ICON);
int frame = Math.max(player.ticksExisted/ANIMATION_FRAME_TIME % ANIMATION_PERIOD - (ANIMATION_PERIOD - ANIMATION_FRAMES), 0);
DrawingUtils.drawTexturedRect(x, y, 0, frame * ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT * ANIMATION_FRAMES);
RenderHelper.disableStandardItemLighting();
// int mouseX = event.getMouseX() - gui.getGuiLeft();
// int mouseY = event.getMouseY() - gui.getGuiTop();
//
// if(mouseX >= x + 1 && mouseX < x + w + 1 && mouseY >= y - 1 && mouseY < y + h + 1){
// GuiUtils.drawHoveringText(Collections.singletonList("You haven't discovered this spell yet"), mouseX,
// mouseY, gui.width, gui.height, 150, Minecraft.getMinecraft().fontRenderer);
// }
}
}
}
}
}
}
@@ -25,15 +25,18 @@ public class GuiConfigWizardry extends GuiConfig {
private static List<IConfigElement> getConfigEntries(){
List<IConfigElement> configList = new ArrayList<IConfigElement>(1);
List<IConfigElement> configList = new ArrayList<>(1);
configList.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class));
configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class));
configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class));
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.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class));
configList.add(new DummyCategoryElement("difficultyConfig", "config." + Wizardry.MODID + ".category." + Settings.DIFFICULTY_CATEGORY, DifficultyCategory.class));
configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class));
configList.add(new DummyCategoryElement("tweaksConfig", "config." + Wizardry.MODID + ".category." + Settings.TWEAKS_CATEGORY, TweaksCategory.class));
configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class));
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("artefactsConfig", "config." + Wizardry.MODID + ".category." + Settings.ARTEFACTS_CATEGORY, ArtefactsCategory.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());
@@ -79,6 +82,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.GAMEPLAY_CATEGORY; }
}
/** Difficulty category of the config gui. */
public static class DifficultyCategory extends CategoryBase {
public DifficultyCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.DIFFICULTY_CATEGORY; }
}
/** Worldgen category of the config gui. */
public static class WorldgenCategory extends CategoryBase {
@@ -89,6 +102,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; }
}
/** Tweaks category of the config gui. */
public static class TweaksCategory extends CategoryBase {
public TweaksCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.TWEAKS_CATEGORY; }
}
/** Commands category of the config gui. */
public static class CommandsCategory extends CategoryBase {
@@ -119,6 +142,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.SPELLS_CATEGORY; }
}
/** Artefacts category of the config gui. */
public static class ArtefactsCategory extends CategoryBase {
public ArtefactsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.ARTEFACTS_CATEGORY; }
}
/** Resistances category of the config gui. */
public static class ResistancesCategory extends CategoryBase {
@@ -65,15 +65,15 @@ public class GuiSelectHUDSkin extends GuiSelectString {
float scale = Math.min((previewRight - previewLeft - 2*previewBorder)/(float)skin.getWidth(),
(previewBottom - previewTop - 2*previewBorder)/(float)skin.getHeight());
float x = (previewLeft + previewRight)/2 - (skin.getWidth()*scale)/2;
float y = (previewBottom + previewTop)/2 + (skin.getHeight()*scale)/2;
float x = (previewLeft + previewRight)/2f - (skin.getWidth()*scale)/2;
float y = (previewBottom + previewTop)/2f + (skin.getHeight()*scale)/2;
GlStateManager.pushMatrix();
GlStateManager.scale(scale, scale, scale);
skin.drawBackground((int)(x/scale), (int)(y/scale), false, false,
Spells.magic_missile.getIcon(), 0.6f, false);
Spells.magic_missile.getIcon(), 0.6f, false, false);
skin.drawText((int)(x/scale), (int)(y/scale), false, false,
Spells.none.getDisplayNameWithFormatting(),
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.gui.handbook;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.util.JsonUtils;
@@ -50,7 +50,7 @@ class Contents {
/** Returns an unmodifiable, flattened collection of all the buttons in this contents. */
Collection<GuiButton> getButtons(){
return WizardryUtilities.flatten(buttons);
return JavaUtils.flatten(buttons);
}
void addEntry(Section section){
@@ -8,7 +8,8 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.ClientProxy;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.gui.GuiButtonInvisible;
import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type;
import electroblob.wizardry.client.gui.GuiButtonTurnPage;
import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.Tier;
@@ -54,7 +55,7 @@ public class GuiWizardHandbook extends GuiScreen {
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook/handbook.png");
/** Global Gson instance for the handbook. */
private static final Gson gson = new Gson();
@@ -244,6 +245,8 @@ public class GuiWizardHandbook extends GuiScreen {
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
this.drawDefaultBackground();
int left = this.width / 2 - GUI_WIDTH / 2;
int top = this.height / 2 - GUI_HEIGHT / 2;
@@ -349,19 +352,19 @@ public class GuiWizardHandbook extends GuiScreen {
this.buttonList.clear();
this.buttonList.add(next = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(previous = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(nextSection = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - BUTTON_SPACING,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(previousSection = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X + BUTTON_SPACING,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(menu = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH/2 - 28,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(bookmark = new GuiButtonInvisible(nextButtonId++, left + 130, top + 172, 11, 19) {
@Override
@@ -398,6 +401,11 @@ public class GuiWizardHandbook extends GuiScreen {
*/
public static void loadHandbookFile(IResourceManager manager){
if(manager == null){
Wizardry.logger.error("Tried to reload the handbook file, but received a null resource manager. Aborting!");
return;
}
IResource handbookFile = getHandbookResource(manager);
if(handbookFile != null){
@@ -6,7 +6,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
@@ -63,7 +63,7 @@ class Section {
}
Collection<GuiButton> getButtons(){
return WizardryUtilities.flatten(buttons);
return JavaUtils.flatten(buttons);
}
/**
@@ -0,0 +1,74 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.block.BlockBookshelf;
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.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.property.IExtendedBlockState;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BakedModelBookshelf implements IBakedModel {
private final IBakedModel bookshelf;
private final IBakedModel[][] books;
public BakedModelBookshelf(IBakedModel bookshelf, IBakedModel[][] books){
this.bookshelf = bookshelf;
this.books = books;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
IBakedModel fallback = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelManager().getMissingModel();
if(state == null) return fallback.getQuads(null, side, rand);
// It took me waaay too long to realise I needed a new list here
List<BakedQuad> quads = new ArrayList<>(bookshelf.getQuads(state, side, rand)); // Lists ain't immutable, chief
IExtendedBlockState extendedState = (IExtendedBlockState)state;
for(int i = 0; i<BlockBookshelf.SLOT_COUNT; i++){
Integer value = extendedState.getValue(BlockBookshelf.BOOKS[i]);
if(value == null || value < 0) return fallback.getQuads(null, side, rand);
if(value < books.length) quads.addAll(books[value][i].getQuads(state, side, rand)); // Empty slots use books.length
}
return quads;
}
@Override
public boolean isBuiltInRenderer(){
return false;
}
// Delegate everything else to the main bookshelf model
@Override
public boolean isAmbientOcclusion(){
return bookshelf.isAmbientOcclusion();
}
@Override
public boolean isGui3d(){
return bookshelf.isGui3d();
}
@Override
public TextureAtlasSprite getParticleTexture(){
return bookshelf.getParticleTexture();
}
@Override
public ItemOverrideList getOverrides(){
return bookshelf.getOverrides();
}
}
@@ -0,0 +1,163 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A special type of model that generates override models dynamically. This allows the same overrides to be applied to
* multiple item models without needing an override version of each one just to point to a texture.
* <p></p>
* To use this system in a model file:<p></p>
* 1. Make a base model file and define the item overrides in it. <b>This model must be placed in the folder
* {@code models/item/override_generators}</b> (unfortunately the folder has to be predefined, see below).<br>
* 2. Make the model files your overrides point to. These should define everything about the model except textures.<br>
* 3. In each model that should generate overrides dynamically, define a single override that points to the location of
* the model from step 1 (the predicate doesn't matter).<br>
* <p></p>
* It is advisable to make the parent of the override model (from step 2) the same as the parent of the model to
* be overridden, so that they share any necessary properties and texture variables. For example, both
* {@code ebwizardry:item/magic_wand.json} and {@code ebwizardry:item/wand_point.json} have {@code item/handheld} as
* their parent. The override model will reference the textures in the original model, so if the override defines a
* face with the texture {@code "#face"}, the texture used will be the file associated with {@code "face"} in the
* original model. For simple models, this will just be the standard {@code "layer0"}.
* @since Wizardry 4.3
* @author Electroblob
*/
// Okay, it's not *that* bad if you only have one override, but imagine having to duplicate every single wand model 5
// times if we wanted 5 animations...
// The nice thing is resource packs don't lose any flexibility this way, they can still redefine the models themselves
// and specify custom models for the overrides if they want, or they can keep using this system - in fact, they GAIN
// flexibility because they can also overwrite the base wand.json model and specify global overrides too
@Mod.EventBusSubscriber(Side.CLIENT)
public class BakedModelGenerateOverrides implements IBakedModel {
// I tried having this as a prefix that gets removed at runtime, which worked fine but it spammed the log with
// errors because the prefixed file doesn't actually exist, so even though it's never used the game still complains
private static final String OVERRIDE_GENERATORS = "override_generators";
private final IBakedModel delegate;
private final ItemOverrideList overrides;
public BakedModelGenerateOverrides(IBakedModel delegate, ItemOverrideList overrides){
this.delegate = delegate;
this.overrides = overrides;
}
@Override
public ItemOverrideList getOverrides(){
return overrides; // The only thing that's not delegated
}
// Delegate everything else
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
return delegate.getQuads(state, side, rand);
}
@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 boolean isAmbientOcclusion(IBlockState state){
return delegate.isAmbientOcclusion(state);
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType){
return delegate.handlePerspective(cameraTransformType);
}
// Baking
@SubscribeEvent
public static void bake(ModelBakeEvent event){
for(ModelResourceLocation location : event.getModelRegistry().getKeys()){
IBakedModel original = event.getModelRegistry().getObject(location);
if(original != null){
original.getOverrides().getOverrides().stream().map(ItemOverride::getLocation)
.filter(l -> l.getPath().contains(OVERRIDE_GENERATORS)).findFirst()
.ifPresent(l -> event.getModelRegistry().putObject(location,
substituteWandModel(event.getModelManager(), location, original, l)));
}
}
}
// We have to use ModelBakeEvent for this because we need the original model to be baked with the vanilla system
private static IBakedModel substituteWandModel(ModelManager modelManager, ModelResourceLocation location, IBakedModel original, ResourceLocation overrideLocation){
try {
IModel unbakedOriginal = ModelLoaderRegistry.getModel(location);
IModel referenceModel = ModelLoaderRegistry.getModel(overrideLocation);
ModelBlock vanillaOriginal = unbakedOriginal.asVanillaModel().orElse(null);
ModelBlock vanillaRefModel = referenceModel.asVanillaModel().orElse(null);
if(vanillaOriginal != null && vanillaRefModel != null){
// So... many... data structures...
List<ItemOverride> overrides = vanillaRefModel.getOverrides();
Map<ResourceLocation, IBakedModel> replacementMap = new HashMap<>();
for(ItemOverride override : overrides){
IModel replacement = ModelLoaderRegistry.getModel(override.getLocation()).retexture(ImmutableMap.copyOf(vanillaOriginal.textures));
replacementMap.put(override.getLocation(), replacement.bake(unbakedOriginal.getDefaultState(), DefaultVertexFormats.ITEM, ModelLoader.defaultTextureGetter()));
}
ItemOverrideListMapped overrideList = new ItemOverrideListMapped(overrides, replacementMap);
return new BakedModelGenerateOverrides(original, overrideList);
}
}catch(Exception exception){
Wizardry.logger.error("Error baking item display override models: ", exception);
}
return modelManager.getMissingModel();
}
}
@@ -28,7 +28,7 @@ import java.util.List;
* <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>
* models into the item models folder and add {@code "shade":false} where appropriate. (If it works, it works, right?)</i>
*
* @author Electroblob
* @author raoulvdberge
@@ -0,0 +1,42 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemOverride;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/** A version of {@link ItemOverrideList} that uses a predefined map to substitute a specific baked model depending on
* the location the currently-applicable override points to. */
public class ItemOverrideListMapped extends ItemOverrideList {
private final Map<ResourceLocation, IBakedModel> replacementMap;
public ItemOverrideListMapped(List<ItemOverride> overrides, Map<ResourceLocation, IBakedModel> replacementMap){
super(overrides);
this.replacementMap = ImmutableMap.copyOf(replacementMap);
}
@Override
public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
if(!stack.isEmpty() && stack.getItem().hasCustomProperties()){
// Get the location the original override points to...
ResourceLocation location = applyOverride(stack, world, entity); // I wonder why this is deprecated?
if(location != null){
return replacementMap.get(location); // ... then substitute in the corresponding generated model
}
}
return originalModel;
}
}
@@ -0,0 +1,99 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.model.IModelState;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
public class ModelBookshelf implements IModel {
private static final ResourceLocation DEFAULT_BOOK_TEXTURE = new ResourceLocation(Wizardry.MODID, "blocks/books");
private static final List<ResourceLocation> bookModelLocations = new ArrayList<>();
private final ModelResourceLocation bookshelfModelLocation;
private final String variant;
static {
for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){
bookModelLocations.add(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/books" + i));
}
}
public ModelBookshelf(String bookshelfModelName){
String[] args = bookshelfModelName.split("#", 2);
String model = args[0];
variant = args.length == 2 ? args[1] : null;
bookshelfModelLocation = new ModelResourceLocation(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/" + model), variant);
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter){
try {
IModel bookshelfModel = ModelLoaderRegistry.getModel(bookshelfModelLocation);
// I don't know why default state works here (surely it ought to be the state param?), but it works so who cares
IBakedModel bookshelf = bookshelfModel.bake(bookshelfModel.getDefaultState(), format, bakedTextureGetter);
ImmutableList<ResourceLocation> textures = BlockBookshelf.getBookTextures();
IBakedModel[][] books = new IBakedModel[textures.size()][BlockBookshelf.SLOT_COUNT];
for(int i = 0; i < textures.size(); i++){
ImmutableMap<String, String> retexturer = ImmutableMap.of("books", textures.get(i).toString());
for(int j = 0; j < BlockBookshelf.SLOT_COUNT; j++){
IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(bookModelLocations.get(j), variant)).retexture(retexturer);
books[i][j] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here!
}
}
// To clarify: the whole point of doing this was to avoid having to bake 4 * 2^12 models per bookshelf type
// Therefore we have to have a baked bookshelf model that's kind of dynamic
return new BakedModelBookshelf(bookshelf, books);
}catch(Exception exception){
Wizardry.logger.error("Error baking bookshelf models: ", exception);
return ModelLoaderRegistry.getMissingModel().bake(state, format, bakedTextureGetter);
}
}
@Override
public Collection<ResourceLocation> getDependencies(){
return bookModelLocations;
}
@Override
public Collection<ResourceLocation> getTextures(){
// I simply have no idea how I'm *supposed* to do it, so I'm using the best solution I can come up with
// This is at least better than manually registering it with TextureStitchEvent
// However I'm also pretty sure that if a texture pack decides to change the texture that the book models point
// to, it won't work... but since nobody ever will, I don't really care!
// Why am I confused?
// 1. How come it magically finds the bookshelf texture, but not the book texture?
// 2. Why don't the textures get retrieved automatically based on getDependencies? Surely that's the point?
// 3. Is it correct to use getModel() to get the dependency models upon construction of this class so I can
// access their textures properly? Or is that too early?
// 4. If so, what's the point of getDependencies, and why should I override it?
return BlockBookshelf.getBookTextures();// ImmutableList.of(DEFAULT_BOOK_TEXTURE);
}
}
@@ -0,0 +1,74 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
// Made with Blockbench 3.5.2
public class ModelBoulder extends ModelBase {
private final ModelRenderer mainGroup;
private final ModelRenderer box1;
private final ModelRenderer box2;
private final ModelRenderer box3;
private final ModelRenderer box4;
private final ModelRenderer box5;
private final ModelRenderer box6;
public ModelBoulder(){
textureWidth = 64;
textureHeight = 32;
mainGroup = new ModelRenderer(this);
mainGroup.setRotationPoint(0.0F, 24.0F, 0.0F);
box1 = new ModelRenderer(this);
box1.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box1);
box1.cubeList.add(new ModelBox(box1, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box2 = new ModelRenderer(this);
box2.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box2);
setRotationAngle(box2, -1.5708F, 0.0F, 0.0F);
box2.cubeList.add(new ModelBox(box2, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box3 = new ModelRenderer(this);
box3.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box3);
setRotationAngle(box3, 0.0F, -1.5708F, 0.0F);
box3.cubeList.add(new ModelBox(box3, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box4 = new ModelRenderer(this);
box4.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box4);
setRotationAngle(box4, 0.0F, 1.5708F, 0.0F);
box4.cubeList.add(new ModelBox(box4, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box5 = new ModelRenderer(this);
box5.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box5);
setRotationAngle(box5, 1.5708F, 0.0F, 0.0F);
box5.cubeList.add(new ModelBox(box5, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box6 = new ModelRenderer(this);
box6.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box6);
setRotationAngle(box6, 3.1416F, 0.0F, 0.0F);
box6.cubeList.add(new ModelBox(box6, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
}
@Override
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
mainGroup.render(f5);
}
public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z){
modelRenderer.rotateAngleX = x;
modelRenderer.rotateAngleY = y;
modelRenderer.rotateAngleZ = z;
}
}
@@ -0,0 +1,35 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
// Made with Blockbench 3.5.4
public class ModelIceBarrier extends ModelBase {
private final ModelRenderer mainGroup;
public ModelIceBarrier(){
textureWidth = 64;
textureHeight = 64;
mainGroup = new ModelRenderer(this);
mainGroup.setRotationPoint(0.0F, 24.0F, 0.0F);
setRotationAngle(mainGroup, 0.0F, 0.0F, 0.7854F);
mainGroup.cubeList.add(new ModelBox(mainGroup, 0, 0, -14.5F, -14.5F, -1.5F, 29, 29, 3, 0.0F, false));
mainGroup.cubeList.add(new ModelBox(mainGroup, 0, 32, -9.0F, -9.0F, -4.0F, 18, 18, 8, 0.0F, false));
}
@Override
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
mainGroup.render(f5);
}
public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z) {
modelRenderer.rotateAngleX = x;
modelRenderer.rotateAngleY = y;
modelRenderer.rotateAngleZ = z;
}
}
@@ -0,0 +1,28 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
public class ModelLoaderBookshelf implements ICustomModelLoader {
private static final String SMART_MODEL_PREFIX = "models/block/smart_model/";
@Override
public void onResourceManagerReload(IResourceManager resourceManager){
// Do nothing
}
@Override
public boolean accepts(ResourceLocation modelLocation){
return modelLocation.getNamespace().equals(Wizardry.MODID) && modelLocation.getPath().startsWith(SMART_MODEL_PREFIX);
}
@Override
public IModel loadModel(ResourceLocation modelLocation){
return new ModelBookshelf(modelLocation.getPath().substring(SMART_MODEL_PREFIX.length()));
}
}
@@ -0,0 +1,48 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
/** Based on ModelEnderCrystal */
public class ModelRemnant extends ModelBase {
private final ModelRenderer cube = new ModelRenderer(this, "cube");
public ModelRemnant(){
this.cube.setTextureOffset(0, 0).addBox(-4.0F, -4.0F, -4.0F, 8, 8, 8);
}
@Override
public void render(Entity entity, float rotationSpeed, float expandFraction, float age, float netHeadYaw, float headPitch, float scale){
GlStateManager.pushMatrix();
// GlStateManager.scale(2.0F, 2.0F, 2.0F);
// GlStateManager.translate(0.0F, -0.5F, 0.0F);
GlStateManager.disableCull();
GlStateManager.translate(0.0F, entity.height/2, 0.0F);
float s = expandFraction + MathHelper.sin(age * 0.1f) * 0.06f;
GlStateManager.scale(s, s, s);
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
this.cube.render(scale);
GlStateManager.scale(0.875F, 0.875F, 0.875F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
this.cube.render(scale);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
}
@@ -5,10 +5,12 @@ 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.ItemBlockMultiTextured;
import electroblob.wizardry.item.ItemCrystal;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
import net.minecraft.block.BlockPlanks;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelBakery;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
@@ -20,11 +22,15 @@ 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.client.model.ModelLoaderRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.oredict.OreDictionary;
import java.util.ArrayList;
import java.util.List;
/**
* Class responsible for registering all of wizardry's item and block models.
*
@@ -37,32 +43,39 @@ public final class WizardryModels {
private WizardryModels(){} // No instances!
/** Keeps track of all items whose models have been registered manually to exclude them from automatic registry of
* standard item models. Internal only, this gets cleared once model registry is complete. */
private static final List<Item> registeredItems = new ArrayList<>();
@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);
ItemBlockMultiTextured crystalBlockItem = (ItemBlockMultiTextured)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);
ItemBlockMultiTextured runestoneItem = (ItemBlockMultiTextured)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);
ItemBlockMultiTextured pedestalItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
registerMultiTexturedModel(pedestalItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.gilded_wood, new StateMap.Builder()
.withName(BlockPlanks.VARIANT).withSuffix("_gilded_wood").build());
ItemBlockMultiTextured gildedWoodItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.gilded_wood);
registerMultiTexturedModel(gildedWoodItem);
// Explanation for all this here -> https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe05_block_dynamic_block_model2
ModelLoaderRegistry.registerLoader(new ModelLoaderBookshelf());
// Items
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
@@ -72,11 +85,9 @@ public final class WizardryModels {
registerWandModel(WizardryItems.advanced_wand);
registerWandModel(WizardryItems.master_wand);
registerItemModel(WizardryItems.spell_book);
registerItemModel(WizardryItems.spell_book); // Also need this or auto-registry will ignore it
// Wildcard registered for wizard trades.
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
registerItemModel(WizardryItems.arcane_tome);
registerItemModel(WizardryItems.wizard_handbook);
registerWandModel(WizardryItems.novice_fire_wand);
registerWandModel(WizardryItems.novice_ice_wand);
@@ -110,160 +121,16 @@ public final class WizardryModels {
registerWandModel(WizardryItems.master_sorcery_wand);
registerWandModel(WizardryItems.master_healing_wand);
registerItemModel(WizardryItems.spectral_sword);
registerItemModel(WizardryItems.spectral_pickaxe);
registerItemModel(WizardryItems.spectral_bow);
registerMultiTexturedModel((ItemSpectralDust)WizardryItems.spectral_dust);
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);
// Automatic item model registry
for(Item item : Item.REGISTRY){
if(!registeredItems.contains(item) && item.getRegistryName().getNamespace().equals(Wizardry.MODID)){
registerItemModel(item); // Standard item model
}
}
registeredItems.clear(); // Might as well clean this up
}
@SubscribeEvent
@@ -280,11 +147,12 @@ public final class WizardryModels {
if(location.getNamespace().equals(Wizardry.MODID)){
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){
IBakedModel original = event.getModelRegistry().getObject(location);
IBakedModel original = event.getModelRegistry().getObject(location);
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")
|| location.getPath().endsWith("imbuement_altar")){ // Ends with to exclude inactive version
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay"));
}else if(location.getPath().contains("spectral_block")){
IBakedModel original = event.getModelRegistry().getObject(location);
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "spectral_block"));
}
}
@@ -302,6 +170,7 @@ public final class WizardryModels {
ModelBakery.registerItemVariants(item, new ModelResourceLocation(item.getRegistryName(), "inventory"));
// Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation(item.getRegistryName(), "inventory"));
registeredItems.add(item);
}
/**
@@ -315,6 +184,7 @@ public final class WizardryModels {
ModelBakery.registerItemVariants(item, new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
// Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
registeredItems.add(item);
}else{
registerItemModel(item);
}
@@ -338,6 +208,8 @@ public final class WizardryModels {
new ModelResourceLocation(item.getModelName(stack), "inventory"));
}
}
registeredItems.add(item);
}
/**
@@ -348,6 +220,7 @@ public final class WizardryModels {
private static void registerItemModel(Item item, int metadata, String variant){
ModelLoader.setCustomModelResourceLocation(item, metadata,
new ModelResourceLocation(item.getRegistryName(), variant));
registeredItems.add(item); // Still ought to do this in case I ever use this method alone for an item
}
}
@@ -0,0 +1,60 @@
package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
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;
//@SideOnly(Side.CLIENT)
@Mod.EventBusSubscriber(Side.CLIENT)
public class ParticleBlockHighlight extends ParticleWizardry {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "particle/block_highlight");
public ParticleBlockHighlight(World world, double x, double y, double z){
super(world, x, y, z, TEXTURE);
this.particleGravity = 0;
this.setMaxAge(160);
// 5 makes it the exact size of a block face, plus a little bit to account for the anti-z-fighting offset
this.particleScale = 5 * (1 + 2 * (float)GeometryUtils.ANTI_Z_FIGHTING_OFFSET);
this.shaded = false;
}
@Override
public boolean shouldDisableDepth(){
return true;
}
@Override
public void onUpdate(){
super.onUpdate();
// Fading
if(this.particleAge > this.particleMaxAge/2){
this.setAlphaF(1 - ((float)this.particleAge - this.particleMaxAge/2f) / (this.particleMaxAge/2f));
}
EnumFacing facing = EnumFacing.fromAngle(yaw);
if(pitch == 90) facing = EnumFacing.UP;
if(pitch == -90) facing = EnumFacing.DOWN;
// Disappears if there is no block behind it (this is the same check used to spawn it)
if(!world.getBlockState(new BlockPos(posX, posY, posZ).offset(facing.getOpposite())).getMaterial().isSolid()){
this.setExpired();
}
}
@SubscribeEvent
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
event.getMap().registerSprite(TEXTURE);
}
}
@@ -59,7 +59,7 @@ public class ParticleBuff extends ParticleWizardry {
float rotationYZ, float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
GlStateManager.pushMatrix();
GlStateManager.pushAttrib();
@@ -0,0 +1,52 @@
package electroblob.wizardry.client.particle;
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;
@Mod.EventBusSubscriber(Side.CLIENT)
public class ParticleCloud extends ParticleWizardry {
private static final ResourceLocation[] TEXTURES = generateTextures("cloud", 4);
public ParticleCloud(World world, double x, double y, double z){
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
this.setRBGColorF(1, 1, 1);
this.particleMaxAge = 48 + this.rand.nextInt(12);
this.particleScale *= 6;
this.setGravity(false);
this.setAlphaF(0);
this.canCollide = false;
this.shaded = true;
}
@Override
public boolean shouldDisableDepth(){
return true;
}
@Override
public void onUpdate(){
super.onUpdate();
// Fading
float fadeTime = this.particleMaxAge * 0.3f;
this.setAlphaF(MathHelper.clamp(Math.min(this.particleAge / fadeTime, (this.particleMaxAge - this.particleAge) / fadeTime), 0, 1));
}
@SubscribeEvent
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
for(ResourceLocation texture : TEXTURES){
event.getMap().registerSprite(texture);
}
}
}
@@ -17,15 +17,4 @@ public class ParticleDust extends ParticleWizardry {
this.setRBGColorF(1, 1, 1);
}
@Override
public void onUpdate(){
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
this.move(this.motionX, this.motionY, this.motionZ);
if(this.particleMaxAge-- <= 0){
this.setExpired();
}
}
}
@@ -35,15 +35,10 @@ public class ParticleFlash extends ParticleWizardry {
int i = this.getBrightnessForRender(partialTicks);
int j = i >> 16 & 65535;
int k = i & 65535;
buffer.pos((double)(f5 - rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 - rotationYZ * f4 - rotationXZ * f4)).tex(0.5D, 0.375D).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.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){
return 15728880;
buffer.pos(f5 - rotationX * f4 - rotationXY * f4, f6 - rotationZ * f4, f7 - rotationYZ * f4 - rotationXZ * f4).tex(0.5D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(f5 - rotationX * f4 + rotationXY * f4, f6 + rotationZ * f4, 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(f5 + rotationX * f4 + rotationXY * f4, f6 + rotationZ * f4, 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(f5 + rotationX * f4 - rotationXY * f4, f6 - rotationZ * f4, f7 + rotationYZ * f4 - rotationXZ * f4).tex(0.25D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
}
}
@@ -0,0 +1,78 @@
package electroblob.wizardry.client.particle;
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.util.ResourceLocation;
import net.minecraft.world.World;
import org.lwjgl.opengl.GL11;
public class ParticleGuardianBeam extends ParticleTargeted {
/** Half the width of the beam. */
private static final float THICKNESS = 0.15f;
private static final ResourceLocation TEXTURE = new ResourceLocation("minecraft:textures/entity/guardian_beam.png");
public ParticleGuardianBeam(World world, double x, double y, double z){
super(world, x, y, z); // Does't use TextureAtlasSprite
this.setRBGColorF(1, 1, 1);
this.setMaxAge(3);
this.particleScale = 1;
}
@Override
public int getFXLayer(){
return 3;
}
@Override
protected void draw(Tessellator tessellator, double length, float partialTicks){
float scale = this.particleScale;
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GlStateManager.pushMatrix();
// Enables texture tiling (Also used for guardian beam, beacon beam and ender crystal beam, amongst others)
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.rotate(Minecraft.getMinecraft().player.ticksExisted + partialTicks, 0, 0, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURE);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
float t = THICKNESS * scale;
float v1 = 2 * (particleAge + partialTicks) / particleMaxAge;
float v2 = v1 + (float)length * 2; // Multiply by 2 to make it squished like it is in vanilla
buffer.pos(-t, 0, 0) .tex(0, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos( t, 0, 0) .tex(0.5, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos( t, 0, length).tex(0.5, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(-t, 0, length).tex(0, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, -t, 0) .tex(0, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, t, 0) .tex(0.5, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, t, length).tex(0.5, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, -t, length).tex(0, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
tessellator.draw();
// 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.popMatrix();
// Makes the rain go weird
//GlStateManager.enableLighting();
}
}
@@ -41,7 +41,7 @@ public class ParticleSphere extends ParticleWizardry {
float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks);
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks);
@@ -1,132 +0,0 @@
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,6 +1,7 @@
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.Tessellator;
@@ -15,6 +16,8 @@ import javax.annotation.Nullable;
/** Superclass for particles with a second target entity or target position. */
public abstract class ParticleTargeted extends ParticleWizardry {
private static final double THIRD_PERSON_AXIAL_OFFSET = 1.2;
protected double targetX;
protected double targetY;
protected double targetZ;
@@ -73,16 +76,26 @@ public abstract class ParticleTargeted extends ParticleWizardry {
float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, 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);
// Translates the particle a short distance in front of the entity
if(this.entity != null && this.shouldApplyOriginOffset()){
if(this.entity != viewer || Minecraft.getMinecraft().gameSettings.thirdPersonView != 0){
Vec3d look = entity.getLook(partialTicks).scale(THIRD_PERSON_AXIAL_OFFSET);
x += look.x;
y += look.y;
z += look.z;
}
}
if(this.target != null){
this.targetX = this.target.prevPosX + (this.target.posX - this.target.prevPosX) * partialTicks;
double correction = this.target.getEntityBoundingBox().minY - this.target.posY;
double correction = this.target.posY - 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;
@@ -133,6 +146,12 @@ public abstract class ParticleTargeted extends ParticleWizardry {
GlStateManager.popMatrix();
}
/** Returns whether the origin of this particle should be moved a short distance in front of the entity it is
* linked to, if any. */
protected boolean shouldApplyOriginOffset(){
return true;
}
/** 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
@@ -46,6 +46,11 @@ public class ParticleVine extends ParticleTargeted {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
}
@Override
protected boolean shouldApplyOriginOffset(){
return false;
}
@Override
protected void draw(Tessellator tessellator, double length, float partialTicks){
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.ClientProxy;
import electroblob.wizardry.entity.ICustomHitbox;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.renderer.BufferBuilder;
@@ -27,7 +27,7 @@ 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
* {@code ParticleBuilder} by exposing all the necessary variables through setters, allowing them to be set on the fly
* rather than needing to be passed into the constructor.
* <p></p>
* The new system is as follows:
@@ -206,7 +206,7 @@ public abstract class ParticleWizardry extends Particle {
this.entity = entity;
// Set these to the correct values
if(entity != null){
this.setPosition(this.entity.posX + relativeX, this.entity.getEntityBoundingBox().minY
this.setPosition(this.entity.posX + relativeX, this.entity.posY
+ relativeY, this.entity.posZ + relativeZ);
this.prevPosX = this.posX;
this.prevPosY = this.posY;
@@ -336,7 +336,7 @@ public abstract class ParticleWizardry extends Particle {
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float lookZ, float lookY,
float lookX, float lookXY, float lookYZ){
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
if(Float.isNaN(this.yaw) || Float.isNaN(this.pitch)){
// Normal behaviour (rotates to face the viewer)
@@ -369,12 +369,18 @@ public abstract class ParticleWizardry extends Particle {
super.renderParticle(buffer, viewer, partialTicks, rotationX, rotationY, rotationZ, rotationYZ, rotationXY);
}
protected void updateEntityLinking(float partialTicks){
protected void updateEntityLinking(Entity viewer, float partialTicks){
// TODO: Still not working, it seems this bug was a thing back in 4.2.x anyway
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);
}else if(this.getFXLayer() == 3){
// Not sure why, but when fx layer is 3, the interp pos is wrong when not linked to an entity
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;
}
}
@@ -467,12 +473,12 @@ public abstract class ParticleWizardry extends Particle {
double searchRadius = 20;
List<Entity> nearbyEntities = WizardryUtilities.getEntitiesWithinRadius(searchRadius, this.posX,
List<Entity> nearbyEntities = EntityUtils.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();
if(nearbyEntities.stream().anyMatch(e -> e instanceof ICustomHitbox
&& ((ICustomHitbox)e).calculateIntercept(new Vec3d(posX, posY, posZ),
new Vec3d(prevPosX, prevPosY, prevPosZ), 0) != null)) this.setExpired();
}
@@ -547,7 +553,7 @@ public abstract class ParticleWizardry extends Particle {
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int, int)} which uses wizardry's
* mod ID automatically. */
public static ResourceLocation[][] generateTextures(String stem, int m, int n){
static ResourceLocation[][] generateTextures(String stem, int m, int n){
return generateTextures(Wizardry.MODID, stem, m, n);
}
@@ -1,176 +0,0 @@
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.size() >= 1 && 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);
boolean headWearHidden = false;
boolean bodyWearHidden = false;
boolean leftArmWearHidden = false;
boolean rightArmWearHidden = false;
boolean leftLegWearHidden = false;
boolean rightLegWearHidden = false;
// Hides the hat layer for bipeds
if(this.model instanceof ModelBiped){
headWearHidden = ((ModelBiped)this.model).bipedHeadwear.isHidden;
((ModelBiped)this.model).bipedHeadwear.isHidden = true;
}
if(this.model instanceof ModelPlayer){
bodyWearHidden = ((ModelPlayer)this.model).bipedBodyWear.isHidden;
leftArmWearHidden = ((ModelPlayer)this.model).bipedLeftArmwear.isHidden;
rightArmWearHidden = ((ModelPlayer)this.model).bipedRightArmwear.isHidden;
leftLegWearHidden = ((ModelPlayer)this.model).bipedLeftLegwear.isHidden;
rightLegWearHidden = ((ModelPlayer)this.model).bipedRightLegwear.isHidden;
((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 = headWearHidden;
if(this.model instanceof ModelPlayer){
((ModelPlayer)this.model).bipedBodyWear.isHidden = bodyWearHidden;
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = leftArmWearHidden;
((ModelPlayer)this.model).bipedRightArmwear.isHidden = rightArmWearHidden;
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = leftLegWearHidden;
((ModelPlayer)this.model).bipedRightLegwear.isHidden = rightLegWearHidden;
}
// 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,151 +0,0 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.client.ClientProxy;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
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.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 stone texture on a petrified creature. Handles dynamic tiling of the stone texture.
*
* @author Electroblob
* @since Wizardry 1.2
*/
public class LayerStone implements LayerRenderer<EntityLivingBase> {
protected ModelBase model;
private final RenderLivingBase<?> renderer;
private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.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 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<?>)renderer).addLayer(new LayerStone((RenderLivingBase<?>)renderer));
}
}
}
public LayerStone(RenderLivingBase<?> renderer){
this.renderer = renderer;
this.model = renderer.getMainModel();
}
// 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.PETRIFIED_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);
ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture();
if(breakingTexture != null){
// Block breaking animation
// TODO: Spider eyes and enderman eyes (any others?) show through the stone when the block is being
// broken...
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
this.renderer.bindTexture(breakingTexture);
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
headPitch, scale);
GlStateManager.disableBlend();
}else{
// Stone texture
this.renderer.bindTexture(texture);
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
headPitch, scale);
}
}
}
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;
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,14 +1,14 @@
package electroblob.wizardry.client.renderer;
/** Instances of this class can calculate their distance from the camera viewpoint and sort themselves accordingly. */
class RayHelper implements Comparable<RayHelper> {
public class RayHelper implements Comparable<RayHelper> {
int ordinal;
double x1, y1, z1;
double x2, y2, z2;
public double x1, y1, z1;
public double x2, y2, z2;
double offsetX, offsetY, offsetZ;
RayHelper(int ordinal, double x1, double y1, double z1, double x2, double y2, double z2, double offsetX,
public RayHelper(int ordinal, double x1, double y1, double z1, double x2, double y2, double z2, double offsetX,
double offsetY, double offsetZ){
this.ordinal = ordinal;
this.x1 = x1;
@@ -37,13 +37,7 @@ class RayHelper implements Comparable<RayHelper> {
@Override
public int compareTo(RayHelper ray){
if(this.getDistanceFromViewpoint() > ray.getDistanceFromViewpoint()){
return -1;
}else if(this.getDistanceFromViewpoint() < ray.getDistanceFromViewpoint()){
return 1;
}else{
return 0;
}
return Double.compare(ray.getDistanceFromViewpoint(), this.getDistanceFromViewpoint());
}
}
@@ -1,186 +0,0 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.entity.construct.EntityFireRing;
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.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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> {
private final ResourceLocation texture;
private float scale = 1.0f;
public RenderFireRing(RenderManager renderManager, ResourceLocation texture, float scale){
super(renderManager);
this.texture = texture;
this.scale = scale;
}
@Override
public void doRender(EntityFireRing entity, double par2, double par4, double par6, float par8, float par9){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float yOffset = 0;
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
this.bindTexture(texture);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
GlStateManager.rotate(-90, 1, 0, 0);
GlStateManager.scale(scale, scale, scale);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex();
buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex();
tessellator.draw();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
// Fire
GlStateManager.disableLighting();
TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher()
.getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture();
int sides = 16;
float height = 1.0f;
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)par2, (float)par4 + 0.05f, (float)par6);
float f1 = 1.0f;
GlStateManager.scale(f1, f1, f1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.getEntityBoundingBox().minY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, -2.3f);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10)
.endVertex();
buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10)
.endVertex();
buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81)
.endVertex();
buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81)
.endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)par2, (float)par4 + 0.05f, (float)par6);
float f1 = 1.0f;
GlStateManager.scale(f1, f1, f1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.getEntityBoundingBox().minY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, 2.3f);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10)
.endVertex();
buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10)
.endVertex();
buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81)
.endVertex();
buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81)
.endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
GlStateManager.enableLighting();
}
@Override
protected ResourceLocation getEntityTexture(EntityFireRing entity){
return null;
}
}
@@ -1,9 +1,9 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.potion.PotionContainment;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -24,14 +24,14 @@ import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber(Side.CLIENT)
public class RenderContainmentField {
private static final ResourceLocation[] textures = new ResourceLocation[8];
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");
for(int i = 0; i< TEXTURES.length; i++){
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/environment/containment_field_" + i + ".png");
}
}
@@ -42,7 +42,7 @@ public class RenderContainmentField {
if(player.isPotionActive(WizardryPotions.containment)){
Vec3d centre = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(player.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)));
Vec3d centre = GeometryUtils.getCentre(NBTUtil.getPosFromTag(player.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)));
float r = PotionContainment.getContainmentDistance(player.getActivePotionEffect(WizardryPotions.containment).getAmplifier());
GlStateManager.pushMatrix();
@@ -65,7 +65,7 @@ public class RenderContainmentField {
GlStateManager.color(1, 1, 1, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(textures[(player.ticksExisted % (textures.length * 2))/2]);
Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURES[(player.ticksExisted % (TEXTURES.length * 2))/2]);
Tessellator tessellator = Tessellator.getInstance();
@@ -245,24 +245,4 @@ public class RenderContainmentField {
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
}
@@ -0,0 +1,191 @@
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.misc.DonationPerksHandler;
import electroblob.wizardry.util.GeometryUtils;
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.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderDonationPerks {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
private static final float BOBBING_PERIOD = 25;
private static final float ROTATION_PERIOD = 60;
private static final double ROTATION_RADIUS = 0.7;
private static final double HEIGHT_FRACTION = 0.8;
private static final double BOBBING_DISTANCE = 0.2;
private static final double FOLLOW_DISTANCE = 1.4;
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
@SubscribeEvent
public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){
EntityPlayer player = event.getEntityPlayer();
float partialTicks = event.getPartialRenderTick();
Element element = DonationPerksHandler.getElement(player);
if(element != null){
int c1 = BlockReceptacle.PARTICLE_COLOURS.get(element)[0];
int r1 = (c1 & 0xFF0000) >> 16;
int g1 = (c1 & 0xFF00) >> 8;
int b1 = (c1 & 0xFF);
int c2 = BlockReceptacle.PARTICLE_COLOURS.get(element)[2];
int r2 = (c2 & 0xFF0000) >> 16;
int g2 = (c2 & 0xFF00) >> 8;
int b2 = (c2 & 0xFF);
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.depthMask(false);
GlStateManager.disableCull();
double x1 = player.chasingPosX - player.posX;
double y1 = player.chasingPosY - player.posY;
double z1 = player.chasingPosZ - player.posZ;
double x2 = player.prevChasingPosX - player.prevPosX;
double y2 = player.prevChasingPosY - player.prevPosY;
double z2 = player.prevChasingPosZ - player.prevPosZ;
double dx = x2 + (x1 - x2) * partialTicks;
double dy = y2 + (y1 - y2) * partialTicks;
double dz = z2 + (z1 - z2) * partialTicks;
float t = player.ticksExisted + partialTicks;
float hMoveFraction = MathHelper.clamp((float)(dx * dx + dz * dz) / 0.3f, 0, 1);
float vMoveFraction = MathHelper.clamp((float)(dy * dy) / 0.3f, 0, 1);
double x = event.getX() + ROTATION_RADIUS * MathHelper.sin(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dx;
double y = event.getY() + HEIGHT_FRACTION * player.height + BOBBING_DISTANCE * MathHelper.sin(t / BOBBING_PERIOD) * (1 - vMoveFraction) + FOLLOW_DISTANCE * dy;
double z = event.getZ() + ROTATION_RADIUS * MathHelper.cos(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dz;
GlStateManager.translate(x, y, z);
GlStateManager.scale(0.4, 0.4, 0.4);
// Makes the 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);
GlStateManager.color(r1/255f, g1/255f, b1/255f);
drawFlare(tessellator);
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(r2/255f, g2/255f, b2/255f);
drawCube(player, tessellator, partialTicks);
GlStateManager.color(1, 1, 1,1);
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.depthMask(true);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
}
private static void drawFlare(Tessellator tessellator){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
Minecraft.getMinecraft().renderEngine.bindTexture(FLARE_TEXTURE);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2
? Minecraft.getMinecraft().getRenderManager().playerViewX
: -Minecraft.getMinecraft().getRenderManager().playerViewX;
GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
float r = 0.5f;
buffer.pos(-r, r, 0).tex(0, 0).endVertex();
buffer.pos( r, r, 0).tex(1, 0).endVertex();
buffer.pos( r, -r, 0).tex(1, 1).endVertex();
buffer.pos(-r, -r, 0).tex(0, 1).endVertex();
tessellator.draw();
GlStateManager.popMatrix();
}
private static void drawCube(EntityPlayer player, Tessellator tessellator, float partialTicks){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
float age = player.ticksExisted + partialTicks;
float rotationSpeed = 2;
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
GlStateManager.scale(0.5, 0.5, 0.5);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
Minecraft.getMinecraft().renderEngine.bindTexture(CUBE_TEXTURES[player.ticksExisted % CUBE_TEXTURES.length]);
Vec3d[] vertices = GeometryUtils.getVertices(new AxisAlignedBB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// Outside
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top
tessellator.draw();
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();
}
}
@@ -0,0 +1,45 @@
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.registry.WizardryPotions;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.GlStateManager.DestFactor;
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import java.util.Random;
@EventBusSubscriber(Side.CLIENT)
public class RenderMirage {
private static final int BLINK_PERIOD_1 = 47; // Use two prime numbers to make it look as chaotic as possible
private static final int BLINK_PERIOD_2 = 71;
private static final double MAX_OFFSET = 3;
private static final Random random = new Random(); // Seed will be set later
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Pre<?> event){
if(event.getEntity().isPotionActive(WizardryPotions.mirage)){
random.setSeed(event.getEntity().ticksExisted / BLINK_PERIOD_1
+ event.getEntity().ticksExisted / BLINK_PERIOD_2);
GlStateManager.pushMatrix();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE); // GhoOOoOostly OooOOOo00oOOo
GlStateManager.color(1, 1, 1, 0.5f);
GlStateManager.translate((random.nextDouble() * 2 - 1) * MAX_OFFSET,
(random.nextDouble() * 2 - 1) * MAX_OFFSET, (random.nextDouble() * 2 - 1) * MAX_OFFSET);
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<?> event){
if(event.getEntity().isPotionActive(WizardryPotions.mirage)){
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.popMatrix();
}
}
}
@@ -1,8 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -11,7 +11,6 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.Mod;
@@ -32,7 +31,7 @@ public class RenderShadowWard {
EntityPlayer player = Minecraft.getMinecraft().player;
if(WizardryUtilities.isCasting(player, Spells.shadow_ward)){
if(EntityUtils.isCasting(player, Spells.shadow_ward)){
GlStateManager.pushMatrix();
@@ -52,7 +51,7 @@ public class RenderShadowWard {
GlStateManager.pushMatrix();
GlStateManager.translate(0, 0, 1.2);
GlStateManager.rotate(player.world.getTotalWorldTime() * -2, 0, 0, 1);
GlStateManager.rotate(player.ticksExisted * -2, 0, 0, 1);
GlStateManager.scale(1.1, 1.1, 1.1);
Tessellator tessellator = Tessellator.getInstance();
@@ -94,7 +93,7 @@ public class RenderShadowWard {
EntityPlayer player = event.getEntityPlayer();
if(WizardryUtilities.isCasting(player, Spells.shadow_ward)){
if(EntityUtils.isCasting(player, Spells.shadow_ward)){
GlStateManager.pushMatrix();
@@ -103,9 +102,7 @@ public class RenderShadowWard {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
GlStateManager.rotate(180, 0, 1, 0);
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
@@ -116,7 +113,7 @@ public class RenderShadowWard {
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.translate(0, 1.2, 0);
GlStateManager.rotate(player.world.getTotalWorldTime() * -2, 0, 0, 1);
GlStateManager.rotate(player.ticksExisted * -2, 0, 0, 1);
GlStateManager.scale(1.1, 1.1, 1.1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
@@ -1,10 +1,10 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Shield;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -13,7 +13,6 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.Mod;
@@ -34,7 +33,7 @@ public class RenderShield {
EntityPlayer player = Minecraft.getMinecraft().player;
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && WizardryUtilities.isCasting(player, Spells.shield)){
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && EntityUtils.isCasting(player, Spells.shield)){
GlStateManager.pushMatrix();
@@ -50,7 +49,25 @@ public class RenderShield {
GlStateManager.rotate(-player.rotationYaw, 0, 1, 0);
GlStateManager.rotate(player.rotationPitch, 1, 0, 0);
GlStateManager.translate(0, 0, 0.8);
GlStateManager.translate(0, 0, 1);
GlStateManager.pushMatrix();
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
// but with translation instead of scaling.
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
GlStateManager.loadIdentity();
GlStateManager.translate(0.5, 0.5, 0);
float s = 1 - ((player.ticksExisted + event.getPartialTicks()) % 5) / 5 * 0.52f;
s = s*s;
GlStateManager.scale(s, s, 1);
GlStateManager.translate(-0.5, -0.5, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
Tessellator tessellator = Tessellator.getInstance();
@@ -58,6 +75,13 @@ public class RenderShield {
render(tessellator);
// Undo the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.enableLighting();
GlStateManager.shadeModel(GL11.GL_FLAT);
@@ -76,7 +100,7 @@ public class RenderShield {
EntityPlayer player = event.getEntityPlayer();
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && WizardryUtilities.isCasting(player, Spells.shield)){
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && EntityUtils.isCasting(player, Spells.shield)){
GlStateManager.pushMatrix();
@@ -89,9 +113,7 @@ public class RenderShield {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
GlStateManager.translate(0, 1.3, 0);
@@ -99,7 +121,25 @@ public class RenderShield {
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
// GlStateManager.rotate(-player.rotationPitch, 1, 0, 0);
GlStateManager.translate(0, 0, 0.8);
GlStateManager.translate(0, 0, 1);
GlStateManager.pushMatrix();
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
// but with translation instead of scaling.
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
GlStateManager.loadIdentity();
GlStateManager.translate(0.5, 0.5, 0);
float s = 1 - ((player.ticksExisted + event.getPartialRenderTick()) % 5) / 5 * 0.52f;
s = s*s;
GlStateManager.scale(s, s, 1);
GlStateManager.translate(-0.5, -0.5, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
Tessellator tessellator = Tessellator.getInstance();
@@ -107,6 +147,13 @@ public class RenderShield {
render(tessellator);
// Undo the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.enableLighting();
GlStateManager.shadeModel(GL11.GL_FLAT);
@@ -1,8 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -12,7 +12,6 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -32,7 +31,7 @@ public class RenderWings {
EntityPlayer player = event.getEntityPlayer();
if(WizardryUtilities.isCasting(player, Spells.flight)){
if(EntityUtils.isCasting(player, Spells.flight)){
GlStateManager.pushMatrix();
@@ -41,9 +40,7 @@ public class RenderWings {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
// GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0);
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
@@ -1,6 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.renderer.RayHelper;
import electroblob.wizardry.entity.construct.EntityBlackHole;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
@@ -16,17 +18,18 @@ import java.util.Collections;
public class RenderBlackHole extends Render<EntityBlackHole> {
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
"textures/entity/dark_ray.png");
private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID,
"textures/entity/black_hole.png");
// TODO: Remove RayHelper and the ray texture entirely and use colours with depth mask off (like imbuement altar)
private static final ResourceLocation RAY_TEXTURE = new ResourceLocation(Wizardry.MODID,
"textures/entity/black_hole/ray.png");
private static final ResourceLocation CENTRE_TEXTURE = new ResourceLocation(Wizardry.MODID,
"textures/entity/black_hole/centre.png");
public RenderBlackHole(RenderManager renderManager){
super(renderManager);
}
@Override
public void doRender(EntityBlackHole blackhole, double x, double y, double z, float fa, float fb){
public void doRender(EntityBlackHole entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
@@ -48,16 +51,10 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
if(blackhole.ticksExisted < 10){
GlStateManager.scale((float)blackhole.ticksExisted / 10, (float)blackhole.ticksExisted / 10,
(float)blackhole.ticksExisted / 10);
}
if(blackhole.ticksExisted > blackhole.lifetime - 10){
float scale = Math.max(0, (float)(blackhole.lifetime - blackhole.ticksExisted) / 10);
GlStateManager.scale(scale, scale, scale);
}
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
this.bindTexture(texture);
this.bindTexture(RAY_TEXTURE);
// In theory this stuff should sort the rays into the correct render order based on the distance from
// the 'camera' (i.e. the player's viewpoint)
@@ -65,24 +62,24 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
// coordinates which were inconsistent with my calculated ones. Since I know those will give the desired
// effect anyway, I just used them directly instead.
ArrayList<RayHelper> rays = new ArrayList<RayHelper>(1);
ArrayList<RayHelper> rays = new ArrayList<>(1);
for(int j = 0; j < 30; j++){
float scale = 3.0f;
float radius = 3.0f * entity.getSizeMultiplier();
int a = blackhole.randomiser[j];
int b = blackhole.randomiser2[j];
int a = entity.randomiser[j];
int b = entity.randomiser2[j];
int sliceAngle = 20 + a;
double x1 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j) * ((float)Math.PI / 180f));
double x1 = radius * MathHelper.sin((entity.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 z1 = radius * MathHelper.cos((entity.ticksExisted + 40 * j) * ((float)Math.PI / 180));
double x2 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double x2 = radius * MathHelper.sin((entity.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 z2 = radius * MathHelper.cos((entity.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double absoluteX = x1 * MathHelper.cos(31 * b);
double absoluteY = z1 * MathHelper.sin(31 * a) + x1 * MathHelper.cos(31 * a) * MathHelper.sin(31 * b);
@@ -142,7 +139,7 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
this.bindTexture(texture2);
this.bindTexture(CENTRE_TEXTURE);
buffer.pos(-0.4, 0.4, 0).tex(0, 0).endVertex();
buffer.pos(0.4, 0.4, 0).tex(1, 0).endVertex();
@@ -164,7 +161,7 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
@Override
protected ResourceLocation getEntityTexture(EntityBlackHole entity){
return texture;
return RAY_TEXTURE;
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
@@ -0,0 +1,51 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.model.ModelBoulder;
import electroblob.wizardry.entity.construct.EntityBoulder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
public class RenderBoulder extends Render<EntityBoulder> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/boulder.png");
private ModelBoulder model = new ModelBoulder();
public RenderBoulder(RenderManager manager){
super(manager);
}
@Override
public void doRender(EntityBoulder entity, double x, double y, double z, float yaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.translate(x, y + entity.height/2, z);
GlStateManager.rotate(180, 0F, 0F, 1F);
GlStateManager.rotate(yaw - 90, 0, 1, 0);
GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks, 0, 0, 1);
GlStateManager.translate(0, -entity.height/2, 0);
// Pass in -1 for the lifetime as the boulder crumbles when the time expires
float s = DrawingUtils.smoothScaleFactor(-1, entity.ticksExisted, partialTicks, 10, 10);
s *= entity.getSizeMultiplier();
GlStateManager.scale(s, s, s);
GlStateManager.translate(0, 0.875, 0); // No idea why it starts 7/8 of a block too low, but it does
this.bindTexture(TEXTURE);
model.render(entity, 0, 0, 0, 0, 0, 0.0625f);
GlStateManager.popMatrix();
}
@Override
protected ResourceLocation getEntityTexture(EntityBoulder entity){
return TEXTURE;
}
}
@@ -1,8 +1,9 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityBubble;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -11,21 +12,21 @@ 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.entity.Entity;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
public class RenderBubble extends Render<EntityBubble> {
private static final ResourceLocation particleTextures = new ResourceLocation("textures/particle/particles.png");
private static final ResourceLocation darkOrbTexture = new ResourceLocation(Wizardry.MODID,
"textures/entity/dark_orb.png");
private static final ResourceLocation PARTICLE_TEXTURES = new ResourceLocation("textures/particle/particles.png");
private static final ResourceLocation ENTRAPMENT_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/entrapment.png");
public RenderBubble(RenderManager renderManager){
super(renderManager);
}
@Override
public void doRender(EntityBubble entity, double par2, double par4, double par6, float par8, float par9){
public void doRender(EntityBubble entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
@@ -33,18 +34,20 @@ public class RenderBubble extends Render<EntityBubble> {
float yOffset = 0;
if(WizardryUtilities.getRider(entity) != null){
yOffset = WizardryUtilities.getRider(entity).height / 2;
Entity rider = EntityUtils.getRider(entity);
if(rider != null){
yOffset = rider.height / 2;
}
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
GlStateManager.translate((float)x, (float)y + yOffset, (float)z);
this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures);
this.bindTexture(entity.isDarkOrb ? ENTRAPMENT_TEXTURE : PARTICLE_TEXTURES);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
if(((EntityBubble)entity).isDarkOrb){
if(entity.isDarkOrb){
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
}else{
@@ -63,35 +66,32 @@ public class RenderBubble extends Render<EntityBubble> {
GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
float f11 = 3.0F;
GlStateManager.scale(f11, f11, f11);
// Bubble 'bursts' so doesn't shrink when is disappears
float s = 3 * DrawingUtils.smoothScaleFactor(entity.isDarkOrb ? entity.lifetime : -1, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
double pixelwidth = (1.0d / 128);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// tessellator.setColorRGBA_I(k1, 128);
// buffer.normal(0.0F, 1.0F, 0.0F);
if(((EntityBubble)entity).isDarkOrb){
buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(0, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(1, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(1, 0).endVertex();
buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(0, 0).endVertex();
if(entity.isDarkOrb){
buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.0D).tex(0, 0).endVertex();
}else{
buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex();
buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth * 8, pixelwidth * 24).endVertex();
buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth * 8, pixelwidth * 17).endVertex();
buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex();
buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 24).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 17).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex();
}
tessellator.draw();
GlStateManager.disableBlend();
if(((EntityBubble)entity).isDarkOrb){
GlStateManager.enableLighting();
}
if(entity.isDarkOrb) GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
@@ -1,6 +1,7 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityDecay;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -14,17 +15,17 @@ import org.lwjgl.opengl.GL11;
public class RenderDecay extends Render<EntityDecay> {
private static final ResourceLocation[] textures = new ResourceLocation[10];
private static final ResourceLocation[] TEXTURES = new ResourceLocation[10];
public RenderDecay(RenderManager renderManager){
super(renderManager);
for(int i = 0; i < 10; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay_" + i + ".png");
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay/decay_" + i + ".png");
}
}
@Override
public void doRender(EntityDecay entity, double par2, double par4, double par6, float par8, float par9){
public void doRender(EntityDecay entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
@@ -34,28 +35,26 @@ public class RenderDecay extends Render<EntityDecay> {
float yOffset = 0;
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
GlStateManager.translate((float)x, (float)y + yOffset, (float)z);
this.bindTexture(textures[((EntityDecay)entity).textureIndex]);
this.bindTexture(TEXTURES[entity.textureIndex]);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
GlStateManager.rotate(-90, 1, 0, 0);
float scale = 2 * Math.min(1, (float)(entity.lifetime - entity.ticksExisted) / 50f);
GlStateManager.scale(scale, scale, scale);
float s = 2 * DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 50);
GlStateManager.scale(s, s, s);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// tessellator.setColorRGBA_I(k1, 128);
// buffer.normal(0.0F, 1.0F, 0.0F);
buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex();
buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex();
buffer.pos(0.0F - f7, 0.0F - f8, 0.01).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.01).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.01).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.01).tex(0, 0).endVertex();
tessellator.draw();
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.EntityDecoy;
@@ -8,7 +8,7 @@ import net.minecraft.client.renderer.entity.RenderBiped;
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.common.ObfuscationReflectionHelper;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -18,8 +18,8 @@ public class RenderDecoy extends RenderBiped<EntityDecoy> {
private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png");
private static final Method getEntityTexture = ReflectionHelper.findMethod(Render.class, "getEntityTexture",
"func_110775_a", Entity.class); // Generic parameter T is erased to Entity at runtime.
private static final Method getEntityTexture = ObfuscationReflectionHelper.findMethod(Render.class, "func_110775_a",
ResourceLocation.class, Entity.class); // Generic parameter T is erased to Entity at runtime.
public RenderDecoy(RenderManager manager){
super(manager, new ModelBiped(0.0f), 0.5f);
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelWizard;
@@ -10,14 +10,15 @@ import net.minecraft.util.ResourceLocation;
//@SideOnly(Side.CLIENT)
public class RenderEvilWizard extends RenderBiped<EntityEvilWizard> {
static final ResourceLocation[] textures = new ResourceLocation[6];
static final ResourceLocation[] TEXTURES = new ResourceLocation[6];
public RenderEvilWizard(RenderManager renderManager){
super(renderManager, new ModelWizard(), 0.5F);
for(int i = 0; i < 6; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard_" + i + ".png");
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard/evil_wizard_" + i + ".png");
}
// Just using the default without overriding models, since the armour sets its own model anyway.
this.addLayer(new LayerBipedArmor(this));
@@ -25,7 +26,7 @@ public class RenderEvilWizard extends RenderBiped<EntityEvilWizard> {
@Override
protected ResourceLocation getEntityTexture(EntityEvilWizard wizard){
return textures[wizard.textureIndex];
return TEXTURES[wizard.textureIndex];
}
}
@@ -0,0 +1,180 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityFireRing;
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.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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> {
private final ResourceLocation texture;
public RenderFireRing(RenderManager renderManager, ResourceLocation texture){
super(renderManager);
this.texture = texture;
}
@Override
public void doRender(EntityFireRing entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float yOffset = 0;
GlStateManager.translate(x, y + yOffset, z);
this.bindTexture(texture);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
GlStateManager.rotate(-90, 1, 0, 0);
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(entity.width * s, entity.width * s, entity.width * s);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
buffer.pos(0.0F - f7, 0.0F - f8, 0.01).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.01).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.01).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.01).tex(0, 0).endVertex();
tessellator.draw();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
// Fire
// TODO: Bleeeuuurugh this is disgusting must fix :0
if(s >= 1){
GlStateManager.disableLighting();
TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher()
.getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture();
float s1 = entity.width/5;
int sides = (int)(16 * s1);
float height = 1.0f;
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)x, (float)y + 0.05f, (float)z);
// GlStateManager.scale(s1, s1, s1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.posY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, -2.3f * s1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((f2 - f3), (0.0F - f5), f61).tex(f9, f10).endVertex();
buffer.pos((-f2 - f3), (0.0F - f5), f61).tex(f71, f10).endVertex();
buffer.pos((-f2 - f3), (height - f5), f61).tex(f71, f81).endVertex();
buffer.pos((f2 - f3), (height - f5), f61).tex(f9, f81).endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)x, (float)y + 0.05f, (float)z);
// GlStateManager.scale(s1, s1, s1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.posY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, 2.3f * s1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((f2 - f3), (0.0F - f5), f61).tex(f9, f10).endVertex();
buffer.pos((-f2 - f3), (0.0F - f5), f61).tex(f71, f10).endVertex();
buffer.pos((-f2 - f3), (height - f5), f61).tex(f71, f81).endVertex();
buffer.pos((f2 - f3), (height - f5), f61).tex(f9, f81).endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
}
GlStateManager.enableLighting();
}
@Override
protected ResourceLocation getEntityTexture(EntityFireRing entity){
return null;
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.projectile.EntityForceArrow;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.construct.EntityForcefield;
import net.minecraft.client.renderer.BufferBuilder;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelHammer;
@@ -0,0 +1,45 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelIceBarrier;
import electroblob.wizardry.entity.construct.EntityIceBarrier;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
public class RenderIceBarrier extends Render<EntityIceBarrier> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_barrier.png");
private ModelIceBarrier model = new ModelIceBarrier();
public RenderIceBarrier(RenderManager manager){
super(manager);
}
@Override
public void doRender(EntityIceBarrier entity, double x, double y, double z, float yaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.translate(x, y + entity.height/2, z);
GlStateManager.rotate(180, 0F, 0F, 1F);
GlStateManager.rotate(yaw, 0, 1, 0);
GlStateManager.translate(0, -entity.height/2 - 0.3, 0);
float s = entity.getSizeMultiplier();
GlStateManager.scale(s, s, s);
this.bindTexture(TEXTURE);
model.render(entity, 0, 0, 0, 0, 0, 0.0625f);
GlStateManager.popMatrix();
}
@Override
protected ResourceLocation getEntityTexture(EntityIceBarrier entity){
return TEXTURE;
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelIceGiant;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.construct.EntityIceSpike;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.projectile.EntityLightningDisc;
import net.minecraft.client.renderer.BufferBuilder;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.projectile.EntityMagicArrow;
import net.minecraft.client.renderer.BufferBuilder;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelPhoenix;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.spell.Possession;
import net.minecraft.client.renderer.entity.Render;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.projectile.EntityMagicProjectile;
import net.minecraft.client.Minecraft;
@@ -0,0 +1,151 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityRadiantTotem;
import electroblob.wizardry.util.GeometryUtils;
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.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.Vec3d;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
public class RenderRadiantTotem extends Render<EntityRadiantTotem> {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
public RenderRadiantTotem(RenderManager manager){
super(manager);
}
@Nullable
@Override
protected ResourceLocation getEntityTexture(EntityRadiantTotem entity){
return CUBE_TEXTURES[entity.ticksExisted % CUBE_TEXTURES.length];
}
@Override
public void doRender(EntityRadiantTotem entity, double x, double y, double z, float entityYaw, float partialTicks){
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.depthMask(false);
GlStateManager.disableCull();
GlStateManager.translate(x, y + entity.height/2, z);
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
drawFlare(tessellator);
drawCube(entity, tessellator, partialTicks);
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.depthMask(true);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
private void drawFlare(Tessellator tessellator){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
// Makes the 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);
GlStateManager.color(1, 0.957f, 0.608f);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
bindTexture(FLARE_TEXTURE);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2
? Minecraft.getMinecraft().getRenderManager().playerViewX
: -Minecraft.getMinecraft().getRenderManager().playerViewX;
GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
float r = 0.5f;
buffer.pos(-r, r, 0).tex(0, 0).endVertex();
buffer.pos( r, r, 0).tex(1, 0).endVertex();
buffer.pos( r, -r, 0).tex(1, 1).endVertex();
buffer.pos(-r, -r, 0).tex(0, 1).endVertex();
tessellator.draw();
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.popMatrix();
}
private void drawCube(EntityRadiantTotem entity, Tessellator tessellator, float partialTicks){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
float age = entity.ticksExisted + partialTicks;
float rotationSpeed = 2;
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
GlStateManager.scale(0.5, 0.5, 0.5);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
bindEntityTexture(entity);
Vec3d[] vertices = GeometryUtils.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector()
.add(0, entity.height/2, 0).scale(-1)));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// Outside
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top
tessellator.draw();
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();
}
}
@@ -0,0 +1,75 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelRemnant;
import electroblob.wizardry.entity.living.EntityRemnant;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import javax.annotation.Nullable;
public class RenderRemnant extends Render<EntityRemnant> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/remnant.png");
private final ModelRemnant model;
public RenderRemnant(RenderManager manager){
super(manager);
model = new ModelRemnant();
}
@Override
public void doRender(EntityRemnant entity, double x, double y, double z, float entityYaw, float partialTicks){
float age = (float)entity.ticksExisted + partialTicks;
GlStateManager.pushMatrix();
GlStateManager.translate((float)x, (float)y, (float)z);
this.bindTexture(TEXTURE);
// float whatIsThis = MathHelper.sin(age * 0.2F) / 2.0F + 0.5F;
// whatIsThis = whatIsThis * whatIsThis + whatIsThis;
if(this.renderOutlines){
GlStateManager.enableColorMaterial();
GlStateManager.enableOutlineMode(this.getTeamColor(entity));
}
float expansion = 0.9f;
if(entity.deathTime > 0){
float f = (entity.deathTime + partialTicks) * 0.25f;
f = 1 - (1 / (f + 1));
expansion -= f * 0.75f;
}else if(entity.hurtTime > 0){
float f = (entity.hurtTime - partialTicks) / entity.maxHurtTime;
// Neat bit of maths borrowed from the camera tilt effect when hurt
f = MathHelper.sin(f * f * f * f * (float)Math.PI);
expansion += f * 0.2f;
}
// if(entity.isAttacking()) s -= 0.4f;
float rotationSpeed = entity.isAttacking() ? 10 : 3;
this.model.render(entity, rotationSpeed, expansion, age, 0, 0, 0.0625f);
if(this.renderOutlines){
GlStateManager.disableOutlineMode();
GlStateManager.disableColorMaterial();
}
GlStateManager.popMatrix();
super.doRender(entity, x, y, z, entityYaw, partialTicks);
}
@Nullable
@Override
protected ResourceLocation getEntityTexture(EntityRemnant entity){
return TEXTURE;
}
}
@@ -1,6 +1,6 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.construct.EntityHealAura;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityMagicConstruct;
import electroblob.wizardry.util.AllyDesignationSystem;
import net.minecraft.client.Minecraft;
@@ -18,24 +18,24 @@ import org.lwjgl.opengl.GL11;
public class RenderSigil extends Render<EntityMagicConstruct> {
private final ResourceLocation texture;
private float scale = 1.0f;
private float rotationSpeed;
private boolean invisibleToEnemies;
public RenderSigil(RenderManager renderManager, ResourceLocation texture, float scale, boolean invisibleToEnemies){
public RenderSigil(RenderManager renderManager, ResourceLocation texture, float rotationSpeed, boolean invisibleToEnemies){
super(renderManager);
this.texture = texture;
this.scale = scale;
this.rotationSpeed = rotationSpeed;
this.invisibleToEnemies = invisibleToEnemies;
}
@Override
public void doRender(EntityMagicConstruct entity, double par2, double par4, double par6, float par8, float par9){
public void doRender(EntityMagicConstruct entity, double x, double y, double z, float entityYaw, float partialTicks){
// Makes the sigil invisible to enemies of the player that created it
if(this.invisibleToEnemies){
// Unfortunately we can't access the caster's allies if they're not online, it only works the other way round
if(entity.getCaster() instanceof EntityPlayer && !AllyDesignationSystem
.isPlayerAlly((EntityPlayer)entity.getCaster(), Minecraft.getMinecraft().player)){
if(entity.getCaster() != Minecraft.getMinecraft().player && entity.getCaster() instanceof EntityPlayer
&& !AllyDesignationSystem.isPlayerAlly((EntityPlayer)entity.getCaster(), Minecraft.getMinecraft().player)){
return;
}
}
@@ -48,7 +48,7 @@ public class RenderSigil extends Render<EntityMagicConstruct> {
float yOffset = 0;
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
GlStateManager.translate((float)x, (float)y + yOffset, (float)z);
this.bindTexture(texture);
float f6 = 1.0F;
@@ -58,19 +58,19 @@ public class RenderSigil extends Render<EntityMagicConstruct> {
GlStateManager.rotate(-90, 1, 0, 0);
// Healing aura rotates slowly
if(entity instanceof EntityHealAura) GlStateManager.rotate(entity.ticksExisted / 3.0f, 0, 0, 1);
if(rotationSpeed != 0) GlStateManager.rotate(entity.ticksExisted * rotationSpeed, 0, 0, 1);
GlStateManager.scale(scale, scale, scale);
float s = entity.width * DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// tessellator.setColorRGBA_I(k1, 128);
// buffer.normal(0.0F, 1.0F, 0.0F);
buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex();
buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex();
buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex();
buffer.pos(0.0F - f7, 0.0F - f8, 0.01).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.01).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.01).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.01).tex(0, 0).endVertex();
tessellator.draw();
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.EntitySpiritHorse;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.EntitySpiritWolf;
@@ -1,5 +1,6 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.client.renderer.entity.layers.LayerStrayMinionClothing;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderSkeleton;
import net.minecraft.entity.monster.AbstractSkeleton;
@@ -0,0 +1,159 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityWitheringTotem;
import electroblob.wizardry.util.GeometryUtils;
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.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.Vec3d;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
public class RenderWitheringTotem extends Render<EntityWitheringTotem> {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
public RenderWitheringTotem(RenderManager manager){
super(manager);
}
@Nullable
@Override
protected ResourceLocation getEntityTexture(EntityWitheringTotem entity){
return CUBE_TEXTURES[entity.ticksExisted % CUBE_TEXTURES.length];
}
@Override
public void doRender(EntityWitheringTotem entity, double x, double y, double z, float entityYaw, float partialTicks){
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.depthMask(false);
GlStateManager.disableCull();
GlStateManager.translate(x, y + entity.height/2, z);
float charge = entity.getHealthDrained() / 50f;
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
s *= 1 + charge * 0.3f; // Gets bigger the more health it drains
GlStateManager.scale(s, s, s);
drawFlare(charge, tessellator);
drawCube(entity, tessellator, partialTicks);
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.depthMask(true);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
private void drawFlare(float redness, Tessellator tessellator){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
int c = DrawingUtils.mix(0xb333e6, 0xff0044, redness);
int r = c >> 16 & 255;
int g = c >> 8 & 255;
int b = c & 255;
// Makes the 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);
GlStateManager.color(r/255f, g/255f, b/255f); // Gets redder the more health it drains
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
bindTexture(FLARE_TEXTURE);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2
? Minecraft.getMinecraft().getRenderManager().playerViewX
: -Minecraft.getMinecraft().getRenderManager().playerViewX;
GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
float radius = 0.5f;
buffer.pos(-radius, radius, 0).tex(0, 0).endVertex();
buffer.pos( radius, radius, 0).tex(1, 0).endVertex();
buffer.pos( radius, -radius, 0).tex(1, 1).endVertex();
buffer.pos(-radius, -radius, 0).tex(0, 1).endVertex();
tessellator.draw();
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.popMatrix();
}
private void drawCube(EntityWitheringTotem entity, Tessellator tessellator, float partialTicks){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
float age = entity.ticksExisted + partialTicks;
float rotationSpeed = 2;
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
GlStateManager.scale(0.5, 0.5, 0.5);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
bindEntityTexture(entity);
Vec3d[] vertices = GeometryUtils.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector()
.add(0, entity.height/2, 0).scale(-1)));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// Outside
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top
tessellator.draw();
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,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelWizard;
@@ -11,14 +11,14 @@ import net.minecraft.util.ResourceLocation;
//@SideOnly(Side.CLIENT)
public class RenderWizard extends RenderBiped<EntityWizard> {
static final ResourceLocation[] textures = new ResourceLocation[6];
static final ResourceLocation[] TEXTURES = new ResourceLocation[6];
public RenderWizard(RenderManager renderManager){
super(renderManager, new ModelWizard(), 0.5F);
for(int i = 0; i < 6; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard_" + i + ".png");
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard/wizard_" + i + ".png");
}
// Just using the default without overriding models, since the armour sets its own model anyway.
this.addLayer(new LayerBipedArmor(this));
@@ -26,7 +26,7 @@ public class RenderWizard extends RenderBiped<EntityWizard> {
@Override
protected ResourceLocation getEntityTexture(EntityWizard wizard){
return textures[wizard.textureIndex];
return TEXTURES[wizard.textureIndex];
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.living.EntityBlazeMinion;
import net.minecraft.client.model.ModelBlaze;
@@ -0,0 +1,115 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityZombieSpawner;
import electroblob.wizardry.util.GeometryUtils;
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.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
import org.lwjgl.opengl.GL11;
public class RenderZombieSpawner extends Render<EntityZombieSpawner> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/zombie_spawner.png");
private static final Vec3d[] HIDDEN_BOX = GeometryUtils.getVertices(new AxisAlignedBB(-1, 0, -1, 1, 2.5, 1));
public RenderZombieSpawner(RenderManager renderManager){
super(renderManager);
}
@Override
public void doRender(EntityZombieSpawner entity, double x, double y, double z, float yaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.translate(x, y, z);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
// The visible bit
GlStateManager.pushMatrix();
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
GlStateManager.rotate((entity.ticksExisted + partialTicks) * 2, 0, 1, 0);
this.bindTexture(TEXTURE);
Vec3d[] vertices = GeometryUtils.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector().scale(-1)));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0, 0, 1, 1); // Bottom
drawFace(buffer, vertices[1], vertices[0], vertices[2], vertices[3], 0, 0, 1, 1); // Top
tessellator.draw();
GlStateManager.popMatrix();
// Hidden box
GlStateManager.disableTexture2D();
GlStateManager.colorMask(false, false, false, false); // Magically hide the zombies
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
// Outside (hides everything)
drawFaceColour(buffer, HIDDEN_BOX[6], HIDDEN_BOX[7], HIDDEN_BOX[2], HIDDEN_BOX[3], 0, 0, 0, 1); // South
drawFaceColour(buffer, HIDDEN_BOX[5], HIDDEN_BOX[6], HIDDEN_BOX[1], HIDDEN_BOX[2], 0, 0, 0, 1); // East
drawFaceColour(buffer, HIDDEN_BOX[4], HIDDEN_BOX[5], HIDDEN_BOX[0], HIDDEN_BOX[1], 0, 0, 0, 1); // North
drawFaceColour(buffer, HIDDEN_BOX[7], HIDDEN_BOX[4], HIDDEN_BOX[3], HIDDEN_BOX[0], 0, 0, 0, 1); // West
drawFaceColour(buffer, HIDDEN_BOX[5], HIDDEN_BOX[4], HIDDEN_BOX[6], HIDDEN_BOX[7], 0, 0, 0, 1); // Top
tessellator.draw();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.enableTexture2D();
GlStateManager.colorMask(true, true, true, true);
GlStateManager.popMatrix();
}
@Override
public void renderMultipass(EntityZombieSpawner entity, double x, double y, double z, float yaw, float partialTicks){
}
@Override
public boolean isMultipass(){
return false;
}
@Override
protected ResourceLocation getEntityTexture(EntityZombieSpawner entity){
return TEXTURE;
}
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();
}
private static void drawFaceColour(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float r, float g, float b, float a){
buffer.pos(topLeft.x, topLeft.y, topLeft.z).color(r, g, b, a).endVertex();
buffer.pos(topRight.x, topRight.y, topRight.z).color(r, g, b, a).endVertex();
buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).color(r, g, b, a).endVertex();
buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).color(r, g, b, a).endVertex();
}
}
@@ -0,0 +1,71 @@
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.spell.Disintegration;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
/**
* Layer used to render the appear/disappear animation for summoned creatures.
*
* @author Electroblob
* @since Wizardry 4.3
*/
@EventBusSubscriber(Side.CLIENT)
public class LayerDisintegrateAnimation<T extends EntityLivingBase> extends LayerTiledOverlay<T> {
private static final int ANIMATION_TICKS = 19; // One less than the max death time
private static final ResourceLocation[] TEXTURES = new ResourceLocation[ANIMATION_TICKS];
static {
for(int i=0; i<ANIMATION_TICKS; i++){
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/disintegrate_overlay/disintegrate_overlay_" + i + ".png");
}
}
public LayerDisintegrateAnimation(RenderLivingBase<?> renderer){
super(renderer, 32, 32);
}
@Override
public boolean shouldRender(T entity, float partialTicks){
return entity.getEntityData().hasKey(Disintegration.NBT_KEY);
}
@Override
public ResourceLocation getTexture(T entity, float partialTicks){
return TEXTURES[MathHelper.clamp(entity.ticksExisted - entity.getEntityData().getInteger(Disintegration.NBT_KEY), 0, ANIMATION_TICKS - 1)];
}
@Override
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.enableLighting();
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Pre<EntityLivingBase> event){
if(event.getEntity().getEntityData().hasKey(Disintegration.NBT_KEY)){
event.getEntity().deathTime = 0;
event.getEntity().setInvisible(true);
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
if(event.getEntity().getEntityData().hasKey(Disintegration.NBT_KEY)) event.getEntity().setInvisible(false);
}
}
@@ -0,0 +1,49 @@
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.registry.WizardryPotions;
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.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the frost texture on creatures with the frostbite effect.
*
* @author Electroblob
* @since Wizardry 1.2
*/
public class LayerFrost extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/frost_overlay.png");
public LayerFrost(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return !entity.isInvisible() && entity.isPotionActive(WizardryPotions.frost) || entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY);
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
return TEXTURE;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
}
@@ -0,0 +1,60 @@
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Possession;
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.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the mind control overlay on creatures with the mind control effect.
*
* @author Electroblob
* @since Wizardry 4.3
*/
public class LayerMindControl extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/mind_control_overlay.png");
public LayerMindControl(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return !entity.isInvisible() && (entity.isPotionActive(WizardryPotions.mind_control) || entity.getEntityData().getBoolean(Possession.NBT_KEY));
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
return TEXTURE;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE);
PotionEffect effect = entity.getActivePotionEffect(WizardryPotions.mind_control);
if(effect != null){
GlStateManager.color(1, 1, 1, Math.min(1, (effect.getDuration() - partialTicks) / 20));
}
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
@Override
protected void applyTextureSpaceTransformations(EntityLivingBase entity, float partialTicks){
float f = entity.ticksExisted + partialTicks;
GlStateManager.translate(f * 0.003f, f * 0.003f, 0);
}
}
@@ -0,0 +1,58 @@
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.client.ClientProxy;
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.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the stone texture on a petrified creature.
*
* @author Electroblob
* @since Wizardry 1.2
*/
// N.B. The rule here is don't restrict the type any more than necessary, so even though we *know* petrified creatures
// are always instances of EntityLiving, we don't need any methods/fields specific to EntityLiving so use ELB instead.
public class LayerStone extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/stone.png");
public LayerStone(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY);
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture();
return breakingTexture == null ? TEXTURE : breakingTexture;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
// N.B. It's one or the other because this effectively gets called twice via RenderStatue
if(ClientProxy.renderStatue.getBlockBreakingTexture() != null){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}else{
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
}
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.entity.living.EntityStrayMinion;
import net.minecraft.client.model.ModelSkeleton;
@@ -0,0 +1,89 @@
package electroblob.wizardry.client.renderer.entity.layers;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.ISummonedCreature;
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.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
/**
* Layer used to render the appear/disappear animation for summoned creatures.
*
* @author Electroblob
* @since Wizardry 4.3
*/
@EventBusSubscriber(Side.CLIENT)
public class LayerSummonAnimation<T extends EntityLivingBase> extends LayerTiledOverlay<T> {
private static final int ANIMATION_TICKS = 19;
private static final int HIDE_MODEL_TICKS = 9;
private static final ResourceLocation[] TEXTURES = new ResourceLocation[ANIMATION_TICKS];
static {
for(int i=0; i<ANIMATION_TICKS; i++){
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/summon_overlay/summon_overlay_" + i + ".png");
}
}
public LayerSummonAnimation(RenderLivingBase<?> renderer){
super(renderer, 32, 32);
}
@Override
public boolean shouldRender(T entity, float partialTicks){
return entity instanceof ISummonedCreature && ((ISummonedCreature)entity).hasAnimation()
&& ((ISummonedCreature)entity).getLifetime() > 0 && getFrameNumber(entity) < ANIMATION_TICKS;
}
@Override
public ResourceLocation getTexture(T entity, float partialTicks){
return TEXTURES[getFrameNumber(entity)];
}
private int getFrameNumber(T entity){
if(!(entity instanceof ISummonedCreature)) throw new IllegalArgumentException("Entity must be an ISummonedCreature");
return Math.min(entity.ticksExisted, Math.max(((ISummonedCreature)entity).getLifetime() - entity.ticksExisted - 1, 0));
}
@Override
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
if(!(entity instanceof ISummonedCreature)) return;
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
int colour = ((ISummonedCreature)entity).getAnimationColour((float)getFrameNumber(entity) / ANIMATION_TICKS);
GlStateManager.color((colour >> 16 & 255) / 255f, (colour >> 8 & 255) / 255f, (colour & 255) / 255f);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Pre<EntityLivingBase> event){
if(shouldHideMainModel(event.getEntity())) event.getEntity().setInvisible(true);
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
if(shouldHideMainModel(event.getEntity())) event.getEntity().setInvisible(false);
}
private static boolean shouldHideMainModel(EntityLivingBase entity){
return entity instanceof ISummonedCreature && ((ISummonedCreature)entity).hasAnimation()
&& ((ISummonedCreature)entity).getLifetime() > 0 && (entity.ticksExisted < HIDE_MODEL_TICKS
|| ((ISummonedCreature)entity).getLifetime() - entity.ticksExisted < HIDE_MODEL_TICKS);
}
}
@@ -0,0 +1,253 @@
package electroblob.wizardry.client.renderer.entity.layers;
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.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.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
import java.util.function.Function;
/**
* Base class for layers that overlay a tiled texture onto the entity model. Handles dynamic tiling of the texture,
* scaling to preserve in-world pixel size (1/16 of a block), and the necessary GL state changes.
*
* @author Electroblob
* @since Wizardry 4.3
*/
public abstract class LayerTiledOverlay<T extends EntityLivingBase> implements LayerRenderer<T> {
private final RenderLivingBase<?> renderer;
private final int textureWidth;
private final int textureHeight;
/** Creates a new {@code LayerTiledOverlay} with the given renderer parameters. */
public LayerTiledOverlay(RenderLivingBase<?> renderer, int textureWidth, int textureHeight){
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.renderer = renderer;
}
/** Creates a new {@code LayerTiledOverlay} with the given renderer. Texture width and height default to 16. */
public LayerTiledOverlay(RenderLivingBase<?> renderer){
this(renderer, 16, 16);
}
/**
* Returns true if this layer should be rendered for the given entity, false if not.
* @param entity The entity being rendered
* @param partialTicks The current partial tick time
* @return True if this layer should be rendered, false if not.
*/
public abstract boolean shouldRender(T entity, float partialTicks);
/**
* Returns the texture to use for the overlay.
* @param entity The entity being rendered
* @param partialTicks The current partial tick time
* @return A {@link ResourceLocation} specifying the texture to use.
*/
public abstract ResourceLocation getTexture(T entity, float partialTicks);
/**
* Returns the model to use for the overlay. Defaults to the renderer's main model.
* @param entity The entity being rendered
* @param partialTicks The current partial tick time
* @return The model to use. Swapping different models in dynamically is fine, but don't create new ones each time.
*/
public ModelBase getModel(T entity, float partialTicks){
return renderer.getMainModel();
}
/**
* Returns whether to render the second layer of mob/player skins (which is not an <i>actual</i> render layer, but
* part of the model) as part of this layer. Defaults to false.
* @param entity The entity being rendered
* @param partialTicks The current partial tick time
* @return True to render any non-hidden second layer boxes, false to always hide them from this layer.
*/
public boolean renderSecondLayer(T entity, float partialTicks){
return false; // Normally it looks kind of weird because second layers typically have holes in them
}
/**
* Allows subclasses to perform additional transformations in the texture space before the model is rendered. This
* can be used for a creeper-charge-like effect. To do this, simply override this method and call the appropriate
* transformation method(s) (translate, rotate, scale) in {@link GlStateManager}. This method does nothing by
* default.
* @param entity The entity being rendered
* @param partialTicks The current partial tick time
*/
protected void applyTextureSpaceTransformations(T entity, float partialTicks){}
@Override
public void doRenderLayer(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
if(shouldRender(entity, partialTicks)){
this.renderer.bindTexture(getTexture(entity, partialTicks));
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
}
}
@Override
public boolean shouldCombineTextures(){
return false;
}
protected static int getBlockBrightnessForEntity(Entity entity){
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(T entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.pushMatrix();
// Enables texture tiling (Also used for guardian beam, beacon beam and ender crystal beam, amongst others)
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.
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
GlStateManager.loadIdentity();
ModelBase model = getModel(entity, partialTicks);
// Calculate the scaling required in each direction
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 / textureWidth;
scaleY = (double)model.boxList.get(0).textureHeight / textureHeight;
}else{ // Fallback to model fields; should never be needed
scaleX = (double)model.textureWidth / textureWidth;
scaleY = (double)model.textureHeight / textureHeight;
}
GlStateManager.scale(scaleX, scaleY, 1);
applyTextureSpaceTransformations(entity, partialTicks);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
boolean headWearHidden = false;
boolean bodyWearHidden = false;
boolean leftArmWearHidden = false;
boolean rightArmWearHidden = false;
boolean leftLegWearHidden = false;
boolean rightLegWearHidden = false;
if(!renderSecondLayer(entity, partialTicks)){
// Hide the hat layer for bipeds, and all the other body part overlays for players
if(model instanceof ModelBiped){
headWearHidden = ((ModelBiped)model).bipedHeadwear.isHidden;
((ModelBiped)model).bipedHeadwear.isHidden = true;
}
if(model instanceof ModelPlayer){
bodyWearHidden = ((ModelPlayer)model).bipedBodyWear.isHidden;
leftArmWearHidden = ((ModelPlayer)model).bipedLeftArmwear.isHidden;
rightArmWearHidden = ((ModelPlayer)model).bipedRightArmwear.isHidden;
leftLegWearHidden = ((ModelPlayer)model).bipedLeftLegwear.isHidden;
rightLegWearHidden = ((ModelPlayer)model).bipedRightLegwear.isHidden;
((ModelPlayer)model).bipedBodyWear.isHidden = true;
((ModelPlayer)model).bipedLeftArmwear.isHidden = true;
((ModelPlayer)model).bipedRightArmwear.isHidden = true;
((ModelPlayer)model).bipedLeftLegwear.isHidden = true;
((ModelPlayer)model).bipedRightLegwear.isHidden = true;
}
}
// Actually render the model
model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks);
model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale);
if(!renderSecondLayer(entity, partialTicks)){
// Reset the hidden status of everything to how it was before
if(model instanceof ModelBiped){
((ModelBiped)model).bipedHeadwear.isHidden = headWearHidden;
}
if(model instanceof ModelPlayer){
((ModelPlayer)model).bipedBodyWear.isHidden = bodyWearHidden;
((ModelPlayer)model).bipedLeftArmwear.isHidden = leftArmWearHidden;
((ModelPlayer)model).bipedRightArmwear.isHidden = rightArmWearHidden;
((ModelPlayer)model).bipedLeftLegwear.isHidden = leftLegWearHidden;
((ModelPlayer)model).bipedRightLegwear.isHidden = rightLegWearHidden;
}
}
// Undo the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
}
/**
* Helper method to initialise a layer renderer for all applicable renderers. This method will work for any layer
* renderer, not just instances of {@code LayerTiledOverlay} (it is stored here purely for convenience).
* @param entityType The class of entity to initialise this layer renderer for (including subclasses)
* @param layerFactory A function that creates a layer for a given renderer, usually a constructor reference
* @param <T> The type of entity this layer renderer is applicable for
*/
@SuppressWarnings("unchecked")
public static <T extends EntityLivingBase> void initialiseLayers(Class<T> entityType, Function<RenderLivingBase<T>, LayerRenderer<? extends T>> layerFactory){
for(Class<? extends Entity> c : Minecraft.getMinecraft().getRenderManager().entityRenderMap.keySet()){
if(entityType.isAssignableFrom(c)){
Render<T> renderer = Minecraft.getMinecraft().getRenderManager().getEntityClassRenderObject(c);
if(renderer instanceof RenderLivingBase<?>){ // Should always be true
// For some reason IntelliJ is quite happy with this cast, even without suppress warnings
// Instinct suggests Java shouldn't be happy casting to RenderLivingBase<T> but somehow it works
((RenderLivingBase<T>)renderer).addLayer(layerFactory.apply((RenderLivingBase<T>)renderer));
}
}
}
// Players have a separate renderer map
if(entityType.isAssignableFrom(EntityPlayer.class)){
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
renderer.addLayer(layerFactory.apply((RenderLivingBase<T>)renderer)); // This does need suppress warnings
}
}
}
/**
* Helper method to initialise a layer renderer for all living entity renderers. This is a shorthand version of
* {@link LayerTiledOverlay#initialiseLayers(Class, Function)}.
* @param layerFactory A function that creates a layer for a given renderer, usually a constructor reference
*/
public static void initialiseLayers(Function<RenderLivingBase<EntityLivingBase>, LayerRenderer<? extends EntityLivingBase>> layerFactory){
initialiseLayers(EntityLivingBase.class, layerFactory);
}
}
@@ -0,0 +1,77 @@
package electroblob.wizardry.client.renderer.overlay;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.WizardryClientEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.FOVUpdateEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderBlinkEffect {
/** The remaining time for which the blink screen overlay effect will be displayed in first-person. Since this is
* only for the first-person player (the instance of which is itself stored in a static variable), this can simply
* be stored statically here, rather than needing to be in {@code WizardData}. */
private static int blinkEffectTimer;
/** The number of ticks the blink effect lasts for. */
private static final int BLINK_EFFECT_DURATION = 8;
private static final ResourceLocation SCREEN_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_overlay.png");
/** Starts the first-person blink overlay effect. */
public static void playBlinkEffect(){
if(Wizardry.settings.blinkEffect) blinkEffectTimer = BLINK_EFFECT_DURATION;
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(Wizardry.settings.blinkEffect){
if(blinkEffectTimer > 0) blinkEffectTimer--;
}else{
blinkEffectTimer = 0;
}
}
}
@SubscribeEvent
public static void onFOVUpdateEvent(FOVUpdateEvent event){
if(blinkEffectTimer > 0){
float f = ((float)Math.max(blinkEffectTimer - 2, 0))/BLINK_EFFECT_DURATION;
event.setNewfov(event.getFov() + f * f * 0.7f);
}
}
@SubscribeEvent
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
if(blinkEffectTimer > 0){
float alpha = ((float)blinkEffectTimer)/BLINK_EFFECT_DURATION;
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, alpha);
GlStateManager.disableAlpha();
WizardryClientEventHandler.renderScreenOverlay(event.getResolution(), SCREEN_OVERLAY_TEXTURE);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
}
}
}
@@ -0,0 +1,40 @@
package electroblob.wizardry.client.renderer.overlay;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.WizardryClientEventHandler;
import electroblob.wizardry.registry.WizardryPotions;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderFrostOverlay {
private static final ResourceLocation SCREEN_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png");
@SubscribeEvent
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.frost)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
WizardryClientEventHandler.renderScreenOverlay(event.getResolution(), SCREEN_OVERLAY_TEXTURE);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
}
}
}
@@ -0,0 +1,130 @@
package electroblob.wizardry.client.renderer.overlay;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.WizardryClientEventHandler;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
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.entity.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderSixthSense {
private static final ResourceLocation MARKER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_marker.png");
private static final ResourceLocation SCREEN_OVERLAY_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png");
private static final int PASSIVE_MOB_MARKER_COLOUR = 0xc6ff00;
private static final int HOSTILE_MOB_MARKER_COLOUR = 0x004a97;
private static final int PLAYER_MARKER_COLOUR = 0xffffff;
@SubscribeEvent
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
WizardryClientEventHandler.renderScreenOverlay(event.getResolution(), SCREEN_OVERLAY_TEXTURE);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
Minecraft mc = Minecraft.getMinecraft();
RenderManager renderManager = event.getRenderer().getRenderManager();
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();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
// Makes the 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);
int colour = PASSIVE_MOB_MARKER_COLOUR;
if(ItemArtefact.isArtefactActive(mc.player, WizardryItems.charm_sixth_sense)){
if(event.getEntity() instanceof IMob) colour = HOSTILE_MOB_MARKER_COLOUR;
else if(event.getEntity() instanceof EntityPlayer) colour = PLAYER_MARKER_COLOUR;
}
int r = colour >> 16 & 255;
int g = colour >> 8 & 255;
int b = colour & 255;
GlStateManager.color(r/255f, g/255f, b/255f, 1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(MARKER_TEXTURE);
buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex();
buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex();
buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex();
buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
// Reverses the colour addition change from before
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.popMatrix();
}
}
}
@@ -0,0 +1,134 @@
package electroblob.wizardry.client.renderer.overlay;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.RayTracer;
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.entity.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.RayTraceResult;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderTargetPointers {
private static final ResourceLocation POINTER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/pointer.png");
private static final ResourceLocation TARGET_POINTER_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/target_pointer.png");
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
Minecraft mc = Minecraft.getMinecraft();
WizardData data = WizardData.get(mc.player);
RenderManager renderManager = event.getRenderer().getRenderManager();
ItemStack wand = mc.player.getHeldItemMainhand();
if(!(wand.getItem() instanceof ISpellCastingItem)){
wand = mc.player.getHeldItemOffhand();
}
// Target selection pointer
if(mc.player.isSneaking() && wand.getItem() instanceof ISpellCastingItem && EntityUtils.isLiving(event.getEntity())
&& data != null && data.selectedMinion != null){
// -> Moved this in here so it isn't called every tick
RayTraceResult rayTrace = RayTracer.standardEntityRayTrace(mc.world, mc.player, 16, false);
if(rayTrace != null && rayTrace.entityHit == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(TARGET_POINTER_TEXTURE);
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
// Summoned creature selection pointer
if(data != null && data.selectedMinion != null && data.selectedMinion.get() == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
mc.renderEngine.bindTexture(POINTER_TEXTURE);
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
tessellator.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.overlay;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.WizardData;
@@ -7,8 +7,8 @@ import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Transportation;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.Location;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.BufferBuilder;
@@ -84,13 +84,13 @@ public class RenderTransportationUI {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
Vec3d position = WizardryUtilities.getCentre(location.pos).subtract(origin);
Vec3d position = GeometryUtils.getCentre(location.pos).subtract(origin);
double distance = position.length();
// The icon lines up perfectly if you render it at actual distance, otherwise view bobbing messes things up
// However, if that's outside the render distance it won't render at all! To fudge our way around this
// problem, we're capping the distance to just below the render distance and adjusting the scale accordingly
double distanceCap = Minecraft.getMinecraft().gameSettings.renderDistanceChunks * 16 - 8;
double displayDist = distance > distanceCap ? distanceCap : distance;
double displayDist = Math.min(distance, distanceCap);
double factor = displayDist/distance;
GlStateManager.translate(position.x * factor, position.y * factor, position.z * factor);
@@ -109,9 +109,9 @@ public class RenderTransportationUI {
float f = location == target ? 1 : 0.5f; // Makes it obvious which one is being aimed at
buffer.pos(-iconSize, iconSize, 0).tex(0, 0).color(f, 1, f, f).endVertex();
buffer.pos(iconSize, iconSize, 0).tex(1, 0).color(f, 1, f, f).endVertex();
buffer.pos(iconSize, -iconSize, 0).tex(1, 1).color(f, 1, f, f).endVertex();
buffer.pos(-iconSize, iconSize, 0).tex(0, 0).color(f, 1, f, f).endVertex();
buffer.pos( iconSize, iconSize, 0).tex(1, 0).color(f, 1, f, f).endVertex();
buffer.pos( iconSize, -iconSize, 0).tex(1, 1).color(f, 1, f, f).endVertex();
buffer.pos(-iconSize, -iconSize, 0).tex(0, 1).color(f, 1, f, f).endVertex();
tessellator.draw();
@@ -157,10 +157,10 @@ public class RenderTransportationUI {
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferbuilder = tessellator.getBuffer();
bufferbuilder.begin(7, DefaultVertexFormats.POSITION_COLOR);
bufferbuilder.pos((double)(-i - 1), (double)(-1 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos((double)(-i - 1), (double)(8 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos((double)(i + 1), (double)(8 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos((double)(i + 1), (double)(-1 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos((-i - 1), (-1 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos((-i - 1), ( 8 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos(( i + 1), ( 8 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
bufferbuilder.pos(( i + 1), (-1 + verticalShift), 0.0D).color(0.0F, 0.0F, 0.0F, 0.25F).endVertex();
tessellator.draw();
GlStateManager.enableTexture2D();

Some files were not shown because too many files have changed in this diff Show More