-
Notifications
You must be signed in to change notification settings - Fork 360
Lua And C
CorsixTH is coded in a mixture of Lua 5.1 and C++, along with a tiny smidgen of C. All high-level logic is written in Lua, and all the C/C++ code is bootstrapping Lua, or providing libraries for Lua code to use. This provides an environment where high-level logic can be written quickly in a modern garbage-collected language, and low-level code which needs to be fast can still be written in C/C++. Other advantages of Lua include:
- Stack traces whenever an error occurs (stack traces are hard to do in portable C)
- Much more difficult to cause segmentation faults / memory access violations
- Easier for non-coders to edit
- Compiler not required to make changes
When the CorsixTH executable is launched, execution starts in main.cpp's main
. This creates a Lua state with standard libraries loaded, transfers control to the Lua C function l_main
, and catches any errors.
l_main
(still in main.cpp) adds various libraries to package.preload
for require
to find. Finally, it loads and transfers control to CorsixTH.lua, passing the command line arguments as parameters.
This is the end of the hardcoded initialisation; all further initialisation is performed by Lua code. Execution continues in Lua mode until the application finishes (except for when Lua calls back into C/C++).
CorsixTH.lua
redefines the standard dofile
function to load from the Lua
subdirectory and only do a file a maximum of one time. It proceeds to load Lua/strict.lua, Lua/class.lua, and Lua/app.lua. Finally it creates an instance of the App
class, assigns it to the global TheApp
, calls setCommandLine
and then init
on the instance, and ends by calling run
.
All of the Lua 5.1 standard libraries (basic library, coroutine
, string
, table
, math
, io
, os
, and debug
) are available to Lua scripts.
The semi-standard LuaFileSystem library will always be available via require "lfs"
. This can be used to iterate directories and perform other file system actions which are not available in the standard os
or io
libraries.
To decompress the original Theme Hospital data files, the RNC library will always be available via require "rnc"
.
rnc.decompress(string)
Takes RNC compressed data as a string argument, and returns a string containing decompressed data, or nil and an error message if the input is not RNC compressed, or an error occurred during decomprssion.
An interface to SDL is always available via require "sdl"
, and is used to handle events, create windows, and play music.
sdl.init([["audio",]("video",]) [["*"]("timer",]))
Initialises the SDL components specified via string arguments, or all components if "*"
is an argument. Returns true on success and false on error.
sdl.getFPS()
If the frame rate is being tracked (which it isn't by default, but it can be by calling sdl.trackFPS
), then returns (as an integer) the number of frames rendered in the last 1000 milliseconds. If the frame rate is not being tracked, then nil is returned.
sdl.trackFPS([false])
Enables or disables frame rate tracking.
sdl.limitFPS([false](false])))
If called with no arguments, or true, then frames are only rendered when required (which is the default behaviour if limitFPS
is never called). If called with false, then frames are rendered all the time except for when there are events to handle. Should only be called when comparing rendering engines or other similar situations. Note that the DX9 rendering engine is also limited to the screen refresh rate unless CORSIX_TH_DX9_UNLIMITED_FPS
is defined in C.
sdl.mainloop(thread)
Waits for events to occur, and calls the given coroutine to handle each one. Also calls the coroutine every 30 milliseconds to handle a timer event, and calls it with a frame event whenever a frame needs to be rendered.
The coroutine should yield true if a repaint is required in response to the event. If the coroutine finishes execution, then mainloop
will return, passing the return values of the coroutine. If the coroutine has an error, then mainloop
will also return, passing the error message.
sdl.getTicks()
Returns the number of milliseconds since the SDL library initialization. Note that this value wraps if the program runs for more than ~49 days.
sdl.wm.setCaption(string)
Sets the window title to the given string.
sdl.wm.setIconWin32()
On Windows, sets the window icon to the icon contained as a resource in the executable and returns true if successful. On other platforms, or in the event of an error, returns false. Note that this must be called after sdl.video.setMode
.
sdl.wm.showCursor([true])
If called with no arguments, or false, then the operating system cursor is hidden. If called with any other value, the operating system cursor is shown.
sdl.audio.loaded
Boolean value which is set to true if audio functions are available, and false is they are not.
sdl.audio.init([frequency [, num_channels [, buffer_size]]](true])))
Initialises the SDL_Mixer library using the given settings. Note that buffer_size
must be a power of 2. Returns true, or false and an error string.
sdl.audio.transcodeXmiToMid(xmi_data)
TODO
sdl.audio.loadMusic(midi_data)
TODO
sdl.audio.loadMusicAsync(midi_data, callback)
TODO
sdl.audio.playMusic(music [num_loops](,))
TODO
sdl.audio.stopMusic()
TODO
sdl.audio.pauseMusic()
TODO
sdl.audio.resumeMusic()
TODO
sdl.audio.setMusicVolume(volume)
TODO
A selection of Theme Hospital related classes and functions will always be available via {{{require "TH"}}}.
TH.LoadStrings(string)
Expects the contents of a Theme Hospital strings file as a string argument, and returns a two-dimensional array of strings. The first dimension is the section number (1-based), and the second dimension is the string number within that section (1-based). In the event of an error, false is returned.
Wraps the C++ class THMap
.
Lua method | C++ function(s) | Glue function |
---|---|---|
load |
THMap::loadFromTHFile |
l_map_load |
size |
THMap::getWidth , **` THMap::getHeight ** |
l_map_getsize |
getCell |
THMap::getNode |
l_map_getcell |
getCellFlags |
THMap::getNode |
l_map_getcellflags |
setCellFlags |
THMap::getNode |
l_map_setcellflags |
setCell |
THMap::getNode |
l_map_setcell |
setWallDrawFlags |
THMap::setAllWallDrawFlags |
l_map_setwallflags |
updateRoomBlueprint |
THMap::getWidth , THMap::getHeight , THMap::getNodeUnchecked
|
l_map_updateblueprint |
updateShadows |
THMap::updateShadows |
l_map_updateshadows |
{{{markRoom}}} | {{{THMap::getWidth}}}, {{{THMap::getHeight}}}, {{{THMap::getNodeUnchecked}}}, {{{THMap::updatePathfinding}}}, {{{THMap::updateShadows}}} | {{{l_map_mark_room}}} |
{{{unmarkRoom}}} | {{{THMap::getWidth}}}, {{{THMap::getHeight}}}, {{{THMap::getNodeUnchecked}}}, {{{THMap::getOriginalNodeUnchecked}}}, {{{THMap::updatePathfinding}}}, {{{THMap::updateShadows}}} | {{{l_map_unmark_room}}} |
{{{setSheet}}} | {{{THMap::setBlockSheet}}} | {{{l_map_set_sheet}}} |
{{{draw}}} | {{{THMap::draw}}} | {{{l_map_draw}}} |
{{{hitTestObjects}}} | {{{THMap::hitTest}}} | {{{l_map_hittest}}} |
Wraps the C++ class THPalette
.
Lua method | C++ function(s) | Glue function |
---|---|---|
load |
THPalette::loadFromTHFile |
l_palette_load |
Wraps the C++ class THRawBitmap
.
Lua method | C++ function(s) | Glue function |
---|---|---|
`` load ` | THRawBitmap::loadFromTHFile |
l_rawbitmap_load |
setPalette |
THRawBitmap::setPalette |
l_rawbitmap_set_pal |
draw |
THRawBitmap::draw |
l_rawbitmap_draw |
Wraps the C++ class THSpriteSheet
.
Lua method | C++ function(s) | Glue function |
---|---|---|
metamethod {{{__len}}} | {{{THSpriteSheet::getSpriteCount}}} | {{{l_spritesheet_count}}} |
{{{load}}} | {{{THSpriteSheet::loadFromTHFile}}} | {{{l_spritesheet_load}}} |
{{{setPalette}}} | {{{THSpriteSheet::setPalette}}} | {{{l_spritesheet_set_pal}}} |
{{{size}}} | {{{THSpriteSheet::getSpriteCount}}}, {{{THSpriteSheet::getSpriteSizeUnchecked}}} | {{{l_spritesheet_size}}} |
{{{draw}}} | {{{THSpriteSheet::drawSprite}}} | {{{l_spritesheet_draw}}} |
Wraps the C++ class THFont
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{sizeOf}}} | {{{THFont::getTextSize}}} | {{{l_font_get_size}}} |
{{{setSheet}}} | {{{THFont::setSpriteSheet}}} | {{{l_font_set_spritesheet}}} |
{{{setSeparation}}} | {{{THFont::setSeparation}}} | {{{l_font_set_sep}}} |
{{{draw}}} | {{{THFont::getTextSize}}}, {{{THFont::drawText}}} | {{{l_font_draw}}} |
{{{drawWrapped}}} | {{{THFont::drawTextWrapped}}} | {{{l_font_draw_wrapped}}} |
Wraps the C++ class THLayers_t
.
Lua method | C++ function(s) | Glue function |
---|---|---|
metamethod {{{__index}}} | {{{THLayers_t::iLayerContents}}} | {{{l_layers_get}}} |
metamethod {{{__newindex}}} | {{{THLayers_t::iLayerContents}}} | {{{l_layers_set}}} |
Wraps the C++ class THAnimationManager
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{load}}} | {{{THAnimationManager::loadFromTHFile}}} | {{{l_anims_load}}} |
{{{setSheet}}} | {{{THAnimationManager::setSpriteSheet}}} | {{{l_anims_set_spritesheet}}} |
{{{getFirstFrame}}} | {{{THAnimationManager::getFirstFrame}}} | {{{l_anims_getfirst}}} |
{{{getNextFrame}}} | {{{THAnimationManager::getNextFrame}}} | {{{l_anims_getnext}}} |
{{{setAnimationGhostPalette}}} | {{{THAnimationManager::setAnimationAltPaletteMap}}} | {{{l_anims_set_alt_pal}}} |
{{{draw}}} | {{{THAnimationManager::drawFrame}}} | {{{l_anims_draw}}} |
Wraps the C++ class THAnimation
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{setAnimation}}} | {{{THAnimation::setFlags}}}, {{{THAnimation::setAnimation}}} | {{{l_anim_set_anim}}} |
{{{setMorph}}} | {{{THAnimation::setMorphTarget}}} | {{{l_anim_set_morph}}} |
{{{setFrame}}} | {{{THAnimation::setFrame}}} | {{{l_anim_set_frame}}} |
{{{getAnimation}}} | {{{THAnimation::getAnimation}}} | {{{l_anim_get_anim}}} |
{{{setTile}}} | {{{THAnimation::removeFromTile}}}, {{{THAnimation::attachToTile}}} | {{{l_anim_set_tile}}} |
{{{getTile}}} | {{{THAnimation::getPrevious}}} | {{{l_anim_get_tile}}} |
{{{setFlag}}} | {{{THAnimation::setFlags}}} | {{{l_anim_set_flag}}} |
{{{setPartialFlag}}} | {{{THAnimation::setFlags}}}, {{{THAnimation::getFlags}}} | {{{l_anim_set_flag_partial}}} |
{{{getFlag}}} | {{{THAnimation::getFlags}}} | {{{l_anim_get_flag}}} |
{{{makeVisible}}} | {{{THAnimation::setFlags}}}, {{{THAnimation::getFlags}}} | {{{l_anim_make_visible}}} |
{{{makeInvisible}}} | {{{THAnimation::setFlags}}}, {{{THAnimation::getFlags}}} | {{{l_anim_make_invisible}}} |
{{{setTag}}} | {{{l_anim_set_tag}}} | |
{{{getTag}}} | {{{l_anim_get_tag}}} | |
{{{setPosition}}} | {{{THAnimation::setPosition}}} | {{{l_anim_set_position}}} |
{{{getPosition}}} | {{{THAnimation::getX}}}, {{{THAnimation::getY}}} | {{{l_anim_get_position}}} |
{{{setSpeed}}} | {{{THAnimation::setSpeed}}} | {{{l_anim_set_speed}}} |
{{{setLayer}}} | {{{THAnimation::setLayer}}} | {{{l_anim_set_layer}}} |
{{{setHitTestResult}}} | {{{l_anim_set_hitresult}}} | |
{{{tick}}} | {{{THAnimation::tick}}} | {{{l_anim_tick}}} |
{{{draw}}} | {{{THAnimation::draw}}} | {{{l_anim_draw}}} |
Wraps the C++ class THPathfinder
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{findDistance}}} | {{{THPathfinder::findPath}}}, {{{THPathfinder::getPathLength}}} | {{{l_path_distance}}} |
{{{isReachableFromHospital}}} | {{{THPathfinder::findPathToHospital}}}, {{{THPathfinder::getPathEnd}}} | {{{l_path_is_reachable_from_hospital}}} |
{{{findPath}}} | {{{THPathfinder::findPath}}}, {{{THPathfinder::pushResult}}} | {{{l_path_path}}} |
{{{findIdleTile}}} | {{{THPathfinder::findIdleTile}}}, {{{THPathfinder::getPathEnd}}} | {{{l_path_idle}}} |
{{{findObject}}} | {{{THPathfinder::visitObjects}}} | {{{l_path_visit}}} |
{{{setMap}}} | {{{THPathfinder::setDefaultMap}}} | {{{l_path_set_map}}} |
Wraps the C++ class THCursor
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{load}}} | {{{THCursor::createFromSprite}}} | {{{l_cursor_load}}} |
{{{use}}} | {{{THCursor::use}}} | {{{l_cursor_use}}} |
{{{setPosition}}} | {{{l_cursor_position}}} |
Wraps the C++ class THRenderTarget
.
Lua method | C++ function(s) | Glue function |
---|---|---|
{{{fillBlack}}} | {{{THRenderTarget::fillBlack}}}, {{{THRenderTarget::getLastError}}} | {{{l_surface_fill_black}}} |
{{{startFrame}}} | {{{THRenderTarget::startFrame}}}, {{{THRenderTarget::getLastError}}} | {{{l_surface_start_frame}}} |
{{{endFrame}}} | {{{THRenderTarget::endFrame}}}, {{{THRenderTarget::getLastError}}} | {{{l_surface_end_frame}}} |
{{{nonOverlapping}}} | {{{THRenderTarget::startNonOverlapping}}}, {{{THRenderTarget::finishNonOverlapping}}} | {{{l_surface_nonoverlapping}}} |
{{{mapRGB}}} | {{{THRenderTarget::mapColour}}} | {{{l_surface_map}}} |
{{{drawRect}}} | {{{THRenderTarget::fillRect}}}, {{{THRenderTarget::getLastError}}} | {{{l_surface_rect}}} |
{{{takeScreenshot}}} | {{{THRenderTarget::takeScreenshot}}}, {{{THRenderTarget::getLastError}}} | {{{l_surface_screenshot}}} |
Wraps the C++ class THSoundArchive
.
Lua method | C++ function(s) | Glue function |
---|---|---|
metamethod __len
|
{{{THSoundArchive::getSoundCount}}} | {{{l_soundarc_count}}} |
{{{load}}} | {{{THSoundArchive::loadFromTHFile}}} | {{{l_soundarc_load}}} |
{{{getFilename}}} | {{{THSoundArchive::getSoundFilename}}} | {{{l_soundarc_filename}}} |
{{{getDuration}}} | {{{THSoundArchive::getSoundDuration}}} | {{{l_soundarc_duration}}} |
{{{getFileData}}} | {{{THSoundArchive::loadSound}}} | {{{l_soundarc_filedata}}} |
Wraps the C++ class THSoundEffects
.
Lua method | C++ function(s) | Glue function |
---|---|---|
setSoundArchive |
THSoundEffects::setSoundArchive |
l_soundfx_set_archive |
play |
THSoundEffects::playSound , THSoundEffects::playSoundAt
|
l_soundfx_play |
setSoundEffectsOn |
THSoundEffects::setSoundEffectsOn |
l_soundfx_sound_effects_on |
setSoundVolume |
THSoundEffects::setSoundEffectsVolume |
l_soundfx_set_sound_volume |
setCamera |
THSoundEffects::setCamera |
l_soundfx_set_camera |