/* * +-------------------+ Christian Schlittchen * | | Enno Rehling * | Eressea PBEM host | Katja Zedel * | (c) 1998 - 2006 | * | | This program may not be used, modified or distributed * +-------------------+ without prior permission by the authors of Eressea. * */ #include #include #include #include "gmtool.h" #include "gmtool_structs.h" #include "console.h" #include "listbox.h" #include "wormhole.h" #include "calendar.h" #include "teleport.h" #include #include #if MUSEUM_MODULE #include #endif #if ARENA_MODULE #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int g_quit; int force_color = 0; newfaction * new_players = 0; state *current_state = NULL; #define IFL_SHIPS (1<<0) #define IFL_UNITS (1<<1) #define IFL_FACTIONS (1<<2) #define IFL_BUILDINGS (1<<3) static WINDOW *hstatus; #ifdef STDIO_CP int gm_codepage = STDIO_CP; #else int gm_codepage = -1; #endif static void unicode_remove_diacritics(const char *rp, char *wp) { while (*rp) { if (gm_codepage >=0 && *rp & 0x80) { size_t sz = 0; unsigned char ch; switch (gm_codepage) { case 1252: unicode_utf8_to_cp1252(&ch, rp, &sz); break; case 437: unicode_utf8_to_cp437(&ch, rp, &sz); break; default: unicode_utf8_to_ascii(&ch, rp, &sz); break; } rp += sz; *wp++ = (char)ch; } else { *wp++ = *rp++; } } *wp = 0; } static void simplify(const char *rp, char *wp) { unicode_remove_diacritics(rp, wp); } int umvwprintw(WINDOW *win, int y, int x, const char *format, ...) { char buffer[128]; va_list args; va_start(args, format); memset(buffer, 0, sizeof(buffer)); vsnprintf(buffer, sizeof(buffer)-1, format, args); va_end(args); simplify(buffer, buffer); return mvwaddstr(win, y, x, buffer); } int umvwaddnstr(WINDOW *w, int y, int x, const char * str, int len) { char buffer[128]; simplify(str, buffer); return mvwaddnstr(w, y, x, buffer, len); } static void init_curses(void) { int fg, bg; initscr(); if (has_colors() || force_color) { short bcol = COLOR_BLACK; short hcol = COLOR_MAGENTA; start_color(); #ifdef WIN32 /* looks crap on putty with TERM=linux */ if (can_change_color()) { init_color(COLOR_YELLOW, 1000, 1000, 0); init_color(COLOR_CYAN, 0, 1000, 1000); } #endif for (fg = 0; fg != 8; ++fg) { for (bg = 0; bg != 2; ++bg) { init_pair((short)(fg + 8 * bg), (short)fg, (short)(bg ? hcol : bcol)); } } attrset(COLOR_PAIR(COLOR_BLACK)); bkgd(' ' | COLOR_PAIR(COLOR_BLACK)); bkgdset(' ' | COLOR_PAIR(COLOR_BLACK)); } keypad(stdscr, TRUE); /* enable keyboard mapping */ meta(stdscr, TRUE); nonl(); /* tell curses not to do NL->CR/NL on output */ cbreak(); /* take input chars one at a time, no wait for \n */ noecho(); /* don't echo input */ scrollok(stdscr, FALSE); refresh(); } void cnormalize(const coordinate * c, int *x, int *y) { *x = c->x; *y = c->y; pnormalize(x, y, c->pl); } map_region *mr_get(const view * vi, int xofs, int yofs) { return vi->regions + xofs + yofs * vi->size.width; } static point *coor2point(const coordinate * c, point * p) { assert(c && p); p->x = c->x * TWIDTH + c->y * TWIDTH / 2; p->y = c->y * THEIGHT; return p; } static window *wnd_first, *wnd_last; static window *win_create(WINDOW * hwin) { window *wnd = calloc(1, sizeof(window)); wnd->handle = hwin; if (wnd_first != NULL) { wnd->next = wnd_first; wnd_first->prev = wnd; wnd_first = wnd; } else { wnd_first = wnd; wnd_last = wnd; } return wnd; } static void untag_region(selection * s, int nx, int ny) { unsigned int key = ((nx << 12) ^ ny); tag **tp = &s->tags[key & (MAXTHASH - 1)]; tag *t = NULL; while (*tp) { t = *tp; if (t->coord.x == nx && t->coord.y == ny) break; tp = &t->nexthash; } if (!*tp) return; *tp = t->nexthash; free(t); return; } static void tag_region(selection * s, int nx, int ny) { unsigned int key = ((nx << 12) ^ ny); tag **tp = &s->tags[key & (MAXTHASH - 1)]; while (*tp) { tag *t = *tp; if (t->coord.x == nx && t->coord.y == ny) return; tp = &t->nexthash; } *tp = calloc(1, sizeof(tag)); (*tp)->coord.x = nx; (*tp)->coord.y = ny; (*tp)->coord.pl = findplane(nx, ny); return; } static int tagged_region(selection * s, int nx, int ny) { unsigned int key = ((nx << 12) ^ ny); tag **tp = &s->tags[key & (MAXTHASH - 1)]; while (*tp) { tag *t = *tp; if (t->coord.x == nx && t->coord.y == ny) return 1; tp = &t->nexthash; } return 0; } static chtype mr_tile(const map_region * mr, int highlight) { int hl = 8 * highlight; if (mr != NULL && mr->r != NULL) { const region *r = mr->r; switch (r->terrain->_name[0]) { case 'o': return '.' | COLOR_PAIR(hl + COLOR_CYAN) | A_BOLD; case 'd': return 'D' | COLOR_PAIR(hl + COLOR_YELLOW) | A_BOLD; case 't': return '%' | COLOR_PAIR(hl + COLOR_YELLOW) | A_BOLD; case 'f': if (r->terrain->_name[1] == 'o') { /* fog */ return '.' | COLOR_PAIR(hl + COLOR_YELLOW) | A_NORMAL; } else if (r->terrain->_name[1] == 'i') { /* firewall */ return '%' | COLOR_PAIR(hl + COLOR_RED) | A_BOLD; } break; case 'h': return 'H' | COLOR_PAIR(hl + COLOR_YELLOW) | A_NORMAL; case 'm': return '^' | COLOR_PAIR(hl + COLOR_WHITE) | A_NORMAL; case 'p': if (r->terrain->_name[1] == 'l') { /* plain */ if (r_isforest(r)) return '#' | COLOR_PAIR(hl + COLOR_GREEN) | A_NORMAL; return '+' | COLOR_PAIR(hl + COLOR_GREEN) | A_BOLD; } else if (r->terrain->_name[1] == 'a') { /* packice */ return ':' | COLOR_PAIR(hl + COLOR_WHITE) | A_BOLD; } break; case 'g': return '*' | COLOR_PAIR(hl + COLOR_WHITE) | A_BOLD; case 's': return 'S' | COLOR_PAIR(hl + COLOR_MAGENTA) | A_NORMAL; } return r->terrain->_name[0] | COLOR_PAIR(hl + COLOR_RED); } return ' ' | COLOR_PAIR(hl + COLOR_WHITE); } static void paint_map(window * wnd, const state * st) { WINDOW *win = wnd->handle; int lines = getmaxy(win); int cols = getmaxx(win); int vx, vy; assert(st); if (!st) return; lines = lines / THEIGHT; cols = cols / TWIDTH; for (vy = 0; vy != lines; ++vy) { int yp = (lines - vy - 1) * THEIGHT; for (vx = 0; vx != cols; ++vx) { map_region *mr = mr_get(&st->display, vx, vy); int attr = 0; int hl = 0; int xp = vx * TWIDTH + (vy & 1) * TWIDTH / 2; int nx, ny; if (mr) { cnormalize(&mr->coord, &nx, &ny); if (tagged_region(st->selected, nx, ny)) { attr |= A_REVERSE; } if (mr->r && (mr->r->flags & RF_MAPPER_HIGHLIGHT)) hl = 1; mvwaddch(win, yp, xp, mr_tile(mr, hl) | attr); } } } } map_region *cursor_region(const view * v, const coordinate * c) { coordinate relpos; int cx, cy; if (c) { relpos.x = c->x - v->topleft.x; relpos.y = c->y - v->topleft.y; cy = relpos.y; cx = relpos.x + cy / 2; return mr_get(v, cx, cy); } return NULL; } static void draw_cursor(WINDOW * win, selection * s, const view * v, const coordinate * c, int show) { int lines = getmaxy(win) / THEIGHT; int xp, yp, nx, ny; int attr = 0; map_region *mr = cursor_region(v, c); coordinate relpos; int cx, cy; if (!mr) return; relpos.x = c->x - v->topleft.x; relpos.y = c->y - v->topleft.y; cy = relpos.y; cx = relpos.x + cy / 2; yp = (lines - cy - 1) * THEIGHT; xp = cx * TWIDTH + (cy & 1) * TWIDTH / 2; cnormalize(&mr->coord, &nx, &ny); if (s && tagged_region(s, nx, ny)) attr = A_REVERSE; if (mr->r) { int hl = 0; if (mr->r->flags & RF_MAPPER_HIGHLIGHT) hl = 1; mvwaddch(win, yp, xp, mr_tile(mr, hl) | attr); } else mvwaddch(win, yp, xp, ' ' | attr | COLOR_PAIR(COLOR_YELLOW)); if (show) { attr = A_BOLD; mvwaddch(win, yp, xp - 1, '<' | attr | COLOR_PAIR(COLOR_YELLOW)); mvwaddch(win, yp, xp + 1, '>' | attr | COLOR_PAIR(COLOR_YELLOW)); } else { attr = A_NORMAL; mvwaddch(win, yp, xp - 1, ' ' | attr | COLOR_PAIR(COLOR_WHITE)); mvwaddch(win, yp, xp + 1, ' ' | attr | COLOR_PAIR(COLOR_WHITE)); } wmove(win, yp, xp); wnoutrefresh(win); } static void paint_status(window * wnd, const state * st) { WINDOW *win = wnd->handle; const char *name = ""; int nx, ny, uid = 0; const char *terrain = "----"; map_region *mr = cursor_region(&st->display, &st->cursor); if (mr && mr->r) { uid = mr->r->uid; if (mr->r->land) { name = (const char *)mr->r->land->name; } else { name = mr->r->terrain->_name; } terrain = mr->r->terrain->_name; } cnormalize(&st->cursor, &nx, &ny); umvwprintw(win, 0, 0, "%4d %4d | %.4s | %.20s (%d)", nx, ny, terrain, name, uid); wclrtoeol(win); } static bool handle_info_region(window * wnd, state * st, int c) { return false; } static void paint_info_region(window * wnd, const state * st) { WINDOW *win = wnd->handle; int size = getmaxx(win) - 2; int line = 0, maxline = getmaxy(win) - 2; map_region *mr = cursor_region(&st->display, &st->cursor); unused_arg(st); werase(win); wxborder(win); if (mr && mr->r) { const region *r = mr->r; if (r->land) { umvwaddnstr(win, line++, 1, (char *)r->land->name, size); } else { umvwaddnstr(win, line++, 1, r->terrain->_name, size); } line++; umvwprintw(win, line++, 1, "%s, age %d", r->terrain->_name, r->age); if (r->land) { mvwprintw(win, line++, 1, "$:%6d P:%5d", rmoney(r), rpeasants(r)); mvwprintw(win, line++, 1, "H:%6d %s:%5d", rhorses(r), (r->flags & RF_MALLORN) ? "M" : "T", r->land->trees[1] + r->land->trees[2]); } line++; if (r->ships && (st->info_flags & IFL_SHIPS)) { ship *sh; wattron(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); mvwaddnstr(win, line++, 1, "* ships:", size - 5); wattroff(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); for (sh = r->ships; sh && line < maxline; sh = sh->next) { mvwprintw(win, line, 1, "%.4s ", itoa36(sh->no)); umvwaddnstr(win, line++, 6, (char *)sh->type->_name, size - 5); } } if (r->units && (st->info_flags & IFL_FACTIONS)) { unit *u; wattron(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); mvwaddnstr(win, line++, 1, "* factions:", size - 5); wattroff(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); for (u = r->units; u && line < maxline; u = u->next) { if (!fval(u->faction, FFL_MARK)) { mvwprintw(win, line, 1, "%.4s ", itoa36(u->faction->no)); umvwaddnstr(win, line++, 6, (char *)u->faction->name, size - 5); fset(u->faction, FFL_MARK); } } for (u = r->units; u && line < maxline; u = u->next) { freset(u->faction, FFL_MARK); } } if (r->units && (st->info_flags & IFL_UNITS)) { unit *u; wattron(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); mvwaddnstr(win, line++, 1, "* units:", size - 5); wattroff(win, A_BOLD | COLOR_PAIR(COLOR_YELLOW)); for (u = r->units; u && line < maxline; u = u->next) { mvwprintw(win, line, 1, "%.4s ", itoa36(u->no)); umvwaddnstr(win, line++, 6, unit_getname(u), size - 5); } } } } static void(*paint_info) (struct window * wnd, const struct state * st); static void paint_info_default(window * wnd, const state * st) { if (paint_info) paint_info(wnd, st); else paint_info_region(wnd, st); } void set_info_function(void(*callback) (struct window *, const struct state *)) { paint_info = callback; } static char *askstring(WINDOW * win, const char *q, char *buffer, size_t size) { werase(win); mvwaddstr(win, 0, 0, (char *)q); wmove(win, 0, (int)(strlen(q) + 1)); echo(); wgetnstr(win, buffer, (int)size); noecho(); return buffer; } static void statusline(WINDOW * win, const char *str) { mvwaddstr(win, 0, 0, (char *)str); wclrtoeol(win); wnoutrefresh(win); } static void terraform_at(coordinate * c, const terrain_type * terrain) { if (terrain != NULL) { region *r; int nx = c->x, ny = c->y; pnormalize(&nx, &ny, c->pl); r = findregion(nx, ny); if (r == NULL) { r = new_region(nx, ny, c->pl, 0); } terraform_region(r, terrain); } } static void terraform_selection(selection * selected, const terrain_type * terrain) { int i; if (terrain == NULL) return; for (i = 0; i != MAXTHASH; ++i) { tag **tp = &selected->tags[i]; while (*tp) { region *r; tag *t = *tp; int nx = t->coord.x, ny = t->coord.y; plane *pl = t->coord.pl; pnormalize(&nx, &ny, pl); r = findregion(nx, ny); if (r == NULL) { r = new_region(nx, ny, pl, 0); } terraform_region(r, terrain); tp = &t->nexthash; } } } static faction *select_faction(state * st) { list_selection *ilist = NULL, **iinsert; list_selection *selected = NULL; faction *f = factions; if (!f) return NULL; iinsert = &ilist; while (f) { char buffer[32]; sprintf(buffer, "%.4s %.26s", itoa36(f->no), f->name); insert_selection(iinsert, NULL, buffer, (void *)f); f = f->next; } selected = do_selection(ilist, "Select Faction", NULL, NULL); st->wnd_info->update |= 1; st->wnd_map->update |= 1; st->wnd_status->update |= 1; if (selected == NULL) return NULL; return (faction *)selected->data; } static const terrain_type *select_terrain(state * st, const terrain_type * default_terrain) { list_selection *ilist = NULL, **iinsert; list_selection *selected = NULL; const terrain_type *terrain = terrains(); if (!terrain) return NULL; iinsert = &ilist; while (terrain) { insert_selection(iinsert, NULL, terrain->_name, (void *)terrain); terrain = terrain->next; } selected = do_selection(ilist, "Terrain", NULL, NULL); st->wnd_info->update |= 1; st->wnd_map->update |= 1; st->wnd_status->update |= 1; if (selected == NULL) return NULL; return (const terrain_type *)selected->data; } static coordinate *region2coord(const region * r, coordinate * c) { c->x = r->x; c->y = r->y; c->pl = rplane(r); return c; } #ifdef __PDCURSES__ #define FAST_UP CTL_UP #define FAST_DOWN CTL_DOWN #define FAST_LEFT CTL_LEFT #define FAST_RIGHT CTL_RIGHT #else #define FAST_UP KEY_PPAGE #define FAST_DOWN KEY_NPAGE #define FAST_LEFT KEY_SLEFT #define FAST_RIGHT KEY_SRIGHT #endif void highlight_region(region * r, int toggle) { if (r != NULL) { if (toggle) r->flags |= RF_MAPPER_HIGHLIGHT; else r->flags &= ~RF_MAPPER_HIGHLIGHT; } } void select_coordinate(struct selection *selected, int nx, int ny, int toggle) { if (toggle) tag_region(selected, nx, ny); else untag_region(selected, nx, ny); } enum { MODE_MARK, MODE_SELECT, MODE_UNMARK, MODE_UNSELECT }; static void select_regions(state * st, int selectmode) { char sbuffer[80]; int findmode; const char *statustext[] = { "mark-", "select-", "unmark-", "deselect-" }; const char *status = statustext[selectmode]; statusline(st->wnd_status->handle, status); doupdate(); findmode = getch(); if (findmode == 'n') { /* none */ int i; sprintf(sbuffer, "%snone", status); statusline(st->wnd_status->handle, sbuffer); if (selectmode & MODE_SELECT) { for (i = 0; i != MAXTHASH; ++i) { tag **tp = &st->selected->tags[i]; while (*tp) { tag *t = *tp; *tp = t->nexthash; free(t); } } } else { region *r; for (r = regions; r; r = r->next) { r->flags &= ~RF_MAPPER_HIGHLIGHT; } } } else if (findmode == 'm') { region *r; sprintf(sbuffer, "%smonsters", status); statusline(st->wnd_status->handle, sbuffer); for (r = regions; r; r = r->next) { unit *u = r->units; for (; u; u = u->next) { if (fval(u->faction, FFL_NPC) != 0) break; } if (u) { if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } } else if (findmode == 'p') { region *r; sprintf(sbuffer, "%splayers", status); statusline(st->wnd_status->handle, sbuffer); for (r = regions; r; r = r->next) { unit *u = r->units; for (; u; u = u->next) { if (fval(u->faction, FFL_NPC) == 0) break; } if (u) { if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } } else if (findmode == 'u') { region *r; sprintf(sbuffer, "%sunits", status); statusline(st->wnd_status->handle, sbuffer); for (r = regions; r; r = r->next) { if (r->units) { if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } } else if (findmode == 's') { region *r; sprintf(sbuffer, "%sships", status); statusline(st->wnd_status->handle, sbuffer); for (r = regions; r; r = r->next) { if (r->ships) { if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } } else if (findmode == 'f') { char fbuffer[12]; sprintf(sbuffer, "%sfaction:", status); askstring(st->wnd_status->handle, sbuffer, fbuffer, 12); if (fbuffer[0]) { faction *f = findfaction(atoi36(fbuffer)); if (f != NULL) { unit *u; sprintf(sbuffer, "%sfaction: %s", status, itoa36(f->no)); statusline(st->wnd_status->handle, sbuffer); for (u = f->units; u; u = u->nextF) { region *r = u->region; if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } else { statusline(st->wnd_status->handle, "faction not found."); beep(); return; } } } else if (findmode == 't') { const struct terrain_type *terrain; sprintf(sbuffer, "%sterrain: ", status); statusline(st->wnd_status->handle, sbuffer); terrain = select_terrain(st, NULL); if (terrain != NULL) { region *r; sprintf(sbuffer, "%sterrain: %s", status, terrain->_name); statusline(st->wnd_status->handle, sbuffer); for (r = regions; r; r = r->next) { if (r->terrain == terrain) { if (selectmode & MODE_SELECT) { select_coordinate(st->selected, r->x, r->y, selectmode == MODE_SELECT); } else { highlight_region(r, selectmode == MODE_MARK); } } } } } else { statusline(st->wnd_status->handle, "unknown command."); beep(); return; } st->wnd_info->update |= 3; st->wnd_status->update |= 3; st->wnd_map->update |= 3; } void loaddata(state *st) { char datafile[MAX_PATH]; askstring(st->wnd_status->handle, "save as:", datafile, sizeof(datafile)); if (strlen(datafile) > 0) { create_backup(datafile); readgame(datafile, false); st->modified = 0; } } void savedata(state *st) { char datafile[MAX_PATH]; askstring(st->wnd_status->handle, "save as:", datafile, sizeof(datafile)); if (strlen(datafile) > 0) { create_backup(datafile); remove_empty_units(); writegame(datafile); st->modified = 0; } } static void handlekey(state * st, int c) { window *wnd; coordinate *cursor = &st->cursor; static char locate[80]; static int findmode = 0; region *r; char sbuffer[80]; static char kbuffer[80]; int n, nx, ny, minpop, maxpop; switch (c) { case FAST_RIGHT: cursor->x += 10; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case FAST_LEFT: cursor->x -= 10; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case FAST_UP: cursor->y += 10; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case FAST_DOWN: cursor->y -= 10; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case KEY_UP: cursor->y++; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case KEY_DOWN: cursor->y--; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case KEY_RIGHT: cursor->x++; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case KEY_LEFT: cursor->x--; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; case 'S': case KEY_SAVE: case KEY_F(2): savedata(st); break; case KEY_F(3): case KEY_OPEN: loaddata(st); break; case 'B': if (!new_players) { join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer)); new_players = read_newfactions(sbuffer); } cnormalize(&st->cursor, &nx, &ny); minpop = config_get_int("seed.population.min", 8); maxpop = config_get_int("seed.population.max", minpop); if (maxpop > minpop) { n = rng_int() % (maxpop - minpop) + minpop; } else { n = minpop; } build_island_e3(&new_players, nx, ny, n, n * 3); st->modified = 1; st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; break; case 0x02: /* CTRL+b */ cnormalize(&st->cursor, &nx, &ny); make_block(nx, ny, 6, newterrain(T_OCEAN)); st->modified = 1; st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; break; case 0x09: /* tab = next selected */ if (regions != NULL) { map_region *mr = cursor_region(&st->display, cursor); if (mr) { region *first = mr->r; region *cur = (first && first->next) ? first->next : regions; while (cur != first) { coordinate coord; region2coord(cur, &coord); cnormalize(&coord, &nx, &ny); if (tagged_region(st->selected, nx, ny)) { st->cursor = coord; st->wnd_info->update |= 1; st->wnd_status->update |= 1; break; } cur = cur->next; if (!cur && first) cur = regions; } } } break; case 'p': if (planes) { plane *pl = planes; if (cursor->pl) { while (pl && pl != cursor->pl) { pl = pl->next; } if (pl && pl->next) { cursor->pl = pl->next; } else { cursor->pl = get_homeplane(); } } else { cursor->pl = planes; } } break; case 'a': if (regions != NULL) { map_region *mr = cursor_region(&st->display, cursor); if (mr && mr->r) { region *cur = mr->r; plane *pl = rplane(cur); if (pl == NULL) { cur = r_standard_to_astral(cur); } else if (is_astral(cur)) { cur = r_astral_to_standard(cur); } else { cur = NULL; } if (cur != NULL) { region2coord(cur, &st->cursor); } else { beep(); } } } break; case 'g': askstring(st->wnd_status->handle, "goto-x:", sbuffer, 12); if (sbuffer[0]) { askstring(st->wnd_status->handle, "goto-y:", sbuffer + 16, 12); if (sbuffer[16]) { st->cursor.x = atoi(sbuffer); st->cursor.y = atoi(sbuffer + 16); st->wnd_info->update |= 1; st->wnd_status->update |= 1; } } break; case 'f': case 0x14: /* C-t */ terraform_at(&st->cursor, select_terrain(st, NULL)); st->modified = 1; st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; break; case 'I': statusline(st->wnd_status->handle, "info-"); doupdate(); do { c = getch(); switch (c) { case 's': st->info_flags ^= IFL_SHIPS; if (st->info_flags & IFL_SHIPS) statusline(st->wnd_status->handle, "info-ships true"); else statusline(st->wnd_status->handle, "info-ships false"); break; case 'b': st->info_flags ^= IFL_BUILDINGS; if (st->info_flags & IFL_BUILDINGS) statusline(st->wnd_status->handle, "info-buildings true"); else statusline(st->wnd_status->handle, "info-buildings false"); break; case 'f': st->info_flags ^= IFL_FACTIONS; if (st->info_flags & IFL_FACTIONS) statusline(st->wnd_status->handle, "info-factions true"); else statusline(st->wnd_status->handle, "info-factions false"); break; case 'u': st->info_flags ^= IFL_UNITS; if (st->info_flags & IFL_UNITS) statusline(st->wnd_status->handle, "info-units true"); else statusline(st->wnd_status->handle, "info-units false"); break; case 27: /* esc */ break; default: beep(); c = 0; } } while (c == 0); break; case 'L': if (global.vm_state) { move(0, 0); refresh(); lua_do((struct lua_State *)global.vm_state); /* todo: do this from inside the script */ clear(); st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; } break; case 12: /* Ctrl-L */ clear(); st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; break; case 'h': select_regions(st, MODE_MARK); break; case 'H': select_regions(st, MODE_UNMARK); break; case 't': select_regions(st, MODE_SELECT); break; case 'T': select_regions(st, MODE_UNSELECT); break; case ';': statusline(st->wnd_status->handle, "tag-"); doupdate(); switch (getch()) { case 'f': case 't': terraform_selection(st->selected, select_terrain(st, NULL)); st->modified = 1; st->wnd_info->update |= 1; st->wnd_status->update |= 1; st->wnd_map->update |= 1; break; case 'm': break; default: statusline(st->wnd_status->handle, "unknown command."); beep(); } break; case ' ': cnormalize(cursor, &nx, &ny); if (tagged_region(st->selected, nx, ny)) untag_region(st->selected, nx, ny); else tag_region(st->selected, nx, ny); break; case 'A': if (!new_players) { join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer)); new_players = read_newfactions(sbuffer); } seed_players(&new_players, false); st->wnd_map->update |= 1; break; case '/': statusline(st->wnd_status->handle, "find-"); doupdate(); findmode = getch(); if (findmode == 'r') { askstring(st->wnd_status->handle, "find-region:", locate, sizeof(locate)); } else if (findmode == 'u') { askstring(st->wnd_status->handle, "find-unit:", locate, sizeof(locate)); } else if (findmode == 'f') { askstring(st->wnd_status->handle, "find-faction:", locate, sizeof(locate)); } else if (findmode == 'F') { faction *f = select_faction(st); if (f != NULL) { strlcpy(locate, itoa36(f->no), sizeof(locate)); findmode = 'f'; } else { break; } } else { statusline(st->wnd_status->handle, "unknown command."); beep(); break; } /* achtung: fall-through ist absicht: */ if (!strlen(locate)) break; case 'n': if (findmode == 'u') { unit *u = findunit(atoi36(locate)); r = u ? u->region : NULL; } else if (findmode && regions != NULL) { struct faction *f = NULL; map_region *mr = cursor_region(&st->display, cursor); region *first = (mr && mr->r && mr->r->next) ? mr->r->next : regions; if (findmode == 'f') { sprintf(sbuffer, "find-faction: %s", locate); statusline(st->wnd_status->handle, sbuffer); f = findfaction(atoi36(locate)); if (f == NULL) { statusline(st->wnd_status->handle, "faction not found."); beep(); break; } } for (r = first;;) { if (findmode == 'r' && r->land && r->land->name && strstr((const char *)r->land->name, locate)) { break; } else if (findmode == 'f') { unit *u; for (u = r->units; u; u = u->next) { if (u->faction == f) { break; } } if (u) break; } r = r->next; if (r == NULL) r = regions; if (r == first) { r = NULL; statusline(st->wnd_status->handle, "not found."); beep(); break; } } } else { r = NULL; } if (r != NULL) { region2coord(r, &st->cursor); st->wnd_info->update |= 1; st->wnd_status->update |= 1; } break; case 'Q': g_quit = 1; break; default: for (wnd = wnd_first; wnd != NULL; wnd = wnd->next) { if (wnd->handlekey) { if (wnd->handlekey(wnd, st, c)) break; } } if (wnd == NULL) { if (kbuffer[0] == 0) { strcpy(kbuffer, "getch:"); } sprintf(sbuffer, " 0x%x", c); strncat(kbuffer, sbuffer, sizeof(kbuffer) - 1); statusline(st->wnd_status->handle, kbuffer); if (strlen(kbuffer) > 70) kbuffer[0] = 0; } break; } } static void init_view(view * display, WINDOW * win) { display->topleft.x = 1; display->topleft.y = 1; display->topleft.pl = get_homeplane(); display->pl = get_homeplane(); display->size.width = getmaxx(win) / TWIDTH; display->size.height = getmaxy(win) / THEIGHT; display->regions = calloc(display->size.height * display->size.width, sizeof(map_region)); } static void update_view(view * vi) { int i, j; for (i = 0; i != vi->size.width; ++i) { for (j = 0; j != vi->size.height; ++j) { map_region *mr = mr_get(vi, i, j); mr->coord.x = vi->topleft.x + i - j / 2; mr->coord.y = vi->topleft.y + j; mr->coord.pl = vi->pl; pnormalize(&mr->coord.x, &mr->coord.y, mr->coord.pl); mr->r = findregion(mr->coord.x, mr->coord.y); } } } state *state_open(void) { state *st = calloc(sizeof(state), 1); st->display.pl = get_homeplane(); st->cursor.pl = get_homeplane(); st->cursor.x = 0; st->cursor.y = 0; st->selected = calloc(1, sizeof(struct selection)); st->modified = 0; st->info_flags = 0xFFFFFFFF; st->prev = current_state; current_state = st; return st; } void state_close(state * st) { assert(st == current_state); current_state = st->prev; free(st); } void run_mapper(void) { WINDOW *hwinstatus; WINDOW *hwininfo; WINDOW *hwinmap; int width, height, x, y; int split = 20; state *st; point tl; /* FIXME: dsiable logging int old_flags = log_flags; log_flags &= ~(LOG_CPERROR | LOG_CPWARNING); */ init_curses(); curs_set(1); set_readline(curses_readline); assert(stdscr); getbegyx(stdscr, x, y); width = getmaxx(stdscr); height = getmaxy(stdscr); hwinmap = subwin(stdscr, getmaxy(stdscr) - 1, getmaxx(stdscr) - split, y, x); hwininfo = subwin(stdscr, getmaxy(stdscr) - 1, split, y, x + getmaxx(stdscr) - split); hwinstatus = subwin(stdscr, 1, width, height - 1, x); st = state_open(); st->wnd_map = win_create(hwinmap); st->wnd_map->paint = &paint_map; st->wnd_map->update = 1; st->wnd_info = win_create(hwininfo); st->wnd_info->paint = &paint_info_default; st->wnd_info->handlekey = &handle_info_region; st->wnd_info->update = 1; st->wnd_status = win_create(hwinstatus); st->wnd_status->paint = &paint_status; st->wnd_status->update = 1; init_view(&st->display, hwinmap); coor2point(&st->display.topleft, &tl); hstatus = st->wnd_status->handle; /* the lua console needs this */ while (!g_quit) { int c; point p; window *wnd; view *vi = &st->display; getbegyx(hwinmap, x, y); width = getmaxx(hwinmap) - x; height = getmaxy(hwinmap) - y; coor2point(&st->cursor, &p); if (st->cursor.pl != vi->pl) { vi->pl = st->cursor.pl; st->wnd_map->update |= 1; } if (p.y < tl.y) { vi->topleft.y = st->cursor.y - vi->size.height / 2; st->wnd_map->update |= 1; } else if (p.y >= tl.y + vi->size.height * THEIGHT) { vi->topleft.y = st->cursor.y - vi->size.height / 2; st->wnd_map->update |= 1; } if (p.x <= tl.x) { vi->topleft.x = st->cursor.x + (st->cursor.y - vi->topleft.y) / 2 - vi->size.width / 2; st->wnd_map->update |= 1; } else if (p.x >= tl.x + vi->size.width * TWIDTH - 1) { vi->topleft.x = st->cursor.x + (st->cursor.y - vi->topleft.y) / 2 - vi->size.width / 2; st->wnd_map->update |= 1; } if (st->wnd_map->update) { update_view(vi); coor2point(&vi->topleft, &tl); } for (wnd = wnd_last; wnd != NULL; wnd = wnd->prev) { if (wnd->update && wnd->paint) { if (wnd->update & 1) { wnd->paint(wnd, st); wnoutrefresh(wnd->handle); } if (wnd->update & 2) { touchwin(wnd->handle); } wnd->update = 0; } } draw_cursor(st->wnd_map->handle, st->selected, vi, &st->cursor, 1); doupdate(); c = getch(); draw_cursor(st->wnd_map->handle, st->selected, vi, &st->cursor, 0); handlekey(st, c); } g_quit = 0; set_readline(NULL); curs_set(1); endwin(); /* FIXME: reset logging log_flags = old_flags; */ state_close(st); } int curses_readline(struct lua_State *L, char *buffer, size_t size, const char *prompt) { unused_arg(L); askstring(hstatus, prompt, buffer, size); return buffer[0] != 0; } void seed_players(newfaction **players, bool new_island) { if (players) { while (*players) { int n = listlen(*players); int k = (n + ISLANDSIZE - 1) / ISLANDSIZE; k = n / k; n = autoseed(players, k, new_island ? 0 : TURNS_PER_ISLAND); if (n == 0) { break; } } } } void make_block(int x, int y, int radius, const struct terrain_type *terrain) { int cx, cy; region *r; plane *pl = findplane(x, y); if (terrain == NULL) return; for (cx = x - radius; cx != x + radius; ++cx) { for (cy = y - radius; cy != y + radius; ++cy) { int nx = cx, ny = cy; pnormalize(&nx, &ny, pl); if (koor_distance(nx, ny, x, y) < radius) { if (!findregion(nx, ny)) { r = new_region(nx, ny, pl, 0); terraform_region(r, terrain); } } } } }