Magi2 pilot#123
Conversation
Lays the adapter-layer base for porting Azure-Peak PR #6406 "Magi 2: The
Awakening" into Emerald Summit. No existing actions or spells are touched —
all new code lives under modular_azurepeak/code/modules/spells/magi2/ and
plugs into the existing /datum/action HUD via the older UpdateButtonIcon API.
What compiles now:
- /datum/action/cooldown/spell — adapted action-spell base (~570 lines).
Cast chain (PreActivate/Activate/before_cast/cast/after_cast), resource
handling (stamina/energy/devotion), INT scaling, armor & weapon penalties,
invocation, charge state, and visual effects all live. Charge mouse-signal
input flow falls back to do_after(); the full start_casting/try_casting
handlers are TODO'd for next session.
- /datum/magic_aspect — port of magic_aspect.dm with inlined spell-list
helpers so we don't have to widen the existing typed mind.AddSpell.
- Spell visual effects (spell_rune_under, wave_up, particle_up, spell_impact)
and mob/living start_/finish_/cancel_spell_visual_effects procs.
- Defines: SPELL_COST_*, SPELL_REQUIRES_*, SPELL_CANCEL_CAST family, aspect
names, charge times, projectile speeds, armor penalties, implement tiers,
plus Magi 2 trait names and movespeed/comsig identifiers that don't exist
in Emerald Summit (no-op until callers populate them).
- Stub status effects (residual_focus, parry_buffer, arcyne_momentum,
recent_weapon) and rogueweapon.implement_refund = 0 default so the spell
base's Arcyne-combat hooks are no-ops until the upstream system ports.
What does not exist yet:
- Any concrete spell subtype (no Spitfire, no Fireball)
- Aspect subtypes (no Pyromancy datum)
- Grimoire TGUI
- Debug verbs to grant spells
Next session: port Spitfire as the first /datum/action/cooldown/spell/projectile
subtype, add a /client debug-grant verb, and smoke-test middle-click casting
on roguetest.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First end-to-end working Magi 2 spell. Action button renders, charging
visuals play, projectile fires in facing direction, fire stacks apply on hit.
New:
- /datum/action/cooldown/spell/projectile — projectile-spell base (adapter
port; drops the modern-HUD arc maptext for a balloon_alert toggle and
skips the COMSIG_PROJECTILE_SELF_ON_HIT signal hookup since ES projectiles
override on_hit directly).
- /datum/action/cooldown/spell/projectile/spitfire_magi2 — Spitfire spell,
plus /obj/projectile/magic/spitfire_magi2 and /spitfire_magi2/arc. Named
with the _magi2 suffix to avoid collision with the existing proc_holder
/obj/effect/proc_holder/spell/invoked/projectile/spitfire.
- cmd_give_magi2_test_spell — admin debug verb. Hooked into
GLOB.admin_verbs_debug_mapping so it appears under "Debug verbs - Enable".
Toggle behavior: running it twice removes the spell.
- icons/mob/actions/mage_pyromancy.dmi — copied from Azure-Peak.
MVP deviations from upstream (deferred until charge mouse-signal handlers port):
- click_to_activate = FALSE so the spell fires in the caster's facing
direction on action-button click. No click-intercept-then-target flow yet.
- charge_required stays TRUE with CHARGETIME_POKE so the rune/particle
visuals exercise, falling back to do_after() in before_cast.
Foundation fixes that unblocked the MVP:
- spell_cooldown.dm: background_icon_state = "bg_spell" (was "spell0" — that
state exists in Azure-Peak's roguespells.dmi but not Emerald Summit's).
- spell_cooldown.dm: unconditional icon_icon/button_icon remap in New().
The parent /datum/action initializes icon_icon to 'icons/mob/actions.dmi',
which short-circuited the previous guarded remap and left buttons drawing
the wrong dmi as background.
Build system fix:
- tools/build/build.ts inputs list was missing modular_*/**, so Juke's
freshness check skipped the DM compile entirely when only modular files
changed. Added modular_*/** to cover all five modular_* directories.
Test plan executed: Court Magician spawned on roguetest, debug verb grants
Spitfire, button appears bottom-left, middle-clicking action fires projectile
forward, sound + invocation + fire stacks all work, button cooldowns ~5.5s.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the three remaining Pyromancy spells, each exercising a different
spell mechanic to validate the adapter base across spell types:
- Fireball — heavy projectile (16s CD, 70 BURN) with on-hit AOE that
applies fire stacks + slowdown in radius 1, plus structural damage to
walls/structures in the same radius. Uses /obj/projectile/magic/aoe/fireball/magi2
(the /rogue path is already taken by the existing proc_holder fireball).
- Fire Blast — 4-tile line burn spell that pushes targets back 2 tiles
and ignites the ground with hotspots. Shields stop further propagation.
- Fire Curtain — 5x2 ground curtain in front of caster with 3s telegraph
then ~10s of hotspot burn. Caster is not immune to their own curtain.
Supporting changes:
- arcyne_strike helper ported in simplified form into _compat_stubs.dm.
Does the apply_damage + run_armor_check + bodypart_attacked_by flow
that the AOE blast damage needs. Strips the parry/clash integration,
attack animation, hit-verb messaging, and integrity status reporting
from the upstream version — those require AP combat helpers we don't
have. The simplified verb-and-message output is adequate for the pilot.
- Debug verb extended to grant all four Pyromancy spells in one click
(toggle: re-run to remove). Verb renamed to
"Give Magi 2 Test Spell (Pyromancy)".
MVP deviations from upstream:
- All three new spells use click_to_activate = FALSE so they fire in
the caster's facing direction. Fire Curtain centers 2 tiles in front
of the caster (upstream centers on the clicked ground target).
- npc_simple_damage_mult on the fireball projectile renamed to
npc_damage_mult to match Emerald Summit's /obj/projectile var name.
Test plan executed: granted all four spells via debug verb, cast each in
sequence on roguetest with dummy targets. Damage, fire stacks, AOE radius,
push, hotspot persistence, and visual effects all check out.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lifts the pilot from "debug verb grants a hardcoded list of spells" to
"debug verb binds an aspect, which grants its spell set as a unit."
End-to-end magic_aspect machinery is now exercised — grant_spells and
revoke_spells from spell_cooldown.dm's foundation actually do work
when called by a real aspect subtype.
New:
- /datum/magic_aspect/pyromancy — wraps the four ported Pyromancy
spells as fixed_spells. Skips upstream's create_campfire (utility
not ported) and fireball/greater + fireball/artillery variants.
- /datum/magic_aspect/hex — minor aspect with one spell, validates
that different aspect picks produce different spell sets.
- /datum/action/cooldown/spell/wither_magi2 — line-of-curse telegraph
spell, applies the existing /datum/status_effect/buff/witherd from
Emerald Summit's spells_status_effects.dm. Uses _magi2 suffix on
spell type and visual effects to avoid colliding with the existing
proc_holder Wither (/obj/effect/proc_holder/spell/invoked/projectile/wither
and /obj/effect/temp_visual/trap/wither).
- icons/mob/actions/mage_hex.dmi copied from Azure-Peak.
Changed:
- _debug.dm: verb renamed to "Bind Magi 2 Aspect (Pilot)" and now
pops an input() picker listing every registered aspect plus an
"Unbind All" sentinel. Bound aspects show a "(bound)" marker.
The bind/unbind helpers (_magi2_bind_aspect / _magi2_unbind_aspect /
_magi2_aspect_is_bound / _magi2_unbind_all) call the aspect datum's
grant_spells / revoke_spells procs.
Deferred (next session candidates):
- The full Grimoire TGUI — 10 .tsx files + 857-line aspect_picker.dm
backend. Out of scope for one session. The input() picker covers the
"let admin bind aspects" need until then.
- court_magician_magi2 advclass that auto-binds Pyromancy on spawn.
- More minor aspects (Hex is the only one wired). The plan's Aegiscraft
needs /conjure_aegis which would pull in a whole aegis system.
Test plan executed: bind Pyromancy → 4 buttons appear and cast cleanly,
re-binding shows "(bound)" marker, bind Hex on top → 5 buttons, Wither
applies witherd to mobs in its line, Unbind All clears every Magi 2
spell from the mind's spell_list.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ling Two related fixes to /datum/action/cooldown/spell: 1. Cooldown timer now renders on the existing /atom/movable/screen/maptext_holder (layer ABOVE_HUD_LAYER, set up by /atom/movable/screen/movable/action_button) instead of on the action button's own .maptext field. The holder sits above the spell foreground icon overlay (which lives at button.layer + 0.1), so the countdown number is no longer drawn underneath busy artwork. The holder's built-in animate() loop handles the per-decisecond countdown, so process() no longer has to rewrite the maptext every tick. 2. StartCooldown now actually honors the override time. Parent /datum/action/cooldown.StartCooldown takes no args and uses initial cooldown_time, so my Activate's call StartCooldown(get_adjusted_cooldown()) was silently dropping INT scaling and armor penalty/weapon penalty on cooldown. The override accepts override_time, falls back to cooldown_time when not passed. process() trims to the bare housekeeping that's still needed when not charging (stop processing once next_use_time <= world.time, refresh button icon so IsAvailable can re-evaluate). The maptext lives on its own animation track. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a new lobby slot that auto-binds the Pyromancy aspect on spawn — no
admin debug verb, no in-round spell learning. Validates that the aspect
machinery plugs into the real job/lobby/spawn flow.
New:
- /datum/job/roguetown/magician_magi2 — clone of the Court Magician job
with title "Court Magician (Magi 2 Pilot)" and display_order set one
slot below the original. Same race/sex restrictions, same traits,
same outfit gear; only the advclass differs.
- /datum/advclass/courtmage_magi2 — sole subclass for the new job.
subclass_spellpoints = 0 (the player doesn't pick spells; the aspect
grants them).
- /datum/outfit/job/magician/basic/magi2 — inherits all gear from the
existing Court Magician basic outfit; pre_equip() calls super, then
instantiates /datum/magic_aspect/pyromancy and grants its spell set
to H.mind. The four Pyromancy spells appear in the action bar on
spawn.
- CTAG_COURTMAGE_MAGI2 category tag so the advclass only rolls under
this job, not the original Court Magician.
Layered on the wrong outfit at first: setting both job.outfit and
advclass.outfit to /datum/outfit/job/magician/basic/magi2 caused the
gear to apply twice (two staves, etc). Job outfit is now the bare parent
/datum/outfit/job/magician (no gear vars set) — gear lives entirely in
the advclass outfit, mirroring how the original Court Magician splits
job and advclass outfits.
Test plan executed: picked Court Magician (Magi 2 Pilot) at lobby,
spawned with the four Pyromancy buttons already in the action bar plus
the standard Court Magician gear (one staff this time), cast each spell.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes Stage 1 of the magi2-pilot plan. Players can now switch aspects
in-round through an item-driven TGUI instead of needing the admin debug
verb.
New:
- /obj/item/book/magi2_grimoire — "Grimoire of Aspects" item with the
existing spellbookbrown art from icons/roguetown/items/books.dmi.
attack_self → ui_interact opens the TGUI. Reads/writes aspect bindings
via the shared _magi2_bind/_magi2_unbind helpers.
- tgui/packages/tgui/interfaces/Magi2Grimoire.tsx — single-file TGUI
listing every registered aspect under Major / Minor sections with a
bind/unbind button per aspect. School color border, latin name + desc
+ spell list per row. Bound aspects show a "(bound)" label and a
highlighted background.
Changed:
- magic_aspect.dm now hosts the four bind helpers (_magi2_aspect_is_bound,
_magi2_bind_aspect, _magi2_unbind_aspect, _magi2_unbind_all). They
were file-private to _debug.dm before; the Grimoire item shares them.
- magician_magi2 outfit pre_equip appends the Grimoire to backpack_contents
so Magi 2 Court Magicians spawn with the picker.
Fixes uncovered during smoke test:
- _magi2_aspect_is_bound used initial(A.fixed_spells) where A was a
typepath variable. BYOND's initial() doesn't reliably read list
contents through a stored typepath, so every aspect always read as
unbound — the Tome showed "Bind" even when the player spawned with
Pyromancy granted. Switched to instantiating the aspect and reading
its actual fixed_spells list.
- Initial book art was the stock /obj/library.dmi/book6 placeholder
(rendered purple/default). Switched to spellbookbrown_0 from the
existing roguetown books.dmi to match the magical-item visual style.
- Renamed to "Grimoire of Aspects" to disambiguate from the existing
/obj/item/book/spellbook which is also named "tome of the arcyne".
Test plan executed: spawned as Court Magician (Magi 2 Pilot), opened the
Grimoire from inventory, saw Pyromancy correctly marked (bound) and
clickable to unbind. Bound Hex, cast Wither. Toggled both aspects on/off
multiple times — picker state stays in sync with mind.spell_list and
the action HUD.
Stage 1 of the magi2-pilot plan is mechanically complete. Open follow-ups
(separate sessions): port additional Major/Minor aspects, port the
Arcyne Ward universal mage armor, port wand/staff implements, click-to-
target charge handlers for diagonal aim, T4 mastery variant system,
encyclopedia view, point-buy utility spells.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the second Major aspect (Cryomancy) and the click-to-target casting
flow that the original plan deferred from Stage 1.
== Cryomancy ==
- /datum/magic_aspect/cryomancy with 5 fixed spells: Frost Bolt,
Frost Blast, Ice Burst, Snap Freeze, Fridigitation. Skipped upstream's
frozen_mist mastery variant and chill_food (redundant with Fridigitation).
- cryomancy/_shared.dm — frost-stack escalation system (frosted1 → 2 → 3
→ burst). Each stack slows movement; tiers 2/3 add action cooldown
multipliers via nextmove_modifier. A fourth stack detonates for 25 BURN
and clears all frost, with a 15s adaptation immunity to prevent perma-stun.
Atom-color tint blue while frosted.
- Frost Bolt — fast cardinal projectile applying one stack, thaws fire.
- Frost Blast — 4-tile line that knocks back and chills, mirror of Fire Blast.
- Ice Burst — heavy AOE projectile (mirror of Fireball) applying frost in 3x3.
- Snap Freeze — ground-targeted 2-tile-radius delayed AOE with telegraph.
- Fridigitation — utility cantrip that deep-freezes food items (extends shelf
life, removes rot timer). targets_items = TRUE so it intentionally fires
on item clicks where other spells ignore them.
- Copied sounds: sound/spellbooks/icicle.ogg, crystal.ogg. Copied icon:
icons/mob/actions/mage_cryomancy.dmi.
- Cross-aspect synergy: Spitfire and Fireball now call has_frost_stacks /
remove_frost_stack on hit — fire shatters one stack per impact while still
applying its own fire stacks. Pyromancy ↔ Cryomancy rivalry works.
== Click-to-target ==
- /datum/action/cooldown/spell now overrides Trigger to set the spell as
the mob's click_intercept when click_to_activate is TRUE. The next click
routes through a new InterceptClickOn proc that calls PreActivate at the
clicked target. Re-clicking the action button toggles selection off with
a "Cancelled X." chat notice.
- InterceptClickOn return semantics tuned for chain-casting:
* UI clicks (action buttons, intent panel) fall through to normal HUD.
* Item clicks fall through to pickup/interact unless targets_items.
* Throw mode bypasses spells entirely (in_throw_mode → return FALSE).
* Cooldown clicks fall through so movement/combat still work between
casts; the spell stays selected for the next ready click.
* Successful casts keep the spell selected so the user can chain-cast.
- before_cast charge phase now runs do_after for ALL charge_required
spells, not just non-click ones. The hold-mouse-to-charge upstream flow
isn't a fit for Emerald Summit's older action HUD, so the click→charge→
cast sequence is uniform.
- Selected spell button transforms 5px upward via matrix.Translate so the
active spell is visually obvious in the action bar. button.pixel_y alone
didn't work because foreground overlays don't inherit it; transform does.
- button.mouse_opacity = MOUSE_OPACITY_OPAQUE so the entire 32x32 icon
area registers clicks (transparent pixels included). Earlier attempt at
bound_width/bound_height for a >32px hitbox caused movement choppiness;
reverted to icon-bounds-only.
All 4 Pyromancy + 4 Cryomancy + 1 Hex spell now use click_to_activate=TRUE.
Test plan executed: bound Cryomancy via Grimoire, frost-bolted a target three
times to escalate to frosted III, fourth hit burst for 25 burn + immunity.
Spitfired the same target → shattered one frost stack and applied fire.
Switched aspects mid-round via Grimoire. Selected Spitfire — button lifted,
clicked diagonally → projectile arced correctly. Clicked equipped sword while
selected → fell through to weapon swap. Toggled throw mode → click went to
throw normally. Re-clicked Spitfire button → "Cancelled Spitfire." in chat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports the signature Magi 2 universal mage armor: a 225-integrity invisible
ward that wears in the H.skin_armor slot and gap-fills coverage around the
player's real equipment at conjure time.
New:
- /datum/action/cooldown/spell/conjure_arcyne_ward_magi2 — toggle spell.
6s charge, 130 energy + 70 upfront stamina, 2-minute cooldown. Recasting
while warded dismisses with a proportional cooldown refund (full HP = no
cooldown, broken = full cooldown). Initial conjure starts no cooldown so
the button stays available for dismiss.
- /obj/item/clothing/suit/roguetown/armor/arcyne_ward_magi2 — the ward
itself. Invisible (icon_state = null), light armor class, ARMOR_LEATHER.
take_damage() flashes an arcane outline filter on the wearer + plays clang
sounds + sparks. On dismiss or break it cleans up the skin_armor slot.
- Integrity HUD: the spell button gets a "[pct]%" counter pinned to the top
edge while the ward is active (white text + black outline, distinct from
the cooldown timer at the bottom). Updates on every take_damage call,
clears on dismiss / break.
- COVERAGE_FULL_BODY_ACTUAL bitmask added to magi2 _defines.dm.
Granted to the Magi 2 Court Magician outfit via pre_equip so all Magi 2
mages spawn with it alongside their aspect — Arcyne Ward is universal in
Magi 2, not aspect-gated.
Deferred from upstream (deliberate scope cut):
- Dragonhide / Crystalhide upgrade variants (those are Autowardry minor
aspect choices; no Autowardry aspect ported yet).
- Paired Regenerate Arcyne Ward action (channelled mending, would need its
own paired-spell wiring to gate availability on a live ward).
- Equipment-signal-driven dynamic coverage updates. Upstream uses
COMSIG_MOB_EQUIPPED_ITEM / DROPITEM to recalculate coverage when the
wearer changes gear — those signals don't exist in Emerald Summit, so
coverage is calculated ONCE at conjure time. Removing armor mid-fight
leaves a gap; adding armor mid-fight produces double coverage on that
slot. Note this in the desc when more eyes are on the pilot.
- blocks_defense_while_channeling combat hook (would tie into parry/dodge
interruption — needs combat-system integration).
Test plan executed: spawned Magi 2 Court Magician, conjured ward (saw "100%"
at button top), took damage in fight → counter dropped, dismissed at 50% →
~60s cooldown started. Equipped helmet before conjure → ward gap-filled
correctly (head hits damaged helmet, not ward). Broke ward in combat →
full 2-minute cooldown.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Third Major aspect for the Grimoire. Lightning plays distinct from
Pyromancy/Cryomancy: hitscan projectiles, no DOT/stacks, gated CC via
a 15-second adaptation cooldown so the kit can't perma-lock a target.
New spells:
- Arc Bolt — minor staple hitscan jolt. 6.5s CD, light electrocute on hit.
/obj/projectile/magic/arc_bolt_magi2 (no name collision in ES).
- Bolt of Lightning — major hitscan with the signature CC stack: 0.5s
immobilize + 6s clickcd + 6s lightningstruck slowdown, all gated by
MT_LIGHTNING_ADAPTATION timer so repeated casts on the same target
print "shock adapted (Ns)" instead of stacking the lock.
- Heaven's Strike — click a tile, ~0.4s telegraph (TELEGRAPH_SKILLSHOT),
then a single high-damage strike on whoever's still on the tile.
Targets caster's zone_selected via arcyne_strike for limb aiming.
- Thunderstrike — three-stage AOE: center (80 BURN, 0.8s), then layer
one (60, 1.2s), then layer two (50, 1.6s). Concentric rings ripple
outward. 20s CD.
Skipped from upstream:
- `light` utility spell (not core to the combat identity).
- `greater_thunderstrike` mastery variant (no T4 mastery system yet).
Adapter changes:
- Reuse existing /obj/effect/temp_visual/trap/thunderstrike +
/thunderstrike_actual from code/modules/spells/spell_types/wizard/
invoked_aoe/thunderstrike.dm rather than redefining via a Fulgurmancy
_shared.dm. ES's existing temp visuals match the AP layer_one /
layer_two subtype pattern exactly.
- Dropped `guard_deflectable = TRUE` on arc_bolt + lightning projectiles
(var doesn't exist on Emerald Summit's /obj/projectile).
- Dropped the second `electrocute_act(0, src, 1, SHOCK_NOSTUN|SHOCK_VISUAL_ONLY)`
visual-only call from Heaven's Strike + Thunderstrike (SHOCK_VISUAL_ONLY
flag missing in ES). spell_impact temp visual still fires for feedback.
- Suffixed /obj/projectile/magic/lightning → /lightning_magi2 to avoid
collision with the existing proc_holder Lightning Bolt projectile.
- Added MT_LIGHTNING_ADAPTATION + LIGHTNING_ADAPTATION_COOLDOWN to magi2
_defines.dm — referenced in the file headers earlier but not actually
defined; first session to need them.
Test plan executed: bound Fulgurmancy via Grimoire, all four buttons appeared.
Arc Bolt strikes a target instantly with light shock. Lightning Bolt locks a
target for 6s — second cast within 15s prints "shock adapted (Ns)". Heaven's
Strike telegraphs then hits the aimed body part. Thunderstrike center fires
first, then concentric layers radiate out 1 then 2 tiles.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Spell implements: held weapons that capture a fraction of the cast's
resource cost and return it to the wielder as energy over 20 seconds,
and pick up an elemental glow when their school's spell fires through them.
New:
- /obj/item/rogueweapon now carries the implement metadata (attuned_color,
base_implement_name, implement_tier, attunement_cd cooldown). Non-implement
weapons stay at tier=0 and behave unchanged. implement_refund was already
on rogueweapon via _compat_stubs from the very first session.
- /obj/item/rogueweapon/proc/attune_implement(spell_color, spell_name) and
the file-local /proc/apply_attunement_glow() helper. 1-minute cooldown
between glow swaps so chain-casting different schools doesn't flicker.
- /obj/item/rogueweapon/woodstaff/implement_magi2 (lesser/greater/grand) —
two-handed staves inheriting the existing ES wooden staff melee profile.
Icons from copied icons/obj/items/staffs.dmi (topaz / emerald / diamond).
- /obj/item/rogueweapon/wand_magi2 (lesser/greater/grand) — one-handed
wands, shield-compatible, lower durability. Icons from copied
icons/obj/items/wands.dmi.
- /datum/status_effect/buff/residual_focus is no longer a no-op stub. It
takes a pool argument on creation (pool = spell's stamina+energy spent
* implement_refund), divides by 20s of duration, and ticks energy back
every second via energy_add(). Hooked from /datum/action/cooldown/spell/
apply_residual_focus() which fires after every successful cast.
- Projectile spell base re-enables the attune_implement call in
ready_projectile() so the implement picks up its color on every shot.
Was commented out in the first session because implements weren't ported.
Outfit:
- Magi 2 Court Magician's r_hand swapped from the existing
/obj/item/rogueweapon/woodstaff/riddle_of_steel/magos to the new
lesser staff implement, so the mage actually spawns with one.
Deferred:
- choose_implement TGUI prompt (Staff vs Wand & Shield) — pilot uses a
fixed lesser staff. The wand subtypes exist so future outfits / shops /
crafting recipes can spawn them.
- Implement crafting recipes (Topaz/Amethyst/Emerald/Sapphire/Quartz/Ruby/
Diamond gem variants in upstream) — out of scope for the pilot.
- Magos/blacksteel/naledi staff variants — kept the existing ES
riddle_of_steel/magos staff intact; the pilot uses the basic implement.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Second Minor aspect for the Grimoire, completing the Arcyne Ward feature set
with the upgrade variants that were deferred when the base ward shipped.
New:
- /datum/magic_aspect/autowardry — Minor aspect granting both upgrade
variants. Upstream uses choice_spells so the player picks one at bind
time; our Grimoire doesn't have choice-spell UI yet, so we grant both
via fixed_spells and rely on the existing arcyne_armor_tier check to
prevent downgrades.
- /datum/action/cooldown/spell/conjure_arcyne_ward_magi2/dragonhide +
matching /obj/item/clothing/.../arcyne_ward_magi2/dragonhide ward item.
300 integrity, ARMOR_DRAGONHIDE (fire-resist armor profile), grants
/datum/status_effect/buff/dragonhide while worn (+1 CON, red outline
filter, TRAIT_FIRE_RESIST). 4 point cost, tier-GREATER.
- /datum/action/cooldown/spell/conjure_arcyne_ward_magi2/crystalhide +
matching ward item. 300 integrity, ARMOR_BRIGANDINE, grants the
crystalhide buff (+1 INT, blue outline filter). On obj_break(), the
ward shatters and throws nearby foes 2 tiles + 20 brute damage.
- ARMOR_DRAGONHIDE and ARMOR_BRIGANDINE armor tables in magi2 _defines
(ES doesn't have these — hand-picked values based on existing
ARMOR_LEATHER / ARMOR_PLATE patterns).
- TRAIT_FIRE_RESIST string trait in magi2 _defines so ADD_TRAIT compiles.
Note: no ES code consumes this trait yet, so dragonhide's fire-damage
reduction is currently inert until upstream's burn-handling logic ports.
Changed:
- /obj/item/clothing/.../arcyne_ward_magi2 (base) now declares
arcyne_armor_tier = ARCYNE_WARD_TIER_BASE. I missed this var when the
base ward shipped; the tier-check logic existed but had no value to
compare against on the base type.
- Base conjure spell cast() now does the upstream-style tier check:
if H.skin_armor already exists and is a higher-tier ward, the cast is
rejected with "A stronger ward already protects me!". Same-or-lower
tier wards get replaced (existing.dismissed = TRUE so cleanup_ward
doesn't start a full cooldown on the displaced ward).
Pilot deviations:
- Both upgrade spells use button_icon_state = "conjure_armor" (the base
ward's icon) as a placeholder — upstream's "conjure_dragonhide" state
doesn't exist in ES's roguespells.dmi yet, and buttons render invisible
if the state isn't found. spell_color still tints the on-cast effects
so casts are visually distinguishable in play.
- choice_spells UI in the Grimoire deferred. Autowardry grants both
upgrade spells via fixed_spells for the pilot. Players who bind it can
cast either ward and swap freely.
Test plan executed: bound Autowardry alongside Pyromancy. Cast Dragonhide
while wearing base ward → base replaced with 300-integrity dragonhide,
+1 CON applied, red outline filter visible. Cast base Arcyne Ward while
wearing dragonhide → "A stronger ward already protects me!" — downgrade
blocked. Cast Crystalhide → swapped from dragonhide to crystalhide cleanly,
+1 INT, blue outline. Broke crystalhide in combat → blast back fired.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fourth Major aspect for the Grimoire. Distinct play style from the elemental
damage trio: telegraphed AOE eruptions, ricocheting stone volleys, and
single-target pin-down.
New spells:
- Gravel Blast — minor staple. 5-projectile spread with per-target damage
falloff (rockshot DR for 1s after first hit) and 2x ricochets off walls.
Only the center stone rolls for blunt crit; outer stones are pure damage.
- Boulder Strike — major projectile. 90 BRUTE on impact, then fragments
into 8 short-range gravel shots in random directions. Self-damage
halved (so the caster surviving their own fragmentation is plausible).
- Emergence — click a tile, ~0.4s telegraph, then stone erupts. Target
tile takes 40 + knockback, adjacent tiles take 15 + knockback, 2x
structural damage to anything in the AOE. Leaves an earthen pillar
with 150 integrity that blocks LOS and movement; auto-deletes after
the 12s cooldown, or shatters into 3 gravel fragments if broken early.
Self-cast safe (caster is unharmed by their own eruption).
- Ensnare — small 3x3 AOE with 0.8s delay, then Immobilize + OffBalance
for 5s on everyone in the area except the caster. Simple animals get
Paralyze instead.
Pilot deviations:
- Dropped intdamfactor + object_damage_multiplier from the gravel and
boulder projectiles. AP declares those vars on /obj/projectile;
Emerald Summit's projectile doesn't. Effects: armor degradation and
2x structure damage scaling on hit are inert. Standard damage,
ricochet (max 2, 80% chance), and fragmentation all still work.
- Dropped ricochet_auto_aim_*, ricochet_incidence_leeway,
ricochet_decay_* — AP-only ricochet refinements that ES doesn't
support. Stones still ricochet, just without the smart aim-assist
after bounce.
- Skipped: Lesser Gravel Blast utility variant, plus the geomancy/
extras (earthen_wall, magicians_rock/stone, meteor_strike) that
aren't core to the aspect's fixed_spells list.
Infrastructure:
- Copied icons/mob/actions/mage_geomancy.dmi and icons/obj/magic_projectiles.dmi
from Azure-Peak.
- Reuses existing ES /obj/effect/temp_visual/kinetic_blast (Emergence AOE
visual) and /obj/effect/temp_visual/ensnare + /ensnare/long. New
types: /obj/structure/earthen_pillar_magi2 +
/obj/effect/temp_visual/trap/emergence_magi2.
Test plan executed: bound Geomancy, all four buttons appeared. Gravel
Blast sprayed a 5-stone spread that ricocheted off walls; second cast
on the same target dropped to reduced damage. Boulder Strike one-shot
a wooden structure and threw 8 fragments on impact. Emergence telegraphed
correctly, knocked back targets in radius 1, left a pillar that blocked
line of sight until cooldown ended. Ensnare locked everyone in the 3x3
for 5 seconds.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fifth Major aspect for the Grimoire. Pure force / gravitational control —
distinct from Geomancy's earth theme. Plus a small UI fix so the Major
Aspects section actually scrolls when it overflows.
Kinesis spells:
- Soulshot — 95-damage piercing beam, up to 4 targets, halved damage
after the first. Hitscan, unstoppable, stopped only by solid objects.
Uses the bloodsteal tracer.
- Greater Arcyne Bolt — 54-damage minor projectile, blunt, the
"Magician's Sling" classic.
- Crush — single-tile telegraphed gravity strike with intdamage_factor 2
(crushes through armor). 60 damage + 1s slowdown on hit.
- Gravity — single-tile knockdown gated by MT_GRAVITY_ADAPTATION 15s
timer. STR-resisted: STR <= 15 takes full 60 damage + Knockdown(5);
STR > 15 takes 15 damage + OffBalance(10). Damage always applies,
CC gated by adaptation.
- Mass Gravity — 3x3 version of Gravity with longer telegraph and
lower per-target damage (40/10 vs 60/15).
Grimoire fix:
- Major Aspects section was using Stack.Item without `grow`, so the
scrollable area had no constraint and the section overflowed past
the window when more than ~4 majors existed. Five majors made
Kinesis the first invisible-without-scroll entry. Both sections now
use Stack.Item grow + Section fill so they split the window's
vertical space and scroll independently.
Adapter notes:
- Dropped guard_deflectable + intdamfactor from projectile defs
(AP-only vars). Damage applies normally; the AP shield-deflection
integration is just absent.
- Reuses ES /obj/effect/temp_visual/gravity and /gravity_trap from
code/modules/spells/spell_types/wizard/invoked_single_target/gravity.dm.
- Reuses ES /obj/effect/temp_visual/kinetic_blast.
- Suffix _magi2 on every new spell/projectile to keep the namespace clean.
- Copied sound/magic/soulshot.ogg, icons/mob/actions/mage_kinesis.dmi,
and icons/mob/actions/mage_shared.dmi from Azure-Peak.
- Added MT_GRAVITY_ADAPTATION + GRAVITY_ADAPTATION_COOLDOWN to magi2
_defines.dm — the magic.dm comment earlier referenced them but they
weren't actually declared.
Test plan executed: bound Kinesis, all five buttons appeared in the
Major Aspects scroll area. Soulshot pierced through two dummies. Greater
Arcyne Bolt + Crush + Gravity (knockdown with adaptation message after
second cast) + Mass Gravity all hit cleanly. Grimoire window now scrolls
both sections independently.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ardry Goal: include every spell from Azure-Peak PR #6406, not just the subsets each aspect was pilot-ported with. Audited each major aspect's spell list against upstream's major_aspects.dm and filled the gaps. Existing aspects (added previously-skipped spells): - Kinesis: drop orphan mass_gravity from aspect (matches upstream — file stays on disk but is unreachable); add gravity_anchor and greater_cleaning. - Pyromancy: +create_campfire. - Fulgurmancy: +light. - Geomancy: +magicians_stone. - Ferramancy: +iron_tempest, +stygian_efflorescence, +arcyne_forge, +readomen. New major aspects: - Augmentation: forcewall, mending + shared soulshot/greater_arcyne_bolt. - Telomancy: arcyne_salvo, energetic_blast, seeker_volley + shared bolt/cleaning. - Battlewardry: battle_ward (with the 5 rune-ward structure subtypes: stun/fire/chill/damage/alarm), arrow_ward, bestow_ward, plus shared forcewall and soulshot/greater_arcyne_bolt. The touch-based Rune Ward spell is deferred — it needs upstream's spell_touch.dm base ported. Bind tracking: replace the "infer bound state from spell presence" heuristic with an explicit list on mind.magi2_bound_aspects. Shared spells across aspects (soulshot, greater_arcyne_bolt) made the heuristic report every aspect with a shared spell as bound the moment one was bound. Unbind now also keeps any spell that another still-bound aspect still wants, so unbinding Telomancy doesn't yank soulshot if Augmentation is still bound. Icon work: pulled from upstream into icons/mob/actions — mage_telomancy.dmi, mage_augmentation.dmi, mage_battlewardry.dmi, mage_conjure.dmi (NEW); refreshed mage_pyromancy/fulgurmancy/geomancy/kinesis/ferramancy.dmi and roguespells.dmi with the upstream supersets that carry the new icon states. rune_wards.dmi copied into icons/roguetown/misc/ for the Battle Ward rune sprites. Adapter compromises (worth noting for later): - spell_guard_check calls in energetic_blast/iron_tempest were dropped — ES has no such proc and the existing arcyne_strike(allow_shield_check = TRUE) path already handles shield blocks. Wave propagation only stops on shield-block. - ricochet_auto_aim_* / ricochet_decay_* / ricochet_incidence_leeway vars on arcyne_salvo's projectile don't exist in ES — kept ricochets_max=2 and ricochet_chance=100, dropped the rest. - PEN_LIGHT/PEN_HEAVY constants don't exist in ES; substituted numeric 10/30. - ward_color var doesn't exist on arcyne_ward_magi2 parent — bestow_ward's outline filter uses GLOW_COLOR_WARD literal. Deferred for follow-up sessions: - Touch-spell base + Rune Ward spell (custom hand attack, intents, radial menu, ash dependency, memorize-allies dialog). - 8 minor aspects: Artifice, Exowardry, Aegiscraft, Displacement, Lesser Kinesis, Lesser Augmentation, Illusion, Hearthcraft. - T4 Mastery system + 9 mastery variants: fireball/greater, fireball/artillery, frozen_mist, greater_thunderstrike, meteor_strike, mass_crush, ascension, blade_dance, arcyne_fortress. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Referenced by greater_cleaning's cleaning_pulse temp_visual and sawblade_volley's grassblade projectile. Was on disk but untracked, so the previous commit would have broken a fresh clone. Synced from local Azure-Peak checkout. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8 deferred minor aspects: Artifice, Exowardry, Aegiscraft, Displacement, Illusion, Lesser Kinesis, Lesser Augmentation, Hearthcraft. Battlewardry completes to 5/5 with Rune Ward (touch-spell base + hand intents + spell_ref weakref on rune structures for live memorize-allies updates). New spell ports: Great Shelter, Blink, Conjure Aegis, Rune Ward. magic_aspect helpers now dispatch on /obj/effect/proc_holder/spell vs /datum/action/cooldown/spell so aspects can grant from either family. Illusion, Lesser Kinesis, Lesser Augmentation reuse existing ES proc_holder spells (invisibility, fetch/repel, the 10-spell utility pool). Bestow Ward: examine readout via integrity_check() flavor tiers + recipient buff-icon status effect; widened to cover every arcyne ward variant. Court Magician pilot now routes Pyromancy bind through _magi2_bind_aspect so the Grimoire shows Unbind from the start. Scroll backplate: every spell across both action systems (legacy proc_holder AND magi2 cooldown) renders with "spell" (idle scroll) and swaps to "spell1" (glowing scroll) when selected. Drops the magi2 5px button-lift cue for the unified scroll-glow indicator. Magi2 base, /spell base, and core touch ChargeHand/on_hand_destroy share the pattern. Legacy touch-spell UX: dropping the arcyne focus dismisses the spell (refunds cooldown + dropmessage), same as re-clicking the action button. Lesser Augmentation icon polish: 11 utility spells use mage_augmentation.dmi states for their action button. Featherfall uses roguespells.dmi/"jump", Leap uses "rune5" as fallbacks (no dedicated sprites upstream). _debug.dm -> zz_debug.dm so alphabetical sort can't put it before _defines.dm. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ck casting - All 8 Mastery variants ported (Greater Fireball, Frozen Mist, Greater Thunderstrike, Meteor Strike, Mass Crush, Ascension, Blade Dance, Arcyne Fortress); each aspect's variants["mastery"] = VARIANT_ADDITIVE, auto-granted to TRAIT_ARCYNE_T4 casters via _magi2_bind_aspect. - Augmentation major aspect now grants the full 12-point buff bag (flattened into fixed_spells; includes Fortitude + Message, unlike Lesser Augmentation). - Magi 2 click-to-target spells now cast on MIDDLE-click (gated in InterceptClickOn), freeing left-click for examine/pickup/interact, matching the legacy spell convention. - Refreshed 11 mage action DMIs from Azure-Peak PR #7285 for the new mastery button icon states. - Re-added spell_scroll_background.dm include (DM had dropped it, blanking the legacy proc_holder spell idle scroll backplate). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Binding-point economy: mind.aspect_resets_used budget (4/rest), Major 4 / Minor 2 / Util 1 reset costs, regen on sleep + new-day tick. Full port of Azure-Peak's staged-attune Grimoire (replaces the MVP Magi2Grimoire): - Backend: core AddSpell dispatches datum spells to Grant(); new mind attune model (major_aspects/minor_aspects + mage_aspect_config + attune_aspect/remove_aspect/ has_aspect/get_spell/ensure_mage_basics/setup_mage_aspects); GLOB.utility_spells. - /datum/aspect_picker (863-line staged picker) adapted to our fork (tgui_always_state, cmp_list_name_asc, TRAIT_ARCYNE_T1..T4). - 12-file GrimoireAspectPicker TGUI suite + AspectPicker.scss + grimoire theme (+ _azure_base.scss mixin partial). - Grimoire item opens the picker; pilot Court Mage spawns config-driven (2 major / 3 minor / 9 util, mastery, ward); legacy _magi2_bind helpers are now thin wrappers over the attune model; Magi2Grimoire retired. - Fix tgui Window theme precedence to upstream's 'theme || config.window.theme' so an explicit interface theme (grimoire parchment) wins over the user tgui pref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Class migration (config-driven): - advclass base gets mage_aspect_config + mage_post_spells; equipme() runs _magi2_setup_caster (config + Grimoire-to-satchel + staff + Prestidigitation, strips legacy tome/amethyst) and skips legacy spellpoints when config is set. - Migrated: Court Magician (T4 2/3/9 mastery), Sorcerer (T3 1/2/6) and its subtypes Spellblade/Arcane Trickster (inherit, configs to refine), Spellsinger (T2 hybrid 0/2/6 + Greater Arcyne Bolt), Magos Thrall trio: Magician's Associate (1/2/6), Alchemist Associate + Magician's Apprentice (1/1/6). - Grimoire opens in setup-mode while the mage has no aspects bound, edit-mode after. - Court Magician starts with the grand staff (magos model); Magos Thralls get the lesser staff; setup_caster skips a duplicate staff if one is already present. - Removed the redundant 'Court Magician (Magi 2 Pilot)' lobby slot. Pointbuy + utilities: - Augmentation (12-pt) and Lesser Augmentation (4-pt, excl. Fortitude) now use real pointbuy_spells; mark_aspect_spell tags proc_holder picks for the picker. - Expanded GLOB.utility_spells to the full spec roster. Touch-cantrip backplate: per-spell active_background_icon_state var so Prestidigitation/Orison (scroll-shaped icons) use the neutral bg_spell frame in idle + selected states instead of doubling the scroll. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… Sorcerer's) Spellblade and Arcane Trickster are /datum/advclass/mage subtypes and were inheriting the Sorcerer's 1/2/6 config, spawning as full casters. Override both: - Spellblade: util-only 0 major / 0 minor / 4 utilities, ward (per spec). - Arcane Trickster: 0 major / 1 minor / 3 utilities, NO ward (it trades away mage armor / has no TRAIT_MAGEARMOR). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
not a draft so I'll assume some feedback welcome! There is a LOT of classes not properly integrated thusfar. This is only after a very brief test. Theres a LOT of classes with magic access so. I'd do a big pass on those. I'm sure theres several more. |
|
mending does not seem to work whatsoever unless im being stupid |
|
Autowardry does not seem to grant the spells it speaks of...or im just. Not sure how it works. Could be being dumb? Idk. |
|
Arcyne Potential must have been reworked in some capacity on azure I've no doubt. Seems to still be using the old magic system. Both the ritous ritual and the ritous spell need to be accounted for in some way...the ritual itself is unique to are code though. I hope its been noticed but. Like a LOT of the older spells are missing icons. Clerical spells and such. |
appreciate the bug reports, most of this stuff has been fixed locally, still working on some other kinks. |
Retires legacy spellpoints in favor of the magi2 aspect system across every remaining caster, plus carries over the prior uncommitted Group A / Arcyne Potential / orphan-cleanup batches. Group B (direct adjust_spellpoints callers): Lich (T3), Heartfelt Magos (T4, = Court Magician), Unbound Deathknight / White Cheesemaker / Loudmouth / Witch Old Magick (T2), Witch Mystagogue (T1) migrated via deferred _magi2_setup_caster. Heartfelt Lord and Prisoner stripped to non-casters per design. Removed 3 dead AGE_OLD spellpoint orphans (nobility Lord/Hand, Heartfelt Hand). Remaining callers (Rituos rite, Spell Points granter book) left out of scope. Implement-staff masking fix: migrated casters whose outfit handed a plain woodstaff never received the magi2 implement (no residual-focus refund). Convention T2=lesser / T3=greater / T4=grand applied — Rogue Mage, Necromancer, Hedge Mage, Sorcerer → greater; Failed Apprentice → lesser. Warscholar (naledi) and Heartfelt Lord (diamond, now non-caster) left as flavor. Witch spell sourcing: Aerosolize moved into the Lesser Kinesis aspect; Guidance already lives in Augmentation/Lesser Augmentation; both free grants removed from the Old Magick witch (now Grimoire-only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@benj8560 good for some more tests on your end if you don't mind |
Witch staff/Grimoire delivery: - Add a grant_staff flag to _magi2_setup_caster; witches now get the Grimoire + ward/config but no implement staff (they cast with a magebag/herbs). - Fix the staff turf-spawn: build the implement in nullspace and put_in_hands, dropping at feet only as a last resort. Removes the spawn "thud" and stops the staff being silently qdel'd when hands are full (affects every grant_items=TRUE caster, not just witches). Persistent bonus-slot fix (review finding #2/#3): - setup_mage_aspects rebuilds mage_aspect_config wholesale, which discarded any slot/utility bonus added before it. Deferred-setup classes (Witch, Loudmouth, White Cheesemaker, Lich, Deathknight) set their config AFTER virtues run, so Arcyne Potential's +3 utilities were clobbered. - Track bonuses on mind.magi2_bonus_utilities / magi2_bonus_minor; setup folds them in every rebuild, so they count whether granted before or after the class config lands (no double-count — stale direct-adds are discarded with the replaced config). - Arcyne Potential virtue uses the persistent utility bonus and only stashes a retrievable Grimoire when the recipient doesn't already carry/own one. - Hedge Mage's bonus minor slot now uses magi2_add_bonus_minor directly instead of a fragile addtimer race. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
just woke up I'll have pass of just about everything my wacky head can think of now |
|
Soulshot lacks a projectile icon now. It had one in the prior version. Edit: Erm. Only sometimes? not sure whats going on there. |
this is still a issue. |
and finally so is this. Believe that concludes my testing. I checked just about every class in the game I could think of. I have balance concerns. Though. I think those can wait for now. |
Naledian warstaff is now a greater implement (27.5% residual-focus refund +
elemental attune glow), name capitalized to 'Naledian Warstaff' and attunes
'of <school>' on cast. Dropped its old gem cast-time-reduction buff — its magic
now comes solely from the magi2 implement. Implement vars live in the magi2
implements folder (not magic_staffs.dm) so the IMPLEMENT_* macros are in scope.
Court Magician and Heartfelt Magos each get a named grand-staff subtype
('Staff of the Court Magos' / 'The Staff of the Heartfelt Magos', same courtstaff
model) that attunes by school, scoped so the generic grand staff is unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Magi2 spells occupy mob.click_intercept independently of the legacy ranged_ability/mmb_intent slot. Mutual exclusion was only wired one way (magi2 on_activation cleared a legacy ability), so arming a legacy spell while a magi2 spell was armed left both selected, producing janky double-attempt behavior. add_ranged_ability() now deselects any active magi2 spell before claiming the slot, mirroring the reverse clearing — all selection transitions now enforce a single active spell. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Binding Autowardry granted no Dragonhide/Crystalhide buttons — only the base ward. Root cause: core RemoveSpell() matches by istype(), and the upgrade wards are subtypes of the base ward, so ensure_mage_basics' RemoveSpell(base_ward) deleted all three; a later pass re-added the base. Now removes the exact base-ward instance instead (both the has-variant and no-ward branches). Ward polish: - Per-ward examine phrase (ward_examine_phrase) so Dragonhide/Crystalhide read distinctly on the wearer instead of the generic 'shimmering arcyne ward'. - Dragonhide/Crystalhide use their real icons (conjure_dragonhide / conjure_crystalhide from mage_conjure.dmi) instead of the conjure_armor placeholder. - Removed the % integrity counter overlaid on ward action buttons. - Ascension icon swapped from borrowed 'stoneskin' to roguespells.dmi 'convergence'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@benj8560 one final pass through |





About The Pull Request
Testing Evidence
Why It's Good For The Game
Magic re-work.