Vectorising an arcade game: VectorKong is Completed

Started by 10yard, April 16, 2022, 01:05:36 PM

Previous topic - Next topic


Part 1: Vectorising the DK background (see below)
Part 2: Vectorising the DK character/fonts ( link to post )
Part 3a: Vectorising some of the DK sprites ( link to post )
Part 3b: Vectorising Kong's sprites ( link to post )
Part 3c: Vectorising Jumpman's sprites ( link to post )
Part 4: Adding a title screen and finishing things off ( link to post )

I'm having a go at vectorising Donkey Kong for MAME.  I'll be hacking the original game,  suppressing the normal pixel output completely and rendering high resolution vectors to the screen.  The techniques i'll be  using could be applied to other classic arcade games.  I'm choosing Donkey Kong because i'm very familiar with the disassembled code and mechanics of the game.
I'm documenting my journey and hopefully you may find it interesting and follow along. 

I took inspiration from this blog which considers if a Donkey Kong port would be feasible on the Vectrex.

My plan is to use the simple vector drawing capabilities of MAME LUA scripting language (available in MAME from version 0.196 to current). 

There are some limitations.  All vectors must be drawn out every frame and there are 60 frames per second.  Optimisation is key and i'll have to do some calculations before I get carried away.  I'll work out how many vectors can be safely drawn each frame without compromising the emulation speed.

First we'll need to write some LUA code to instantiate the emulated screen device in MAME so we can draw to it.

scr = manager.machine.screens[":screen"]

Then we'll need a simple function to draw a vector.  This function draws a line from x,y position to another x,y position.  Anything we draw,  including other shapes,  will me made up of vectors and will use this function.

function vector(y1, x1, y2, x2)
scr:draw_line(y1+wobble(), x1+wobble(), y2+wobble(), x2+wobble(), intensity())
vector_count = vector_count + 1

We'll keep tally of the number of vectors we draw.
Notice the funky "wobble" and "intensity" options.  These are used to make vectors appear less boring on screen.

We can make a polyline function to draw multiple vectors that are chained together like etch-a-sketch

function polyline(data)
-- draw multiple chained lines from a table of x, y data points
local _y, _x
for _i=1, #data, 2 do
if _y and _x then vector(data[_i], data[_i+1], _y, _x) end
_y, _x =data[_i], data[_i+1]

We can make a function to draw a box by chaining 4 lines with our polyline function.  We can specify the height and width of the box.

function box(y, x, h, w)
-- draw a simple box at given position with height and width

We can make a function to draw a circle by chaining lots of vectors together.  I ran with 20 vectors per circle.

function circle(y, x, r)
-- draw a 20 segment circle at given position with radius
local _save_segy, _save_segx
for _segment=0, 360, 18 do
local _angle = _segment * (math.pi / 180)
local _segy, _segx = y + r * math.sin(_angle), x + r * math.cos(_angle)
if _save_segy then vector(_save_segy, _save_segx, _segy, _segx) end
_save_segy, _save_segx = _segy, _segx

We now have our 4 drawing functions so lets try to max out MAME to see how many vectors we can draw per frame.
I made a function for testing the limits.  This function will cycle through 4 different drawing tests (lines, polylines, boxes and circles).  We can increase the limit gradually and keep a check on MAME average emulation speed (Function key F11).

function debug_limits(limit)
local _rnd, _ins = math.random, table.insert
local _cycle = math.floor(scr:frame_number() % 720 / 180)  -- cycle through the 4 tests, each 3 seconds long
if _cycle == 0 then
for _=1, limit do vector(256, 224, _rnd(248), _rnd(224)) end  -- single vectors
elseif _cycle == 1 then
_d={}; for _=0,limit do _ins(_d,_rnd(256)); _ins(_d,_rnd(224)) end; polyline(_d)  -- polylines
elseif _cycle == 2 then
for _=1, limit/20 do circle(_rnd(200)+24, _rnd(176)+24, _rnd(16)+8) end  -- circles
for _=1, limit / 4 do box(_rnd(216), _rnd(200), _rnd(32)+8, _rnd(24)+8) end  -- boxes

Results show we can safely draw 1000 vectors per frame when tested in MAME versions 0.196 and 0.242 running on Windows 10 at 1920x1080. Reducing the resolution increases the limit.  1200 per frame is ok too but let's keep it safely inside the limits.  I'll also test on Raspberry Pi 4 later.

This video shows our tests running on top of Donkey Kong attract mode.

I think 1000 vectors should be sufficient.  I'll use vectors sparingly and keep tally of the numbers as I progress.

Let's start by building background graphics for the girders stage.  Here are functions to draw girders and ladders.

A simple girder is made from 2 parallel lines.  We can add detail later if our vector count allows.

function draw_girder(y1, x1, y2, x2)
-- draw parallel vectors (offset by 7 pixels) to form a girder.  Co-ordinates relate to the bottom vector.
vector(y1,   x1,   y2, x2, intensity())
vector(y1+7, x1, y2+7, x2, intensity())

A ladder is a more complex object with legs and rungs.  We'll need to deal with broken ladders too.  We can treat broken ladders as 2 separate ladders that are set slightly apart.

function draw_ladder(y, x, h)
-- draw a single ladder at given y, x position of given height in pixels
vector(y, x,   y+h, x)  -- left leg
vector(y, x+8, y+h, x+8)  -- right leg
for i=0, h-2 do  -- draw rung every 4th pixel (skipping 2 pixels at bottom)
if i % 4 == 0 then vector(y+i+2, x, y+i+2, x+8) end

We now test drawing girders and ladders.  In total we have used 139 vectors.

Now for some other background detail for the girders stage.  The oilcan, hammers and the stacked barrels.

function draw_oilcan(y, x)
box(y,     x,  1,  16)  -- outline of oil can
box(y+1, x+1, 14,  14)  -- bottom lip
box(y+15,  x,  1,  16)  -- top lip
box(y+7,   x+4,  3,   3)  -- "O"
vector(y+7, x+9, y+10, x+9)  -- "I"
polyline({y+7,x+13,y+7,x+11,y+10,x+11})  -- "L"
vector(y+5, x+1, y+5, x+15)  -- horizontal stripe
vector(y+12, x+1, y+12, x+15)  -- horizontal stripe

function draw_hammer(y, x)
polyline({y+5,x,y+7,x,y+8,x+1,y+8,x+8,y+7,x+9,y+5,x+9,y+4,x+8,y+4,x+1,y+5,x})  -- hammer
box(y,   x+4, 4, 1)  -- bottom handle
box(y+8, x+4, 1, 1)  -- top handle

function draw_barrel(y, x)
-- draw an upright/stacked barrel
polyline({y+3,x,y+12,x,y+15,x+2,y+15,x+7,y+12,x+9,y+3,x+9,y,x+7,y,x+7,y,x+2,y+3,x})  -- barrel outline
vector(y+1,  x+1, y+1,  x+8)  -- horizontal bands
vector(y+14, x+1, y+14, x+8)
vector(y+2,  x+3, y+13, x+3)  -- vertical bands
vector(y+2,  x+6, y+13, x+6)

