A JKDF2 and MOTS engine recreation.
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.
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.
|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.|
|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.
Sent to the cog that created a form whenever an event for the form is generated. For example, when a button is clicked.
|sourceref||system (1)||formID that sent the message|
|senderref||system (1)||id of the control|
|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.
|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|
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.
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.
flags=0x240 symbols message startup message pulse end code startup: setpulse(0.01); return; 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); BitmapDestroy(bmp); return; end
|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|
Sent to a thing’s capture cog when a playing keyframe hits a COG Marker.
|sourceref||thing (3)||the thing whose animation hit a COG marker|
|senderref||system (1)||id of the marker, either 0 or 1|
Sent to all cogs whenever a thing or surface is right-clicked.
|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.