Give arcane essence per-slot mana and make one cinder gem enough

Arcane essence granted max mana through a single modifier with slot `armor`.
On a weapon that reads as "+200 Max Mana when worn", which is nonsense - you
do not wear a greatsword - and it gave every armour piece the same amount.

Each slot now carries its own modifier: 1300 on a helmet, 900 chest, 800 legs,
700 boots, and 900 in the main hand / 400 offhand so a weapon grants it while
held. They stack per module, so a piece built out of arcane essence that also
carries an arcane gem counts twice.

Cinder essence moves to slot `any`, so a single gem grants fire immunity
wherever it sits rather than only from an armour piece. The attribute is capped
at 1 and the handler treats anything at or above 1 as immune, so extra pieces
neither stack nor dilute it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
modules-and-missing-materials
Amir Alexander Abdelbaki 2026-08-12 20:05:14 +02:00
parent 5828bbfb01
commit b7348a69a1
4 changed files with 67 additions and 15 deletions

View File

@ -144,6 +144,14 @@ Requires `pillow` and `numpy`.
and lore rather than by kind, so those are generated from a table with shared and lore rather than by kind, so those are generated from a table with shared
stat bands per tier. What actually tells them apart in game is the palette, stat bands per tier. What actually tells them apart in game is the palette,
and that still comes from each gem's own texture. and that still comes from each gem's own texture.
- Arcane essence gives max mana per slot: 1300 on a helmet, 900 chest, 800
legs, 700 boots, 900 held in the main hand, 400 offhand. Separate per-slot
modifiers rather than one `armor`-wide one, so a weapon reads as a held bonus
instead of claiming something "when worn". They stack per module - a piece
built out of arcane essence that also carries an arcane gem counts twice.
- Cinder essence uses slot `any`, so one gem grants fire immunity wherever it
sits, worn or held. The attribute caps at 1 and the handler treats anything at
or above 1 as immune, so extra pieces neither add nor dilute.
- Material properties belong under the `default` key, not `armor` or `handheld`. - Material properties belong under the `default` key, not `armor` or `handheld`.
A gem socketed into armour is not tagged `armor`: gem modules declare which A gem socketed into armour is not tagged `armor`: gem modules declare which
material properties they accept and list only `default` plus their own gem material properties they accept and list only `default` plus their own gem

View File