Drawing all this stuff increases the vector count to 232.  That's pretty good going.

We still have sprites for Jumpman, Pauline, Fireballs, Donkey Kong, hammer smashing, oilcan flames and barrels to deal with.  We'll also need to make a vector font to show the scores, level, bonus timer, bonus points and other messages.
I'll look at adding vector sprites in the next part.  The vector sprites will have to be cleverly linked to the game logic so they behave similarly to the pixel sprites.

Here are the functions responsible for varying the intensity and wobble of the vectors.

function intensity()
-- we can vary the brightness of the vectors
return ({0xbbffffff, 0xddffffff, 0xffffffff})[math.random(3)]

function wobble()
-- random change of the vector offset
return math.random(-40, 60) / 100 -- random change of the vector offset

Here's a video showing progress with the girders stage.  I've hacked out the original background graphics for now so they don't interfere with our new vector graphics.
This is all monochrome for now,  but maybe later we can think about adding splashes of colour.

My experimental code is on github at



I don't have anything productive to add - just wanted to note my appreciation for this project and your extremely interesting write-up for it. I'm absolutely fascinated by your work and will be following along.

Best of luck!


this is a really cool idea! im genuinely excited to see how this pans out (and im replying so im notified for updates lmao). i hope this goes well!


Quote from: Shadic on April 16, 2022, 01:24:40 PM
I don't have anything productive to add - just wanted to note my appreciation for this project and your extremely interesting write-up for it. I'm absolutely fascinated by your work and will be following along.

Best of luck!

Quote from: capnkatie on April 16, 2022, 03:51:29 PM
this is a really cool idea! im genuinely excited to see how this pans out (and im replying so im notified for updates lmao). i hope this goes well!

and thanks for following.


This looks really original and cool, hope to see it finished.  :thumbsup:


Quote from: NiO on April 18, 2022, 08:13:39 PM
This looks really original and cool, hope to see it finished.  :thumbsup:



In this part 2,  I'm going to be vectorising the character/fonts that are used by the game.

The Donkey Kong rom includes 2 graphics banks.  Bank 1 includes 256 characters which are sized at 8 x 8 pixels.  Bank 2 includes 128 sprites which are sized at 16 x 16 pixels.
We'll be working with bank 1.  The characters in this bank comprise mostly of numbers, letters and punctuation.

The Donkey Kong game outputs characters from bank 1 to the screen by utilising video RAM.  The video RAM is located in RAM memory from address 7440 to 77ff.
We can see video RAM in the MAME debugger.

mame dkong -debug

In the debugger,  open a new memory window,  increase the byte width from 16 to 32 bytes (by pressing Ctrl+P 16 times), enter 7440 into the box and select "Run" from the menu.
*The 32 bytes correspond to the height of the screen in DK i.e. 32 characters x 8 pixels = 256 pixel high.  Address 7440 relates to the top-right corner of the screen.  The video ram appears rotated when viewed in the debugger.

I've annotated the debugger to show how we can determine characters that are output to the screen.  The character chart below can be used to translate each of the hexadecimal values to a specific character graphic.  For example,  the wording for "HIGH SCORE" is made up from hex values: 18,19,17,18,10,23,13,1F,22,15.  In the chart,  "H" is found at 10 + 8 = 18.  "I" is found at 10 + 9 = 19  etc.

For reference,  the video ram is showing the content of the name registration screen.  It's perfect for our test because it contains the full alphabet of characters.

I fleshed out a function to iterate through all of the 896 characters (32 x 28 characters) in video ram. We're getting ahead of ourselves so we'll come back to this function later when we've figured out how to draw characters from bank 1 as vectors.

function draw_vector_characters()
-- Output vector characters based on contents of video ram ($7440-77ff)
local _addr = 0x7440
for _x=223, 0, -8 do  -- 32 characters
for _y=255, 0, -8 do  -- 28 characters
_addr = _addr + 1

Ok,  so now we can look into vectorising the pixel characters from bank 1.

We may as well start with zero,  the first character in the bank.   The image below shows how I can draw a vector outline by joining all of the points with 8 lines. The points are relative to 0,0 at the bottom-left corner.

So our y, x points forming the zero character are: 0,2 - 0,4 - 2,6 - 4,6 - 6,4 - 6,2 - 4,0 - 2,0 - 0,2

This can be plotted to the screen using our existing polyline function as follows.


I took on the tedious task of vectorising all of the characters we're going to need and stored the results into a vector library.  This will give us convenient access in our LUA program. 
The ID in the vector library e.g. [0x00] matches with the hexadecimal value in our character lookup table above.

