- 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>
70 lines
3.6 KiB
Markdown
70 lines
3.6 KiB
Markdown
# 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.
|