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();
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.

No comments:

Post a Comment