vector_lib[0x00] = {0,2,0,4,2,6,4,6,6,4,6,2,4,0,2,0,0,2} -- 0
vector_lib[0x01] = {0,0,0,6,BR,BR,0,3,6,3,5, 1} -- 1
vector_lib[0x02] = {0,6,0,0,5,6,6,3,5,0} -- 2
vector_lib[0x03] = {1,0,0,1,0,5,1,6,2,6,3,5,3,2,6,6,6,1} -- 3
vector_lib[0x04] = {0,5,6,5,2,0,2,7} -- 4
vector_lib[0x05] = {1,0,0,1,0,5,2,6,4,5,4,0,6,0,6,5} -- 5
vector_lib[0x06] = {3,0,1,0,0,1,0,5,1,6,2,6,3,5,3,0,6,2,6,5} -- 6
vector_lib[0x07] = {6,0,6,6,0,2} -- 7
vector_lib[0x08] = {2,0,0,1,0,5,2,6,5,0,6,1,6,4,5,5,2,0} -- 8
vector_lib[0x09] = {0,1,0,4,2,6,5,6,6,5,6,1,5,0,4,0,3,1,3,6} -- 9
vector_lib[0x11] = {0,0,4,0,6,3,4,6,0,6,BR,BR,2,6,2,0}  -- A
vector_lib[0x12] = {0,0,6,0,6,5,5,6,4,6,3,5,2,6,1,6,0,5,0,0,BR,BR,3,0,3,4}  -- B
vector_lib[0x13] = {1,6,0,5,0,2,2,0,4,0,6,2,6,5,5,6} -- C
vector_lib[0x14] = {0,0,6,0,6,4,4,6,2,6,0,4,0,0} -- D
vector_lib[0x15] = {0,5,0,0,6,0,6,5,BR,BR,3,0,3,4} -- E
vector_lib[0x16] = {0,0,6,0,6,6,BR,BR,3,0,3,5} -- F
vector_lib[0x17] = {3,4,3,6,0,6,0,2,2,0,4,0,6,2,6,6} -- G
vector_lib[0x18] = {0,0,6,0,BR,BR,3,0,3,6,BR,BR,0,6,6,6} -- H
vector_lib[0x19] = {0,0,0,6,BR,BR,0,3,6,3,BR,BR,6,0,6,6} -- I
vector_lib[0x1a] = {1,0,0,1,0,5,1,6,6,6} -- J
vector_lib[0x1b] = {0,0,6,0,BR,BR,3,0,0,6,BR,BR,3,0,6,6} -- K
vector_lib[0x1c] = {6,0,0,0,0,5} -- L
vector_lib[0x1d] = {0,0,6,0,2,3,6,6,0,6}  -- M
vector_lib[0x1e] = {0,0,6,0,0,6,6,6} -- N
vector_lib[0x1f] = {1,0,5,0,6,1,6,5,5,6,1,6,0,5,0,1,1,0} -- O
vector_lib[0x20] = {0,0,6,0,6,5,5,6,3,6,2,5,2,0} -- P
vector_lib[0x21] = {1,0,5,0,6,1,6,5,5,6,2,6,0,4,0,1,1,0,BR,BR,0,6,2,3} -- Q
vector_lib[0x22] = {0,0,6,0,6,5,5,6,4,6,2,3,2,0,2,3,0,6} -- R
vector_lib[0x23] = {1,0,0,1,0,5,1,6,2,6,4,0,5,0,6,1,6,4,5,5} -- S
vector_lib[0x24] = {6,0,6,6,BR,BR,6,3,0,3} -- T
vector_lib[0x25] = {6,0,1,0,0,1,0,5,1,6,6,6} -- U
vector_lib[0x26] = {6,0,3,0,0,3,3,6,6,6} -- V
vector_lib[0x27] = {6,0,2,0,0,1,4,3,0,5,2,6,6,6}  -- W
vector_lib[0x28] = {0,0,6,6,3,3,6,0,0,6} -- X
vector_lib[0x29] = {6,0,3,3,6,6,BR,BR,3,3,0,3} -- Y
vector_lib[0x2a] = {6,0,6,6,0,0,0,6} -- Z
vector_lib[0x2b] = {0,0,1,0,1,1,0,1,0,0}  -- dot
vector_lib[0x2c] = {3,0,3,5} -- dash
vector_lib[0x2d] = {5,0,5,6} -- underscore
vector_lib[0x2e] = {4,3,4,3,BR,BR,2,3,2,3} -- colon
vector_lib[0x2f] = {5,0,5,6} -- Alt underscore
vector_lib[0x34] = {2,0,2,5,BR,BR,4,0,4,5} -- equals
vector_lib[0x35] = {3,0,3,5} -- dash
vector_lib[0x44] = {0,5,4,5,4,7,2,7,0,8,BR,BR,2,5,2,7,BR,BR,4,10,1,10,0,11,0,12,1,13,4,13,BR,BR,0,15,4,15,4,17,2,17,2,18,0,18,0,15,BR,BR,2,15,2,17,BR,BR,0,23,0,21,4,21,4,23,BR,BR,2,21,2,22,BR,BR,0,25,4,25,0,28,4,28,BR,BR,0,30,4,30,4,32,3,33,1,33,0,32,0,30} -- rub / end
vector_lib[0x49] = {0,4,2,2,5,2,7,4,7,8,5,10,2,10,0,8,0,4,BR,BR,2,7,2,5,5,5,5,7} -- copyright
vector_lib[0x6c] = {2,0,2,4,3,5,4,4,5,5,6,4,6,0,2,0,BR,BR,4,4,4,0,BR,BR,3,7,2,8,2,11,3,12,5,12,6,11,6,8,5,7,3,7,BR,BR,2,14,6,14,2,19,6,19,BR,BR,6,21,3,21,2,22,2,25,3,26,6,26,BR,BR,2,28,2,31,4,31,4,28,5,28,6,29,6,31, B,BR,6,-2,6,-5,-12,-5,-12,36,6,36,6,33,BR,BR,0,-3,-10,-3,-10,34,0,34,0,-3} -- bonus
vector_lib[0x9f] = {2,0,0,2,0,13,2,15,5,15,7,13,7,2,5,0,2,0,BR,BR,5,3,5,7,BR,BR,5,5,2,5,BR,BR,2,8,5,8,4,10,5,12,2,12} -- TM
vector_lib[0xb0] = {0,0,0,8,BR,BR,1,0,1,8,BR,BR,7,0,7,8,BR,BR,6,0,6,8} -- Block
vector_lib[0xb1] = {0,0,7,0,7,7,0,7,0,0} -- Box
vector_lib[0xb7] = {0,1,1,1,1,2,6,2,6,1,7,1,7,3,6,3,6,5,7,5,7,7,6,7,6,6,1,6,1,7,0,7,0,1} -- Rivet
vector_lib[0xdd] = {0,0,7,0,BR,BR,4,0,4,4,BR,BR,1,4,7,4,BR,BR,2,9,1,6,7,6,7,9,BR,BR,5,6,5,9,BR,BR,7,11,2,11,3,14,BR,BR,3,16,7,16,7,18,6,19,5,18,5,16,BR,BR,7,22,5,21,BR,BR,3,21,3,21} -- Help (big H)
vector_lib[0xed] = {7,0,5,0,BR,BR,6,0,6,4,BR,BR,7,4,4,4,BR,BR,7,9,7,6,4,6,3,9,BR,BR,5,6,5,9,BR,BR,7,11,3,11,2,14,BR,BR,1,16,7,16,7,19,3,19,3,16,BR,BR,7,22,2,21,BR,BR,0,20,0,21} -- Help (little H)
vector_lib[0xfb] = {5,1,6,2,6,5,5,6,4,6,2,3,BR,BR,0,3,0,3} -- question mark
vector_lib[0xfd] = {-1,0,7,0} -- vertical line
vector_lib[0xfe] = {0,0,7,0,7,7,0,7,0,0} -- cross
vector_lib[0xff] = {5,2,7,2,7,4,5,4,5,2,BR,BR,5,3,2,3,0,1,BR,BR,2,3,0,5,BR,BR,4,0,3,1,3,5,4,6} -- jumpman / stick man

Some entries in the table are more complex than others e.g. [0x6c] is the bonus box,  it is comprised of multiple characters as it is convenient for us to link them together.

We can now complete the work on our vector drawing function we fleshed out earlier and visualise the results in MAME.

function draw_vector_characters()
-- Output vector characters based on contents of video ram ($7400-77ff)
local _addr = 0x7440
for _x=223, 0, -8 do
for _y=255, 0, -8 do
polyline(vector_lib[mem:read_u8(_addr)], _y - 6, _x - 6)
_addr = _addr + 1

Here's what the name registration screen looks like now:

and the DK title screen:

and here's a gameplay video showing our progress.

Note that I've hacked the game for now so that we will only play on the barrel stage.  This function gets called at the end of the stage.

local STAGE, LEVEL =  0x6227, 0x6229
function debug_stay_on_girders()
mem:write_u8(STAGE, 1);
mem:write_u8(LEVEL, mem:read_u8(LEVEL) + 1)


In this part 3,  I'm going to be adding vector sprites for Oilcan Flames, Fireballs, Barrels, Pauline and Paulines Love Heart.  I've also introduced colour.

First thing to report is my graphics optimisation resulting in a significant increase to the vectors that can be drawn per frame.
I was able to triple the limit - to 3000 vectors per frame*.  We're not going to get anywhere near those numbers.
* Tested with MAME version 0.243 running fullscreen opengl video.

