promotional bannermobile promotional banner

Runic Skills

Runic Skills is a Minecraft 1.20.1 Forge RPG progression mod that adds ten levelable skills, auto-scaling passive bonuses, and hundreds of toggleable perks so players can shape a real build through normal gameplay. Designed for the Runecraft modpack.

File Details

Runic Skills 1.0.0 (Forge 1.20.1)

  • R
  • Apr 24, 2026
  • 1.15 MB
  • 1.1K
  • 1.20.1
  • Forge

File Name

runicskills-1.0.0.jar

Supported Versions

  • 1.20.1

Curse Maven Snippet

Forge

implementation fg.deobf("curse.maven:runic-skills-1516283:7976244")
Curse Maven does not yet support mods that have disabled 3rd party sharing

Learn more about Curse Maven

[1.0.0] - 2026-04-24

First stable release. Consolidates all 0.9.x work (item-lock master toggle, perk/passive kill switches, perk-swap cooldown, perk-group datapacks, Legendary Tabs hardening, tooltip/config fixes) under a 1.0 milestone. No further pre-1.0 versions will be cut.

Added

  • maxActivePerks config (HandlerCommonConfig, group general, default 0 = unlimited, range 0–256). Caps the number of perks a player can have enabled at once. Enforced server-side in TogglePerkSP on the rank 0 → ≥1 transition (rank-ups on already-active perks bypass the cap, matching existing school-attunement semantics). Iron's Spells school attunements count against this cap in addition to ironsMaxSchoolSelections. Mirrored through CommonConfigSyncCP.
  • disabledPerks / disabledPassives kill-switch lists (HandlerCommonConfig, @ListGroup of String). Disabled entries cannot be enabled / leveled-up and their effects are suppressed; rank and level data are preserved in NBT so re-enabling restores state. Perks: Perk.isEnabled() returns false for disabled perks, so every event handler short-circuits automatically. Passives: RegistryAttributes.modifierAttributes passes enabled=false to amplifyAttribute, which removes the modifier — single choke point means all attribute effects drop to zero. /skillsreload now re-runs modifierAttributes for every connected player so passive-disable changes take effect without relog. Registry path ("berserker") and full-id ("runicskills:berserker") forms are both accepted. Mirrored through CommonConfigSyncCP.
  • perkSwapCooldownTicks config (HandlerCommonConfig, default 0 = no cooldown, range 0–72000). Per-player cooldown between perk enables. Piggybacks on the existing perkCooldowns map via new SkillCapability.COOLDOWN_PERK_SWAP constant, which already ticks down every server tick via TickEventHandler. Applies only on rank-0 → rank-≥1 transitions; rank-ups and disables bypass. Persists in save NBT so logging out during cooldown preserves remaining time.
  • skillLevelUpCostMultiplier config (HandlerCommonConfig, default 1.0, range 0.1–10.0). Scales the vanilla XP cost of leveling a skill. Applied in both SkillLevelUpSP.requiredPoints (XP-points cost) and requiredExperienceLevels (level-gate) so high-level players can't bypass an increased cost. Mirrored through CommonConfigSyncCP so the GUI cost display stays synced with the server.
  • Data-driven perk groups — new datapack loader at data/<namespace>/perk_groups/*.json. Schema: { "max_active": int, "perks": [string], "message": string? }. New classes: PerkGroup (record), PerkGroupManager (static volatile map, firstBlockingGroup(capability, perkName) helper), PerkGroupsReloadListener (extends Forge SimpleJsonResourceReloadListener, subscribed via AddReloadListenerEvent), PerkGroupsSyncCP (new PLAY_TO_CLIENT packet, sent on login and /skillsreload). Enforced in TogglePerkSP alongside the existing hardcoded school-attunement check — both systems run independently. No default groups are shipped; opt-in for pack makers. Lenient JSON parsing tolerates trailing commas / comments. Per-file parse errors are logged and skipped without blocking the rest of the load.

Changed

  • Bumped network channel PROTOCOL_VERSION from 3 to 4. Wire format of CommonConfigSyncCP has grown (six new fields: maxActivePerks, disabledPerks, disabledPassives, perkSwapCooldownTicks, skillLevelUpCostMultiplier; plus the new PerkGroupsSyncCP packet). Old clients connected to 0.9.9 servers (and vice versa) will get a clean connection refusal instead of a silent state-corruption bug. Please ensure clients and servers update together.
  • /skillsreload now also broadcasts PerkGroupsSyncCP to every connected player so datapack perk-group changes propagate without relog.
  • Perk.isEnabled(Player) and Perk.isEnabled() now consult RegistryPerks.isDisabled(perk) before returning true, covering every perk-effect event handler in a single choke point.
  • RegistryAttributes.modifierAttributes now checks RegistryPassives.isDisabled(passive) per-passive and removes rather than adds the attribute modifier for disabled entries.

Fixed

  • Item-lock requirement tooltips ignored the enableItemLocks master toggle. RegistryClientEvents.onTooltipDisplay appended the "Requirements:" block whenever HandlerSkill.getValue(...) returned a non-null list, without consulting the config that gates server-side enforcement in SkillCapability.canUse(...). Players who disabled item locks could freely use tools like the trident but still saw "Requires Level X" text, leading to widespread confusion that the lock was still active. Fixed by checking HandlerCommonConfig.HANDLER.instance().enableItemLocks before rendering — when locks are off, the requirement block is hidden so the UI matches enforcement. The synced value from CommonConfigSyncCP is authoritative on joined clients; pre-join/main-menu tooltips fall back to the local config value, matching the existing behavior of ClientCapabilityAccess.canUseItemClient.
  • Mod failed to load when Legendary Tabs was not installed. RunicSkillsClient$ClientProxy is a @Mod.EventBusSubscriber class, so Forge's AutomaticEventSubscriber.inject loads it at mod construction via Class.forName(..., true, loader). The clientSetup method contained an inline lambda () -> TabsMenu.register(new LegendaryTabRunicSkills()), which the Java compiler desugared into a synthetic lambda$clientSetup$3 method on ClientProxy itself. The JVM verifier walks that method body at class-load time, finds the INVOKESTATIC sfiomn/.../TabsMenu.register(TabBase) + NEW com/otectus/.../LegendaryTabRunicSkills pair, and has to check that LegendaryTabRunicSkills is assignable to TabBase. That assignability check eager-resolves TabBase, which fails with NoClassDefFoundError when Legendary Tabs is absent — even though the if (LegendaryTabsIntegration.isModLoaded()) guard means the lambda would never actually run. Fixed by replacing the inline lambda with a method reference LegendaryTabsClientIntegration::registerTab that delegates to a new registerTab() static method on the existing client-integration class. ClientProxy's bytecode now only references LegendaryTabsClientIntegration (a plain class not in the optional-mod namespace); the sfiomn.* types stay confined to LegendaryTabsClientIntegration and LegendaryTabRunicSkills, which are only class-loaded when the isModLoaded() guard passes. The inline sfiomn.legendarytabs.api.tabs_menu.TabsMenu.register(...) call and the now-unused import com.otectus.runicskills.client.gui.LegendaryTabRunicSkills were removed from RunicSkillsClient.java.
  • Skill-selection hover border misaligned. skill_card_hover.png is 74×26 (a symmetric green halo with 4px of glow on each horizontal side of the underlying 66×26 button), but RunicSkillsScreen.drawOverview was passing OVERVIEW_SLOT_WIDTH (66) as both the blit size and the textureWidth argument to GuiGraphics.blit. With a mis-declared texture width, OpenGL normalised UV against 66 while the image is really 74 pixels wide — so the entire halo got squashed horizontally into the 66-wide button rect, pulling the visible border inside the button outline instead of glowing around it. Fixed by introducing OVERVIEW_HOVER_TEX_WIDTH/HEIGHT constants (74×26), passing them as the real texture dimensions, and shifting the blit position by (74-66)/2 = 4px left so the halo sits centered around the button with the expected 4px outer glow on each side.
  • Skill-selection tooltip could be overpainted by adjacent cells. drawOverview was calling Utils.drawToolTip inside the skill-iteration loop, so any cell drawn after the hovered one (specifically the right-column neighbour) rendered on top of the tooltip. Fixed by capturing the hovered skill in a local, completing the loop, and rendering the tooltip once after every cell has painted. Defensive against future art changes even though the 74×26 halo fits entirely within the 11px inter-column gap.

Notes

  • Vanilla /reload updates perk-group state server-side but does not auto-push to clients (matches existing lock-items behavior). Use /skillsreload for full propagation.
  • All five new config options default to behavior-preserving values (0, empty list, 1.0); upgrading an existing world changes nothing until the admin opts in.
  • No NBT schema changes; save-compatible with 0.9.7.