Files
sacred_k/docs/chain-lightning.md
T
khallaandClaude Opus 5.5 16c2844400 Auto-attack: next target, melee/ranged rule after arts; chain range = cast range
- autoattack: when the target dies, the nearest enemy within close range
  (contact distance + a step) takes over; after an art the hero auto-attacks
  if the art was melee (walked up / cast close by), or ranged with a ranged
  weapon, or ranged with an enemy already in melee range; measured action
  codes and weapon classes in game/engine/orders.h
- chainlightning: jumps reach Blitzschlag's cast range (20 ray segments of
  30, the art code's hit check) via the shared NearestHostile search
- guards: burning bone render hook had the wrong signature (device plus a
  4x4 matrix by value, ret 0x44) and corrupted the stack when fire arrows hit
- crash reports follow the ebp chain before the raw stack scan

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 09:16:47 +02:00

3.6 KiB
Raw Blame History

Chain lightning (src/mods/chainlightning.cpp)

Rebalancing request (user, 2026-10-15) for the Battlemage's Blitzschlag (art 0x21, icon GUI_SPELL_BLITZSCHLAG; 0x0e has the same icon):

  • hits per chain N = 3 + level / 5 (the first target included);
  • chains B = 1 + level / 10, all starting at the first target ("hero -> T1 -> A1 -> A2 ..., T1 -> B1 -> B2 ...");
  • a chain that finds no new target hits the previous one again (a lone enemy takes N hits).

How the game casts it (measured with an object / hit log, 2026-10-15)

About 0.3 s after the cast order (0x105, art 0x21) the caster's art code (0x5222c0, Blitzschlag part around 0x526c91..0x527102):

  • builds the visual path: up to 20 segment objects of type 799 (0x31f), each created linked to the previous one (CreateAt third argument), ids kept at caster +0x263;
  • creates the bolt object, type 872 (0x368), at the target, linked to the last segment, copies the caster's +0x2b5 to its +0x39, and sends it a cEventFX 0x60 (0x41e0a0: [2] caster id, [3] target id, [4..7] position, [8..13] two strings, [14] damage, [16..29] damage block);
  • plays sound 0x7db.

The bolt object then sends the hit (cEvent_creature 5 from 0x5b52d3, attacker = caster, +0x14 = 0x368, +0x18 = the bolt object's id).

The game's own chain (measured 2026-10-15)

The game's Blitzschlag already chains. The 0x60 event's [14] is the number of hits left in the chain (3 at level 1, from the art level, 0x4d24f0). After its hit, a bolt with hits left looks for the next target in its update (cObjectManager::FindJumpTarget 0x6005d0, called at 0x5b469f with (position, 100000 = squared range, caster, current target): the nearest creature, excluding only caster and current target), builds the arc (segment objects 799 created at 0x5b4aff, each set up with a cEventFX), and creates the next bolt at the target (0x5b4d7c) with [14] - 1. No target: the chain ends (a lone enemy is hit once). Two enemies: it bounces between them.

The mod

  • The first bolt of a hero's cast (the first 0x60 bolt setup after the cast order) gets [14] = N = 3 + level / 5 (level = cooldown entry level + bonus levels of the art cast).
  • The jump search call (0x5b469f) goes to NextTarget (thunk passing the bolt in ebp): for bolts of our chains (bolt id -> chain, filled when a bolt of a chain is set up at the target the chain said it jumps to) the next target is the nearest enemy within the game's range not hit by this chain yet, else another enemy, else the current one again (a lone enemy takes all N hits). Other bolts use the game's search.
  • B = 1 + level / 10 chains: while the first bolt is set up, B - 1 more bolts are created at the first jump targets of the other chains (copies of the event with [14] = N - 1); their first hop has no arc.

First version (bolts copied for every jump, not using the game's chain) replaced the same day: it doubled the jumps.

Jump range = cast range (2026-10-16)

User request: a jump reaches as far as a cast. Blitzschlag's hit check is in the art code (cCreature art switch 0x5222c0, case 0x21): the ray from the caster towards the target is laid out in segments of 30, at most 20, each on passable ground (0x636b60); the bolt is created on the target only if the ray reaches it, otherwise the visible ray just stops. So the cast range is 20 × 30 = 600. CastRange() in chainlightning.cpp returns it for every jump (also the first hop of extra chains), instead of the game's jump search radius (squared 100000, about 316). It is the one place to make the range dynamic later. Jumps do not check the ground in between.