I was able to programmatically clear the roms sprite banks during startup.  There is an overhead for MAME to draw stuff and we won't need the games original pixel graphics at all.  My previous hack was just to hide them in the background.  This is a much better solution.

function clear_graphic_banks()
-- clear the contents of the DK graphics banks 1 and 2
local _bank1, _bank2 = bnk.regions[":gfx1"], bnk.regions[":gfx2"]
if _bank1 and _bank2 then
for _addr=0, 0xfff do _bank1:write_u8(_addr, 0) end  -- clear bank 1
for _addr=0, 0x1fff do _bank2:write_u8(_addr, 0) end  -- clear bank 2

So back to the main focus of this update.  I made progress with drawing and animating sprites.

I made vectors versions of the needed sprites and added them to my program's vector library.  I used a paint program to draw lines and to experiment with how things would look e.g.

I added flames to the oilcan first as I thought that would be fairly simple.  The flames ignite after the first blue barrel strikes the oilcan.  I check the flame (memory location 0x6a29) is active (value is between 0x40 and 0x43) and then draw flames as vectors in yellow and red.  The red flame extends upwards in a simple random animation.

function draw_oilcan_and_flames(y, x)
draw_object("oilcan",  y, x)
local _sprite = read(0x6a29)
if _sprite >= 0x40 and _sprite <= 0x43 then  -- oilcan is on fire
draw_object("flames", y+16, x, YEL)  -- base of flames
draw_object("flames", y+16+math.random(0,3), x, RED)  -- flames extend upwards

Fireballs were taxing.  There are 6 fireball slots in RAM.  Each fireball has 32 bytes of data including status (active/inactive), current x/y position, colour and direction of movement.  I check each fireball slot every frame looking for active fireballs,  I draw active fireballs in 3 parts:  The body in yellow,  the flames extending upwards in red and the eyes in red.  The flames were given a simple animation similar to the oilcan flame effect.  If the fireball changes direction then I flip the fireball vectors so they are mirrored when drawn.

function draw_fireballs()
local _y, _x
for _i, _addr in ipairs(FIREBALLS) do
if read(_addr, 1) then  -- fireball is active
_y, _x = 247 - read(_addr+5), read(_addr+3) - 22
if read(_addr+0xd, 1) then
vector_flip = 13  -- fireball is moving right so flip the vectors
draw_object("fire-1", _y, _x, YEL) -- draw body
draw_object("fire-2", _y+math.random(0,3), _x, RED)  -- draw flames extending upwards
draw_object("fire-3", _y+1, _x, RED)  -- draw eyes
vector_flip = 0

Here's the Oilcan with flames throwing out a fireball.

Barrels were even more taxing than fireballs.  There are 8 barrel slots in RAM.  Similar to fireballs,  each barrel has 32 bytes of corresponding data.  The data comprises of status (inactive/being deployed/rolling), color indicator (normal or blue), crazy barrel indicator, movement direction, current x/y position and a bunch of other stuff. 

If the barrel is crazy/wild or going down a ladder then I draw a longer sideways barrel.  These can be blue or brown.

otherwise I draw a circular rolling barrel.  These can also be blue or brown.

Here's my function for handling the barrels and their rolling animation.

function draw_barrels()
local _y, _x, _type, _state
for _i, _addr in ipairs(BARRELS) do
if read(_addr) > 0 and read(0x6200, 1) and read(_addr+3) > 0 then  -- barrel active and Jumpman alive
_y, _x = 251 - read(_addr+5), read(_addr+3) - 20
_type = read(_addr+0x15) + 1  -- type of barrel: 1 is normal, 2 is blue/skull

if read(_addr+1, 1) or bits(read(_addr+2))[1] == 1 then  -- barrel is crazy or going down a ladder
_state = read(_addr+0xf)
draw_object("down", _y, _x-2, ({BRN, CYN})[_type])
draw_object("down-"..tostring(_state % 2 + 1), _y, _x - 2, ({LBR, BLU})[_type])
else  -- barrel is rolling
_state = barrel_state[_addr] or 0
if scr:frame_number() % 10 == 0 then
if read(_addr+2, 2) then _state = _state - 1 else _state = _state+1 end  -- roll left or right?
barrel_state[_addr] = _state
draw_object("roll", _y, _x, ({BRN, CYN})[_type])
draw_object(({"roll-", "skull-"})[_type]..tostring(_state % 4 + 1), _y, _x,({LBR, BLU})[_type])

I made a simple vector representation of Pauline and her love heart/broken heart.
This function check for Paulines animation state.  The original DK uses several sprites for Pauline as she rocks back and forth.  I made her Jump up instead to keep the effect simple.

function draw_pauline()
local _y, _x = 235 - read(0x6903), 90
if read(0x6905) ~= 17 and read(0x6a20, 0) then _y = _y + 3 end  -- Pauline jumps when heart not showing
draw_object("paul-1", _y, _x, MAG)
draw_object("paul-2", _y, _x, PNK)

This function reads the status of the love heart (full or broken) and position prior to drawing.

function draw_loveheart()
_y, _x = 250 - read(0x6a23), read(0x6a20) - 23
if _x > 0 then draw_object(read(0x6a21) + 0xf00, _y, _x, MAG) end

Lastly,  I made points appear when jumping or smashing barrels.  I adjusted the y position so points do not overlap with girders.

function draw_points()
-- draw 100, 300, 500 or 800 when points awarded
if read(0x6a30) > 0 then
_y, _x = 254 - read(0x6a33), read(0x6a30) - 22
draw_object(read(0x6a31) + 0xf00, _y+3, _x, YEL)  -- move points up a little so they don't overlap as much

The hammer smashing animation still needs to be done,  along with other sprites for Jumpman and Donkey Kong.  They should be in the next part.

Here's a gameplay video showing my progress so far (without hammer smashing).   I've used a simple box to represent Jumpman's position on screen.  Hey,  it's almost playable like this.


This is absolutely wild. I never imagined this was possible. Keep it up!


Quote from: ewzzy on May 06, 2022, 06:22:55 PM
This is absolutely wild. I never imagined this was possible. Keep it up!

Thank you!  There's a lot more to do but I'll keep going at it.  I'm working on Kong graphics at the mo.


What's the point of vectorising a game? Is it like cool or something?  :D


Quote from: Cyneprepou4uk on May 07, 2022, 05:27:15 AM
What's the point of vectorising a game? Is it like cool or something?  :D

What's the point in any of this?  :laugh:   For me,  these are fun projects and they facilitate focussed learning (Z80 assembly, LUA and geometry). 

Vectorising makes it possible to scale the game to high resolution displays without pixelation.


In this part 3b,  I'll be adding vector sprites for Donkey Kong himself.  There's lots of work to do for sprites so I figured I would break part 3 up into multiple sub-parts.

Ok,  our first thing to tackle is the Kong facing forwards graphic which is his default stance.  He appears like this on the "title screen",  the "how high can you get" screen and on the girders stage when he's not deploying barrels.

