eme version 0.2a - seme version 0.2

  1. Introduction
  2. Tutorial: How to draw a map ?
  3. Tutorial: How to create a map format in SEME ?
  4. Tutorial: How to write a plugin ?
  5. Tutorial: How to create a new type of property ?
  6. The user interface
  7. seme
  8. ieme
  9. The API
  10. FAQ
  11. Portability
  12. Known bugs
  13. Reference: Application Program Interface
  14. Reference: emetool
  15. Index

The API



  1. The plugin
  2. Property types
    1. The boolean property
    2. The color property
    3. The number property
    4. The percent property
    5. The string property
    6. The list property
    7. The datafile object property
    8. The composite property
  3. The Wrapper
    1. Creating a map
    2. Accessing a map
  4. User
    1. Configuration
    2. Brush
    3. User data
    4. User menu
    5. User tool
  5. Commands
  6. Creating a new command
  7. Creating a new property type

This is a quick presentation of the application program interface. For a reference, see the Application Program Interface Reference. For a tutorial, see the Plugin Tutorial.


1 - The plugin


A map contains map properties and layers. Map properties are global data for the map, such as a title, light level, probability of encounters, etc. Layers are tiles arrays that should be used for data with a position, such as ground texture, characters, etc.

There are two possible types of layers:

Both map properties and layers can have various types (called property types):

To adapt the program to your needs, you must write a "plugin". (Note: this will be called plugin even if it is not dynamically loaded). You need to provide functions for creating a new map, loading and saving a map. You can also provide functions to be added to the menu (see 4.4 - User menu). In the future, you will also be able to add tools.

The functions to create, load and save a map must have these header:


int plugin_new(void);
int plugin_save(const char *fname, const Map *map); // 'fname' encoding: U_CURRENT
int plugin_load(const char *fname); // 'fname' encoding: U_CURRENT

These functions must be compiled in C++. They must return an error code, 0 meaning no error. Some error codes are defined in the file plugin.h. To access the map, you can use a wrapper defined in wrapper.h. Unless you are doing complex things, you should always use the wrapper (see 3 - The Wrapper).

Several other functions must also be provided:


int plugin_init(int argc, const char **argv); // 'argv' encoding: U_ASCII
void plugin_exit(void);

These two functions allow you to initialize and clean whatever you need to. The argv parameter contains all the command line parameter that are not understood by eme core.


const char *plugin_help(void); // return value encoding: U_ASCII

plugin_help should return an ASCII string describing the command line option(s) used by your code. For example it is used in seme to describe the --format= option.


const char *plugin_about(void); // return value encoding: U_CURRENT

plugin_about should return a string (in the current encoding) to be displayed in the Help->About box.


int plugin_encoding(void);

plugin_encoding must return one of the defines U_ASCII, U_UTF8 or U_UNICODE (defined in Allegro). This function is called really early (i.e. before entering 'main'), so you should assume that nothing is initialized.

You should compile the plugin with the same defines and compilation options than you used for compiling eme. You should link with lib/libathm.a, the Allegro library and the Allegro Theme library. To compile your plugin, you can use emetool (see the emetool reference).

There is a tutorial for writing a plugin and an example.


2 - Property types


Here are described the properties, their look on the GUI and on the map, and their specific functions. All properties have simple Set and Get functions, some also have more complex ones. For more information on the properties structure see 7 - Creating a new property type.

2.1 - The boolean property

This property has two possible values. It can represent a flag. On the GUI, it appears as a button that is sunken (true) or raised (false). On the map, the word "On" is written if the value is true, else the word "Off" is written. The Creator has no function specific to the boolean type. The Property has a function Set(int value), and also Set() and Reset().

To use this property, include cbool.h.

See the Property Boolean reference.

2.2 - The color property

