Iron's Spells 'n Spellbooks

Iron's Spells 'n Spellbooks

18M Downloads

Active Spell Overlay Customization Request

Willem07 opened this issue · 4 comments

commented

Feature description

Please add my changes to the Active Spell in the user interface so it can be moved around the screen.
Feel free to edit the icon's position according to your preferences; it is centered by default.

package io.redspace.ironsspellbooks.gui.overlays;

import com.mojang.blaze3d.systems.RenderSystem;
import io.redspace.ironsspellbooks.IronsSpellbooks;
import io.redspace.ironsspellbooks.api.registry.SpellRegistry;
import io.redspace.ironsspellbooks.api.spells.AbstractSpell;
import io.redspace.ironsspellbooks.api.spells.ISpellContainer;
import io.redspace.ironsspellbooks.config.ClientConfigs;
import io.redspace.ironsspellbooks.item.CastingItem;
import io.redspace.ironsspellbooks.item.Scroll;
import io.redspace.ironsspellbooks.player.ClientMagicData;
import io.redspace.ironsspellbooks.registries.ItemRegistry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.gui.overlay.ForgeGui;
import net.minecraftforge.client.gui.overlay.IGuiOverlay;

public class ActiveSpellOverlay implements IGuiOverlay {
    public static ActiveSpellOverlay instance = new ActiveSpellOverlay();

    protected static final ResourceLocation WIDGETS_LOCATION = new ResourceLocation("textures/gui/widgets.png");
    public final static ResourceLocation TEXTURE = new ResourceLocation(IronsSpellbooks.MODID, "textures/gui/icons.png");

    public enum Anchor {
        Center, TopLeft, TopRight, BottomLeft, BottomRight
    }

    public void render(ForgeGui gui, GuiGraphics guiHelper, float partialTick, int screenWidth, int screenHeight) {
        Player player = Minecraft.getInstance().player;
        if (player == null) return;

        ItemStack stack = player.getMainHandItem();
        AbstractSpell spell;

        if (hasRightClickCasting(stack.getItem())) {
            if (ISpellContainer.isSpellContainer(stack)) {
                spell = ISpellContainer.get(stack).getSpellAtIndex(0).getSpell();
            } else {
                spell = ClientMagicData.getSpellSelectionManager().getSelectedSpellData().getSpell();
            }
        } else {
            stack = player.getOffhandItem();
            if (hasRightClickCasting(stack.getItem())) {
                if (ISpellContainer.isSpellContainer(stack)) {
                    spell = ISpellContainer.get(stack).getSpellAtIndex(0).getSpell();
                } else {
                    spell = ClientMagicData.getSpellSelectionManager().getSelectedSpellData().getSpell();
                }
            } else {
                return;
            }
        }

        if (stack.isEmpty() || spell == SpellRegistry.none()) {
            return;
        }

        // Get position settings from ClientConfigs
        int configOffsetX = ClientConfigs.ACTIVE_SPELL_OVERLAY_X_OFFSET.get();
        int configOffsetY = ClientConfigs.ACTIVE_SPELL_OVERLAY_Y_OFFSET.get();
        Anchor anchor = ClientConfigs.ACTIVE_SPELL_OVERLAY_ANCHOR.get();

        // Determine the position based on the binding
        int positionX = getPositionX(anchor, screenWidth) + configOffsetX;
        int positionY = getPositionY(anchor, screenHeight) + configOffsetY;

        guiHelper.blit(WIDGETS_LOCATION, positionX, positionY, 24, 22, 29, 24);
        guiHelper.blit(spell.getSpellIconResource(), positionX + 3, positionY + 4, 0, 0, 16, 16, 16, 16);

        float cooldownPercent = ClientMagicData.getCooldownPercent(spell);
        if (cooldownPercent > 0 && !stack.getItem().equals(ItemRegistry.SCROLL.get())) {
            int pixels = (int) (16 * cooldownPercent + 1f);
            guiHelper.blit(TEXTURE, positionX + 3, positionY + 20 - pixels, 47, 87, 16, pixels);
        }
    }

    private static boolean hasRightClickCasting(Item item) {
        return item instanceof Scroll || item instanceof CastingItem;
    }

    private static int getPositionX(Anchor anchor, int screenWidth) {
        switch (anchor) {
            case TopLeft: return 20; // You can customize it as you wish
            case TopRight: return screenWidth - 20 - 29; // Adjust the width and height of the icon
            case BottomLeft: return 20; // Customize it as you like
            case BottomRight: return screenWidth - 20 - 29; // Adjust the width and height of the icon
            case Center:
            default: return screenWidth / 2 - 14; // For the center, you can set the offset
        }
    }