I used a paint program to help me draw out vectors on top of a template.  The template includes co-ordinates which will help me translate my work to a list of points we can draw in the game.  The graphic is mirrored along a center line so I need only draw vectors for one side and then use my existing mirror function to complete the work.  I'm using bright colours for now so they stand out.  These will be replaced with different shades of brown when added to the game.

The list of Kong's vectors is quite large.


I used the same approach to draw vectors for Kong deploying barrels.  Kong grabs a barrel from the left side and releases a barrel to the right side.  The graphic is mirrored so I need only draw one side view.

In the paint tool,  I remove the template layer so I can visualise better how the vectors will look.

There's also the graphic of Kong holding a barrel about to be deployed.  I shuffled his chest up and to the left so it was not obscured by the barrel.  With vectors,  there isn't really a foregound/background, everything just overlaps and can confuse the look.

Here's the result when all 4 vector graphics are animated.  I'm really happy with how this is looking.

Finally,  there's a growling mouth to add.  Kong growls on the title screen and also at the end of each stage.  The growling vectors are overlaid onto a front facing Kong.

The title screen and how high screens can now be finished.

I added Kong's graphics to my LUA code.  This function places kong on screen at a given position.  The function queries the state of Kong to determine which of the 4 vectors graphics to display and whether we should make him growl.  The function deals with the mirroring when necessary.  When deploying barrels, I also had to overlay a barrel graphic (brown or blue) into Kong's hands.

function draw_kong(y, x, growl)
local _state = read(0x691d) -- state of kong - is he deploying a barrel?
local _data -- barrel data
if read(0x6382,0x80,0x81) then _data = {"skull-1",CYN,BLU} else _data = {"roll-1",LBR,MBR} end
if _state == 173 then
-- Kong releasing barrel to right
draw_object("dk-side", y, x+1, BRN)
draw_object("rolling", y, x+44, _data[2])
draw_object(_data[1], y, x+44, _data[3])
elseif _state == 45 then
-- Kong grabbing barrel from left (mirrored)
draw_object("dk-side", y, x-3, BRN, 42)
draw_object("rolling", y, x-15, _data[2])
draw_object(_data[1], y, x-15, _data[3])
elseif _state == 42 then
-- Kong front facing - holding a barrel
draw_object("dk-hold", y, x, BRN)  -- left side
draw_object("dk-hold", y, x+20, BRN, 20) -- mirrored right side
draw_object("down", y+2, x+12.4, _data[2])
draw_object("down-1", y+2, x+12.4, _data[3])
-- Default Kong front facing
draw_object("dk-front", y, x, BRN)  -- left side
draw_object("dk-front", y, x+20, BRN, 20) -- mirrored right side
if growl then
draw_object("dk-growl", y, x, MBR)  --left side
draw_object("dk-growl", y, x+20, MBR,20)  -- mirrored right side

Here's a video to show progress.


In this part 3c,  I'll be working on the sprites for Jumpman a.k.a. Mario.  There's lots of drawing to be done.  By the end of this part we should have a fully playable barrels stage. 

There are 18 variations of Jumpman:

3 x walking sprites
2 x jumping sprites
4 x ladder climbing sprites
6 x hammering sprites
3 x dying sprites

All these sprites are stored in graphic bank 2 of the Donkey Kong ROM at the offset position shown below e.g. jumping sprites are numbered 14 to 15.

As was done for Kong graphics (in part 3b),  I used a paint program to help me draw out the vectors for all of the Jumpman sprites.



Ladder Climbing:



I added all 18 of my drawings to the vector library in my program.

