Retail Clerk '90

A little advance warning.. this page is kind of rambling and is updated at seemingly random times. It will remain that way until I'm done working on Retail Clerk '90.

What's the purpose of all this?

Back in 2019 I released a Sega Genesis game demo called Retail Clerk '89. It wasn't my first attempt, or even tenth, to create a game demo. All my previous attempts failed because I was too caught-up trying to build a reusable framework first. In a textbook sense that may be a good thing to do. In my reality it prevented me from ever finishing anything.

With Retail Clerk '89 I took a different approach - just start hacking away at a demo until it barely works. It wasn't pretty but I finished something this time. It's not exactly what I wanted to do. My starting idea was something like Tombs & Treasure where the mall hallways were the overworld and entering stores changed to that Shadowgate-like perspective. I'm not doing that with Retail Clerk '90 either but perhaps in whatever comes next. Anyway, by the time I was done I had the basics in place: walking around, talking to people, managing items, playing music, and text paging.

I knew I wasn't going to stop at one demo. I wanted to keep building on this until I had something more closely resembling my original plan. The first step is untangling the mess I made into modular parts. That's really the goal of Retail Clerk '90, take what I have and make it reusable.

This page is documenting my attempts at:

  1. Extracting the reusable parts of Retail Clerk '89 into a game engine of sorts.
  2. Figuring out how to create build tooling that generates any code that can be reasonably auto-generated.
Along the way it's possible that someone reading this might learn something helpful.

Retail Clerk "Engine"

I've been doing professional software development for over 20 years and hobby development for even longer. Despite that I have no idea what the difference is between a framework, engine, library, and random assortment of related code. Yes, I know the technical definitions for each of these. Projects seem to choose one of these as a label with no regards to the definition.

I'm going with the Retail Clerk "Engine" - although I won't claim it meets the formal definition of a game engine today (or maybe even tomorrow).

Retail Clerk Main Loop

Retail Clerk '89 had a main loop, it worked but was disorganized. It checked for things in an illogical order that resulted in a lot of jumping around. I wasn't close to having framerate issues because of it. It was just difficult to understand the flow, even for the odd person who wrote it.

In Retail Clerk '90 it is better organized into a flow I can explain. At the top is all the system and game initialization code. Obvious enough place for it.

Then we enter the main loop. At each pass through the loop we wait for a VBlank to occur (60 times a second). During that window of time controllers are read and sprite z-order is sorted. That second part is still a little hacky. Sprite z-ordering is difficult on the Genesis, maybe I'm not smart enough to understand it. I kept having issues where sprites were not overlapping correctly. Resorting the sprite list at every VBlank is overkill but it sure works. It's the only thing that has worked reliably for me.

After returning to the main loop we check for all the game states where the player is not playing the game. There are checks for the title screen, load screen, and information screen. If the game is in any of these states there's a branch to process the corresponding screen.

If all those checks fail then the player is actually playing the game. It had to happen eventually. Then there are checks for other states in order of importance. Checking for pause/unpause is first since the game can be paused at any time. They there's a check for whether an action is queued. Actions include things like talking to an NPC or changing rooms. If there are no actions queued the next check is for a scripted event. Think of this like checking for whether a cut-scene is running. After that is a check for whether the player is on a status (menu) screen. If all of those fail then the player is in the overworld doing stuff.

The last two check are for whether the player did something like open the menu, interact with an object, or move.

This general flow likely won't change much as I evolve this "engine". If I ever add battles (unlikely) there would be changes for sure.


The game itself is centered around something I called a "scene" for lack of a better term. A scene is the area the player is currently in. The definition includes everything required to draw the scene and the objects the player can interact with.

Scene Object

This idea was originally meant to represent the first-person Shadowgate-like rooms I mentioned before. I'm happy that ultimately this structure worked for everything. I really mean "everything".. everything in Retail Clerk '90 is a scene. Each store is a scene, the title screen is a scene, anything else is also a scene. It's a structure that is very reusable and makes creating new screens & places simple.

Let's breakdown what a scene object contains...

The first two fields are address pointers to the tiles and palettes used to draw the scene. The font and dialog tiles are loaded by default in every scene so they are not included in the definition. The Scenery and Text sections contain the instructions for what to draw. Each Scenery entry has an address pointer to a pattern which I admit is a confusing name since the Genesis VDP also has patterns. In this case it's the order to draw tiles. Text entries don't have a pattern since the text itself describes where formatting like line breaks occur. Text entries just have a pointer to the text, a draw location, and which palette to use.

The Objects list contains the location of things in the scene the player can interact with. It does not include draw instructions which can be confusing. Anything that needs to be drawn goes in the Scenery block.

Collision data is another address pointer to a list of 1s & 0s that are used to determine where the player can/can't walk.

The Exists list is simply four values that indicate what scene to load when the player leaves the current scene.

