# Dwarf arts: Kanonenschuss, Mörsergranate, Eingraben ## Requests (2026-10-25 / 2026-10-26) - Kanonenschuss (art 0x43f, `gui_spells_kanone`): "remove the knockback, instead it can be just a stun" (chosen: no push, only a stun); the range increased for the wider view; the stun particles not on targets that avoided the stun. The game's stun resistance stays (2026-10-26: "we dont ignore anything, the skill is fine for now"). - Mörsergranate (art 0x43e, `gui_spells_moerser`): "the radius of the explosion should increase every 5 skill levels". ## Measured - The cannon's code is the combat move function `0x5145d0`, case 0x43f. Its ray: `cmp edi,19h` at `0x517389`, 25 steps of 40 = 1000 range. Hostile targets get `cEvent_creature` 5 from `0x51768d`. - Stun chance `0x4fc810(level, creature)` = level × 5 / creature level × 100 (`UI_STAT_STUNNCHANCE`). On a successful roll: `0x5936e0(MOTION_HIT_A 0xaf, 3.0, 0.25)` at `0x517709` (pushes from `0x5176f8`), then the stun order `cEvent_creature` 3 with +0x14 = 100, sent by `call [eax+18h]` at `0x5177aa` (returns to `0x5177ad`). - The stun *is* the knockback. `cCreature::ReceiveOrder` `0x52e400` case 3: first the resistance `vf24(0x50)` (`0x55d0a0`: flag +0x4f0 & 0x1000 resists 2 in 3, effect 0x35d a percentage); otherwise push left +0x24d = 100, flag +0x200 |= 0x20000, attacker +0x259, ResetToIdle. `cCreature::Update` branch `0x594c82` moves a creature with that flag away from the attacker and lowers the push left by each step (`0x594deb`), measured about 100 units in 0.3 s; at 0 the flag is cleared. - The impact effect `TYPE_FX_DWARF_SHOOT_IMPACT` (0x47f) is created on every hit target by `CreateAt` at `0x517855` (target in ebx), resisted or not. - Mörsergranate: the grenade (type 0x3fa) lands and creates `TYPE_FX_GRENADEIMPACT` (0x3fc), which gathers its targets with `cObjectManager 0x5ff820` (called from `0x5ab400` at `0x5ab63d`): a creature is hit when its distance is below the impact's size (150, type table `0x428bb0`) plus its own type's size. ## The mod (`src/mods/dwarfarts.cpp`) - The HIT_A motion is jumped over (`0x5176f8`: `jmp` past the call). - The push step at `0x594deb` calls `PushStepThunk`: creatures the cannon stunned (order 3 from `0x5177ad`, kept with an end time) move by 0 and keep their push left above 0 for 1.5 s, so they stand still, then carry on. - The impact effect is created only on targets in the stun state. - The ray gets 45 steps (1800); confirmed to reach across the widened view. - The impact's gather call gets a radius of size × (1 + 0.1 × (level / 5)), with the owner's Mörsergranate level (`game::ArtLevel`). The growth 0.1 per step is our choice; not yet seen in the game (Borf's level is 2). ## Mörsergranate impact marker (asked 2026-10-25, done 2026-10-26) "Show exactly where the grenade will land and its explosion radius, a red circle with low alpha on the ground." Measured: - The landing point is decided at launch (`0x5145d0` case 0x43e): the target's position, or, closer than 200, a point 200 from the dwarf with a +-15 jitter. It goes to the new grenade (type 0x3fa) in cEventFX 0x96, constructed at `0x5169fa` (`cEventFX::Construct` 0x41e0a0, thiscall, ret 0x44: code, owner id, target id, position area / x / y / level, ...). - The impact gathers its targets at exactly that point, on several frames. The grenade object stays about 30 s after the explosion. - Ground -> screen: `0x622700` (world units -> world pixels, the isometric plane), then (pixel - camera +0x14/+0x18) / zoom +0xb60 + screen centre (floating texts 0x41e546). Checked with the hero: the screen centre. - The world view draws the ground (`0x62d530`, ground quad list), then the objects. Mod: the construct call notes grenade, owner, landing point and radius (`ImpactRadius`, shared with the hit check); after `0x62d530` a disc (alpha 0x40) with a 2 px rim (alpha 0xa0) is drawn there, without depth test, until the impact of that owner's grenade at that point gathers its targets. Tooltip (2026-10-26, "the skills tooltip does not show the current radius"): a game::Art tooltip filter adds "Explosionsradius: N" after the last value line of each level section (current level, and the next level's in the skill book), in that line's colour, from the same `ImpactRadiusAt(level)` as the hit check and the marker. ## Grenade impact crash (2026-10-26) Crashes at sacred.exe+0x361b54 (2026-10-26 00:12 and 22:39) in the particle drawer `0x7613e0`, called by `cParticleSystem_grenadeimpact::Render` (vf04 0x7b66b0): colour = table +0x20d0 at row (+0x3d) * 256 + life (+0x2c), and a particle had a life of over 500000. Guard (`mods/guards.cpp`): live particles with life >= 256 or row >= 3 are switched off before drawing. Logged in the next session: 5 particles, all row 3, life 255, garbage sizes (e.g. 190555, 4.6e-40): the explosion emits into a fourth row whose emitter parameters it never sets (vf10 0x7b6980 fills three rows at +0x30d0). No crash since. ## Eingraben: the dwarf's escape (request 2026-10-28) "I want it to be the Dwarfs option to Teleport/Escape. The Skill will cast a protective barrier around the Dwarf (current visual effect), clear him of all movement impending effects and then teleport to the position marked by the cursor (if possible). The Teleport is visually shown by the dwarf going underground and coming out again at target position." Range: the visible area; the barrier is the skill's current defensive bonus for 3 s; digging in and out 1 s each; "he should be able to see the target location", compared to the battlemage's Phasenverschiebung; the original lock in place goes. Found in the code: - Spells run in `cCreature::CastSpell` (0x5222c0, thiscall (cast), ret 4; cast +0x08 / +0x0c the ground point, +0x20 the spell id). Eingraben is spell 0x39 (`gui_moves_eingraben`, launch type 5 around the caster). - Case 0x39 creates object type 0x480 at the caster with cEventFX 0x9d (owner and target the caster) and plays sound 0x840: the barrier, which follows the dwarf. - Case 1 (Phasenverschiebung, launch type 3) walks from the caster towards the target in steps of 10 while the cell is passable (`cWorld::IsPassable` 0x636b60) and keeps the last point a creature can stand on (`cWorld::CanStand` 0x636eb0); a wall or cliff in the way ends the walk. Logged (2026-10-28): the barrier adds, all with art 0x39, kind 0x3a value 0 from its particles (0x7b9602), the lock in place (the dwarf was stuck 2-3 s), and kind 2 value 0.5 plus kinds 7..0xa value 30 from its update (0x5b5862..), the defensive bonus (resistances up while it lasts). Mod (`mods/dwarfarts.cpp`): Eingraben's definition +0x3c becomes 3 (a point); after the original case the dwarf is teleported to the end of Phasenverschiebung's walk towards the point (the cursor keeps it within the visible area). The effect hook (`aurarecast.cpp`, `KeepEffect`) drops kind 0x3a for art 0x39 and cuts the art's other effects to 3 s. Confirmed 2026-10-28: aimed, stops before walls and cliffs, casts at once at the screen edge, the barrier moves with him, no lock, bonus for 3 s. Open: clearing slows and stuns, 1 s digging in and out.