Monster Graphics Renderer - HoraceAndTheSpider/Bloodwych-68k GitHub Wiki
Monster graphics renderer
This page describes how Bloodwych positions and assembles the large monsters numbered $64 and above. These monsters use raw Atari ST four-plane pictures, component-layout tables, facing and distance lookups, and colour-grade selections.
The labels and file positions below refer to Bloodwych SPS 439. Extracted filenames follow the Extracted Data File Types convention.
Rendering in plain English
The game does not simply choose one monster picture and paste it in the middle of the viewport. It performs the following steps:
- Identify the dungeon view cell and one of its four sub-positions occupied by the monster.
- Decide whether that sub-position is visible from the player's current view.
- Convert the visible location into one of six apparent-distance slots and obtain its base screen X and Y position.
- Convert the monster's facing direction into front, side, back, or mirrored-side artwork.
- Select pictures inside the monster's
.gfxdata using.offsetstables. - Draw one or more components using the monster's
.positions,.heights, or packed.layoutdata. - Apply the palette selected for the monster's colour grade.
This distinction explains why some data belongs to the shared renderer in gfx-data/, while body, eye, arm, claw, and other component data belongs beside one particular monster in monsters/.
Shared view and renderer tables
These tables affect several monster types and should normally remain fixed. Changing them can move or hide every monster which uses the same view geometry.
| Source label | BW439 file position | Extracted file | Purpose |
|---|---|---|---|
Monster_SubPosition_DepthAdjustments |
$95B2 |
gfx-data/Monster_SubPosition_DepthAdjustments.positions |
Adjusts the selected one-of-four sub-position before the position is converted into a graphics-distance slot. |
Monster_ViewCell_DepthSlots |
$95B7 |
gfx-data/Monster_ViewCell_DepthSlots.lookup |
Assigns each potentially visible view cell a base depth slot. $FF means that position cannot be seen. |
Monster_Depth_GfxSlots |
$95CA |
gfx-data/Monster_Depth_GfxSlots.lookup |
Converts the intermediate depth slot into one of six apparent monster distances. |
Monster_GfxSlot_YPositions |
$95D2 |
gfx-data/Monster_GfxSlot_Y.positions |
Gives each apparent distance its base vertical position in the dungeon viewport. |
Monster_ViewCell_SubPosition_XPositions |
$18700 |
gfx-data/Monster_ViewCell_SubPosition_X.positions |
Supplies the horizontal position for each view-cell and sub-position combination. This is the table which produces the non-mirrored extreme-left/extreme-right cases observed in the game. |
Monster_RenderFlags_LookupTable |
$984C |
gfx-data/Monster_RenderFlags.lookup |
Converts the low five bits of a monster's render state into two flags. Component renderers test these flags to select the normal or alternate left and right arm/claw pictures. |
Monster_Facing_GfxVariants_LookupTable |
$991A |
gfx-data/Monster_Facing_GfxVariants.lookup |
Maps four facing directions to front, side, back, or mirrored-side graphic variants. The high bit marks a mirrored result. |
Monster_DistanceGroups_LookupTable |
$A1B2 |
gfx-data/Monster_DistanceGroups.lookup |
Maps six visible-distance slots to four stored size groups: 0,0,1,1,2,3. Crab, Dragon, Behemoth, and Entropy therefore reuse artwork at the first two pairs of distances. |
The first two tables are five and 19 bytes long respectively. They are genuine odd-sized byte tables and are immediately contiguous with the following renderer data; no padding byte exists between them in the original executable. Devpac pads an odd-length INCBIN, so a byte-identical source build must emit these two files as exact dc.b data or otherwise combine them without introducing alignment bytes.
The Prepare_CentredMonster_ScreenPosition entry point forces the centre sub-position before using the general Prepare_Monster_ScreenPosition routine. Summons are the important exception: they retain humanoid-style front/back sub-position placement rather than always occupying the centre of a dungeon square.
Two source drawing coordinate bases
The renderer uses two different low-level drawing routines. This matters when interpreting the Y positions in monster-specific tables:
Draw_Monster_CompositeBitmapaddsPlayer_Data+$0008tod5before calculating its destination. It therefore works in the full screen's coordinate basis.Draw_Monster_16PixelStripobtains its destination from the pointer at-$0008(a3)and usesd4/d5relative to that pre-positioned drawing area.
The difference is part of the original display layout, not a Behemoth, Crab, Dragon, or Entropy correction table. A reconstruction must retain the drawing routine associated with each component before treating a stored Y value as a literal viewport coordinate.
Summon
Summons use six body distances with front, side, and back variants. At the four nearest distances they can also draw two independently selected arm components.
| Extracted file | Purpose |
|---|---|
Summon_Body.layout |
Six body vertical adjustments followed by six body heights. Its final bytes also overlap the beginning of the packed body-width data used by the original renderer, so the adjacent tables must remain contiguous when recreating the native structure. |
Summon_ArmVariants.lookup |
Maps facing direction to an arm variant and mirror flag. It also contributes bytes to the packed body-width data. |
Summon_Arms.heights |
Arm heights; these bytes complete the packed body-width table which begins in the preceding structures. |
Summon_PrimaryArm.positions |
Packed X/Y positions for the first arm by distance, facing, and animation state. $FFFF means that no component should be drawn. |
Summon_SecondaryArm.positions |
Equivalent positions for the second arm. |
Summon.offsets |
Eighteen body-picture offsets: six distances by three facing variants. |
Summon_Arms.offsets |
Twelve arm-picture offsets: four distances by three arm variants. |
The apparent overlap between the first three tables is genuine. It is a compact original-game representation, not an extraction error.
Behemoth and Entropy
Behemoth and Entropy use the same renderer and the same 98-byte descriptor shape:
- 62 bytes of packed
.layoutdata; - a four-byte pointer to the monster's
.gfxblock; - 24 bytes containing twelve body-picture offsets;
- eight bytes containing four limb or claw-picture offsets.
The source build should generate the four-byte graphic pointer from a label rather than treat it as editable raw data.
| Extracted file | Purpose |
|---|---|
Entropy_LimbMirroring.flags |
Enables the Entropy-specific reversal of limb mirroring in the shared renderer. |
Entropy.layout |
Packed body and limb dimensions, positions, spacing, and mirroring rules. |
Entropy.offsets |
Twelve body-picture offsets followed by four limb-picture offsets. |
Behemoth_LimbMirroring.flags |
Corresponding Behemoth mirroring control; its original value differs from Entropy. |
Behemoth.layout |
Packed body and claw dimensions, positions, spacing, and mirroring rules. |
Behemoth.offsets |
Twelve body-picture offsets. |
Behemoth_Claws.offsets |
Four claw-picture offsets. |
The Crab's reused Behemoth claws
The closest front-facing Crab loads Behemoth_Claws.offsets and uses pictures stored at GFX_Behemoth + $11B8. The reused graphic span ends at $13E8, so it is $230 bytes long. Relative to a separate copy, its four offsets would be:
$0000
$00A8
$0150
$01C0
The Super App can treat this as a copy-on-write alias. An unchanged project continues to use the Behemoth data. If the user supplies different Crab claws, a source/relocation build can materialise Crab_FrontClaws.gfx, generate normalised offsets, and redirect the Crab renderer's graphic and offset-table references.
Dragons
Large and small Dragons share Dragon.gfx and the same component-offset tables. They have different initial body placement tables.
| Extracted file | Purpose |
|---|---|
Dragon_Composite_XY.positions |
Four X and four Y positions for the small Dragon followed by four X and four Y positions for the large Dragon. |
Dragon_Side_X.positions |
Additional horizontal movement for side-facing Dragons by size group and visible side. |
Dragon_Claws.layout |
Claw widths, heights, X positions, and Y positions across distance, facing, and animation variants. |
Dragon_Body.layout |
Body widths and heights plus the facing-to-variant and mirroring lookup. |
Dragon_MirroredHalf_X.positions |
Horizontal spacing used when a symmetrical body is completed by drawing its mirrored second half. |
Dragon.offsets |
Fifteen body-picture offsets followed by twelve claw-picture offsets. |
Why a Small Dragon uses the next graphic size
Draw_LittleDragon sets d2 to 1; Draw_BigDragon sets it to 0. Both routines first use their own X/Y placement table at the current Monster_DistanceGroups_LookupTable result. They then execute add.w d2,d1 before selecting body and claw pictures.
Consequently, with g as the current stored distance group:
Large Dragon: placement group g, graphic group g
Small Dragon: placement group g, graphic group g + 1
Thus a closest Small Dragon deliberately uses the same shared body picture as a Large Dragon one stored graphic group farther away, while remaining positioned for the closest space. The increment also reaches the original routine's static-claw cutoff sooner for Small Dragons.
The X/Y and component-layout values are useful advanced editing controls. The picture offsets should normally be regenerated from imported artwork.
Crab
The Crab is assembled from more small rendering paths than the other centred monsters. Its code selects different body, face, front-claw, back-claw, and side components according to distance, facing, and animation flags.
| Extracted file | Purpose |
|---|---|
Crab_Front.layout |
Placement and height values used when the Crab faces the player, including placement for the reused Behemoth claws. |
Crab_SideNear.layout |
Dimensions and positions for the nearest side-facing components. |
Crab_SideFar.layout |
Dimensions and positions for the more distant side-facing components. |
Crab_FaceAndSideClaw.layout |
Face heights and Y positions together with the closest side-claw X positions. |
Crab_BackClaw.layout |
Dimensions and positions for the rear-facing claws. |
Crab_Body.layout |
Body X/Y positions, widths, heights, and mirrored-half placement. |
Crab.offsets |
Offsets for the body, face, front, back, and side components. |
Crab.gfx and CrabClaw.gfx are separate extracted files but form one logical address space in the original renderer. Offset $0458 is exactly the boundary between them. A decoder must therefore be able to resolve later Crab.offsets entries into CrabClaw.gfx.
Crab component order and packed tables
Draw_Crab first draws the two composite body halves. It then calls the component routine, which draws the face or side piece before any relevant claws. The visible order is therefore:
body half → mirrored body half → face / side piece → claws
At the two nearest stored groups, a front-facing Crab draws a face from Crab.gfx and two claws reused from the Behemoth graphic block. The four front-claw Y values in Crab_Front.layout are selected by group and animation state. The following four X values are selected separately by group and physical left/right side. These indexes must not be combined: animation changes a claw's picture and Y position, but does not exchange its left/right X location.
The packed Crab companion files can be read as follows:
| Extracted file | Packed fields used by the original routine |
|---|---|
Crab_Body.layout |
Two body-half X positions for each of four groups, followed by four Y positions, four widths, and four heights. |
Crab_Front.layout |
Far-front component data first; then nearest-front claw Y positions by group/animation, claw X positions by group/side, and two claw heights. |
Crab_FaceAndSideClaw.layout |
Two face heights, two face Y positions, then the closest side-piece X positions. The closest side piece itself has a hard-coded eight-pixel height in the code. |
Crab_BackClaw.layout |
Back-claw X positions by group/side, followed by Y positions and heights. |
Crab_SideNear.layout |
Near-side component heights, X positions by group/side/animation, and Y positions by group/animation. |
Crab_SideFar.layout |
Static far-side component heights, X positions by group/side, and Y positions. |
For back-facing animation, the original routine mirrors the first claw and leaves the second unmirrored. For side-facing animation, one facing direction uses a mirrored component and tests render-flag bit 1; the opposite direction uses an unmirrored component and tests bit 0. These are code-path rules, rather than additional data tables. At the farther stored groups the Crab uses static components and does not animate claws.
What should be editable?
- Keep the shared
gfx-data/view, facing, distance, and animation mappings fixed in the normal editor. They should still be extracted and displayed for diagnosis. - Expose monster-specific
.positionsand parsed.layoutfields as advanced controls with an immediate viewport preview. - Recalculate
.offsets,.heights, and other dimensions when importing templates, then validate them against the original structure. - Allow
.coloursgrade selections and palette data to be edited with strict palette-index validation.
See also: