Thursday 15 January 2015

Incursion PDCurses problems

Switching to the wide character API, the main problem was that the way Incursion encoded it's glyphs was incompatible.  Glyphs are encoded in the form 0xBFCC, where B is the index of the background colour, F is the index of the foreground colour and CC is the 8 bit character code.  F was generally put in place by doing F*256.   To increase the size of character codes, required widespread changes and searching and dealing with a variety of integer values which were involved.

The lower level engine also now uses non-representative values for it's glyph codes.  Mapping to character codes is done in each of the UI layers.

Unfortunately there are some teething problems with getting a working set of character codes.  The red question marks in the following image are GLYPH_FLOOR2, but the UTF-16 code chosen is not available in the default DOS console font.


So, what is the font?  Apparently, it's Terminal.


Here is the Terminal character set.


I knocked up a wizard menu option to dump the glyphs out.


Note that there are characters here which are from a proper UTF-16 font.  Take for example ARROW RIGHT, ARROW LEFT, ARROW UP and ARROW DOWN in the fourth and third to last rows.  Where do they come from?  No idea!  Another question is why does the first glyph for PERSON cause a missing space and the first row to be misaligned?  No idea!

One question I have is whether it is possible to detect missing glyphs and dynamically substitute them.  Curses has an API which allows the character at a given coordinate to be queried.  If I write an character unsupported by the current font, and query it back, do I get the code I wrote or the code that gets displayed?

Sunday 11 January 2015

Incursion PDCurses progress

After implementing use of the Windows file API for the Curses GUI implementation for Incursion, and a few final polishing moves, it now compiles.


Incursion running under PDCurses


Implementing Curses colour support

Getting the colours working, is a series of steps.  The first of which is to call start_color() immediately after initialising the screen (via the initscr() call all Curses programs do).
    bWindow = initscr();
    start_color();
All the desired colours also need to be identified to Curses as an index of RGB triples.  Curses colour values are in the range 0-1000, where programmers will know that rational thinking people store theirs in the range 0-256.  But that's a simple adjustment to make, as long as you know to make it.
    init_color(i, (Colors[i][0] * 1000) / 256, (Colors[i][1] * 1000) / 256, (Colors[i][2] * 1000) / 256)
And now we face the worst eccentricity of Curses, the fact that every printed character embeds its foreground and background colour as a colour index. This means that writing a character to a window coordinate is done in the following way.
    int16 colourpair_index = colour_pair_to_index(fg_index, bg_index);
    c |= COLOR_PAIR(colourpair_index);
    mvwaddch(bScreen, y, x, c);
This means that every foreground and background colour combination employed, needs to be registered. In theory, we could register all 256 possible combinations to begin with, but it turns out registering them on demand works just as well.
int16 cursesTerm::colour_pair_to_index(int16 fg, int16 bg) {
 int16 index;
 // Find out if we have already allocated the colour pair.  If so return it's index.
 for (index = 0; index < colour_pair_index; index++)
  if (colour_pairs[index][0] == bg && colour_pairs[index][1] == fg)
   return index;

 // Oops, we're out of allocatible pairs.
 if (colour_pair_index >= 256) {
  index = 0;
 } else {
  // Otherwise, add the pair in, and return the new index.
  index = colour_pair_index;
  colour_pairs[index][0] = bg;
  colour_pairs[index][1] = fg;
  colour_pair_index += 1;

  init_pair(index, fg, bg);
 }
 return index;
}
There is one remaining piece of the Curses colour puzzle remaining. Some documentation claims that each colour needs to be enabled for every single window which uses it, so in theory as a colour is registered, all windows should get a call to wattron(). But in practice, this does not seem to be needed. If I were to do it, it would be done in colour_pair_to_index() above after the call to init_pair().
    WINDOW *windows[] = { bScreen, bScroll, bCurrent, bSave };
    for (int32 j = 0; j < 4; j++)
        wattron(windows[j], COLOR_PAIR(index));
It may turn out that in the long run, it will be necessary to do this, should someone try and get Incursion's implementation to compile against ncurses on Linux or MacOS.


Incursion Curses support status

At this point, the game generally works.

However, there is a slight problem. Incursion allocates a window for the scrolling buffer, and this is larger than the game window. The player can of course page up and down through scroll text, and as they do so, the displayed range is copied onto the area of the game window where the pageable text is displayed. However, Curses limits the size of windows that can be created via it's newwin() function to the same or smaller number of rows and columns. This means that no pageable text is displayed, which is somewhat problematic for the player. The next step will likely be just allocating an array of chtype elements, to replace the scrolling buffer window. It's not a big deal, and just needs to be done.