/* The main program of the curses interface to Xconq. Copyright (C) 1986-1989, 1991-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 "kpublic.h" #include "cmdline.h" #include "cconq.h" #ifdef THINK_C /* This is to get the command line reader in Think C on the Mac. */ #include #endif /* THINK_C */ #ifdef __MWERKS__ #ifndef MAC #define MAC #endif #include #endif /* __MWERKS__ */ extern int autofinish_start; extern int autofinish_count; static void describe_help(int arg, char *key, TextBuffer *buf); #ifdef MAC /* temporary */ char **current_cursor; char **sendcursor; char **receivecursor; #endif int announced = FALSE; char *announcemsg = NULL; /* The side of the one human player. */ Side *dside = NULL; /* move to where a change has occured */ int follow_action; /* The location currently being examined. */ int curx, cury; int tmpcurx, tmpcury; /* The unit currently being examined, if any. */ Unit *curunit; Unit *tmpcurunit; /* The current interaction mode. */ enum mode mode; /* The pushed interaction mode. */ enum mode prevmode; /* The current input character. */ char inpch; /* The command's numeric argument. */ int prefixarg; /* These are used when asking for a unit type. */ char *ustr; int *uvec; int *bvec; /* length and number of notice lines */ int nw, nh; /* last current x and y (-1,-1 initially) */ int lastx, lasty; /* current prompt on display */ char *text1; char *text2; /* data about string under construction */ int reqstrbeg; int reqstrend; /* The help window. */ struct ccwin *helpwin; /* Cached help info. */ HelpNode *cur_help_node; HelpNode *help_help_node; HelpNode *topics_help_node; struct ccwin *datewin; struct ccwin *sideswin; struct ccwin *toplineswin; struct ccwin *clockwin; /* Display of a piece of the world */ struct ccwin *mapwin; /* Unit listing */ struct ccwin *listwin; struct ccwin *closeupwin; struct ccwin *sidecloseupwin; /* Map dimensions. */ int mw, mh; VP *mvp; /* lower left of viewport in cell coords */ int vx, vy; /* Boundaries of viewport in cell coords */ int vw, vh; /* 1/2 (rounded down) of above values */ int vw2, vh2; int lastvcx, lastvcy; /* Num text lines in unit info window. */ int infoh; /* List dimensions. */ int lw, lh; /* Side list dimensions. */ int sh; /* How to draw the map. */ int drawterrain; /* Display units on the map? */ int drawunits; /* Display unit names/numbers on the map? */ int drawnames; int drawpeople; int draw_cover; enum listsides listsides; int test; int value; int sorton; int sortorder; /* True when the display is up and running. */ int active; enum movie_type { movie_null, movie_miss, movie_hit, movie_death, movie_nuke }; struct a_movie { char *type; enum movie_type itype; int args[5]; }; /* Number of movies waiting to be played. */ int numscheduled; /* Movies to be played. */ struct a_movie *movies; /* The main program just invokes everything else. */ int main(int argc, char *argv[]) { #ifdef THINK_C /* This is how Think C picks up a command line. */ argc = ccommand(&argv); #endif #ifdef __MWERKS__ /* This is how Metrowerks C picks up a command line. */ argc = ccommand(&argv); /* Configure the SIOUX window. */ SIOUXSettings.initializeTB = TRUE; SIOUXSettings.standalone = FALSE; SIOUXSettings.setupmenus = FALSE; SIOUXSettings.autocloseonquit = FALSE; SIOUXSettings.asktosaveonclose = TRUE; SIOUXSettings.showstatusline = TRUE; #endif #ifdef MAC _maccur_pgm_name = "Cconq"; #endif printf("\n Welcome to Curses Xconq version %s\n\n", version_string()); init_library_path(NULL); print_any_news(); printf("%s", license_string()); clear_game_modules(); init_data_structures(); parse_command_line(argc, argv, general_options); #ifdef DEBUGGING update_debugging(); #endif /* DEBUGGING */ if (option_game_to_join != NULL) { int rslt; /* (should parse variant specs, then comment that they must be ignored) */ /* (should accept player specs) */ /* Go through once more and complain about anything not used. */ parse_command_line(argc, argv, leftover_options); hosting = FALSE; rslt = open_remote_connection(option_game_to_join, hosting); if (rslt > 0) { rslt = send_join("xxx"); if (rslt) { master_rid = 1; while (TRUE) { receive_data(0); if (!beforestart) { break; } } } else { close_remote_connection(0); } } } else { /* (should volunteer to restore saved game if one found?) */ load_all_modules(); check_game_validity(); parse_command_line(argc, argv, variant_options); set_variants_from_options(); parse_command_line(argc, argv, player_options); set_players_from_options(); /* Go through once more and complain about anything not used. */ parse_command_line(argc, argv, leftover_options); /* (should call launch_game() here) */ make_trial_assignments(); if (option_game_to_host != NULL) { hosting = TRUE; open_remote_connection(option_game_to_host, hosting); } calculate_globals(); run_synth_methods(); final_init(); assign_players_to_sides(); #ifndef HAVE_SELECT /* If no way to check for input periodically, then we must force sequentiality. This must happen before the first run_game. */ set_g_use_side_priority(TRUE); #endif /* Run through game calcs, but don't let anything move yet; this gives display code better values to work with. */ run_game(0); init_display(); } init_interaction(); redraw(); init_signal_handlers(); while (TRUE) { #ifdef HAVE_SELECT please somebody write this code net_run_game(1); #else run_local_ai(1, 20); /* assumes no real-timeness */ while (probability(99)) { /* a hack */ net_run_game(9); run_local_ai(2, 20); } maybe_handle_input(0); #endif } /* Humor the compiler. */ return 0; } /* Nonempty display name not actually used, but needed to keep things straight. */ Player * add_default_player(void) { Player *player = add_player(); player->name = getenv("USER"); player->displayname = "term"; return player; } void make_default_player_spec(void) { default_player_spec = "term"; } /* Set up the basic user interface for a side. */ void init_ui(Side *side) { if (side_wants_display(side)) { if (dside == NULL) { side->ui = (struct a_ui *) xmalloc(sizeof(int)); dside = side; active = FALSE; follow_action = FALSE; cur_help_node = NULL; mode = prevmode = SURVEY; prefixarg = -1; } else { fprintf(stderr, "More than one side wanted a display!\n"); exit(1); } } } /* Open display, create all the windows we'll need, do misc setup things, and initialize some globals to out-of-range values for recognition later. */ void init_display(void) { int x, y, i; if (dside == NULL) { fprintf(stderr, "No side wanted a display!\n"); exit(1); } Dprintf("Will try to open screen as display...\n"); /* Go through the standard curses setup sequence. */ initscr(); nonl(); noecho(); cbreak(); #if 0 /* def MAC */ /* can't do this yet */ nodelay(stdscr, 1); #endif clear(); /* Set up random globals. */ nw = min(BUFSIZE, 60); nh = 1; text1 = xmalloc (BUFSIZE); text2 = xmalloc (BUFSIZE); text1[0] = '\0'; text2[0] = '\0'; ustr = xmalloc (MAXUTYPES); uvec = (int *) xmalloc (MAXUTYPES * sizeof(int)); bvec = (int *) xmalloc (MAXUTYPES * sizeof(int)); /* Compute the division of the screen. */ mw = (2 * COLS) / 3; infoh = 4; mh = LINES - 2 - infoh - 1; mvp = new_vp(); /* Each cell is actually 2 chars or "pixels" wide. This is a difference from the standard power==0 parameters, so we do a cheesy thing and tweak the array directly. */ hws[0] = 2; set_view_power(mvp, 0); set_view_size(mvp, mw, mh); lw = COLS - mw - 1; sh = numtotsides + 1; /* The height of the list window is what's left over after making space for the side list and status lines. */ lh = LINES - sh - 1 - 1; pick_a_focus(dside, &x, &y); set_view_focus(mvp, x, y); lastvcx = lastvcy = -1; /* Set default values for the display controls. */ drawterrain = TRUE; drawunits = TRUE; drawnames = FALSE; /* they clutter up the screen */ listsides = allsides; curunit = NULL; /* Create all the windows. */ toplineswin = create_window(0, 0, nw, 2); datewin = create_window(nw, 0, COLS - nw, 1); clockwin = create_window(nw, 1, COLS - nw, 1); closeupwin = create_window(0, 2, mw, infoh); mapwin = create_window(0, 2 + infoh, mw, mh + 1); sideswin = create_window(mw + 1, 2, lw, sh); listwin = create_window(mw + 1, 2 + sh, lw, lh); /* Help window covers the screen. */ helpwin = create_window(0, 0, COLS, LINES); /* Set up the initial scroll position of the map. */ set_scroll(); /* Make a buffer full of dashes, for use on mode lines. */ if (dashbuffer == NULL) { dashbuffer = xmalloc(COLS + 1); for (i = 0; i < COLS; ++i) dashbuffer[i] = '-'; dashbuffer[COLS] = '\0'; } init_ui_chars(); active = TRUE; Dprintf("Successfully opened display!\n"); } void init_interaction(void) { curunit = NULL; find_next_and_look(); if (curunit) { mode = MOVE; } else { mode = SURVEY; move_survey(mvp->vcx, mvp->vcy); } show_cursor(); /* Set up some cconq-specific help nodes. */ if (cur_help_node == NULL) { help_help_node = add_help_node("help", describe_help, 0, first_help_node); key_commands_help_node = add_help_node("commands", describe_key_commands, 0, first_help_node); long_commands_help_node = add_help_node("long commands", describe_long_commands, 0, key_commands_help_node); topics_help_node = add_help_node("topics", describe_topics, 0, first_help_node); cur_help_node = topics_help_node; } /* By default, require manual `return' to finish each turn. */ net_set_autofinish(dside, FALSE); } /* Windows in curses are just simple bounding boxes. */ struct ccwin * create_window(int x, int y, int w, int h) { struct ccwin *newwin; if (x + w > COLS) w = COLS - x; if (y + h > LINES) h = LINES - y; DGprintf("Creating %dx%d window at %d,%d\n", w, h, x, y); newwin = (struct ccwin *) xmalloc(sizeof(struct ccwin)); newwin->x = x; newwin->y = y; newwin->w = w; newwin->h = h; return newwin; } /* Generic input character acquisition. */ /* (should have a global that indicates whether this is in a modal place, then run_game not called on timeout, but waits until out of modal thing (perhaps aborts modal dialog automatically, then runs?) */ int wait_for_char(void) { show_cursor(); /* Only take 7-bit chars. */ inpch = getch() & 0177; return inpch; } void maybe_handle_input(int tmout) { Unit *unit; if (active_display(dside)) { if (mode == MOVE) { if (curunit != NULL && in_play(curunit) && has_acp_left(curunit) && (curunit->plan && !curunit->plan->asleep && !curunit->plan->reserve && !curunit->plan->delayed) ) { /* should scroll over to unit */ } else { unit = autonext_unit_inbox(dside, curunit, mvp); if (unit && unit->plan && !unit->plan->asleep && !unit->plan->reserve && !unit->plan->delayed && unit->plan->waitingfortasks ) { make_current(unit); } else { make_current(autonext_unit(dside, curunit)); } } } #ifdef HAVE_SELECT please somebody write this code #else wait_for_char(); #endif /* Program will not work without following due to compiler bug. (?) */ Dprintf("key is %c", inpch); /* Interpret the input. */ interpret_input(); } } void interpret_input(void) { int dir; if (inpch == REDRAW) { /* Redraw everything but leave the interaction state unchanged. */ redraw(); } else if (mode == HELP) { interpret_help(); } else if (isdigit(inpch)) { /* Build up a numeric prefix argument. */ if (prefixarg < 0) { prefixarg = 0; } else { prefixarg *= 10; } prefixarg += (inpch - '0'); sprintf(text1, "Arg: %d", prefixarg); show_toplines(); } else { clear_toplines(); if ((dir = iindex(inpch, dirchars)) >= 0) { if (prefixarg < 0) prefixarg = 1; do_dir_2(dir, prefixarg); } else if ((dir = iindex(lowercase(inpch), dirchars)) >= 0) { if (prefixarg < 0) prefixarg = 9999; if (mode == SURVEY) prefixarg = 10; do_dir_2(dir, prefixarg); } else { dside->prefixarg = prefixarg; execute_command(dside, inpch); } /* Reset the arg so we don't get confused next time around */ prefixarg = -1; } } /* Interpret a direction character. */ void do_dir_2(int dir, int n) { int nx, ny, rslt; HistEventType reason; char failbuf[BUFSIZE]; if (mode == SURVEY) { if (point_in_dir_n(curx, cury, dir, n, &nx, &ny)) { move_survey(nx, ny); } else if (n >= 2 && point_in_dir_n(curx, cury, dir, n / 2, &nx, &ny)) { move_survey(nx, ny); } else { xbeep(); } } else if (curunit == NULL) { return; /* no beep? */ } else if (n > 1) { net_set_move_dir_task(curunit, dir, n); } else { if (point_in_dir(curx, cury, dir, &nx, &ny)) { rslt = advance_into_cell(dside, curunit, nx, ny, unit_at(nx, ny), &reason); if (!rslt) { advance_failure_desc(failbuf, curunit, reason); notify(dside, "%s", failbuf); xbeep(); } } else { xbeep(); } } } int auto_attack_on_move(Unit *unit, Unit *unit2) { return TRUE; } void move_survey(int nx, int ny) { if (inside_area(nx, ny)) { make_current_at(nx, ny); } else { /* Complain if we're trying to move the cursor offworld. */ xbeep(); } } /* Ensure that given location is visible. */ void put_on_screen(int x, int y) { if (!in_middle(x, y)) { set_view_focus(mvp, x, y); center_on_focus(mvp); set_map_viewport(); show_map(); show_cursor(); } show_closeup(); } /* Prompt for a yes/no answer with a settable default. */ int ask_bool(char *question, int dflt) { char ch; prevmode = mode; mode = PROMPT; sprintf(text1, "%s [%s]", question, (dflt ? "yn" : "ny")); show_toplines(); ch = wait_for_char(); if (dflt ? (lowercase(ch) == 'n') : (lowercase(ch) == 'y')) dflt = !dflt; mode = prevmode; clear_toplines(); return dflt; } /* Prompt for a type of a unit from player, maybe only allowing some types to be accepted. Also allow specification of no unit type. We do this by scanning the vector, building a string of chars and a vector of unit types, so as to be able to map back when done. */ int ask_unit_type(char *prompt, short *possibles) { char ch; int numtypes = 0, u; for_all_unit_types(u) { bvec[u] = 0; if (possibles == NULL || possibles[u]) { bvec[u] = 1; uvec[numtypes] = u; ustr[numtypes] = unitchars[u]; numtypes++; } } if (numtypes == 0) { return NONUTYPE; } else { prevmode = mode; mode = PROMPT; ustr[numtypes] = '\0'; sprintf(text1, "%s [%s] ", prompt, ustr); show_toplines(); } while (1) { ch = wait_for_char(); if (ch == ESCAPE) { mode = prevmode; clear_toplines(); return NONUTYPE; } if (iindex(ch, ustr) >= 0) { mode = prevmode; clear_toplines(); return uvec[iindex(ch, ustr)]; } xbeep(); } } /* Prompt for a type of a terrain from player, maybe only allowing some types to be accepted. Also allow specification of no terrain type. We do this by scanning the vector, building a string of chars and a vector of terrain types, so as to be able to map back when done. */ int ask_terrain_type(char *prompt, short *possibles) { char ch; int numtypes = 0, t, type; for_all_terrain_types(t) { bvec[t] = 0; if (possibles == NULL || possibles[t]) { bvec[t] = 1; uvec[numtypes] = t; ustr[numtypes] = terrchars[t]; numtypes++; } } if (numtypes == 0) { type = NONTTYPE; } else if (numtypes == 1) { type = uvec[0]; bvec[type] = 0; } else { ustr[numtypes] = '\0'; sprintf(text1, "%s [%s] ", prompt, ustr); show_toplines(); } while (1) { ch = wait_for_char(); if (ch == ESCAPE) { clear_toplines(); return NONTTYPE; } if (iindex(ch, ustr) >= 0) { clear_toplines(); return uvec[iindex(ch, ustr)]; } xbeep(); } } /* Ask for a direction. */ int ask_direction(char *prompt, int *dirp) { char ch; prevmode = mode; mode = PROMPTXY; sprintf(text1, "%s", prompt); sprintf(text2, " [direction keys]"); show_toplines(); save_cur(); while (1) { ch = wait_for_char(); if (ch == ESCAPE) { restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); return FALSE; } *dirp = iindex(ch, dirchars); if (*dirp >= 0) { restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); return TRUE; } else { xbeep(); } } } /* User is asked to pick a position on map. This will iterate until '.' designates the final position. */ int ask_position(char *prompt, int *xp, int *yp) { char ch; int dir, nx, ny; prevmode = mode; mode = PROMPTXY; sprintf(text1, "%s", prompt); sprintf(text2, " [direction keys to move, '.' to set]"); show_toplines(); save_cur(); while (1) { ch = wait_for_char(); if (ch == '.') { *xp = curx; *yp = cury; restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); return TRUE; } if (ch == ESCAPE) { restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); return FALSE; } if ((dir = iindex(ch, dirchars)) >= 0) { point_in_dir(curx, cury, dir, &nx, &ny); if (inside_area(nx, ny)) { curx = nx; cury = ny; put_on_screen(curx, cury); show_cursor(); } else { xbeep(); } } else { xbeep(); } } } /* Save away the currently selected position. */ void save_cur(void) { tmpcurx = curx; tmpcury = cury; tmpcurunit = curunit; } /* Restore the saved "cur" slots. */ void restore_cur(void) { curx = tmpcurx; cury = tmpcury; curunit = tmpcurunit; } /* Read a string from the prompt window. Deletion is allowed, and a cursor is displayed. */ int ask_string(char *prompt, char *dflt, char **strp) { char ch; int done = FALSE, rslt = FALSE; sprintf(text1, "%s ", prompt); reqstrbeg = strlen(text1); /* If a default was supplied, add it. */ if (dflt != NULL) strcat(text1, dflt); reqstrend = strlen(text1); prevmode = mode; mode = PROMPT; show_toplines(); while (!done) { ch = wait_for_char(); switch (ch) { case '\r': case '\n': /* Return a copy of the part of the buffer that was typed into. */ if (strp != NULL) *strp = copy_string(text1 + reqstrbeg); done = TRUE; rslt = TRUE; break; case ESCAPE: xbeep(); done = TRUE; break; case BACKSPACE: case 0x7f: if (reqstrend > reqstrbeg) { --reqstrend; } else { xbeep(); } break; default: if (reqstrend < BUFSIZE-2) { /* Insert the character. */ (text1)[reqstrend++] = ch; } else { xbeep(); } } /* Make sure we're always properly terminated. */ (text1)[reqstrend] = '\0'; show_toplines(); } /* We're done, put everything back the way it was. */ mode = prevmode; clear_toplines(); show_cursor(); return rslt; } /* Ask for a side. */ Side * ask_side(char *prompt, Side *dflt) { char ch; Side *rslt = dflt; prevmode = mode; mode = PROMPT; sprintf(text1, "%s ", prompt); show_toplines(); while (1) { show_cursor(); ch = wait_for_char(); if (ch == ESCAPE) { rslt = dflt; break; } if (between('0', ch, '9')) { rslt = side_n(ch - '0'); break; } if (ch == '\r' || ch == '\n') break; xbeep(); } mode = prevmode; clear_toplines(); return rslt; } int ask_unit(char *prompt, Unit **unitp) { char ch; int ndirs, dir, nx, ny, rslt = FALSE; *unitp = NULL; prevmode = mode; mode = PROMPTXY; sprintf(text1, "%s", prompt); sprintf(text2, " [direction keys to move, '.' to set]"); show_toplines(); save_cur(); while (1) { ch = wait_for_char(); if (ch == '.') { restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); rslt = TRUE; break; } if (ch == ESCAPE) { restore_cur(); mode = prevmode; clear_toplines(); show_cursor(); break; } /* Also recognize the "in" ("occupant") command. */ if (ch == 'i') { if (*unitp != NULL) *unitp = find_next_occupant(*unitp); make_current(*unitp); continue; } ndirs = char_to_dir(ch, &dir, NULL, NULL); if (ndirs > 0) { point_in_dir(curx, cury, dir, &nx, &ny); if (inside_area(nx, ny)) { make_current_at(nx, ny); if (curunit != NULL) *unitp = curunit; } else { xbeep(); } } else { xbeep(); } } return rslt; } /* Given a unit, make it be the one that we are "looking at". */ void make_current(Unit *unit) { curunit = unit; if (in_play(unit)) { curx = unit->x; cury = unit->y; /* Make sure the screen shows this unit's char. */ draw_row(curx, cury, 1); } show_closeup(); show_cursor(); } /* Give a location, make it be current, and pick a unit there to be the current unit. */ void make_current_at(int x, int y) { Unit *unit; curx = x; cury = y; curunit = NULL; /* Look for a top-level unit to become the new current one. */ for_all_stack(x, y, unit) { if (unit->side == dside) { make_current(unit); break; } } put_on_screen(curx, cury); } /* (should add individual screen scrolling also) */ void interpret_help(void) { HelpNode *prevhelpnode = cur_help_node; extern int first_visible_help_pos, last_visible_help_pos; switch (inpch) { case ' ': first_visible_help_pos = last_visible_help_pos; show_help(); break; case 'n': cur_help_node = cur_help_node->next; break; case 'p': cur_help_node = cur_help_node->prev; break; case 'b': xbeep(); break; case 't': cur_help_node = topics_help_node; break; case '?': cur_help_node = help_help_node; break; case ESCAPE: case 'q': mode = prevmode; redraw(); break; default: xbeep(); break; } if (prevhelpnode != cur_help_node) { first_visible_help_pos = last_visible_help_pos = 0; show_help(); } } static void describe_help(int arg, char *key, TextBuffer *buf) { tbcat(buf, "' ' to page down through a node\n"); tbcat(buf, "'n' to go to next help node\n"); tbcat(buf, "'p' to go to previous node\n"); tbcat(buf, "'b' to go back to last node\n"); tbcat(buf, "'t' to go to topics list\n"); tbcat(buf, "'?' to go to this help info\n"); tbcat(buf, "'q' or ESC to go back to game\n"); } /* Reading is usually pretty fast, so don't do anything here. */ void announce_read_progress(void) { } /* Announce the start of a time-consuming computation. */ void announce_lengthy_process(char *msg) { n_seconds_elapsed(0); announcemsg = copy_string(msg); if (announcemsg) { printf("%s;", announcemsg); fflush(stdout); announcemsg = NULL; announced = TRUE; } } void announce_progress(int percentdone) { if (n_seconds_elapsed(2)) { printf(" %d%%,", percentdone); fflush(stdout); announced = TRUE; } } void finish_lengthy_process(void) { if (announced) { printf(" done.\n"); announced = FALSE; } } /* This should be called before any sort of normal exit. */ void exit_cconq(void) { close_displays(); printf("\n%s\n", get_scores(dside)); exit(0); } void flush_display_buffers(Side *side) { if (active_display(side)) { refresh(); } } /* A predicate that tests whether our display can validly be written to. */ int active_display(Side *side) { return (side == dside && dside != NULL && side_has_display(dside) && active); } void update_area_display(Side *side) { if (active_display(side)) refresh(); } /* Draw an individual detailed cell, as a row of one, on all maps. */ void update_cell_display(Side *side, int x, int y, int flags) { if (active_display(side)) { draw_row(x, y, 1); if (flags & UPDATE_ALWAYS) refresh(); } } /* Curses updates efficiently, so don't need special routine. */ void update_side_display(Side *side, Side *side2, int rightnow) { if (active_display(side)) { show_side_list(); if (rightnow) refresh(); } if (numatypes > 0 && g_side_can_research() && dside->research_topic == NOADVANCE) { /* Should eventually pop up a side research dialog instead. */ auto_pick_side_research(dside); } } /* Just change the part of the unit list relevant to the given unit. */ void update_unit_display(Side *side, Unit *unit, int rightnow) { if (active_display(side) && unit != NULL) { if (curunit == unit) { show_closeup(); show_map(); } show_side_list(); show_list(); if (rightnow) refresh(); } } void update_unit_acp_display(Side *side, Unit *unit, int rightnow) { update_unit_display(side, unit, rightnow); } void update_turn_display(Side *side, int rightnow) { if (active_display(side)) { show_game_date(); if (rightnow) refresh(); if (g_turn() >= autofinish_start + autofinish_count) { net_set_autofinish(dside, FALSE); } } } void update_action_display(Side *side, int rightnow) { if (active_display(side)) { show_side_list(); if (rightnow) refresh(); } } void update_action_result_display(Side *side, Unit *unit, int rslt, int rightnow) { Action *action; if (active_display(side)) { action = (unit->act ? &(unit->act->nextaction) : NULL); if (action == NULL) return; DGprintf("%s %s result is %s\n", unit_desig(unit), action_desig(action), hevtdefns[rslt].name); if (rightnow) refresh(); } } void update_event_display(Side *side, HistEvent *hevt, int rightnow) { /* (should add to some sort of history display) */ } void update_fire_at_display(Side *side, Unit *unit, Unit *unit2, int m, int rightnow) { /* (should flash at target) */ } /* This is for animation of fire-into actions. */ void update_fire_into_display(Side *side, Unit *unit, int x, int y, int z, int m, int rightnow) { /* (should flash at target) */ } /* Updates to clock need to be sure that display changes immediately. */ void update_clock_display(Side *side, int rightnow) { if (active_display(side)) { show_clock(); show_cursor(); if (rightnow) refresh(); } } void update_message_display(Side *side, Side *sender, char *str, int rightnow) { if (active_display(side)) { notify(side, "%s", str); if (rightnow) refresh(); } } void update_all_progress_displays(char *str, int s) { } void update_everything(void) { redraw(); } void action_point(Side *side, int x, int y) { if (!active_display(dside)) return; if (!inside_area(x, y)) return; /* (should scroll over to make action point visible, but probably not change current point?) */ } void close_displays(void) { if (!active_display(dside)) return; clear(); refresh(); endwin(); active = FALSE; DGprintf("Display \"%s\" closed.\n", dside->player->displayname); } /* An init error needs to have the command re-run. */ void low_init_error(char *str) { fprintf(stderr, "Error: %s.\n", str); fflush(stderr); } /* A warning just gets displayed, no other action is taken. */ void low_init_warning(char *str) { fprintf(stdout, "Warning: %s.\n", str); fflush(stdout); } /* A run error is fatal. */ void low_run_error(char *str) { close_displays(); fprintf(stderr, "Error: %s.\n", str); fflush(stderr); fprintf(stderr, "Saving the game..."); write_entire_game_state(error_save_filename()); fprintf(stderr, " done.\n"); fflush(stderr); exit(1); } /* Runtime warnings are for when it's important to bug the players, usually a problem with Xconq or a game design. */ void low_run_warning(char *str) { if (active_display(dside)) { notify(dside, "Warning: %s; continue? ", str); wait_for_char(); } else { low_init_warning(str); } } void print_form(Obj *form) { print_form_and_value(stdout, form); } void end_printing_forms(void) { } /* Even a curses interface can do simple "movies". */ int schedule_movie(Side *side, char *movie, ...) { int i, itype; va_list ap; if (numscheduled >= 10) return FALSE; if (side != dside) return FALSE; if (empty_string(movie)) return FALSE; if (movies == NULL) movies = (struct a_movie *) xmalloc(10 * sizeof(struct a_movie)); memset(&(movies[numscheduled]), 0, sizeof(struct a_movie)); movies[numscheduled].type = movie; itype = movie_null; if (strcmp(movie, "miss") == 0) itype = movie_miss; else if (strcmp(movie, "hit") == 0) itype = movie_hit; else if (strcmp(movie, "death") == 0) itype = movie_death; else if (strcmp(movie, "nuke") == 0) itype = movie_nuke; movies[numscheduled].itype = itype; va_start(ap, movie); for (i = 0; i < 5; ++i) movies[numscheduled].args[i] = va_arg(ap, int); va_end(ap); ++numscheduled; return TRUE; } void play_movies(SideMask sidemask) { int j, unitid, blasttype; Unit *unit; if (!side_in_set(dside, sidemask)) return; for (j = 0; j < numscheduled; ++j) { switch (movies[j].itype) { case movie_null: break; case movie_miss: case movie_hit: case movie_death: unitid = movies[j].args[0]; unit = find_unit(unitid); if (unit == NULL || !in_area(unit->x, unit->y)) continue; blasttype = (movies[j].itype == movie_miss ? 0 : (movies[j].itype == movie_hit ? 1 : 2)); draw_blast(unit->x, unit->y, 0); break; case movie_nuke: draw_blast(unit->x, unit->y, 1); break; default: break; } } numscheduled = 0; } void make_generic_image_data(ImageFamily *imf) { } #ifdef MAC int serial_port_dialog() { /* (should provide choice of modem vs printer port) */ return 0; } #endif /* MAC */ void unit_research_dialog(Unit *unit) { auto_pick_unit_research(unit); } void add_remote_locally(int rid, char *str) { } void send_chat(int rid, char *str) { }