@ -48,15 +48,45 @@
"attributes": [ "attributes": [
{ {
"attribute": "irons_spellbooks:max_mana", "attribute": "irons_spellbooks:max_mana",
"value": "100", "value": "1300",
"operation": "+", "operation": "+",
"slot": "armor" "slot": "head"
},
{
"attribute": "irons_spellbooks:max_mana",
"value": "900",
"operation": "+",
"slot": "chest"
},
{
"attribute": "irons_spellbooks:max_mana",
"value": "800",
"operation": "+",
"slot": "legs"
},
{
"attribute": "irons_spellbooks:max_mana",
"value": "700",
"operation": "+",
"slot": "feet"
},
{
"attribute": "irons_spellbooks:max_mana",
"value": "900",
"operation": "+",
"slot": "mainhand"
},
{
"attribute": "irons_spellbooks:max_mana",
"value": "400",
"operation": "+",
"slot": "offhand"
}, },
{ {
"attribute": "irons_spellbooks:mana_regen", "attribute": "irons_spellbooks:mana_regen",
"value": "0.15", "value": "0.15",
"operation": "*", "operation": "*",
"slot": "armor" "slot": "any"
}, },
{ {
"attribute": "irons_spellbooks:spell_power", "attribute": "irons_spellbooks:spell_power",

View File

@ -52,13 +52,13 @@
"attribute": "cmmodular:fire_immunity", "attribute": "cmmodular:fire_immunity",
"value": "1", "value": "1",
"operation": "+", "operation": "+",
"slot": "armor" "slot": "any"
}, },
{ {
"attribute": "irons_spellbooks:fire_magic_resist", "attribute": "irons_spellbooks:fire_magic_resist",
"value": "0.2", "value": "0.2",
"operation": "*", "operation": "*",
"slot": "armor" "slot": "any"
}, },
{ {
"attribute": "irons_spellbooks:fire_spell_power", "attribute": "irons_spellbooks:fire_spell_power",

View File

@ -511,23 +511,33 @@ MATERIALS = [
enchantability=32, mining_speed=11, toughness=3, armor_durability=38, enchantability=32, mining_speed=11, toughness=3, armor_durability=38,
armor_toughness=2.5, groups=("crystal", "gem"), textures=("crystal", "bright"), armor_toughness=2.5, groups=("crystal", "gem"), textures=("crystal", "bright"),
# Everything hangs off `default`. A gem socketed into armour is not # Everything hangs off `default`. A gem socketed into armour is not
# tagged `armor` - the gem modules declare which material properties they # tagged `armor` or `handheld` - the gem modules declare which material
# accept and both list only `default` plus their own gem tags - so # properties they accept and both list only `default` plus their own gem
# anything filed under `armor` or `handheld` silently never applied. The # tags - so anything filed elsewhere silently never applied.
# attribute's own slot decides where it counts instead. #
# Each slot gets its own modifier rather than one `armor`-wide one. That
# is what lets a helmet be worth more than boots, and it stops a sword
# from advertising a bonus "when worn", which is what a slot of `armor`
# on a weapon reads as.
properties={ properties={
"default": { "default": {
"emissive": {"sky": 6, "block": 6}, "emissive": {"sky": 6, "block": 6},
"attributes": [ "attributes": [
# Per armour piece, so a full set is roughly +400 on top of attribute("irons_spellbooks:max_mana", 1300, "head"),
# the 100 base pool Iron's Spells gives a player. attribute("irons_spellbooks:max_mana", 900, "chest"),
attribute("irons_spellbooks:max_mana", 100, "armor"), attribute("irons_spellbooks:max_mana", 800, "legs"),
attribute("irons_spellbooks:mana_regen", 0.15, "armor", "*"), attribute("irons_spellbooks:max_mana", 700, "feet"),
# Held, not worn.
attribute("irons_spellbooks:max_mana", 900, "mainhand"),
attribute("irons_spellbooks:max_mana", 400, "offhand"),
attribute("irons_spellbooks:mana_regen", 0.15, "any", "*"),
attribute("irons_spellbooks:spell_power", 0.10, "mainhand", "*"), attribute("irons_spellbooks:spell_power", 0.10, "mainhand", "*"),
attribute("irons_spellbooks:cooldown_reduction", 0.05, "mainhand", "*"), attribute("irons_spellbooks:cooldown_reduction", 0.05, "mainhand", "*"),
], ],
}, },
}), }),
# These stack per module: a piece built out of arcane essence that also
# carries an arcane gem counts twice.
M("cinder_essence", "irons_spellbooks", "crystal", "Cinder", M("cinder_essence", "irons_spellbooks", "crystal", "Cinder",
"irons_spellbooks:cinder_essence", "irons_spellbooks:cinder_essence",
@ -546,8 +556,12 @@ MATERIALS = [
# above 1 as immune, so this is genuine immunity rather than a # above 1 as immune, so this is genuine immunity rather than a
# resistance that stacks up to it. # resistance that stacks up to it.
"attributes": [ "attributes": [
attribute(FIRE_IMMUNITY, 1, "armor"), # Slot `any`, so a single gem grants immunity wherever it
attribute("irons_spellbooks:fire_magic_resist", 0.20, "armor", "*"), # sits - worn or held. The handler treats anything at or
# above 1 as immune, and the attribute is capped at 1, so
# extra pieces add nothing and nothing can dilute it.
attribute(FIRE_IMMUNITY, 1, "any"),
attribute("irons_spellbooks:fire_magic_resist", 0.20, "any", "*"),
attribute("irons_spellbooks:fire_spell_power", 0.20, "mainhand", "*"), attribute("irons_spellbooks:fire_spell_power", 0.20, "mainhand", "*"),
], ],
}, },