    private static int getPositionY(Anchor anchor, int screenHeight) {
        switch (anchor) {
            case TopLeft:
            case TopRight: return 20; // Height for the top corners
            case BottomLeft:
            case BottomRight: return screenHeight - 20 - 24; // Adjust the height of the icon
            case Center:
            default: return screenHeight / 2 - 12; // For the center
        }
    }
}
package io.redspace.ironsspellbooks.config;

import io.redspace.ironsspellbooks.gui.overlays.ManaBarOverlay;
import io.redspace.ironsspellbooks.gui.overlays.RecastOverlay;
import io.redspace.ironsspellbooks.gui.overlays.SpellBarOverlay;
import io.redspace.ironsspellbooks.gui.overlays.ActiveSpellOverlay; // Added for the new class
import io.redspace.ironsspellbooks.gui.overlays.ManaBarOverlay.Anchor;
import io.redspace.ironsspellbooks.gui.overlays.ManaBarOverlay.Display;
import net.minecraftforge.common.ForgeConfigSpec;

public class ClientConfigs {
    public static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
    public static final ForgeConfigSpec.ConfigValue<Boolean> SHOW_FIRST_PERSON_ARMS;
    public static final ForgeConfigSpec.ConfigValue<Boolean> SHOW_FIRST_PERSON_ITEMS;
    public static final ForgeConfigSpec.ConfigValue<Boolean> REPLACE_GHAST_FIREBALL;
    public static final ForgeConfigSpec.ConfigValue<Boolean> REPLACE_BLAZE_FIREBALL;
    public static final ForgeConfigSpec.ConfigValue<Integer> MANA_BAR_Y_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> MANA_BAR_X_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> MANA_TEXT_X_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> MANA_TEXT_Y_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Boolean> MANA_BAR_TEXT_VISIBLE;
    public static final ForgeConfigSpec.ConfigValue<Boolean> ENABLE_BOSS_MUSIC;
    public static final ForgeConfigSpec.ConfigValue<ManaBarOverlay.Anchor> MANA_BAR_ANCHOR;
    public static final ForgeConfigSpec.ConfigValue<ManaBarOverlay.Display> MANA_BAR_DISPLAY;
    public static final ForgeConfigSpec.ConfigValue<ManaBarOverlay.Display> SPELL_BAR_DISPLAY;
    public static final ForgeConfigSpec.ConfigValue<Integer> SPELL_BAR_Y_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> SPELL_BAR_X_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<SpellBarOverlay.Anchor> SPELL_BAR_ANCHOR;
    public static final ForgeConfigSpec.ConfigValue<RecastOverlay.Anchor> RECAST_ANCHOR;
    public static final ForgeConfigSpec.ConfigValue<Integer> RECAST_Y_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> RECAST_X_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<ActiveSpellOverlay.Anchor> ACTIVE_SPELL_OVERLAY_ANCHOR;

    // New parameters for ActiveSpellOverlay
    public static final ForgeConfigSpec.ConfigValue<Integer> ACTIVE_SPELL_OVERLAY_X_OFFSET;
    public static final ForgeConfigSpec.ConfigValue<Integer> ACTIVE_SPELL_OVERLAY_Y_OFFSET;

    public static final ForgeConfigSpec SPEC;

    public ClientConfigs() {
    }