This property represents a color. On the GUI, it appears as an hexagon to choose the color, a rectangle from black to white to choose the intensity and a rectangle drawn in the current color, which when double-clicked popups up a color selection dialog (one slider for each of red, green and blue). On the map, this property appears as a colored rectangle. The Creator has no function specific to the color type. The Property has two functions for setting its value: Set(int red, int green, int blue) and Set(int packed_color) and four functions to query its value: GetRed(), GetGreen(), GetBlue() and Get(). The last one returns a packed color. The color components are values between 0 and 255. To pack and unpack the colors there are four defines:


int Color::red(int packed_color)
int Color::green(int packed_color)
int Color::blue(int packed_color)
int Color::pack(int red, int green, int blue)

To use this property, include ccolor.h.

See the Property Color reference.

2.3 - The number property

This property represents an integer number. On the GUI, it appears as an editable field. On the map, the value is printed (in decimal). Neither the Creator, nor the Property itself has functions specific to the number type.

To use this property, include cnumber.h.

See the Property Number reference.

2.4 - The percent property

This property also represents an integer, but between 0 and a maximum value. On the GUI it appears as a slider. On the map, it appears as a number. There is no specific function.

To use this property, include cpercent.h.

See the Property Percent reference.

2.5 - The string property

This property represents a string, with a maximum number of characters. On the GUI it appears as an editable field. The text is printed on the map. The maximum number of characters must be given to the Creator. The Property itself has no specific function.

To use this property, include cstring.h.

See the Property String reference.

2.6 - The list property

This property represents a list of strings (or names). It can be used to represent an enumerated type. On the GUI it appears as a list (one selectable item). The item name is printed on the map. The Creator must be given a NULL terminated array of strings. The Property itself has no specific function.

To use this property, include clist.h.

See the Property List reference.

2.7 - The datafile object property

This property represent an object from a datafile. On the GUI it appears as a list (one selectable item) of images (if the type is either DAT_BITMAP or DAT_RLE_SPRITE) or names. On the map it appears as an image, a name or a four letter type id. A datafile object can be referenced by either its name (the datafile must not have been stripped) or its index. Nested datafiles are not supported. It is possible to set a drawing callback:


typedef void (*DatafileObject::DrawingCallback) (
  const DatafileObject::Property *tile, BITMAP *bmp,
  int x, int y, int w, int h, float scale, int l, int i, int j
);
SetDrawingCallback(DatafileObject::DrawingCallback *cb);

To use this property, include cdataf.h.

See the Property DatafileObject reference.

2.8 - The composite property

This property is a container of other properties. It can be used to group some related properties. On the GUI, it appears as a button that, when clicked, pops up a box with the contained properties. On the map, by default, all the contained properties are displayed. The usage of this property is described in the Wrapper composite reference. You can also set a callback for drawing the tile on the map:


typedef void (*PCOMPO_DRAW_CB) (
  const Composite::Property *tile, BITMAP *bmp,
  int x, int y, int w, int h, float scale, int l, int i, int j
);
SetDrawingCallback(PCOMPO_DRAW_CB *cb);

To use this property, include ccompo.h.


3 - The Wrapper


The Wrapper class is meant to encapsulate eme internals. It may change in the future, but I'll try to change as few things as possible.

To use the wrapper, include wrapper.h.

See the wrapper reference.

Wrappers can be used for two things:

