|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 19011 (0x4a43) Types: TextFile Names: »input.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/X/Xconq/input.c«
/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */ /* This program may be used, copied, modified, and redistributed freely */ /* for noncommercial purposes, so long as this notice remains intact. */ /* RCS $Header: input.c,v 1.1 88/06/21 12:30:17 shebs Exp $ */ /* To accommodate simultaneous movement by several players, our setup must */ /* be a little elaborate. Basically, the program keeps the initiative; */ /* when a side's movement phase needs input, it posts a "request" and a */ /* handler for that request. When X gets an event, data about it is */ /* preprocessed and then stuffed into the request structure. At the end */ /* of a movement subphase, the requests are fulfilled by calling the handler */ /* and passing it the side and a pointer to the request. */ /* The bad part about all this is that *every* *single* interaction must */ /* be handled this way - no more calling a routine to get a value and */ /* waiting until it is complete! :-( */ #include "config.h" #include "misc.h" #include "dir.h" #include "period.h" #include "side.h" #include "unit.h" #include "global.h" #include "map.h" /* Magic values (unlikely to show up as player input) */ #define DFLTFLAG (-12345) /* flags when default arg to be used */ #define NEGFLAG (-12346) /* flags when default arg to be used */ #define ITERFLAG (-12347) /* flags args that are "itertime" */ #define ESCAPE '\033' /* standard abort character */ #define BACKSPACE '\010' /* for fixing strings being entered */ /* The following structure is used only for the dispatch table. */ typedef struct func_tab { char fchar; /* character to match against */ int (*code)(); /* pointer to command's function */ int defaultarg; /* default for argument */ bool needunit; /* true if cmd always applies to a unit */ char *help; /* short documentation string */ } FuncTab; extern int giventime; /* flags use of chess clock */ /* Predeclarations of all commands. */ int do_sentry(), do_delay(), do_wakeup(), do_wakemain(), do_embark(), do_exit(), do_resign(), do_message(), do_follow(), do_redraw(), do_disband(), do_return(), do_sit(), do_product(), do_idle(), do_help(), do_version(), do_save(), do_standing(), do_ident(), do_moveto(), do_coast(), do_survey_mode(), do_unit_info(), do_printables(), do_occupant(), do_options(), do_name(), do_unit(), do_give_unit(), do_center(), do_mark_unit(), do_add_player(), do_patrol(), do_give(), do_take(); char dirchars[] = DIRCHARS; /* typable characters for directions */ /* The command table itself. Default iterations of ITERFLAG actually turn */ /* into the value of "itertime", which can be set by options cmd. */ FuncTab commands[] = { 's', do_sentry, ITERFLAG, TRUE, "put a unit on sentry duty", 'w', do_wakemain, 0, FALSE, "wake units up from whatever they were doing", ' ', do_sit, 1, FALSE, "(Space Bar) make unit be on sentry this turn only", 'r', do_return, 1, TRUE, "return unit to nearest city/transport", 'd', do_delay, 1, TRUE, "delay unit's move until later in turn", 'f', do_follow, ITERFLAG, TRUE, "follow a designated leader", 'm', do_moveto, 1, TRUE, "move to given location", 'z', do_survey_mode, 1, FALSE, "toggle between survey and move modes", 'a', do_occupant, 1, TRUE, "look at the occupants", 'e', do_embark, 1, TRUE, "embark units onto transport occupying same unit", 'x', do_mark_unit, 1, TRUE, "mark unit for later reference", 'g', do_give, -1, TRUE, "give supplies to the transport", 't', do_take, -1, TRUE, "take supplies from the transport", 'c', do_center, 1, FALSE, "designate current location as center of action", 'o', do_options, 5, FALSE, "set various options", 'p', do_printables, 1, FALSE, "put various data into files for printing", ' ', NULL, 0, 0, "", 'P', do_product, 1, TRUE, "set/change unit production", 'I', do_idle, ITERFLAG, TRUE, "set unit to not produce anything", 'W', do_wakeup, 0, FALSE, "wake ALL units in area, including occupants", 'D', do_disband, 1, TRUE, "disband a unit and send it home", 'F', do_coast, ITERFLAG, TRUE, "follow a coastline", 'Z', do_patrol, ITERFLAG, TRUE, "go on a patrol", 'O', do_standing, 1, TRUE, "set standing orders for occupants", 'G', do_give_unit, -2, TRUE, "turn unit over to another side", 'M', do_message, -2, FALSE, "send a message to other sides", 'C', do_name, 1, FALSE, "call a unit or side by a name", 'X', do_resign, -2, FALSE, "resign from the game", 'Q', do_exit, 1, FALSE, "kill game for all players", 'S', do_save, 1, FALSE, "save the game into a file", 'A', do_add_player, 1, FALSE, "add a new player to the game", '?', do_help, 1, FALSE, "display help info", '/', do_ident, 1, FALSE, "identify things on screen", '=', do_unit_info, 1, FALSE, "display details about a type of unit", 'V', do_version, 1, FALSE, "display program version", '\\', do_unit, 1, FALSE, "build a new unit (Build mode only)", '\022', do_redraw, 1, FALSE, "(^R) redraw screen", '\014', do_redraw, 1, FALSE, "(^L) redraw screen", '\0', NULL, 0, FALSE, NULL /* end of table marker */ }; /* Just to get everything off on the right foot. */ init_requests(side) Side *side; { side->reqvalue = DFLTFLAG; } /* Post a request - need only know the handler that will be called to */ /* fulfill the request later, and sometimes a relevant unit. */ request_input(side, unit, handler) Side *side; Unit *unit; int (*handler)(); { if (Debug) printf("Making %s request ", side->name); if (!humanside(side)) fprintf(stderr, "Gack!\n"); if (!side->reqactive) { if (Debug) printf("(accepted)\n"); side->reqactive = TRUE; side->reqhandler = handler; side->reqtype = GARBAGE; side->requnit = unit; /* any other data slots are setup by callers of this routine */ } else { if (Debug) printf("(ignored)\n"); } } /* Make a request go away. Careful about using this! */ cancel_request(side) Side *side; { if (side->reqactive) { if (Debug) printf("Canceling %s request\n", side->name); side->reqactive = FALSE; erase_cursor(side); clear_info(side); flush_output(side); } } /* Handle all requests at once. This routine *will* wait forever for */ /* input (polling is evil). This is also the right place to draw the */ /* unit cursor, since it is unwanted unless we are waiting on input. */ handle_requests() { bool waiters = FALSE; Side *side; for_all_sides(side) { if (side->reqactive) { waiters = TRUE; /* wasteful to call this for everybody... */ show_info(side); draw_cursor(side); } } if (waiters) { get_input(); for_all_sides(side) { if (side->reqactive && side->reqtype != GARBAGE) { if (Debug) printf("Fulfilling %s request with input type %d\n", side->name, side->reqtype); side->reqactive = FALSE; (*(side->reqhandler))(side); erase_cursor(side); clear_info(side); flush_output(side); } } } } /* Acquire a command from the player. Command may be prefixed with a */ /* nonnegative number which is given as an argument to the command function */ /* (otherwise arg defaults to value in third column of command table). */ /* This routine takes in both keyboard and mouse input, other kinds of */ /* devices should be handled here also (rriiiight...). Don't do any of */ /* this if the clock ran out, just supply an innocuous command. */ x_command(side) Side *side; { int dir, terr, sign = 1; switch (side->reqtype) { case KEYBOARD: if (Debug) printf("%s keyboard input: %c (%d)\n", side->name, side->reqch, side->reqvalue); if (isdigit(side->reqch)) { if (side->reqvalue == DFLTFLAG) { side->reqvalue = 0; } else if (side->reqvalue == NEGFLAG) { side->reqvalue = 0; sign = -1; } else { side->reqvalue *= 10; } side->reqvalue += (side->reqvalue >= 0 ? 1 : -1) * (side->reqch - '0'); side->reqvalue *= sign; sprintf(side->promptbuf, "Arg: %d", side->reqvalue); show_prompt(side); request_input(side, side->curunit, x_command); return; } else { clear_prompt(side); if (Build && ((terr = find_terr(side->reqch)) >= 0)) { if (side->reqvalue == DFLTFLAG) side->reqvalue = 0; do_terrain(side, terr, side->reqvalue); } else if (side->reqch == '-') { side->reqvalue = NEGFLAG; request_input(side, side->curunit, x_command); return; } else if ((dir = iindex(side->reqch, dirchars)) >= 0) { if (side->reqvalue == DFLTFLAG) side->reqvalue = 1; do_dir(side, dir, side->reqvalue); } else if ((dir = iindex(lowercase(side->reqch), dirchars)) >= 0) { if (side->reqvalue == DFLTFLAG) side->reqvalue = side->itertime; if (side->mode == SURVEY) side->reqvalue = 10; do_dir(side, dir, side->reqvalue); } else { execute_command(side, side->reqch, side->reqvalue); } } break; case MAPPOS: if (Debug) printf("%s map input: %d,%d (%d)\n", side->name, side->reqx, side->reqy, side->reqvalue); clear_prompt(side); if (side->reqx == side->curx && side->reqy == side->cury) { if (side->curunit) do_sit(side, 1); break; } else { if (side->teach) { cache_moveto(side, side->reqx, side->reqy); } else { switch (side->mode) { case MOVE: if (side->curunit) { order_moveto(side->curunit, side->reqx, side->reqy); side->curunit->orders.flags &= ~SHORTESTPATH; } break; case SURVEY: move_survey(side, side->reqx, side->reqy); break; default: case_panic("mode", side->mode); } } } break; default: break; } if (giventime && !side->timedout && side->timeleft <= 0) { side->timedout = TRUE; notify(side, "You ran out of time!!"); beep(side); } /* Reset the arg so we don't get confused next time around */ side->reqvalue = DFLTFLAG; } /* The requester proper, which just sets up the hook to call the main */ /* command interpreter when some input comes by. */ request_command(side) Side *side; { request_input(side, side->curunit, x_command); } /* Search in command table and execute function if found, complaining if */ /* the command is not recognized. Many commands operate on the "current */ /* unit", and all uniformly error out if there is no current unit, so put */ /* that test here. Also fix up the arg if the value passed is one of the */ /* specially recognized ones. */ execute_command(side, ch, n) Side *side; char ch; int n; { struct func_tab *cmd; for (cmd = commands; cmd->fchar != '\0'; ++cmd) { if (ch == cmd->fchar) { if (n == DFLTFLAG) n = cmd->defaultarg; if (n == NEGFLAG) n = 0 - cmd->defaultarg; if (n == ITERFLAG) n = side->itertime; if (Debug) printf("... actual arg is %d\n", n); if (cmd->needunit) { if (side->curunit != NULL) { (*(cmd->code))(side, side->curunit, n); } else { cmd_error(side, "No unit to operate on here!"); } } else { (*(cmd->code))(side, n); } return; } } cmd_error(side, "Unknown command '%c'", ch); } /* Help for the main command mode just dumps part of the table, and a little */ /* extra info about what's not in the table. This may go onto a screen or */ /* into a file, depending on where this was called from. */ command_help(side) Side *side; { FuncTab *cmd; wprintf(side, "To move a unit, use the mouse or [hlyubn]"); wprintf(side, "[HLYUBN] moves unit repeatedly in that direction"); wprintf(side, "Mousing unit makes it sit, mousing enemy unit attacks"); if (Build) wprintf(side, "Terrain characters set what will be painted."); wprintf(side, " "); for (cmd = commands; cmd->fchar != '\0'; ++cmd) { wprintf(side, "%c %s", cmd->fchar, cmd->help); } } /* The handler for unit production needs to make sure valid input has */ /* been received, will put in another request if not. */ x_product_type(side) Side *side; { int u; if ((u = grok_unit_type(side)) >= 0) { set_product(side->requnit, u); set_schedule(side->requnit); } else { request_input(side, side->requnit, x_product_type); } } /* This is called when production is to be set or changed. Note that since */ /* the user has a pretty dull choice if there is only one possible type of */ /* unit to build, in such cases we can bypass requests altogether. */ request_new_product(unit) Unit *unit; { int u; Side *us = unit->side; if (humanside(us)) { sprintf(spbuf, "%s will build: ", unit_handle(us, unit)); u = ask_unit_type(us, spbuf, utypes[unit->type].make); if (u < 0) { make_current(us, unit); request_input(us, unit, x_product_type); } else { set_product(unit, u); set_schedule(unit); } } else { set_product(unit, machine_product(unit)); set_schedule(unit); } } /* 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. */ ask_unit_type(side, prompt, possibles) Side *side; char *prompt; short *possibles; { int numtypes = 0, u, type; for_all_unit_types(u) { side->bvec[u] = 0; if (possibles == NULL || possibles[u]) { side->bvec[u] = 1; side->ustr[numtypes] = utypes[u].uchar; side->uvec[numtypes] = u; numtypes++; } } if (numtypes == 0) { type = NOTHING; } else if (numtypes == 1) { type = side->uvec[0]; side->bvec[type] = 0; } else { side->ustr[numtypes] = '\0'; sprintf(side->promptbuf, "%s [%s] ", prompt, side->ustr); show_prompt(side); draw_unit_list(side, side->bvec); type = -1; } return type; } /* Do something with the char or unit type that the player entered. */ grok_unit_type(side) Side *side; { int i, type = -1; switch (side->reqtype) { case KEYBOARD: echo_at_prompt(side, side->reqch); if (side->reqch == '?') { help_unit_type(side); } else if (side->reqch == ESCAPE) { type = NOTHING; } else if (iindex(side->reqch, side->ustr) == -1) { notify(side, "Unit type '%c' not in \"%s\"!", side->reqch, side->ustr); } else { type = find_unit_char(side->reqch); } break; case UNITTYPE: if (between(0, side->requtype, period.numutypes-1) && side->bvec[side->requtype]) { type = side->requtype; } else { notify(side, "Not a valid unit type!"); } break; default: break; } if (type >= 0) { clear_prompt(side); for_all_unit_types(i) side->bvec[i] = 0; draw_unit_list(side, side->bvec); } return type; } /* User is asked to pick a position on map. This will iterate until the */ /* space bar designates the final position. */ ask_position(side, prompt) Side *side; char *prompt; { sprintf(side->promptbuf, "%s [mouse or keys to move, space bar to set]", prompt); show_prompt(side); side->tmpcurx = side->curx; side->tmpcury = side->cury; side->tmpcurunit = side->curunit; } /* Restore the saved "cur" slots. */ restore_cur(side) Side *side; { side->curx = side->tmpcurx; side->cury = side->tmpcury; side->curunit = side->tmpcurunit; } /* Interpret the user's input in response to a position request. All we */ /* have to comprehend is direction keys and mouse hits. Space bars and */ /* unmoving mice both mean that a position has been decided on. */ grok_position(side) Side *side; { int dir; switch (side->reqtype) { case KEYBOARD: if (side->reqch == ' ') { clear_prompt(side); return TRUE; } else if ((dir = iindex(side->reqch, dirchars)) >= 0) { side->reqposx = wrap(side->reqposx + dirx[dir]); side->reqposy = side->reqposy + diry[dir]; } else if ((dir = iindex(lowercase(side->reqch), dirchars)) >= 0) { side->reqposx = wrap(side->reqposx + 10*dirx[dir]); side->reqposy = side->reqposy + 10*diry[dir]; } break; case MAPPOS: if (side->reqx == side->reqposx && side->reqy == side->reqposy) { clear_prompt(side); return TRUE; } else { side->reqposx = side->reqx; side->reqposy = side->reqy; } break; default: break; } side->curx = side->reqposx; side->cury = side->reqposy; side->curunit = NULL; show_info(side); return FALSE; } /* Prompt for a yes/no answer with a settable default. */ ask_bool(side, question, dflt) Side *side; char *question; bool dflt; { sprintf(side->promptbuf, "%s [%s]", question, (dflt ? "yn" : "ny")); show_prompt(side); side->reqvalue2 = dflt; } /* Figure out what the answer actually is, keeping the default in mind. */ grok_bool(side) Side *side; { bool dflt = side->reqvalue2; char ch = side->reqch; if (side->reqtype == KEYBOARD) { if (dflt ? (lowercase(ch) == 'n') : (lowercase(ch) == 'y')) dflt = !dflt; } clear_prompt(side); return dflt; } /* Prompt for a single character. */ ask_char(side, question, choices) Side *side; char *question, *choices; { sprintf(side->promptbuf, "%s [%s]", question, choices); show_prompt(side); } /* The char has already been processed, so just pass it through. */ grok_char(side) Side *side; { if (side->reqtype == KEYBOARD) { clear_prompt(side); return side->reqch; } else { return '\0'; } } /* Read a string from the prompt window. Deletion is allowed, and a */ /* cursor is displayed (this should definitely be a toolkit call...) */ /* Some restrictions on what strings can be read - for instance can't */ /* read or default to a NULL string. */ ask_string(side, prompt, dflt) Side *side; char *prompt, *dflt; { if (dflt == NULL) { sprintf(side->promptbuf, "%s ", prompt); } else { sprintf(side->promptbuf, "%s (default \"%s\") ", prompt, dflt); } show_prompt(side); side->reqstrbeg = strlen(side->promptbuf); side->reqcurstr = side->reqstrbeg; write_str_cursor(side); side->reqdeflt = dflt; } /* Dig a character from the filled-in request and add it into the string. */ /* Keep returning NULL until we get something. */ char * grok_string(side) Side *side; { if (side->reqtype == KEYBOARD) { if (side->reqch == '\r' || side->reqch == '\n' || side->reqcurstr >= BUFSIZE-2) { if (side->reqcurstr == side->reqstrbeg && side->reqdeflt != NULL) { strcpy(side->promptbuf+side->reqstrbeg, side->reqdeflt); } else { (side->promptbuf)[side->reqcurstr] = '\0'; } clear_prompt(side); return copy_string(side->promptbuf + side->reqstrbeg); } else { if (side->reqch == BACKSPACE) { if (side->reqcurstr > side->reqstrbeg) --side->reqcurstr; } else { echo_at_prompt(side, side->reqch); (side->promptbuf)[side->reqcurstr++] = side->reqch; } } write_str_cursor(side); (side->promptbuf)[side->reqcurstr+1] = '\0'; } return NULL; }