/* echoarea.cc 1.11 95/12/28 23:24:28 */ // xspacewarp by Greg Walker (gow@math.orst.edu) // This is free software. Non-profit redistribution and/or modification // is allowed and welcome. // actions and other functions for handling data entry of shield // levels, faser/torpedo firing angles, coordinates of sectors // to leap to, game skill levels, self-destruct // codes. Implements a primitive terminal input with // backspacing. #include #include #include // isdigit() #include // sscanf() #include // strcmp() #include "common.hh" #include "params.hh" #include "globals.hh" #include "space_objects.hh" // enum Weapon #include "c_block.hh" #include "messages.hh" extern void make_universe(int); extern void draw_mission(void); #define MAXINPUT 4 // a minus sign, 3 digits, a NULL static Block cursor; static char inputstr[MAXINPUT+1]; // saves kbd input static int inputlen = 0; // tracks how many chars deposited in inputstr void echo(const char *, int); void clear_echo(void); // the input() action reads values for angles, sector coors, etc. void input(Widget w, XEvent *event, String *str, Cardinal *len) { char buf[2]; // hold XLookupString() result KeySym ksym; // not used // wrong states for kbd input if (gamestate.input == ACTION || gamestate.input == REPLAY || gamestate.visual == MISSION || gamestate.visual == ORIENTATION || gamestate.input == PAUSE) { return; } // key events only if ((event->type != KeyPress) && (event->type != KeyRelease)) return; if ((inputlen == MAXINPUT)) return; // for sector input, inputstr = 2 1-digit coors & NULL separator in between if ((gamestate.input == NEWSECTOR) && (inputlen == 3)) return; if ((gamestate.input == SHIELDLEVEL) && (inputlen == 3)) // 3 digits for percent return; if (XLookupString((XKeyEvent *)event, buf, 2, &ksym, NULL) != 1) return; inputstr[inputlen] = buf[0]; inputlen++; buf[1] = '\000'; // NULL terminate echo(buf, 1); // comma in between coords in echo region and a NULL between coors in inputstr if ((gamestate.input == NEWSECTOR) && (inputlen == 1)) { echo(",", 1); inputstr[inputlen] = '\000'; inputlen++; } } // for unreading data void backspace(Widget w, XEvent *event, String *str, Cardinal *len) { Point point; int newcol; // wrong states for kbd input if (gamestate.input == ACTION || gamestate.input == REPLAY || gamestate.visual == MISSION || gamestate.visual == ORIENTATION || gamestate.input == PAUSE) { return; } if (inputlen == 0) // no input to backspace over return; // graphically erase echoed data if ((gamestate.input == NEWSECTOR) && (inputlen == 2)) // erase comma { inputlen--; // backspace over NULL separator newcol = cursor.getcol() - 2; cursor.setcol(newcol); point = cursor.northwest(); XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC, point.x, point.y, 2*BLOCKW, BLOCKH); XFillRectangle(DISPLAY, pixmap, defrv_GC, point.x, point.y, 2*BLOCKW, BLOCKH); } else { newcol = cursor.getcol() - 1; cursor.setcol(newcol); point = cursor.northwest(); XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC, point.x, point.y, BLOCKW, BLOCKH); XFillRectangle(DISPLAY, pixmap, defrv_GC, point.x, point.y, BLOCKW, BLOCKH); } inputlen--; // delete data from buffer } // this action does a lot. it produces the appropriate response // when the end-of-input key (normally, "enter" or "return") is // pressed. void endinput(Widget w, XEvent *event, String *str, Cardinal *len) { int m, n, row, col, shields, skill; double deg; const double PI = 3.141592654; // wrong states for kbd input if (gamestate.input == ACTION || gamestate.input == REPLAY || gamestate.visual == MISSION || gamestate.visual == ORIENTATION || gamestate.input == PAUSE) { return; } // extract input from inputstr inputstr[inputlen] = '\000'; // data gets interpreted differently according to the gamestate.input // setting. gamestate.input indicates why the data was being entered. switch (gamestate.input) { case CODE: // user entered a self-destruct code if (strcmp(inputstr, SDESTRUCTCODE)) // correct destruct code? break; endever.selfdestruct(); return; break; case FASERANGLE: // user entered a faser firing angle if (sscanf(inputstr, "%lf", °) <= 0) endever.shoot(FASER, DEFANGLE); else endever.shoot(FASER, PI * deg / (double)180.0); // convert to radians break; case NEWSECTOR: // user entered coordinates of a sector to leap to if (inputlen != 3) // missing a coordinate break; m = sscanf(&inputstr[0], "%d", &row); n = sscanf(&inputstr[2], "%d", &col); if ((m <= 0) || (n <= 0)) break; if ((row < 1) || (row > UROWS) || (col < 1) || (col > UCOLS)) break; endever.leap(row, col); // gamestate.visual is set within endever.leap() if (universe[row-1][col-1].getbhpop()) // warning message if blackhole { clear_echo(); echo(bh_warning_str, XtNumber(bh_warning_str)); gamestate.input = ACTION; // restore keys: stop data entry keybindings return; } break; case SHIELDLEVEL: // user entered a shield level setting m = sscanf(inputstr, "%d", &shields); if ((m <= 0) || (shields < 0) || (shields > 100)) // a percent break; endever.setshields(shields); break; case SKILL: // user entered a skill level m = sscanf(inputstr, "%d", &skill); if ((m <= 0) || (skill < MINSKILL) || (skill > MAXSKILL)) { m = inputlen; for (n = 0; n < m; n++) // try again till get it right backspace(NULL, NULL, NULL, NULL); return; } gamestate.skill = skill; make_universe(skill); clear_echo(); // reset cursor for next use draw_mission(); gamestate.visual = MISSION; return; case TORPANGLE: // user entered a torpedo firing angle if (sscanf(inputstr, "%lf", °) <= 0) endever.shoot(TORPEDO, DEFANGLE); else endever.shoot(TORPEDO, PI * deg / (double)180.0); // convert to radians break; } clear_echo(); gamestate.input = ACTION; // restore keys: stop data entry keybindings } // draw a string in the echoareas (lower left) of both the // pixmap and the widget. len = string length w/o the NULL. void echo(const char *str, int len) { Point point; int newcol; // echo input string point = cursor.origin(); XDrawString(DISPLAY, XtWindow(widget), def_GC, point.x, point.y, str, len); XDrawString(DISPLAY, pixmap, def_GC, point.x, point.y, str, len); // update cursor newcol = cursor.getcol() + len; cursor.setcol(newcol); } // initialize the cursor using the geometry info in app_data void init_echo(void) { cursor.setrow(ECHOROW); cursor.setcol(ECHOCOL); } // erase the echo area in both pixmap and the widget. void clear_echo(void) { Point point; int width; inputlen = 0; // reset for next use if (cursor.getcol() == ECHOCOL) // nothing in echo area return; width = BLOCKW*(cursor.getcol() - ECHOCOL); cursor.setcol(ECHOCOL); // reset cursor position point = cursor.northwest(); XFillRectangle(DISPLAY, XtWindow(widget), defrv_GC, point.x, point.y, width, BLOCKH); XFillRectangle(DISPLAY, pixmap, defrv_GC, point.x, point.y, width, BLOCKH); } // end