_lib["jm-0"] = {6,6,6,7,5,7,5,6,6,6,BR,BR,8,6,7,5,BR,BR,2,5,1,5,1,4,0,4,0,7,2,7,2,5,BR,BR,2,10,1,10,1,9,0,9,0,12,2,12,2,10,BR,BR,10,3,11,3,11,5,10,5,10,3,BR,BR,11,3,10,5,BR,BR,10,3,11,5,BR,BR,14,8,13,9,11,8,11,9,12,10,12,11,10,11,10,13,14,11,BR,BR,8,10,8,11,7,12,5,12,3,10,BR,BR,7,9,6,10,5,9,BR,RED,14,3,14,11,16,10,16,8,14,5,BR,BR,6,9,7,8,7,5,5,4,3,4,2,5,BR,BR,2,7,3,8,3,9,2,10,BR,BR,4,11,2,12,BR,GRY,14,6,13,5,12,3,11,2,11,3,10,4,9,4,8,6,BR,BR,8,10,10,11,BR,BR,5,9,4,8,3,9,3,10,BR,BR,13,6,13,7,12,7,12,6,13,6}
_lib["jm-1"] = {10,3,11,3,11,5,10,5,10,3,BR,BR,11,3,10,5,BR,BR,10,3,11,5,BR,BR,14,8,13,9,11,8,11,9,12,10,12,11,10,11,10,13,14,11,BR,BR,8,2,8,6,BR,BR,6,3,6,5,BR,BR,8,10,8,12,BR,BR,6,11,6,12,5,14,BR,BR,6,7,6,8,5,8,5,7,6,7,BR,BR,5,2,2,2,2,4,4,4,4,3,5,3,5,2,BR,BR,3,13,2,14,0,13,0,12,1,12,2,11,3,13,BR,RED,14,3,14,11,16,10,16,8,14,5,BR,BR,4,4,7,6,7,9,6,11,3,13,BR,BR,2,4,3,7,3,8,2,11,BR,GRY,14,6,13,5,12,3,11,2,11,3,10,4,9,4,8,6,BR,BR,8,10,10,11,BR,BR,13,6,13,7,12,7,12,6,13,6,BR,BR,8,2,8,1,7,1,6,2,6,3,BR,BR,7,14,7,15,5,15,5,14}
_lib["jm-2"] = {9,2,10,2,10,4,9,4,9,2,BR,BR,10,2,9,4,BR,BR,9,2,10,4,BR,BR,13,7,12,8,10,7,10,8,11,9,11,10,9,10,9,12,13,10,BR,BR,6,4,7,9,7,11,6,12,5,11,4,4,BR,BR,2,6,2,8,0,8,0,5,1,5,1,6,2,6,BR,BR,4,13,4,15,1,15,1,14,2,14,2,13,4,13,BR,RED,13,2,13,10,15,9,15,7,13,4,BR,BR,4,4,2,6,BR,BR,2,8,3,8,3,9,2,13,BR,BR,6,12,4,13,BR,GRY,13,5,12,4,11,2,10,1,10,2,9,3,8,3,7,5,BR,BR,7,9,9,10,BR,BR,12,5,12,6,11,6,11,5,12,5,BR,BR,6,4,6,2,5,2,4,4}
_lib["jm-3"] = {14,2,11,1,8,3,7,3,BR,BR,14,4,11,3,9,5,BR,BR,9,10,10,12,10,13,9,14,7,12,BR,BR,4,3,4,6,3,6,3,2,4,3,BR,BR,2,7,2,10,1,11,1,7,2,7,BR,BR,14,5,13,4,10,4,9,6,9,9,10,11,13,11,14,10,BR,RED,15,5,15,10,14,10,13,9,13,6,14,5,15,5,BR,BR,4,3,5,2,6,2,7,3,8,5,9,5,9,6,7,6,6,7,6,8,7,9,9,9,9,10,8,10,7,12,4,12,2,10,BR,GRY,14,2,15,3,15,4,14,4}
_lib["jm-4"] = {15,6,13,6,14,5,12,2,11,1,10,1,10,2,11,4,BR,BR,15,11,13,11,14,12,13,14,10,14,BR,BR,6,4,6,7,5,7,5,3,6,4,BR,BR,3,7,3,10,2,11,2,7,3,7,BR,RED,15,6,15,11,14,10,14,7,15,6,BR,BR,6,4,8,2,9,2,11,4,12,6,13,6,13,7,12,7,11,8,11,9,12,10,13,10,13,11,12,11,11,13,10,14,7,14,3,10}
_lib["jm-5"] = {7,1,7,5,6,5,6,1,7,1,BR,BR,3,7,3,10,2,11,2,7,3,7,BR,BR,11,12,9,14,7,15,6,15,BR,RED,7,1,8,0,9,0,11,2,12,4,12,6,11,7,11,8,12,9,12,10,11,12,9,13,5,13,3,10,BR,BR,3,7,5,9,6,9,7,7,7,5,BR,GRY,6,13,5,14,5,15,6,15}
_lib["jm-6"] = {14,5,13,4,10,4,9,6,9,9,10,11,13,11,14,10,BR,BR,5,1,7,1,9,3,9,5,BR,BR,9,10,9,12,7,14,4,14,BR,BR,0,3,0,7,1,7,1,4,0,3,BR,BR,1,8,0,8,0,12,1,11,1,8,BR,RED,15,5,15,10,14,10,13,9,13,6,14,5,15,5,BR,BR,1,7,2,6,3,7,3,8,2,9,1,8,BR,BR,1,4,2,3,6,3,8,5,9,5,9,6,6,6,5,7,5,8,6,9,9,9,9,10,8,10,6,12,2,12,1,11,BR,GRY,5,1,4,1,4,2,5,3,BR,BR,5,12,4,13,4,14,5,14}
_lib["jm-8"] = {11,3,11,5,10,5,10,3,11,3,BR,BR,10,3,11,5,BR,BR,11,3,10,5,BR,BR,14,11,10,13,10,10,12,11,12,9,BR,BR,14,7,9,7,7,9,6,11,7,12,8,11,9,9,14,9,BR,BR,8,6,7,5,BR,RED,16,9,16,10,14,11,14,9,BR,BR,14,3,14,7,BR,BR,14,5,16,8,BR,GRY,14,7,16,8,16,9,14,9,BR,BR,13,6,13,7,12,7,12,6,13,6,BR,BR,14,6,13,5,12,3,11,2,BR,BR,10,4,9,4,8,6,BR,BR,10,10,9,10,BR,RED,7,9,7,5,5,4,3,4,2,5,BR,BR,7,12,2,12,BR,BR,2,7,3,8,3,9,2,10,BR,BLU,6,6,6,7,5,7,5,6,6,6,BR,BR,2,5,2,7,0,7,0,4,1,4,1,5,2,5,BR,BR,2,10,2,12,0,12,0,9,1,9,1,10,2,10}
_lib["jm-9"] = {11,3,11,5,10,5,10,3,11,3,BR,BR,11,3,10,5,BR,BR,10,3,11,5,BR,BR,14,8,13,9,11,8,11,9,12,10,12,11,10,11,10,13,14,11,BR,BR,8,2,8,10,7,11,6,10,6,2,BR,RED,14,3,14,11,16,10,16,8,14,5,BR,GRY,13,6,13,7,12,7,12,6,13,6,BR,BR,14,6,13,5,12,3,11,2,11,3,BR,BR,10,4,9,4,8,5,BR,BR,8,10,10,10,BR,BR,8,2,8,0,7,0,6,2,BR,RED,6,4,3,4,2,5,BR,BR,2,7,3,8,3,9,2,10,BR,BR,2,12,5,11,7,11,BR,BLU,6,6,5,6,5,7,6,7,BR,BR,2,5,2,7,0,7,0,4,1,4,1,5,2,5,BR,BR,2,10,2,12,0,12,0,9,1,9,1,10,2,10}
_lib["jm-10"] = {11,3,11,5,10,5,10,3,11,3,BR,BR,10,3,11,5,BR,BR,11,3,10,5,BR,BR,14,11,10,13,10,10,12,11,12,9,BR,BR,14,7,9,7,7,9,6,11,7,12,8,11,9,9,14,9,BR,BR,8,6,7,5,BR,RED,16,9,16,10,14,11,14,9,BR,BR,14,3,14,7,BR,BR,14,5,16,8,BR,GRY,14,7,16,8,16,9,14,9,BR,BR,13,6,13,7,12,7,12,6,13,6,BR,BR,14,6,13,5,12,3,11,2,BR,BR,10,4,9,4,8,6,BR,BR,10,10,9,10,BR,RED,7,9,7,5,4,4,BR,BR,2,4,3,7,3,8,2,11,BR,BR,7,12,5,11,3,13,BR,BLU,4,4,4,3,5,3,5,2,2,2,2,4,4,4,BR,BR,2,11,3,13,2,14,0,13,0,12,1,12,2,11,BR,BR,6,6,6,7,5,7,5,6,6,6}
_lib["jm-11"] = {11,3,11,5,10,5,10,3,11,3,BR,BR,11,3,10,5,BR,BR,10,3,11,5,BR,BR,14,8,13,9,11,8,11,9,12,10,12,11,10,11,10,13,14,11,BR,BR,8,2,8,10,7,11,6,10,6,2,BR,RED,14,3,14,11,16,10,16,8,14,5,BR,GRY,13,6,13,7,12,7,12,6,13,6,BR,BR,14,6,13,5,12,3,11,2,11,3,BR,BR,10,4,9,4,8,5,BR,BR,8,10,10,10,BR,BR,8,2,8,0,7,0,6,2,BR,RED,6,5,4,4,BR,BR,2,4,3,7,3,8,2,11,BR,BR,7,11,5,11,3,13,BR,BLU,6,6,5,6,5,7,6,7,BR,BR,5,2,5,3,4,3,4,4,2,4,2,2,5,2,BR,BR,2,11,3,13,2,14,0,13,0,12,1,12,2,11}
_lib["jm-12"] = {11,2,11,4,10,4,10,2,11,2,BR,BR,10,2,11,4,BR,BR,11,2,10,4,BR,BR,14,10,10,12,10,9,12,10,12,8,BR,BR,14,6,9,6,7,8,6,10,7,11,8,10,9,8,14,8,BR,BR,8,5,7,4,BR,RED,16,8,16,9,14,10,14,8,BR,BR,14,2,14,6,BR,BR,14,4,16,7,BR,GRY,14,6,16,7,16,8,14,8,BR,BR,13,5,13,6,12,6,12,5,13,5,BR,BR,14,5,13,4,12,2,11,1,BR,BR,10,3,9,3,8,6,BR,BR,10,9,9,9,BR,RED,7,8,7,4,4,4,2,6,BR,BR,2,8,3,8,3,9,2,13,BR,BR,7,11,6,11,4,13,BR,BLU,6,6,6,7,5,7,5,6,6,6,BR,BR,0,5,0,8,2,8,2,6,1,6,1,5,0,5,BR,BR,4,13,4,15,1,15,1,14,2,14,2,13,4,13}
_lib["jm-13"] = {11,2,11,4,10,4,10,2,11,2,BR,BR,11,2,10,4,BR,BR,10,2,11,4,BR,BR,14,7,13,8,11,7,11,8,12,9,12,10,10,10,10,12,14,10,BR,BR,8,2,8,10,7,11,6,10,6,2,BR,RED,14,2,14,10,16,9,16,7,14,4,BR,GRY,13,5,13,6,12,6,12,5,13,5,BR,BR,14,5,13,4,12,2,11,1,11,2,BR,BR,10,3,9,3,8,4,BR,BR,8,10,10,10,BR,BR,8,2,8,0,7,0,6,2,BR,RED,6,4,4,4,2,6,BR,BR,2,8,3,8,3,9,2,13,BR,BR,4,13,6,11,7,11,BR,BLU,6,6,5,6,5,7,6,7,BR,BR,2,6,1,6,1,5,0,5,0,8,2,8,2,6,BR,BR,4,13,4,15,1,15,1,14,2,14,2,13,4,13}
_lib["jm-14"] = {9,3,10,3,10,5,9,5,9,3,BR,BR,10,3,9,5,BR,BR,9,3,10,5,BR,BR,13,8,12,9,10,8,10,9,11,10,11,11,9,11,9,13,13,11,BR,BR,1,1,4,1,4,2,3,2,3,3,1,3,1,1,BR,BR,3,12,5,13,3,15,2,15,3,14,2,13,3,12,BR,BR,7,3,7,6,BR,BR,5,2,5,5,BR,BR,7,10,8,13,BR,BR,5,11,6,14,BR,BR,5,7,5,8,4,8,4,7,5,7,BR,RED,13,3,13,11,15,10,15,8,13,5,BR,BR,3,3,6,6,6,9,5,11,3,11,3,12,BR,BR,1,3,2,7,2,8,0,10,0,11,2,13,BR,GRY,13,6,12,5,11,3,10,2,10,3,9,4,8,4,7,6,BR,BR,7,10,9,11,BR,BR,12,6,12,7,11,7,11,6,12,6,BR,BR,7,3,7,1,6,1,5,2,BR,BR,8,13,8,15,7,15,6,14}
_lib["jm-15"] = {10,4,11,4,11,6,10,6,10,4,BR,BR,11,4,10,6,BR,BR,10,4,11,6,BR,BR,14,9,13,10,11,9,11,10,12,11,12,12,10,12,10,14,14,12,BR,BR,9,11,9,13,7,16,6,16,BR,BR,6,12,7,13,6,14,BR,BR,7,3,8,4,9,6,9,8,BR,BR,5,4,6,5,7,7,7,8,BR,BR,2,1,3,2,2,3,3,4,1,5,0,3,2,1,BR,BR,2,6,3,7,2,8,3,9,1,10,0,8,2,6,BR,RED,14,4,14,12,16,11,16,9,14,6,BR,BR,3,4,4,5,6,6,7,8,7,10,6,12,3,13,1,10,BR,BR,1,5,2,6,BR,BR,3,9,4,10,BR,GRY,14,7,13,6,12,4,11,3,11,4,10,5,9,5,9,6,BR,BR,8,11,10,12,BR,BR,13,7,13,8,12,8,12,7,13,7,BR,BR,7,3,6,2,5,2,4,3,5,4,BR,BR,6,14,5,14,4,15,4,16,6,16,BR,BR,0,12,1,13,2,15,BR,BR,0,14,1,16}
_lib["jm-120"] = {14,4,10,2,10,4,BR,BR,12,4,14,6,BR,BR,14,11,10,13,10,11,BR,BR,12,11,14,9,BR,BR,11,4,11,6,10,7,10,8,11,9,11,11,10,10,10,9,9,8,9,7,10,6,10,5,11,4,BR,BR,9,5,8,4,9,2,BR,BR,9,10,8,11,9,13,BR,BR,6,4,6,3,8,1,BR,BR,6,11,6,12,8,14,BR,BR,6,5,6,6,5,6,5,5,6,5,BR,BR,6,9,6,10,5,10,5,9,6,9,BR,BR,4,3,2,5,1,4,2,1,3,1,4,3,BR,BR,2,10,4,12,3,13,3,14,2,14,1,11,2,10,BR,RED,14,3,14,12,BR,BR,14,4,15,5,15,10,14,11,BR,BR,4,3,6,4,7,5,8,5,8,6,7,6,6,7,6,8,7,9,8,9,8,10,7,10,6,11,4,12,BR,BR,2,5,3,7,3,8,2,10,BR,GRY,13,6,13,7,12,7,12,6,13,6,BR,BR,13,8,13,9,12,9,12,8,13,8,BR,BR,10,4,8,6,BR,BR,10,11,8,9,BR,BR,8,1,9,0,10,0,10,1,9,2,BR,BR,9,13,10,14,10,15,9,15,8,14,BR,BR,10,4,12,4,BR,BR,10,11,12,11}
_lib["jm-121"] = {10,2,11,1,14,2,14,3,13,3,12,4,10,2,BR,BR,5,2,3,4,2,3,1,3,1,2,4,1,5,2,BR,BR,10,5,10,6,9,6,9,5,10,5,BR,BR,6,5,6,6,5,6,5,5,6,5,BR,BR,11,6,12,6,14,8,BR,BR,10,9,11,8,13,9,BR,BR,4,6,3,6,1,8,BR,BR,5,9,4,8,2,9,BR,BR,11,10,13,10,11,14,BR,BR,4,10,2,10,4,14,BR,BR,11,12,9,14,BR,BR,6,14,4,12,BR,BR,11,11,9,11,8,10,7,10,6,11,4,11,5,10,6,10,7,9,8,9,9,10,10,10,11,11,BR,RED,12,14,3,14,BR,BR,11,14,10,15,5,15,4,14,BR,BR,12,4,11,6,10,7,10,8,9,8,9,7,8,6,7,6,6,7,6,8,5,8,5,7,4,6,3,4,BR,BR,10,2,8,3,7,3,5,2,BR,GRY,14,8,15,9,15,10,14,10,13,9,BR,BR,2,9,1,10,0,10,0,9,1,8,BR,BR,9,8,11,10,BR,BR,6,8,4,10,BR,BR,9,12,9,13,8,13,8,12,9,12,BR,BR,7,12,7,13,6,13,6,12,7,12}
_lib["jm-122"] = {10,2,12,3,11,7,10,7,10,6,9,5,10,2,BR,BR,6,4,6,5,5,5,5,4,6,4,BR,BR,2,2,2,5,4,7,3,8,2,8,0,6,0,0,1,0,2,2,BR,BR,8,10,8,11,5,11,5,10,8,10,BR,BR,8,10,5,11,BR,BR,8,11,5,10,BR,BR,0,14,0,9,1,8,1,11,2,11,1,13,2,14,BR,RED,0,14,8,14,BR,BR,0,14,1,16,3,16,6,14,BR,BR,14,8,15,9,15,13,14,14,12,14,11,13,11,9,12,8,14,8,BR,BR,10,2,9,1,7,1,6,0,4,0,3,1,2,1,BR,BR,9,5,8,4,7,4,6,7,5,8,BR,GRY,2,2,2,1,1,0,0,0,0,2,BR,BR,5,8,5,9,6,10,BR,BR,8,11,9,11,8,12,6,13,5,14,BR,BR,5,12,5,13,4,13,4,12,5,12}

