/* Map graphics for the curses interface to Xconq. Copyright (C) 1986-1989, 1991-1996, 1998-2000 Stanley T. Shebs. Xconq is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. See the file COPYING. */ #include "conq.h" #include "cconq.h" static int xform(int x, int y, int *sxp, int *syp); static void draw_divider(void); static void draw_terrain_row(int x, int y, int len); static void draw_units(int x, int y); static void draw_people(int x, int y); static void draw_legend(int x, int y); static void draw_unit_details(Unit *unit); static void describe_cell(int x, int y, int tview, char *filler, char *buf); static void organize_list_contents(void); static void add_unit_to_list(Unit *unit); static void draw_unit_list_entry(int line); static void draw_type_list_entry(int line); char *dashbuffer; /* When true, draw the terrain character in both character positions of a cell; otherwise draw in just the left position and put a blank in the right position. */ int use_both_chars = TRUE; /* When true, use standout mode to highlite mode lines and such. Standout mode increases visual clutter somewhat, so it's not obviously good or obviously bad. */ int use_standout = FALSE; int drawlinear = TRUE; char linear_char = '\\'; char bord_char = '\0'; char conn_char = '\0'; int listtype = 0; int firstvisible = 0; int lastvisible = 30; UnitVector *listvector = NULL; int listnumunits = 0; int first_visible_help_pos; int last_visible_help_pos; /* Completely redo a screen, making no assumptions about appearance. This is a last-gasp measure; most redrawing should be restricted to only the directly affected windows. Also this shouldn't be done without the user's permission, since it will blow away impending input. */ void redraw(void) { if (active_display(dside)) { switch (mode) { case SURVEY: case MOVE: case PROMPT: case PROMPTXY: clear(); draw_divider(); show_toplines(); show_game_date(); show_clock(); show_side_list(); show_list(); show_closeup(); show_map(); break; case HELP: case MORE: show_help(); break; default: break; } show_cursor(); refresh(); } } static void draw_divider(void) { int i; for (i = 2; i < LINES; ++i) { mvaddstr(i, mw, "|"); } } /* Decide whether given location is not too close to edge of screen. We do this because it's a pain to move units when half the adjacent cells aren't even visible. This routine effectively places a lower limit of 5x5 for the map window. (I think) */ int in_middle(int x, int y) { int sx, sy; xform(x, y, &sx, &sy); return ((between (3, sx, mw - 3) || !between(2, x, area.width-3)) && (between (3, sy, mh - 3) || !between(2, y, area.height-3))); } static int xform(int x, int y, int *sxp, int *syp) { xform_cell(mvp, x, y, sxp, syp); return TRUE; } void set_scroll(void) { int sx, sy, hexadj = hexagon_adjust(mvp); sx = mvp->sx; sy = mvp->sy; if (mw < (mvp->totsw - hexadj)) sx = max(sx, hexadj); else sx = hexadj; if (mh >= mvp->totsh) sy = 0; set_view_position(mvp, sx, sy); } void set_map_viewport(void) { /* Compute the size of the viewport. */ vw = min(area.width, mw / 2 + 1); vh = min(area.height, mh); /* Compute the bottom visible row. */ vy = (mvp->totsh - mvp->sy) - vh; /* Adjust to keep its value from being outside the area. */ vy = max(0, min(vy, area.height - vh)); /* Compute the first visible column. */ vx = mvp->sx / 2 - vy / 2 - 1; DGprintf("Set %dx%d viewport at %d,%d\n", vw, vh, vx, vy); } /* Display a map and all of its paraphernalia. */ void show_map(void) { int y1, y2, y, x1, x2; int halfheight = area.height / 2; clear_window(mapwin); set_map_viewport(); /* Compute top and bottom rows to be displayed. */ y1 = min(vy + vh, area.height - 1); y2 = vy; for (y = y1; y >= y2; --y) { /* Adjust the right and left bounds to fill the viewport as much as possible, without going too far (the drawing code will clip, but clipped drawing is still expensive). */ x1 = vx - (y - vy) / 2; x2 = x1 + vw + 2; /* If the area doesn't wrap, then we might have to stop drawing before we reach the edge of the viewport. */ if (!area.xwrap) { x1 = max(0, min(x1, area.width - 1)); x2 = max(0, min(x2, area.width)); if (x1 + y > area.width + halfheight) continue; if (x2 + y < halfheight) continue; if (x2 + y > area.width + halfheight) x2 = area.width + halfheight - y; if (x1 + y < halfheight) x1 = halfheight - y; } draw_row(x1, y, x2 - x1); } /* Draw modeline in standout if desired and possible. */ if (use_standout) standout(); tmpbuf[0] = '\0'; if (!drawterrain) { if (strlen(tmpbuf) > 0) strcat(tmpbuf, ","); strcat(tmpbuf, "noterrain"); } if (drawunits) { if (strlen(tmpbuf) > 0) strcat(tmpbuf, ","); strcat(tmpbuf, "units"); } if (drawpeople) { if (strlen(tmpbuf) > 0) strcat(tmpbuf, ","); strcat(tmpbuf, "people"); } if (drawnames) { if (strlen(tmpbuf) > 0) strcat(tmpbuf, ","); strcat(tmpbuf, "names"); } memcpy(spbuf, dashbuffer, mw); memcpy(spbuf+2, "Map", 3); memcpy(spbuf+7, (mode == SURVEY ? "Survey" : "-Move-"), 6); memcpy(spbuf+17, "(", 1); memcpy(spbuf+18, tmpbuf, strlen(tmpbuf)); memcpy(spbuf+18+strlen(tmpbuf), ")", 1); spbuf[mw] = '\0'; /* (should add followaction flag to status line?) */ draw_text(mapwin, 0, mh, spbuf); if (use_standout) standend(); } void draw_row(int x0, int y0, int len) { int x; if (drawterrain) { draw_terrain_row(x0, y0, len); } /* Draw sparse things on top of the basic row. */ if (people_sides_defined() && drawpeople) { for (x = x0; x < x0 + len; ++x) { if (!inside_area(x, y0)) continue; draw_people(x, y0); } } /* Draw units on top of people in cell. */ if (drawunits) { for (x = x0; x < x0 + len; ++x) { if (!inside_area(x, y0)) continue; draw_units(x, y0); } } /* Draw names last. A long name may obscure a number of units, but it obscures the terrain anyway, so you can't expect to play effectively when names are displayed. */ if (drawnames) { for (x = x0; x < x0 + len; ++x) { if (!inside_area(x, y0)) continue; draw_legend(x, y0); } } } /* Draw a single row of just terrain. */ static void draw_terrain_row(int x0, int y0, int len) { char ch, ch2; int x, sx, sy, t, t2; xform(x0, y0, &sx, &sy); for (x = x0; x < x0 + len; ++x) { ch = ((x % 2 == 0 && y0 % 2 == 0) ? unseen_char_2 : unseen_char_1); ch2 = ' '; if (terrain_visible(dside, x, y0)) { t = terrain_at(x, y0); ch = terrchars[t]; ch2 = (use_both_chars ? ch : ' '); /* Just provide a hint at presence of aux terrain. */ if (drawlinear && any_aux_terrain_defined()) { for_all_terrain_types(t2) { if (aux_terrain_defined(t2)) { if (aux_terrain_at(x, y0, t2)) { ch2 = linear_char; break; } } } } /* Don't draw the second char if showing view coverage. */ if (draw_cover && !dside->show_all && cover(dside, x, y0) <= 0) ch2 = ' '; } if (cur_at(mapwin, sx, sy)) addch(ch); if (cur_at(mapwin, sx + 1, sy)) addch(ch2); sx += 2; } } /* Draw a single unit char pair as appropriate. */ static void draw_units(int x, int y) { int draw = FALSE, xw, sx, sy, u, s; UnitView *uview; xw = wrapx(x); /* Always make the curunit take precedence. */ if (in_play(curunit) && curunit->x == xw && curunit->y == y) { u = curunit->type; s = curunit->side->id; draw = TRUE; } else if ((uview = unit_view_at(dside, x, y)) != NULL) { u = view_type(uview); s = view_side(uview)->id; draw = TRUE; } if (draw) { xform(x, y, &sx, &sy); if (cur_at(mapwin, sx, sy)) { addch(unitchars[u]); } if (cur_at(mapwin, sx + 1, sy)) { if (between(1, s, 9)) addch(s + '0'); else if (s >= 10) /* This could get weird if s > 36, but not much chance of that because MAXSIDES < 31 always. */ addch(s - 10 + 'A'); } } } /* Indicate what kind of people are living in the given cell. */ static void draw_people(int x, int y) { int pop, sx, sy; if (terrain_visible(dside, x, y) && (pop = people_side_at(wrapx(x), y)) != NOBODY) { xform(x, y, &sx, &sy); if (cur_at(mapwin, sx + 1, sy)) addch(pop + '0'); } } /* Draw any text that should be associated with this cell. */ /* (could precompute what the string will lap over and move or truncate str), should be deterministic for each mag, so redraw doesn't scramble */ /* do geofeatures, label at a cell with nothing else, and declared as the feature's "center" */ static void draw_legend(int x, int y) { int sx, sy, pixlen; char legend[100], *str; Feature *feature; Unit *unit; UnitView *uview; uview = unit_view_at(dside, x, y); if (uview == NULL) return; /* Draw a unit's name or number. */ /* (This will only do top unit in hex, what about others?) */ if (drawunits && (unit = unit_at(x, y)) != NULL) { /* still has a bug if types happen to match - need to use view date instead? */ if (view_type(uview) != unit->type) return; if (unit->name != NULL) { strcpy(legend, unit->name); } else { /* Turns out we have nothing to make a legend with. */ /* Note that, unlike some other interfaces, this one does not ever display unit numbers, since it makes the screen too cluttered to read. */ return; } xform(x, y, &sx, &sy); draw_text(mapwin, sx + 2, sy, legend); } else { feature = feature_at(x, y); if (feature != NULL) { if (feature->size == 1 && (str = feature_name_at(x, y)) != NULL) { xform(x, y, &sx, &sy); pixlen = strlen(str) / 2; draw_text(mapwin, sx - pixlen, sy, str); } } } } /* (should change text[12] to an array) */ void low_notify(Side *side, char *str) { char *lastblank, extrabuf[BUFSIZE]; if (!active_display(side)) return; if (strlen(str) > toplineswin->w) { strcpy(extrabuf, str); lastblank = strchr(extrabuf + toplineswin->w - 10, ' '); if (lastblank) { strcpy(text2, lastblank + 1); *lastblank = '\0'; } strcpy(text1, extrabuf); } else { strcpy(text1, str); } DGprintf("%s\n", text1); DGprintf("%s\n", text2); show_toplines(); /* Put cursor back to where it was. */ show_cursor(); refresh(); } void show_toplines(void) { clear_window(toplineswin); draw_text(toplineswin, 0, 0, text1); draw_text(toplineswin, 0, 1, text2); } void clear_toplines(void) { text1[0] = '\0'; text2[0] = '\0'; show_toplines(); } /* Display all the details of the currently-selected unit/cell. */ /* (should use generic code) */ void show_closeup(void) { int u, t, tview; char *filler = "Empty "; Unit *unit = NULL; Side *side2; UnitView *uview; clear_window(closeupwin); if (inside_area(curx, cury)) { if (terrain_visible(dside, curx, cury)) { tview = buildtview(terrain_at(curx, cury)); } else { tview = terrain_view(dside, curx, cury); } if (dside->show_all || in_play(curunit)) { unit = (in_play(curunit) ? curunit : unit_at(curx, cury)); if (in_play(unit)) { /* If there is a unit there, we can at least see basic info. */ sprintf(tmpbuf, "%s", unit_handle(dside, unit)); draw_text(closeupwin, 0, 0, tmpbuf); /* Show more about one of our own units. */ if (unit->side == dside) draw_unit_details(unit); filler = "In "; } } else { uview = unit_view_at(dside, curx, cury); if (uview != NULL) { filler = "In "; u = view_type(uview); side2 = view_side(uview); sprintf(tmpbuf, "%s %s", side_adjective(side2), u_type_name(u)); draw_text(closeupwin, 0, 0, tmpbuf); } } /* Describe the cell/transport here. */ if (unit != NULL && unit->transport != NULL) { t = terrain_at(unit->x, unit->y); sprintf(tmpbuf, "In %s (%s at %d,%d)", short_unit_handle(unit->transport), t_type_name(t), unit->x, unit->y); } else { describe_cell(curx, cury, tview, filler, tmpbuf); } draw_text(closeupwin, 0, 1, tmpbuf); } else { sprintf(tmpbuf, "??? Off-area %d,%d ???", curx, cury); draw_text(closeupwin, 0, 1, tmpbuf); } } static void describe_cell(int x, int y, int tview, char *filler, char *buf) { int t; char *featname; if (tview != UNSEEN) { t = vterrain(tview); /* Now describe terrain and position. */ sprintf(buf, "%s%s", filler, t_type_name(t)); featname = feature_name_at(x, y); if (featname != NULL) { strcat(buf, " "); strcat(buf, featname); } if (elevations_defined()) { strcat(buf, " "); elevation_desc(buf+strlen(buf), x, y); } /* (should put all weather on own line) */ temperature_desc(buf+strlen(buf), x, y); /* add clouds and winds desc here */ linear_desc(buf+strlen(buf), x, y); } else { sprintf(buf, ""); } tprintf(buf, " at %d,%d", x, y); } /* Describe the state of the given unit, in maximal detail. */ static void draw_unit_details(Unit *unit) { int t; /* Say which unit this is. */ sprintf(spbuf, "%s", unit_handle(dside, unit)); /* Describe the "important" parameters like hit points and moves. */ strcat(spbuf, " "); hp_desc(tmpbuf, unit, TRUE); strcat(spbuf, tmpbuf); /* (should say something about parts here) */ strcat(spbuf, " "); acp_desc(tmpbuf, unit, TRUE); strcat(spbuf, tmpbuf); draw_text(closeupwin, 0, 0, spbuf); /* Mention transport and other units stacked here. */ if (unit->transport != NULL) { sprintf(spbuf, "In %s", short_unit_handle(unit->transport)); } else { t = terrain_at(unit->x, unit->y); describe_cell(unit->x, unit->y, buildtview(t), "In ", spbuf); others_here_desc(tmpbuf, unit_at(unit->x, unit->y)); if (strlen(tmpbuf) > 0) { strcat(spbuf, ", "); strcat(spbuf, tmpbuf); } } draw_text(closeupwin, 0, 1, spbuf); /* Very briefly list the numbers and types of the occupants. */ spbuf[0] = '\0'; if (unit->occupant != NULL) { strcpy(spbuf, "Occ "); occupants_desc(tmpbuf, unit); strcat(spbuf, tmpbuf); } draw_text(closeupwin, 0, 2, spbuf); /* Describe the state of all the supplies. */ supply_desc(spbuf, unit, 0); draw_text(closeupwin, 0, 3, spbuf); /* Describe the current plans, tasks, etc. */ /* (needs much improvement) */ if (unit->plan) { int row = 5; Task *task; plan_desc(spbuf, unit); draw_text(closeupwin, 0, 4, spbuf); for (task = unit->plan->tasks; task != NULL; task = task->next) { task_desc(spbuf, unit->side, unit, task); draw_text(closeupwin, 0, row++, spbuf); } } } /* Basic routine that displays the list of sides. */ void show_side_list(void) { char ismoving, progress[20], *dpyname; int sy = 0, totacp; Side *side2; extern int curpriority; /* Ensure subwin is clear. */ clear_window(sideswin); for_all_sides(side2) { ismoving = ' '; /* (should use generic test) */ if ((g_use_side_priority() ? (curpriority == side2->priority) : (!side2->finishedturn))) ismoving = '*'; if (side2->designer) { strcpy(progress, "DESIGN "); } else if (side2->ingame) { totacp = side_initacp(side2); if (totacp > 0) { sprintf(progress, "%3d%%", (100 * side_acp(side2)) / totacp); /* We get to see our actual total acp as well. */ if (dside == side2) { tprintf(progress, "/%-3d", totacp); } else { strcat(progress, " "); } } else { strcpy(progress, " -- "); } } else { if (side_lost(side2)) { strcpy(progress, " Lost "); } else if (side_won(side2)) { strcpy(progress, " Won! "); } else { strcpy(progress, " Gone "); } } dpyname = ""; if (side2->player && side2->player->displayname) dpyname = side2->player->displayname; sprintf(spbuf, "%d%c %s %s (%s)", side_number(side2), ismoving, progress, short_side_title(side2), dpyname); draw_text(sideswin, 0, sy, spbuf); sy += 1; } /* Draw the modeline. */ if (use_standout) standout(); memcpy(spbuf, dashbuffer, lw); memcpy(spbuf+1, "Sides", 5); spbuf[lw] = '\0'; draw_text(sideswin, 0, sh - 1, spbuf); if (use_standout) standend(); } /* Display the date. */ void show_game_date(void) { clear_window(datewin); /* First line of the game state. */ /* (should cache this date string a la Mac version?) */ draw_text(datewin, 1, 0, absolute_date_string(g_turn())); /* (should use second line for something) */ } /* General list display routine. */ /* (should track beginning/end of displayed list, draw only visible elts) */ /* (should add scrolling interaction) */ static void organize_list_contents(void) { Side *side2; Unit *unit; /* Build up the array of units for this list. */ listnumunits = 0; clear_unit_vector(listvector); /* We always see our own units. */ for_all_side_units(dside, unit) { add_unit_to_list(unit); } for_all_sides(side2) { if (dside != side2) { for_all_side_units(side2, unit) { if (side_sees_image(dside, unit)) { add_unit_to_list(unit); } } } } /* Now sort the list according to its keys. */ sort_unit_vector(listvector); } static void add_unit_to_list(Unit *unit) { if (alive(unit)) { add_unit_to_vector(listvector, unit, FALSE); /* (should apply other inclusion criteria too?) */ ++listnumunits; } } void show_list(void) { int i = 0, line = 1; char *maincat = "xxxxx", *filter = "?yyyy?"; clear_window(listwin); tmpbuf[0] = '\0'; switch (listtype) { case 0: if (listvector == NULL) { listvector = make_unit_vector(1000); listnumunits = 0; for (i = 0; i < MAXSORTKEYS; ++i) { tmpsortkeys[i] = bynothing; } tmpsortkeys[0] = byside; organize_list_contents(); } for (line = firstvisible; line <= lastvisible; ++line) { draw_unit_list_entry(line); } maincat = "Units"; switch (listsides) { case ourside: filter = "-Own--"; break; case ourallies: filter = "Allied"; break; case allsides: filter = "-ALL--"; break; default: filter = "??????"; break; } for (i = 0; i < MAXSORTKEYS; ++i) { if (i == 0) { strcat(tmpbuf, "by "); } else if (tmpsortkeys[i] != bynothing) { strcat(tmpbuf, ","); } switch (tmpsortkeys[i]) { case byside: strcat(tmpbuf, "side"); break; case bytype: strcat(tmpbuf, "type"); break; case byname: strcat(tmpbuf, "name"); break; case byactorder: strcat(tmpbuf, "act"); break; case bylocation: strcat(tmpbuf, "loc"); break; case bynothing: break; default: strcat(tmpbuf, "???"); break; } } break; case 1: for_all_unit_types(line) { draw_type_list_entry(line); } maincat = "Types"; filter = "------"; } /* Draw the modeline, in standout if possible. */ if (use_standout) standout(); memcpy(spbuf, dashbuffer, lw); memcpy(spbuf+1, maincat, 5); memcpy(spbuf+7, filter, 6); memcpy(spbuf+14, tmpbuf, strlen(tmpbuf)); spbuf[lw] = '\0'; draw_text(listwin, 0, lh - 1, spbuf); if (use_standout) standend(); refresh(); } void cycle_list_type(void) { listtype = (listtype + 1) % 2; } void cycle_list_filter(void) { listsides = (listsides + 1) % 3; } void cycle_list_order(void) { tmpsortkeys[0] = (tmpsortkeys[0] + 1) % numsortkeytypes; sort_unit_vector(listvector); } /* Alter the numbers for a single type of unit. Should be called right after any changes. Formatted to look nice, but kind of messy to set up correctly; display should not jump back and forth as the numbers change in size. */ int firsttypevisible = 0; int lasttypevisible = MAXUTYPES; static void draw_type_list_entry(int line) { int u, num; u = line + firsttypevisible; if (!between(0, u, numutypes)) return; if (u > lasttypevisible) return; sprintf(spbuf, " %c ", unitchars[u]); /* Our unit total (right-justified) */ num = num_units_in_play(dside, u); if (num > 0) { sprintf(tmpbuf, "%4d", num); } else { sprintf(tmpbuf, " "); } strcat(spbuf, tmpbuf); /* Our units under construction. */ num = num_units_incomplete(dside, u); if (num > 0) { sprintf(tmpbuf, "(%d)", num); } else { sprintf(tmpbuf, " "); } strcat(spbuf, tmpbuf); /* Our total gains over the game. */ if (total_gain(dside, u) > 0) { sprintf(tmpbuf, " %4d ", total_gain(dside, u)); } else { sprintf(tmpbuf, " "); } strcat(spbuf, tmpbuf); /* Our total losses over the game. */ if (total_loss(dside, u) > 0) { sprintf(tmpbuf, "- %-4d", total_loss(dside, u)); } else { sprintf(tmpbuf, " "); } strcat(spbuf, tmpbuf); draw_text(listwin, 1, line, spbuf); } static void draw_unit_list_entry(int line) { Unit *unit = unit_in_vector(listvector, line); char tmpbuf[BUFSIZE]; if (unit == NULL) return; if (unit == curunit) { draw_text(listwin, 0, line, "*"); } if (alive(unit)) { name_or_number(unit, tmpbuf); sprintf(spbuf, "%c%d %-16s ", unitchars[unit->type], side_number(unit->side), tmpbuf); if (has_acp_left(unit)) { tprintf(spbuf, "%3d", unit->act->acp); } if (unit->plan && unit->plan->asleep) { strcat(spbuf, "z"); } if (unit->plan && unit->plan->reserve) { strcat(spbuf, "r"); } if (unit->plan && unit->plan->waitingfortasks) { strcat(spbuf, "w"); } /* do hp also? */ } else { sprintf(spbuf, "--"); } draw_text(listwin, 1, line, spbuf); } void show_clock(void) { #if 0 int time = 0; if (realtime_game()) { time_desc(spbuf, time); draw_text(clockwin, 0, 0, spbuf); } #endif } /* General window clearing. */ void clear_window(struct ccwin *win) { int i; if (win->x == 0 && win->y == 0 && win->w == COLS && win->h == LINES) { clear(); } else if (between(0, win->x, COLS-1) && between(0, win->w, COLS-1) && between(0, win->y, LINES-1) && between(0, win->h, LINES-1)) { for (i = 0; i < win->w; ++i) tmpbuf[i] = ' '; tmpbuf[win->w] = '\0'; for (i = 0; i < win->h; ++i) mvaddstr(win->y + i, win->x, tmpbuf); } else { printf("error: win %d is %dx%d @ %d,%d\n", (int) win, win->w, win->h, win->x, win->y); } } /* Draw a large blot over the area. */ void draw_blast(int x, int y, int r) { int sx, sy; xform(x, y, &sx, &sy); if (cur_at(mapwin, sx, sy)) { addstr("##"); refresh(); if (r > 0) { if (cur_at(mapwin, sx-1, sy+1)) addstr("####"); if (cur_at(mapwin, sx-2, sy)) addstr("######"); if (cur_at(mapwin, sx-1, sy-1)) addstr("####"); refresh(); } } } /* Drawing text is easy, but we do need to do clipping manually. */ int draw_text(struct ccwin *win, int x, int y, char *str) { int i, slen, linestart = 0; if (y < 0) y = win->h - y; if (cur_at(win, x, y)) { slen = strlen(str); for (i = 0; i < slen; ++i) { if (str[i] == '\n') { if (y < win->h) { if (cur_at(win, x, ++y)) linestart = i; else break; } else { /* Ran out of room */ return i; } } else if (x + (i - linestart) < win->w) { addch(str[i]); } } } return (-1); } /* Make a beep by writing ^G. */ void xbeep(void) { #ifdef MAC /* the libcurses library already includes a beep function */ beep(); #else putchar('\007'); #endif } /* (should break helpstring into lines before displaying, so scrolling calcs simpler) */ void show_help(void) { int i, slen, x = 0, y = 0, more = FALSE; char *str; clear(); cur_at(helpwin, 4, y); if (use_standout) standout(); else addstr("*** "); addstr(cur_help_node->key); if (use_standout) standend(); else addstr(" ***"); ++y; cur_at(helpwin, 0, y); str = get_help_text(cur_help_node); slen = strlen(str); for (i = first_visible_help_pos; i < slen; ++i) { if (str[i] == '\n' || x > helpwin->w - 2) { if (y < helpwin->h - 2) { ++y; cur_at(helpwin, 0, y); x = 0; if (!(str[i] == '\n')) { addch(str[i]); ++x; } } else { more = TRUE; last_visible_help_pos = i; break; } } else { addch(str[i]); ++x; } } y = helpwin->h - 1; cur_at(helpwin, (more ? 9 : 0), y); addstr(" ['n' for next, 'p' for prev, 'q' to end]"); if (more) { cur_at(helpwin, 0, y); if (use_standout) standout(); addstr("--More--"); if (use_standout) standend(); } /* Position the cursor. */ cur_at(helpwin, (more ? 8 : 0), y); } /* Put the terminal's cursor at an appropriate place. */ void show_cursor(void) { int sx, sy; if (active_display(dside)) { switch (mode) { case SURVEY: case MOVE: if (curunit != NULL && in_play(curunit) && !(curunit->x == curx && curunit->y == cury)) { curx = curunit->x; cury = curunit->y; } if (!in_middle(curx, cury)) { set_view_focus(mvp, curx, cury); center_on_focus(mvp); set_map_viewport(); show_map(); } xform(curx, cury, &sx, &sy); if (!cur_at(mapwin, sx, sy)) abort(); break; case HELP: cur_at(helpwin, 0, 0); break; case MORE: xbeep(); break; case PROMPT: /* This doesn't account for two-line prompts. */ if (!cur_at(toplineswin, strlen(text1), 0)) abort(); break; case PROMPTXY: if (!in_middle(curx, cury)) { set_view_focus(mvp, curx, cury); center_on_focus(mvp); set_map_viewport(); show_map(); } xform(curx, cury, &sx, &sy); if (!cur_at(mapwin, sx, sy)) abort(); break; default: abort(); } refresh(); } } /* Position the cursor, being careful to test for sensibility. */ int cur_at(struct ccwin *win, int x, int y) { int sx, sy; if (x < 0 || x >= win->w || y < 0 || y >= win->h) { /* Just return false if something is wrong; the caller should test this and react apropriately. */ return FALSE; } else { sx = win->x + x; sy = win->y + y; if (between(0, sx, COLS-1) && between(0, sy, LINES-1)) { move(sy, sx); } else { /* Bad. Very bad. */ abort(); } return TRUE; } }