    static {
        BUILDER.comment("##############################################################################################");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##   ATTENTION: These are client configs. For gameplay settings, go to the SERVER CONFIGS   ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##                                                                                          ##");
        BUILDER.comment("##############################################################################################");
        BUILDER.comment("");

        // Configuration for ManaBar
        BUILDER.push("UI");
        BUILDER.push("ManaBar");
        BUILDER.comment("By default (Contextual), the mana bar only appears when you are holding a magic item or are not at max mana.");
        MANA_BAR_DISPLAY = BUILDER.defineEnum("manaBarDisplay", Display.Contextual);
        BUILDER.comment("Used to adjust mana bar's position (11 is one full hunger bar up).");
        MANA_BAR_X_OFFSET = BUILDER.define("manaBarXOffset", 0);
        MANA_BAR_Y_OFFSET = BUILDER.define("manaBarYOffset", 0);
        MANA_BAR_TEXT_VISIBLE = BUILDER.define("manaBarTextVisible", true);
        MANA_BAR_ANCHOR = BUILDER.defineEnum("manaBarAnchor", Anchor.Hunger);
        MANA_TEXT_X_OFFSET = BUILDER.define("manaTextXOffset", 0);
        MANA_TEXT_Y_OFFSET = BUILDER.define("manaTextYOffset", 0);
        BUILDER.pop();

        // Configuration for SpellBar
        BUILDER.push("SpellBar");
        BUILDER.comment("By default (Always), the spell bar always shows the spells in your equipped spellbook. Contextual will hide them when not in use.");
        SPELL_BAR_DISPLAY = BUILDER.defineEnum("spellBarDisplay", Display.Always);
        BUILDER.comment("Used to adjust spell bar's position.");
        SPELL_BAR_X_OFFSET = BUILDER.define("spellBarXOffset", 0);
        SPELL_BAR_Y_OFFSET = BUILDER.define("spellBarYOffset", 0);
        SPELL_BAR_ANCHOR = BUILDER.defineEnum("spellBarAnchor", SpellBarOverlay.Anchor.Hotbar);
        BUILDER.pop();

        // Configuration for RecastOverlay
        BUILDER.push("RecastOverlay");
        RECAST_ANCHOR = BUILDER.defineEnum("recastAnchor", RecastOverlay.Anchor.TopCenter);
        RECAST_X_OFFSET = BUILDER.define("recastXOffset", 0);
        RECAST_Y_OFFSET = BUILDER.define("recastYOffset", 0);
        BUILDER.pop();

        // Configuration for ActiveSpellOverlay
        BUILDER.push("ActiveSpellOverlay");
        BUILDER.comment("Adjusts the position of the Active Spell Overlay on the screen.");
        ACTIVE_SPELL_OVERLAY_X_OFFSET = BUILDER.define("activeSpellOverlayXOffset", 0);
        ACTIVE_SPELL_OVERLAY_Y_OFFSET = BUILDER.define("activeSpellOverlayYOffset", 0);
        ACTIVE_SPELL_OVERLAY_ANCHOR = BUILDER.defineEnum("activeSpellOverlayAnchor", ActiveSpellOverlay.Anchor.Center);
        BUILDER.pop();

        // Configuration for Animations
        BUILDER.push("Animations");
        BUILDER.comment("What to render in first person while casting.");
        SHOW_FIRST_PERSON_ARMS = BUILDER.define("showFirstPersonArms", true);
        SHOW_FIRST_PERSON_ITEMS = BUILDER.define("showFirstPersonItems", true);
        BUILDER.pop();

        // Configuration for Renderers
        BUILDER.push("Renderers");
        BUILDER.comment("By default, both fireballs are replaced with an enhanced model used by fire spells.");
        REPLACE_GHAST_FIREBALL = BUILDER.define("replaceGhastFireballs", true);
        REPLACE_BLAZE_FIREBALL = BUILDER.define("replaceBlazeFireballs", true);
        BUILDER.pop();

        // Configuration for Music
        BUILDER.push("Music");
        ENABLE_BOSS_MUSIC = BUILDER.define("enableBossMusic", true);
        BUILDER.pop();

        SPEC = BUILDER.build();
    }
}

How it improves the player experience

Some interfaces from other mods overlap with the active spell, so I decided to move it to a different location.

commented

Your request makes sense it is just not at the top of our list to look at right now. If you wanted to get it into the mod in the short term a pull request with the changes would be the way to go.

commented

I have a few thoughts on this.

  • What version of the mod are you referring to?
  • If we add configurability to this it should follow the same mechanics as our other overlays (see SpellBarOverlay for how it does anchoring + offsets.)
  • Any suggested code changes of this scale should come in the form of a pull request.
  • The default configuration for a change like this should be to mirror the current behavior
commented

Mod Version: irons_spellbooks-1.20.1-3.4.0.2

I’m not a programming expert, and this was my first time coding in Java.

I made some changes to my modpack to prevent conflicts with interfaces from other mods, and I wanted to share a small addition for customizing the config, which requests a feature for interface customization.

I was surprised to find that any interface from your mod could be moved, except for the active spell.

If it’s convenient for you, feel free to take a small part of the code and develop it further in the future to integrate it into the mod itself.

This is not mandatory; it's just a request.
2024-10-05_22 38 42

commented

closing because feature requests are now exclusively being handled on discord https://discord.gg/TRzEdrndM2