I created a function to draw Jumpman's vectors to the screen.  The function queries the game memory to determine which of the 18 sprites to draw and whereabouts to draw it.
The sprite number is held in RAM at offset 0x694d.  This values matches one of the 18 Jumpman sprites we have in the vector library.  If the sprite number is greater than 128 then we subtract 128 and mirror the output to screen so that Jumpman faces the opposite direction.
e.g. for sprite 142,  we subtract 128 to get 14 which is the jumping left sprite.  We mirror it to get a jumping right sprite.

This function also deals with hammer smashing after Jumpman grabs the hammer.  The hammer follows Jumpman's position and the smashing movement is animated.
Items that are smashed by the hammer are cleared from the screen and a destruction animation is shown.

function draw_jumpman()
local _y, _x = 255 - read(0x6205), read(0x6203) - 15
local _sprite = read(0x694d)
local _sprite_mod = _sprite % 128
local _bright_blue = BLU + 0x1f1f0f
local _smash_offset = 9
local _grab

if _y < 255 then
if vector_lib["jm-"..tostring(_sprite_mod)] then
-- right facing sprites are mirrored.
-- < 128 are left facing sprites
-- (0,1,2) = walking, (3,4,5,6) = climbing, (8,9,10,11,12,13) = hammer smashing,
-- (14,15) = jumping, (120,121,122) = dead

