Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
This is the API rference for Umbra. Please note that it only covers the part of the API that is exposed to the developer, id est, public and protected class members.
<<tabs api
"Classes" "Classes" [[Classes]]
"Methods" "Methods" [[Methods]]
"Operators" "Operators" [[Operators]]
"Callbacks" "Callbacks" [[Callbacks]]
"Enums" "Enums" [[Enums]]
"Flags" "Flags" [[Flags]]
"Defines" "Defines" [[Defines]]
>>
Umbra is a general purpose framework for applications that use libtcod, mainly games. It is thought to provide a "module player" functionality more than anything else, meaning that it lacks any out-of-the-box support for game logic or other game elements. The game/app developer will still need to write everything from scratch, but Umbra will help divide the game into manageable chunks, or [[modules|What is a module]]. Also, the [[engine|The engine]] takes control over the main game loop and screen flushing, provides a global keybindings method and lets the developer define module-specific keybindings.
Umbra is released under the BSD licence and thus may be freely used by anyone for any purpose. The licence text is as follows:
!!Umbra
''Copyright (c) 2009, 2010 Mingos, Jice''
''All rights reserved.''
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* The names of Mingos or Jice may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY MINGOS & JICE "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MINGOS OR JICE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Modules and widgets alike are accessed in the same way, as the engine does not distinguish between them.
The method used to gain access to a module from within the engine is [[UmbraEngine::getModule]]. It takes the [[module ID number|Module ID]] as parametre, and returns a pointer to the module with that ID.
!Example
<code C++>
myModule->getEngine()->getModule(2)->setActive(true);
</code>
The above code, executed from within a module, will fetch a pointer to the engine, then to a module with an ID number of 2, and activate it.
If the module's [[name|Naming modules]] is known, it is also possible to access it by its name.
!Example
<code C++>
myModule->getEngine()->getModule("my module")->setActive(true);
</code>
Bear in mind that this method, although more intuitive and easier to use, is also slower.
A registered module resides inside the modules list, but before the engine actually runs any of the modules from the list, the developer has to specify which one needs to be activated. Typically, a main menu, a title screen or a credits screen will be the first activated module.
Module activation is extremely simple. Once a module is registered, it is given an ID number, which the developer can deduce from the order in which the modules were registered. This number can then be used to activate a module. The method that actually does the trick is [[UmbraModule::activateModule]]. Instead of the ID number, the method also accepts the module reference as an argument. In both cases, the chosen module will be put on a separate list of modules destinated for activation in the next frame. The engine should run with no problems once a module is active.
It would be nice if there were keyboard callbacks also in module scope, not only globally.
The BSOD is an internal module created with the sole purpose of notifying the developer that a non fatal error has occurred in the program. It is supposed to be used in conjunction with the methods in the [[UmbraLog]] class.
The BSOD is, as the name implies, a blue screen. It's not as annoying as its namesake, as it has the form of a small, semitransparent blue box that disappears a few seconds after it is displayed. Additionally, it can be moved around in case it happens to cover something important or even closed with a close button in the top right corner.
The module, when triggered using the method [[UmbraEngine::displayError]], will display the last registered log mesage (or the line "No messages logged" in case it is displayed before any messages have been added to the log). Here is an example use:
<code C++>
if (errorCondition) {
UmbraLog::error("Example error message.");
UmbraEngine::getInstance()->displayError();
}
</code>
Please note that this module requires the console emulator to be active, otherwise it won't be displayed. For this reason, the BSOD is only useful for displaying non fatal errors that occur after the engine initialisation. In case a fatal error is encountered, it will typically call {{{exit(1);}}} instead of {{{displayError();}}}. The BSOD is useless in such situations, and the error message can only be retrieved from the log file saved to the disc.
*[[UmbraCallbackFontDown]]
*[[UmbraCallbackFontUp]]
*[[UmbraCallbackFullscreen]]
*[[UmbraCallbackPause]]
*[[UmbraCallbackQuit]]
*[[UmbraCallbackScreenshot]]
*[[UmbraCallbackSpeedometer]]
<<tiddler ChangeLogSymbols>>
!Current HEAD revision
|!Category|!Committed on|!Change|!Committed by|
!Current release
!!!Umbra 10.11 Alpha, 13-November-2010
|!Category|!Committed on|!Change|!Committed by|
|NEW|7-Nov-2010|Added a credits display method|Mingos|
|NEW|6-Nov-2010|~UmbraLog introduced as a more powerful successor of ~UmbraError|Mingos|
|REM|6-Nov-2010|~UmbraError has been removed|Mingos|
|NEW|2-Nov-2010|deactivateAll now has an optional parametre controlling whether to ignore module fallbacks or not|Mingos|
|API|24-Oct-2010|Several methods' access level changed from public to protected in ~UmbraModule|Mingos|
|NEW|24-Oct-2010|~UmbraEngine::getModuleId method now can be used with module name instead of a module pointer|Mingos|
|NEW|24-Oct-2010|~UmbraModule::getID method added. ~UmbraEngine::getModuleId(~UmbraModule*) is thus deprecated|Mingos|
|API|24-Oct-2010|Event-related methods in ~UmbraModule have been renamed in order to prevent confusion, eg. activate() -> ~onActivate()|Mingos|
|API|18-Oct-2010|Umbra files have been renamed in order not to collide with other possible filenames|Mingos|
|NEW|18-Oct-2010|Added an extra version of the ~UmbraEngine::loadModuleConfiguration method|Mingos|
|NEW|15-Oct-2010|Module factory added|Jice|
|API|14-Oct-2010|External module configuration (API break!)|Jice|
|BUG|13-Oct-2010|Module activation method now correctly places modules with the lowest priority|Mingos|
|API|13-Oct-2010|~UmbraEngine now has four constructors|Mingos|
!Previous releases
!!!Umbra 10.10 Alpha, 9-October-2010
|!Category|!Committed on|!Change|!Committed by|
|API|2-Oct-2010|Error reporting now uses error levels|Mingos|
|NEW|6-Oct-2010|Added timeouts to modules|Mingos|
|NEW|9-Oct-2010|Added Javadoc-style comments|Mingos|
!!!Umbra 10.09 Alpha, 19-September-2010
|!Category|!Committed on|!Change|!Committed by|
|NEW|19-Sep-2010|First official release of Umbra|Mingos & Jice|
|!Symbol|!Meaning|
|NEW|new feature|
|REM|removed feature|
|API|changed feature and/or API break|
|BUG|bug fix|
<<tabs methods
"Engine" "UmbraEngine" [[UmbraEngine]]
"Module" "UmbraModule" [[UmbraModule]]
"ModuleFactory" "UmbraModuleFactory" [[UmbraModuleFactory]]
"Widget" "UmbraWidget" [[UmbraWidget]]
"Callback" "UmbraCallback" [[UmbraCallback]]
"Key" "UmbraKey" [[UmbraKey]]
"Point" "UmbraPoint" [[UmbraPoint]]
"Rect" "UmbraRect" [[UmbraRect]]
"Circle" "UmbraCircle" [[UmbraCircle]]
"Log" "UmbraLog" [[UmbraLog]]
>>
In order to run, Umbra needs to load some configuration variables. These are stored in an external file called {{{umbra.txt}}} and typically placed in the {{{data/cfg}}} directory. The file's construction is the following:
<code C++>
/*
* UMBRA CONFIGURATION FILE
*/
config {
rootWidth = 80
rootHeight = 60
fontID = 0
fullScreen = false
debug = true
fontDir = "data/img/"
moduleChain = "demo"
}
</code>
The variables used are the following:
*{{{rootWidth}}}: the width of the root console in cells. Defaults to 80. Mandatory.
*{{{rootHeight}}}: the height of the root console. Defaults to 60. Mandatory.
*{{{fontID}}}: the ID number of the font to be used (from the set of all registered fonts). Defaults to 0. Mandatory.
*{{{fullScreen}}}: whether the application should run in full screen mode. Defaults to {{{false}}}. Mandatory.
*{{{debug}}}: whether the application should run in debug mode (debug mode displays errors on the root console). Defaults to {{{true}}}. Mandatory.
*{{{fontDir}}}: the directory where Umbra should look for fonts. Defaults to "data/img". Optional, but will be added automatically if not present.
*{{{moduleChain}}}: the name of the module chain to be loaded by the [[external module configuration|External module configuration]] loader. No default value. Optional.
It is not necessary to always write a configuration file by hand. It's enough to run an Umbra-powered application once and the file with default values will be generated automatically.
!Creating a basic callback
In order to create a callback, it is necessary to create a class that will hold the callback-specific information. It needs to inherit the base callback class, [[UmbraCallback]]. The most basic callback declaration would be this:
<code C++>
class MyCallback: public UmbraCallback {
public:
MyCallback();
private:
void action();
}
</code>
The constructor will typically initialise the keystroke data held by the [[UmbraCallback]] mother class, while the {{{action}}} method will implement custom code that will be executed once the callback's keystroke has been detected:
<code C++>
MyCallback::MyCallback() {
key.assign(TCODK_F4,0,true,false,false);
}
void MyCallback::action() {
getEngine()->deactivateAll();
}
</code>
Note that overriding the {{{action}}} method is mandatory. Please refer to the [[UmbraKey]] class for more details on how the keystroke works.
!Creating a more complex callback
There's another method that can be overridden in the [[UmbraCallback]] mother class: {{{evaluate}}}. It's the method that is invoked every main loop iteration and serves the purpose of comparing the keyboard input with the keystroke the callback listens to. The [[UmbraCallbackQuit]] does exactly that, as it needs to listen to two different key combinations:
<code C++>
class UmbraCallbackQuit: public UmbraCallback {
public:
UmbraCallbackQuit ();
private:
UmbraKey key2;
inline bool evaluate (UmbraKey k) { if (k == key || k == key2) return true; else return false; }
void action ();
};
</code>
Creating a module is essentially easy. Here's the simplest way:
<code C++>
class MyModule : public UmbraModule {};
</code>
Yes, that's right. By creating any class that inherits [[UmbraModule]] you can create a perfectly valid module that will be recognised by the engine.
Creating a ''valid'' module is one thing. Making it ''functional'' is a different story. Registering the above module is legal, but no code would ever be executed should it be run.
Each module, along with [[UmbraModule]], inherits a set of virtual methods, which are called by the engine. These need to be overridden. First, let's have a look at the method that is probably the most interesting to the developer: [[UmbraModule::update]].
It's recommended to give a name to the module. This makes it possible to manage this module through the external module configuration file.
<code C++>
MyModule::MyModule() : UmbraModule("myModule") { ... }
// or
MyModule::MyModule() { setName("myModule"); }
</code>
!Update
The module needs to update its internal logic in order to work. This is what [[UmbraModule::update]] is supposed to do. Imagine you have a chess program. The updating would be roughly something like this:
<code C++>
bool MyModule::update (void) {
if (computersTurn) {
calculateMove();
refreshFigurePositions();
computersTurn = false;
}
if (!checkmate && !stalemate) return true;
else return false;
}
</code>
In other words, the updating of a chess program would:
#check whether it's the computer's move,
##if it is, find the best move available,
##make that move by updating the figures positions
##switch to the player's turn
#check whether the game has ended
##if it hasn't, return true (module keeps active)
##if it has, return false (deactivate module)
A key feature of [[UmbraModule::update]] is the effect its return value has on the entire module. Should it return {{{false}}}, the module will automatically be marked for deactivation. If the method returns {{{true}}}, the module still can get deactivated, for instance, by another module.
If the update method isn't overridden, it will always return true. This might be useful in case of modules that simply display a static piece of graphics or text, or have the render method depend on an external source.
!Render
The [[UmbraModule::render]] does exactly what its name indicates: takes care of graphical representation. There are no rules regarding how the rendering should look like. Umbra leaves this to the developer's intuition and specific needs.
One possibility is to do it the straightforward way: draw everything directly to the root console. There are many situations that do not require a more sophisticated solution. Imagine a module that simply renders a smoky background behing the items in the main menu. It's the lowest layer and it won't overwrite anything. It'll also probably cover the entire console's area, so the simple rendering is perfectly acceptable.
Another way would be using an offscreen console. It is a good solution to blit an offscreen console over the root if, for instance, only a small area with a dynamic position needs to be rendered. Imagine a dialogue balloon appearing over a character in a roguelike game. Its position and size wouldn't be fixed, so blitting an offscreen console would be the only sensible solution.
!Example module
Here's an example of an extremely simple module.
{{{credits.hpp}}}:
<code C++>
#ifndef MODULE_CREDITS_HPP
#define MODULE_CREDITS_HPP
class Credits : public UmbraModule {
public:
Credits ();
//the constructor
void activate (void); //to be run on module activation
bool update (void);
void render (void);
void keyboard (TCOD_key_t &key); //keyboard input
private:
std::string credits; //credits text
uint32 startTime; //used for the timeout
uint32 duration; //used for the timeout
int x, y; //rendering starting position
};
#endif
</code>
The module Credits contains no specific methods, it only overrides four [[UmbraModule]]'s virtual methods. {{{activate}}} is the custom code that will be executed each time the module is activated. {{{update}}} is overridden as well and will be used to determine the deactivation condition: a timeout. {{{render}}} displays the credits text, and {{{keyboard}}} parses keyboard input. None of these needs to be called manually, as the engine knows exactly what to do with those methods.
The variables are very important for this module, even though all but one might as well be replaced by literals. {{{std::string credits}}} is the actual credits text that will be displayed. {{{uint32 startTime}}} is a variable that will be used to determine how long the module has been running. {{{uint32 duration}}} is how long, in milliseconds, we want the module to run before it's deactivated (timeout). Finally, {{{int x, y}}} are the coordinates where the credits will be displayed.
{{{credits.cpp}}}:
<code C++>
#include "umbra.hpp"
#include "credits.hpp"
#include <stdio.h>
//constructor
Credits::Credits () {
credits = "Game Title\n"
"by Game Author\n"
"\n"
"powered by:\n"
"libtcod 1.5.1 by Jice & Mingos,\n"
"Umbra 0.1 by Mingos & Jice";
duration = 3000;
x = UmbraConfig::rootWidth/2;
y = (UmbraConfig::rootHeight/2)-3;
}
//update: check the timeout
bool Credits::update (void) {
if (TCODSystem::getElapsedMilli()-startTime < duration)
return true;
else
return false;
}
//render: put the credits text on the screen
void Credits::render (void) {
TCODConsole::root->setForegroundColor(TCODColor::white);
TCODConsole::root->setBackgroundColor(TCODColor::black);
TCODConsole::root->printCenter(x,y,TCOD_BKGND_NONE,credits.c_str());
}
//keyboard input
void Credits::keyboard (TCOD_key_t &key) {
if (key.vk == TCODK_SPACE) setActive (false);
}
//activation code
void Credits::activate (void) {
startTime = TCODSystem::getElapsedMilli();
}
</code>
The constructor, as can be expected, sets the initial values for the module's variables. The credits text is set, the timeout is fixed to 3 seconds (3000 milliseconds), and the coordinates are chosen for the credits renderer.
The {{{update}}} method only does one thing: check whether the module has timed out. It compares the elapsed time to the module's activation time plus the timeout.
It's logical that the {{{render}}} method also needn't be complicated: it just sets the background and foreground colours and prints the credits string at the coordinates {{{[x,y]}}}.
The activation custom code is very simple. It is launched only when the module is activated, so we can safely put the {{{startTime}}} initialisation there. As soon as the module's activated, this method will check the current elapsed time and assign its value to the {{{startTime}}} variable. It will thus become the module's starting time and a base to evaluate the timeout.
Finally, the {{{keyboard}}} parsing. It does a simple check: if the space bar is pressed, the module will be deactivated.
The entire module, when registered and activated, will be enough to create a simple application that displays the credits text until 3 seconds have passed or until the space bar is pressed (whichever happens first).
Creating a ''valid'' widget is as easy as creating a ''valid'' module:
<code C++>
class MyWidget : public UmbraWidget {};
</code>
Again, the engine will happily accept this widget as a working module (remember that the engine doesn't distinguish between modules and widgets because widgets actually inherit [[UmbraModule]]), but activating it will result in nothing happening.
Apart from all [[UmbraModule::update]] and [[UmbraModule::render]], which should be overridden pretty much the same way as in the case of [[UmbraModule]] (see [[Creating a module]]), overriding the method [[UmbraModule::mouse]] is pretty much mandatory if you wish to use additional widget-related features such as pushbuttons. Also, there is one particularity in how the rendering should be taken care of.
There are also a few interesting member objects which must be initialised in order to be used in the widget. The objects are the drag zone, the minimise button and the close button. They are completely interactive, but you should take care of proper display.
!Mouse input
Custom mouse input may be defined using [[UmbraModule::mouse]], but it is not necessary if all interactivity that's desired is the functional close button, the minimise button and/or the drag zone. Additional interactive objects will require custom mouse input.
!Rendering a widget
It is highly recommended to use an offscreen console. All rendering should be done on it, never directly on the root console. If a widget is rendered directly on the root console, it doesn't have dynamic position on the screen and thus needn't be a widget at all.
Mouse input, more specifically widget dragging, will be applied to {{{UmbraRect UmbraWidget::rect}}}, so it's convenient to end the rendering method with the following line:
<code C++>
TCODConsole::blit(consoleName,0,0,rect.w,rect.h,TCODConsole::root,rect.x,rect.y,1.0f,0.5f);
</code>
where {{{consoleName}}} is the offscreen console used in the rendered widget. The transparency values may differ, of course, and needn't be fixed to 1.0f and 0.5f.
This is not a requirement, but rather a useful guideline. Dealing with rendering this way spares additional code required to refer to dynamic cell positions, as well as transparency issues.
!Drag zone
The widget always comes with a very special member object: {{{UmbraRect dragZone}}}. The drag zone is a rectangular area that will react to the mouse hovering over it and to being clicked and dragged. It will result in the entire widget being dragged around the console. In order for it to work, the draggable area needs to be specified. This is done via the [[UmbraWidget::setDragZone]] method. Specifying a valid set of coordinates inside the widget will activate draggability. That's it, an active drag zone already lets the widget be dragged around.
Bear in mind that the fact that a widget is draggable doesn't make it self-explanatory. It is generally recommended to indicate the draggable area in one way or another. The way that internal modules use is highlighting the widget's label. Here's the code used in [[BSOD]] rendering method:
<code C++>
...
bsod->setForegroundColor(TCODColor::white);
bsod->printFrame(0,0,30,8,true,TCOD_BKGND_NONE,"Umbra BSOD");
...
if (dragZone.mouseHover || isDragging) {
bsod->setBackgroundColor(TCODColor::lightRed);
bsod->rect(9,0,12,1,false,TCOD_BKGND_SET);
}
...
</code>
As you can see, there are two variables that are used in the process: {{{dragZone.mouseHover}}} and {{{isDragging}}}. They are set automatically each frame, so there's no need to bother with how they work. The former indicates whether the mouse cursor is hovering over the drag zone and the latter indicates whether the widget is being dragged. As can be observed in the code snippet, if any of the two conditions is met, the background colour of the area corresponding to the widget's label ("Umbra BSOD", printed in white by the {{{TCODConsole::printFrame}}} method from libtcod) is changed to light red. If none of the conditions is satisfied, the label remains white.
!Minimise and close buttons
Another useful feature of a widget are the minimise and close buttons. They are implemented as {{{UmbraPoint minimiseButton}}} and {{{UmbraPoint closeButton}}}. As with the drag zone, the widget will need to be instructed where the points are supposed to be placed. This can be achieved using the [[UmbraPoint::set]] method. Once instructed about the placement of the buttons within the widget (specifically, relative to the coordinates [0,0] of {{{UmbraRect UmbraWidget::rect}}}), the widget will be able to collect data from the mouse and determine whether the cursor is hovering over the buttons and whether it's down. The hover and down events can be retrieved directly from the points using the variables {{{UmbraPoint::mouseHover}}} and {{{UmbraPoint::mouseDown}}}:
<code C++>
if (closeButton.mouseDown) setActive(false);
</code>
The above code will check whether the mouse button has been pressed on the close button of a widget and if so, it will deactivate the widget.
The buttons will not be rendered themselves. Rendering them has to be taken care of in the widget's rendering method. The below code snippet has been copied from the [[Speed-o-meter]] widget:
<code C++>
if (closeButton.mouseHover)
speed->setForegroundColor(TCODColor::red); //button is active
else
speed->setForegroundColor(TCODColor::lightGrey); //button is not active
speed->putChar(closeButton.x,closeButton.y,'X',TCOD_BKGND_SET);
</code>
In the above code snippet, {{{speed}}} is the offscreen console the widget uses. Note that {{{mouseHover}}} variable is used to determine the colour of the button character.
This module displays a simple credits line: "Powered by Umbra [version] [status]". It can be triggered using the method [[UmbraEngine::printCredits]].
The following people had a say in the creation of Umbra:
;[[Mingos]]
:Main developer of Umbra. The developer of a talkie game Umbrarum Regnum and co-developer of libtcod.
:*http://www.umbrarumregnum.net
:*http://www.umbraprojekt.pl
;Jice
:Main developer of Umbra. The guy behind libtcod (The Doryen Library), TCOD (The Chronicles of Doryen) and The Cave (the first project using Umbra).
:*http://doryen.eptalys.net
;Merc
:The developer of The Way of Fallen and the creator of the original engine concept Umbra is based on.
;[[Scautura]]
:Documentation proofreading - thanks a million, mate!
[[UmbraEngine::deactivateAll]] fails to close an application when the active modules have fallbacks. The fallbacks are activated upon module deactivation and the application continues.
There are few defines in Umbra:
;{{{UMBRA_TITLE}}}
:the string {{{"Umbra"}}}
;{{{UMBRA_VERSION}}}
:the string containing the version of Umbra that's being used. Umbra uses Ubuntu-style versioning (e.g., version {{{10.09}}} has been released in September 2010).
;{{{UMBRA_STATUS}}}
:the string containing the status of the Umbra build that's being used, e.g. {{{"Alpha"}}}.
;{{{UMBRA_LICENCE}}}
:the string containing the licence text, as quoted [[here|About Umbra]]. You are encouraged to display it somewhere in your application.
Beyond defining the basic properties of the modules, the module configuration file makes it possible to set custom parametres for the modules to use, using libtcod 1.5.1 dynamic properties syntax:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
Inside the module "myModule", you can access the parametres with the getters:
<code C++>
bool UmbraModule::getBoolParam(const char *name);
int UmbraModule::getCharParam(const char *name);
int UmbraModule::getIntParam(const char *name);
float UmbraModule::getFloatParam(const char *name);
const char *UmbraModule::getStringParam(const char *name);
TCODColor UmbraModule::getColourParam(const char *name);
TCOD_dice_t UmbraModule::getDiceParam(const char *name);
</code>
Note that those getters mustn't be used in the module constructor, since the custom parametres have not yet been parsed at that moment.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
In some cases, it is much more comfortable to define the modules' fallbacks in an external file. They can be defined along with a few other properties in Umbra's external configuration file. To use this feature, it's enough to register all the modules that are needed in the engine, then call [[UmbraEngine::loadModuleConfiguration]]. The configuration file can define multiple module chains, allowing to have different modes for the application (or it could also be different applications in the same .exe):
<code C++>
moduleChain "chainName" {
module "moduleName" {
timeout=0
priority=0
fallback="otherModuleName"
active
}
module "otherModuleName" {
timeout=0
priority=0
}
}
</code>
The file contains a list of {{{moduleChain}}} structures. The application activates one of those chains by calling the [[UmbraEngine::loadModuleConfiguration]] method. It's not allowed to start more than one chain at a time.
The {{{moduleChain}}} structure contains one or several {{{module}}} structures. Each {{{module}}} structure has a name. A module with the same name is required to be registered in the engine before calling [[UmbraEngine::loadModuleConfiguration]].
In the module structure, several properties can be defined (they're all optional):
* {{{timeout}}}: same as calling [[UmbraModule::setTimeout]] in the code
* {{{priority}}}: same as calling [[UmbraModule::setPriority]] in the code
* {{{fallback}}}: name of the module's fallback. The fallback module should be defined in the file as well.
* {{{active}}}: if this flag is present, the module is activated during the engine initialisation.
The errors that have been appended to the log using the method [[UmbraLog::error]] and the like can be displayed in a variety of ways.
Probably the easiest way is to use [[UmbraEngine::displayError]] method. It will activate the [[BSOD]] module and automatically display the last registered error. It can be called at any time and will work only if the root console is initialised.
If only the last message is of interest, but for some reason BSOD use is not an option, the last registered error message can also be retrieved with [[UmbraLog::get]].
Any other message can be retrieved as well using the [[UmbraLog::get]] method as well. It's possible to specify the message index that needs to be retrieved. In case it's necessary to check how many messages have been logged so far, [[UmbraLog::size]] will do that (it can count all logged messages or just ones that have a specific error level).
The difference between the two concepts is very important. The engine needs to be instantiated in order to become available at all, but in order to run it also needs to be configured.
!Instantiation
This will typically be the very first thing to do in your code:
<code C++>
UmbraEngine engine();
</code>
The above line of code ''instantiates'' the engine, ie, creates an instance of the [[UmbraEngine]] class that can be [[referenced|Referencing the engine]]. Without it, all attempts to use the engine would result in a compilation error.
When the engine is instantiated, its constructor assigns the engine's variables initial values and, if the developer requests it, also activates default [[callbacks|What are callbacks]]. A freshly instantiated engine is not useable yet.
!Initialisation
After instantiating the engine, it is configured according to the developer's needs. Modules are registered, and so are fonts, and finally at least one module is activated. Additional configuration possibilities exist as well. See the [[things to do before initialising the engine|Using Umbra]] for more details on this subject.
As soon as the engine is properly configured, it can be initialised. The initialisation is a simple process, yet it relies on the initial configuration. The configuration file is loaded, and information contained therein is used to launch the main application window (libtcod console). At least one font needs to be registered in order to launch the console. If none have been registered, the engine will look in the fonts directory specified in the configuration file and try to automatically register font files with the correct naming pattern. See [[Font autodetection]] for details.
After the engine is successfully initialised, it can be run.
<code C++>
if (engine.initialise()) return engine.run();
else return 1;
</code>
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["enum"])'>>
The error log is a list of error messages registered throughout a single run of the application. Errors can be appended to that list, and each time an error is reported, it will be sent to stderr (in case of console-based applications, stderr is the system console) and also to a text file {{{log.txt}}} located in the application's working directory.
The errors are added automatically when something goes wrong inside Umbra, but the developer can also use the log to append custom error messages.
The messages are stored with useful information about them, including the exact time of occurrence, the error level (ranging from info to fatal error) and indenting (nesting) information. The nesting is used to locate the approximate place of occurrence in the call stack.
It would be nice to have fading in the modules.
The general idea is to make the module store the fade value between 0.0 and 1.0, without actually implementing any visual effects (because fading can mean any effect: opacity change, whiteout/blackout, console position change, displayed area change...).
It is not yet certain how the mechanism should work. To be discussed.
A fallback (not to be confused with a callback) is a simple integer ID number that can be assigned to any registered module.
When a module is deactivated, it can adopt one of two behaviours:
#Do nothing, simply end itself,
#Trigger the activation of another module
The second case is called a fallback, since a deactivated module //falls back// to another one.
This behaviour can be defined at the moment of module registering. The method that registers a module is [[UmbraEngine::registerModule]], with the declaration {{{UmbraEngine::registerModule (UmbraModule * module, int fallback)}}}. The second argument, the integer variable called {{{fallback}}} is optional. If it is not specified, the registered module will simply end when it's deactivated. On the other hand, if another module's ID is passed to the method as the second argument, the deactivation of the registered module will trigger the activation of another one, with ID number equal to the one specified as {{{fallback}}}.
Let's see this in an example situation:
<code C++>
int main () {
enum { MOD_CREDITS, MOD_MAINMENU }; //module IDs
UmbraEngine engine;
engine.registerModule(new Credits(),MOD_MAINMENU); //with fallback
engine.registerModule(new Menu()); //no fallback
engine.activateModule(MOD_CREDITS);
if (engine.initialise()) return engine.run();
else return 1;
}
</code>
In line 02. we declare an enumeration that coincides with the ID numbers that are assigned to registered modules (see [[UmbraEngine::registerModule]] for details). In line 04. we register a module called Credits. It is assigned a fallback {{{MOD_MAINMENU}}}. In the next line, another module is registered, called Menu, but without a falback. Finally, in line 06., the module with ID {{{MOD_CREDITS}}} is activated.
How will the engine behave? The first activated module is supposedly a credits screen, which is usually displayed only during a few seconds in order to honour copyright holders (in case of Umbra, the creators of SDL, libtcod and Umbra itself would be mentioned). When it ends, usually after a specified amount of time has passed, it will give place to another screen, usually related to the game. In our small example, it is a main menu screen. When the main menu ends, it will not fall back to any module automatically, as there are usually various possibilities for the main menu to end. It may trigger the character creation screen, savegame choice screen, options screen or simply end the game. For this reason, no fallback has been specified in line 05. Had it been assigned a fallback, the main menu would always fall back to the specified module, which is not desired.
*[[UmbraRegisterCallbackFlag]]
By default Umbra will look for {{{font<w>x<h>[_<layout>].png}}} files in the {{{./data/img/}}} directory. You can change this directory by adding a fontDir variable in umbra.txt, for example :
<code c++>
/*
* UMBRA CONFIGURATION FILE
*/
config {
rootWidth = 80
rootHeight = 60
fontID = 1
fullScreen = false
debug = false
fontDir = "./fonts"
}
</code>
If no font file is found, we fall back to the default libtcod behaviour: we'll try to use the default {{{"./terminal.png"}}} font file.
Font files names must have the following format:
{{{font<w>x<h>[_<layout>].png}}}
w and h represent the character width and height in the font.
layout is either {{{TCOD}}}, {{{INCOL}}} or {{{INROW}}}. If missing, {{{TCOD}}} is assumed. Fonts must be grayscale (see libtcod setCustomFont documentation).
Examples :
{{{font8x8.png}}}: grayscale font with 8 pixels wide square characters, TCOD layout
{{{font10x10_INCOL.png}}}: grayscale font with 10 pixels wide square characters, ASCII in columns layout
{{{font10x12_INROW.png}}}: grayscale font with non square characters, ASCII in rows layout
All those fonts will be automatically registered and sorted by character "size" = width * height, smallest to biggest. When the game runs, you can switch the current font by pressing pageUp and pageDown (by default). The font selected will be remembered the next time you run the game.
If you want a finer control on the way fonts are loaded, you can register them manually using [[UmbraEngine::registerFont]].
;see also:
:[[Registering fonts]]
Apparently, if there are multiple fonts registered and their layout is not the same, switching between them using Umbra inbuilt font switcher doesn't work. This might be a libtcod-specific bug though.
//~~(Part of the [[ForEachTiddlerPlugin]])~~//
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
''Using JavaScript''
To give you a lot of flexibility the [[ForEachTiddlerMacro]] uses JavaScript in its arguments. Even if you are not that familiar with JavaScript you may find forEachTiddler useful. Just have a look at the various ready-to-use [[ForEachTiddlerExamples]] and adapt them to your needs.
''The Elements of the Macro''
The arguments of the ForEachTiddlerMacro consist of multiple parts, each of them being optional.
<<slider chkFETInClause [[inClause]] "inClause" "inClause">>
<<slider chkFETWhereClause [[whereClause]] "whereClause" "whereClause">>
<<slider chkFETSortClause [[sortClause]] "sortClause" "sortClause">>
<<slider chkFETScriptClause [[scriptClause]] "scriptClause" "scriptClause">>
<<slider chkFETActions [[Action Specification]] "Action Specification" "Action Specification">>
''Using Macros and ">" inside the forEachTiddler Macro''
You may use other macro calls into the expression, especially in the actionParameters. To avoid that the {{{>>}}} of such a macro call is misinterpreted as the end of the {{{<<forEachTiddler...>>}}} macro you must escape the {{{>>}}} of the inner macro with {{{$))}}} E.g. if you want to use {{{<<tiddler ...>>}}} inside the {{{forEachTiddler}}} macro you have to write {{{<<tiddler ...$))}}}.
In addition it is necessary to escape single {{{>}}} with the text {{{$)}}}.
''Using {{{<<tiddler ... with: ...>>}}} to re-use ForEachTiddler definitions''
Sometimes you may want to use a certain ForEachTiddler definition in slight variations. E.g. you may want to list either the tiddlers tagged with "ToDo" and in the other case with "Done". To do so you may use "Tiddler parameters". Here an example:
Replace the variable part of the ForEachTiddler definition with $1 ($2,... $9 are supported). E.g. you may create the tiddler "ListTaggedTiddlers" like this
{{{
<<forEachTiddler
where
'tiddler.tags.contains("$1")'
>>
}}}
Now you can use the ListTaggedTiddlers for various specific tags, using the {{{<<tiddler ...>>}}} macro:
{{{
<<tiddler ListTaggedTiddlers with: "systemConfig">>
}}}
{{{
<<tiddler ListTaggedTiddlers with: "Plugin">>
}}}
See also [[ForEachTiddlerExamples]].
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
Changing the font when in fullscreen mode reverts back to windowed mode. While not a fatal bug, this can be annoying.
The source of the bug may be Umbra or libtcod - to be investigated.
The [[UmbraModule]] provides two virtual methods which can be overridden in a custom module. These are [[UmbraModule::initialise]] and [[UmbraModule::activate]]. The difference between the two might seem a bit blurry, so let's have a closer look at what they do.
!Initialisation
The [[UmbraModule::initialise]] method is run only once per module in the entire execution time of the application. In fact, for some modules, it might never be run, but in this case, the module simply never gets activated. What we need to bear in mind is that the method will be run only when the module is activated ''for the first time''. Every subsequent activation will not trigger initialisation anymore.
What's the practical use of this feature? The initialisation might as well be handled in the constructor, one might remark. The answer is simple: the module might depend on information that's not available at the moment the constructor is run. Sure, in most cases it won't. But what if it does? Well, that's when this method can provide a delayed initialisation or a "second constructor".
!Activation
The activation of a module consists essentially of putting the module on the activation list, and commencing to run it, starting from the subsequent frame (or main loop iteration). During activation, it might be necessary to run some sort of custom code. This is when [[UmbraModule::activate]] comes in. It is run once per module activation.
A simple example of its practical use would be a module with a timeout, such as the [[BSOD]]. During the activation of the [[BSOD]], the module needs to check and store the elapsed time from application start. This value is set every time the module is activated in order to know when it's supposed to be deactivated. Until it's actually deactivated, the value will not change. The effect is a timeout: the module deactivates itself X time after activation.
Paired with [[UmbraModule::activate]] is also [[UmbraModule::deactivate]]. In case a module needs to run some special code upon deactivation, it's enough to override this method.
Umbra has a couple of predefined callbacks. You can see the full list [[here|Callbacks]].
These are the internal callbacks, which can be registered automatically, providing the engine is initialised with a [[flag|UmbraRegisterCallbackFlag]] that enables this. They can also be registered manually - please refer to the individual callbacks in order to check how they can be registered.
<<tabs api
"Bug reports" "Bug reports" [[IssueTrackerBugs]]
"RFEs" "Requests for enhancement" [[IssueTrackerRfes]]
>>
<<tabs bugs
"Open" "Open bugs" [[IssueTrackerBugsOpen]]
"Closed" "Closed bugs" [[IssueTrackerBugsClosed]]
>>
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["issueTracker","bug","closed"])'>>
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["issueTracker","bug","open"])'>>
<<tabs rfes
"Open" "Open RFEs" [[IssueTrackerRfesOpen]]
"Closed" "Closed RFEs" [[IssueTrackerRfesClosed]]
>>
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["issueTracker","RFE","closed"])'>>
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["issueTracker","RFE","open"])'>>
[[About Umbra]]
[[Using Umbra]]
[[API reference]]
[[Issue tracker]]
[[Change log]]
[[Credits|CreditsPage]]
<<tabs methods
"Engine" "UmbraEngine methods" [[UmbraEngineMethods]]
"Module" "UmbraModule methods" [[UmbraModuleMethods]]
"ModuleFactory" "UmbraModuleFactory methods" [[UmbraModuleFactoryMethods]]
"Widget" "UmbraWidget methods" [[UmbraWidgetMethods]]
"Callback" "UmbraCallback methods" [[UmbraCallbackMethods]]
"Key" "UmbraKey methods" [[UmbraKeyMethods]]
"Point" "UmbraPoint methods" [[UmbraPointMethods]]
"Rect" "UmbraRect methods" [[UmbraRectMethods]]
"Circle" "UmbraCircle methods" [[UmbraCircleMethods]]
"Log" "UmbraLog methods" [[UmbraLogMethods]]
>>
One of the developers of this humble engine. Please visit my websites for more details about what I do.
*http://www.umbraprojekt.pl/
*http://www.umbrarumregnum.net/
Module ID, as the name implies, is a unique identification number assigned to a module.
The ID numbers are integers, assigned to the modules in an ascending order, starting from 0. The assignment is done automatically when [[registering modules|Registering modules]]. The first module that is registered in the engine will be given an ID of 0, the next one will have an ID of 1 and so on.
The ID numbers are then used for [[referencing modules|Accessing modules and widgets]].
There are different approaches to managing the module ID numbers in an application:
!Manual assignment
Since the way the engine assigns ID numbers is predictable, one can register modules in a predetermined order an then simply use constants:
<code C++>
enum {
MOD_INTRO,
MOD_MENU,
MOD_GAME
};
UmbraEngine engine;
engine.registerModule(new moduleIntro());
engine.registerModule(new moduleMenu());
engine.registerModule(new moduleGame());
UmbraModule * currentModule = engine.getModule(MOD_INTRO);
</code>
The modules need to be registered in the exact same order as specified in the enum.
!Hash table
It's also possible to create a hash table or an associative array and call the modules by their key, typically a string. A decent way of achieving this is the {{{std::map}}} class.
<code C++>
UmbraEngine engine;
std::map<std::string, int> modules;
modules.insert("intro",engine.registerModule(new moduleIntro()));
modules.insert("menu",engine.registerModule(new moduleMenu()));
modules.insert("game",engine.registerModule(new moduleGame()));
UmbraModule * currentModule = engine.getModule(modules["intro"]);
</code>
!Variables
Module ID numbers can be stored in variables, although it would be important to make their scope large enough so that they are accessible from any place in the code.
<code C++>
UmbraEngine engine;
int moduleIntroID = engine.registerModule(new moduleIntro());
int moduleMenuID = engine.registerModule(new moduleMenu());
int moduleGameID = engine.registerModule(new moduleGame());
UmbraModule * currentModule = engine.getModule(moduleIntroID);
</code>
Modules and widgets, when activated, are put in the active modules' list in an order controlled by the priority. This impacts the module's behaviour and appearance.
!Updating
Since the modules are updated according to the priority in ascending order, a module with a priority of 1 will be updated before a module with a priority of 2. It can be illustrated by a practical example:
<code C++>
MyModule1 * mod1 = new MyModule1();
mod1->setPriority(1);
MyModule2 * mod2 = new MyModule2();
mod1->setPriority(2);
</code>
Let's assume both {{{mod1}}} and {{{mod2}}} have similar keyboard parsing, sharing some keyboard events. Since {{{mod1}}} has a priority of 1, it will be parsed before {{{mod2}}}. If {{{mod1}}} detects a keystroke it reacts to, it will clear that keyboard event, so when {{{mod2}}} gets updated, the keyboard event list will be empty.
!Rendering
On the other hand, module rendering is done in the inverse order. What this means is that if {{{mod1}}} and {{{mod2}}} overlap each other on the screen, {{{mod1}}} will be rendered after {{{mod2}}}, thus it will appear on top of {{{mod2}}}.
There should be an ~UmbraModule method for setting a timeout on a module.
The module would store the activation time and deactivate itself automatically after the time specified in the timeout has passed.
Good for things like credits screens that are supposed to be displayed only during a few seconds.
Every module that is registered in the engine has a name that is stored internally. There are several ways to set the module's name: by calling the ~UmbraModule constructor, by using an extra parametre to the [[UmbraEngine::registerModule]] method or by calling the [[UmbraModule::setName]] method.
!Example
<code C++>
class Module1: public UmbraModule {
Module1(const char * name);
...
}
Module1::Module1(const char * name): UmbraModule(name) {
...
}
engine.registerModule(new Module1("name1"));
</code>
<code C++>
engine.registerModule(new Module2(), "name2");
</code>
<code C++>
int mod3 = engine.registerModule(new Module3());
engine.getModule(mod3)->setName("name3");
</code>
Module names are a very useful feature because they allow for a very intuitive way of referencing them:
<code C++>
engine.getModule("my module");
</code>
Furthermore, module names are a requisite if an [[external module configuration|Defining module chains in an external file]] is going to be used. However, if the [[module factory|Using a module factory]] is in use, the names are assigned automatically and they shouldn't be reset. Thus, module naming methods should be considered only in regard to simplifying module referencing.
*[[UmbraKey::operator ==]]
*[[UmbraPoint::operator ==]]
<div id='header' class='header' macro='gradient vert #555555 #3b3b3b '>
<div class='siteTitle'>
<div class='siteTitleInner' refresh='content' tiddler='SiteTitle'></div>
<div class='siteSubtitleInner' refresh='content' tiddler='SiteSubtitle'></div>
</div>
<span id='topMenu' refresh='content' tiddler='MainMenu'></span>
</div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
Since the engine is the basic and most important class in all Umbra-powered applications, it is obvious that it needs to be referenced across the code. There are three ways of achieving this:
#Via a global variable,
#Via a static engine instance getter,
#Via a helper method.
!Global variable
The simplest way to create a universal engine reference valid in all parts of code is to create a global engine instance. It will be available to all the classes throughout the source code. The global instance can be created by adding a declaration in {{{main.hpp}}}:
<code C++>
extern UmbraEngine engine;
</code>
and an implementation in {{{main.cpp}}}, before {{{main()}}}. This will allow to reference the engine by its global variable name:
<code C++>
#include <main.hpp>
UmbraEngine engine ("data/cfg/umbra.txt",true,false);
...
int main (void) {
engine.setWindowTitle("Umbra demo");
...
}
</code>
!Engine instance getter
The engine has a static getter method that will retrieve a pointer to the engine instance: [[UmbraEngine::getInstance]]. Since it is a static method, it can be called anywhere throughout the code. If the engine has not been create, the first call to this function will create it with default parameters. Example:
<code C++>
class Example {
inline void title (void) {
UmbraEngine::getInstance()->setWindowTitle("Umbra demo");
}
};
</code>
!Helper methods
There are two special cases where helper methods have been introduced in order to spare the developer the necessity to write the entire code of the above method call. In [[UmbraModule]] and [[UmbraCallback]] special methods are available: [[UmbraModule::getEngine]] and [[UmbraCallback::getEngine]]. All classes that inherit either of the two may use this simplified syntax to retrieve a pointer to the engine:
<code C++>
class MyModule : public UmbraModule {
inline void title (void) {
getEngine()->setWindowTitle("Umbra demo");
}
};
</code>
Please note that [[UmbraWidget]] inherits [[UmbraModule]] and thus the [[UmbraModule::getEngine]] method is also available inside widgets.
As with the modules, registering a font "tells" the engine that there is a font file ready to be used. It has nothing to do with actually using the font.
Registering a font can be done using [[UmbraEngine::registerFont]] method. It is necessary to specify the font file, as well as the number of columns and rows. Optionally, the layout can also be specified, although this is not necessary for fonts that use the default TCOD layout.
Once the fonts are registered, the engine will be able to choose the correct font file to load.
Registering a module is basically "telling" the engine that a module has been instantiated and that you would like to make it useable. Registering a module can be done by calling [[UmbraEngine::registerModule]] method.
Note that you may both instantiate the module before calling [[UmbraEngine::registerModule]] and pass the method a reference to the module, or you may simply instantiate the module inside the method's argument.
<code C++>
UmbraEngine engine();
UmbraModule * mod1 = new MyModule1();
engine.registerModule(mod1);
int mod2id = engine.registerModule(new MyModule2());
</code>
In the above example, we can access both modules in different ways:
<code C++>
mod1->method();
engine.getModule(mod2)->method();
</code>
Please read [[Accessing modules and widgets]] for more detailed information on this topic.
Reporting an error is extremely easy in Umbra. It's enough to add this line of code:
<code C++>
UmbraLog::error("Error message");
</code>
This method will send the specified error message to stderr, append it to the error list and also save the file {{{log.txt}}}, containing all of the messages registered so far, to the hard disc.
Bear in mind that this method accepts printf-like text formatting. Ultimately, you can also pass a {{{std::string}}} as the argument instead of {{{const char*}}}.
You can also log non-errors. There are specialised methods for closing and opening blocks, as well as separate methods for each error level.
Proofreader. Nothing more.
The renderer should be specified in the config file, not passed to the engine's initialisation function. The user should be able to change it in case the default one doesn't work.
<<closeAll>>
<<saveChanges>>
<<newTiddler>>
<<newTiddler
label:"new bug report"
tag:"issueTracker"
tag:"bug"
tag:"open"
>>
<<newTiddler
label:"new RFE"
tag:"issueTracker"
tag:"RFE"
tag:"open"
>>
<<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
<<tabs txtMainTab "Tags" "All tags" TabTags "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "More" "More lists" TabMore>>
/***
This CSS by ~DaveBirss.
***/
/*{{{*/
.tabSelected {
background: #fff;
}
.tabUnselected {
background: #eee;
}
#sidebar {
color: #000;
background: transparent;
}
#sidebarOptions {
background: #fff;
}
#sidebarOptions input {
border: 1px solid #ccc;
}
#sidebarOptions input:hover, #sidebarOptions input:active, #sidebarOptions input:focus {
border: 1px solid #000;
}
#sidebarOptions .button {
color: #999;
}
#sidebarOptions .button:hover {
color: #000;
background: #fff;
border-color:white;
}
#sidebarOptions .button:active {
color: #000;
background: #fff;
}
#sidebarOptions .sliderPanel {
background: transparent;
}
#sidebarOptions .sliderPanel A {
color: #999;
}
#sidebarOptions .sliderPanel A:hover {
color: #000;
background: #fff;
}
#sidebarOptions .sliderPanel A:active {
color: #000;
background: #fff;
}
.sidebarSubHeading {
color: #000;
}
#sidebarTabs {`
background: #fff
}
#sidebarTabs .tabSelected {
color: #000;
background: #fff;
border-top: solid 1px #ccc;
border-left: solid 1px #ccc;
border-right: solid 1px #ccc;
border-bottom: none;
}
#sidebarTabs .tabUnselected {
color: #999;
background: #eee;
border-top: solid 1px #ccc;
border-left: solid 1px #ccc;
border-right: solid 1px #ccc;
border-bottom: none;
}
#sidebarTabs .tabContents {
background: #fff;
}
#sidebarTabs .txtMoreTab .tabSelected {
background: #fff;
}
#sidebarTabs .txtMoreTab .tabUnselected {
background: #eee;
}
#sidebarTabs .txtMoreTab .tabContents {
background: #fff;
}
#sidebarTabs .tabContents .tiddlyLink {
color: #999;
border:none;
}
#sidebarTabs .tabContents .tiddlyLink:hover {
background: #fff;
color: #000;
border:none;
}
#sidebarTabs .tabContents {
color: #000;
}
#sidebarTabs .button {
color: #666;
}
#sidebarTabs .tabContents .button:hover {
color: #000;
background: #fff;
}
#sidebar {color:#999;}
/*}}}*/
powering libtcod-based applications
The Speed-o-meter, or "Speedo", is a benchmarking tool. It removes the frames per second limit so that the usage of the CPU core running the main program thread can go up to 100%. It also displays a small window with a graph depicting the time spent on various calculations:
* ''update'' - time spent inside your active modules' [[update()|UmbraModule::update]] method,
* ''render'' - time spent inside your active modules' [[render()|UmbraModule::render]] method,
* ''system'' - the time spent on "system calls" (everything not belonging to the above two).
Apart fromt this information, Speedo will also tell you how long it took to render the last frame (in milliseconds - constant values of 40ms and above should be considered a warning, especially on a fast CPU) and how many frames per second your program manages to render (if this value is much higher than your usual frames per second limit, you have no performance issues).
When turned off, Speedo will set the frames per second limit back to its previous value.
If UmbraEngine is initialised with the additional callbacks enabled (see [[UmbraEngine::UmbraEngine]]), Speedo will turn itself on and off when you press F5.
Please remember to use a code profiler, such as gprof, to profile your code. Speedo is not a profiler, it can merely inform you of possible performance issues, but will not identify any bottlenecks in the program.
Umbra is not a library, but a framework in the form of a collection of C++ source code files. As such, absolutely no linking is required to start using Umbra.
What is necessary, however, is adding the code that comes with Umbra to your project. Since you're not likely to want to modify Umbra files, save in some corner cases where you might need a higher grade of customisation, you are advised to place Umbra files in a separate directory so that they don't distract you from your own code.
Once you have done that, all you need to do to be able to start using Umbra is including its main header file in your code:
<code C++>
#include "umbra.hpp"
</code>
Remember that ''Umbra requires libtcod to run''!
[[StyleSheetSyntaxHighlighter]]
/*{{{*/
/*Monochrome Theme for TiddlyWiki*/
/*Design and CSS by Saq Imtiaz*/
/*Version 1.0*/
/*}}}*/
/*{{{*/
body {background:#3B3B3B; color:#C3C3C3; font:12px Verdana, Helvetica, sans-serif;
}
#header {padding: 0em 0em 0em 0em; background:transparent; font-family: arial,helvetica; font-size:12px;
}
.siteTitle {
padding-top: 32px;
padding-bottom: 32px;
float:left;
font-family: 'Trebuchet MS' sans-serif;
font-weight: bold;
font-size: 48px;
color: #ccc; margin-right:1em;margin-left:0.5em;
}
.siteTitleInner {
margin-bottom: -8px;
}
.siteSubtitleInner {
font-size: 12px;
font-weight: normal;
border-top: 2px solid #ccc;
}
#topMenu br {display:none;}
#topMenu a, #topMenu .tiddlyLink, #topMenu .button {margin:0em; color:#666; padding: 92px 15px 15px 15px; border:none; border-right: 1px solid #666;float:left;}
#topMenu {border-left: 1px solid #666; float:left;margin:0;}
#topMenu a:hover {color:#ccc; background:#3b3b3b;}
#displayArea {margin-left:1.35em; margin-right:17.65em; margin-top:0.5em; padding-top:1em; padding-bottom:10px;}
.tiddler {background:#454545; margin-bottom:20px; padding:1em 2em 1em 2em; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; }
a, a:hover{
color:#fff;
text-decoration: none; background:transparent;
}
.viewer a, .viewer a:hover{border-bottom:1px dotted #fff; font-weight:normal;}
.viewer .button, .editorFooter .button{
color: #fff;
border: 1px solid #fff;
}
.viewer .button:hover,
.editorFooter .button:hover, .viewer .button:active, .viewer .highlight,.editorFooter .button:active, .editorFooter .highlight{
color: #fff;
background: #3B3B3B;
border-color: #3B3B3B;
}
.title {color:#ccc; font-family:'Lucida Grande', Verdana, Sans-Serif; font-size:1.5em;
}
.subtitle, .subtitle a { color: #777; font-size: 0.95em;margin:0.2em;}
.shadow .title{color:#777;}
.toolbar {font-size:90%; margin-bottom: 5px;}
.selected .toolbar a {color:#666;border:0;}
.selected .toolbar a:hover {color:#999; background:transparent;border:0;}
.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active{color:#666;border:0; background:transparent;border:0;}
.tagging, .tagged {
border: 1px solid #555;
background-color: #444;
-moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px;
}
.selected .tagging, .selected .tagged {
background-color: #3B3B3B;
border: 1px solid #666;
}
.tagging .listTitle, .tagged .listTitle {
color: #666;
}
.selected .tagging .listTitle, .selected .tagged .listTitle {
color: #aaa;
}
.tagging .button, .tagged .button {
color: #838383;
}
.selected .tagging .button, .selected .tagged .button {
color:#c3c3c3;
}
.highlight, .marked {background:transparent; color:#111; border:none; text-decoration:underline;}
.tagging .button:hover, .tagged .button:hover, .tagging .button:active, .tagged .button:active {
border: none; background:transparent; text-decoration:underline; color:#333;
}
#sidebarOptions {margin-top:1em;}
#sidebar {margin-right:1.35em;}
#sidebarTabs .tabContents {
font-family: arial,helvetica;}
#sidebarOptions a, #sidebarOptions a:hover{border:none;color:#666;}
#sidebarOptions a:hover, #sidebarOptions a:active {background:#454545; color:#ccc;}
#sidebarTabs .tabContents {background:#454545;border:0px solid #666; border-right:1px solid #454545;}
#sidebarOptions input {background:#ccc; border:1px solid #666;}
#sidebarTabs .tabContents .tiddlyLink, #sidebarTabs .tabContents .button{color:#666;font-weight:normal;}
#sidebarTabs .tabContents .tiddlyLink:hover, #sidebarTabs .tabContents .button:hover {color:#ccc; background:transparent;}
.listTitle {color:#777;}
#sidebarTabs .tabSelected,#sidebarTabs .tabSelected:hover{background:#454545;border:none;color:#ccc; border:1px solid #454545;}
#sidebarTabs .tabUnselected{background:#3B3B3B; border:1px solid #454545; color:#666;}
#sidebarTabs .txtMoreTab .tabSelected,
#sidebarTabs .txtMoreTab .tab:hover,
#sidebarTabs .txtMoreTab .tabContents{
color: #ccc;
background: #3B3B3B; border:1px solid #3B3B3B;
}
#sidebarTabs .txtMoreTab .tabUnselected {
color: #777; border:1px solid #3B3B3B;
background: #454545;
}
#sidebarTabs .tabContents .button:hover, #sidebarTabs .tabContents .highlight, #sidebarTabs .tabContents .marked, #sidebarTabs .tabContents a.button:active{color:#ccc; background:transparent;}
#sidebarOptions .sliderPanel {
background: #454545; font-size: .9em;
}
#sidebarOptions .sliderPanel input {border:1px solid #666; background:#ccc;}
#sidebarOptions .sliderPanel .txtOptionInput {border:1px solid #666;width:9em;}
#sidebarOptions .sliderPanel a {font-weight:normal; color:#666;background-color: #454545; border-bottom:1px dotted #333;}
#sidebarOptions .sliderPanel a:hover {
color:#ccc;
background-color: #454545;
border:none;
border-bottom:1px dotted #111;
}
.popup {
background: #3B3B3B;
border: 1px solid #454545;
}
.popup li.disabled {
color: #000;
}
.popup li a, .popup li a:visited {
color: #777;
border: none;
}
.popup li a:hover {
background: #3b3b3b;
color: #c3c3c3;
border: none;
}
.popup hr {
color: #777;
background: #777;
border-bottom: 1px;
}
.listBreak div{
border-bottom: 1px solid #777;
}
#messageArea {
border: 4px dotted #ccc;
background: #454545;
color: #777;
font-size:90%;
}
#messageArea .button{
color: #3B3B3B;
background:#ccc;
border: 1px solid #ccc;
}
#messageArea .button:hover {
color: #ccc;
background: #3B3B3B;
border-color: #3B3B3B;
}
.viewer blockquote {
border-left: 5px solid #3B3B3B; background:#3B3B3B
}
.viewer table, .viewer td {
border: 1px solid #2E2E2E;
}
.viewer th, thead td {
background: #3B3B3B;
border: 1px solid #3B3B3B;
color: #ccc;
}
.viewer pre {
border: 1px solid #3b3b3b;
background: #5F5F5F;
}
.viewer code {
color: #c3c3c3; background:#5f5f5f;
}
.viewer hr {
border-top: dashed 1px #222; margin:0 1em;
}
.editor input {
border: 1px solid #ccc; margin-top:5px;
}
.editor textarea {
border: 1px solid #ccc;
}
h1,h2,h3,h4,h5 { color: #9c9c9c; background: transparent; padding-bottom:2px; font-family: Arial, Helvetica, sans-serif; }
h1 {font-size:18px;}
h2 {font-size:16px;}
h3 {font-size: 14px;}
a.tiddlyLinkExisting, a.tiddlyLinkExisting:hover {
font-weight: bold;
}
.viewer .tabset {
padding: 12px 0px;
}
.viewer a.tab {
padding: 10px 5px;
border-top-right-radius: 10px; -moz-border-radius-topright: 10px; -webkit-border-top-right-radius: 10px;
border: 2px solid #888888;
font-size: 80%;
}
.viewer .tabContents {
background: #383838;
color: #C3C3C3;
}
.viewer ul {
list-style: none;
}
.viewer ul li {
padding-left: 20px;
margin-bottom: 3px;
background: url(data:image/png;base64,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) left top no-repeat transparent;
}
#tiddlerDisplay .title {
padding-left: 40px;
padding-top: 15px;
border-top: 3px solid #666666;
background: url(data:image/png;base64,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%3D) left bottom no-repeat #505050;
}
#tiddlerDisplay .subtitle { background: #505050; padding: 5px 40px; margin: 0 0 10px 0; border-bottom: 3px solid #666666; }
.siteTitle {
padding-left: 32px;
background: url(data:image/png;base64,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) left center no-repeat;
}
/***
StyleSheet for ~SyntaxHighlighter
***/
/*{{{*/
.dp-highlighter
{
font-family: "Consolas", "Courier New", Courier, mono, serif;
font-size: 12px;
background-color: #E7E5DC;
width: 99%;
overflow: auto;
margin: 18px 0 18px 0 !important;
padding-top: 1px; /* adds a little border on top when controls are hidden */
}
/* clear styles */
.dp-highlighter ol,
.dp-highlighter ol li,
.dp-highlighter ol li span
{
margin: 0;
padding: 0;
border: none;
}
.dp-highlighter a,
.dp-highlighter a:hover
{
background: none;
border: none;
padding: 0;
margin: 0;
}
.dp-highlighter .bar
{
padding-left: 45px;
}
.dp-highlighter.collapsed .bar,
.dp-highlighter.nogutter .bar
{
padding-left: 0px;
}
.dp-highlighter ol
{
list-style: decimal; /* for ie */
background-color: #fff;
margin: 0px 0px 1px 45px !important; /* 1px bottom margin seems to fix occasional Firefox scrolling */
padding: 0px;
color: #5C5C5C;
}
.dp-highlighter.nogutter ol,
.dp-highlighter.nogutter ol li
{
list-style: none !important;
margin-left: 0px !important;
}
.dp-highlighter ol li,
.dp-highlighter .columns div
{
list-style: decimal-leading-zero; /* better look for others, override cascade from OL */
list-style-position: outside !important;
border-left: 3px solid #6CE26C;
background-color: #F8F8F8;
color: #5C5C5C;
padding: 0 3px 0 10px !important;
margin: 0 !important;
line-height: 14px;
}
.dp-highlighter.nogutter ol li,
.dp-highlighter.nogutter .columns div
{
border: 0;
}
.dp-highlighter .columns
{
background-color: #F8F8F8;
color: gray;
overflow: hidden;
width: 100%;
}
.dp-highlighter .columns div
{
padding-bottom: 5px;
}
.dp-highlighter ol li.alt
{
background-color: #FFF;
color: inherit;
}
.dp-highlighter ol li span
{
color: black;
background-color: inherit;
}
/* Adjust some properties when collapsed */
.dp-highlighter.collapsed ol
{
margin: 0px;
}
.dp-highlighter.collapsed ol li
{
display: none;
}
/* Additional modifications when in print-view */
.dp-highlighter.printing
{
border: none;
}
.dp-highlighter.printing .tools
{
display: none !important;
}
.dp-highlighter.printing li
{
display: list-item !important;
}
/* Styles for the tools */
.dp-highlighter .tools
{
padding: 3px 8px 3px 10px;
font: 9px Verdana, Geneva, Arial, Helvetica, sans-serif;
color: silver;
background-color: #f8f8f8;
padding-bottom: 10px;
border-left: 3px solid #6CE26C;
}
.dp-highlighter.nogutter .tools
{
border-left: 0;
}
.dp-highlighter.collapsed .tools
{
border-bottom: 0;
}
.dp-highlighter .tools a
{
font-size: 9px;
color: #a0a0a0;
background-color: inherit;
text-decoration: none;
margin-right: 10px;
}
.dp-highlighter .tools a:hover
{
color: red;
background-color: inherit;
text-decoration: underline;
}
/* About dialog styles */
.dp-about { background-color: #fff; color: #333; margin: 0px; padding: 0px; }
.dp-about table { width: 100%; height: 100%; font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; }
.dp-about td { padding: 10px; vertical-align: top; }
.dp-about .copy { border-bottom: 1px solid #ACA899; height: 95%; }
.dp-about .title { color: red; background-color: inherit; font-weight: bold; }
.dp-about .para { margin: 0 0 4px 0; }
.dp-about .footer { background-color: #ECEADB; color: #333; border-top: 1px solid #fff; text-align: right; }
.dp-about .close { font-size: 11px; font-family: Tahoma, Verdana, Arial, sans-serif !important; background-color: #ECEADB; color: #333; width: 60px; height: 22px; }
/* Language specific styles */
.dp-highlighter .comment, .dp-highlighter .comments { color: #808080; background-color: inherit; }
.dp-highlighter .string { color: blue; background-color: inherit; }
.dp-highlighter .keyword { color: navy; font-weight: bold; background-color: inherit; }
.dp-highlighter .preprocessor { color: green; background-color: inherit; }
/*}}}*/
/***
!Metadata:
|''Name:''|SyntaxHighlighterPlugin|
|''Description:''|Code Syntax Highlighter Plugin for TiddlyWiki.|
|''Version:''|1.1.3|
|''Date:''|Oct 24, 2008|
|''Source:''|http://www.coolcode.cn/show-310-1.html|
|''Author:''|Ma Bingyao (andot (at) ujn (dot) edu (dot) cn)|
|''License:''|[[GNU Lesser General Public License|http://www.gnu.org/licenses/lgpl.txt]]|
|''~CoreVersion:''|2.4.1|
|''Browser:''|Firefox 1.5+; InternetExplorer 6.0; Safari; Opera; Chrome; etc.|
!Syntax:
{{{
<code options>
codes
</code>
}}}
!Examples:
{{{
<code java>
public class HelloWorld {
public static void main(String args[]) {
System.out.println("HelloWorld!");
}
}
</code>
}}}
!Revision History:
|''Version''|''Date''|''Note''|
|1.1.2|Oct 15, 2008|Optimize Highlight|
|1.0.0|Oct 13, 2008|Initial release|
!Code section:
***/
//{{{
var dp={sh:{Toolbar:{},Utils:{},RegexLib:{},Brushes:{},Strings:{AboutDialog:"<html><head><title>About...</title></head><body class=\"dp-about\"><table cellspacing=\"0\"><tr><td class=\"copy\"><p class=\"title\">dp.SyntaxHighlighter</div><div class=\"para\">Version: {V}</p><p><a href=\"http://www.dreamprojections.com/syntaxhighlighter/?ref=about\" target=\"_blank\">http://www.dreamprojections.com/syntaxhighlighter</a></p>©2004-2007 Alex Gorbatchev.</td></tr><tr><td class=\"footer\"><input type=\"button\" class=\"close\" value=\"OK\" onClick=\"window.close()\"/></td></tr></table></body></html>"},ClipboardSwf:null,Version:"1.5.1"}};dp.SyntaxHighlighter=dp.sh;dp.sh.Toolbar.Commands={ExpandSource:{label:"+ expand source",check:function($){return $.collapse},func:function($,_){$.parentNode.removeChild($);_.div.className=_.div.className.replace("collapsed","")}},ViewSource:{label:"view plain",func:function($,_){var A=dp.sh.Utils.FixForBlogger(_.originalCode).replace(/</g,"<"),B=window.open("","_blank","width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=0");B.document.write("<textarea style=\"width:99%;height:99%\">"+A+"</textarea>");B.document.close()}},CopyToClipboard:{label:"copy to clipboard",check:function(){return window.clipboardData!=null||dp.sh.ClipboardSwf!=null},func:function($,A){var B=dp.sh.Utils.FixForBlogger(A.originalCode).replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&");if(window.clipboardData)window.clipboardData.setData("text",B);else if(dp.sh.ClipboardSwf!=null){var _=A.flashCopier;if(_==null){_=document.createElement("div");A.flashCopier=_;A.div.appendChild(_)}_.innerHTML="<embed src=\""+dp.sh.ClipboardSwf+"\" FlashVars=\"clipboard="+encodeURIComponent(B)+"\" width=\"0\" height=\"0\" type=\"application/x-shockwave-flash\"></embed>"}alert("The code is in your clipboard now")}},PrintSource:{label:"print",func:function($,B){var _=document.createElement("IFRAME"),A=null;_.style.cssText="position:absolute;width:0px;height:0px;left:-500px;top:-500px;";document.body.appendChild(_);A=_.contentWindow.document;dp.sh.Utils.CopyStyles(A,window.document);A.write("<div class=\""+B.div.className.replace("collapsed","")+" printing\">"+B.div.innerHTML+"</div>");A.close();_.contentWindow.focus();_.contentWindow.print();alert("Printing...");document.body.removeChild(_)}},About:{label:"?",func:function(_){var A=window.open("","_blank","dialog,width=300,height=150,scrollbars=0"),$=A.document;dp.sh.Utils.CopyStyles($,window.document);$.write(dp.sh.Strings.AboutDialog.replace("{V}",dp.sh.Version));$.close();A.focus()}}};dp.sh.Toolbar.Create=function(B){var A=document.createElement("DIV");A.className="tools";for(var _ in dp.sh.Toolbar.Commands){var $=dp.sh.Toolbar.Commands[_];if($.check!=null&&!$.check(B))continue;A.innerHTML+="<a href=\"#\" onclick=\"dp.sh.Toolbar.Command('"+_+"',this);return false;\">"+$.label+"</a>"}return A};dp.sh.Toolbar.Command=function(_,$){var A=$;while(A!=null&&A.className.indexOf("dp-highlighter")==-1)A=A.parentNode;if(A!=null)dp.sh.Toolbar.Commands[_].func($,A.highlighter)};dp.sh.Utils.CopyStyles=function(A,_){var $=_.getElementsByTagName("link");for(var B=0;B<$.length;B++)if($[B].rel.toLowerCase()=="stylesheet")A.write("<link type=\"text/css\" rel=\"stylesheet\" href=\""+$[B].href+"\"></link>")};dp.sh.Utils.FixForBlogger=function($){return(dp.sh.isBloggerMode==true)?$.replace(/<br\s*\/?>|<br\s*\/?>/gi,"\n"):$};dp.sh.RegexLib={MultiLineCComments:new RegExp("/\\*[\\s\\S]*?\\*/","gm"),SingleLineCComments:new RegExp("//.*$","gm"),SingleLinePerlComments:new RegExp("#.*$","gm"),DoubleQuotedString:new RegExp("\"(?:\\.|(\\\\\\\")|[^\\\"\"\\n])*\"","g"),SingleQuotedString:new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'","g")};dp.sh.Match=function(_,$,A){this.value=_;this.index=$;this.length=_.length;this.css=A};dp.sh.Highlighter=function(){this.noGutter=false;this.addControls=true;this.collapse=false;this.tabsToSpaces=true;this.wrapColumn=80;this.showColumns=true};dp.sh.Highlighter.SortCallback=function($,_){if($.index<_.index)return-1;else if($.index>_.index)return 1;else if($.length<_.length)return-1;else if($.length>_.length)return 1;return 0};dp.sh.Highlighter.prototype.CreateElement=function(_){var $=document.createElement(_);$.highlighter=this;return $};dp.sh.Highlighter.prototype.GetMatches=function(_,B){var $=0,A=null;while((A=_.exec(this.code))!=null)this.matches[this.matches.length]=new dp.sh.Match(A[0],A.index,B)};dp.sh.Highlighter.prototype.AddBit=function($,A){if($==null||$.length==0)return;var C=this.CreateElement("SPAN");$=$.replace(/ /g," ");$=$.replace(/</g,"<");$=$.replace(/\n/gm," <br>");if(A!=null){if((/br/gi).test($)){var _=$.split(" <br>");for(var B=0;B<_.length;B++){C=this.CreateElement("SPAN");C.className=A;C.innerHTML=_[B];this.div.appendChild(C);if(B+1<_.length)this.div.appendChild(this.CreateElement("BR"))}}else{C.className=A;C.innerHTML=$;this.div.appendChild(C)}}else{C.innerHTML=$;this.div.appendChild(C)}};dp.sh.Highlighter.prototype.IsInside=function(_){if(_==null||_.length==0)return false;for(var A=0;A<this.matches.length;A++){var $=this.matches[A];if($==null)continue;if((_.index>$.index)&&(_.index<$.index+$.length))return true}return false};dp.sh.Highlighter.prototype.ProcessRegexList=function(){for(var $=0;$<this.regexList.length;$++)this.GetMatches(this.regexList[$].regex,this.regexList[$].css)};dp.sh.Highlighter.prototype.ProcessSmartTabs=function(E){var B=E.split("\n"),$="",D=4,A="\t";function _(A,E,_){var B=A.substr(0,E),C=A.substr(E+1,A.length),$="";for(var D=0;D<_;D++)$+=" ";return B+$+C}function C(B,C){if(B.indexOf(A)==-1)return B;var D=0;while((D=B.indexOf(A))!=-1){var $=C-D%C;B=_(B,D,$)}return B}for(var F=0;F<B.length;F++)$+=C(B[F],D)+"\n";return $};dp.sh.Highlighter.prototype.SwitchToList=function(){var C=this.div.innerHTML.replace(/<(br)\/?>/gi,"\n"),B=C.split("\n");if(this.addControls==true)this.bar.appendChild(dp.sh.Toolbar.Create(this));if(this.showColumns){var A=this.CreateElement("div"),_=this.CreateElement("div"),E=10,G=1;while(G<=150)if(G%E==0){A.innerHTML+=G;G+=(G+"").length}else{A.innerHTML+="·";G++}_.className="columns";_.appendChild(A);this.bar.appendChild(_)}for(var G=0,D=this.firstLine;G<B.length-1;G++,D++){var $=this.CreateElement("LI"),F=this.CreateElement("SPAN");$.className=(G%2==0)?"alt":"";F.innerHTML=B[G]+" ";$.appendChild(F);this.ol.appendChild($)}this.div.innerHTML=""};dp.sh.Highlighter.prototype.Highlight=function(C){function A($){return $.replace(/^\s*(.*?)[\s\n]*$/g,"$1")}function $($){return $.replace(/\n*$/,"").replace(/^\n*/,"")}function _(B){var E=dp.sh.Utils.FixForBlogger(B).split("\n"),C=new Array(),D=new RegExp("^\\s*","g"),$=1000;for(var F=0;F<E.length&&$>0;F++){if(A(E[F]).length==0)continue;var _=D.exec(E[F]);if(_!=null&&_.length>0)$=Math.min(_[0].length,$)}if($>0)for(F=0;F<E.length;F++)E[F]=E[F].substr($);return E.join("\n")}function D(A,$,_){return A.substr($,_-$)}var F=0;if(C==null)C="";this.originalCode=C;this.code=$(_(C));this.div=this.CreateElement("DIV");this.bar=this.CreateElement("DIV");this.ol=this.CreateElement("OL");this.matches=new Array();this.div.className="dp-highlighter";this.div.highlighter=this;this.bar.className="bar";this.ol.start=this.firstLine;if(this.CssClass!=null)this.ol.className=this.CssClass;if(this.collapse)this.div.className+=" collapsed";if(this.noGutter)this.div.className+=" nogutter";if(this.tabsToSpaces==true)this.code=this.ProcessSmartTabs(this.code);this.ProcessRegexList();if(this.matches.length==0){this.AddBit(this.code,null);this.SwitchToList();this.div.appendChild(this.bar);this.div.appendChild(this.ol);return}this.matches=this.matches.sort(dp.sh.Highlighter.SortCallback);for(var E=0;E<this.matches.length;E++)if(this.IsInside(this.matches[E]))this.matches[E]=null;for(E=0;E<this.matches.length;E++){var B=this.matches[E];if(B==null||B.length==0)continue;this.AddBit(D(this.code,F,B.index),null);this.AddBit(B.value,B.css);F=B.index+B.length}this.AddBit(this.code.substr(F),null);this.SwitchToList();this.div.appendChild(this.bar);this.div.appendChild(this.ol)};dp.sh.Highlighter.prototype.GetKeywords=function($){return"\\b"+$.replace(/ /g,"\\b|\\b")+"\\b"};dp.sh.BloggerMode=function(){dp.sh.isBloggerMode=true};dp.sh.HighlightAll=function(N,B,K,I,O,E){function A(){var $=arguments;for(var _=0;_<$.length;_++){if($[_]==null)continue;if(typeof($[_])=="string"&&$[_]!="")return $[_]+"";if(typeof($[_])=="object"&&$[_].value!="")return $[_].value+""}return null}function J($,_){for(var A=0;A<_.length;A++)if(_[A]==$)return true;return false}function L(A,B,C){var _=new RegExp("^"+A+"\\[(\\w+)\\]$","gi"),$=null;for(var D=0;D<B.length;D++)if(($=_.exec(B[D]))!=null)return $[1];return C}function C(B,A,_){var $=document.getElementsByTagName(_);for(var C=0;C<$.length;C++)if($[C].getAttribute("name")==A)B.push($[C])}var T=[],P=null,M={},$="innerHTML";C(T,N,"pre");C(T,N,"textarea");if(T.length==0)return;for(var R in dp.sh.Brushes){var F=dp.sh.Brushes[R].Aliases;if(F==null)continue;for(var G=0;G<F.length;G++)M[F[G]]=R}for(G=0;G<T.length;G++){var _=T[G],U=A(_.attributes["class"],_.className,_.attributes["language"],_.language),Q="";if(U==null)continue;U=U.split(":");Q=U[0].toLowerCase();if(M[Q]==null)continue;P=new dp.sh.Brushes[M[Q]]();_.style.display="none";P.noGutter=(B==null)?J("nogutter",U):!B;P.addControls=(K==null)?!J("nocontrols",U):K;P.collapse=(I==null)?J("collapse",U):I;P.showColumns=(E==null)?J("showcolumns",U):E;var D=document.getElementsByTagName("head")[0];if(P.Style&&D){var S=document.createElement("style");S.setAttribute("type","text/css");if(S.styleSheet)S.styleSheet.cssText=P.Style;else{var H=document.createTextNode(P.Style);S.appendChild(H)}D.appendChild(S)}P.firstLine=(O==null)?parseInt(L("firstline",U,1)):O;P.Highlight(_[$]);P.source=_;_.parentNode.insertBefore(P.div,_)}};version.extensions.SyntaxHighLighterPlugin={major:1,minor:1,revision:3,date:new Date(2008,10,24)};dp.sh.ClipboardSwf="clipboard.swf";dp.sh.Highlight=function(_,Q,B,J,H,M,D){function A(){var $=arguments;for(var _=0;_<$.length;_++){if($[_]==null)continue;if(typeof($[_])=="string"&&$[_]!="")return $[_]+"";if(typeof($[_])=="object"&&$[_].value!="")return $[_].value+""}return null}function I($,_){for(var A=0;A<_.length;A++)if(_[A]==$)return true;return false}function K(A,B,C){var _=new RegExp("^"+A+"\\[(\\w+)\\]$","gi"),$=null;for(var D=0;D<B.length;D++)if(($=_.exec(B[D]))!=null)return $[1];return C}var N=null,$="innerHTML";if(this.registered==undefined){var L={};for(var O in dp.sh.Brushes){var E=dp.sh.Brushes[O].Aliases;if(E==null)continue;for(var F=0;F<E.length;F++)L[E[F]]=O}this.registered=L}Q=Q.split(":");language=Q[0].toLowerCase();if(this.registered[language]==null)return;N=new dp.sh.Brushes[this.registered[language]]();_.style.display="none";N.noGutter=(B==null)?I("nogutter",Q):!B;N.addControls=(J==null)?!I("nocontrols",Q):J;N.collapse=(H==null)?I("collapse",Q):H;N.showColumns=(D==null)?I("showcolumns",Q):D;var C=document.getElementsByTagName("head")[0],P=document.getElementById(N.CssClass);if(N.Style&&C&&!P){P=document.createElement("style");P.setAttribute("id",N.CssClass);P.setAttribute("type","text/css");if(P.styleSheet)P.styleSheet.cssText=N.Style;else{var G=document.createTextNode(N.Style);P.appendChild(G)}C.appendChild(P)}N.firstLine=(M==null)?parseInt(K("firstline",Q,1)):M;N.Highlight(_[$]);N.source=_;_.parentNode.insertBefore(N.div,_)};config.formatters.push({name:"SyntaxHighlighter",match:"^<code[\\s]+[^>]+>\\n",element:"pre",handler:function(_){this.lookaheadRegExp=/<code[\s]+([^>]+)>\n((?:^[^\n]*\n)+?)(^<\/code>$\n?)/mg;this.lookaheadRegExp.lastIndex=_.matchStart;var $=this.lookaheadRegExp.exec(_.source);if($&&$.index==_.matchStart){var C=$[1],B=$[2];if(config.browser.isIE)B=B.replace(/\n/g,"\r");var A=createTiddlyElement(_.output,this.element,null,null,B);dp.sh.Highlight(A,C);_.nextMatch=$.index+$[0].length}}});config.formatterHelpers.enclosedTextHelper=function(_){this.lookaheadRegExp.lastIndex=_.matchStart;var $=this.lookaheadRegExp.exec(_.source);if($&&$.index==_.matchStart){var B=$[1];if(config.browser.isIE)B=B.replace(/\n/g,"\r");var A=createTiddlyElement(_.output,this.element,null,null,B);switch(_.matchText){case"/*{{{*/\n":dp.sh.Highlight(A,"css");break;case"//{{{\n":dp.sh.Highlight(A,"js");break;case"<!--{{{-->\n":dp.sh.Highlight(A,"xml");break}_.nextMatch=$.index+$[0].length}};dp.sh.Brushes.AS3=function(){var _="class interface package",$="Array Boolean Date decodeURI decodeURIComponent encodeURI encodeURIComponent escape "+"int isFinite isNaN isXMLName Number Object parseFloat parseInt "+"String trace uint unescape XML XMLList "+"Infinity -Infinity NaN undefined "+"as delete instanceof is new typeof "+"break case catch continue default do each else finally for if in "+"label return super switch throw try while with "+"dynamic final internal native override private protected public static "+"...rest const extends function get implements namespace set "+"import include use "+"AS3 flash_proxy object_proxy "+"false null this true "+"void Null";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"blockcomment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("^\\s*#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"definition"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"},{regex:new RegExp("var","gm"),css:"variable"}];this.CssClass="dp-as";this.Style=".dp-as .comment { color: #009900; font-style: italic; }"+".dp-as .blockcomment { color: #3f5fbf; }"+".dp-as .string { color: #990000; }"+".dp-as .preprocessor { color: #0033ff; }"+".dp-as .definition { color: #9900cc; font-weight: bold; }"+".dp-as .keyword { color: #0033ff; }"+".dp-as .variable { color: #6699cc; font-weight: bold; }"};dp.sh.Brushes.AS3.prototype=new dp.sh.Highlighter();dp.sh.Brushes.AS3.Aliases=["as","actionscript","ActionScript","as3","AS3"];dp.sh.Brushes.Bash=function(){var _="alias bg bind break builtin cd command compgen complete continue "+"declare dirs disown echo enable eval exec exit export fc fg "+"getopts hash help history jobs kill let local logout popd printf "+"pushd pwd read readonly return set shift shopt source "+"suspend test times trap type typeset ulimit umask unalias unset wait",$="case do done elif else esac fi for function if in select then "+"time until while";this.regexList=[{regex:dp.sh.RegexLib.SingleLinePerlComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("[()[\\]{}]","g"),css:"delim"},{regex:new RegExp("\\$\\w+","g"),css:"vars"},{regex:new RegExp("\\w+=","g"),css:"vars"},{regex:new RegExp("\\s-\\w+","g"),css:"flag"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"builtin"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-bash";this.Style=".dp-bash .builtin {color: maroon; font-weight: bold;}"+".dp-bash .comment {color: gray;}"+".dp-bash .delim {font-weight: bold;}"+".dp-bash .flag {color: green;}"+".dp-bash .string {color: red;}"+".dp-bash .vars {color: blue;}"};dp.sh.Brushes.Bash.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Bash.Aliases=["bash","sh"];dp.sh.Brushes.Batch=function(){var _="APPEND ATTRIB CD CHDIR CHKDSK CHOICE CLS COPY DEL ERASE DELTREE "+"DIR EXIT FC COMP FDISK FIND FORMAT FSUTIL HELP JOIN "+"LABEL LOADFIX MK MKDIR MEM MEMMAKER MORE MOVE MSD PCPARK "+"PRINT RD RMDIR REN SCANDISK SHARE SORT SUBST SYS "+"TIME DATE TREE TRUENAME TYPE UNDELETE VER XCOPY",$="DO ELSE FOR IN CALL CHOICE GOTO SHIFT PAUSE ERRORLEVEL "+"IF NOT EXIST LFNFOR START SETLOCAL ENDLOCAL ECHO SET";this.regexList=[{regex:new RegExp("REM.*$","gm"),css:"comment"},{regex:new RegExp("::.*$","gm"),css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("[()[\\]{}]","g"),css:"delim"},{regex:new RegExp("%\\w+%","g"),css:"vars"},{regex:new RegExp("%%\\w+","g"),css:"vars"},{regex:new RegExp("\\w+=","g"),css:"vars"},{regex:new RegExp("@\\w+","g"),css:"keyword"},{regex:new RegExp(":\\w+","g"),css:"keyword"},{regex:new RegExp("\\s/\\w+","g"),css:"flag"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"builtin"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-batch";this.Style=".dp-batch .builtin {color: maroon; font-weight: bold;}"+".dp-batch .comment {color: gray;}"+".dp-batch .delim {font-weight: bold;}"+".dp-batch .flag {color: green;}"+".dp-batch .string {color: red;}"+".dp-batch .vars {color: blue;font-weight: bold;}"};dp.sh.Brushes.Batch.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Batch.Aliases=["batch","dos"];dp.sh.Brushes.ColdFusion=function(){this.CssClass="dp-coldfusion";this.Style=".dp-coldfusion { font: 13px \"Courier New\", Courier, monospace; }"+".dp-coldfusion .tag, .dp-coldfusion .tag-name { color: #990033; }"+".dp-coldfusion .attribute { color: #990033; }"+".dp-coldfusion .attribute-value { color: #0000FF; }"+".dp-coldfusion .cfcomments { background-color: #FFFF99; color: #000000; }"+".dp-coldfusion .cfscriptcomments { color: #999999; }"+".dp-coldfusion .keywords { color: #0000FF; }"+".dp-coldfusion .mgkeywords { color: #CC9900; }"+".dp-coldfusion .numbers { color: #ff0000; }"+".dp-coldfusion .strings { color: green; }";this.mgKeywords="setvalue getvalue addresult viewcollection viewstate";this.keywords="var eq neq gt gte lt lte not and or true false "+"abs acos addsoaprequestheader addsoapresponseheader "+"arrayappend arrayavg arrayclear arraydeleteat arrayinsertat "+"arrayisempty arraylen arraymax arraymin arraynew "+"arrayprepend arrayresize arrayset arraysort arraysum "+"arrayswap arraytolist asc asin atn binarydecode binaryencode "+"bitand bitmaskclear bitmaskread bitmaskset bitnot bitor bitshln "+"bitshrn bitxor ceiling charsetdecode charsetencode chr cjustify "+"compare comparenocase cos createdate createdatetime createobject "+"createobject createobject createobject createobject createodbcdate "+"createodbcdatetime createodbctime createtime createtimespan "+"createuuid dateadd datecompare dateconvert datediff dateformat "+"datepart day dayofweek dayofweekasstring dayofyear daysinmonth "+"daysinyear de decimalformat decrementvalue decrypt decryptbinary "+"deleteclientvariable directoryexists dollarformat duplicate encrypt "+"encryptbinary evaluate exp expandpath fileexists find findnocase "+"findoneof firstdayofmonth fix formatbasen generatesecretkey "+"getauthuser getbasetagdata getbasetaglist getbasetemplatepath "+"getclientvariableslist getcontextroot getcurrenttemplatepath "+"getdirectoryfrompath getencoding getexception getfilefrompath "+"getfunctionlist getgatewayhelper gethttprequestdata gethttptimestring "+"getk2serverdoccount getk2serverdoccountlimit getlocale "+"getlocaledisplayname getlocalhostip getmetadata getmetricdata "+"getpagecontext getprofilesections getprofilestring getsoaprequest "+"getsoaprequestheader getsoapresponse getsoapresponseheader "+"gettempdirectory gettempfile gettemplatepath gettickcount "+"gettimezoneinfo gettoken hash hour htmlcodeformat htmleditformat "+"iif incrementvalue inputbasen insert int isarray isbinary isboolean "+"iscustomfunction isdate isdebugmode isdefined isk2serverabroker "+"isk2serverdoccountexceeded isk2serveronline isleapyear islocalhost "+"isnumeric isnumericdate isobject isquery issimplevalue issoaprequest "+"isstruct isuserinrole isvalid isvalid isvalid iswddx isxml "+"isxmlattribute isxmldoc isxmlelem isxmlnode isxmlroot javacast "+"jsstringformat lcase left len listappend listchangedelims listcontains "+"listcontainsnocase listdeleteat listfind listfindnocase listfirst "+"listgetat listinsertat listlast listlen listprepend listqualify "+"listrest listsetat listsort listtoarray listvaluecount "+"listvaluecountnocase ljustify log log10 lscurrencyformat lsdateformat "+"lseurocurrencyformat lsiscurrency lsisdate lsisnumeric lsnumberformat "+"lsparsecurrency lsparsedatetime lsparseeurocurrency lsparsenumber "+"lstimeformat ltrim max mid min minute month monthasstring now "+"numberformat paragraphformat parameterexists parsedatetime pi "+"preservesinglequotes quarter queryaddcolumn queryaddrow querynew "+"querysetcell quotedvaluelist rand randomize randrange refind "+"refindnocase releasecomobject removechars repeatstring replace "+"replacelist replacenocase rereplace rereplacenocase reverse right "+"rjustify round rtrim second sendgatewaymessage setencoding "+"setlocale setprofilestring setvariable sgn sin spanexcluding "+"spanincluding sqr stripcr structappend structclear structcopy "+"structcount structdelete structfind structfindkey structfindvalue "+"structget structinsert structisempty structkeyarray structkeyexists "+"structkeylist structnew structsort structupdate tan timeformat "+"tobase64 tobinary toscript tostring trim ucase urldecode urlencodedformat "+"urlsessionformat val valuelist week wrap writeoutput xmlchildpos "+"xmlelemnew xmlformat xmlgetnodetype xmlnew xmlparse xmlsearch xmltransform "+"xmlvalidate year yesnoformat";this.stringMatches=new Array();this.attributeMatches=new Array()};dp.sh.Brushes.ColdFusion.prototype=new dp.sh.Highlighter();dp.sh.Brushes.ColdFusion.Aliases=["coldfusion","cf"];dp.sh.Brushes.ColdFusion.prototype.ProcessRegexList=function(){function B(_,$){_[_.length]=$}function A(A,$){for(var _=0;_<A.length;_++)if(A[_]==$)return _;return-1}var _=null,$=null;this.GetMatches(new RegExp("\\b(\\d+)","gm"),"numbers");this.GetMatches(new RegExp(this.GetKeywords(this.mgKeywords),"igm"),"mgkeywords");this.GetMatches(dp.sh.RegexLib.SingleLineCComments,"cfscriptcomments");this.GetMatches(dp.sh.RegexLib.MultiLineCComments,"cfscriptcomments");this.GetMatches(new RegExp("(<|<)!---[\\s\\S]*?---(>|>)","gm"),"cfcomments");$=new RegExp("(cfset\\s*)?([:\\w-.]+)\\s*=\\s*(\".*?\"|'.*?')*","gm");while((_=$.exec(this.code))!=null){if(_[1]!=undefined&&_[1]!="")continue;if(_[3]!=undefined&&_[3]!=""&&_[3]!="\"\""&&_[3]!="''"){B(this.matches,new dp.sh.Match(_[2],_.index,"attribute"));B(this.matches,new dp.sh.Match(_[3],_.index+_[0].indexOf(_[3]),"attribute-value"));B(this.stringMatches,_[3]);B(this.attributeMatches,_[2])}}this.GetMatches(new RegExp("(<|<)/*\\?*(?!\\!)|/*\\?*(>|>)","gm"),"tag");$=new RegExp("(?:<|<)/*\\?*\\s*([:\\w-.]+)","gm");while((_=$.exec(this.code))!=null)B(this.matches,new dp.sh.Match(_[1],_.index+_[0].indexOf(_[1]),"tag-name"));$=new RegExp(this.GetKeywords(this.keywords),"igm");while((_=$.exec(this.code))!=null)if(A(this.attributeMatches,_[0])==-1)B(this.matches,new dp.sh.Match(_[0],_.index,"keywords"));$=new RegExp("cfset\\s*.*(\".*?\"|'.*?')","gm");while((_=$.exec(this.code))!=null)if(_[1]!=undefined&&_[1]!=""){B(this.matches,new dp.sh.Match(_[1],_.index+_[0].indexOf(_[1]),"strings"));B(this.stringMatches,_[1])}while((_=dp.sh.RegexLib.DoubleQuotedString.exec(this.code))!=null)if(A(this.stringMatches,_[0])==-1)B(this.matches,new dp.sh.Match(_[0],_.index,"strings"));while((_=dp.sh.RegexLib.SingleQuotedString.exec(this.code))!=null)if(A(this.stringMatches,_[0])==-1)B(this.matches,new dp.sh.Match(_[0],_.index,"strings"))};dp.sh.Brushes.Cpp=function(){var _="ATOM BOOL BOOLEAN BYTE CHAR COLORREF DWORD DWORDLONG DWORD_PTR "+"DWORD32 DWORD64 FLOAT HACCEL HALF_PTR HANDLE HBITMAP HBRUSH "+"HCOLORSPACE HCONV HCONVLIST HCURSOR HDC HDDEDATA HDESK HDROP HDWP "+"HENHMETAFILE HFILE HFONT HGDIOBJ HGLOBAL HHOOK HICON HINSTANCE HKEY "+"HKL HLOCAL HMENU HMETAFILE HMODULE HMONITOR HPALETTE HPEN HRESULT "+"HRGN HRSRC HSZ HWINSTA HWND INT INT_PTR INT32 INT64 LANGID LCID LCTYPE "+"LGRPID LONG LONGLONG LONG_PTR LONG32 LONG64 LPARAM LPBOOL LPBYTE LPCOLORREF "+"LPCSTR LPCTSTR LPCVOID LPCWSTR LPDWORD LPHANDLE LPINT LPLONG LPSTR LPTSTR "+"LPVOID LPWORD LPWSTR LRESULT PBOOL PBOOLEAN PBYTE PCHAR PCSTR PCTSTR PCWSTR "+"PDWORDLONG PDWORD_PTR PDWORD32 PDWORD64 PFLOAT PHALF_PTR PHANDLE PHKEY PINT "+"PINT_PTR PINT32 PINT64 PLCID PLONG PLONGLONG PLONG_PTR PLONG32 PLONG64 POINTER_32 "+"POINTER_64 PSHORT PSIZE_T PSSIZE_T PSTR PTBYTE PTCHAR PTSTR PUCHAR PUHALF_PTR "+"PUINT PUINT_PTR PUINT32 PUINT64 PULONG PULONGLONG PULONG_PTR PULONG32 PULONG64 "+"PUSHORT PVOID PWCHAR PWORD PWSTR SC_HANDLE SC_LOCK SERVICE_STATUS_HANDLE SHORT "+"SIZE_T SSIZE_T TBYTE TCHAR UCHAR UHALF_PTR UINT UINT_PTR UINT32 UINT64 ULONG "+"ULONGLONG ULONG_PTR ULONG32 ULONG64 USHORT USN VOID WCHAR WORD WPARAM WPARAM WPARAM "+"char bool short int __int32 __int64 __int8 __int16 long float double __wchar_t "+"clock_t _complex _dev_t _diskfree_t div_t ldiv_t _exception _EXCEPTION_POINTERS "+"FILE _finddata_t _finddatai64_t _wfinddata_t _wfinddatai64_t __finddata64_t "+"__wfinddata64_t _FPIEEE_RECORD fpos_t _HEAPINFO _HFILE lconv intptr_t "+"jmp_buf mbstate_t _off_t _onexit_t _PNH ptrdiff_t _purecall_handler "+"sig_atomic_t size_t _stat __stat64 _stati64 terminate_function "+"time_t __time64_t _timeb __timeb64 tm uintptr_t _utimbuf "+"va_list wchar_t wctrans_t wctype_t wint_t signed",$="break case catch class const __finally __exception __try "+"const_cast continue private public protected __declspec "+"default delete deprecated dllexport dllimport do dynamic_cast "+"else enum explicit extern if for friend goto inline "+"mutable naked namespace new noinline noreturn nothrow "+"register reinterpret_cast return selectany "+"sizeof static static_cast struct switch template this "+"thread throw true false try typedef typeid typename union "+"using uuid virtual void volatile whcar_t while";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("^ *#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"datatypes"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-cpp";this.Style=".dp-cpp .datatypes { color: #2E8B57; font-weight: bold; }"};dp.sh.Brushes.Cpp.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Cpp.Aliases=["cpp","c","c++"];dp.sh.Brushes.CSharp=function(){var $="abstract as base bool break byte case catch char checked class const "+"continue decimal default delegate do double else enum event explicit "+"extern false finally fixed float for foreach get goto if implicit in int "+"interface internal is lock long namespace new null object operator out "+"override params private protected public readonly ref return sbyte sealed set "+"short sizeof stackalloc static string struct switch this throw true try "+"typeof uint ulong unchecked unsafe ushort using virtual void while";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("^\\s*#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-c";this.Style=".dp-c .vars { color: #d00; }"};dp.sh.Brushes.CSharp.prototype=new dp.sh.Highlighter();dp.sh.Brushes.CSharp.Aliases=["c#","c-sharp","csharp"];dp.sh.Brushes.CSS=function(){var _="ascent azimuth background-attachment background-color background-image background-position "+"background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top "+"border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color "+"border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width "+"border-bottom-width border-left-width border-width border cap-height caption-side centerline clear clip color "+"content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display "+"elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font "+"height letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top "+"margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans "+"outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page "+"page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position "+"quotes richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress "+"table-layout text-align text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em "+"vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index",$="above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder "+"both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed "+"continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double "+"embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia "+"gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic "+"justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha "+"lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower "+"navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset "+"outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side "+"rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow "+"small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize "+"table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal "+"text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin "+"upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow",A="[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif";this.regexList=[{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("\\#[a-zA-Z0-9]{3,6}","g"),css:"value"},{regex:new RegExp("(-?\\d+)(.\\d+)?(px|em|pt|:|%|)","g"),css:"value"},{regex:new RegExp("!important","g"),css:"important"},{regex:new RegExp(this.GetKeywordsCSS(_),"gm"),css:"keyword"},{regex:new RegExp(this.GetValuesCSS($),"g"),css:"value"},{regex:new RegExp(this.GetValuesCSS(A),"g"),css:"value"}];this.CssClass="dp-css";this.Style=".dp-css .value { color: black; }"+".dp-css .important { color: red; }"};dp.sh.Highlighter.prototype.GetKeywordsCSS=function($){return"\\b([a-z_]|)"+$.replace(/ /g,"(?=:)\\b|\\b([a-z_\\*]|\\*|)")+"(?=:)\\b"};dp.sh.Highlighter.prototype.GetValuesCSS=function($){return"\\b"+$.replace(/ /g,"(?!-)(?!:)\\b|\\b()")+":\\b"};dp.sh.Brushes.CSS.prototype=new dp.sh.Highlighter();dp.sh.Brushes.CSS.Aliases=["css"];dp.sh.Brushes.Delphi=function(){var $="abs addr and ansichar ansistring array as asm begin boolean byte cardinal "+"case char class comp const constructor currency destructor div do double "+"downto else end except exports extended false file finalization finally "+"for function goto if implementation in inherited int64 initialization "+"integer interface is label library longint longword mod nil not object "+"of on or packed pansichar pansistring pchar pcurrency pdatetime pextended "+"pint64 pointer private procedure program property pshortstring pstring "+"pvariant pwidechar pwidestring protected public published raise real real48 "+"record repeat set shl shortint shortstring shr single smallint string then "+"threadvar to true try type unit until uses val var varirnt while widechar "+"widestring with word write writeln xor";this.regexList=[{regex:new RegExp("\\(\\*[\\s\\S]*?\\*\\)","gm"),css:"comment"},{regex:new RegExp("{(?!\\$)[\\s\\S]*?}","gm"),css:"comment"},{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("\\{\\$[a-zA-Z]+ .+\\}","g"),css:"directive"},{regex:new RegExp("\\b[\\d\\.]+\\b","g"),css:"number"},{regex:new RegExp("\\$[a-zA-Z0-9]+\\b","g"),css:"number"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-delphi";this.Style=".dp-delphi .number { color: blue; }"+".dp-delphi .directive { color: #008284; }"+".dp-delphi .vars { color: #000; }"};dp.sh.Brushes.Delphi.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Delphi.Aliases=["delphi","pascal"];dp.sh.Brushes.Java=function(){var $="abstract assert boolean break byte case catch char class const "+"continue default do double else enum extends "+"false final finally float for goto if implements import "+"instanceof int interface long native new null "+"package private protected public return "+"short static strictfp super switch synchronized this throw throws true "+"transient try void volatile while";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("\\b([\\d]+(\\.[\\d]+)?|0x[a-f0-9]+)\\b","gi"),css:"number"},{regex:new RegExp("(?!\\@interface\\b)\\@[\\$\\w]+\\b","g"),css:"annotation"},{regex:new RegExp("\\@interface\\b","g"),css:"keyword"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-j";this.Style=".dp-j .annotation { color: #646464; }"+".dp-j .number { color: #C00000; }"};dp.sh.Brushes.Java.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Java.Aliases=["java"];dp.sh.Brushes.JScript=function(){var $="abstract boolean break byte case catch char class const continue debugger "+"default delete do double else enum export extends false final finally float "+"for function goto if implements import in instanceof int interface long native "+"new null package private protected public return short static super switch "+"synchronized this throw throws transient true try typeof var void volatile while with";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("^\\s*#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-c"};dp.sh.Brushes.JScript.prototype=new dp.sh.Highlighter();dp.sh.Brushes.JScript.Aliases=["js","jscript","javascript"];dp.sh.Brushes.Lua=function(){var $="break do end else elseif function if local nil not or repeat return and then until while this",_="math\\.\\w+ string\\.\\w+ os\\.\\w+ debug\\.\\w+ io\\.\\w+ error fopen dofile coroutine\\.\\w+ arg getmetatable ipairs loadfile loadlib loadstring longjmp print rawget rawset seek setmetatable assert tonumber tostring";this.regexList=[{regex:new RegExp("--\\[\\[[\\s\\S]*\\]\\]--","gm"),css:"comment"},{regex:new RegExp("--[^\\[]{2}.*$","gm"),css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"func"},];this.CssClass="dp-lua"};dp.sh.Brushes.Lua.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Lua.Aliases=["lua"];dp.sh.Brushes.Mxml=function(){this.CssClass="dp-mxml";this.Style=".dp-mxml .cdata { color: #000000; }"+".dp-mxml .tag { color : #0000ff; }"+".dp-mxml .tag-name { color: #0000ff; }"+".dp-mxml .script { color: green; }"+".dp-mxml .metadata { color: green; }"+".dp-mxml .attribute { color: #000000; }"+".dp-mxml .attribute-value { color: #990000; }"+".dp-mxml .trace { color: #cc6666; }"+".dp-mxml .var { color: #6699cc; }"+".dp-mxml .comment { color: #009900; }"+".dp-mxml .string { color: #990000; }"+".dp-mxml .keyword { color: blue; }"};dp.sh.Brushes.Mxml.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Mxml.Aliases=["mxml"];dp.sh.Brushes.Mxml.prototype.ProcessRegexList=function(){function H(_,$){_[_.length]=$}function B(B,_){var A=0,$=false;for(A=0;A<B.length;A++)if(_.index>B[A].firstIndex&&_.index<B[A].lastIndex)$=true;return $}var $=0,F=null,D=null,A=null,C="",E=new Array(),_="abstract boolean break byte case catch char class const continue debugger "+"default delete do double else enum export extends false final finally float "+"for function goto if implements import in instanceof int interface long native "+"new null package private protected public return short static super switch "+"synchronized this throw throws transient true try typeof var void volatile while with",G=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("^\\s*#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords("trace"),"gm"),css:"trace"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"keyword"}];A=new RegExp("<\\!\\[CDATA\\[(.|\\s)*?\\]\\]>","gm");while((F=A.exec(this.code))!=null){C=F[0].substr(0,12);H(this.matches,new dp.sh.Match(C,F.index,"cdata"));C=F[0].substr(12,F[0].length-12-6);for(var I=0;I<G.length;I++)while((D=G[I].regex.exec(C))!=null)H(this.matches,new dp.sh.Match(D[0],F.index+12+D.index,G[I].css));C=F[0].substr(F[0].length-6,6);H(this.matches,new dp.sh.Match(C,F.index+F[0].length-6,"cdata"));E.push({firstIndex:F.index,lastIndex:F.index+F[0].length-1})}this.GetMatches(new RegExp("(<|<)!--\\s*.*?\\s*--(>|>)","gm"),"comments");A=new RegExp("([:\\w-.]+)\\s*=\\s*(\".*?\"|'.*?'|\\w+)*|(\\w+)","gm");while((F=A.exec(this.code))!=null){if(F[1]==null)continue;if(B(E,F))continue;H(this.matches,new dp.sh.Match(F[1],F.index,"attribute"));if(F[2]!=undefined)H(this.matches,new dp.sh.Match(F[2],F.index+F[0].indexOf(F[2]),"attribute-value"))}A=new RegExp("(?:<|<)/*\\?*\\s*([:\\w-.]+)","gm");while((F=A.exec(this.code))!=null){if(B(E,F))continue;C=F[0].substr(4,F[0].length-4);switch(C){case"mx:Script":case"/mx:Script":H(this.matches,new dp.sh.Match(F[0]+">",F.index,"script"));break;case"mx:Metadata":case"/mx:Metadata":H(this.matches,new dp.sh.Match(F[0]+">",F.index,"metadata"));break;default:H(this.matches,new dp.sh.Match(F[0],F.index,"tag-name"));break}}A=new RegExp("\\?>|>|/>","gm");while((F=A.exec(this.code))!=null){if(B(E,F))continue;H(this.matches,new dp.sh.Match(F[0],F.index,"tag"))}};dp.sh.Brushes.Perl=function(){var _="abs accept alarm atan2 bind binmode bless caller chdir chmod chomp chop chown chr chroot close closedir connect cos crypt dbmclose dbmopen defined delete dump each endgrent endhostent endnetent endprotoent endpwent endservent eof exec exists exp fcntl fileno flock fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getppid getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt glob gmtime grep hex import index int ioctl join keys kill lc lcfirst length link listen localtime lock log lstat m map mkdir msgctl msgget msgrcv msgsnd no oct open opendir ord pack pipe pop pos print printf prototype push q qq quotemeta qw qx rand read readdir readline readlink readpipe recv ref rename reset reverse rewinddir rindex rmdir scalar seek seekdir semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unlink unpack unshift untie utime values vec waitpid wantarray warn write qr",$="s select goto die do package redo require return continue for foreach last next wait while use if else elsif eval exit unless switch case",A="my our local";this.regexList=[{regex:dp.sh.RegexLib.SingleLinePerlComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("(\\$|@|%)\\w+","g"),css:"vars"},{regex:new RegExp(this.GetKeywords(_),"gmi"),css:"func"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"},{regex:new RegExp(this.GetKeywords(A),"gm"),css:"declarations"}];this.CssClass="dp-perl"};dp.sh.Brushes.Perl.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Perl.Aliases=["perl"];dp.sh.Brushes.Php=function(){var _="abs acos acosh addcslashes addslashes "+"array_change_key_case array_chunk array_combine array_count_values array_diff "+"array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill "+"array_filter array_flip array_intersect array_intersect_assoc array_intersect_key "+"array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map "+"array_merge array_merge_recursive array_multisort array_pad array_pop array_product "+"array_push array_rand array_reduce array_reverse array_search array_shift "+"array_slice array_splice array_sum array_udiff array_udiff_assoc "+"array_udiff_uassoc array_uintersect array_uintersect_assoc "+"array_uintersect_uassoc array_unique array_unshift array_values array_walk "+"array_walk_recursive atan atan2 atanh base64_decode base64_encode base_convert "+"basename bcadd bccomp bcdiv bcmod bcmul bindec bindtextdomain bzclose bzcompress "+"bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite ceil chdir "+"checkdate checkdnsrr chgrp chmod chop chown chr chroot chunk_split class_exists "+"closedir closelog copy cos cosh count count_chars date decbin dechex decoct "+"deg2rad delete ebcdic2ascii echo empty end ereg ereg_replace eregi eregi_replace error_log "+"error_reporting escapeshellarg escapeshellcmd eval exec exit exp explode extension_loaded "+"feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents "+"fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype "+"floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread fscanf "+"fseek fsockopen fstat ftell ftok getallheaders getcwd getdate getenv gethostbyaddr gethostbyname "+"gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt "+"getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext "+"gettimeofday gettype glob gmdate gmmktime ini_alter ini_get ini_get_all ini_restore ini_set "+"interface_exists intval ip2long is_a is_array is_bool is_callable is_dir is_double "+"is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long "+"is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault "+"is_string is_subclass_of is_uploaded_file is_writable is_writeable mkdir mktime nl2br "+"parse_ini_file parse_str parse_url passthru pathinfo readlink realpath rewind rewinddir rmdir "+"round str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split "+"str_word_count strcasecmp strchr strcmp strcoll strcspn strftime strip_tags stripcslashes "+"stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk "+"strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime "+"strtoupper strtr strval substr substr_compare",$="and or xor __FILE__ __LINE__ array as break case "+"cfunction class const continue declare default die do else "+"elseif empty enddeclare endfor endforeach endif endswitch endwhile "+"extends for foreach function include include_once global if "+"new old_function return static switch use require require_once "+"var while __FUNCTION__ __CLASS__ "+"__METHOD__ abstract interface public implements extends private protected throw";this.regexList=[{regex:dp.sh.RegexLib.SingleLineCComments,css:"comment"},{regex:dp.sh.RegexLib.MultiLineCComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp("\\$\\w+","g"),css:"vars"},{regex:new RegExp(this.GetKeywords(_),"gmi"),css:"func"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-c"};dp.sh.Brushes.Php.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Php.Aliases=["php"];dp.sh.Brushes.Python=function(){var $="and assert break class continue def del elif else "+"except exec finally for from global if import in is "+"lambda not or pass print raise return try yield while",_="None True False self cls class_";this.regexList=[{regex:dp.sh.RegexLib.SingleLinePerlComments,css:"comment"},{regex:new RegExp("^\\s*@\\w+","gm"),css:"decorator"},{regex:new RegExp("(['\"]{3})([^\\1])*?\\1","gm"),css:"comment"},{regex:new RegExp("\"(?!\")(?:\\.|\\\\\\\"|[^\\\"\"\\n\\r])*\"","gm"),css:"string"},{regex:new RegExp("'(?!')*(?:\\.|(\\\\\\')|[^\\''\\n\\r])*'","gm"),css:"string"},{regex:new RegExp("\\b\\d+\\.?\\w*","g"),css:"number"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"special"}];this.CssClass="dp-py";this.Style=".dp-py .builtins { color: #ff1493; }"+".dp-py .magicmethods { color: #808080; }"+".dp-py .exceptions { color: brown; }"+".dp-py .types { color: brown; font-style: italic; }"+".dp-py .commonlibs { color: #8A2BE2; font-style: italic; }"};dp.sh.Brushes.Python.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Python.Aliases=["py","python"];dp.sh.Brushes.Ruby=function(){var $="alias and BEGIN begin break case class def define_method defined do each else elsif "+"END end ensure false for if in module new next nil not or raise redo rescue retry return "+"self super then throw true undef unless until when while yield",_="Array Bignum Binding Class Continuation Dir Exception FalseClass File::Stat File Fixnum Fload "+"Hash Integer IO MatchData Method Module NilClass Numeric Object Proc Range Regexp String Struct::TMS Symbol "+"ThreadGroup Thread Time TrueClass";this.regexList=[{regex:dp.sh.RegexLib.SingleLinePerlComments,css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp(":[a-z][A-Za-z0-9_]*","g"),css:"symbol"},{regex:new RegExp("(\\$|@@|@)\\w+","g"),css:"variable"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"},{regex:new RegExp(this.GetKeywords(_),"gm"),css:"builtin"}];this.CssClass="dp-rb";this.Style=".dp-rb .symbol { color: #a70; }"+".dp-rb .variable { color: #a70; font-weight: bold; }"};dp.sh.Brushes.Ruby.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Ruby.Aliases=["ruby","rails","ror"];dp.sh.Brushes.Sql=function(){var _="abs avg case cast coalesce convert count current_timestamp "+"current_user day isnull left lower month nullif replace right "+"session_user space substring sum system_user upper user year",$="absolute action add after alter as asc at authorization begin bigint "+"binary bit by cascade char character check checkpoint close collate "+"column commit committed connect connection constraint contains continue "+"create cube current current_date current_time cursor database date "+"deallocate dec decimal declare default delete desc distinct double drop "+"dynamic else end end-exec escape except exec execute false fetch first "+"float for force foreign forward free from full function global goto grant "+"group grouping having hour ignore index inner insensitive insert instead "+"int integer intersect into is isolation key last level load local max min "+"minute modify move name national nchar next no numeric of off on only "+"open option order out output partial password precision prepare primary "+"prior privileges procedure public read real references relative repeatable "+"restrict return returns revoke rollback rollup rows rule schema scroll "+"second section select sequence serializable set size smallint static "+"statistics table temp temporary then time timestamp to top transaction "+"translation trigger true truncate uncommitted union unique update values "+"varchar varying view when where with work",A="all and any between cross in join like not null or outer some";this.regexList=[{regex:new RegExp("--(.*)$","gm"),css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:dp.sh.RegexLib.SingleQuotedString,css:"string"},{regex:new RegExp(this.GetKeywords(_),"gmi"),css:"func"},{regex:new RegExp(this.GetKeywords(A),"gmi"),css:"op"},{regex:new RegExp(this.GetKeywords($),"gmi"),css:"keyword"}];this.CssClass="dp-sql";this.Style=".dp-sql .func { color: #ff1493; }"+".dp-sql .op { color: #808080; }"};dp.sh.Brushes.Sql.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Sql.Aliases=["sql"];dp.sh.Brushes.Vb=function(){var $="AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto "+"Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate "+"CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType "+"Date Decimal Declare Default Delegate Dim DirectCast Do Double Each "+"Else ElseIf End Enum Erase Error Event Exit False Finally For Friend "+"Function Get GetType GoSub GoTo Handles If Implements Imports In "+"Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module "+"MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing "+"NotInheritable NotOverridable Object On Option Optional Or OrElse "+"Overloads Overridable Overrides ParamArray Preserve Private Property "+"Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume "+"Return Select Set Shadows Shared Short Single Static Step Stop String "+"Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until "+"Variant When While With WithEvents WriteOnly Xor";this.regexList=[{regex:new RegExp("'.*$","gm"),css:"comment"},{regex:dp.sh.RegexLib.DoubleQuotedString,css:"string"},{regex:new RegExp("^\\s*#.*","gm"),css:"preprocessor"},{regex:new RegExp(this.GetKeywords($),"gm"),css:"keyword"}];this.CssClass="dp-vb"};dp.sh.Brushes.Vb.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Vb.Aliases=["vb","vb.net"];dp.sh.Brushes.Xml=function(){this.CssClass="dp-xml";this.Style=".dp-xml .cdata { color: #ff1493; }"+".dp-xml .tag, .dp-xml .tag-name { color: #069; font-weight: bold; }"+".dp-xml .attribute { color: red; }"+".dp-xml .attribute-value { color: blue; }"};dp.sh.Brushes.Xml.prototype=new dp.sh.Highlighter();dp.sh.Brushes.Xml.Aliases=["xml","xhtml","xslt","html","xhtml"];dp.sh.Brushes.Xml.prototype.ProcessRegexList=function(){function B(_,$){_[_.length]=$}var $=0,A=null,_=null;this.GetMatches(new RegExp("(<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](>|>)","gm"),"cdata");this.GetMatches(new RegExp("(<|<)!--\\s*.*?\\s*--(>|>)","gm"),"comments");_=new RegExp("([:\\w-.]+)\\s*=\\s*(\".*?\"|'.*?'|\\w+)*|(\\w+)","gm");while((A=_.exec(this.code))!=null){if(A[1]==null)continue;B(this.matches,new dp.sh.Match(A[1],A.index,"attribute"));if(A[2]!=undefined)B(this.matches,new dp.sh.Match(A[2],A.index+A[0].indexOf(A[2]),"attribute-value"))}this.GetMatches(new RegExp("(<|<)/*\\?*(?!\\!)|/*\\?*(>|>)","gm"),"tag");_=new RegExp("(?:<|<)/*\\?*\\s*([:\\w-.]+)","gm");while((A=_.exec(this.code))!=null)B(this.matches,new dp.sh.Match(A[1],A.index+A[0].indexOf(A[1]),"tag-name"))}
//}}}
Umbra's heart is contained within the [[UmbraEngine]] class. It is a very special class that must be instantiated in order for Umbra to run any program.
[[UmbraEngine]] contains a series of public methods that the developer has at his disposal. These methods will be used to register new modules, activate and deactivate them, register fonts, add callbacks containing global keybindings along with their respective custom code, display error messages (hopefully not!) and so on.
There needs to be one and only one instance of the engine, no more, no less. After having registered modules, fonts and callbacks and having configured other things the developer might need, the engine can then be initialised. The initialisation process contains a series of self checks and will only be successful if no obstacles are encountered. In case there are problems, an error log will be created in a text file in the working directory. The file will contain the error(s) the engine has encountered.
Once the initialisation is successful, the engine can be run. Running the engine initiates the main program loop. In it, modules are executed, activated and deactivated, mouse and keyboard input is read and so on. This is when the program is actually executed.
A timeout is set time period after which a module automatically deactivates itself. The timeout, off by default, can be set by calling the [[UmbraModule::setTimeout]] method. After this is done, the module will initialise its timeout each time it is activated.
The entire process is handled internally, so the only thing the developer needs to worry about is setting the timeout. This is typically done in the module's constructor:
<code C++>
MyModule::MyModule() {
setTimeout(5000);
}
</code>
Let's begin with source code for a //Hello world// program. First, let's create the following {{{main.hpp}}} file:
<code C++>
#include "umbra.hpp" //assumes its search directory is known
class HelloWorld : public UmbraModule {
void render ();
};
</code>
Then, let's create the following {{{main.cpp}}} file:
<code C++>
#include "main.hpp"
void HelloWorld::render () {
TCODConsole::root->print(0,0,"Hello world!");
}
int main (void) {
UmbraEngine engine;
engine.registerModule(new HelloWorld());
engine.activateModule(0);
if (engine.initialise()) return engine.run();
else return 1;
}
</code>
This is the simplest program one can create using Umbra. All it does is display the line "Hello world!" and wait patiently for the user to close the program window. Now, let's analyse the program.
The file {{{main.hpp}}} starts with an {{{#include "umbra.hpp"}}} statement. Obviously, the compiler needs to know where to look for the file, so it's best you specify it as one of your search directories. This is Umbra's main header file and contains all the other header files required by the library. This is the only header you will need to include to make Umbra work.
The next statement is {{{class HelloWorld : public UmbraModule}}}. This creates a class called {{{HelloWorld}}} that inherits an [[UmbraModule]]. Please refer to the [[What is a module]] and [[Creating a module]] sections to learn the details. All we need to know right now is that Umbra will not recognise our class if it doesn't inherit an [[UmbraModule]].
One of the methods we can define and implement is {{{void render ()}}}. This is the method that Umbra will call internally in order to make our module render things on the screen. As you can see, we don't have to call it manually anywhere in the code. All it contains is a libtcod-specific method call {{{TCODConsole::root->print(0,0,TCOD_BKGND_NONE,"Hello world!");}}}. This is what prints the line "Hello world!" on the console. ''Note that we don't include [[libtcod|http://doryen.eptalys.net/libtcod]] anywhere in the code - Umbra does it internally.''
After implementing the method in {{{main.cpp}}}, we can finally write the heart of our program. First thing we need to do is to create an instance of [[UmbraEngine]], conveniently called {{{engine}}}. Lines 09. and 10. are crucial for the engine to start running. The first one creates an instance of our {{{HelloWorld}}} class and registers it as a module inside the engine. The next line makes the engine activate the newly registered module. The argument we pass it is an integer value of 0 - this is the ID that our module was assigned. Please refer to [[UmbraEngine::registerModule]] for more details on how module ID numbers are assigned.
Finally, we reach the point where the module is registered and active, and the only missing thing is running the engine. First, we need to initialise it, and if the initialisation is successful, run the engine. That's it, this is the code that powers a "Hello world" program made with Umbra.
~UmbraCallback is a mother class that should be inherited by all [[callbacks|What are callbacks]]. It provides a basic set of methods necessary to create a callback and make it work.
When a callback is [[created|Creating a callback]], the class that inherits ~UmbraCallback, it should specify the keystroke that the callback should listen to, as well as custom code that will be launched when the keystroke the listener listens to is detected.
This class contains a pure virtual method and thus cannot be instantiated without it being inherited by a child class.
!Methods
<<tiddler UmbraCallbackMethods>>
!Description
Contains custom code, executed when the callback listener detects the keystroke it listens to.
!Construction
<code C++>
virtual void action (
) = 0;
</code>
!Access level
{{{protected}}}
!Remarks
This method is declared as pure virtual, thus it is mandatory to override it. It should never be called manually, as the engine will take care of calling it when it's necessary to do so.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","tutorial"])'>>
!Description
Custom code for the callback listener, evaluating whether the keystroke it catches matches the keystroke it is supposed to be listening to.
!Construction
<code C++>
virtual bool evaluate (
UmbraKey k
);
</code>
!Parametres
*{{{UmbraKey k}}}: the keystroke the listener will compare with what it is listening to. This is passed automatically by the engine.
!Return value
{{{bool}}}: {{{true}}} if the keystroke matches the one(s) the callback listens to, {{{false}}} otherwise.
!Access level
{{{protected}}}
!Remarks
This method is called automatically by the engine. It should not be overlodaded unless the callback should listen to something unusual, such as two different keystrokes.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","tutorial"])'>>
!Description
Provides a pointer to the engine.
!Construction
<code C++>
UmbraEngine * getEngine (
);
</code>
!Return value
{{{UmbraEngine *}}}: a pointer to the engine.
!Access level
{{{protected}}}
!Remarks
This method is included in the {{{UmbraCallback}}} to make communication with the game engine easier. Using it enables you to call the engine's methods from within the module (for instance, module activation).
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","tutorial"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","tutorial"])'>>
!Description
Activates previous font from the fonts list.
!Keybinding
{{{PgDn}}}
!Action
<code C++>
if (getEngine()->activateFont(-1)) getEngine()->reinitialise();
</code>
!Remarks
If the currently used font is the first on the list, the callback has no effect. Otherwise, the font is activated and the engine is reinitialised.
Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackFontDown());
</code>
!Description
Activates next font from the fonts list.
!Keybinding
{{{PgUp}}}
!Action
<code C++>
if (getEngine()->activateFont(1)) getEngine()->reinitialise();
</code>
!Remarks
If the currently used font is the last on the list, the callback has no effect. Otherwise, the font is activated and the engine is reinitialised.
Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackFontUp());
</code>
!Description
Toggle fullscreen/windowed mode.
!Keybinding
{{{Alt + Enter}}}
!Action
<code C++>
TCODConsole::setFullscreen(!TCODConsole::isFullscreen());
</code>
!Remarks
Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackFullscreen());
</code>
*[[UmbraCallback::action]]
*[[UmbraCallback::evaluate]]
*[[UmbraCallback::getEngine]]
!Description
Toggles engine pause on and off.
!Keybinding
{{{Pause}}}
!Action
<code C++>
getEngine()->setPause(!getEngine()->getPause());
</code>
!Remarks
Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackPause());
</code>
!Description
Quits the application.
!Keybinding
{{{Alt + F4}}} or {{{Ctrl + q}}}
!Action
<code C++>
getEngine()->deactivateAll();
</code>
!Remarks
Listens to two different keystroke combinations. Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackQuit());
</code>
!Description
Saves a screenshot.
!Keybinding
{{{PrtSc}}}
!Action
<code C++>
TCODSystem::saveScreenshot(NULL);
</code>
!Remarks
Registered automatically if default callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackScreenshot());
</code>
!Description
Toggles the Speed-o-meter internal module on and off.
!Keybinding
{{{F5}}}
!Action
<code C++>
if (getEngine()->getModule(UMBRA_INTERNAL_SPEEDOMETER)->getActive()) {
getEngine()->deactivateModule(UMBRA_INTERNAL_SPEEDOMETER);
}
else {
getEngine()->activateModule(UMBRA_INTERNAL_SPEEDOMETER);
}
</code>
!Remarks
Registered automatically if additional callbacks are enabled during engine initialisation. If not, it can be registered manually:
<code C++>
UmbraEngine::getInstance()->registerCallback(new UmbraCallbackSpeedometer());
</code>
Although UmbraCircle complements [[UmbraPoint]] and [[UmbraRect]], it is not directly related to the [[UmbraWidget]] or any other Umbra component, as it is not used internally. It's a convenience class, made to be used directly by the developer in case a circle-related behaviour, such as checking whether a point is within a certain radius from another point, is required.
It implements basic methods that allow the circle to be moved and resized. Also, it is possible to check whether a point (or a set of coordinates) is within the circle.
!Methods
<<tiddler UmbraCircleMethods>>
!See also
* [[UmbraPoint]]
* [[UmbraRect]]
!Description
Constructor for a circle object.
!Construction
<code C++>
UmbraCircle () {
x = 0;
y = 0;
r = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraCircle (int r) {
x = 0;
y = 0;
this->r = r;
mouseHover = false;
}
</code>
<code C++>
UmbraCircle (int x, int y) {
this->x = x;
this->y = y;
r = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraCircle (const UmbraPoint &p) {
x = p.x;
y = p.y;
r = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraCircle (const UmbraPoint &p, int r) {
x = p.x;
y = p.y;
this->r = r;
mouseHover = false;
}
</code>
!Parametres
*{{{int x}}}: the X coordinate of the circle's centre
*{{{int y}}}: the Y coordinate of the circle's centre
*{{{int r}}}: the circle's radius
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the circle's centre coordinates
!Example
<code C++>
UmbraPoint p(20,30);
UmbraCircle c1();
UmbraCircle c2(10,12,4);
UmbraCircle c3(p,2);
</code>
!Description
Checks whether a given set of coordinates or UmbraPoint instance are within a circle.
!Construction
<code C++>
bool contains (
int px,
int py
);
</code>
<code C++>
bool contains (
const UmbraPoint &p
);
</code>
!Parametres
*{{{int px}}}: the X coordinate to be checked
*{{{int py}}}: the Y coordinate to be checked
*{{{const UmbraPoint &p}}}: an instance of UmbraPoint to be checked
!Return value
{{{bool}}}: {{{true}}} if the coordinates are within the circle, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
This method is typically used to check whether the mouse cursor is hovering over a given circle.
!Example
<code C++>
UmbraPoint p1(10,20), p2(15,18);
UmbraCircle c(20,20,8);
printf("%s, %s, %s\n",
c.contains(p1) ? "true" : "false",
c.contains(p2) ? "true" : "false",
c.contains(12,12) ? "true" : "false"
);
</code>
!!!Output
{{{false, true, false}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","tutorial"])'>>
!Description
Sets the circle centre's position, as well as its radius.
!Construction
<code C++>
void set (
int x,
int y,
int r
);
</code>
<code C++>
void set (
const UmbraPoint &p,
int r
);
</code>
!Parametres
*{{{int x}}}: the X coordinate of the circle's centre
*{{{int y}}}: the Y coordinate of the circle's centre
*{{{int r}}}: the circle's radius
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the circle centre's coordinates
!Access level
{{{public}}}
!Remarks
Should it be necessary to only cgange the circle's radius, it's more convenient to use [[UmbraCircle::setRadius]].
In case the circle only needs to be moved to a different position, without resizing it, it's best to use [[UmbraCircle::setPos]].
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","tutorial"])'>>
!Description
Moves the circle to a different position.
!Construction
<code C++>
void setPos (
int x,
int y
);
</code>
<code C++>
void setPos (
const UmbraPoint &p
);
</code>
!Parametres
*{{{int x}}}: the X coordinate of the circle's centre
*{{{int y}}}: the Y coordinate of the circle's centre
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the circle centre's coordinates
!Access level
{{{public}}}
!Remarks
This method only changes the circle's position (or, more precisely, its centre's position), but does not change its radius.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","tutorial"])'>>
!Description
Changes the circle's radius.
!Construction
<code C++>
void setRadius (
int r
);
</code>
!Parametres
*{{{int r}}}: the circle's new radius
!Access level
{{{public}}}
!Remarks
This method only changes the circle's radius, leaving it at the same position it was at before.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["circle","tutorial"])'>>
* [[UmbraCircle::contains]]
* [[UmbraCircle::setPos]]
* [[UmbraCircle::setRadius]]
* [[UmbraCircle::set]]
* [[UmbraCircle::UmbraCircle]]
The class UmbraEngine contains the heart of Umbra. An instance of UmbraEngine needs to be created in order for Umbra to become alive:
<code C++>
UmbraEngine engine;
</code>
Umbra will look for its {{{umbra.txt}}} configuration file in {{{./data/cfg/}}} by default. If you want it in another directory, use this syntax:
<code C++>
UmbraEngine engine("my/directory/filename.txt");
</code>
The default set of registered callbacks can also be altered in the constructor:
<code C++>
UmbraEngine engine(UMBRA_REGISTER_ALL);
</code>
Of course, you can set both of the above:
<code C++>
UmbraEngine engine("my/directory/filename.txt", UMBRA_REGISTER_ALL);
</code>
Then, several [[UmbraModule]]s need to be declared. The modules are assigned unique id numbers. The first registered module has {{{id=0}}}, the second one has {{{id=1}}} and so on. Make sure you can refer to those modules in some way, for instance, by creating an enumeration with unique names. The registerModule function also returns the registered module id.
<code C++>
engine.registerModule(new Credits());
</code>
You can also register a module with a fallback module that will be automatically activated when the current module is deactivated (when its update function returns {{{false}}}):
<code C++>
int mainMenuId = engine.registerModule(new MainMenu());
engine.registerModule(new Credits(), mainMenuId);
</code>
Then you have to activate at least one module, the game starting screen.
<code C++>
engine.activateModule(mainMenuId);
</code>
Finally, the engine is initialised and run:
<code C++>
if (engine.init()) return engine.run();
else return 1;
</code>
The [[UmbraEngine::initialise]] method actually creates the game window. The [[UmbraEngine::run]] method contains the main game loop, which exits when all modules are deactivated.
See [[Font autodetection]] for details about how font are loaded by Umbra.
<code C++>
UmbraEngine engine;
engine.registerModule(new Credits());
int mainMenuId = engine.registerModule(new MainMenu());
engine.registerModule(new Credits(), mainMenuId);
engine.activateModule(mainMenuId);
if (engine.init()) return engine.run();
else return 1;
</code>
!Methods
<<tiddler UmbraEngineMethods>>
!Description
Constructor for the engine.
!Construction
<code C++>
UmbraEngine (
const char *fileName = "data/cfg/umbra.txt",
UmbraRegisterCallbackFlag flag = UMBRA_REGISTER_DEFAULT
);
</code>
!Parametres
*{{{const char *fileName}}}: the string containing the file name (with path) to Umbra's configuration file. Defaults to {{{"data/cfg/umbra.txt"}}}.
*{{{UmbraRegisterCallbackFlag flag}}}: the flag, as defined in [[UmbraRegisterCallbackFlag]], telling the engine which [[internal callbacks|Internal callbacks]] should be registered.
!Remarks
Before being initialised, the engine needs to be instantiated. The constructor may take 2, 1 or no arguments, as they both have default values.
!Example
<code C++>
//use default values
UmbraEngine engine;
</code>
<code C++>
//specify the config file, but leave callbacks at default
UmbraEngine engine("path/to/config/file.txt");
</code>
<code C++>
//specify which callbacks to register, but leave path at default
UmbraEngine engine(UMBRA_REGISTER_ALL);
</code>
<code C++>
//specify everything explicitly
UmbraEngine engine("path/to/config/file.txt",UMBRA_REGISTER_ALL);
</code>
!Description
Activates a previously registered font.
!Construction
<code C++>
bool activateFont (
int shift = 0
);
</code>
!Parametres
*{{{int shift}}}: a value of 0 will cause no effect at all. This is the default value, so specifying none will also cause no effect. A positive value will switch to the next font in the registered fonts list. A negative value will switch to the previous font in the registered fonts list.
!Return value
{{{bool}}}: {{{true}}} if the requested font could be activated, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
This method will reinitialise the root console.
If the engine is created with the {{{registerDefaultCallbacks}}} parametre set to {{{true}}}, this method can be accessed anytime during the application runtime by pressing the page up and page down keys.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","tutorial"])'>>
!Description
Puts a module on the activation list. It will be activated in the next main game loop iteration (next frame).
!Construction
<code C++>
void activateModule (
int moduleId
);
</code>
<code C++>
void activateModule (
UmbraModule * mod
);
</code>
<code C++>
void activateModule (
UmbraInternalModuleID id
);
</code>
<code C++>
void activateModule (
const char * moduleName
);
</code>
!Parametres
*{{{int moduleId}}} - the ID number of a module, returned by the [[UmbraEngine::registerModule]] method.
*{{{UmbraModule * mod}}} - pointer to a module, returned by the [[UmbraEngine::getModule]] methods.
*{{{UmbraInternalModuleID id}}} - the ID number of an internal module, as defined in the [[UmbraInternalModuleID]] enum.
*{{{const char *moduleName}}} - the name of a module.
!Access level
{{{public}}}
!Remarks
You may use four ways of specifying a module: either specify the integer user module ID, pass a reference to the module in question, its name, or specify an internal module ID.
!Example
<code c++>
//module IDs
enum {
MOD_CREDITS,
MOD_MAIN_MENU
};
UmbraEngine engine;
engine.registerModule (new Module1(), MOD_CREDITS);
UmbraModule * mod = new Module2();
engine.registerModule (mod, MOD_MAIN_MENU);
engine.activateModule(MOD_CREDITS); //user module ID
engine.activateModule(mod); //module reference
engine.activateModule(UMBRA_INTERNAL_SPEEDOMETER); //internal module ID
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Deactivates all active modules.
!Construction
<code C++>
void deactivateAll (
bool ignoreFallbacks = false
);
</code>
!Parametres
*{{{bool ignoreFallbacks}}}: whether the fallbacks should be ignored or not.
!Access level
{{{public}}}
!Remarks
If the active modules' respective fallbacks are supposed to be activated, the {{{ignoreFallbacks}}} parametre can be omitted or set to {{{false}}}.
If the program is supposed to end, the {{{ignoreFallbacks}}} parametre can be set to {{{true}}}. This will ignore all active modules' respective fallbacks, effectively ending the application.
!Example
<code c++>
class MyModule : public UmbraModule {
...
void MyModule::shutdown (void) {
getEngine()->deactivateAll(true);
}
};
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Puts a module on the deactivation list. It will be deactivated in the next main game loop iteration (next frame).
!Construction
<code C++>
void deactivateModule (
int moduleId
);
</code>
<code C++>
void deactivateModule (
UmbraModule * mod
);
</code>
<code C++>
void deactivateModule (
UmbraInternalModuleID id
);
</code>
<code C++>
void deactivateModule (
const char * moduleName
);
</code>
!Parametres
*{{{int moduleId}}} - the ID number of a module, returned by the [[UmbraEngine::registerModule]] method.
*{{{UmbraModule * mod}}} - pointer to a module, returned by the [[UmbraEngine::getModule]] methods.
*{{{UmbraInternalModuleID id}}} - the ID number of an internal module, as defined in the [[UmbraInternalModuleID]] enum.
*{{{const char * moduleName}}} - the module name.
!Access level
{{{public}}}
!Remarks
You may use four ways of specifying a module: either specify the integer user module ID, pass a reference to the module in question, its name, or specify an internal module ID.
!Example
<code c++>
//module IDs
enum {
MOD_CREDITS,
MOD_MAIN_MENU
};
UmbraEngine engine;
engine.registerModule (new Module1(), MOD_CREDITS);
UmbraModule * mod = new Module2();
engine.registerModule (mod, MOD_MAIN_MENU);
engine.activateModule(MOD_CREDITS); //user module ID
engine.activateModule(mod); //module reference
engine.activateModule(UMBRA_INTERNAL_SPEEDOMETER); //internal module ID
engine.deactivateModule(MOD_CREDITS); //user module ID
engine.deactivateModule(mod); //module reference
engine.deactivateModule(UMBRA_INTERNAL_SPEEDOMETER); //internal module ID
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Displays a [[BSOD]] with the last error in the [[error log|Error log]].
!Construction
<code C++>
void displayError (
);
</code>
!Access level
{{{public}}}
!Remarks
This method will display the error only if the debug mode is set to {{{true}}} in the configuration file.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Retrieves the path to the directory where font files are stored.
!Construction
<code C++>
const char * getFontDir (
);
</code>
!Return value
{{{const char*}}}: the path to the font directory.
!Access level
{{{public}}}
!Remarks
The path will be relative to the application's working directory. For instance, if the working directory is {{{/home/mingos/Documents/umbra/}}} and the font files are kept in {{{/home/mingos/Documents/umbra/data/img/}}}, this method will return {{{/data/img/}}}.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","tutorial"])'>>
!Description
Gets the ID number of the font that is currently in use.
!Construction
<code C++>
int getFontID (
);
</code>
!Return value
{{{int}}}: the ID number of the currently used font
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","tutorial"])'>>
!Description
Returns a pointer the the engine.
!Construction
<code C++>
static UmbraEngine * getInstance (
);
</code>
!Return value
{{{UmbraEngine*}}}: a pointer to the engine.
!Access level
{{{public}}}
!Remarks
Since modules aren't intrinsically aware of the engine, this static method hands them a possibility to access the engine's methods.
This method is static, so it can be called without having to use the engine instance's name.
It can be called from anywhere in the code.
!Example:
<code c++>
UmbraEngine engine;
UmbraEngine::getInstance()->registerFont(32,8,"data/img/font10x10.png");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","tutorial"])'>>
!Description
Retrieves the currently used keyboard mode (the way the engine reacts to keyboard events).
!Construction
<code C++>
UmbraKeyboardMode getKeyboardMode (
);
</code>
!Return value
{{{UmbraKeyboardMode}}}: the keyboard mode, as defined in the [[UmbraKeyboardMode]] enum.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","tutorial"])'>>
!Description
Retrieves a reference to the module with the ID passed in the argument.
!Construction
<code C++>
UmbraModule * getModule (
int moduleId
);
</code>
<code C++>
UmbraModule * getModule (
UmbraInternalModuleID id
);
</code>
<code C++>
UmbraModule * getModule (
const char * moduleName
);
</code>
!Parametres
*{{{int moduleId}}}: the ID number of a module, returned by the [[UmbraEngine::registerModule]] method.
*{{{UmbraInternalModuleID id}}}: the ID number of an internal module, as defined in the [[UmbraInternalModuleID]] enum.
*{{{const char * moduleName}}}: the name of the module
!Return value
{{{UmbraModule*}}}: reference to the UmbraModule object with the specified ID
!Access level
{{{public}}}
!Remarks
Both user modules (integer ID) and internal modules (UmbraInternalModuleID) can be retrieved that way.
Modules need to be registered to be retrieved. They needn't be active or even initialised.
!Example
<code C++>
const int modID = 0;
UmbraEngine engine;
engine.registerModule (new Module(), modID);
UmbraModule * mod = engine.getModule (modID); //registered user module
UmbraModule * mod2 = engine.getModule (UMBRA_INTERNAL_SPEEDOMETER); //an internal module
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","tutorial"])'>>
!Description
Retrieves the specified module's ID number based on its name or a reference to it.
!Construction
<code C++>
int getModuleId (
UmbraModule * mod
);
</code>
<code C++>
int getModuleId (
const char * name
);
</code>
!Parametres
*{{{UmbraModule * mod}}}: a pointer to the module whose ID number is to be retrieved
*{{{const char * name}}}: the name of the module whose ID number is to be retrieved
!Return value
{{{int}}}: the module's ID number on success or {{{-1}}} on failure
!Access level
{{{public}}}
!Remarks
The method taking a pointer to the module as parametre is deprecated. Use [[UmbraModule::getID]] instead.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","tutorial"])'>>
!Description
Gets the total number of registered fonts.
!Construction
<code C++>
int getNbFonts (
);
</code>
!Return value
{{{int}}}: the number of fonts that are currently registered and ready for use.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","tutorial"])'>>
!Description
Checks whether the engine is paused or not.
!Construction
<code C++>
bool getPause (
);
</code>
!Return value
{{{bool}}}: {{{true}}} if the engine is paused, {{{false}}} otherwise.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","tutorial"])'>>
!Description
Gets the height of the root console.
!Construction
<code C++>
int getRootHeight (
);
</code>
!Return value
{{{int}}}: the height of the root console in cells.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","tutorial"])'>>
!Description
Gets the width of the root console.
!Construction
<code C++>
int getRootWidth (
);
</code>
!Return value
{{{int}}}: the width of the root console in cells.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","tutorial"])'>>
!Description
Initialises the engine.
!Construction
<code C++>
bool initialise (
TCOD_renderer_t renderer = TCOD_RENDERER_SDL
);
</code>
!Parametres
*{{{TCOD_renderer_t renderer}}}: the renderer libtcod should use. For a list of available renderers, please refer to [[libtcod documentation|http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/console_init_root.html?c=false&cpp=true&cs=false&py=false&lua=false]]. Defaults to SDL.
!Return value
{{{bool}}}: {{{true}}} if the engine has been initialised successfully, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
The engine needs to be initialised before registering any modules and fonts. It cannot run if it is not initialised.
!Example
<code c++>
UmbraEngine engine;
engine.registerModule(new MyModule());
engine.activateModule(0);
if (engine.initialise()) return engine.run();
else return 1;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","engineClass","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","tutorial"])'>>
!Description
Loads an entire module configuration from an external configuration file.
!Construction
<code C++>
bool loadModuleConfiguration (
const char * filename,
UmbraModuleFactory *factory,
const char * chainName = NULL
);
</code>
<code C++>
bool loadModuleConfiguration (
const char * filename,
const char * chainName = NULL
);
</code>
!Parametres
*{{{const char * filename}}}: the file (with relative path) containing the module chains configuration.
*{{{UmbraModuleFactory * factory}}}: a class that creates a module from its name. If you don't provide a factory, you have to register all the modules by hand. See [[UmbraModuleFactory]].
*{{{const char * chainName}}}: the name of the chain configuration to be loaded. Leave at default to either load the configuration defined in {{{umbra.txt}}} or, if it's missing, the first encountered configuration.
!Return value
{{{bool}}}: {{{true}}} if the configuration has been loaded correctly, {{{false}}} otherwise (check the error log in this case).
!Access level
{{{public}}}
!Remarks
This method is not necessary in simple projects, but is useful if a single project should have the possibility to run different programs (or versions of the same program) using a single executable.
It must be run before the engine is initialised. The best way to call it is shown in the example below.
!Example
The methid can be called in four different ways:
<code C++>
if (engine.loadModuleConfiguration("data/cfg/module.txt",new MyModuleFactory(),"demo") && engine.initialise()) return engine.run();
else return 1;
</code>
<code C++>
if (engine.loadModuleConfiguration("data/cfg/module.txt","demo") && engine.initialise()) return engine.run();
else return 1;
</code>
<code C++>
if (engine.loadModuleConfiguration("data/cfg/module.txt",new MyModuleFactory()) && engine.initialise()) return engine.run();
else return 1;
</code>
<code C++>
if (engine.loadModuleConfiguration("data/cfg/module.txt") && engine.initialise()) return engine.run();
else return 1;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","tutorial"])'>>
!Description
Displays a credits line.
!Construction
<code C++>
void printCredits (
int x,
int y,
uint32 duration = 10000
);
</code>
!Parametres
*{{{int x}}}: the {{{x}}} position on the root console where the credits should be printed
*{{{int y}}}: the {{{y}}} position on the root console where the credits should be printed
*{{{uint32 duration}}} (optional): the duration, in milliseconds, of the credits display
!Access level
{{{public}}}
!Remarks
The credits will simply output a credits line. It will start fading away at exactly half the specified duration, and fade out completely after the other half has passed.
This method may be used as many times as needed.
!Example
<code c++>
void MyModule::onActivate() {
getEngine()->printCredits(20,20);
}
</code>
!Description
Registers a callback, creating an active listener.
!Construction
<code C++>
void registerCallback (
UmbraCallback * cbk
);
</code>
!Parametres
*{{{UmbraCallback * cbk}}}: a pointer to a callback.
!Access level
{{{public}}}
!Remarks
In order to be registered, a callback needs to be [[created|Creating a callback]]. It can be instantiated using the {{{new}}} operator inside the method's argument.
!Example
<code C++>
UmbraEngine engine;
engine.registerCallback(new MyCallback());
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["callback","tutorial"])'>>
!Description
Registers a font file to be used by libtcod.
!Construction
<code C++>
void UmbraEngine::registerFont (
int columns,
int rows,
const char * filename,
int flags
);
</code>
!Parametres
*{{{int columns}}}: the number of columns in the font image file
*{{{int rows}}}: the number of rown int the font image file
*{{{const char * filename}}}: the name of the file containing the font image
*{{{int flags}}}: the flags that should be used to register the font. Please refer to [[libtcod documentation|http://doryen.eptalys.net/data/libtcod/doc/1.5.0/console/console_set_bitmap_font_size.html]] to check the available flags.
!Access level
{{{public}}}
!Remarks
If you don't want Umbra to use [[Font autodetection]], you have to register your font(s) manually before initialising the engine.
Use this method to register each font, ordered smallest to biggest. The parametres are the same as in libtcod's {{{setCustomFont}}} method. See libtcod documentation for details.
Each registered font is assigned an ID, starting with 0 and incrementing. The user can change the font by pressing pageUp or pageDown (by default). You can know what font is currently used with [[UmbraEngine::getFontID]].
!Example:
<code C++>
UmbraEngine engine;
engine.registerFont(32,8,"./fonts/arial8x8.png", TCOD_FONT_LAYOUT_TCOD|TCOD_FONT_GREYSCALE);
engine.registerFont(32,8,"./fonts/arial10x10.png", TCOD_FONT_LAYOUT_TCOD|TCOD_FONT_GREYSCALE);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["font","tutorial"])'>>
!Description
Registers a user defined module for future activation.
!Construction
<code C++>
int registerModule (
UmbraModule * module
);
</code>
!Parametres
*{{{UmbraModule * module}}}: a pointer to the module to be registered.
!Return value
{{{int}}}: the registered module's ID number.
!Access level
{{{public}}}
!Remarks
The method requires the creation of a pointer to a module. The easiest way to achieve this is to call the {{{new}}} operator inside the method's argument.
The method registers the newly created module for further use and assigns it an ID number. This number is generated automatically: the first registered module gets ID 0, the next is assigned 1, then 2, and so on, progressively. It is always wise to control the order in which the modules are registered.
!Example
<code C++>
UmbraEngine engine;
int mod1 = engine.registerModule(new MyModule()); //mod1 = 0
int mod2 = engine.registerModule(new MyModule2()); //mod2 = 1;
engine.getModule(mod2)->setFallback(mod1); // mod2 falls back to the previous one
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["module","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["module","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["module","tutorial"])'>>
!Description
Reinitialises the engine.
!Construction
<code C++>
void reinitialise (
TCOD_renderer_t renderer = TCOD_RENDERER_SDL
);
</code>
!Parametres
*{{{TCOD_renderer_t renderer}}}: the renderer libtcod should use. For a list of available renderers, please refer to [[libtcod documentation|http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/console_init_root.html?c=false&cpp=true&cs=false&py=false&lua=false]]. Defaults to SDL.
!Access level
{{{public}}}
!Remarks
Reinitialising the engine is necessary when some changes are made to the engine settings that do not take effect until the root console is closed and initialised anew.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","engineClass","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","tutorial"])'>>
!Description
Initiates the main game loop, effectively running the application.
!Construction
<code C++>
int run (
);
</code>
!Return value
{{{int}}}: 0 on successful exit or other number on abnormal termination. In case a nonzero value is returned, the error log will probably contain the description of what caused the error.
!Access level
{{{public}}}
!Remarks
The engine needs to be run to initiate the application's main loop. The engine needs to be instantiated and successfully initialised before it can be run.
!Example
<code c++>
UmbraEngine engine;
engine.registerModule (new MyModule());
engine.activateModule(0);
if (engine.initialise()) {
return engine.run();
}
else
return 1;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engine","tutorial"])'>>
!Description
Sets the keyboard mode (the way the engine reacts to keyboard events).
!Construction
<code C++>
void setKeyboardMode (
UmbraKeyboardMode mode
);
</code>
!Parametres
*{{{UmbraKeyboardMode mode}}}: keyboard mode, as defined en the UmbraKeyboardMode enum.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["keyboard","tutorial"])'>>
!Description
Pauses or unpauses the engine.
!Construction
<code C++>
void setPause (
bool pause
);
</code>
!Parametres
*{{{bool pause}}}: {{{true}}} will pause the engine, while {{{false}}} will unpause it.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","tutorial"])'>>
!Description
Gets the height of the root console.
!Construction
<code C++>
static void setRootDimensions (
int w,
int h
);
</code>
!Parametres
*{{{int w}}}: root console width in cells
*{{{int h}}}: root console height in cells
!Access level
{{{public}}}
!Remarks
Calling this method will [[reinitialise|UmbraEngine::reinitialise]] the engine.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineSettings","tutorial"])'>>
!Description
Sets the game window title.
!Construction
<code C++>
void setWindowTitle (
const char * title,
...
);
</code>
<code C++>
void setWindowTitle (
std::string title
);
</code>
!Parametres
*{{{const char * title}}}: the string containing the window's title. Uses {{{printf}}}-like formatting.
*{{{std::string title}}}: the string containing the window's title.
!Access level
{{{public}}}
!Remarks
This method should be used prior to calling [[UmbraEngine::initialise]]. Should the title change afterwards, the change will be seen only after calling [[UmbraEngine::reinitialise]].
!Example
<code C++>
UmbraEngine engine;
engine.registerModule(new MyModule());
engine.activateModule(0);
engine.setWindowTitle("My game window title");
if (engine.initialise()) return engine.run();
else return 1;
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["init","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["init","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["init","tutorial"])'>>
<<forEachTiddler where 'tiddler.tags.containsAll(["method","engineClass"])' sortBy 'tiddler.title'>>
!Description
ID numbers for all internal modules available in Umbra.
!Construction
<code C++>
enum UmbraInternalModuleID {
UMBRA_INTERNAL_SPEEDOMETER,
UMBRA_INTERNAL_BSOD,
UMBRA_INTERNAL_MAX
};
</code>
!Values
*{{{UMBRA_INTERNAL_SPEEDOMETER}}}: The Speed-o-meter widget (name : "umbraSpeedometer").
*{{{UMBRA_INTERNAL_BSOD}}}: The error notification widget (name : "umbraBSOD").
*{{{UMBRA_INTERNAL_MAX}}}: Total number of internal modules available.
!Example
<code C++>
UmbraEngine engine;
engine.activateModule(UMBRA_INTERNAL_SPEEDOMETER);
// equivalent to
engine.activateModule("umbraSpeedometer");
</code>
!See also:
:[[UmbraEngine::displayError]]
The ~UmbraKey class serves the sole purpose of recording a key combination in an easy and readable way. It stores the information necessary to represent any simple keystroke combination, such as {{{alt}}} + {{{enter}}}, etc.
Objects of this type are used inside the [[UmbraCallback]].
!Keystroke comparison
~UmbraKey provides a way to compare two keystroke combinations using a single overloaded operator:
<code C++>
UmbraKey k1(TCODK_ENTER,0,true,false,false);
UmbraKey k2(TCODK_F4,0,true,false,false);
UmbraKey k3(TCODK_F4,0,true,false,false);
if (k1 == k2) std::cout << "Same!" << std::endl;
else std::cout << "Different!" << std::endl;
if (k2 == k3) std::cout << "Same!" << std::endl;
else std::cout << "Different!" << std::endl;
</code>
!!!Output:
{{{Different!}}}
{{{Same!}}}
!Description
Constructor for a keystroke object.
!Construction
<code C++>
UmbraKey () {
vk = TCODK_NONE;
c = 0;
alt = false;
ctrl = false;
shift = false;
}
</code>
<code C++>
UmbraKey (TCOD_keycode_t vk, char c, bool alt, bool ctrl, bool shift) {
this->vk = vk;
this->c = c;
this->alt = alt;
this->ctrl = ctrl;
this->shift = shift;
}
</code>
!Parametres
*{{{TCOD_keycode_t vk}}}: a key code, as defined in [[libtcod documentation|http://doryen.eptalys.net/data/libtcod/doc/1.5.0/console/keycode_t.html]]
*{{{char c}}}: a printable character. A value of {{{0}}} means "no character".
*{{{bool alt}}}: {{{true}}} for {{{alt}}} key pressed, {{{false}}} otherwise.
*{{{bool ctrl}}}: {{{true}}} for {{{ctrl}}} key pressed, {{{false}}} otherwise.
*{{{bool shift}}}: {{{true}}} for {{{shift}}} key pressed, {{{false}}} otherwise.
!Example
<code C++>
//alt + enter
UmbraKey key(TCODK_ENTER,0,true,false,false);
</code>
!Description
Stores a keystroke combination.
!Construction
<code C++>
void assign (
TCOD_keycode_t vk,
char c,
bool alt,
bool ctrl,
bool shift
);
</code>
!Parametres
*{{{TCOD_keycode_t vk}}}: a key code, as defined in [[libtcod documentation|http://doryen.eptalys.net/data/libtcod/doc/1.5.0/console/keycode_t.html]]
*{{{char c}}}: a printable character. A value of {{{0}}} means "no character".
*{{{bool alt}}}: {{{true}}} for {{{alt}}} key pressed, {{{false}}} otherwise.
*{{{bool ctrl}}}: {{{true}}} for {{{ctrl}}} key pressed, {{{false}}} otherwise.
*{{{bool shift}}}: {{{true}}} for {{{shift}}} key pressed, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
Should it be necessary to copy the contents of one UmbraKey object to another, it is best to use the {{{=}}} operator.
<code C++>
UmbraKey key1(TCODK_ENTER,0,true,false,false);
UmbraKey key2 = key1;
</code>
!Description
Compares the keystrokes stored in two instances of the UmbraKey class.
!Example
<code C++>
UmbraKey k1(TCODK_ENTER,0,true,false,false);
UmbraKey k2 = k1;
UmbraKey k3(TCODK_F4,0,true,false,false);
printf("k1 == k2? %s\n", k1 == k2 ? "true" : "false");
printf("k2 == k3? %s\n", k2 == k3 ? "true" : "false");
</code>
!!!Output
{{{k1 == k2? true}}}
{{{k2 == k3? false}}}
*[[UmbraKey::assign]]
*[[UmbraKey::UmbraKey]]
!Description
Keyboard modes are the ways in which Umbra reacts to keyboard input.
!Construction
<code C++>
enum UmbraKeyboardMode {
UMBRA_KEYBOARD_WAIT,
UMBRA_KEYBOARD_WAIT_NOFLUSH,
UMBRA_KEYBOARD_RELEASED,
UMBRA_KEYBOARD_PRESSED,
UMBRA_KEYBOARD_PRESSED_RELEASED
};
</code>
!Values
*{{{UMBRA_KEYBOARD_WAIT}}}: Main loop only executes after some keyboard input has been detected (for turn-based games).
*{{{UMBRA_KEYBOARD_WAIT_NOFLUSH}}}: Same as above, but without flushing the keyboard events.
*{{{UMBRA_KEYBOARD_RELEASED}}}: React only to key release events.
*{{{UMBRA_KEYBOARD_PRESSED}}}: React only to key press events.
*{{{UMBRA_KEYBOARD_PRESSED_RELEASED}}}: React to both key press and key release events.
~UmbraLog is a class that does not take part in handling the game itself. As its name implies, it is there to handle error logs and aid in preventing the occurrence of errors. It is essentially a list of all logged messages, which can be used use for debugging.
~UmbraLog has only static methods, most of which as public. They can be called at any point in the program.
Umbra uses this class internally in many situations to notify the developer of any possible mistakes, such as trying to load a module that hasn't been initialised yet. The last logged message can be displayed on {{{stderr}}} (default behaviour), but if Umbra is in debug mode, it will also activate a warning prompt, humorously called the [[BSOD]].
Should a message be logged during the program execution, a log file called {{{log.txt}}} will be created in the program's execution directory for later review. Please remember to read it if there are any issues, as it may contain indications regarding what you can do to repair the issue.
!Methods
<<tiddler UmbraLogMethods>>
!Description
Closes a block in the message log.
!Construction
<code C++>
static int closeBlock (
UmbraLogResult result = UMBRA_LOGRESULT_NONE
);
</code>
!Parametres
*{{{UmbraLogResult result}}} (optional): the result of the block (success, failure or none)
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
Using this method will append an info message the the log.
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
Should the closing block end with a failure, it is wise to add a message before calling closeBlock, specifying what prevented the block from ending successfully.
!Example
<code C++>
UmbraLog::openBlock("Checking file existence.");
if (!TCODSystem::fileExists("myfile.txt") {
UmbraLog::error("File not found");
UmbraLog::closeBlock(UMBRA_LOGRESULT_FAILURE);
}
else {
UmbraLog::info("File found");
UmbraLog::closeBlock(UMBRA_LOGRESULT_SUCCESS);
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Appends an error message to the log.
!Construction
<code C++>
static int error (
const char * str,
...
);
</code>
<code C++>
static int error (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the error message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the error message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "error";
UmbraLog::error("This is an %s message.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Appends a fatal error message to the log.
!Construction
<code C++>
static int fatalError (
const char * str,
...
);
</code>
<code C++>
static int fatalError (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the fatal error message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the fatal error message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "fatal error";
UmbraLog::fatalError("This is a %s message.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Retrieves a message string from the log.
!Construction
<code C++>
static const char * get (
int idx = -1
);
</code>
!Parametres
*{{{int idx}}} (optional): the message index to retrieve
!Return value
{{{const char *}}}: a message string
!Access level
{{{public}}}
!Remarks
Should the parametre be omitted, the last logged message will be retrieved.
The message index is returned by each method that appends a new message to the log.
!Example
<code C++>
std::cout << "The first message reads: " << UmbraLog::get(0) << std::endl;
std::cout << "The last message reads: " << UmbraLog::get() << std::endl;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Appends an info message to the log.
!Construction
<code C++>
static int info (
const char * str,
...
);
</code>
<code C++>
static int info (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the info message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the info message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "info";
UmbraLog::info("This is an %s message.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Appends a notice message to the log.
!Construction
<code C++>
static int notice (
const char * str,
...
);
</code>
<code C++>
static int notice (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the notice message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the notice message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "notice";
UmbraLog::notice("This is a %s message.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Opens a block in the message log.
!Construction
<code C++>
static int openBlock (
const char * str,
...
);
</code>
<code C++>
static int openBlock (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the block opening message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the block opening message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
Using this method will append an info message the the log.
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "opening block";
UmbraLog::openBlock("This is an %s.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Checks how many messages have been placed in the log so far. It can be used to count all messages or only ones that are of a given type.
!Construction
<code C++>
static int size (
);
</code>
<code C++>
static int size (
UmbraLogType type
);
</code>
!Parametres
*{{{UmbraLogType type}}}: message type to count
!Return value
{{{int}}}: number of messages in the log
!Access level
{{{public}}}
!Example
<code C++>
std::cout << "The log contains " << UmbraLog::size()
<< " messages, out of which " << UmbraLog::size(UMBRA_LOGTYPE_ERROR)
<< " are errors." << std::endl;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
!Description
Appends a warning message to the log.
!Construction
<code C++>
static int warning (
const char * str,
...
);
</code>
<code C++>
static int warning (
std::string str
);
</code>
!Parametres
*{{{const char * str}}}: the string contaning the warning message. The string uses {{{printf}}}-like formatting.
*{{{std::string str}}}: the string contaning the warning message.
!Return value
{{{int}}}: message ID number in the log.
!Access level
{{{public}}}
!Remarks
The returned ID number may be used to retrieve the message later using [[UmbraLog::get]].
!Example
<code C++>
const char * s = "warning";
UmbraLog::warning("This is a %s message.",s);
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["error","tutorial"])'>>
*[[UmbraLog::openBlock]]
*[[UmbraLog::info]]
*[[UmbraLog::notice]]
*[[UmbraLog::warning]]
*[[UmbraLog::error]]
*[[UmbraLog::fatalError]]
*[[UmbraLog::closeBlock]]
*[[UmbraLog::size]]
*[[UmbraLog::get]]
!Description
Log block closing results. Used to indicate whether a block is closed because of a successful or unsuccessful execution of a chunk of code.
!Construction
<code C++>
enum UmbraLogResult {
UMBRA_LOGRESULT_FAILURE,
UMBRA_LOGRESULT_SUCCESS,
UMBRA_LOGRESULT_NONE
};
</code>
!Values
*{{{UMBRA_LOGRESULT_FAILURE}}}: Failure: the block is closed because of some error.
*{{{UMBRA_LOGRESULT_SUCCESS}}}: Success: whatever needed to be done in the block, was done successfully.
*{{{UMBRA_LOGRESULT_NONE}}}: None: the block is closed with no or irrelevant end result.
!Description
Log levels. They are used internally while logging messages in the log and when getting the log size with log type filtering.
!Construction
<code C++>
enum UmbraLogType {
UMBRA_LOGTYPE_INFO,
UMBRA_LOGTYPE_NOTICE,
UMBRA_LOGTYPE_WARNING,
UMBRA_LOGTYPE_ERROR,
UMBRA_LOGTYPE_FATAL
};
</code>
!Values
*{{{UMBRA_LOGTYPE_INFO}}}: Information: just an informative message.
*{{{UMBRA_LOGTYPE_NOTICE}}}: A notice: an unexpected situation that does not produce ill effects, but a message is nonetheless appropriate.
*{{{UMBRA_LOGTYPE_WARNING}}}: A warning: a situation that doesn't interrupt the application, but may produce unwanted effects.
*{{{UMBRA_LOGTYPE_ERROR}}}: An error: a situation that donesn't interrupt the application, but is guaranteed to produce unwanted effects.
*{{{UMBRA_LOGTYPE_FATAL}}}: A fatal error: an unrecoverable error that prevents the application from continuing.
UmbraModule is a single "chunk" of the game, with its own renderer and keybindings. It might be the main menu, the character creation screen, the overworld view, the trading screen or whatever might need a separate view. Modules might also be sub-screens like dialogs or widgets (the inventory dialog for example). Finally, modules can be some non-visual game logic one might want to easily enable or disable. In that case, it's only necessary override the {{{update()}}} method and leave an empty {{{render()}}} method.
By default, an activated module will do nothing. It will simply stay there in memory until it's deactivated. Features can be added to it to it by overriding some of its methods:
The [[UmbraModule::initialise]] is called only once: when the module is activated for the first time. All costly resource loading stuff like bitmap loading can be put there.
Modules can be activated by calling one of the [[UmbraEngine::activateModule]] functions. The [[UmbraModule::activate]] is called each time the module is activated. It's possible to reset some internal data in this function.
A module is deactivated when its {{{update()}}} function returns false or when the [[UmbraEngine::deactivateModule]] function is called. The [[UmbraModule::deactivate]] function is called each time the module is deactivated. Module termination code should be placed there.
The main Umbra game loop has 3 phases:
# Control management
# World updating
# World rendering
* During the control management phase, [[UmbraModule::keyboard]] and [[UmbraModule::mouse]] methods are called for each active module.
* During the world updating phase, [[UmbraModule::update]] is called for each active module. In this function, the module can activate or deactivate other modules using [[UmbraEngine::activateModule]] and [[UmbraEngine::deactivateModule]] methods. The updated module can deactivate itself by returning false in this method.
* During the render phase, [[UmbraModule::render]] is called for each active module.
!Methods
<<tiddler UmbraModuleMethods>>
!Description
Checks whether the module is active.
!Construction
<code C++>
bool getActive (
);
</code>
!Return value
{{{bool}}}: {{{true}}} if the module is active, {{{false}}} otherwise.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Retrieves the value of a custom boolean parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
bool getBoolParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{bool}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
bool myParam = UmbraEngine::getInstance()->getModule("myModule")->getBoolParam("aBoolParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Retrieves the value of a custom character parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
int getCharParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{int}}}: the value of the custom parametre
!Access level
{{{public}}}
!Remarks
This method returns an {{{int}}} instead of a {{{char}}} because it allows the use of Unicode characters as well.
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
char myParam = UmbraEngine::getInstance()->getModule("myModule")->getCharParam("aCharParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Retrieves the value of a custom colour parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
TCODColor getColourParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{TCODColor}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
TCODColor myParam = UmbraEngine::getInstance()->getModule("myModule")->getColourParam("aColourParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Retrieves the value of a custom dice parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
TCOD_dice_t getDiceParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{TCOD_dice_t}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
TCOD_dice_t myParam = UmbraEngine::getInstance()->getModule("myModule")->getDiceParam("aDiceParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Provides a pointer to the engine.
!Construction
<code C++>
UmbraEngine * getEngine (
);
</code>
!Return value
{{{UmbraEngine*}}}: a pointer to the engine.
!Access level
{{{protected}}}
!Remarks
This method is included in the {{{UmbraModule}}} to make communication with the game engine easier. Using it enables you to call the engine's methods from within the module (for instance, module activation).
!Example
<code C++>
void MyModule::keyboard (TCOD_ket_t &key) {
if (key.vk == TCODK_ESCAPE) {
getEngine()->deactivateAll();
}
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["engineReference","tutorial"])'>>
!Description
Fetched the ID number of the module's fallback.
!Construction
<code C++>
int getFallback (
);
</code>
!Return value
{{{int}}}: fallback module's ID number. In case there is no fallback, the method will return {{{-1}}}.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","tutorial"])'>>
!Description
Retrieves the value of a custom floating point parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
float getFloatParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{float}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
float myParam = UmbraEngine::getInstance()->getModule("myModule")->getFloatParam("aFloatParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Retrieves the module's ID number.
!Construction
<code C++>
int getID (
);
</code>
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","tutorial"])'>>
!Description
Retrieves the value of a custom integer parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
int getIntParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{int}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
int myParam = UmbraEngine::getInstance()->getModule("myModule")->getIntParam("anIntParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Checks whether the module is paused.
!Construction
<code C++>
bool getPause (
);
</code>
!Return value:
{{{bool}}}: {{{true}}} if the module is paused, {{{false}}} otherwise.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Checks the module's priority.
!Construction
<code C++>
int getPriority (
);
</code>
!Return value
{{{int}}}: The module's priority.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","tutorial"])'>>
!Description
Retrieves the module's current status.
!Construction
<code C++>
UmbraModuleStatus getStatus (
);
</code>
!Return value
{{{UmbraModuleStatus}}}: the module's current status. The values are defined in the [[UmbraModuleStatus]] enum.
!Access level
{{{public}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Retrieves the value of a custom string parametre defined in the [[external configuration file|Defining custom parametres for the modules]]
!Construction
<code C++>
const char * getStringParam (
const char * name
);
</code>
!Parametres
* {{{const char * name}}}: the name of the custom parametre
!Return value
{{{const char *}}}: the value of the custom parametre
!Access level
{{{public}}}
!Example
module.txt:
<code C++>
moduleChain "myChain" {
module "myModule" {
bool aBoolParam = true
char aCharParam = '@'
int anIntParam = 4
float aFloatParam = 3.14
string aStringParam = "aStringValue"
color aColourParam = #00FF00
dice aDiceParam = "3d20"
}
}
</code>
C++ code:
<code C++>
const char * myParam = UmbraEngine::getInstance()->getModule("myModule")->getStringParam("aStringParam");
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["tutorial","moduleConfiguration"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>>
!Description
Handles keyboard input.
!Construction
<code C++>
virtual void keyboard (
TCOD_key_t &key
);
</code>
!Parametres
*{{{TCOD_key_t &key}}}: address of the {{{TCOD_key_t}}} variable, catching keyboard input.
!Access level
{{{public}}}
!Remarks
This method will be called automatically each frame. It shouldn't be called manually, but rather overloaded in each module.
You should put only what's relevant to the given module in this method. Each module may react to different keybindings.
It is a good idea to zero the {{{key}}} variable out in case a relevant keystroke is detected so that modules with a lower priority don't parse the same keystroke again.
!Example:
<code C++>
void MyModule::keyboard (TCOD_key_t &key) {
if (key.vk == TCODK_SPACE) {
doSomething();
key.vk = TCODK_NONE;
}
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","tutorial"])'>>
!Description
Handles mouse input.
!Construction
<code C++>
virtual void mouse (
TCOD_mouse_t &ms
);
</code>
!Parametres
*{{{TCOD_mouse_t &ms}}}: address of the {{{TCOD_mouse_t}}} variable that collects mouse events.
!Access level
{{{public}}}
!Remarks
This method will be called automatically each frame. It shouldn't be called manually, but rather overloaded in each module that requires mouse input.
It is a good idea to zero the {{{ms}}} variable out in case some relevant input (usually a click in a given area) is parsed, otherwise modules with a lower priority will try to parse the same input.
!Example:
<code C++>
void MyModule::mouse (TCOD_mouse_t &ms) {
if (ms.lbutton) {
doSomething();
ms.lbutton = 0;
}
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["input","tutorial"])'>>
!Description
Custom code executed upon module activation.
!Construction
<code C++>
virtual void onActivate (
);
</code>
!Access level
{{{protected}}}
!Remarks
This method will be called automatically whenever the module containing it is activated. It cannot be called manually.
It will typically contain code such as the activation of other modules (for instance, main game view may internally activate a message log).
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","tutorial"])'>>
!Description
Custom code executed upon module deactivation.
!Construction
<code C++>
virtual void onDeactivate (
);
</code>
!Access level
{{{protected}}}
!Remarks
This method will be called automatically whenever the module containing it is deactivated. It cannot be called manually.
It will typically contain code such as the deactivation of other modules (for instance, main game view may internally deactivate a message log).
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","tutorial"])'>>
!Description
Custom code executed at module initialisation.
!Construction
<code C++>
virtual void onInitialise (
);
</code>
!Access level
{{{protected}}}
!Remarks
This method will be called automatically when the module is activated for the first time. It shall be called once and only once.
It may contain any code, typically some resource allocation which cannot be done in the module's constructor.
Should memory be allocated in this method, it is wise to deallocate it in the destructor.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","tutorial"])'>>
!Description
Custom code executed upon module pausing.
!Construction
<code C++>
virtual void onPause (
);
</code>
!Access level
{{{protected}}}
!Remarks
This method will be called automatically whenever the module containing it is paused. It cannot be called manually.
It may contain any code, for instance, pausing the background music.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","tutorial"])'>>
!Description
Custom code executed upon module unpausing.
!Construction
<code C++>
virtual void onResume (
void
);
</code>
!Access level
{{{protected}}}
!Remarks
This method will be called automatically whenever the module containing it is unpaused. It cannot be called manually.
It may contain any code, for instance, unpausing the background music.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["autoCalling","tutorial"])'>>
!Description
Handles module rendering on the screen.
!Construction
<code C++>
virtual void render (
);
</code>
!Access level
{{{public}}}
!Remarks
This method will be called automatically each frame. It shouldn't be called manually, but rather overloaded in each module.
!Description
Activates or deactivates the module.
!Construction
<code C++>
void setActive (
bool active
);
</code>
!Parametres
*{{{bool active}}}: a value of {{{true}}} tells the method to activate the module. A value of {{{false}}} deactivates the module.
!Access level
{{{public}}}
!Remarks
Note that when a module is activated or deactivated, [[UmbraModule::activate]] or [[UmbraModule::deactivate]], respectively, are called in the following iteration of the main application loop.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleStatus","tutorial"])'>>
!Description
Sets the fallback module (the module that will be activated when the current one is deactivated).
!Construction
<code C++>
void setFallback (
int fback
);
</code>
<code C++>
void setFallback (
const char * moduleName
);
</code>
!Parametres
*{{{int fback}}}: the ID number of the fallback module.
*{{{const char * moduleName}}}: the name of the fallback module.
!Access level
{{{protected}}}
!Remarks
When the module is deactivated, the module specified as the argument to [[UmbraModule::setFallback]] will be activated.
The ID number of the fallback module is the number returned by [[UmbraEngine::registerModule]]. It can also be retrieved with [[UmbraEngine::getModuleId]] or [[UmbraModule::getID]].
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["fallback","tutorial"])'>>
!Description
Sets the name of a module for easy referencing.
!Construction
<code C++>
void setName (
const char * name
);
</code>
!Parametres
*{{{const char * name}}}: module mane to be assigned.
!Access level
{{{protected}}}
!Remarks
This method should be used only when module configuration is not [[externalised|Defining module chains in an external file]] using a [[module factory|Using a module factory]], as in such a case names are assigned automatically.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleReference","tutorial"])'>>
!Description
Pauses or unpauses the module.
!Construction
<code C++>
void setPause (
bool paused
);
</code>
!Parametres
{{{bool paused}}}: {{{true}}} pauses the module, {{{false}}} unpauses it.
!Access level
{{{public}}}
!Remarks
Note that when a module is paused or unpaused, [[UmbraModule::pause]] or [[UmbraModule::resume]], respectively, are called in the following iteration of the main application loop.
!Description
Sets the module's priority.
!Construction
<code C++>
void setPriority (
int priority
);
</code>
!Parametres
*{{{int priority}}}: the module's priority.
!Access level
{{{protected}}}
!Remarks
This method is best used in the module's constructor.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["priority","tutorial"])'>>
!Description
Sets a timeout on the module.
!Construction
<code C++>
void setTimeout (
uint32 val
);
</code>
!Parametres
*{{{uint32 val}}}: the time the module should remain active before timing out, in milliseconds.
!Access level
{{{protected}}}
!Remarks
All modules and widgets start out without a timeout; it needs to be set up, preferably in the module's constructor.
In case a timeout has been set and it needs to be removed, it should be set to 0 milliseconds.
Any changes made to the timeout will be reflected only when the module is activated.
Once the timeout is set, the engine takes care of it, thus it requires no further attention.
!Example
<code C++>
MyModule::MyModule {
//set the timeout to 5 seconds
setTimeout(5000);
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["timeout","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["timeout","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["timeout","tutorial"])'>>
!Description
Updates the module's internal logic.
!Construction
<code C++>
virtual bool update (
);
</code>
!Return value
{{{bool}}}: the method is expected to return {{{true}}} if the module should remain active in the next main loop iteration, {{{false}}} if it should be deactivated.
!Access level
{{{public}}}
!Remarks
:This method will be called automatically each frame. It shouldn't be called manually, but rather overloaded in each module.
:The developer is expected to adhere to the rules concerning the return value. Making the method return a value of {{{false}}} will deactivate the module.
Create a class that inherits UmbraModuleFactory to allow automatic registration of your modules. There's a single method to implement: [[UmbraModuleFactory::createModule]].
<code C++>
class UmbraModuleFactory {
public:
virtual UmbraModule *createModule(const char *name) = 0;
};
</code>
Example :
<code C++>
class ModuleFactory : public UmbraModuleFactory {
public :
UmbraModule * createModule(const char *name) {
if ( strcmp(name,"matrix") == 0 ) return new Matrix();
else if ( strcmp(name,"demo") == 0 ) return new Demo();
else if ( strcmp(name,"rabbit") == 0 ) return new RabbitWidget();
else if ( strcmp(name,"panel") == 0 ) return new Panel();
else if ( strcmp(name,"credits") == 0 ) return new Credits();
else {
UmbraError::add(UMBRA_ERRORLEVEL_ERROR,"ModuleFactory::createModule: encountered an unknown module name: %s.",name);
return NULL;
}
}
};
</code>
!Description
Creates different types of modules.
!Construction
<code C++>
UmbraModule * UmbraModuleFactory::createModule (
const char * name
) = 0;
</code>
!Parametres
*{{{const char * name}}}: the name of the module retrieved from the module configuration file
!Return
{{{UmbraModule *}}}: a pointer to an instance of a module of a given type.
!Remarks
This method is pure virtual, thus it's mandatory to override it.
It is used to create different modules based on the name retrieved from the module configuration file.
It is called automatically when [[UmbraEngine::loadModuleConfiguration]] is called with a pointer to the [[UmbraModuleFactory]] instance is passed as its parametre.
!Example:
<code C++>
UmbraModule * MyModuleFactory::createModule(const char *name) {
if ( strcmp(name,"matrix") == 0 ) return new Matrix();
else if ( strcmp(name,"demo") == 0 ) return new Demo();
else if ( strcmp(name,"rabbit") == 0 ) return new RabbitWidget();
else if ( strcmp(name,"panel") == 0 ) return new Panel();
else if ( strcmp(name,"credits") == 0 ) return new Credits();
else {
UmbraError::add(UMBRA_ERRORLEVEL_ERROR,"ModuleFactory::createModule: encountered an unknown module name: %s.",name);
return NULL;
}
}
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["moduleConfiguration","tutorial"])'>>
*[[UmbraModuleFactory::createModule]]
<<forEachTiddler where 'tiddler.tags.containsAll(["method","moduleClass"])' sortBy 'tiddler.title'>>
!Description
Identifiers for various module states.
!Construction
<code C++>
enum UmbraModuleStatus {
UMBRA_UNINITIALISED,
UMBRA_INACTIVE,
UMBRA_ACTIVE,
UMBRA_PAUSED
};
</code>
!Values
*{{{UMBRA_UNINITIALISED}}}: Modules that haven't yet been initialised will have this state.
*{{{UMBRA_INACTIVE}}}: After initialisation, this status is applied to the modules. It's also applied when a module is deactivated.
*{{{UMBRA_ACTIVE}}}: When a module is activated, it will have this state.
*{{{UMBRA_PAUSED}}}: An active module, if paused, will be marked with this status.
The class UmbraPoint is one of the additions to the [[UmbraWidget]] that differentiate it from an [[UmbraModule]]. As the name implies, it defines the behaviour of a single point (or, more accurately, a single cell on the console).
The UmbraPoint is used within every [[UmbraWidget]] to define the behaviour of 2 points: the minimise button and the close button. The method [[UmbraWidget::mouse]], which is supposed to be called in each iteration of the main application loop (see below), will automatically register mouse hover and mouse down events, which may then be used to trigger events such as a rollover, etc.
!Constructor
In order to create a point, one of two constructors may be used. The first one takes no arguments at all:
<code C++>
UmbraPoint p;
</code>
This will create a point at the position {{{[0,0]}}} of the parent widget. The point's position can be then changed using the [[UmbraPoint::set]] method.
The second constructor addresses the necessity of setting a point's position within a widget and accepts the coordinates as parametres:
<code C++>
UmbraPoint p(10,1);
</code>
The mouse events are set to false initially.
!Mouse events in a widget
A widget declaration should also contain the mouse parser method declaration:
<code C++>
class MyWidget: public UmbraWidget {
...
void mouse(TCOD_mouse_t &ms);
...
};
</code>
The problem is that the developer shouldn't be forced to think about UmbraPoint and [[UmbraRect]] mouse events updating in each widget separately. That's why [[UmbraWidget]] overrides this method as well and takes care of the mouse events for [[UmbraPoint]]s and [[UmbraRect]]s present par default in every widget.
Unfortunately, this method needs to be called manually. This is done by calling [[UmbraWidget::mouse]] at the beginning of the mouse parsing method:
<code C++>
void MyWidget::mouse(TCOD_mouse_t &ms) {
UmbraWidget::mouse(ms);
...
}
</code>
This ensures the possibility to write custom mouse code (for instance, when defining additional elements that should react to the mouse) while at the same time retaining the functionality of the default widget elements. For more information, please refer to [[UmbraWidget::mouse]].
!Setting and getting mouse events
The UmbraPoint has two boolean members that are used to indicate the mouse status relative to the point: {{{mouseHover}}} and {{{mouseDown}}}. They are public members, so they can be set and retrieved directly:
<code C++>
UmbraPoint p;
... //some code here
p.mouseHover = false;
if (p.mouseDown) doSomething();
</code>
The member function [[UmbraPoint::is]] is used for detecting mouse hover - please refer to this method description for additional information.
!Methods
<<tiddler UmbraPointMethods>>
!See also
*[[UmbraRect]]
*[[UmbraWidget]]
*[[UmbraWidget::mouse]]
!Description
Constructor for a point object.
!Construction
<code C++>
UmbraPoint () {
x = 0;
y = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraPoint (int x, int y) {
this->x = x;
this->y = y;
mouseHover = false;
}
</code>
!Parametres
*{{{int x}}}: the X coordinate of a point
*{{{int y}}}: the Y coordinate of a point
!Example
<code C++>
UmbraPoint p1;
UmbraPoint p2(10,20);
</code>
!Description
Checks whether a given set of coordinates match those of an UmbraPoint object.
!Construction
<code C++>
bool is (
int px,
int py
);
</code>
!Parametres
*{{{int px}}}: the X coordinate to be checked
*{{{int py}}}: the Y coordinate to be checked
!Return value
{{{bool}}}: {{{true}}} if the coordinates match those of the UmbraPoint object, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
This method is typically used to check whether the mouse cursor is hovering over a given point.
Should it be necessary to check the coordinates of two UmbraPoint instances against one another, [[UmbraPoint::operator ==]] provides that functionality.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","tutorial"])'>>
!Description
Compares the coordinates of two UmbraPoint instances.
!Remarks
It only takes the coordinates into consideration. Mouse events are ignored.
!Example
<code C++>
UmbraPoint p1(10,20), p2(10,20), p3(11,21);
printf("p1 == p2? %s\n", p1 == p2 ? "true" : "false");
printf("p1 == p3? %s\n", p1 == p3 ? "true" : "false");
</code>
!!!Output
{{{p1 == p2? true}}}
{{{p1 == p3? false}}}
!Description
Sets the coordinates of an UmbraPoint object.
!Construction
<code C++>
void set (
int x,
int y
);
</code>
!Parametres
*{{{int x}}}: the X coordinate to be assigned
*{{{int y}}}: the Y coordinate to be assigned
!Access level
{{{public}}}
!Remarks
Should it be necessary to copy an UmbraPoint instance, the {{{=}}} operator can be used:
<code C++>
UmbraPoint p1(10,20);
UmbraPoint p2 = p1;
</code>
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["point","tutorial"])'>>
*[[UmbraPoint::is]]
*[[UmbraPoint::set]]
*[[UmbraPoint::UmbraPoint]]
UmbraRect is one of the additions to UmbraWidget, along with UmbraPoint. It defines a rectangle as well as interactions with the mouse cursor and an UmbraPoint. Every UmbraWidget includes two UmbraRect objects: the [[widget area|Widget area]] and the [[drag zone|Drag zone]].
!Constructor
UmbraRect takes four arguments in the constructor: the [x,y] coordinates of the top left corner, the width and the height. It can be instantiated like this:
<code C++>
UmbraRect rectangle(0,0,UmbraEngine::getInstance->getRootWidth(),UmbraEngine::getInstance->getRootHeight());
</code>
The above code will, of course, create a rectangle that occupies the entire root console's area.
Calling the constructor with no arguments will create a rectangle object as well, but all values default to 0 and thus require to be set manually, for instance using [[UmbraRect::set]].
!Mouse events
In case of the rectangles present in an UmbraWidget par default, the mouse events are interpreted automatically, provided the widget's [[UmbraWidget::mouse]] is overridden correctly. For other uses, it is possible to check whether the mouse cursor is within the rectangle using [[UmbraRect::isInside]]. The generalised code may look like this:
<code C++>
SomeClass::mouse(TCOD_mouse_t &ms) {
if (rectangle.isInside(ms.cx,ms,cy) {
rectangle.mouseHover = true;
if (ms.lbutton) rectangle.mouseDown = true;
else rectangle.mouseDown = false;
}
else rectangle.mouseHover = rectangle.mouseDown = false;
}
</code>
Using code that's functionally equivalent to the example above makes it possible to use the {{{mouseHover}}} and {{{mouseDown}}} public members of the UmbraRect class:
<code C++>
if (rectangle.mouseHover) doSomething();
if (rectangle.mouseDown) doSomethingMore();
</code>
!Methods
<<tiddler UmbraRectMethods>>
!See also
*[[UmbraPoint]]
*[[UmbraWidget]]
*[[UmbraWidget::mouse]]
!Description
Constructor for a rectangle object.
!Construction
<code C++>
UmbraRect () {
x = 0;
y = 0;
w = 0;
h = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraRect (int x, int y) {
this->x = x;
this->y = y;
w = 0;
h = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraRect (const UmbraPoint &p) {
x = p.x;
y = p.y;
w = 0;
h = 0;
mouseHover = false;
}
</code>
<code C++>
UmbraRect (int x, int y, int w, int h) {
this->x = x;
this->y = y;
this->w = w;
this->h = h;
mouseHover = false;
}
</code>
<code C++>
UmbraRect (const UmbraPoint &p, int w, int h) {
x = p.x;
y = p.y;
this->w = w;
this->h = h;
mouseHover = false;
}
</code>
!Parametres
*{{{int x}}}: the X coordinate of the rectangle's top left corner
*{{{int y}}}: the Y coordinate of the rectangle's top left corner
*{{{int w}}}: the rectangle's width
*{{{int h}}}: the rectangle's height
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the rectangle's top left corner's coordinates
!Example
<code C++>
UmbraRect r1;
UmbraRect r2(10,20,5,10);
</code>
!Description
Checks whether a given set of coordinates or UmbraPoint instance are within a rectangle.
!Construction
<code C++>
bool contains (
int px,
int py
);
</code>
<code C++>
bool contains (
const UmbraPoint &p
);
</code>
!Parametres
*{{{int px}}}: the X coordinate to be checked
*{{{int py}}}: the Y coordinate to be checked
*{{{const UmbraPoint &p}}}: an instance of UmbraPoint to be checked
!Return value
{{{bool}}}: {{{true}}} if the coordinates are within the rectangle, {{{false}}} otherwise.
!Access level
{{{public}}}
!Remarks
This method is typically used to check whether the mouse cursor is hovering over a given rectangle.
!Example
<code C++>
UmbraPoint p1(10,10), p2(20,20);
UmbraRect r(5,5,10,10);
printf("%s, %s, %s, %s\n",
r.contains(p1) ? "true" : "false",
r.contains(p2) ? "true" : "false",
r.contains(12,12) ? "true" : "false",
r.contains(18,18) ? "true" : "false"
);
</code>
!!!Output
{{{true, false, true, false}}}
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","tutorial"])'>>
!Description
Sets the top left corner's coordinates, as well as the width and the height of a rectangle.
!Construction
<code C++>
void set (
int x,
int y,
int w,
int h
);
</code>
<code C++>
void set (
const UmbraPoint &p,
int w,
int h
);
</code>
!Parametres
*{{{int x}}}: the X coordinate of the rectangle's top left corner
*{{{int y}}}: the Y coordinate of the rectangle's top left corner
*{{{int w}}}: the rectangle's width
*{{{int h}}}: the rectangle's height
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the rectangle's top left corner's coordinates
!Access level
{{{public}}}
!Remarks
Should it be necessary to only resize a rectangle, it's more convenient to use [[UmbraRect::setSize]].
In case the rectangle only needs to be moved to a different position, without resizing it, it's best to use [[UmbraRect::setPos]].
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","tutorial"])'>>
!Description
Moves the rectangle to a different position.
!Construction
<code C++>
void setPos (
int x,
int y
);
</code>
<code C++>
void setPos (
const UmbraPoint &p
);
</code>
!Parametres
*{{{int x}}}: the X coordinate of the rectangle's top left corner
*{{{int y}}}: the Y coordinate of the rectangle's top left corner
*{{{const UmbraPoint &p}}}: the point whose coordinates are to become the rectangle's top left corner's coordinates
!Access level
{{{public}}}
!Remarks
This method only changes the rectangle's position (or, more precisely, its top left corner's position), but does not resize it.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","tutorial"])'>>
!Description
Resizes a rectangle.
!Construction
<code C++>
void setSize (
int w,
int h
);
</code>
!Parametres
*{{{int w}}}: the rectangle's new width
*{{{int h}}}: the rectangle's new height
!Access level
{{{public}}}
!Remarks
This method only resizes a rectangle, leaving its top left corner at the same position it was at before.
!See also
<<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","enum"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","method"])'>><<forEachTiddler where 'tiddler != context.inTiddler && tiddler.tags.containsAll(["rect","tutorial"])'>>
*[[UmbraRect::contains]]
*[[UmbraRect::set]]
*[[UmbraRect::setPos]]
*[[UmbraRect::setSize]]
*[[UmbraRect::UmbraRect]]
!Description
Flags that control which internal callbacks should be registered during engine initialisation.
!Construction
<code C++>
enum UmbraRegisterCallbackFlag {
UMBRA_REGISTER_NONE = 0x00000000,
UMBRA_REGISTER_DEFAULT = 0x00000001,
UMBRA_REGISTER_ADDITIONAL = 0x00000010,
UMBRA_REGISTER_ALL = 0x11111111
};
</code>
!Values
*{{{UMBRA_REGISTER_NONE}}}: Register no callbacks.
*{{{UMBRA_REGISTER_DEFAULT}}}: Register only the default callbacks: quit, save screenshot, font switcher, pause and toggle fullscreen. This is the default flag in [[UmbraEngine]] constructor.
*{{{UMBRA_REGISTER_ADDITIONAL}}}: Register additional callback: Speed-o-meter.
*{{{UMBRA_REGISTER_ALL}}}: Register all internal callbacks.
!Example
<code C++>
UmbraEngine engine = UmbraEngine("data/cfg/umbra.txt",UMBRA_REGISTER_ALL);
</code>
*[[UmbraWidget::mouse]]
*[[UmbraWidget::setDragZone]]
#Basics
##[[Starting a project using Umbra]]
##[[Umbra Hello World]]
##[[Configuration variables]]
#Engine
##[[The engine]]
##[[Referencing the engine]]
##[[Engine instantiation versus initialisation]]
##Things to do before initialising the engine
###[[Registering modules]]
###[[Registering fonts]]
###[[Font autodetection]]
###[[Activating modules]]
#Modules and widgets
##[[What is a module]]
##[[What is a widget]]
##[[Creating a module]]
##[[Module ID]]
##[[Naming modules]]
##[[Fallbacks]]
##[[Timeouts]]
##External module configuration
###[[Defining module chains in an external file]]
###[[Using a module factory]]
###[[Defining custom parametres for the modules]]
##[[Creating a widget]]
##Internal modules
###[[Speed-o-meter]]
###[[BSOD]]
###[[Credits]]
##[[Accessing modules and widgets]]
##[[Initialisation versus activation]]
##[[Module priority]]
##Input handling
###[[Keyboard input]]
###[[Mouse input in modules]]
###[[Mouse input in widgets]]
####[[Minimise and close buttons]]
####[[Widget area]]
####[[Drag zone]]
#Callbacks
##[[What are callbacks]]
##[[Creating a callback]]
##[[Using a callback]]
##[[Internal callbacks]]
#Errors
##[[Error log]]
##[[Reporting an error]]
##[[Displaying an error]]
Once a callback is [[created|Creating a callback]], starting to use it is extremely easy. The callback only needs to be registered in the engine:
<code C++>
UmbraEngine::getInstance()->registerCallback(new MyCallback());
</code>
Starting from this point, the callback is active and listens to the incoming keystrokes.
If there are several module chains, registering all the modules while only a few of them are actual used is a waste of resources. In that case, it's better to provide an [[UmbraModuleFactory]] so that only the modules used by the selected chain are registered inside the engine. The module factory provides a single method returning a pointer to an [[UmbraModule]], based on a retrieved name. When the factory is provided as a parametre for the [[UmbraEngine::loadModuleConfiguration]] method, there exists no need to register any modules in the code explicitly. They'll be automatically registered during the file parsing.
Exemple of a simple factory (from Umbra's demo):
<code C++>
class ModuleFactory: public UmbraModuleFactory {
public:
UmbraModule * createModule(const char *name) {
if ( strcmp(name,"matrix") == 0 ) return new Matrix();
else if ( strcmp(name,"demo") == 0 ) return new Demo();
else if ( strcmp(name,"rabbit") == 0 ) return new RabbitWidget();
else if ( strcmp(name,"panel") == 0 ) return new Panel();
else if ( strcmp(name,"credits") == 0 ) return new Credits();
else {
UmbraError::add(UMBRA_ERRORLEVEL_ERROR,"ModuleFactory::createModule: encountered an unknown module name: %s.",name);
return NULL;
}
}
};
</code>
How to use it :
<code C++>
engine.loadModuleConfiguration("data/cfg/module.txt", new ModuleFactory());
</code>
Callbacks are basically global keybindings. The engine provides a listener that will listen to all the registered callbacks and trigger a custom action for each callback in case the listener determines its keybinding has been used.
In order to use a callback, an object of type [[UmbraCallback]] will need to be [[created|Creating a callback]] and [[registered|Using a callback]] in the engine.
Please check the list of [[predefined callbacks|Callbacks]] to see whether the keybinding you would like to create hasn't been created already.
From the general point of view, a module is a "chunk" of an application that can be clearly separated from the others. For instance, a single screen, such as a menu or a credits screen, will typically be a module. The developer should understand a module as ''a class derived from [[UmbraModule]]'', ie, one that inherits [[UmbraModule]].
Each module has its own set of typical methods that the engine can call, the most important ones being:
#[[UmbraModule::update]]
#[[UmbraModule::render]]
#[[UmbraModule::keyboard]]
#[[UmbraModule::mouse]]
The first of the four is used to update the module's internal logic. For instance, a main map view in a real time roguelike game will update the mobs' position on the map, adjust their reactions towards the player character, calculate their attacks and so on.
The rendering method will represent the above data graphically, ie, will print the map view on the screen, with the mob positions updated, etc.
The next two methods take care of player input. Mouse status and pressed keys will be parsed and correct actions will be taken if a specified input is encountered.
All of the above are absolutely independent from other modules. Modules can be rendered on top of each other, so there can be multiple active modules at all times. For instance, one might prefer to make the message display a separate module, while keeping the map in a different one.
There's also a special kind of module, called widget, that offers extra interactivity. See [[What is a widget]] for more details.
A widget is, essentially, a module. It inherits the [[UmbraModule]] class, so it can be updated and rendered in the exact same fashion a standard module would. The difference is that a widget offers additional interactivity options.
Widgets include special areas that can be defined to make the widget become interactive. Per default, a widget has a drag zone, a close button and a minimise button. If they are given legal values, they can be shown in the widget, and also react to mouse movement and clicks.
It is not recommended to use widgets in games that only use keyboard control, since they are slower than normal modules and will be able to offer the same functionality.
There should be an internal module that displays the credits for Umbra, pretty much like libtcod's credits() function.
Currently, the engine clears the console each frame. Not all games need this and the console clearing results in a waste of CPU ticks. This should be made configureable - the whole console clearing might then be substituted with a sinmple {{{if}}} statement, saving some CPU ticks.
When there's at least one module active and another one is being activated, but with a lower priority setting (ie, higher priority number), it is not placed at the end of the active modules list, but rather before the last module, resulting in it not being on the bottom, as expected.
This is caused by the ~UmbraEngine::doActivateModule() method inserting modules only before another element. If the list has been scanned until the end and no suitable place was found, it should simply push the new module at the end of the list.
It is now fixed.
Modules should contain a string with a custom module name (set via the ctor and possibly a setName() method). This would bring the following advantages:
- possibility to activate and deactivate modules by name: engine.activateModule("demo"), engine.deactivateModule("demo"), engine.getModule("name")
- possibility to externalise the module activation sequence (controlled by a text config file).
The [[UmbraEngine::registerFont]] method currently only accepts the filename with full path. It should also accept the filename with no path and look in the default fonts directory.