The NPC list is similar to the Objects list. This defines where NPCs will be drawn if any are in the scene. Which NPCs are in the scene is not defined here. The game maintains a list of NPC to Scene mappings allowing NPCs to change locations.

The last entry is a pointer to which music to play in the scene. I originally wanted different music in each store. That clearly did not pan out. That will probably wait until I try making a 16-bit CD game. Even then, expect a lot of bad public domain tracks.


This is not a bad place to transition to the build tooling. This section isn't quite complete yet, it's missing the memory map for example, but let's move on anyway. The build tooling is mostly (not exclusively) focused on things that go into a scene.

Build Tooling

In one of my many failed game development projects I started with a UI to build various objects but didn't have a great plan for how the game would actually work. This time around a UI, if I even decide to create one, will be the last piece.

Build overview

The not-secret long-term goal of all this is to generate a game for multiple consoles. This means keeping all the external assets like images, palette definitions, and scene definitions in some convenient file format. At the moment this is a combination of png, json, and csv files.

Then I'm planning a set of tools to convert all these things into platform-specific code. The Genesis is first since that's what I already have working. Next will likely be another 68000-based system out of laziness. After I burn a couple months learning a new assembly language it will be something else.

You can see the working version of it here: https://github.com/huguesjohnson/RetailClerk90/tree/main/build-tools. I'll maybe get around to documenting how that current assortment of code actually works.


In terms of what is being auto-generated today, I think looking at the ROM map is the best place to start.

Things that are currently auto-generated

The first block is not code - it's all the various constants used throughout the game. These are currently generated from csv files.

The header is more or less also a series of constants. I have that defined in the build script. This part will have to be reworked to support other consoles.

The next block is console-specific code - if I was smarter I'd separate it into CPU-specific code with wrappers for console-specific things. After that is the action script which is like 90% of the game logic. I would really like to abstract that out an intermediate language to make it more portable. This by far the biggest piece of work ahead of me in terms of building a reusable game engine.

The sound driver is last in the code block for kind of a silly reason. If if goes before the action script then it bumps some functions calls into a range where long branches are needed.

Outside of the console initialization data everything in the data block is generated. More to come on how each of these work.

Lookup tables are exactly what they sound like. I need to automated their generation next.

The section called "resources" is easy to confuse with the data block. Everything in the data block should be the same across versions. Things in resources are volatile based on the version. Text may vary based on language. Music files definitely would vary based on platform. Tiles may be the same in some cases but I expect not always. Looking at this now, palettes should probably go here too. I guess when I thought of the resources section I based it off how dlls are structured.


Let's go through an example build file section by section to see how everything is generated today. The latest version is here probably: https://github.com/huguesjohnson/RetailClerk90/blob/main/build.json.