_grab = read(0x6218, 1)
if _grab then -- grabbing hammer
if _sprite < 128 then _sprite = 10 else _sprite = 138 end  -- change default rite to a hammer grab

-- Display one of Jumpman's various sprites
if _sprite < 128 then
draw_object("jm-"..tostring(_sprite), _y-7, _x-7, _bright_blue)
elseif _sprite == 248 then
draw_object("jm-"..tostring(_sprite-128), _y, _x-7, _bright_blue, nil, 8)  -- flip y
draw_object("jm-"..tostring(_sprite-128), _y-7, _x, _bright_blue, 8)  -- flip x

-- Add the hammer?
if _grab then -- grabbing hammer
draw_object("hammer-up", _y+9, _x-3, MBR)
elseif _sprite_mod >= 8 and _sprite_mod <= 13 then  -- using hammer
if _sprite == 8 or _sprite == 10 then
draw_object("hammer-up", _y+9, _x-3, MBR)
elseif _sprite == 12 or _sprite == 140 then
draw_object("hammer-up", _y+9, _x-4, MBR)
elseif _sprite == 136 or _sprite == 138 then
draw_object("hammer-up", _y+9, _x-5, MBR)
elseif _sprite == 9 or _sprite == 11 or _sprite == 13 then
draw_object("hammer-down", _y-4, _x-16, MBR)
_smash_offset = -4
elseif _sprite == 137 or _sprite == 139 or _sprite == 141 then
draw_object("hammer-down", _y-4, _x+13, MBR, 4)
_smash_offset = -4

-- Add smashed item?
_sprite = read(0x6a2d)
if read(0x6a2c) > 0 and _sprite >= 0x60 and _sprite <= 0x63 then
draw_object(0xf00 + _sprite, _y+_smash_offset, read(0x6a2c) - 21, BLU)

This gif shows all of the vector sprites in action plus the hammer smashing.

The barrel stage is now fully playable.  Yay!

I've put out a release of the vectorkong plugin for test purposes.  This release will allow you to play the barrel stage only.

You can download the test release from my github at

Installing is simple.  You should extract the vectorkong folder from the ZIP and copy it over to your MAME plugins folder.

The Plugin is run by adding "-plugin vectorkong" to your MAME arguments e.g.

mame dkong -plugin vectorkong

or you can enable the plugin in your MAME configuration.

I've tested with the latest MAME version 0.244 and it's compatible with all MAME versions from 0.196
There may be a few things to tidy up or optimise.  Any feedback would be greatly appreciated.

I'd like to see how the vectors scale up with higher resolutions.
Does any one have a super high resolution monitor?  I've tested up to 1920 x 1080 with rotated display.  I'm using opengl rendering "-video opengl" for my tests. It would be great to see screenshots at higher resolutions.


In this final part,  I'll complete work on the project by adding a title screen which appears when the game starts,  tweak some of the graphical issues I encountered during testing and build a release which will be made available for download.

First up, I figure that the game needs a better title screen.  The screen would appear on start-up and have a large VECTOR KONG logo made up from flashing vectors.
The game rom was tweaked to display the title screen on launch for 4 seconds before reverting to the default screen sequence.

To action this change,  the machine code of the rom was adjusted during the game initialisation in MAME.  It's pretty neat to make rom changes this way - it's automatically done by the plugin, so no files are change and you don't have to patch anything. 

The first line in code below sets the A register to 6 (title screen) instead of 1 (high score screen).
The second line maximises the time to display this screen to 255 frames.

mem:write_direct_u32(0x01ea, 0x0000063e) -- force game to start on the title screen
mem:write_direct_u16(0x07d3, 0xff3e)     -- increase timer for title screen

Next,  I'll address the graphical tweaks.  There were a few things that didn't feel right after playing through the test release a few times.

1) Hammer
The hit box of the downward smashing hammer was not nearly wide enough - compared to the original game.  I've increased the size by a few pixels and also added lines of destruction which appear as the hammer smashes hits the girder.

2) Bonus/Countdown timer
The timer needed to convey some urgency when time was running out.  I've made the vector characters turn red when the timer drops below 1000.

3) Dying Mario
I've fixed his fat head, mustache and cap when spinning around.  It just didn't look right in my original drawing.  Also,  fixed his lumpy butt and moved his pocket further up when laying on his back.  The straps on his overalls were too long when climbing too.

4) Fonts
The "I" and "II" characters were added as they are necessary for 2 player games.
The "B" character was not quite right.  I fixed it.

Here's an updated gameplay video.

I've decided to limit this project to the barrels stage.  I love how this plays and focusses on the barrels.  I may come back to revisit other stage in the future.

So for now,  the project is complete.  You can download the release from my github at

I've also added Vector Kong to my Donkey Kong Arcade Frontend (DKAFE).  It's simple to set up and then you can play all of the DK hacks on Windows or Pi4 systems. More info at