home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / curses / cdraw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  26.8 KB  |  1,185 lines  |  [TEXT/R*ch]

  1. /* Map graphics for the curses interface to Xconq.
  2.    Copyright (C) 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. #include "conq.h"
  11. #include "cconq.h"
  12.  
  13. static int xform PARAMS ((int x, int y, int *sxp, int *syp));
  14. static void draw_divider PARAMS ((void));
  15. static void draw_terrain_row PARAMS ((int x, int y, int len));
  16. static void draw_units PARAMS ((int x, int y));
  17. static void draw_people PARAMS ((int x, int y));
  18. static void draw_legend PARAMS ((int x, int y));
  19. static void draw_unit_details PARAMS ((Unit *unit));
  20. static void describe_cell PARAMS ((int x, int y, int tview, char *filler,
  21.                   char *buf));
  22. static void organize_list_contents PARAMS ((void));
  23. static void add_unit_to_list PARAMS ((Unit *unit));
  24. static void draw_unit_list_entry PARAMS ((int line));
  25. static void draw_type_list_entry PARAMS ((int line));
  26.  
  27. char *dashbuffer;
  28.  
  29. /* When true, draw the terrain character in both character positions
  30.    of a cell; otherwise draw in just the left position and put a blank
  31.    in the right position. */
  32.  
  33. int use_both_chars = TRUE;
  34.  
  35. /* When true, use standout mode to highlite mode lines and such.
  36.    Standout mode increases visual clutter somewhat, so it's not
  37.    obviously good or obviously bad. */
  38.  
  39. int use_standout = FALSE;
  40.  
  41. int drawlinear = TRUE;
  42.  
  43. char linear_char = '#';
  44.  
  45. char bord_char = '\0';
  46.  
  47. char conn_char = '\0';
  48.  
  49. int listtype = 0;
  50.  
  51. int firstvisible = 0;
  52. int lastvisible = 30;
  53. UnitVector *listvector = NULL;
  54. int listnumunits = 0;
  55.  
  56. int first_visible_help_pos;
  57. int last_visible_help_pos;
  58.  
  59. /* Completely redo a screen, making no assumptions about appearance.
  60.    This is a last-gasp measure; most redrawing should be restricted to
  61.    only the directly affected windows.  Also this shouldn't be done
  62.    without the user's permission, since it will blow away impending
  63.    input. */
  64.  
  65. void
  66. redraw()
  67. {
  68.     if (active_display(dside)) {
  69.     switch (mode) {
  70.       case SURVEY:
  71.       case MOVE:
  72.       case PROMPT:
  73.       case PROMPTXY:
  74.         clear();
  75.         draw_divider();
  76.         show_toplines();
  77.         show_game_date();
  78.         show_clock();
  79.         show_side_list();
  80.         show_list();
  81.         show_closeup();
  82.         show_map();
  83.         break;
  84.       case HELP:
  85.       case MORE:
  86.         show_help();
  87.         break;
  88.       default:
  89.         break;
  90.     }
  91.     show_cursor();
  92.     refresh();
  93.     }
  94. }
  95.  
  96. static void
  97. draw_divider()
  98. {
  99.     int i;
  100.  
  101.     for (i = 2; i < LINES; ++i) {
  102.     mvaddstr(i, mw, "|");
  103.     }
  104. }
  105.  
  106. /* Decide whether given location is not too close to edge of screen.
  107.    We do this because it's a pain to move units when half the adjacent
  108.    cells aren't even visible.  This routine effectively places a lower
  109.    limit of 5x5 for the map window. (I think) */
  110.  
  111. int
  112. in_middle(x, y)
  113. int x, y;
  114. {
  115.     int sx, sy;
  116.  
  117.     xform(x, y, &sx, &sy);
  118.     return ((between (3, sx, mw - 3) || !between(2, x, area.width-3))
  119.         && (between (3, sy, mh - 3) || !between(2, y, area.height-3)));
  120. }
  121.  
  122. static int
  123. xform(x, y, sxp, syp)
  124. int x, y, *sxp, *syp;
  125. {
  126.     xform_cell(mvp, x, y, sxp, syp);
  127.     return TRUE;
  128. }
  129.  
  130. void
  131. set_scroll()
  132. {
  133.     int sx, sy, hexadj = hexagon_adjust(mvp);
  134.  
  135.     sx = mvp->sx;  sy = mvp->sy; 
  136.     if (mw < (mvp->totsw - hexadj))
  137.       sx = max(sx, hexadj);
  138.     else
  139.       sx = hexadj;
  140.     if (mh >= mvp->totsh)
  141.       sy = 0;
  142.     set_view_position(mvp, sx, sy);
  143. }
  144.  
  145. void
  146. set_map_viewport()
  147. {
  148.     /* Compute the size of the viewport. */
  149.     vw = min(area.width, mw / 2 + 1);
  150.     vh = min(area.height, mh);
  151.     /* Compute the bottom visible row. */
  152.     vy = (mvp->totsh - mvp->sy) - vh;
  153.     /* Adjust to keep its value from being outside the area. */
  154.     vy = max(0, min(vy, area.height - vh));
  155.     /* Compute the first visible column. */
  156.     vx = mvp->sx / 2 - vy / 2 - 1;
  157.     DGprintf("Set %dx%d viewport at %d,%d\n", vw, vh, vx, vy);
  158. }
  159.  
  160. /* Display a map and all of its paraphernalia. */
  161.  
  162. void
  163. show_map()
  164. {
  165.     int y1, y2, y, x1, x2;
  166.     int halfheight = area.height / 2;
  167.  
  168.     clear_window(mapwin);
  169.     set_map_viewport();
  170.     /* Compute top and bottom rows to be displayed. */
  171.     y1 = min(vy + vh, area.height - 1);
  172.     y2 = vy;
  173.     for (y = y1; y >= y2; --y) {
  174.     /* Adjust the right and left bounds to fill the viewport as
  175.        much as possible, without going too far (the drawing code
  176.        will clip, but clipped drawing is still expensive). */
  177.     x1 = vx - (y - vy) / 2;
  178.     x2 = x1 + vw + 2;
  179.     /* If the area doesn't wrap, then we might have to stop
  180.        drawing before we reach the edge of the viewport. */
  181.     if (!area.xwrap) {
  182.         x1 = max(0, min(x1, area.width - 1));
  183.         x2 = max(0, min(x2, area.width));
  184.         if (x1 + y > area.width + halfheight)
  185.           continue;
  186.         if (x2 + y < halfheight)
  187.           continue;
  188.         if (x2 + y > area.width + halfheight)
  189.           x2 = area.width + halfheight - y;
  190.         if (x1 + y < halfheight)
  191.           x1 = halfheight - y;
  192.     }
  193.     draw_row(x1, y, x2 - x1);
  194.     }
  195.     /* Draw modeline in standout if desired and possible. */
  196.     if (use_standout)
  197.       standout();
  198.     tmpbuf[0] = '\0';
  199.     if (drawunits) {
  200.     strcat(tmpbuf, "units");
  201.     }
  202.     if (drawnames) {
  203.     if (strlen(tmpbuf) > 0)
  204.       strcat(tmpbuf, ",");
  205.     strcat(tmpbuf, "names");
  206.     }
  207.     if (drawpeople) {
  208.     if (strlen(tmpbuf) > 0)
  209.       strcat(tmpbuf, ",");
  210.     strcat(tmpbuf, "people");
  211.     }
  212.     memcpy(spbuf, dashbuffer, mw);
  213.     memcpy(spbuf+2, "Map", 3);
  214.     memcpy(spbuf+7, (mode == SURVEY ? "Survey" : "-Move-"), 6);
  215.     memcpy(spbuf+17, "(", 1);
  216.     memcpy(spbuf+18, tmpbuf, strlen(tmpbuf));
  217.     memcpy(spbuf+18+strlen(tmpbuf), ")", 1);
  218.     spbuf[mw] = '\0';
  219.     /* (should add followaction flag to status line?) */
  220.     draw_text(mapwin, 0, mh, spbuf);
  221.     if (use_standout)
  222.       standend();
  223. }
  224.  
  225. void
  226. draw_row(x0, y0, len)
  227. int x0, y0, len;
  228. {
  229.     int x;
  230.  
  231.     if (drawterrain) {
  232.     draw_terrain_row(x0, y0, len);
  233.     }
  234.     /* Draw sparse things on top of the basic row. */
  235.     for (x = x0; x < x0 + len; ++x) {
  236.     if (!inside_area(x, y0))
  237.       continue;
  238.     /* The relative ordering of these is quite important.  Note that
  239.        each should be prepared to run independently also, since the
  240.        other displays might have been turned off. */
  241.     if (people_sides_defined() && drawpeople) {
  242.         draw_people(x, y0);
  243.     }
  244.     if (drawunits) {
  245.         draw_units(x, y0);
  246.     }
  247.     }
  248.     /* Need a second loop so names are superimposed properly. */
  249.     for (x = x0; x < x0 + len; ++x) {
  250.     if (drawnames) {
  251.         draw_legend(x, y0);
  252.     }
  253.     }
  254. }
  255.  
  256. /* Draw a single row of just terrain. */
  257.  
  258. static void
  259. draw_terrain_row(x0, y0, len)
  260. int x0, y0, len;
  261. {
  262.     char ch, ch2;
  263.     int x, sx, sy, t, t2;
  264.  
  265.     xform(x0, y0, &sx, &sy);
  266.     for (x = x0; x < x0 + len; ++x) {
  267.     ch = ((x % 2 == 0 && y0 % 2 == 0) ? unseen_char_2 : unseen_char_1);
  268.     ch2 = ' ';
  269.     if (terrain_visible(dside, x, y0)) {
  270.         t = terrain_at(x, y0);
  271.         ch = terrchars[t];
  272.         ch2 = (use_both_chars ? ch : ' ');
  273.         /* Just provide a hint at presence of aux terrain. */
  274.         if (drawlinear && any_aux_terrain_defined()) {
  275.         for_all_terrain_types(t2) {
  276.             if (aux_terrain_defined(t2)) {
  277.             if (aux_terrain_at(x, y0, t2)) {
  278.                 ch2 = linear_char;
  279.                 break;
  280.             }
  281.             }
  282.         }
  283.         }
  284.     }
  285.     if (cur_at(mapwin, sx, sy))
  286.       addch(ch);
  287.     if (cur_at(mapwin, sx + 1, sy))
  288.       addch(ch2);
  289.     sx += 2;
  290.     }
  291. }
  292.  
  293. /* Draw a single unit char pair as appropriate. */
  294.  
  295. static void
  296. draw_units(x, y)
  297. int x, y;
  298. {
  299.     int draw = FALSE;
  300.     int xw, sx, sy, uview, u, s;
  301.     Unit *unit;
  302.  
  303.     xw = wrapx(x);    
  304.     if (units_visible(dside, xw, y)) {
  305.     if (in_play(curunit) && curunit->x == xw && curunit->y == y) {
  306.         unit = curunit;
  307.     } else {
  308.         unit = unit_at(xw, y);
  309.     }
  310.     if (unit != NULL) {
  311.         u = unit->type;
  312.         s = side_number(unit->side);
  313.         draw = TRUE;
  314.     }
  315.     } else {
  316.     uview = unit_view(dside, xw, y);
  317.     if (uview != EMPTY) {
  318.         u = vtype(uview);
  319.         s = vside(uview);
  320.         draw = TRUE;
  321.     }
  322.     }
  323.     if (draw) {
  324.     xform(x, y, &sx, &sy);
  325.     if (cur_at(mapwin, sx, sy)) {
  326.         addch(unitchars[u]);
  327.     }
  328.     if (cur_at(mapwin, sx + 1, sy)) {
  329.         if (between(1, s, 9))
  330.           addch(s + '0');
  331.         else if (s >= 10)
  332.           /* This could get weird if s > 36, but not much
  333.          chance of that because MAXSIDES < 31 always. */
  334.           addch(s - 10 + 'A');
  335.     }
  336.     }
  337. }
  338.  
  339. /* Indicate what kind of people are living in the given cell. */
  340.  
  341. static void
  342. draw_people(x, y)
  343. int x, y;
  344. {
  345.     int pop, sx, sy;
  346.  
  347.     if (terrain_visible(dside, x, y)
  348.     && (pop = people_side_at(wrapx(x), y)) != NOBODY) {
  349.     xform(x, y, &sx, &sy);
  350.     if (cur_at(mapwin, sx + 1, sy))
  351.       addch(pop + '0');
  352.     }
  353. }
  354.  
  355. /* Draw any text that should be associated with this cell. */
  356.  
  357. /* (could precompute what the string will lap over and move or truncate str),
  358.    should be deterministic for each mag, so redraw doesn't scramble */
  359.  
  360. /* do geofeatures, label at a cell with nothing else, and declared as the
  361.    feature's "center" */
  362.  
  363. static void
  364. draw_legend(x, y)
  365. int x, y;
  366. {
  367.     int sx, sy, pixlen;
  368.     char legend[100], *str;
  369.     int uview = unit_view(dside, x, y);
  370.     Feature *feature;
  371.     Unit *unit;
  372.  
  373.     if (uview == EMPTY)
  374.       return;
  375.     /* Draw a unit's name or number. */
  376.     /* (This will only do top unit in hex, what about others?) */
  377.     if (drawunits
  378.     && (unit = unit_at(x, y)) != NULL) {
  379.     /* still has a bug if types happen to match - need to use view date
  380.        instead? */
  381.     if (vtype(uview) != unit->type)
  382.       return;
  383.     if (unit->name != NULL) {
  384.         strcpy(legend, unit->name);
  385.     } else {
  386.         /* Turns out we have nothing to make a legend with. */
  387.         /* Note that, unlike some other interfaces, this one
  388.            does not ever display unit numbers, since it makes the
  389.            screen too cluttered to read. */
  390.         return;
  391.     }
  392.     xform(x, y, &sx, &sy);
  393.     draw_text(mapwin, sx + 2, sy, legend);
  394.     } else {
  395.     feature = feature_at(x, y);
  396.     if (feature != NULL) {
  397.         if (feature->size == 1 && (str = feature_name_at(x, y)) != NULL) {
  398.         xform(x, y, &sx, &sy);
  399.         pixlen = strlen(str) / 2;
  400.         draw_text(mapwin, sx - pixlen, sy, str);
  401.         }
  402.     }
  403.     }
  404. }
  405.  
  406. /* (should change text[12] to an array) */
  407.  
  408. void
  409. low_notify(side, str)
  410. Side *side;
  411. char *str;
  412. {
  413.     char *lastblank, extrabuf[BUFSIZE];
  414.  
  415.     if (!active_display(side))
  416.       return;
  417.     if (strlen(str) > toplineswin->w) {
  418.     strcpy(extrabuf, str);
  419.     lastblank = strchr(extrabuf + toplineswin->w - 10, ' '); 
  420.     if (lastblank) {
  421.         strcpy(text2, lastblank + 1);
  422.         *lastblank = '\0';
  423.     }
  424.     strcpy(text1, extrabuf);
  425.     } else {
  426.     strcpy(text1, str);
  427.     }
  428.     DGprintf("%s\n", text1);
  429.     DGprintf("%s\n", text2);
  430.     show_toplines();
  431.     /* Put cursor back to where it was. */
  432.     show_cursor();
  433.     refresh();
  434. }
  435.  
  436. void
  437. show_toplines()
  438. {
  439.     clear_window(toplineswin);
  440.     draw_text(toplineswin, 0, 0, text1);
  441.     draw_text(toplineswin, 0, 1, text2);
  442. }
  443.  
  444. void
  445. clear_toplines()
  446. {
  447.     text1[0] = '\0';
  448.     text2[0] = '\0';
  449.     show_toplines();
  450. }
  451.  
  452. /* Display all the details of the currently-selected unit/cell. */
  453.  
  454. /* (much of this should be made into kernel routines shared by interfaces) */
  455.  
  456. void
  457. show_closeup()
  458. {
  459.     int u, tview, uview;
  460.     char *filler = "Empty ";
  461.     Unit *unit = NULL;
  462.     Side *side2;
  463.  
  464.     clear_window(closeupwin);
  465.     if (inside_area(curx, cury)) {
  466.     if (terrain_visible(dside, curx, cury)) {
  467.         tview = buildtview(terrain_at(curx, cury));
  468.     } else {
  469.         tview = terrain_view(dside, curx, cury);
  470.     }
  471.     if (units_visible(dside, curx, cury)) {
  472.         unit = (in_play(curunit) ? curunit : unit_at(curx, cury));
  473.         if (in_play(unit)) {
  474.         /* If there is a unit there, we can at least see basic info. */
  475.         sprintf(tmpbuf, "%s", unit_handle(dside, unit));
  476.         draw_text(closeupwin, 0, 0, tmpbuf);
  477.         if (unit->side == dside) {
  478.             draw_unit_details(unit);
  479.         }
  480.         filler = "In ";
  481.         }
  482.     } else {
  483.         uview = unit_view(dside, curx, cury);
  484.         if (uview != EMPTY) {
  485.         filler = "In ";
  486.         u = vtype(uview);
  487.         side2 = side_n(vside(uview));
  488.         sprintf(tmpbuf, "%s %s",
  489.             side_adjective(side2), u_type_name(u));
  490.         draw_text(closeupwin, 0, 0, tmpbuf);
  491.         }
  492.     }
  493.     /* Describe the cell here. */
  494.     describe_cell(curx, cury, tview, filler, tmpbuf);
  495.     draw_text(closeupwin, 0, 1, tmpbuf);
  496.     } else {
  497.     sprintf(tmpbuf, "??? Off-area %d,%d ???", curx, cury);
  498.     draw_text(closeupwin, 0, 1, tmpbuf);
  499.     }
  500. }
  501.  
  502. static void
  503. describe_cell(x, y, tview, filler, buf)
  504. int x, y, tview;
  505. char *filler, *buf;
  506. {
  507.     int t;
  508.     char *featname;
  509.  
  510.     if (tview != UNSEEN) {
  511.     t = vterrain(tview);
  512.     /* Now describe terrain and position. */
  513.     sprintf(buf, "%s%s", filler, t_type_name(t));
  514.     featname = feature_name_at(x, y);
  515.     if (featname != NULL) {
  516.         strcat(buf, " ");
  517.         strcat(buf, featname);
  518.     }
  519.     elevation_desc(buf+strlen(buf), x, y);
  520.     /* (should put all weather on own line) */
  521.     temperature_desc(buf+strlen(buf), x, y);
  522.     /* add clouds and winds desc here */
  523.     linear_desc(buf+strlen(buf), x, y);
  524.     } else {
  525.     sprintf(buf, "<unknown>");
  526.     }
  527.     tprintf(buf, " at %d,%d", x, y);
  528. }
  529.  
  530. /* Describe the state of the given unit, in maximal detail. */
  531.  
  532. static void
  533. draw_unit_details(unit)
  534. Unit *unit;
  535. {
  536.     int u = unit->type, u2, m, nums[MAXUTYPES];
  537.     int terr = terrain_at(unit->x, unit->y);
  538.     Unit *occ, *top;
  539.  
  540.     /* Say which unit this is. */
  541.     sprintf(spbuf, "%s", unit_handle(dside, unit));
  542.     /* Describe the "important" parameters like hit points and moves. */
  543.     strcat(spbuf, "  ");
  544.     hp_desc(tmpbuf, unit, TRUE);
  545.     strcat(spbuf, tmpbuf);
  546.     /* (should say something about parts here) */
  547.     strcat(spbuf, "  ");
  548.     acp_desc(tmpbuf, unit, TRUE);
  549.     strcat(spbuf, tmpbuf);
  550.     draw_text(closeupwin, 0, 0, spbuf);
  551.     /* Mention transport and other units stacked here. */
  552.     if (unit->transport != NULL) {
  553.     sprintf(spbuf, "In %s", short_unit_handle(unit->transport));
  554.     } else {
  555.     describe_cell(unit->x, unit->y, buildtview(terr), "In ", spbuf);
  556.     /* make this a "stacked_at" macro? */
  557.     top = unit_at(unit->x, unit->y);
  558.     if (top != NULL && top->nexthere != NULL) {
  559.         strcat(spbuf, ", ");
  560.         for_all_unit_types(u2)
  561.           nums[u2] = 0;
  562.         for_all_stack(unit->x, unit->y, occ)
  563.           ++nums[occ->type];
  564.         --nums[u];  /* don't count ourselves */
  565.         for_all_unit_types(u2) {
  566.         if (nums[u2] > 0) {
  567.             tprintf(spbuf, "%d %1s  ", nums[u2], utype_name_n(u2, 1));
  568.         }
  569.         }
  570.         strcat(spbuf, " here also");
  571.     }
  572.     }
  573.     draw_text(closeupwin, 0, 1, spbuf);
  574.     /* Very briefly list the numbers and types of the occupants. */
  575.     spbuf[0] = '\0';
  576.     if (unit->occupant != NULL) {
  577.     strcpy(spbuf, "Occ ");
  578.     /* (should be occ_desc in nlang.c?) */
  579.     for_all_unit_types(u2)
  580.       nums[u2] = 0;
  581.     for_all_occupants(unit, occ)
  582.       ++nums[occ->type];
  583.     for_all_unit_types(u2) {
  584.         if (nums[u2] > 0) {
  585.         tprintf(spbuf, "%d %1s  ", nums[u2], utype_name_n(u2, 1));
  586.         }
  587.     }
  588.     }
  589.     draw_text(closeupwin, 0, 2, spbuf);
  590.     /* Describe the state of all the supplies. */
  591.     /* (should be supply_desc in kernel?) */
  592.     spbuf[0] = '\0';
  593.     for_all_material_types(m) {
  594.     if (um_storage_x(u, m) > 0) {
  595.         sprintf(tmpbuf, "%s %d/%d  ",
  596.             m_type_name(m), unit->supply[m], um_storage_x(u, m));
  597.         strcat(spbuf, tmpbuf);
  598.     }
  599.     }
  600.     draw_text(closeupwin, 0, 3, spbuf);
  601.     /* Describe the current plans, tasks, etc. */
  602.     /* (needs much improvement) */
  603.     if (unit->plan) {
  604.     int row = 5;
  605.     Task *task;
  606.  
  607.     plan_desc(spbuf, unit);
  608.     draw_text(closeupwin, 0, 4, spbuf);
  609.     for (task = unit->plan->tasks; task != NULL; task = task->next) {
  610.         task_desc(spbuf, task);
  611.         draw_text(closeupwin, 0, row++, spbuf);
  612.     }
  613.     }
  614. }
  615.  
  616. /* Basic routine that displays the list of sides. */
  617.  
  618. void
  619. show_side_list()
  620. {
  621.     char ismoving, progress[20], *dpyname;
  622.     int sy = 0, totacp;
  623.     Side *side2;
  624.     extern int curpriority;
  625.  
  626.     /* Ensure subwin is clear. */
  627.     clear_window(sideswin);
  628.     for_all_sides(side2) {
  629.         ismoving = ' ';
  630.     if ((g_use_side_priority()
  631.          ? (curpriority == side2->priority)
  632.          : (!side2->finishedturn)))
  633.       ismoving = '*';
  634.     if (side2->designer) {
  635.         strcpy(progress, "DESIGN  ");
  636.     } else if (side2->ingame) {
  637.         totacp = side_initacp(side2);
  638.         if (totacp > 0) {
  639.         sprintf(progress, "%3d%%", (100 * side_acp(side2)) / totacp);
  640.         /* We get to see our actual total acp as well. */
  641.         if (dside == side2) {
  642.             tprintf(progress, "/%-3d", totacp);
  643.         } else {
  644.             strcat(progress, "    ");
  645.         }
  646.         } else {
  647.         strcpy(progress, "   --   ");
  648.         }
  649.     } else {
  650.         if (side_lost(side2)) {
  651.         strcpy(progress, " Lost   ");
  652.         } else if (side_won(side2)) {
  653.         strcpy(progress, " Won!   ");
  654.         } else {
  655.         strcpy(progress, " Gone   ");
  656.         }
  657.     }
  658.     dpyname = "";
  659.     if (side2->player->displayname)
  660.       dpyname = side2->player->displayname;
  661.     sprintf(spbuf, "%d%c %s %s (%s)",
  662.         side_number(side2), ismoving, progress,
  663.         side_name(side2), dpyname);
  664.     draw_text(sideswin, 0, sy, spbuf);
  665.     sy += 1;
  666.     }
  667.     /* Draw the modeline. */
  668.     if (use_standout)
  669.       standout();
  670.     memcpy(spbuf, dashbuffer, lw);
  671.     memcpy(spbuf+1, "Sides", 5);
  672.     spbuf[lw] = '\0';
  673.     draw_text(sideswin, 0, sh - 1, spbuf);
  674.     if (use_standout)
  675.       standend();
  676. }
  677.  
  678. /* Display the date. */
  679.  
  680. void
  681. show_game_date()
  682. {
  683.     clear_window(datewin);
  684.     /* First line of the game state. */
  685.     /* (should cache this date string a la Mac version?) */
  686.     draw_text(datewin, 1, 0, absolute_date_string(g_turn()));
  687.     /* (should use second line for something) */
  688. }
  689.  
  690. /* General list display routine. */
  691.  
  692. /* (should track beginning/end of displayed list, draw only visible elts) */
  693. /* (should add scrolling interaction) */
  694.  
  695. static void
  696. organize_list_contents()
  697. {
  698.     Side *side2;
  699.     Unit *unit;
  700.  
  701.     /* Build up the array of units for this list. */
  702.     listnumunits = 0;
  703.     clear_unit_vector(listvector);
  704.     /* We always see our own units. */
  705.     for_all_side_units(dside, unit) {
  706.     add_unit_to_list(unit);
  707.     }
  708.     for_all_sides(side2) {
  709.     if (dside != side2) {
  710.         for_all_side_units(side2, unit) {
  711.         if (side_sees_image(dside, unit)) {
  712.             add_unit_to_list(unit);
  713.         }
  714.         }
  715.     }
  716.     }
  717.     for_all_side_units(indepside, unit) {
  718.     if (side_sees_image(dside, unit)) {
  719.         add_unit_to_list(unit);
  720.     }
  721.     }
  722.     /* Now sort the list according to its keys. */
  723.     sort_unit_vector(listvector);
  724. }
  725.  
  726. static void
  727. add_unit_to_list(unit)
  728. Unit *unit;
  729. {
  730.     if (alive(unit)) {
  731.     add_unit_to_vector(listvector, unit, FALSE);
  732.     /* (should apply other inclusion criteria too?) */
  733.     ++listnumunits;
  734.     }
  735. }
  736.  
  737. void
  738. show_list()
  739. {
  740.     int i = 0, line = 1;
  741.     char *maincat = "xxxxx", *filter = "?yyyy?";
  742.  
  743.     clear_window(listwin);
  744.     tmpbuf[0] = '\0';
  745.     switch (listtype) {
  746.       case 0:
  747.     if (listvector == NULL) {
  748.         listvector = make_unit_vector(1000);
  749.         listnumunits = 0;
  750.         for (i = 0; i < MAXSORTKEYS; ++i) {
  751.         tmpsortkeys[i] = bynothing;
  752.         }
  753.         tmpsortkeys[0] = byside;
  754.         organize_list_contents();
  755.     }
  756.     for (line = firstvisible; line <= lastvisible; ++line) {
  757.         draw_unit_list_entry(line);
  758.     }
  759.     maincat = "Units";
  760.     switch (listsides) {
  761.       case ourside:
  762.         filter = "-Own--";
  763.         break;
  764.       case ourallies:
  765.         filter = "Allied";
  766.         break;
  767.       case allsides:
  768.         filter = "-ALL--";
  769.         break;
  770.       default:
  771.         filter = "??????";
  772.         break;
  773.     }
  774.         for (i = 0; i < MAXSORTKEYS; ++i) {
  775.         if (i == 0) {
  776.             strcat(tmpbuf, "by ");
  777.         } else if (tmpsortkeys[i] != bynothing) {
  778.             strcat(tmpbuf, ",");
  779.         }
  780.         switch (tmpsortkeys[i]) {
  781.           case byside:
  782.             strcat(tmpbuf, "side");
  783.             break;
  784.           case bytype:
  785.             strcat(tmpbuf, "type");
  786.             break;
  787.           case byname:
  788.             strcat(tmpbuf, "name");
  789.             break;
  790.           case byactorder:
  791.             strcat(tmpbuf, "act");
  792.             break;
  793.           case bylocation:
  794.             strcat(tmpbuf, "loc");
  795.             break;
  796.           case bynothing:
  797.             break;
  798.           default:
  799.             strcat(tmpbuf, "???");
  800.             break;
  801.         }
  802.         }
  803.     break;
  804.       case 1:
  805.     for_all_unit_types(line) {
  806.         draw_type_list_entry(line);
  807.     } 
  808.     maincat = "Types";
  809.     filter = "------";
  810.     }
  811.     /* Draw the modeline, in standout if possible. */
  812.     if (use_standout)
  813.       standout();
  814.     memcpy(spbuf, dashbuffer, lw);
  815.     memcpy(spbuf+1, maincat, 5);
  816.     memcpy(spbuf+7, filter, 6);
  817.     memcpy(spbuf+14, tmpbuf, strlen(tmpbuf));
  818.     spbuf[lw] = '\0';
  819.     draw_text(listwin, 0, lh - 1, spbuf);
  820.     if (use_standout)
  821.       standend();
  822.     refresh();
  823. }
  824.  
  825. void
  826. cycle_list_type()
  827. {
  828.     listtype = (listtype + 1) % 2;
  829. }
  830.  
  831. void
  832. cycle_list_filter()
  833. {
  834.     listsides = (listsides + 1) % 3;
  835. }
  836.  
  837. void
  838. cycle_list_order()
  839. {
  840.     tmpsortkeys[0] = (tmpsortkeys[0] + 1) % numsortkeytypes;
  841.     sort_unit_vector(listvector);
  842. }
  843.  
  844. /* Alter the numbers for a single type of unit.  Should be called right
  845.    after any changes.  Formatted to look nice, but kind of messy to set
  846.    up correctly; display should not jump back and forth as the numbers
  847.    change in size. */
  848.  
  849. int firsttypevisible = 0;
  850. int lasttypevisible = MAXUTYPES;
  851.  
  852. static void
  853. draw_type_list_entry(line)
  854. int line;
  855. {
  856.     int u, num;
  857.  
  858.     u = line + firsttypevisible;
  859.     if (!between(0, u, numutypes))
  860.       return;
  861.     if (u > lasttypevisible)
  862.       return;
  863.     sprintf(spbuf, " %c ", unitchars[u]);
  864.     /* Our unit total (right-justified) */
  865.     num = num_units_in_play(dside, u);
  866.     if (num > 0)    {
  867.     sprintf(tmpbuf, "%4d", num);
  868.     } else {
  869.     sprintf(tmpbuf, "    ");
  870.     }
  871.     strcat(spbuf, tmpbuf);
  872.     /* Our units under construction (left-justified) */
  873.     num = num_units_incomplete(dside, u);
  874.     if (num > 0) {
  875.     sprintf(tmpbuf, "(%d)", num);
  876.     } else {
  877.     sprintf(tmpbuf, "    ");
  878.     }
  879.     strcat(spbuf, tmpbuf);
  880.     draw_text(listwin, 1, line, spbuf);
  881. }
  882.  
  883. /* (should add these back in somewhere?) */
  884. #if 0
  885.     /* Our total possessions over the game */
  886.     /* Make sure that subsequent write goes into valid string area. */
  887.     strcat(spbuf,  "            ");
  888.     if (total_gain(side, u) > 0) {
  889.         sprintf(tmpbuf, "%4d", total_gain(side, u));
  890.         sprintf(spbuf+10, "%s", tmpbuf);
  891.     }
  892.     /* Our total losses over the game */
  893.     strcat(spbuf,  "            ");
  894.     if (total_loss(side, u) > 0) {
  895.         sprintf(tmpbuf, "- %d", total_loss(side, u));
  896.         sprintf(spbuf+15, "%s", tmpbuf);
  897.     }
  898. #endif
  899.  
  900. static void
  901. draw_unit_list_entry(line)
  902. int line;
  903. {
  904.     Unit *unit = listvector->units[line].unit;
  905.     char tmpbuf[BUFSIZE];
  906.  
  907.     if (unit == NULL)
  908.       return;
  909.     if (unit == curunit) {
  910.     draw_text(listwin, 0, line, "*");
  911.     }
  912.     if (alive(unit)) {
  913.     name_or_number(unit, tmpbuf);
  914.     sprintf(spbuf, "%c%d %-16s ",
  915.         unitchars[unit->type], side_number(unit->side), tmpbuf);
  916.     if (unit->act && unit->act->acp > 0) {
  917.         tprintf(spbuf, "%3d", unit->act->acp);
  918.     }
  919.     if (unit->plan && unit->plan->asleep) {
  920.         strcat(spbuf, "z");
  921.     }
  922.     if (unit->plan && unit->plan->reserve) {
  923.         strcat(spbuf, "r");
  924.     }
  925.     if (unit->plan && unit->plan->waitingfortasks) {
  926.         strcat(spbuf, "w");
  927.     }
  928.     /* do hp also? */
  929.     } else {
  930.     sprintf(spbuf, "--");
  931.     }
  932.     draw_text(listwin, 1, line, spbuf);
  933. }
  934.  
  935. void
  936. show_clock()
  937. {
  938. #if 0
  939.     int time = 0;
  940.  
  941.     if (realtime_game()) {
  942.     time_desc(spbuf, time);
  943.     draw_text(clockwin, 0, 0, spbuf);
  944.     }
  945. #endif
  946. }
  947.  
  948. /* General window clearing. */
  949.  
  950. void
  951. clear_window(win)
  952. struct ccwin *win;
  953. {
  954.     int i;
  955.  
  956.     if (win->x == 0
  957.     && win->y == 0
  958.     && win->w == COLS
  959.     && win->h == LINES) {
  960.     clear();
  961.     } else if (between(0, win->x, COLS-1)
  962.            && between(0, win->w, COLS-1)
  963.            && between(0, win->y, LINES-1)
  964.            && between(0, win->h, LINES-1)) {
  965.     for (i = 0; i < win->w; ++i)
  966.       tmpbuf[i] = ' ';
  967.     tmpbuf[win->w] = '\0';
  968.     for (i = 0; i < win->h; ++i)
  969.       mvaddstr(win->y + i, win->x, tmpbuf);
  970.     } else {
  971.     printf("error: win %d is %dx%d @ %d,%d\n",
  972.            (int) win, win->w, win->h, win->x, win->y);
  973.     }
  974. }
  975.  
  976. #if 0
  977. /* Draw a large blot over the area. */
  978.  
  979. void
  980. draw_mushroom(x, y, i)
  981. int x, y, i;
  982. {
  983.     int sx, sy;
  984.  
  985.     xform(x, y, &sx, &sy);
  986.     if (cur_at(mapwin, sx, sy)) {
  987.     addstr("##");
  988.     refresh();
  989.     if (i > 0) {
  990.         if (cur_at(mapwin, sx-1, sy+1))
  991.           addstr("####");
  992.         if (cur_at(mapwin, sx-2, sy))
  993.           addstr("######");
  994.         if (cur_at(mapwin, sx-1, sy-1))
  995.           addstr("####");
  996.         refresh();
  997.     }
  998.     }
  999. }
  1000. #endif
  1001.  
  1002. /* Drawing text is easy, but we do need to do clipping manually. */
  1003.  
  1004. int
  1005. draw_text(win, x, y, str)
  1006. struct ccwin *win;
  1007. int x, y;
  1008. char *str;
  1009. {
  1010.     int i, slen, linestart = 0;
  1011.  
  1012.     if (y < 0)
  1013.       y = win->h - y;
  1014.     if (cur_at(win, x, y)) {
  1015.     slen = strlen(str);
  1016.     for (i = 0; i < slen; ++i) {
  1017.         if (str[i] == '\n') {
  1018.         if (y < win->h) {
  1019.             if (cur_at(win, x, ++y))
  1020.               linestart = i;
  1021.             else
  1022.               break;
  1023.         } else {
  1024.             /* Ran out of room */
  1025.             return i;
  1026.         }
  1027.         } else if (x + (i - linestart) < win->w) {
  1028.         addch(str[i]);
  1029.         }
  1030.     }
  1031.     }
  1032.     return (-1);
  1033. }
  1034.  
  1035. /* Make a beep by writing ^G. */
  1036.  
  1037. void
  1038. xbeep()
  1039. {
  1040. #ifdef THINK_C /* the libcurses library already includes a beep function */
  1041.     beep();
  1042. #else
  1043.     putchar('\007');
  1044. #endif
  1045. }
  1046.  
  1047. /* (should break helpstring into lines before displaying, so scrolling
  1048.    calcs simpler) */
  1049.  
  1050. void
  1051. show_help()
  1052. {
  1053.     int i, slen, x = 0, y = 0, more = FALSE;
  1054.     char *str;
  1055.  
  1056.     clear();
  1057.     cur_at(helpwin, 4, y);
  1058.     if (use_standout)
  1059.       standout();
  1060.     else
  1061.       addstr("*** ");
  1062.     addstr(cur_help_node->key);
  1063.     if (use_standout)
  1064.       standend();
  1065.     else
  1066.       addstr(" ***");
  1067.     ++y;
  1068.     cur_at(helpwin, 0, y);
  1069.     str = get_help_text(cur_help_node);
  1070.     slen = strlen(str);
  1071.     for (i = first_visible_help_pos; i < slen; ++i) {
  1072.     if (str[i] == '\n' || x > helpwin->w - 2) {
  1073.         if (y < helpwin->h - 2) {
  1074.         ++y;
  1075.         cur_at(helpwin, 0, y);
  1076.         x = 0;
  1077.         if (!(str[i] == '\n')) {
  1078.             addch(str[i]);
  1079.             ++x;
  1080.         }
  1081.         } else {
  1082.         more = TRUE;
  1083.         last_visible_help_pos = i;
  1084.         break;
  1085.         }
  1086.     } else {
  1087.         addch(str[i]);
  1088.         ++x;
  1089.     }
  1090.     }
  1091.     y = helpwin->h - 1;
  1092.     cur_at(helpwin, (more ? 9 : 0), y);
  1093.     addstr(" ['n' for next, 'p' for prev, 'q' to end]");
  1094.     if (more) {
  1095.     cur_at(helpwin, 0, y);
  1096.     if (use_standout)
  1097.       standout();
  1098.     addstr("--More--");
  1099.     if (use_standout)
  1100.       standend();
  1101.     }
  1102.     /* Position the cursor. */
  1103.     cur_at(helpwin, (more ? 8 : 0), y);
  1104. }
  1105.  
  1106. /* Put the terminal's cursor at an appropriate place. */
  1107.  
  1108. void
  1109. show_cursor()
  1110. {
  1111.     int sx, sy;
  1112.  
  1113.     if (active_display(dside)) {
  1114.     switch (mode) {
  1115.       case SURVEY:
  1116.       case MOVE:
  1117.         if (curunit != NULL
  1118.         && in_play(curunit)
  1119.         && !(curunit->x == curx && curunit->y == cury)) {
  1120.         curx = curunit->x;  cury = curunit->y;
  1121.         }
  1122.         if (!in_middle(curx, cury)) {
  1123.         set_view_focus(mvp, curx, cury);
  1124.         center_on_focus(mvp);
  1125.         set_map_viewport();
  1126.         show_map();
  1127.         }
  1128.         xform(curx, cury, &sx, &sy);
  1129.         if (!cur_at(mapwin, sx, sy))
  1130.           abort();
  1131.         break;
  1132.       case HELP:
  1133.         cur_at(helpwin, 0, 0);
  1134.         break;
  1135.       case MORE:
  1136.         xbeep();
  1137.         break;
  1138.       case PROMPT:
  1139.         /* This doesn't account for two-line prompts. */
  1140.         if (!cur_at(toplineswin, strlen(text1), 0))
  1141.           abort();
  1142.         break;
  1143.       case PROMPTXY:
  1144.         if (!in_middle(curx, cury)) {
  1145.         set_view_focus(mvp, curx, cury);
  1146.         center_on_focus(mvp);
  1147.         set_map_viewport();
  1148.         show_map();
  1149.         }
  1150.         xform(curx, cury, &sx, &sy);
  1151.         if (!cur_at(mapwin, sx, sy))
  1152.           abort();
  1153.         break;
  1154.       default:
  1155.         abort();
  1156.     }
  1157.     refresh();
  1158.     }
  1159. }
  1160.  
  1161. /* Position the cursor, being careful to test for sensibility. */
  1162.  
  1163. int
  1164. cur_at(win, x, y)
  1165. struct ccwin *win;
  1166. int x, y;
  1167. {
  1168.     int sx, sy;
  1169.  
  1170.     if (x < 0 || x >= win->w || y < 0 || y >= win->h) {
  1171.     /* Just return false if something is wrong; the caller should
  1172.        test this and react apropriately. */
  1173.     return FALSE;
  1174.     } else {
  1175.     sx = win->x + x;  sy = win->y + y;
  1176.     if (between(0, sx, COLS-1) && between(0, sy, LINES-1)) {
  1177.         move(sy, sx);
  1178.     } else {
  1179.         /* Bad. Very bad. */
  1180.         abort();
  1181.     }
  1182.     return TRUE;
  1183.     }
  1184. }
  1185.