A JKDF2 and MOTS engine recreation.

Project maintained by bahstrike Hosted on GitHub Pages — Theme by mattgraham


There are a number of improvements to the COG language in Smith.


You can place an autoexec.cog file in your JK directory to have a COG load and run regardless of what level is loaded.

It will be instantiated after all level cogs are instantiated, so its resulting ID will be equal to the total number of level cogs; but it will be lower than any subsequently loaded inventory cogs.

Language features

Strings!! Yes, the “flex” value type in Smith’s COG engine natively supports text and even seamless conversion between strings and other value types. This does not cause any issues with existing JK assets, but unlocks great flexibility for levels/mods designed for Smith. For example, SetThingModel(player, "kyh4.3do"); is totally cool. Even MoveToFrame(door, "2", "12.35"); is perfectly valid; the parsing to int and float happens automatically since MoveToFrame is expecting numerical values for those arguments.

I also plan to add support for using the binary addition operator to concatenate strings (eg. blah = playername + " is a tool";) and for unary postfix increment (eg. for(x=0; x<100; x++)). These features aren’t implemented right now but it’s really easy to do so and some rainy day I’m writing code instead of information webpages then it’ll get in there.

New verbs


API Description
System Core functionality
Form Allows creation of popup dialog boxes. Use to create editing or debugging tools.
Bitmap Allows procedural generation of images for various uses like render-to-texture.
Thing More control over things
Animation Keyframe animation extensions
Debug Miscellaneous stuff intended more for debugging rather than game functionality.



Verb Description
cogID = LoadCOG(filename) Loads a cog
result = IsSmith() simply returns 1 (true). in JK this would be an invalid verb so it would become 0. use this to detect if your level/mod is running in JK or Smith


Calling FormCreate will generate a popup window and return a form ID that can be used for all subsequent form operations.

Any actions on the form that result in a callback will be sent back to the cog as a formevent message.

formevent message

Sent to the cog that created a form whenever an event for the form is generated. For example, when a button is clicked.

Parameter Type Description
sourceref system (1) formID that sent the message
senderref system (1) id of the control


Verb Description
MessageBox(text) displays a simple modal message box popup
formID = FormCreate(title, width, height) create a new popup
FormDestroy(formID) closes and removes the popup
flowHandle = FormGetFlow(formID) gets the main flow layout panel
flowHandle = FlowAddFlow(flowHandle) adds an embedded flow layout panel
FlowAddLabel(flowHandle, text) adds a label
FlowBreak(flowHandle) instructs the layout engine to set the last control as horizontal break. new controls will be added underneath
FlowAddButton(flowHandle, text, controlID) adds a button; controlID will be supplied as senderref in formevent when clicked
matPreviewHandle = FlowAddMaterialPreview(flowHandle, width, height) adds a material preview for use in displaying MAT data
SetMaterialPreviewMaterial(matPreviewHandle, matFilename) sets a MAT filename for display
SetMaterialPreviewEmissive(matPreviewHandle, matFilename) sets a MAT filename for display; displays the emissive/selfilluminated texture component
filepath = BrowseFile(flags, filter) allows user to pick file. flags 0x0 for opening file (must exist), flags 0x1 for saving file. filter format is like “Text Files (*.txt)|*.txt”. note this does not actually open or create a file; just returns a file path. returns -1 if canceled


Calling BitmapCreate will generate a permanent bitmap object. It is your responsibility to either keep reusing it or call BitmapDestroy when finished otherwise memory leak will occur.


Verb Description
bmpID = BitmapCreate(width, height) create a new bitmap. make sure to call BitmapDestroy when finished to avoid memory leak
BitmapDestroy(bmpID) frees bitmap from memory
BitmapClear(bmpID, color) color is specified as string, either “r g b” or “r g b a” with each component being 0.0 - 1.0
bmpID = BitmapLoad(filename) loads a jpg,png,bmp as a new bitmap. make sure to call BitmapDestroy when finished to avoid memory leak
bmpID = BitmapRenderWorld(thing, width, height, fov) renders the world from a thing’s perspective, generating a bitmap of the desired width/height, and with the specified camera field-of-view. make sure to call BitmapDestroy when finished to avoid memory leak
BitmapRenderSoundSpectrum(soundChannel, width, height, numPoints, bgClr, clrA, thickA, clrB, thickB) renders a playing sound’s spectrum analyzation as a bitmap with the desired line colors
BitmapDrawBitmap(bmpID, overlayBmpID) draws overlayBmpID on top. simple method- only draws to 0,0 and does not perform any scaling
BitmapDrawLine(bmpID, x1, y1, x2, y2, color, thickness) draws line using “r g b” or “r g b a” color, and specified thickness in pixels
BitmapGenerateMat(bmpID, materialName, colormapName, cel[, emissiveBmpID]) generates a fake .mat file in memory with the specified name and colormap and cel. can be used to override existing materials. optional last parameter defines an emissive map if desired

Example: Security Camera (render-to-texture)

Repeatedly render the world from a ghost thing’s perspective into a bitmap. Inject that bitmap as a fake material filename of “rendertest.mat” and set the texture of the security TV’s surface to it. Also pregenerates a horizontal “scanlines” overlay to superimpose to make it look more like a tv screen.


Example: Flashing head

Really dumb example that shows that you can dynamically replace any .mat file with a custom bitmap; including on all 3dos such as the back of kyle’s head in this case.



message startup
message pulse




player = GetLocalPlayerThing();
playerSec = GetThingSector(player);

bmp = BitmapCreate(16, 16);

BitmapClear(bmp, tostring(rand()) + " " + tostring(rand()) + " " + tostring(rand()) + " 1.0");
BitmapGenerateMat(bmp, "kyfaceb.mat", GetSectorColormap(playerSec), 0);






Verb Description
modelNodeID = GetThingJointNode(thing, jointID) gets the model node ID from a given thing’s puppet. returns -1 if error
AttachThingToThingNode(attachThing, baseThing, modelNodeID, offset, orient) attaches attachThing to a specific model node of baseThing. it will follow any movement and animations of baseThing



keymarker message

Sent to a thing’s capture cog when a playing keyframe hits a COG Marker.

Parameter Type Description
sourceref thing (3) the thing whose animation hit a COG marker
senderref system (1) id of the marker, either 0 or 1



debugraycast message

Sent to all cogs whenever a thing or surface is right-clicked.

Parameter Type Description
sourceref none (0) always 0
senderref thing (3) or surface (6) id of the thing or surface that was clicked


Since Smith uses FMOD for the sound engine, it natively supports any sound format that FMOD supports. Of course this includes WAV files of any rate and bit depth, but also MP3 and even stuff like MOD.

Seriously, you can do stuff like PlaySoundLocal("lindecis-rekindling.mp3", 1, 0, 0); and it literally just works. Chill track BTW you should check it.