diff --git a/src/main/java/electroblob/wizardry/CommonProxy.java b/src/main/java/electroblob/wizardry/CommonProxy.java index cb2e1441..367352c7 100644 --- a/src/main/java/electroblob/wizardry/CommonProxy.java +++ b/src/main/java/electroblob/wizardry/CommonProxy.java @@ -46,13 +46,14 @@ public class CommonProxy { public void registerKeyBindings(){ } - public void registerSpellHUD(){} public net.minecraft.client.model.ModelBiped getWizardArmourModel(){ return null; } public void initGuiBits(){} + + public void registerResourceReloadListener(){} // SECTION Particles // =============================================================================================================== @@ -126,6 +127,8 @@ public class CommonProxy { public void setToNumberSliderEntry(Property property){ } + + public void setToHUDChooserEntry(Property property){} // public void setToEntityNameEntry(Property property){} /** @@ -148,4 +151,9 @@ public class CommonProxy { return null; } + /** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */ + public Set getSpellHUDSkins(){ + return null; + } + } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java index f409ecf5..3994f11b 100644 --- a/src/main/java/electroblob/wizardry/Settings.java +++ b/src/main/java/electroblob/wizardry/Settings.java @@ -164,6 +164,10 @@ public final class Settings { public boolean showSummonedCreatureNames = true; /** [Client-only] The position of the spell HUD. */ public GuiPosition spellHUDPosition = GuiPosition.BOTTOM_LEFT; + + public static final String DEFAULT_HUD_SKIN_KEY = "default"; // Defined here so it's not in a client-only class. + /** [Client-only] The string identifier of the skin used for the spell HUD. */ + public String spellHUDSkin = DEFAULT_HUD_SKIN_KEY; /** Set of constants for each of the four positions that the spell HUD can be in. */ public enum GuiPosition { @@ -190,6 +194,8 @@ public final class Settings { GuiPosition(String name, boolean flipX, boolean flipY){ this.name = name; + this.flipX = flipX; + this.flipY = flipY; } /** @@ -458,6 +464,12 @@ public final class Settings { property.setLanguageKey("config." + Wizardry.MODID + ".spell_hud_position"); spellHUDPosition = GuiPosition.fromName(property.getString()); propOrder.add(property.getName()); + + property = config.get(CLIENT_CATEGORY, "spellHUDSkin", DEFAULT_HUD_SKIN_KEY, "The skin used for the spell HUD.", Wizardry.proxy.getSpellHUDSkins().toArray(new String[0])); + property.setLanguageKey("config." + Wizardry.MODID + ".spell_hud_skin"); + Wizardry.proxy.setToHUDChooserEntry(property); + spellHUDSkin = property.getString(); + propOrder.add(property.getName()); property = config.get(CLIENT_CATEGORY, "showSummonedCreatureNames", true, "Whether to show summoned creatures' names and owners above their heads."); property.setLanguageKey("config." + Wizardry.MODID + ".show_summoned_creature_names"); diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 42b156ca..c746803a 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -120,6 +120,8 @@ public class Wizardry { public void preInit(FMLPreInitializationEvent event){ logger = event.getModLog(); + + proxy.registerResourceReloadListener(); settings.initConfig(event); @@ -162,9 +164,7 @@ public class Wizardry { // Event Handlers GameRegistry.registerWorldGenerator(generator, 0); - MinecraftForge.EVENT_BUS.register(new WizardryKeyHandler()); MinecraftForge.EVENT_BUS.register(instance); - proxy.registerSpellHUD(); // This can't easily be converted to use the new @Mod.EventBusSubscriber system NetworkRegistry.INSTANCE.registerGuiHandler(this, new WizardryGuiHandler()); WizardryPacketHandler.initPackets(); diff --git a/src/main/java/electroblob/wizardry/WizardryKeyHandler.java b/src/main/java/electroblob/wizardry/WizardryKeyHandler.java deleted file mode 100644 index 99852c4c..00000000 --- a/src/main/java/electroblob/wizardry/WizardryKeyHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package electroblob.wizardry; - -import org.lwjgl.input.Keyboard; - -import electroblob.wizardry.client.ClientProxy; -import electroblob.wizardry.packet.PacketControlInput; -import electroblob.wizardry.packet.WizardryPacketHandler; -import net.minecraft.client.Minecraft; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.InputEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; - -public class WizardryKeyHandler { - - boolean NkeyPressed = false; - boolean BkeyPressed = false; - boolean NkeyAlreadyPressed = false; - boolean BkeyAlreadyPressed = false; - - @SubscribeEvent - public void onKeyInput(InputEvent.KeyInputEvent event){ - - // Key pressed - if(Keyboard.getEventKeyState()){ - - if(Wizardry.proxy instanceof ClientProxy){ - - if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ - if(!NkeyPressed){ - NkeyPressed = true; - }else{ - NkeyAlreadyPressed = true; - } - if(!NkeyAlreadyPressed){ - // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); - WizardryPacketHandler.net.sendToServer(msg); - } - } - - if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ - if(!BkeyPressed){ - BkeyPressed = true; - }else{ - BkeyAlreadyPressed = true; - } - if(!BkeyAlreadyPressed){ - // Packet building - IMessage msg = new PacketControlInput.Message( - PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); - WizardryPacketHandler.net.sendToServer(msg); - } - } - } - } - - // Key released - else{ - if(NkeyPressed){ - NkeyPressed = false; - NkeyAlreadyPressed = false; - }else if(BkeyPressed){ - BkeyPressed = false; - BkeyAlreadyPressed = false; - } - } - } -} diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 93deb66d..d688d89f 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -130,6 +130,8 @@ import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.renderer.entity.RenderBlaze; import net.minecraft.client.resources.I18n; +import net.minecraft.client.resources.IReloadableResourceManager; +import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; @@ -185,17 +187,20 @@ public class ClientProxy extends CommonProxy { ClientRegistry.registerKeyBinding(PREVIOUS_SPELL); } - @Override - public void registerSpellHUD(){ - MinecraftForge.EVENT_BUS.register(new GuiSpellDisplay(Minecraft.getMinecraft())); - } - @Override public void initGuiBits(){ mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, new ResourceLocation("textures/font/ascii.png"), Minecraft.getMinecraft().renderEngine, false); GuiWizardHandbook.initDisplayRecipes(); } + + @Override + public void registerResourceReloadListener(){ + IResourceManager manager = Minecraft.getMinecraft().getResourceManager(); + if(manager instanceof IReloadableResourceManager){ + ((IReloadableResourceManager)manager).registerReloadListener(GuiSpellDisplay::loadSkins); + } + } // SECTION Misc // =============================================================================================================== @@ -204,6 +209,11 @@ public class ClientProxy extends CommonProxy { public void setToNumberSliderEntry(Property property){ property.setConfigEntryClass(NumberSliderEntry.class); } + + @Override + public void setToHUDChooserEntry(Property property){ + property.setConfigEntryClass(SpellHUDSkinChooserEntry.class); + } @Override public World getTheWorld(){ @@ -214,6 +224,11 @@ public class ClientProxy extends CommonProxy { public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){ Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, sound, volume, pitch, repeat)); } + + @Override + public Set getSpellHUDSkins(){ + return GuiSpellDisplay.getSkinKeys(); + } // SECTION Items // =============================================================================================================== diff --git a/src/main/java/electroblob/wizardry/client/SpellHUDSkinChooserEntry.java b/src/main/java/electroblob/wizardry/client/SpellHUDSkinChooserEntry.java new file mode 100644 index 00000000..a6872673 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/SpellHUDSkinChooserEntry.java @@ -0,0 +1,38 @@ +package electroblob.wizardry.client; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import electroblob.wizardry.client.gui.GuiSelectHUDSkin; +import electroblob.wizardry.client.gui.GuiSpellDisplay; +import net.minecraftforge.client.gui.ForgeGuiFactory.ForgeConfigGui.ModIDEntry; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.GuiConfigEntries; +import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry; +import net.minecraftforge.fml.client.config.IConfigElement; + +/** + * Custom config GUI for spell HUD skin selection; displays a list of all the loaded skins and a preview of the currently + * selected skin. based off of {@link ModIDEntry} from Forge. + */ +public class SpellHUDSkinChooserEntry extends SelectValueEntry { + + public SpellHUDSkinChooserEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) + { + super(owningScreen, owningEntryList, prop, getSelectableValues()); + if (this.selectableValues.size() == 0) + this.btnValue.enabled = false; + } + + private static Map getSelectableValues(){ + return GuiSpellDisplay.getSkins().entrySet().stream().collect(Collectors.toMap(Entry::getKey, + e -> e.getValue().getName())); + } + + @Override // Copied from superclass to use custom child screen GUI class + public void valueButtonPressed(int slotIndex){ + mc.displayGuiScreen(new GuiSelectHUDSkin(this.owningScreen, configElement, slotIndex, selectableValues, currentValue, enabled())); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index 39cf3b92..ced104f6 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -7,8 +7,6 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.item.ItemSpectralBow; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.packet.PacketControlInput; -import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.spell.Flight; @@ -38,7 +36,6 @@ import net.minecraft.util.math.RayTraceResult; import net.minecraft.village.MerchantRecipe; import net.minecraftforge.client.event.FOVUpdateEvent; import net.minecraftforge.client.event.GuiContainerEvent; -import net.minecraftforge.client.event.MouseEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderLivingEvent; import net.minecraftforge.client.event.RenderPlayerEvent; @@ -47,7 +44,6 @@ import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -101,37 +97,6 @@ public final class WizardryClientEventHandler { // } } - // Shift-scrolling to change spells - @SubscribeEvent - public static void onMouseEvent(MouseEvent event){ - - EntityPlayer player = Minecraft.getMinecraft().player; - ItemStack wand = player.getHeldItemMainhand(); - - if(!(wand.getItem() instanceof ItemWand)){ - wand = player.getHeldItemOffhand(); - // If the player isn't holding a wand, then nothing else needs to be done. - if(!(wand.getItem() instanceof ItemWand)) return; - } - - if(Minecraft.getMinecraft().inGameHasFocus && !wand.isEmpty() && event.getDwheel() != 0 && player.isSneaking() - && Wizardry.settings.enableShiftScrolling){ - - event.setCanceled(true); - - if(event.getDwheel() > 0){ - // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); - WizardryPacketHandler.net.sendToServer(msg); - - }else if(event.getDwheel() < 0){ - // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); - WizardryPacketHandler.net.sendToServer(msg); - } - } - } - @SubscribeEvent public static void onFOVUpdateEvent(FOVUpdateEvent event){ diff --git a/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java b/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java new file mode 100644 index 00000000..8e4fea67 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/WizardryControlHandler.java @@ -0,0 +1,132 @@ +package electroblob.wizardry.client; + +import org.lwjgl.input.Keyboard; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.gui.GuiSpellDisplay; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.packet.PacketControlInput; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.util.WandHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.client.event.MouseEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.InputEvent; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** Event handler class responsible for handling wizardry's controls. */ +@SideOnly(Side.CLIENT) +@Mod.EventBusSubscriber(Side.CLIENT) +public class WizardryControlHandler { + + static boolean NkeyPressed = false; + static boolean BkeyPressed = false; + static boolean NkeyAlreadyPressed = false; + static boolean BkeyAlreadyPressed = false; + + @SubscribeEvent + public static void onKeyInput(InputEvent.KeyInputEvent event){ + + // Key pressed + if(Keyboard.getEventKeyState()){ + + if(Wizardry.proxy instanceof ClientProxy){ + + EntityPlayer player = Minecraft.getMinecraft().player; + ItemStack wand = player.getHeldItemMainhand(); + + if(!(wand.getItem() instanceof ItemWand)){ + wand = player.getHeldItemOffhand(); + // If the player isn't holding a wand, then nothing else needs to be done. + if(!(wand.getItem() instanceof ItemWand)) return; + } + + if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + if(!NkeyPressed){ + NkeyPressed = true; + }else{ + NkeyAlreadyPressed = true; + } + if(!NkeyAlreadyPressed){ + selectNextSpell(wand); + } + } + + if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + if(!BkeyPressed){ + BkeyPressed = true; + }else{ + BkeyAlreadyPressed = true; + } + if(!BkeyAlreadyPressed){ + selectPreviousSpell(wand); + } + } + } + } + + // Key released + else{ + if(NkeyPressed){ + NkeyPressed = false; + NkeyAlreadyPressed = false; + }else if(BkeyPressed){ + BkeyPressed = false; + BkeyAlreadyPressed = false; + } + } + } + + // Shift-scrolling to change spells + @SubscribeEvent + public static void onMouseEvent(MouseEvent event){ + + EntityPlayer player = Minecraft.getMinecraft().player; + ItemStack wand = player.getHeldItemMainhand(); + + if(!(wand.getItem() instanceof ItemWand)){ + wand = player.getHeldItemOffhand(); + // If the player isn't holding a wand, then nothing else needs to be done. + if(!(wand.getItem() instanceof ItemWand)) return; + } + + if(Minecraft.getMinecraft().inGameHasFocus && !wand.isEmpty() && event.getDwheel() != 0 && player.isSneaking() + && Wizardry.settings.enableShiftScrolling){ + + event.setCanceled(true); + + if(event.getDwheel() > 0){ + selectNextSpell(wand); + }else if(event.getDwheel() < 0){ + selectPreviousSpell(wand); + } + } + } + + private static void selectNextSpell(ItemStack wand){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + // GUI switch animation + WandHelper.selectNextSpell(wand); // Makes sure the spell is set immediately for the client + GuiSpellDisplay.playSpellSwitchAnimation(true); + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.SELECT_SPELL, 1)); + } + + private static void selectPreviousSpell(ItemStack wand){ + // Packet building + IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); + WizardryPacketHandler.net.sendToServer(msg); + // GUI switch animation + WandHelper.selectPreviousSpell(wand); // Makes sure the spell is set immediately for the client + GuiSpellDisplay.playSpellSwitchAnimation(false); + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.SELECT_SPELL, 1)); + } +} diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSelectHUDSkin.java b/src/main/java/electroblob/wizardry/client/gui/GuiSelectHUDSkin.java new file mode 100644 index 00000000..1da12067 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSelectHUDSkin.java @@ -0,0 +1,106 @@ +package electroblob.wizardry.client.gui; + +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.collect.Lists; + +import electroblob.wizardry.client.gui.GuiSpellDisplay.Skin; +import electroblob.wizardry.registry.Spells; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraftforge.fml.client.config.GuiSelectString; +import net.minecraftforge.fml.client.config.IConfigElement; + +public class GuiSelectHUDSkin extends GuiSelectString { + + public GuiSelectHUDSkin(GuiScreen parentScreen, IConfigElement configElement, int slotIndex, Map selectableValues, Object currentValue, boolean enabled){ + super(parentScreen, configElement, slotIndex, selectableValues, currentValue, enabled); + } + + @Override + public void initGui(){ + super.initGui(); + this.entryList.setDimensions(150, height, 43, height-43); + this.entryList.left = 10; + this.entryList.headerPadding = 5; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks){ + + super.drawScreen(mouseX, mouseY, partialTicks); + + GlStateManager.disableLighting(); + + if(this.currentValue instanceof String){ + + this.drawString(this.fontRenderer, "Preview:", 170, 44, 0xffffff); + + int previewLeft = 170; + int previewRight = width-10; + int previewTop = 60; + int previewBottom = height-43; + + this.drawGradientRect(previewLeft, previewTop, previewRight, previewBottom, 0x88000000, 0x88000000); + + int previewBorder = 10; + + Skin skin = GuiSpellDisplay.getSkin((String)this.currentValue); + + 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; + + GlStateManager.pushMatrix(); + + GlStateManager.scale(scale, scale, scale); + + skin.drawBackground((int)(x/scale), (int)(y/scale), false, false, + Spells.magic_missile.getIcon(), 0.6f, false); + + skin.drawText((int)(x/scale), (int)(y/scale), false, false, + Spells.none.getDisplayNameWithFormatting(), + Spells.magic_missile.getDisplayNameWithFormatting(), + Spells.none.getDisplayNameWithFormatting(), 0); + + GlStateManager.popMatrix(); + + Skin hovered = getHoveredSkin(mouseX, mouseY); + + if(hovered != null){ + this.drawToolTip(Lists.newArrayList("\u00A7a" + hovered.getName(), "\u00A7e" + hovered.getDescription()), + mouseX, mouseY); + } + } + + GlStateManager.enableLighting(); + } + + /** Returns the skin corresponding to the list entry being hovered over, or null if there is none. */ + @Nullable + private Skin getHoveredSkin(int mouseX, int mouseY){ + + int index = this.entryList.getSlotIndexFromScreenCoords(mouseX, mouseY); + + if(index >= 0 && index <= this.entryList.listEntries.size() && mouseY <= this.entryList.bottom){ + + Object object = entryList.getListEntry(index).getValue(); + + if(object instanceof String){ + return GuiSpellDisplay.getSkin((String)object); + } + } + + return null; + } + + @Override // Stops the world being visible behind the GUI when configuring from within a world + public void drawWorldBackground(int tint){ + this.drawBackground(tint); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java index 583d418b..fff4d965 100644 --- a/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java +++ b/src/main/java/electroblob/wizardry/client/gui/GuiSpellDisplay.java @@ -1,10 +1,25 @@ package electroblob.wizardry.client.gui; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import electroblob.wizardry.Settings; import electroblob.wizardry.SpellGlyphData; import electroblob.wizardry.WizardData; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.ClientProxy; +import electroblob.wizardry.client.MixedFontRenderer; import electroblob.wizardry.constants.Constants; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.registry.Spells; @@ -15,42 +30,78 @@ import electroblob.wizardry.util.WandHelper; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.Gui; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager.DestFactor; import net.minecraft.client.renderer.GlStateManager.SourceFactor; +import net.minecraft.client.resources.IResource; +import net.minecraft.client.resources.IResourceManager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.util.JsonUtils; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; +import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; -public class GuiSpellDisplay extends Gui { +@Mod.EventBusSubscriber(Side.CLIENT) +public class GuiSpellDisplay { - private Minecraft mc; - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud.png"); - - // Measurements - /** Width of the used portion of the HUD texture */ private static final int HUD_WIDTH = 128; - /** Height of the used portion of the HUD texture */ private static final int HUD_HEIGHT = 64; - /** Distance (in x and y) of the spell icon from the screen edge */ private static final int SPELL_ICON_INSET = 2; - /** Line spacing for the spell name (when on two lines) */ private static final int TEXT_LINE_SPACING = 1; - /** Distance in x from the edge of the screen to the spell name */ private static final int TEXT_INSET_X = 42; - /** Distance in x of the cooldown bar from the screen edge */ private static final int COOLDOWN_BAR_INSET_X = 42; - /** Length of the cooldown bar portion of the HUD texture */ private static final int COOLDOWN_BAR_LENGTH = 79; - /** Height of the cooldown bar portion of the HUD texture */ private static final int COOLDOWN_BAR_HEIGHT = 5; - /** Width and height of the spell icon (very unlikely to change!) */private static final int SPELL_ICON_SIZE = 32; + private static final ResourceLocation INDEX = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud/_index.json"); - public GuiSpellDisplay(Minecraft minecraft){ - super(); - this.mc = minecraft; + /** A map which stores all loaded HUD skin objects. This gets wiped on resource pack reload and repopulated with + * mappings as specified by {@code _index.json} (these stack between resource packs). The keys in the map correspond + * to the keys in {@code _index.json}, and are sorted in that order, with skins belonging to resource packs sorted + * from lowest to highest priority. The skins in the base mod will therefore always be first. */ + private static final Map skins = new LinkedHashMap<>(12); // 12 is the number of skins packaged with the mod + + private static final Gson gson = new Gson(); + + /** Width and height of the spell icon (very unlikely to change!) */ + private static final int SPELL_ICON_SIZE = 32; + /** Number of ticks the spell switching animation plays for. */ + private static final int SPELL_SWITCH_TIME = 4; + /** Scale of the next/previous spell names. */ + private static final float SPELL_NAME_SCALE = 0.5f; + /** Opacity of the next/previous spell names, as a fraction. */ + private static final float SPELL_NAME_OPACITY = 0.3f; + + /** Controls the spell switching animation. Positive when switching to the next spell, negative when switching to + * the previous spell. Decremented in magnitude by 1 each tick until it reaches 0 again. */ + private static int switchTimer = 0; + + /** + * Starts the spell switching animation. + * @param next True to switch to the next spell, false for the previous spell. + */ + public static void playSpellSwitchAnimation(boolean next){ + switchTimer = next ? SPELL_SWITCH_TIME : -SPELL_SWITCH_TIME; } - + + /** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */ + public static Set getSkinKeys(){ + return Collections.unmodifiableSet(skins.keySet()); + } + + /** Returns an unmodifiable view of the loaded spell HUD skins map. */ + public static Map getSkins(){ + return Collections.unmodifiableMap(skins); + } + + /** Returns the skin that corresponds to the given key. */ + public static Skin getSkin(String key){ + return skins.get(key); + } + + // Normally when extending Gui, you'd have to have an instance to access its methods. However, we're not actually + // using any of them, so this class may as well not bother and just be a static event handler. Neat! @SubscribeEvent - public void draw(RenderGameOverlayEvent event){ + public static void draw(RenderGameOverlayEvent event){ + + Minecraft mc = Minecraft.getMinecraft(); - EntityPlayer player = this.mc.player; + EntityPlayer player = mc.player; // If the player has a wand in each hand, only displays for the one in the main hand. @@ -65,100 +116,502 @@ public class GuiSpellDisplay extends Gui { int width = event.getResolution().getScaledWidth(); int height = event.getResolution().getScaledHeight(); + boolean flipX = Wizardry.settings.spellHUDPosition.flipX; + boolean flipY = Wizardry.settings.spellHUDPosition.flipY; + + 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."); + return; + } + } + + // '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; + Spell spell = WandHelper.getCurrentSpell(wand); int cooldown = WandHelper.getCurrentCooldown(wand); - float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade) * Constants.COOLDOWN_REDUCTION_PER_LEVEL; + 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)); + String spellName = getFormattedSpellName(spell, player, cooldown); + String nextSpellName = getFormattedSpellName(WandHelper.getNextSpell(wand), player, WandHelper.getNextCooldown(wand)); - if(player.isPotionActive(WizardryPotions.font_of_mana)){ - // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously - cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); + skin.drawText(x, y, flipX, flipY, prevSpellName, spellName, nextSpellName, animationProgress); + + }else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){ + + float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade) * Constants.COOLDOWN_REDUCTION_PER_LEVEL; + + if(player.isPotionActive(WizardryPotions.font_of_mana)){ + // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously + cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); + } + + boolean discovered = true; + + if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){ + 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.capabilities.isCreativeMode && !spell.isContinuous){ + // Subtracted partial tick time to make it smoother + progress = (spell.cooldown * cooldownMultiplier - (float)cooldown + event.getPartialTicks()) + /(spell.cooldown * cooldownMultiplier); + } + + skin.drawBackground(x, y, flipX, flipY, icon, progress, player.capabilities.isCreativeMode); + } - - boolean flipX = Wizardry.settings.spellHUDPosition.flipX; - boolean flipY = Wizardry.settings.spellHUDPosition.flipY; - + } + + /** + * Makes the given opaque colour translucent with the given opacity. + * @param colour An integer colour code, should be a 6-digit hexadecimal (i.e. opaque). + * @param opacity The opacity to apply to the given colour, as a fraction between 0 and 1. + * @return The resulting integer colour code, which will be an 8-digit hexadecimal. + */ + private static int makeTranslucent(int colour, float opacity){ + return colour + ((int)(0xff * opacity * 0x01000000)); + } + + /** Gets the name of the given spell, with formatted added according to its cooldown and whether the given player + * has discovered it. + * @param spell The spell to get the name of. + * @param The player to test for having discovered the given spell. + * @param cooldown The spell's current cooldown. + * @return The spell name, with relevant formatting added, for use with the {@link MixedFontRenderer}. + */ + private static String getFormattedSpellName(Spell spell, EntityPlayer player, int cooldown){ + boolean discovered = true; if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){ discovered = WizardData.get(player).hasSpellBeenDiscovered(spell); } + + // Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect + String format = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode(); + if(!discovered) format = "\u00A79"; + + String name = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world); + name = format + name; + if(!discovered) name = "#" + name + "#"; + + return name; + } + + /** + * Draws the given string at the given position, scaling it if it does not fit within the given width. + * @param font A {@code FontRenderer} object. + * @param text The text to display. + * @param x The x position of the top-left corner of the text. + * @param y The y position of the top-left corner of the text. + * @param scale The scale that the text should be if it does not exceed the maximum width. + * @param colour The colour to render the text in, supports translucency. + * @param width The maximum width of the text. This is not scaled; you should pass in the width of the actual + * area of the screen in which the text needs to fit. + * @param centre Whether to adjust the y position such that the centre of the text lines up with where its centre + * would be if it was not scaled (automatically or manually). + * @param alignR True to right-align the text, false for normal left alignment. + */ + private static void drawScaledStringToWidth(FontRenderer font, String text, float x, float y, float scale, int colour, float width, boolean centre, boolean alignR){ + + float textWidth = font.getStringWidth(text) * scale; + float textHeight = font.FONT_HEIGHT * scale; + + if(textWidth > width){ + scale *= width/textWidth; + }else if(alignR){ // Alignment makes no difference if the string fills the entire width + x += width - textWidth; + } + + if(centre) y += (font.FONT_HEIGHT - textHeight)/2; + + drawScaledTranslucentString(font, text, x, y, scale, colour); + } + + /** Draws the given string at the given position, scaling the text by the specified factor. Also enables blending to + * render text in semitransparent colours (e.g. 0x88ffffff). */ + private static void drawScaledTranslucentString(FontRenderer font, String text, float x, float y, float scale, int colour){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.scale(scale, scale, scale); + // Because we scaled it, the coordinates have to be scaled inversely + x /= scale; + y /= scale; + font.drawStringWithShadow(text, x, y, colour); + GlStateManager.disableBlend(); + GlStateManager.popMatrix(); + } + + @SubscribeEvent + public static void onLivingUpdateEvent(LivingUpdateEvent event){ + if(event.getEntity() == Minecraft.getMinecraft().player){ // Makes sure this only gets called once each tick. + if(switchTimer > 0) switchTimer--; + else if(switchTimer < 0) switchTimer++; + } + } + + /** Called from preInit in the main mod class (via the proxies) to initialise the HUD skins, and again on each + * resource reload. */ + public static void loadSkins(IResourceManager manager){ + + try { - if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){ - - // Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect - String colour = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode(); - if(!discovered) colour = "\u00A79"; - String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world); - FontRenderer font = discovered ? this.mc.fontRenderer : this.mc.standardGalacticFontRenderer; - - int maxWidth = HUD_WIDTH - TEXT_INSET_X; - int stringWidth = font.getStringWidth(spellName); - int iconMidpoint = (SPELL_ICON_SIZE + 2*SPELL_ICON_INSET)/2; // Should be 18 - - // TODO: Make a 'scrolling' animation when changing spells, using the text alpha channel to fade it in - // and out. - if(stringWidth <= maxWidth){ - //GL11.glPushMatrix(); - //GL11.glEnable(GL11.GL_BLEND); - //OpenGlHelper.glBlendFunc(770, 771, 1, 0); // Taken from GuiInGame. TODO: Replace with OpenGL caps. - // Single line is rendered more centrally - font.drawStringWithShadow(colour + spellName, flipX ? width - TEXT_INSET_X - stringWidth : TEXT_INSET_X, - // The text is an odd number of pixels high so we need to subtract an extra 1 when at the bottom - flipY ? iconMidpoint - font.FONT_HEIGHT/2 : height - iconMidpoint - font.FONT_HEIGHT/2 - 1, 0xffffffff); - //GL11.glDisable(GL11.GL_BLEND); - //GL11.glPopMatrix(); - }else{ - - int lineNumber = 0; - - List lines = font.listFormattedStringToWidth(spellName, maxWidth); - - for(String line : lines){ - int lineWidth = font.getStringWidth((String)line); - font.drawStringWithShadow(colour + (String)line, flipX ? width - TEXT_INSET_X - lineWidth : TEXT_INSET_X, - // This time there are two lines so we need to subtract the full line height, and an extra 1 for the spacing - (flipY ? iconMidpoint - font.FONT_HEIGHT - 1 : height - iconMidpoint - font.FONT_HEIGHT - 1) - // Note that there should only ever be two lines maximum - + lineNumber*(font.FONT_HEIGHT + TEXT_LINE_SPACING), 0xffffffff); - lineNumber++; + List indexFiles = manager.getAllResources(INDEX); + + skins.clear(); // Wipes the skins map before repopulating it + + for(IResource indexFile : indexFiles){ + + BufferedReader reader = new BufferedReader(new InputStreamReader(indexFile.getInputStream())); + + JsonElement je = gson.fromJson(reader, JsonElement.class); + JsonObject json = je.getAsJsonObject(); + + // Need to iterate over these since we don't know what they're called or how many there are + for(Entry entry : json.entrySet()){ + + String key = entry.getKey(); // Find out what each element is called, this will be the skins map key + + // It's a good idea to use JsonUtils because it produces more helpful error messages (that pack + // makers should understand). + + JsonObject skinData = JsonUtils.getJsonObject(json, key); + + String[] splitName = ResourceLocation.splitObjectName(JsonUtils.getString(skinData, "texture")); + ResourceLocation texture = new ResourceLocation(splitName[0], "textures/" + splitName[1] + ".png"); + + splitName = ResourceLocation.splitObjectName(JsonUtils.getString(skinData, "metadata")); + ResourceLocation metadata = new ResourceLocation(splitName[0], "textures/" + splitName[1] + ".json"); + + // The nice thing about this is it overwrites the existing mapping, and since the index files are in + // ascending order of resource pack priority, this means resource packs can override existing skins + // by specifying one with the same key. + skins.put(key, new Skin(texture, metadata)); } } - }else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){ + } catch (IOException e){ + // If an exception is thrown, chances are the resource pack did not have a spell_hud folder, so nothing + // else needs to be done. + Wizardry.logger.error("Error reading spell HUD skin index file: ", e); + } + } + + /** Instances of this class represent individual HUD skins, complete with texture and all necessary metadata. This + * class serves to separate the logic behind the spell HUD from its actual rendering. + * All information and processing done within this class relates only to the actual drawing; spells and such like + * must be queried outside of this class and fed into the methods as appropriate. */ + public static class Skin { + + /** The texture file for this skin. */ + private final ResourceLocation texture; + /** The display name of the skin in the config menu. */ + private String name; + /** The description of the skin shown when its button is hovered over in the config menu. */ + private String description; + + /** Width of the entire spell HUD. */ + private int width; + /** Height of the entire spell HUD. */ + private int height; + + /** Whether the entire HUD is flipped when on the right-hand side of the screen. If this is false, the HUD will + * still appear on the right-hand side of the screen, but in the same orientation as on the left-hand side. */ + private boolean mirrorX; + /** Whether the entire HUD is flipped when at the top of the screen. If this is false, the HUD will + * still appear at the top of the screen, but in the same orientation as at the bottom. */ + private boolean mirrorY; + + /** Distance of the spell icon from the left edge of the screen (or right edge when flipped). */ + private int spellIconInsetX; + /** Distance of the spell icon from the bottom edge of the screen (or top edge when flipped). */ + private int spellIconInsetY; + + /** Distance of the spell name from the left edge of the screen (or right edge when flipped). */ + private int textInsetX; + /** Distance of the spell name from the bottom edge of the screen (or the top edge when flipped). */ + private int textInsetY; + + /** Horizontal distance between the start of adjacent spell names. */ + private int cascadeOffsetX; + /** Vertical distance between the start of adjacent spell names. */ + private int cascadeOffsetY; + + /** Distance of the cooldown bar from the left edge of the screen (or right edge when flipped). */ + private int cooldownBarX; + /** Distance of the cooldown bar from the bottom edge of the screen (or top edge when flipped). */ + private int cooldownBarY; + /** Length of the cooldown bar. */ + private int cooldownBarLength; + /** Height of the cooldown bar. */ + private int cooldownBarHeight; + + /** Whether the cooldown bar is flipped horizontally when the HUD is on the right-hand side of the screen. */ + private boolean cooldownBarMirrorX; + /** Whether the cooldown bar is flipped vertically when the HUD is at the top of the screen. */ + private boolean cooldownBarMirrorY; + + /** Whether the cooldown bar progress overlay is shown when the cooldown bar is full (i.e. when progress = 1). */ + private boolean showCooldownWhenFull; + + private final Minecraft mc; + + /** Creates a new skin with the given texture and reads its values from the given metadata json file. */ + public Skin(ResourceLocation texture, ResourceLocation metadata){ + + mc = Minecraft.getMinecraft(); + + this.texture = texture; + + try { + // This time we only want the highest priority file + IResource metadataFile = Minecraft.getMinecraft().getResourceManager().getResource(metadata); + BufferedReader reader = new BufferedReader(new InputStreamReader(metadataFile.getInputStream())); + + JsonElement je = gson.fromJson(reader, JsonElement.class); + + parseJson(je.getAsJsonObject()); + + } catch (IOException e){ + // If an exception is thrown, chances are the resource pack did not have a spell_hud folder, so nothing + // else needs to be done. + Wizardry.logger.error("Error reading spell HUD skin metadata file: ", e); + } + } + + /** Returns the display name of this HUD skin, which is shown in the config GUI. */ + public String getName(){ + return name; + } + + /** Returns the description of this HUD skin, which is shown in its tooltip in the config GUI. */ + public String getDescription(){ + return description; + } + + /** Returns the overall width of this spell HUD skin. */ + public int getWidth(){ + return width; + } + + /** Returns the overall height of this spell HUD skin. */ + public int getHeight(){ + return height; + } + + /** Actually reads the metadata values for this skin from the json file. */ + private void parseJson(JsonObject json){ + + // For now, all the keys must be present for the metadata file to work (the only ones that could reasonably + // have a default anyway are the mirror values). + + name = JsonUtils.getString(json, "name"); + description = JsonUtils.getString(json, "description"); + + width = JsonUtils.getInt(json, "width"); + if(width > 128) Wizardry.logger.warn("The width of the spell HUD skin " + name + " exceeds 128, this may cause it to render strangely."); + height = JsonUtils.getInt(json, "height"); + + JsonObject mirror = JsonUtils.getJsonObject(json, "mirror"); + mirrorX = JsonUtils.getBoolean(mirror, "x"); + mirrorY = JsonUtils.getBoolean(mirror, "y"); + + JsonObject spellIconInset = JsonUtils.getJsonObject(json, "spell_icon_inset"); + spellIconInsetX = JsonUtils.getInt(spellIconInset, "x"); + spellIconInsetY = JsonUtils.getInt(spellIconInset, "y"); + + JsonObject textInset = JsonUtils.getJsonObject(json, "text_inset"); + textInsetX = JsonUtils.getInt(textInset, "x"); + textInsetY = JsonUtils.getInt(textInset, "y"); + + JsonObject cascadeOffset = JsonUtils.getJsonObject(json, "spell_cascade_offset"); + cascadeOffsetX = JsonUtils.getInt(cascadeOffset, "x"); + cascadeOffsetY = JsonUtils.getInt(cascadeOffset, "y"); + + JsonObject cooldownBar = JsonUtils.getJsonObject(json, "cooldown_bar"); + cooldownBarX = JsonUtils.getInt(cooldownBar, "x"); + cooldownBarY = JsonUtils.getInt(cooldownBar, "y"); + cooldownBarLength = JsonUtils.getInt(cooldownBar, "length"); + cooldownBarHeight = JsonUtils.getInt(cooldownBar, "height"); + + JsonObject cooldownBarMirror = JsonUtils.getJsonObject(cooldownBar, "mirror"); + cooldownBarMirrorX = JsonUtils.getBoolean(cooldownBarMirror, "x"); + cooldownBarMirrorY = JsonUtils.getBoolean(cooldownBarMirror, "y"); + + showCooldownWhenFull = JsonUtils.getBoolean(cooldownBar, "show_when_full"); + + } + + // The idea of these methods is that everything in here relates only to the actual drawing of the HUD. In other + // words, all processing of which spells to draw and so on is done outside of here. This means that the config + // GUI can easily display its preview without having a player or wand stack object to query. + + /** + * Draws the background layer of this HUD skin at the given position with the given orientations, with the given + * spell icon and cooldown bar progress. + * + * @param x The x-coordinate of the corner of the spell HUD. The bottom left corner of the actual texture + * will always be at this position unless mirrorX/Y is false, so for example if flipX is false and flipY is true, + * this will be the corner of the HUD that is closest to the top left corner of the screen. + * @param y The y-coordinate of the corner of the spell HUD; see above. + * @param flipX Whether to flip the HUD horizontally. + * @param flipY Whether to flip the HUD vertically. + * @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. + */ + public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode){ + + // Moves the origin if the HUD does not mirror; neatens the rest of the code. + if(flipX && !mirrorX) x -= width; + if(flipY && !mirrorY) y += height; + GlStateManager.pushMatrix(); GlStateManager.enableBlend(); GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); GlStateManager.color(1, 1, 1); // Spell illustration - this is now done first so it is behind the HUD texture - this.mc.renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); + mc.renderEngine.bindTexture(icon); - WizardryUtilities.drawTexturedRect(flipX ? width - SPELL_ICON_INSET - SPELL_ICON_SIZE : SPELL_ICON_INSET, - flipY ? SPELL_ICON_INSET : height - SPELL_ICON_INSET - SPELL_ICON_SIZE, 0, 0, 32, 32, 32, 32); + 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; - this.mc.renderEngine.bindTexture(texture); + WizardryUtilities.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE); // Background of spell hud - WizardryUtilities.drawTexturedFlippedRect(flipX ? width-HUD_WIDTH : 0, flipY ? 0 : height-HUD_HEIGHT, - // The 128 here is a uv value, not a dimension, and hence is left as a hardcoded number. - player.capabilities.isCreativeMode ? 128 : 0, 0, HUD_WIDTH, HUD_HEIGHT, 256, 256, flipX, flipY); + mc.renderEngine.bindTexture(texture); + + x1 = flipX && mirrorX ? x - width : x; + 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? + WizardryUtilities.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY); // Cooldown bar - if(!player.capabilities.isCreativeMode && cooldown > 0){ + if(!creativeMode && cooldownBarProgress > 0 && (showCooldownWhenFull || cooldownBarProgress < 1)){ - int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown) - / (double)(spell.cooldown * cooldownMultiplier)) * COOLDOWN_BAR_LENGTH); - // Likewise, the 64 here is a uv value and therefore left as a hardcoded number. - WizardryUtilities.drawTexturedFlippedRect(flipX ? width - COOLDOWN_BAR_INSET_X - COOLDOWN_BAR_LENGTH : COOLDOWN_BAR_INSET_X, - flipY ? 1 : height-1-COOLDOWN_BAR_HEIGHT, 0, 64, l, COOLDOWN_BAR_HEIGHT, 256, 256, false, flipY); + int l = (int)(cooldownBarProgress * cooldownBarLength); + + x1 = flipX && mirrorX ? x - cooldownBarX - (cooldownBarMirrorX ? l : cooldownBarLength) : x + cooldownBarX; + y1 = flipY && mirrorY ? y + cooldownBarY : y - cooldownBarY - cooldownBarHeight; + + 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 + + WizardryUtilities.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY); } GlStateManager.popMatrix(); // Blend needs to be left enabled here because otherwise the hotbar becomes opaque } + + /** + * Draws the text layer of this HUD skin at the given position with the given orientations, with the given + * spell name strings. + * + * @param x The x-coordinate of the corner of the spell HUD. The bottom left corner of the actual texture + * will always be at this position, so for example if flipX is false and flipY is true, this will be the corner + * of the HUD that is closest to the top left corner of the screen. + * @param y The y-coordinate of the corner of the spell HUD; see above. + * @param flipX Whether to flip the HUD horizontally. + * @param flipY Whether to flip the HUD vertically. + * @param prevSpellName The name of the previous spell. This string will be drawn directly using the + * {@link MixedFontRenderer}; as such it should be supplied with formatting codes and # characters already + * appended. + * @param spellName The name of the currently selected spell; see above. + * @param nextSpellName The name of the next spell; see above. + * @param animationProgress The progress of the spell switching animation, as a fraction between 0 and 1 + * (inclusive). Positive values indicate switching forwards, negative values indicate switching backwards, and + * a value of zero indicates that the spell is not currently being switched. + */ + public void drawText(int x, int y, boolean flipX, boolean flipY, String prevSpellName, String spellName, String nextSpellName, float animationProgress){ + + // Moves the origin if the HUD does not mirror; neatens the rest of the code. + if(flipX && !mirrorX) x -= width; + if(flipY && !mirrorY) y += height; + + FontRenderer font = ClientProxy.mixedFontRenderer; // On this occasion we're client-side so this is OK + + // Position of the selected spell name in normal display, also used for interpolation when animating + int x1 = flipX && mirrorX ? x - width : x + textInsetX; + // The text is an odd number of pixels high so we need to subtract an extra 1 when not flipped + int y1 = flipY && mirrorY ? y + textInsetY - font.FONT_HEIGHT/2 + 2 : y - textInsetY - font.FONT_HEIGHT/2 - 1; + + int maxWidth = width - textInsetX; // Maximum width of the text + + if(animationProgress == 0){ // Normal display + + float xPrev = flipX && mirrorX ? x - width : x + textInsetX - (flipY ? -1 : 1) * cascadeOffsetX; + float xNext = flipX && mirrorX ? x - width : x + textInsetX + (flipY ? -1 : 1) * cascadeOffsetX; + // Don't ask me why adding 1 to this makes it look more even, it just does! + float yPrev = y1 - (cascadeOffsetY + 1); // No need to account for flipY because previous is always above. + float yNext = y1 + cascadeOffsetY; // No need to account for flipY because next is always below. + float maxWidthPrev = maxWidth + (flipY ? -1 : 1) * cascadeOffsetX; + float maxWidthNext = maxWidth - (flipY ? -1 : 1) * cascadeOffsetX; + int nextPrevClr = makeTranslucent(0xffffff, SPELL_NAME_OPACITY); + + drawScaledStringToWidth(font, prevSpellName, xPrev, yPrev, SPELL_NAME_SCALE, nextPrevClr, maxWidthPrev, true, flipX && mirrorX); + drawScaledStringToWidth(font, spellName, x1, y1, 1, 0xffffffff, maxWidth, true, flipX && mirrorX); + drawScaledStringToWidth(font, nextSpellName, xNext, yNext, SPELL_NAME_SCALE, nextPrevClr, maxWidthNext, true, flipX && mirrorX); + + }else{ // Switching spells + + boolean reverse = animationProgress < 0; + if(reverse) animationProgress = 1 - Math.abs(animationProgress); // Simplest way of reversing the animation + + float xPrev = flipX && mirrorX ? x - width : x + textInsetX - (flipY ? -1 : 1) * cascadeOffsetX * animationProgress; + float xNext = flipX && mirrorX ? x - width : x + textInsetX + (flipY ? -1 : 1) * cascadeOffsetX * (1 - animationProgress); + float yPrev = y1 - (cascadeOffsetY + 1) * animationProgress; // No need to account for flipY because previous is always above. + float yNext = y1 + cascadeOffsetY * (1 - animationProgress); // No need to account for flipY because next is always below. + float maxWidthPrev = maxWidth + (flipY ? -1 : 1) * cascadeOffsetX * animationProgress; + float maxWidthNext = maxWidth - (flipY ? -1 : 1) * cascadeOffsetX * (1 - animationProgress); + float scalePrev = SPELL_NAME_SCALE + (1 - SPELL_NAME_SCALE) * (1 - animationProgress); + float scaleNext = SPELL_NAME_SCALE + (1 - SPELL_NAME_SCALE) * (animationProgress); + int clrPrev = makeTranslucent(0xffffff, SPELL_NAME_OPACITY + (1 - SPELL_NAME_OPACITY) * (1 - animationProgress)); + int clrNext = makeTranslucent(0xffffff, SPELL_NAME_OPACITY + (1 - SPELL_NAME_OPACITY) * animationProgress); + + if(reverse){ // Switching to previous spell + + // Only renders the next spell and the current one + drawScaledStringToWidth(font, spellName, xPrev, yPrev, scalePrev, clrPrev, maxWidthPrev, true, flipX && mirrorX); + drawScaledStringToWidth(font, nextSpellName, xNext, yNext, scaleNext, clrNext, maxWidthNext, true, flipX && mirrorX); + + }else{ // Switching to next spell + + // Only renders the previous spell and the current one + drawScaledStringToWidth(font, prevSpellName, xPrev, yPrev, scalePrev, clrPrev, maxWidthPrev, true, flipX && mirrorX); + drawScaledStringToWidth(font, spellName, xNext, yNext, scaleNext, clrNext, maxWidthNext, true, flipX && mirrorX); + + } + } + } + } } diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index f9a2b90f..5b50eb61 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -37,6 +37,7 @@ public final class WizardrySounds { public static final SoundEvent SPELL_LOOP_WIND = createSound("wind"); public static final SoundEvent SPELL_EARTHQUAKE = createSound("rumble"); public static final SoundEvent SPELL_FORCE = createSound("force"); + public static final SoundEvent SELECT_SPELL = createSound("select"); /** Trick borrowed from the Twilight Forest, makes things neater. */ public static SoundEvent createSound(String name){ @@ -65,5 +66,6 @@ public final class WizardrySounds { event.getRegistry().register(SPELL_LOOP_WIND); event.getRegistry().register(SPELL_EARTHQUAKE); event.getRegistry().register(SPELL_FORCE); + event.getRegistry().register(SELECT_SPELL); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/util/WandHelper.java index a98788fb..f4a01a04 100644 --- a/src/main/java/electroblob/wizardry/util/WandHelper.java +++ b/src/main/java/electroblob/wizardry/util/WandHelper.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Set; +import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.spell.Spell; @@ -116,50 +117,82 @@ public final class WandHelper { return Spells.none; } + + /** Returns the spell after the currently selected spell for the given wand, or the 'none' spell if the wand has no + * spell data. */ + public static Spell getNextSpell(ItemStack wand){ + + Spell[] spells = getSpells(wand); + + if(wand.getTagCompound() != null){ + return spells[getNextSpellIndex(wand)]; + } + + return Spells.none; + } + + /** Returns the spell before the currently selected spell for the given wand, or the 'none' spell if the wand has no + * spell data. */ + public static Spell getPreviousSpell(ItemStack wand){ + + Spell[] spells = getSpells(wand); + + if(wand.getTagCompound() != null){ + return spells[getPreviousSpellIndex(wand)]; + } + + return Spells.none; + } /** Selects the next spell in this wand's list of spells. */ public static void selectNextSpell(ItemStack wand){ // 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades - if(getSpells(wand).length < 0) setSpells(wand, new Spell[5]); + if(getSpells(wand).length < 0) setSpells(wand, new Spell[ItemWand.BASE_SPELL_SLOTS]); if(wand.getTagCompound() != null){ - - int numberOfSpells = getSpells(wand).length; - int selectedSpell = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); - - // Greater than or equal to so that if attunement upgrades are somehow removed by NBT modification it just - // resets. - if(selectedSpell >= numberOfSpells - 1){ - selectedSpell = 0; - }else{ - selectedSpell++; - } - - wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, selectedSpell); - + wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, getNextSpellIndex(wand)); } } /** Selects the previous spell in this wand's list of spells. */ public static void selectPreviousSpell(ItemStack wand){ - // 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades - if(getSpells(wand).length < 0) setSpells(wand, new Spell[5]); - // This cannot possibly be null here, and yet I am getting an NPE... + if(getSpells(wand).length < 0) setSpells(wand, new Spell[ItemWand.BASE_SPELL_SLOTS]); + if(wand.getTagCompound() != null){ - - int numberOfSpells = getSpells(wand).length; - int selectedSpell = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); - - if(selectedSpell <= 0){ - selectedSpell = numberOfSpells - 1; - }else{ - selectedSpell--; - } - - wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, selectedSpell); + wand.getTagCompound().setInteger(SELECTED_SPELL_KEY, getPreviousSpellIndex(wand)); } } + + private static int getNextSpellIndex(ItemStack wand){ + + int numberOfSpells = getSpells(wand).length; + int spellIndex = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); + + // Greater than or equal to so that if attunement upgrades are somehow removed by NBT modification it just + // resets. + if(spellIndex >= numberOfSpells - 1){ + spellIndex = 0; + }else{ + spellIndex++; + } + + return spellIndex; + } + + private static int getPreviousSpellIndex(ItemStack wand){ + + int numberOfSpells = getSpells(wand).length; + int spellIndex = wand.getTagCompound().getInteger(SELECTED_SPELL_KEY); + + if(spellIndex <= 0){ + spellIndex = numberOfSpells - 1; + }else{ + spellIndex--; + } + + return spellIndex; + } /** * Returns an array of the cooldowns for each spell bound to the given wand. As of Wizardry 1.1, this array is not @@ -210,6 +243,28 @@ public final class WandHelper { // Don't need to check if the tag compound is null since the above check is equivalent. return cooldowns[wand.getTagCompound().getInteger(SELECTED_SPELL_KEY)]; } + + /** Returns the given wand's cooldown for the spell after the currently selected spell, or 0 if the wand has no + * cooldown data. */ + public static int getNextCooldown(ItemStack wand){ + + int[] cooldowns = getCooldowns(wand); + + if(cooldowns.length == 0) return 0; + // Don't need to check if the tag compound is null since the above check is equivalent. + return cooldowns[getNextSpellIndex(wand)]; + } + + /** Returns the given wand's cooldown for the spell before the currently selected spell, or 0 if the wand has no + * cooldown data. */ + public static int getPreviousCooldown(ItemStack wand){ + + int[] cooldowns = getCooldowns(wand); + + if(cooldowns.length == 0) return 0; + // Don't need to check if the tag compound is null since the above check is equivalent. + return cooldowns[getPreviousSpellIndex(wand)]; + } /** Sets the given wand's cooldown for the currently selected spell. */ public static void setCurrentCooldown(ItemStack wand, int cooldown){ diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 17165266..79eb401e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -710,6 +710,7 @@ config.ebwizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist config.ebwizardry.spell_hud_position=Spell HUD Position +config.ebwizardry.spell_hud_skin=Spell HUD Skin config.ebwizardry.cast_command_name=Cast Spell Command Name config.ebwizardry.discoverspell_command_name=Discover Spell Command Name config.ebwizardry.ally_command_name=Set Ally Command Name @@ -746,6 +747,7 @@ config.ebwizardry.cast_command_multiplier_limit.tooltip=Upper limit for the mult config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry:wizard). config.ebwizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry:wizard). config.ebwizardry.spell_hud_position.tooltip=The position of the spell HUD. +config.ebwizardry.spell_hud_skin.tooltip=Change the look of the spell HUD... config.ebwizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. config.ebwizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. config.ebwizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index fe97b0ae..449313b5 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -710,6 +710,7 @@ config.ebwizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit config.ebwizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist config.ebwizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist config.ebwizardry.spell_hud_position=Spell HUD Position +config.ebwizardry.spell_hud_skin=Spell HUD Skin config.ebwizardry.cast_command_name=Cast Spell Command Name config.ebwizardry.discoverspell_command_name=Discover Spell Command Name config.ebwizardry.ally_command_name=Set Ally Command Name @@ -746,6 +747,7 @@ config.ebwizardry.cast_command_multiplier_limit.tooltip=Upper limit for the mult config.ebwizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry:wizard). config.ebwizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. ebwizardry:wizard). config.ebwizardry.spell_hud_position.tooltip=The position of the spell HUD. +config.ebwizardry.spell_hud_skin.tooltip=Change the look of the spell HUD... config.ebwizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. config.ebwizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. config.ebwizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 649770ba..42258e4b 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -35,5 +35,7 @@ "sparkle": {"category": "player","sounds": [{"name": "ebwizardry:sparkle","stream": false}]}, "wind": {"category": "player","sounds": [{"name": "ebwizardry:wind","stream": false}]}, - "rumble": {"category": "player","sounds": [{"name": "ebwizardry:rumble","stream": false}]} + "rumble": {"category": "player","sounds": [{"name": "ebwizardry:rumble","stream": false}]}, + + "select": {"category": "player","sounds": [{"name": "ebwizardry:select","stream": false}]} } \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/sounds/select.ogg b/src/main/resources/assets/ebwizardry/sounds/select.ogg new file mode 100644 index 00000000..cf9450ce Binary files /dev/null and b/src/main/resources/assets/ebwizardry/sounds/select.ogg differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud.png deleted file mode 100644 index 8a6cc2ee..00000000 Binary files a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud.png and /dev/null differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/_index.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/_index.json new file mode 100644 index 00000000..b9cc2d74 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/_index.json @@ -0,0 +1,50 @@ +{ + "default": { + "texture": "ebwizardry:gui/spell_hud/default", + "metadata": "ebwizardry:gui/spell_hud/default" + }, + "classic": { + "texture": "ebwizardry:gui/spell_hud/classic", + "metadata": "ebwizardry:gui/spell_hud/classic" + }, + "vanilla_style": { + "texture": "ebwizardry:gui/spell_hud/vanilla_style", + "metadata": "ebwizardry:gui/spell_hud/vanilla_style" + }, + "redwood": { + "texture": "ebwizardry:gui/spell_hud/redwood", + "metadata": "ebwizardry:gui/spell_hud/redwood" + }, + "silverwood": { + "texture": "ebwizardry:gui/spell_hud/silverwood", + "metadata": "ebwizardry:gui/spell_hud/silverwood" + }, + "stone": { + "texture": "ebwizardry:gui/spell_hud/stone", + "metadata": "ebwizardry:gui/spell_hud/stone" + }, + "sandstone": { + "texture": "ebwizardry:gui/spell_hud/sandstone", + "metadata": "ebwizardry:gui/spell_hud/sandstone" + }, + "jungle": { + "texture": "ebwizardry:gui/spell_hud/jungle", + "metadata": "ebwizardry:gui/spell_hud/jungle" + }, + "spell_book": { + "texture": "ebwizardry:gui/spell_hud/spell_book", + "metadata": "ebwizardry:gui/spell_hud/spell_book" + }, + "minimal": { + "texture": "ebwizardry:gui/spell_hud/minimal", + "metadata": "ebwizardry:gui/spell_hud/minimal" + }, + "no_icon": { + "texture": "ebwizardry:gui/spell_hud/no_icon", + "metadata": "ebwizardry:gui/spell_hud/no_icon" + }, + "skyrim_style": { + "texture": "ebwizardry:gui/spell_hud/skyrim_style", + "metadata": "ebwizardry:gui/spell_hud/skyrim_style" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.json new file mode 100644 index 00000000..4e198adc --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.json @@ -0,0 +1,33 @@ +{ + "name": "Classic", + "description": "The original spell HUD, for that nostalgic feel!", + "width": 128, + "height": 36, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 17 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 42, + "y": 0, + "length": 82, + "height": 6, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.png new file mode 100644 index 00000000..dd656158 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/classic.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.json new file mode 100644 index 00000000..b4173a15 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.json @@ -0,0 +1,33 @@ +{ + "name": "Default", + "description": "The default, oak wood look of the spell HUD.", + "width": 128, + "height": 50, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 2, + "y": 8 + }, + "cooldown_bar": { + "x": 42, + "y": 2, + "length": 79, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.png new file mode 100644 index 00000000..ce310941 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/default.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.json new file mode 100644 index 00000000..e5273e4b --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.json @@ -0,0 +1,33 @@ +{ + "name": "Jungle", + "description": "For the most intrepid explorers!", + "width": 128, + "height": 37, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 41, + "y": 2, + "length": 82, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.png new file mode 100644 index 00000000..0015cb10 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/jungle.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.json new file mode 100644 index 00000000..f63b60f3 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.json @@ -0,0 +1,33 @@ +{ + "name": "Minimal", + "description": "A modern, sleek, minimalist look.", + "width": 128, + "height": 36, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 39, + "y": 16 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 36, + "y": 1, + "length": 86, + "height": 2, + "mirror": { + "x": true, + "y": true + }, + "show_when_full": false + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.png new file mode 100644 index 00000000..ba38076d Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/minimal.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.json new file mode 100644 index 00000000..cbee2a6f --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.json @@ -0,0 +1,33 @@ +{ + "name": "No Icon", + "description": "A variation on the default skin that doesn't show the spell icon.", + "width": 96, + "height": 37, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": -999, + "y": -999 + }, + "text_inset": { + "x": 10, + "y": 18 + }, + "spell_cascade_offset": { + "x": 2, + "y": 8 + }, + "cooldown_bar": { + "x": 10, + "y": 2, + "length": 79, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.png new file mode 100644 index 00000000..e8e8615b Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/no_icon.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.json new file mode 100644 index 00000000..634b5f72 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.json @@ -0,0 +1,33 @@ +{ + "name": "Redwood", + "description": "A variation on the default skin with red-coloured wood and a purple-pink cooldown bar.", + "width": 128, + "height": 50, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 2, + "y": 8 + }, + "cooldown_bar": { + "x": 42, + "y": 2, + "length": 79, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.png new file mode 100644 index 00000000..ae931624 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/redwood.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.json new file mode 100644 index 00000000..513d7068 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.json @@ -0,0 +1,33 @@ +{ + "name": "Sandstone", + "description": "Explore ancient ruins with this desert-themed skin!", + "width": 128, + "height": 37, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 41, + "y": 2, + "length": 82, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.png new file mode 100644 index 00000000..e4b23c41 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/sandstone.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.json new file mode 100644 index 00000000..83c64dbf --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.json @@ -0,0 +1,33 @@ +{ + "name": "Silverwood", + "description": "A variation on the default skin with silver-coloured wood and a blue cooldown bar.", + "width": 128, + "height": 50, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 2, + "y": 8 + }, + "cooldown_bar": { + "x": 42, + "y": 2, + "length": 79, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.png new file mode 100644 index 00000000..e097a57e Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/silverwood.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.json new file mode 100644 index 00000000..2ffc29c6 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.json @@ -0,0 +1,33 @@ +{ + "name": "Skyrim-style", + "description": "A HUD skin in the styling of The Elder Scrolls V, for that Skyrim feel!", + "width": 128, + "height": 38, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 3, + "y": 3 + }, + "text_inset": { + "x": 42, + "y": 19 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 44, + "y": 2, + "length": 79, + "height": 3, + "mirror": { + "x": true, + "y": false + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.png new file mode 100644 index 00000000..846f69cf Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/skyrim_style.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.json new file mode 100644 index 00000000..eef32610 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.json @@ -0,0 +1,33 @@ +{ + "name": "Spell book", + "description": "Knowledge is power, or so they say.", + "width": 128, + "height": 38, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 41, + "y": 20 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 41, + "y": 3, + "length": 79, + "height": 3, + "mirror": { + "x": true, + "y": true + }, + "show_when_full": false + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.png new file mode 100644 index 00000000..50feb347 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/spell_book.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.json new file mode 100644 index 00000000..e5662623 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.json @@ -0,0 +1,33 @@ +{ + "name": "Stone", + "description": "A HUD skin hewn from solid rock, with a red cooldown bar.", + "width": 128, + "height": 37, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 42, + "y": 18 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 41, + "y": 2, + "length": 82, + "height": 3, + "mirror": { + "x": false, + "y": true + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.png new file mode 100644 index 00000000..e09ba13e Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/stone.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.json b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.json new file mode 100644 index 00000000..6c541a38 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.json @@ -0,0 +1,33 @@ +{ + "name": "Vanilla-style", + "description": "A vanilla Minecraft-styled skin, for the purists out there.", + "width": 128, + "height": 36, + "mirror": { + "x": true, + "y": true + }, + "spell_icon_inset": { + "x": 2, + "y": 2 + }, + "text_inset": { + "x": 40, + "y": 18 + }, + "spell_cascade_offset": { + "x": 0, + "y": 8 + }, + "cooldown_bar": { + "x": 40, + "y": 0, + "length": 81, + "height": 5, + "mirror": { + "x": false, + "y": false + }, + "show_when_full": true + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.png b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.png new file mode 100644 index 00000000..3e6e037b Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/gui/spell_hud/vanilla_style.png differ