{
	"basePath":".",
	"backupPath":"../../Backup/RetailClerk90/2021",

[...]

This is the most simple part, it's defining the working paths.


[...]
	"memoryMap":{
		"sourceFile":"design/constants/MemoryMap.csv",
		"destinationFile":"src/const_MemoryMap.X68",
		"baseAddress":"FFFF0000"
	},
[...]

This section generates the memory map from a .csv file. The .cvs file looks like:


;-------------------------------------------------------------------------------
; debug registers
;-------------------------------------------------------------------------------
MEM_DEBUG_1,2,general debug register
MEM_DEBUG_2,2,general debug register
;-------------------------------------------------------------------------------
; table to sort the draw order of sprites
;-------------------------------------------------------------------------------
MEM_SPRITE_SORT_TABLE_SORTED,2,0000=sorted
MEM_SPRITE_SORT_TABLE_ID0,2,sort table ID 0
MEM_SPRITE_SORT_TABLE_VALUE0,2,sort table value 0
[...]

Thing that start with ';' are comments - if I thought ahead I would have used the more standard '//'. I'm sure I'll get around to that eventually. Each row then has a constant name, size (number of bytes), and comment. It generates code that looks like:


;-------------------------------------------------------------------------------
; debug registers
;-------------------------------------------------------------------------------
MEM_DEBUG_1=$FFFF0000	; general debug register
MEM_DEBUG_2=$FFFF0002	; general debug register
;-------------------------------------------------------------------------------
; table to sort the draw order of sprites
;-------------------------------------------------------------------------------
MEM_SPRITE_SORT_TABLE_SORTED=$FFFF0004	; 0000=sorted
MEM_SPRITE_SORT_TABLE_ID0=$FFFF0006	; sort table ID 0

The end the size values are used to determine the start address of each thing in the memory map.

The next section is similar, it is used to build constants:


[...]
	"constants":{
		"fileMap":{
			"design/constants/Characters.csv":"src/const_Characters.X68",
			[...]
			"design/constants/RetailClerk90.csv":"src/const_RetailClerk90.X68"

		},
		"includeFilePath":"src/inc_Constants.X68"

[...]

It's very simple, just convert key-value pairs in a csv to the code equivalent.

The next section has a little more heavy lifting:


[...]
	"collision":{
		"collisionMap":{
			"design/collision/BasementCafe.png":"src/collision-maps/BasementCafeCollision.X68",
			[...]
			"design/collision/WWTV.png":"src/collision-maps/WWTVCollision.X68"
		},
		"includeFilePath":"src/inc_CollisionMaps.X68"	
[...]

My general approach for collision detection hasn't changed much since I wrote this: https://huguesjohnson.com/programming/genesis/collision-detection/. Generating the collision data is new though.

Example collision map

For lack of a better idea I'm generating the collision data based on images. Pictured here is the collision map for the first scene in Retail Clerk '90. It's just a little version of the scene minus the scenery. The work to convert the image to collision data is here: https://github.com/huguesjohnson/RetailClerk90/blob/main/build-tools/src/com/huguesjohnson/retailclerk/build/BMPtoCollisionData.java


I found this was an easy way to generate the collision data. A smarter version of me would have it in the scene definition. The objects being drawn are defined there and it wouldn't be too difficult to flag objects as being solid and figuring out the collision data based on that. Yeah, that's a good idea. I should add that to my mental backlog.

Next up are the palettes:


[...]
	"palettes":{
		"paletteMap":[
			{
				"name":"PaletteBlack",
				"sourceFilePath":"design/img/swatches/black.png",
				"destinationFilePath":"src/palettes/Black.X68",
				"exclude":"false"
			},
			[...]			
			{
				"name":"PaletteWWHall",
				"sourceFilePath":"design/img/swatches/wwhall.png",
				"destinationFilePath":"src/palettes/WWHall.X68"
			}
		],
		"includeFilePath":"src/inc_Palettes.X68"
[...]

The palette and tile generation mostly works like what I described here: https://huguesjohnson.com/programming/genesis/palette-tile-generation/.

Example palette

The general idea is I take an image and map the first 16 colors in it to their nearest Sega Genesis equivalent. Assuming I ever support another console this is code I'll need to extend a little.


The tiles are next:


[...]
	"tiles":{
		"tilesets":[
			{
				"name":"TransparentTile",
				"palette":"PalettePeople",
				"sourceFilePath":"design/img/scene-tiles/transparent.png",
				"destinationFilePath":"src/tiles/scene-tiles/transparent-tile.X68",
				"allowDuplicateTiles":"false"
			},
			[...]
			{
				"name":"MenuMap",
				"palette":"PaletteMenu00",
				"sourceFilePath":"design/img/scene-tiles/map.png",
				"destinationFilePath":"src/tiles/scene-tiles/menu-map.X68",
				"patternFilePath":"src/patterns/menu-map.X68"
			}
		],
		"tileIncludeFilePath":"src/inc_Tiles.X68",
		"patternIncludeFilePath":"src/inc_PatternsGenerated.X68"		
[...]

This is similar to the article I linked to a few lines up. A tileset is an image sized to some multiple of 8x8. Each pixel in the tileset is mapped to the nearest color in the assigned palette. A pattern may or may not be created for each tileset. It really depends whether I'll be later drawing the tileset in a generic pattern (like 32x32 for example) or if that tileset has a unique shape (like some store-specific scenery).

Building sprites is very similar to building tiles:


[...]
	"sprites":{
		"sprites":[
			{
				"name":"Eryn",
				"sourceFilePath":"design/img/sprite-tiles/pc-eryn.png",
				"destinationFilePath":"src/tiles/sprite-tiles/pc-eryn.X68"
			},
			[...]
			{
				"name":"Victor",
				"sourceFilePath":"design/img/sprite-tiles/npc-victor.png",
				"destinationFilePath":"src/tiles/sprite-tiles/npc-victor.X68"
			}	
		],
		"palette":"PalettePeople",
		"includeFilePath":"src/inc_SpriteTiles.X68",
		"characterDefinitionFilePath":"src/data_CharacterDefinitions.X68",
		"constantDefinitionPath":"src/const_CharacterIDs.X68",
		"nameLookupTableFilePath":"src/text/table_CharacterNames.X68",
		"nameFilePath":"src/text/en-us/CharacterNames.X68",
		"baseId":"2000"
					
[...]

There are a couple differences. Like one palette is used to create all sprites. This also builds constants for each character ID and a text file with their names.

The most complicated part is building the scenes. It looks simple at a glance:


[...]
	"scenes":{
		"scenePaths":[
			"/design/scene-json/legal.json",
			[...]
			"/design/scene-json/basementoffice.json"
		],
		"includeFilePath":"src/inc_Scenes.X68"
[...]

That's because all the real stuff is buried in the scene definitions. Let's dig into one:


[...]
{
	"name":"SceneDenimCountry",
	"id":"SCENE_ID_DENIMCOUNTRY",
	"destinationFilePath":"src/scenes/DenimCountry.X68",
[...]

So far it's nice and simple.

Now we get to importing tiles. Each tileset name needs to be included in the prior tilesets section. These are loaded into memory so we can later build the scenery entries. This might make sense in a little bit.


[...]
	"tilesetNames":[
		"TransparentTile",
		[...]
		"DenimCountryTableLow"
	],
[...]

The next part of the scene definition is the palette names. I suppose these don't need to exist in the palettes section but the ROM won't compile later on if they don't.


[...]
	"paletteNames":[
		"PaletteDenimCountry00",
		"PaletteDenimCountry01",
		"PaletteDenimCountry02",
		"PalettePeople"
	],
[...]

Scenery is where a few things come together. Let's start with the definition:


[...]
	"scenery":[
		{
			"patternName":"PatternWoodFloorV",
			"comment":"background",
			"tilesetIndex":1,
			"tilesetOffset":0,
			"highPriority":false,
			"paletteNumber":0,
			"repeat":13,
			"layer":"VDP_VRAM_WRITE_B",
			"row":"00000000",
			"column":"00000000"
		},
		[...]
		{
			"patternName":"PatternDenimCountryTableLow",
			"tilesetIndex":6,
			"tilesetOffset":0,
			"highPriority":false,
			"paletteNumber":2,
			"repeat":0,
			"layer":"VDP_VRAM_WRITE_A",
			"row":"07000000",
			"column":"00080000"
		}
	],
[...]

Again there's some Genesis-specific stuff in here I need to clean-up later. Layer, row, and column all need to be re-worked to something generic before trying to support a 2nd console.

This json ultimately leads to code that looks like:


[...]
	dc.l	PatternDenimCountryTableLow
	;		%pccvhnnnnnnnnnnn
	dc.w	%0100000000110100 ; vdp pattern
	dc.w	$0000 ; repeat=0
	dc.l	VDP_VRAM_WRITE_A+$00080000+$07000000 ; initial drawing location
[...]

The next section in the scene definition is for the location of virtual objects.


[...]
	"objects":[
		{
			"id":"OBJ_SCENE_DC_MERCH",
			"x":160,
			"y":232,
			"width":71,
			"height":40
		},

[...]

The mechanics of working with objects hasn't changed much since I wrote this: https://huguesjohnson.com/programming/genesis/objects/.

Next up is a pointer to the collision data:


[...]
	"collisionDataName":"DenimCountryCollisionStart",
[...]

Then there's a section to define the exits in the order of south, north, west, east:


[...]
	"exitIds":[
		"$FFFF",
		"SCENE_ID_EWHALL",
		"SCENE_ID_SOUTH_CENTER",
		"$FFFF"
	],
[...]

The next part defines where NPCs would be located in the scene:


[...]
	"npcLocations":[
		{
			"x":192,
			"y":144,
			"direction":"DIRECTION_DOWN",
			"movementFrequency":65535,
			"movementPatternName":"NullMovement"
		},
		[...]
		}
[...]

The last part of the scene definition is the pointer to the background music:


[...]
	"bgmName":"BGM_Mall"
}

Alright, let's move back to the main build file. This next part is very console-specific and will eventually be reworked:


[...]
	"header":{
		"filePath":"src/init_Header.X68",
		"copyright":"\u0027(C)HUJO \u0027",
		"cartName":"\u0027Retail Clerk 90                                 \u0027",
		"romStart":"$00000000",
		"romEnd":"RomEnd",
		"ramStartEnd":"$FFFF0000,$FFFFFFFF",
		"sramType":"\u0027RA\u0027,$F8,$20",
		"sramStart":"SRAM_START",
		"sramEnd":"SRAM_END",
		"comment":"\u0027https://HuguesJohnson.com/              \u0027"
	},
[...]

Almost at the end now... here are the commands to build the ROM.


[...]
	"assembly":[
		{
			"assemblerPath":"src/",
			"arguments":"vasmm68k_mot -o ../build/RetailClerk90.bin -Fbin -spaces -D_DEBUG_\u003d0 -D_ATGAMES_HACKS_\u003d0 RetailClerk90.X68"
		},
		[...]
	],
[...]

The last step is packaging the ROM and any associated text files into an archive.


[...]
	"packageParameters":[
		{
			"includeFilePaths":[
				"/build/RetailClerk90.bin",
				"CREDITS",
				"LICENSE",
				"README.md"
			],
			"packagePath":"/build/RetailClerk90.zip"
		},
		[...]
		}
	]
}

Congratulations on making it this far. I'm not what will be added next right now.


This page uses Lightbox2 by Lokesh Dhakar licensed under the The MIT License.