Note: when using a wrapper to modify a map, the user cannot undo the changes. If you want your user to be able to undo the changes, use a command (see 5 - Commands.

3.1 - Creating a map

To create a new map, use one of these Wrapper constructors:


Wrapper(int width, int height, int num_layers, int num_map_props)
Wrapper(int width, int height)

See the Wrapper Constructor reference.

You will also need to call these functions to specify some geometrical informations:


SetMapShape(MAP_SHAPE shape)
SetTileShape(TILE_SHAPE shape, int width, int height)
SetTileOffset(int dx, int dy, ODD odd)

If you didn't specify the number of layers or map properties in the constructor, you can use:


SetNumLayers(int num_layers)
SetNumProperties(int num_map_props)

Then you have to fill in the layers and map properties.

To set a map property type and value, use:


SetProperty<Type>(int prop_index, const char *name, value) // 'name' encoding: U_CURRENT

To set a layer type, use:


SetLayer<Type>(int layer_index, const char *name, LAYER_TYPE type, default_value) // 'name' encoding: U_CURRENT
SetLayer<Type>(int layer_index, const char *name, LAYER_TYPE type, default_value, parameter)
SetLayer<Type>(int layer_index, const char *name, LAYER_TYPE type, int i, j, w, h, default_value)
SetLayer<Type>(int layer_index, const char *name, LAYER_TYPE type, int i, j, w, h, default_value, parameter)

See the Wrapper SetLayer reference.

To fill a layer with its default value use:


FillLayer(int layer_index)

To set the value of one tile, use:


SetTileValue<Type>(int layer_index, int i, int j, value)

When the map initialization is done, you must call this function to tell the program you're done:


SetMap()

Notes: All tiles of all full layers must have been initialized. You must not call this function if there was an error. You cannot use this wrapper object to access the map after calling this function

3.2 - Accessing a map

To access a map, create the wrapper with one of these constructors:


Wrapper(Map *map)
Wrapper(const Map *map)

See the Wrapper Constructor reference.

The const version should be used when you want only to read the map (for example in the plugin_save function). You should also declare the wrapper as const, like this:


int plugin_save(const char *file, const Map *map) // 'file' encoding: U_CURRENT
{
  const Wrapper the_wrapper(map);
  ...
}

So the compiler will complain if you try to call a function that modifies the map. Methods that modify a map will not work on a const map, even if the wrapper is non-const.

You can get various informations with these functions:


int GetMapWidth() const
int GetMapHeight() const
bool IsIn(int i, int j)
bool TileExists(int layer_index, int i, int j)
int FindLayerIndex(const char *layer_name) // 'layer_name' encoding: U_CURRENT
bool LayerIs<Type>(int layer_index)

You can iterate on the tiles with ordered iterators with the following functions:


int BeginI(int layer_index) const
int EndI(int layer_index) const
int BeginJ(int layer_index) const
int EndJ(int layer_index) const
type GetTileValue<Type>(int layer_index, int i, int j)

See the Wrapper Iterators reference. Note that there are also const version of these iterators.

Example, using a Number layer:


for (int i=wrapper.BeginI(layer); i!=wrapper.EndI(layer); ++i) {
  for (int j=wrapper.BeginJ(layer); j!=wrapper.EndJ(layer); ++j) {
    if (wrapper.TileExists(layer, i, j)) {
      int value = wrapper.GetTileValue<Number>(layer, i, j);
      ...
    }
  }
}

You can iterate on the tiles with out of order iterators, with the following functions:


iterator Begin(int layer_index)
iterator End(int layer_index)
iterator Next(int layer_index)
type GetTileValue(int layer_index, iterator id)
int GetTileI(int layer_index, iterator id)
int GetTileJ(int layer_index, iterator id)

See the Wrapper Iterators reference. Note that there are also const version of these iterators.

Example, using a Number layer:


for (Wrapper::iterator id=wrapper.Begin(layer); id!=wrapper.End(layer); id=wrapper.Next(layer)) {
  int value = wrapper.GetTileValue(layer, id);
}

You can access a map property with:


type GetProperty(int property_index) const

4 - User


4.1 - Configuration

You can enable or disable various parts of the GUI (called GUI features) with the function:


GraphicalUserInterface::AllowFeature(bool yesno, group, features)

To configure the GUI, include gui.h.

See the GUI User configuration reference and the Features reference.

Note that no feature is allowed by default, which means that you have to call this function (typically in plugin_init).

To allow all features (be careful, some features require you to provide a callback):


GUI.AllowFeatures(true, Features::Group::Everything);

To allow "safe" features (i.e. the ones which do not need callback or special handling):


/* Allow every thing */
GUI.AllowFeatures(true, Features::Group::Everything);
/* Disallow "unsafe" features */
GUI.AllowFeatures(false, Features::Group::VariableLayerCount);
GUI.AllowFeatures(false, Features::Group::VariableLayerSize);
GUI.AllowFeatures(false, Features::Group::Brush);

If you do not disallow VariableLayerCount, you must handle maps with any layer count in plugin_save and give a callback to create layers:


void create_layer(Map *map, int layer_index)
{
  ...
}

int plugin_init(int argc, char **argv)
{
  ...
  GUI.SetCreateLayer(create_layer);
  ...
}

See the function new_layer in the file ieme/plugin.cc for an example.

If you do not disallow VariableLayerSize, you must be able to handle layers with various sizes and positions.

If you do not disallow Brush, you must specify callback functions to load and save brushes:


Brush *load_brush(const char *filename) // 'filename' encoding: U_CURRENT
{
  Brush *brush = new Brush();
  ...
  if (error) return NULL;
  else return brush;
}
bool save_brush(const char *filename, const Brush *brush) // 'filename' encoding: U_CURRENT
{
  ...
  if (error) return false;
  else return true;
}

int plugin_init(int argc, const char **argv) // 'argv' encoding: U_ASCII
{
  ...
  GUI.SetLoadBrush(load_brush);
  GUI.SetSaveBrush(save_brush);
  ...
}

See also 4.2 - Brush.

Note that interaction between brush and variable layer count may be unexpected (but it shouldn't crash).

4.2 - Brush

A brush is a part of a map, that can contain any number of layers. All layers in the brush are sparse, even if the equivalent layer in the map was full.

To use brushes, include brush.h.

See the Brush reference and the functions load_brush and save_brush in the file ieme/plugin.cc for an example.

4.3 - User data

If some of the map data cannot be represented by a tiled layer or a map property, you can add them to the map via the user data. The used data type is void*.

You can set and access a user data by using the Wrapper functions:


SetUserdata(void *userdata)
void *GetUserdata() const

You can also specify a function to draw the user data:


void draw_user_data(void *data, BITMAP *bmp, int i, int j, float scale)
{
}

int plugin_init(int argc, char **argv)
{
  ...
  GUI.SetDrawUserdata(draw_user_data);
  ...
}

See the GUI SetDrawUserdata reference.

4.4 - User menu

You can add your own menu entries. You need to include both globals.h and gui.h to be able to access the global variable GUI. One of the GUI method is:


SetUserMenu(int index, const char *name, void (*callback)(Map *map), int hotkey) // 'name' encoding: U_CURRENT

See the GUI SetUserMenu reference, 5 - Commands and the tutorial part 7 - Miscellanea.

4.5 - User tool

This functionality is not yet implemented.


5 - Commands


A command manager is associated to each map. It is a stack containing the last n commands (n being the number of undo levels), and the index of the last executed command. All commands before this index have been executed and all commands after it have been undone. When the user undoes a command, the last executed command is undone and the index is decremented. When the user redo a command, the index is incremented and the new last executed command is re-executed.

This algorithm requires several things to work:

To execute a command, you need to create a command (with new), and ask the map wrapper to execute it. Do not delete the command after execution. Example:


Command *cmd = new CommandCut(map, layer_index, selection);
wrapper.Execute(cmd);

To use a command, include the corresponding header (see the reference documentation).

For an example of using commands, see the Plugin tutorial.

There exists commands to:


6 - Creating a new command


For a simple and commented example, see src/cmdcut.h and src/cmdcut.cc.

The commands are implemented as classes derived from the abstract base class Command, defined in command.h.


#include "command.h"
class MyCommand: public Command {
  /* My command member functions */
};

The two main member functions are Execute and UnExecute. These two functions are called, respectively, when executing or re-executing (i.e. undoing an undo) a command and when undoing it.


class MyCommand: public Command {
  virtual void Execute();
  virtual void UnExecute();
};

Since Execute is called for the first execution and the following ones, setup code should go in the constructor. The destructor has only to do the cleanup.

The constructor can receive anything as its parameters. The setup usually consists of getting a reference to the data worked on (such as the map, layer, etc.) and saving the part of the data that will be modified by Execute. It is as important to get this right than to get right the Execute and UnExecute functions. It is usually better to save a bit more than needed than too little.

The member function IsEmpty() should return true (non-zero) if the command will not, in fact, change the data. It is used, for example, when the user, with the 'Draw' tool, clicks two times on the same tile. Both clicks will trigger the creation of a command, but the second command would do nothing, since the first one has already changed the tile value. When an IsEmpty function returns true, the corresponding command is not inserted in the command stack and is deleted without being executed.


class MyCommand: public Command {
  virtual int IsEmpty() const;
};

The last member function, Merge, is optional. It is currently used by only one command. It should attempt to merge next_command with the command it belongs to, returning true (non-zero) if it succeeded or false (0) if it failed. If the return value was true, next_command is immediately executed and destroyed, without being inserted in the stack. You most probably do not want to redefine Merge.


class MyCommand: public Command {
  virtual int Merge(Command *next_command);
};

7 - Creating a new property type


You can look at the number property for an example (src/cnumber.h and src/cnumber.cc).

A property type consists of a structure defining two types (the actual C type of the property and the type of an additionnal data) and containing two classes (the property itself and its constructor).


struct MyType {
  typedef ... Base;
  typedef ... Param;
  class Creator;
  class Property;
};

The additionnal data is a data common to all properties in a layer, such as the file name for the DatafileObject property. If you don't know if you need an additional data, you probably don't need it: define it to void.

The structure can then be used as a type for the Wrapper template functions, for example:


class Wrapper {
  template <typename Type> Type::Base GetTileValue(int l, int i, int j);
};
...
int value = wrapper.GetTileValue<MyType>(layer, i, j);

You'll probably need to downcast from BaseProperty to MyType::Property and from BaseCreator to MyType::Creator. You can use the Cast function for this:


MyType::Property *p = Cast(base_property);
const MyType::Property *p = Cast(const_base_property);
MyType::Creator *c = Cast(base_creator);
const MyType::Creator *c = Cast(const_base_creator);

The Property class must derive from BaseProperty, defined in prop.h:


#include "prop.h"
struct MyType {
  class Property: public BaseProperty {
  };
};

The property typically holds a value, the constructor inititialize it, the destructor may do some cleanup. In the simplest case the code will look like this:


struct MyType {
  class Property: public BaseProperty {
  public:
    Property(Base value): BaseProperty(), value_(value) {}
    ~Property() {}
  private:
    Base value_;
  };
};

Several methods must be defined:


struct MyType {
  class Property: public BaseProperty {
  public:
    void Set(Base value);
    Base Get() const;

    Property *Clone() const;
    void CopyFrom(const BaseProperty *p);
    int IsEqualTo(const BaseProperty *p) const;
  };
};

The Set method must set the property value to the given one.

The Get method must return the property value.

The Clone method must return a newly created property with the same type and value as the current one.

The CopyFrom method must copy the value of the parameter to the current property.

The IsEqualTo method must return true (non-zero) if the parameter value is equal to the property's, else false (0).

In the simplest case the code will look like this:


struct MyType {
  class Property: public BaseProperty {
  public:
    void Set(Base value) { value_ = value; }
    Base Get() const { return value_; }

    Property *Clone() const { return new Property(Get()); }
    void CopyFrom(const BaseProperty *p) { Set(Cast<MyType>(p)->Get()); }
    int IsEqualTo(const BaseProperty *p) const { return Get()==Cast<MyType>(p)->Get(); }
  };
};

If the operators = and == are defined for the Base type, and if you don't need complex setup in the constructor (for example, you don't allocate memory), you can use a pre-defined template:


#include "template.h"
struct MyType {
  typedef ... Base; // Define only the base type
  typedef void Param;
  typedef TemplateProperty<Base> Property;
  class Creator;
};

A template property with an additional parameter also exists (you also need operators = and == for the Param type):


#include "template.h"
struct MyType {
  typedef ... Base;  // Define the Base
  typedef ... Param; // and Param types
  typedef TemplateProperty2<Base, Param> Property;
  class Creator;
};

The Creator class must derive from BaseCreator, defined in creator.h:


#include "creator.h"
struct MyType {
  class Creator: public BaseCreator {
  };
};

One of the following constructors must be defined (note that you must use this parameters order):


struct MyType {
  class Creator: public BaseCreator {
  public:
    Creator(StaticString *name, Base value);
    // or
    Creator(StaticString *name, Param param, Base value);
  };
};

If Param was defined as void, define the first constructor.

If you use a Param, define the second constructor.

You'll also need a clone function:


struct MyType {
  class Creator: public BaseCreator {
    // write this function
    Creator(const Creator *other);

    // copy this function as is
    Creator *Clone() const { return new Creator(this); }
  };
};

Several other functions must be defined:


struct MyType {
  class Creator: public BaseCreator {
  public:
    // copy this function as is
    Property *Create(Base value) { return new Property(value); }

    // write these four functions
    virtual void Draw(
      const BaseProperty *p, BITMAP *bmp, int x, int y, int w, int h,
      float scale, int l, int i, int j
    ) const;
    virtual int PrintValue(
      const BaseProperty *p, BITMAP *bmp, int x, int y, int color
    ) const;
    void UpdateEntry(Entry *e, const BaseProperty *p) const;
    void UpdateProperty(BaseProperty *p, const Entry *e) const;
  };
};

The Draw function must display the property p on the bitmap bmp.

The PrintValue function must display on the bitmap bmp a text describing the property p. It must return the number of lines printed.

The UpdateEntry function must copy the value of the property p to the entry e.

The UpdateProperty function must copy the value of the entry e to the property p.

An Entry represents an Allegro DIALOG, you must include entry.h to use it. Use the following function in UpdateEntry to initialize the dialog:


void Entry::SetState(
  const char *name, // 'name' encoding: U_CURRENT
  int (*proc)(int, DIALOG*, int),
  int d1, int d2,
  void *dp, void *dp2, void *dp3,
  int flags
)

The name parameter is the title to display above the dialog. The other parameters are the same than the Allegro DIALOG fields.

In the UpdateProperty function, you can read the dialog values with the following functions:


int Entry::GetD1State() const
int Entry::GetD2State() const
void *Entry::GetDPState() const
void *Entry::GetDP2State() const
void *Entry::GetDP3State() const
int GetFlagsState() const

Example, using d_slider_proc: The object holds the value in d2, the value ranges from 0 to d1.


#include "entry.h"

void MyType::Creator::UpdateEntry(Entry *e, const BaseProperty *p) const
{
  /* Get the layer name */
  const char *name = GetName()->string(); // 'name' encoding: U_CURRENT
  /* Maximum slider value */
  int max = 100;
  /* Cast the property to the actual type */
  const MyType::Property *property = Cast<MyType>(p);
  /* Property value */
  int value = property->Get();
  e->SetState(name, d_slider_proc, max, value, NULL, NULL, NULL, 0);

  /* Could also be written :
     e->SetState(
       GetName()->string(), d_slider_proc, 100, Cast<MyType>(p)->Get(),
       NULL, NULL, NULL, 0
     );
   */
}

void MyType::Creator::UpdateProperty(BaseProperty *p, const Entry *e) const
{
  /* Cast the property to the actual type */
  MyType::Property *property = Cast<MyType>(p);
  /* Get the value from the entry */
  int value = e->GetD2State();
  /* Set the property value */
  property->Set(value);

  /* Could also be written :
     Cast<MyType>(p)->Set(e->GetD2State());
   */
}

Last modified on 21/5/2003