Skip to content

Monster Graphics Renderer

HoraceAndTheSpider edited this page Jul 21, 2026 · 3 revisions

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:

  1. Identify the dungeon view cell and one of its four sub-positions occupied by the monster.
  2. Decide whether that sub-position is visible from the player's current view.
  3. Convert the visible location into one of six apparent-distance slots and obtain its base screen X and Y position.
  4. Convert the monster's facing direction into front, side, back, or mirrored-side artwork.
  5. Select pictures inside the monster's .gfx data using .offsets tables.
  6. Draw one or more components using the monster's .positions, .heights, or packed .layout data.
  7. 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_CompositeBitmap adds Player_Data+$0008 to d5 before calculating its destination. It therefore works in the full screen's coordinate basis.
  • Draw_Monster_16PixelStrip obtains its destination from the pointer at -$0008(a3) and uses d4/d5 relative 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 .layout data;
  • a four-byte pointer to the monster's .gfx block;
  • 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 .positions and parsed .layout fields 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 .colours grade selections and palette data to be edited with strict palette-index validation.

See also:

Clone this wiki locally