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 0.9.7 (Forge 1.20.1)

  • R
  • Apr 23, 2026
  • 1.13 MB
  • 75
  • 1.20.1
  • Forge

File Name

runicskills-0.9.7.jar

Supported Versions

  • 1.20.1

Curse Maven Snippet

Forge

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

Learn more about Curse Maven

[0.9.7] - 2026-04-22

Added

  • enableItemLocks master toggle in HandlerCommonConfig (default true). When off, every entry in runicskills.lockItems.json5 and every integration-generated lock is ignored — SkillCapability.canUse(...) short-circuits to true. This is the single switch users were looking for when they "disabled itemlock" expecting items like the trident to become usable; previously, the only "lock" toggles in the YACL UI were dropLockedItems (only controls auto-dropping, not the lock check) and the per-integration *EnableLockItems flags (only gate integration-generated entries, not the vanilla list that contains the trident). The new flag is mirrored into ClientCapabilityAccess.canUseItemClient so client-side checks (used by MixGunItem and gun-mod integrations) honour it too.
  • ConfigSyncCP.sendToAllPlayers() — broadcasts the lock-items list to every connected client. Wired into /skillsreload and /registeritem so cache changes propagate without requiring every player to relog.

Changed

  • dropLockedItems config comment now notes that it has no effect when enableItemLocks is off.
  • Bumped network channel PROTOCOL_VERSION from 2 to 3. CommonConfigSyncCP now carries the enableItemLocks boolean ahead of dropLockedItems; the wire format is incompatible with the previous version.
  • /skillsreload now also re-sends CommonConfigSyncCP and DynamicConfigSyncCP to every connected player so config edits made between joins are picked up without requiring relogs.

Fixed

  • Trident (and every other vanilla locked item) appeared "stuck" locked even after the user toggled lock-related options off. Root cause: there was no master toggle. dropLockedItems and the *EnableLockItems per-integration flags do not gate the vanilla lockItemList entries (which include minecraft:trident#strength:20;dexterity:18), so anyone trying to "disable itemlock" had no effect on vanilla items. The new enableItemLocks toggle gives a single switch.
  • /registeritem <skill> <level> did not refresh the cache when adding a skill to an existing locked-item entry. The "remove" and "add new item" branches called HandlerSkill.ForceRefresh(); the "add skill to existing item" branch only saved the file to disk, so the new requirement was silently ignored until the next reload. Now refreshes on all three branches.
  • Lock-items cache was server-only after /skillsreload and /registeritem. Connected clients kept their stale Skills map, and since InteractionEventHandler (which calls canUseItem) runs on both sides, a client with the stale cache would cancel right-clicks before the server even saw them. Both commands now broadcast ConfigSyncCP to every player.

[0.9.6] - 2026-04-22

Added

  • LegendaryTabsClientIntegration.synchronizeTabStripAcrossScreens — runs once at FMLLoadCompleteEvent (i.e. after every mod's TabsMenu.register call has drained) and reflectively does a two-way sync against Legendary Tabs' private tabsScreens map: - Forward: every tab registered on InventoryScreen is re-registered on RunicSkillsScreen with its original priority preserved, so the Skills page shows the same full tab strip a player sees on the vanilla inventory (same tabs, same order, same X anchor). - Reverse: the Skills tab is also registered on every other screen that already hosts Legendary Tabs' built-in InventoryTab (FirstAid Medkit, Curios, Travelers Backpack, Reskillable/Pufferfish skill pages, etc.), so it stays visible when the player switches between inventory-companion screens. - The whole pass is wrapped in try/catch against NoSuchFieldException / IllegalAccessException / ClassCastException; any failure logs a single warning and the mod falls back to the prior "Skills tab only on the Skills screen" behaviour.
  • libs/l2tabs-0.3.3.jar — compile-time API stub (3.4 KB). Contains only the four type signatures (BaseTab, TabToken, TabManager, TabRegistry with its TabFactory functional interface) that TabRunicSkills and RunicSkillsClient reference. compileOnly keeps it out of the shipped jar; when the real L2Tabs 0.3.3 mod is installed, its classes shadow the stub at runtime. Lets the project build when the upstream L2Tabs jar isn't locally available, without any build-script changes. Ships with a L2TABS_STUB_README.txt documenting the arrangement.

Changed

  • Skills tab now renders from Legendary Tabs' own tab_menu_buttons.png atlas at (u=27, v=92) — the plain silver sword tile that no built-in tab class claims. Hover state uses the +54 U shift that every built-in tab uses. This removes the renderItem(leveling_book, …) overlay, the custom legendary_tab.png texture, and makes the Skills tab byte-for-byte identical in frame shape, shading, palette, and hover transition to every neighbouring tab — including the exact highlight bevel rows that hand-drawn reproductions were missing (which previously read as "1 pixel too tall" because the flat top edge lacked the real tab's 2-row white highlight strip).
  • LegendaryTabsClientIntegration (new) — split out of LegendaryTabsIntegration so the shared class stays dedicated-server-safe (no net.minecraft.client.* imports — silences the :checkSidedImports lint task). The client file lives under client/integration/, fully inside the allowlist.
  • Default legendaryTabsPriority lowered from 80 to 15. Bytecode audit of Legendary Tabs 1.1.3.1 confirms ScreenInfo.tabs is a TreeMap<Integer, List<TabBase>> (ascending order = render order) and the built-in tab priorities are InventoryTab=10, Backpacked/TravelersBackpack=20, Reskillable*=30, PassiveSkillTree/PufferfishsSkills=40, BodyDamage=50, Diet=60, FtbQuests=70, Maps (JourneyMap/MapAtlases/Xaeros)=75, FtbTeams=80. Priority 15 sits strictly between Inventory and everything else, so Skills renders as the second tab in the strip regardless of which integrations are loaded. Config comment updated with the full priority table.

Fixed

  • Skills tab rendered as a floating book icon instead of a proper tab inside Legendary Tabs. Root cause: LegendaryTabRunicSkills.render drew only the item via gfx.renderItem(…) with no background. Legendary Tabs' TabButton.renderWidget does not invoke super, so tabs are fully responsible for drawing their own 26×22 frame; every built-in tab does so by blitting from the shared atlas. Fixed by switching to an atlas blit (see Changed).
  • Opening the Skills screen collapsed Legendary Tabs' strip to just the Skills icon. TabsMenu.initScreenButtons keys off screen.getClass() (exact match, not instanceof) and iterates only tabs explicitly registered for that class. Fixed by the forward half of synchronizeTabStripAcrossScreens.
  • Skills tab disappeared when any other inventory-companion screen (Medkit, Curios, Backpack, …) was open. Same root cause as above but in the other direction — our tab wasn't registered on those screens' classes. Fixed by the reverse half of synchronizeTabStripAcrossScreens.
  • Build failed with "Could not find dev.xkmc.l2tabs:l2tabs:0.3.3" cascading into 20+ follow-on resolution errors. Single root cause: libs/l2tabs-0.3.3.jar was missing and no public Maven repository hosts the coordinate. When :__obfuscated configuration failed on that one dep, ForgeGradle's deobf pipeline aborted without populating bundled_deobf_repo for any of the other fg.deobf(...) entries. Fixed by shipping the compile-time stub (see Added).

Removed

  • assets/runicskills/textures/gui/legendary_tab.png — custom hand-drawn frame texture. No longer referenced now that the Skills tab blits directly from Legendary Tabs' shared atlas.

[0.9.5] - 2026-04-22

Added

  • Legendary Tabs (Sfiomn) integration. When legendarytabs is present, Runic Skills registers a native TabsMenu tab (LegendaryTabRunicSkills) during FMLClientSetupEvent so the Skills tab participates in Legendary Tabs' own UI instead of being drawn twice.
  • LegendaryTabsIntegration compat layer (detects the mod via ModList and gates the native-tab registration).
  • build.gradle now pulls sfiomn.legendarytabs:legendarytabs:1.20.1-1.1.3.1 as a compileOnly dependency, resolved from the local libs/ directory (jar is not redistributed — drop it in yourself to build).
  • legendaryTabsPriority config in HandlerConfigClient — defaults to 500; controls ordering within Legendary Tabs' strip (lower = earlier).
  • Three new lang keys (tooltip.perk.rank, tooltip.perk.next_rank, tooltip.edit_title) translated across all 17 supplied languages; they replace hard-coded English strings in the perk tooltip and the title-edit button.
  • HandlerConditions now registers EntityKilledBy as the canonical title-condition name; the typoed EntiyKilledBy remains registered as a deprecated alias so existing title configs continue to work.

Changed

  • MixInventoryScreen now bails out of its render and mouse-click injects when Legendary Tabs is loaded, deferring to the native tab registration. Prevents double-rendering of the Runic Skills tab inside Legendary Tabs' wrapped screens. The two guard branches are consolidated into a runicskills$externalTabsActive() helper and getRecipeBookComponent().isVisible() is now called once per frame (also null-guarded) instead of twice.
  • KubeJSIntegration.postLevelUpEvent caches its reflective Class/Method/field lookups on first successful resolve instead of redoing six reflective calls every level-up.
  • Utils.FONT_COLOR is now final; added SKILL_ABBR_COLOR, SKILL_LEVEL_COLOR, TITLE_SELECTED_COLOR, TITLE_UNSELECTED_COLOR constants and switched the corresponding hard-coded values in RunicSkillsScreen.
  • MixVillager haggler-delta map now drops entries that no longer correspond to offers on the villager, preventing a minor memory leak when a trade is completed between UI opens.

Fixed

  • Legendary Tabs integration — three distinct integration defects discovered via jar-disassembly audit: - (A) Duplicate tab strip rendered on the Skills screen. RunicSkillsScreen.render() called DrawTabs.render/mouseClicked/onClose unconditionally, and the MixInventoryScreen bail-out only covered the vanilla InventoryScreen. Now gated behind !L2TabsIntegration.isModLoaded() && !LegendaryTabsIntegration.isModLoaded() at all three call sites. - (B) Skills tab invisible on the vanilla inventory strip. Default legendaryTabsPriority was 500; disassembly of sfiomn.legendarytabs.client.tabs_menu.InventoryTab (priority 10) and FtbQuestsTab (priority 70) revealed that Legendary Tabs uses small priority integers and paginates overflow. At 500 our tab always landed on a later page. Default dropped to 80 so the Skills tab sits right after built-in tabs on page 1. Config comment updated to explain the priority convention for pack authors. - (C) Legendary Tabs strip hidden on the Skills screen. LegendaryTabRunicSkills registered RunicSkillsScreen.class with the vanilla-inventory VANILLA_GUI_HEIGHT = 166, but the Skills panel is actually 194 pixels tall (PANEL_HEIGHT). TabsMenu.initScreenButtons computed topScreenPos = (screenHeight - 166) / 2 and drew the strip 14 pixels too low — underneath our panel background. Now registers RunicSkillsScreen with RUNIC_SKILLS_GUI_HEIGHT = 194.
  • Singleplayer world won't load — kicks player back to the multiplayer screen: MixPlayer.runicskills$modifyMaxAir fires during Entity.<init> (specifically the setAirSupply(getMaxAirSupply()) call in the constructor), which runs before LivingEntity.defineSynchedData registers DATA_HEALTH_ID. The perk check inside (Perk.isEnabled(player) → player.isDeadOrDying() → player.getHealth() → SynchedEntityData.get(DATA_HEALTH_ID)) NPE'd because the accessor hadn't been registered yet. Server thread threw "Couldn't place player in world / Invalid player data", disconnected the integrated-server client, and the UI flow bounced to the last-seen join screen. Fixed in two places: (1) Perk.isEnabled(Player) now uses player.isRemoved() (a simple field read) instead of player.isDeadOrDying() to gate dead players; (2) MixPlayer.getMaxAirSupply bails early when SkillCapability.get(player) returns null, which is always the case during Entity.<init> (capabilities are attached by Forge after the constructor returns), guaranteeing we never invoke Perk.isEnabled inside the constructor path.
  • Crash on attack-range modifier (Better Combat / AttackRangeExtensions): MixTargetFinder.apply$AttackRangeModifiers had a switch statement with no break; between case ADD and case MULTIPLY, causing every additive modifier to also be applied multiplicatively (and vice versa). Rewritten as an arrow-form switch expression.
  • Crash when a skill-gated gun is fired (PointBlank): MixGunItem.tryFire called ci.cancel() on a CallbackInfoReturnable<Boolean> with no return value set, which is illegal under Mixin and threw IllegalStateException. Now calls ci.setReturnValue(false).
  • Crash on empty title queue: TitleQueue.peek() and dequeue() no longer throw NoSuchElementException when the queue is empty; peek() returns null and dequeue() is a no-op.
  • NPE in OverlaySkillGui: when HandlerSkill.getValue(skill) returned null (unknown skill key), showWarning still armed showTicks > 0, causing the render loop to NPE on skills.size(). Now bails early without setting the ticker.
  • Multiple NPE paths when the local skill capability is not yet synced: RunicSkillsScreen.drawTitleButton, handleOverviewClick, the title list renderer, buildDetailPageState, and RegistryClientEvents.onTooltipDisplay now all null-guard SkillCapability.getLocal() and fall back to sensible empty-state rendering (requirement colour defaults to red, detail page returns null, title button shows blank).
  • DrawTabs.renderTabVisual matrix-stack leak: nested pushPose/popPose pairs are now wrapped in try/finally, so a throwing renderItem (e.g. a buggy third-party item renderer) no longer leaks two pose frames into subsequent GUI rendering.
  • TitleCommand NPE on unset: setTitle(..., false) now null-guards SkillCapability.get(player) before calling setUnlockTitle.
  • SkillCondition.ProcessVariable NPE: returns a processed value of 0 and bails cleanly when the player's skill capability isn't attached yet.

Removed

  • ScreenTabEvents dead-placeholder class. The LegendaryTabsIntegration javadoc previously pointed to it as the active renderer for Legendary Tabs — in reality the native TabBase is used. Javadoc rewritten to describe the real mechanism.