matt@aplvax.UUCP (05/11/84)
This is the source code to ve(6) - a screen oriented empire tool (set net.games.emp). Compile as cc -o ve ve.c -O -lcurses -ltermcap matthew diaz Cut here: ---------------------------8<------------------------------------- /* * V E * * Visual Empire (with due respect to Peter Langston) * * Written by Matthew Diaz and Michael Baldwin * * Syntax: * ve [-a|c] census commodity map spy.... */ #include <ctype.h> #include <stdio.h> #include <curses.h> #include <signal.h> /* Things you may want to change */ #define VIPATH "/usr/ucb/vi" #define EXPATH "/usr/ucb/ex" #define MAPSIZE 64 #define OFF MAPSIZE/2 #define MCOLS 79 /* Width of the map window */ #define MLINES 17 #define NOX 0 /* Expand flags for getline */ #define EX 1 #define NOSU 0 /* Survey or no survey for mapdr */ #define SURV 1 #define ESC '\033' #define ODD(x) ((x)&01) #define EVEN(x) (!((x)&01)) #define VALID(x, y) (EVEN(x)&&EVEN(y)||ODD(x)&&ODD(y)) #define CTRL(c) (('c')-0100) /* Offsets into sector arrays for various items */ #define CIV 0 #define MIL 1 #define FOOD 2 #define SH 3 #define GUN 4 #define PL 5 #define IRON 6 #define DUST 7 #define BAR 8 #define OIL 9 #define LCM 10 #define HCM 11 #define EFF 12 #define MOB 13 #define MIN 14 #define GMIN 15 #define FERT 16 #define PET 17 #define DES 18 #define COU 19 #define CONT 20 #define DEL 21 char usage[] = "Usage: ve [-c|a out] files..."; /* * If you want accounting change the filename below (make sure * it is in 622 at least). Everytime someone plays that version * a line will be entered showing login name, elapsed time and * date */ #ifdef ACCTNG char acct[] = "/v0/usr/matt/.c/games/ve/veacct"; #endif struct sector { int x, y; /* Sector coordinates */ char surv; /* Survey value */ char mark; /* Mark character */ char own; /* Do you own or know that sector? */ int val[30]; /* Values as described by items */ char del[12]; /* Deliver routes */ char cnt[12]; /* Contracts */ } map[MAPSIZE][MAPSIZE]; struct item { char *nm; /* Item name - prefix */ int len; /* Length of prefix (for strncmp) */ } items[] = { "civ", 3, "mil", 3, "foo", 3, "sh", 2, "gun", 3, "pl", 2, "iro", 3, "dus", 3, "bar", 3, "oil", 3, "lcm", 3, "hcm", 3, "eff", 3, "mob", 3, "min", 3, "gmi", 3, "fer", 3, "pet", 3, "des", 3, "cou", 3, "con", 3, "del", 3, "", 0 }; int docen(); int domap(); int docom(); int dospy(); int doint(); struct funsw { /* list of prefixes and functions */ char *type; int (*func)(); } fsw[] = { "cen", docen, "map", domap, "com", docom, "spy", dospy, 0, 0, }; char macros[127][BUFSIZ]; /* Les macros */ char peekc; /* Lets you poke a character */ int startx; /* Starting and last x values on map */ int starty; int minx; /* Min and max used for map reading */ int maxx; int miny; int curmark = '>'; /* Current marking character */ int range = 10; /* Range for survey */ int curx, cury; /* Current x and y coordinates in map array */ FILE *outf; /* Output file pointer */ char oname[BUFSIZ]; /* Output file name */ main(argc, argv) int argc; char *argv[]; { struct funsw *fp; char buf[BUFSIZ]; /* Line buffer */ FILE *inpf; /* Input file pointer */ #ifdef ACCTNG FILE *actf; /* Accounting file */ long sclock; long eclock; #endif if (argc == 1) puts(usage), exit(1); #ifdef ACCTNG time(&sclock); #endif argv++; while (*argv) { /* Open/create command file */ if (**argv == '-' && (argv[0][1] == 'c'||argv[0][1] == 'a')) { if ((outf = fopen(*++argv, (argv[0][1] == 'c') ? "w" : "a")) == NULL) perror(*argv), exit(1); strcpy(oname, *argv++); continue; } /* * Process empire report files. This is * done by looking at the second line of the file * which contains a string of the form * cen ... (e.g. cen # >cen.out) * The first word indicates the type of file it * is (census, map, etc). */ if ((inpf = fopen(*argv, "r")) == NULL) perror(*argv), exit(1); if (fgets(buf, sizeof buf, inpf) == NULL || fgets(buf, sizeof buf, inpf) == NULL) printf("%s: wrong format\n", *argv), exit(1); for (fp = fsw; *fp->type; fp++) if (!strncmp(fp->type, buf, strlen(fp->type))) (fp->func)(inpf); fclose(inpf); argv++; } startx = minx + OFF; /* Set to upper left corner of map */ if (ODD(startx)) startx--; starty = miny + OFF; if (ODD(starty)) starty--; signal(SIGINT, SIG_IGN); /* Ignore common signals */ signal(SIGQUIT, SIG_IGN); initscr(); /* start up curses etc. */ startit(); commands(); /* process all commands */ move(LINES-1, 0); /* done - move to bottom of screen */ clrtoeol(); refresh(); #ifdef ACCTNG /* save accounting information */ if ((actf = fopen(acct, "a")) != NULL) { time(&eclock); if (!getpw(getuid(), buf) && (bp = index(buf, ':'))) *bp = 0; else sprintf(buf, "%d", getuid()); eclock -= sclock; fprintf(actf, "%s\t%2d:%02d\t%s", buf, eclock/60, eclock % 60, ctime(&sclock)); fclose(actf); } #endif endit(); /* reset terminal */ } /* * Input file processing functions * * These routines parse the information from the input files, * many times in a very ugly way. * * Each routine checks to see if there is a comma in the 4th position. * This is to make certain that the line is of the form x,y and not * another message (e.g. Bad weather). */ /* * docen - Read in the census file */ docen(inpf) FILE *inpf; { register int x, y; char buf[BUFSIZ]; struct sector *mp; while (fgets(buf, sizeof buf, inpf) != NULL) { if (buf[3] != ',') continue; x = atoi(buf); y = atoi(&buf[4]); mp = &map[x+OFF][y+OFF]; mp->own = 1; mp->x = x; mp->y = y; mp->del[CIV] = buf[19]; mp->del[MIL] = buf[20]; mp->del[FOOD] = buf[21]; mp->cnt[CIV] = buf[23]; mp->cnt[MIL] = buf[24]; mp->cnt[FOOD] = buf[25]; mp->val[COU] = -1; mp->val[DES] = (buf[8]=='-') ? '~' : ((buf[8]=='^') ? '&' : buf[8]); mp->val[EFF] = atoi(&buf[10]); mp->val[MOB] = atoi(&buf[15]); sscanf(&buf[31], "%d%d%d%d%d%d%d", &mp->val[CIV], &mp->val[MIL], &mp->val[FOOD], &mp->val[MIN], &mp->val[GMIN], &mp->val[FERT], &mp->val[PET]); } } /* * domap - Do the map file. This is a real kludge where you * try to figure out where you are on the map by * looking at the coordinates on the side. */ domap(inpf) FILE *inpf; { register int x, y; int sign; char minb[5]; /* Number buffers for min and max x */ char maxb[5]; char *sp = minb; char *lp = maxb; char buf[BUFSIZ]; char *bp; fgets(buf, sizeof buf, inpf); /* Now determine the minimum x value */ *sp++ = buf[4]; *lp++ = buf[strlen(buf) - 2]; sign = (index(buf, '-')) ? -1 : 1; fgets(buf, sizeof buf, inpf); *sp++ = buf[4]; *lp++ = buf[strlen(buf) - 2]; *sp = 0; *lp = 0; minx = atoi(minb); maxx = atoi(maxb); if (sign == -1) { if (minx > 0) minx *= -1; } else if (minx > maxx) { maxx *= -1; minx *= -1; } fgets(buf, sizeof buf, inpf); miny = atoi(buf); for (y = miny; buf[2] != ' '; y++) { for (bp = &buf[4], x = minx; x <= maxx; x++, bp++) { if (!map[x+OFF][y+OFF].val[DES]) map[x+OFF][y+OFF].val[DES] = *bp; } fgets(buf, sizeof buf, inpf); } } /* * docom - Do the commodities */ docom(inpf) FILE *inpf; { register int x, y; char buf[BUFSIZ]; struct sector *mp; while (fgets(buf, sizeof buf, inpf) != NULL) { if (buf[3] != ',') continue; x = atoi(buf); y = atoi(&buf[4]); mp = &map[x+OFF][y+OFF]; mp->own = 1; sscanf(&buf[14], "%c%c%c%c%c%c%c%c%c", &mp->del[SH], &mp->del[GUN], &mp->del[PL], &mp->del[IRON], &mp->del[DUST], &mp->del[BAR], &mp->del[OIL], &mp->del[LCM], &mp->del[HCM]); sscanf(&buf[24], "%c%c%c%c%c%c%c%c%c", &mp->cnt[SH], &mp->cnt[GUN], &mp->cnt[PL], &mp->cnt[IRON], &mp->cnt[DUST], &mp->cnt[BAR], &mp->cnt[OIL], &mp->cnt[LCM], &mp->cnt[HCM]); sscanf(&buf[34], "%d%d%d%d%d%d%d%d%d", &mp->val[SH], &mp->val[GUN], &mp->val[PL], &mp->val[IRON], &mp->val[DUST], &mp->val[BAR], &mp->val[OIL], &mp->val[LCM], &mp->val[HCM]); } } /* * dospy - Do the spy reports */ dospy(inpf) FILE *inpf; { register int x, y; char buf[BUFSIZ]; struct sector *mp; while (fgets(buf, sizeof buf, inpf) != NULL) { if (buf[3] != ',') continue; x = atoi(buf); y = atoi(&buf[4]); mp = &map[x+OFF][y+OFF]; if (mp->own) continue; mp->own = 1; mp->val[COU] = atoi(&buf[8]); if (buf[14] == 'N') /* ignore "No report... */ continue; mp->val[DES] = buf[11]; mp->val[EFF] = atoi(&buf[13]); sscanf(&buf[18], "%d%d%d%d%d%d%d", &mp->val[CIV], &mp->val[MIL], &mp->val[SH], &mp->val[GUN], &mp->val[IRON], &mp->val[PL], &mp->val[FOOD]); } } /* * commands - Process the various input commands */ commands() { register char c; register int x, y; /* Indexes into the map array */ register int i; register struct item *ip; int tx, ty; int crou = -1; /* current route */ int status; int pflg = 1; /* print census flag */ int surmap = 0; /* Set if surmap map should be displayed */ int update = 0; char *bp; char buf[BUFSIZ]; char prbuf[BUFSIZ]; x = y = OFF; /* Initialize x and y */ mapdr(NOSU); /* Draw map, census header and census */ cenhd(); pcen(y, x); move(y-starty, x-startx); while (refresh(), ((c = getac()) != 'q')) { update = 0; curx = x; cury = y; switch(c) { case 'y': /* Movement commands */ if (x > 0 && y > 0) { x--; y--; } break; case 'u': if (x < MAPSIZE - 2 && y > 0) { x++; y--; } break; case 'j': if (x < MAPSIZE - 3) x += 2; break; case 'n': if (x < MAPSIZE - 2 && y < MAPSIZE - 2) { x++; y++; } break; case 'b': if (x > 0 && y < MAPSIZE - 2) { x--; y++; } break; case 'g': if (x > 1) x -= 2; break; case CTRL(B): case CTRL(N): if (y < MAPSIZE - 7) y += 6; else if (y < MAPSIZE - 3) y += 2; break; case CTRL(Y): case CTRL(U): if (y > 5) y -= 6; else if (y > 1) y -= 2; break; case CTRL(G): if (x > 5) x -= 6; else if (x > 1) x -= 2; break; case CTRL(J): if (x < MAPSIZE - 7) x += 6; else if (x < MAPSIZE - 3) x += 2; break; case '\f': /* Redraw the screen */ clear(); mapdr(surmap); cenhd(); break; case '?': /* Do query at bottom */ doquest(); mapdr(surmap); break; case 'C': case 'c': /* Clear marks */ clearmks(c=='C'); mapdr(surmap); break; case 'M': /* Reset mark */ curmark = '>'; break; case 'm': /* Change mark char */ getline(buf, "mark: ", NOX); if (*buf) curmark = *buf; break; case 'P': /* Turn off census refresh */ pflg = (pflg == 0); move(LINES-1, 0); clrtoeol(); printw("Printing %s", pflg ? "on" : "off"); break; case 'a': /* Append to the file */ if (outf) { getline(buf, "", EX); if (*buf) { strcat(buf, "\n"); fputs(buf, outf); } } else putline("No output file specified - use O"); break; case 'O': /* Change/create output file */ getline(buf, "New output file: ", NOX); if (outf) fclose(outf); if ((outf = fopen(buf, "a")) == NULL) putline("%s: cannot create", buf); else strcpy(oname, buf); break; case 's': /* Set a macro */ getline(buf, "macro name: ", NOX); getline(macros[*buf], "define: ", NOX); break; case 'd': /* Delete a macro */ getline(buf, "delete macro: ", NOX); *macros[*buf] = 0; break; case 'V': /* Call vi */ case 'E': /* Call ex */ if (outf) { fclose(outf); if (fork() == 0) { endit(); if (c == 'V') execl(VIPATH, "vi", oname, 0); else execl(EXPATH, "ex", oname, 0); } else wait(&status); startit(); clear(); mapdr(surmap); cenhd(); if ((outf = fopen(oname, "a")) == NULL) putline("Cannot reopen %s", oname); } else putline("No output file"); break; case 'S': /* Survey */ getline(buf, "Survey: ", NOX); survey(buf); mapdr(surmap = SURV); break; case 'R': /* Range for Survey */ sprintf(prbuf, "Range (%d): ", range*10); getline(buf, prbuf, NOX); if (*buf) { range = atoi(buf)/10; if (range < 1) { putline("range should be >= 10"); range = 10; } } break; case CTRL(F): /* Control F - flip maps */ mapdr((surmap = (surmap == NOSU))); break; case 'r': /* route */ getline(buf, "Route: "); for (crou = -1, ip = items; ip->len; ip++) if (!strncmp(buf, ip->nm, ip->len)) crou = ip - items; if (crou < 0) putline("I don't know about %s", buf); break; case 'w': /* walk along route */ if (crou > -1 && map[x][y].del[crou] != '.') peekc = map[x][y].del[crou]; break; case 'p': /* Print census */ break; case 'l': getline(buf, "Leap to: "); if (*buf && (bp = (char *)index(buf, ','))) { tx = atoi(buf) + OFF; ty = atoi(++bp) + OFF; if (tx > 0 && tx < MAPSIZE && ty > 0 && ty < MAPSIZE && VALID(tx, ty)) { x = tx; y = ty; } else putline("Illegal coordinates"); } break; } if (x < startx || x > startx+MCOLS) { startx = (x < startx) ? x : x-MCOLS; update++; } if (y < starty || y > starty+MLINES) { starty = (y < starty) ? y : y-MLINES; update++; } if (update) { mapdr(surmap); touchwin(stdscr); } if (pflg || c == 'p') pcen(y, x); move(y-starty, x-startx); } } /* * doquest - Parse the ? commands */ doquest() { int pass = 0; char buf[BUFSIZ]; char *bp = buf; char *tp; getline(bp, "?", NOX); if (!*bp) return; clearmks(0); for (;;) { if (tp = (char *)index(bp, '&')) { *tp++ = 0; mark(bp, pass); bp = tp; } else { mark(bp, pass); break; } pass++; } } /* * mark - Mark the chart according to the cmnd */ mark(sp, pass) char *sp; int pass; { register int itm1; register int itm2; register int x, y; register int num; register int val; register int markit; register struct item *ip; register struct item *tp; char cmd[20]; char *cp = cmd; struct sector *mp; while (isalpha(*cp++ = *sp++)); /* get first word */ *--cp = 0; if (!*--sp) return; for (ip = items; ip->len; ip++) /* check it */ if (!strncmp(cmd, ip->nm, ip->len)) break; if (!ip->len) { putline("I don't know about %s\n", cmd); return; } itm1 = ip - items; /* * at this point, cmd contains the left side, *sp * is the operator and sp+1 is the right side. */ num = (itm1 == DES) ? *(sp+1) : atoi(sp+1); if (itm1 == CONT || itm1 == DEL) { for (tp = items; tp->len; tp++) if (!strncmp(sp+1, tp->nm, tp->len)) break; if (!tp->len) { putline("I don't know about %s", cmd); return; } itm2 = tp - items; } for (y = 0; y < MAPSIZE; y++) for (x = 0; x < MAPSIZE; x++) { mp = &map[x][y]; if (!VALID(x, y) || !mp->own) continue; markit = 0; val = mp->val[itm1]; if (itm1 == CONT) { num = '$'; val = mp->cnt[itm2]; } else if (itm1 == DEL) { num = '.'; /* KLUDGE */ *sp = '#'; val = mp->del[itm2]; } switch( *sp) { case '=': if (val == num) markit++; break; case '#': if (val != num) markit++; break; case '>': if (val > num) markit++; break; case '<': if (val < num) markit++; break; } if (markit && (!pass || (pass && mp->mark))) mp->mark = curmark; else if (mp->mark == curmark) mp->mark = 0; } } /* * clearmks - Clear the marks. If all flag is set than ALL marks are * cleared otherwise just curmark marks are cleared. */ clearmks(all) int all; { register int x, y; for (y = 0; y < MAPSIZE; y++) for (x = 0; x < MAPSIZE; x++) if (all || map[x][y].mark == curmark) map[x][y].mark = 0; } /* * survey - Do a surveygram for the given parameter */ survey(sp) char *sp; { register struct item *ip; register int itm; register int x, y; register struct sector *mp; for (ip = items; ip->len; ip++) if (!strncmp(sp, ip->nm, ip->len)) break; if (!ip->len) { putline("I don't know about %s", sp); return; } itm = ip - items; for (y = 0; y < MAPSIZE; y++) for (x = 0; x < MAPSIZE; x++) if ((mp = &map[x][y])->own) { mp->surv = mp->val[itm]/range; if (mp->surv > 35) mp->surv = '$'; else mp->surv += (mp->surv>9)?('A'-10):'0'; } } /* * getline - Get a line from the bottom of the screen, * using pr as a prompt if non-zero * If ex is set, then expand macros */ getline(bp, pr, ex) char *bp; char *pr; int ex; { register int x; register int y; char c; char *mp; char *np; char nbuf[10]; /* Number buffer */ char *ip = bp; move(LINES-1, 0); clrtoeol(); if (*pr) addstr(pr); while (refresh(), (c = getch()) != '\r') { if (ex && *macros[c]) { /* check for macros */ mp = macros[c]; while (*mp) { if (*mp == '.') { /* expand . */ mp++; sprintf(np = nbuf, "%d,%d ", curx-OFF, cury-OFF); while (*ip++ = *np++); ip--; addstr(nbuf); } else addch( *ip++ = *mp++); } continue; } switch(c) { case '\b': /* backspace */ if (ip > bp) { ip--; addstr("\b \b"); } continue; case '\\': /* backslash */ addstr("\\\b"); refresh(); c = getch(); break; case '.': /* expand . */ sprintf(np = nbuf, "%d,%d ", curx-OFF, cury-OFF); while (*ip++ = *np++); ip--; addstr(nbuf); continue; case ESC: /* jump to current pos */ getyx(stdscr, y, x); move(cury-starty, curx-startx); refresh(); sleep(1); move(y, x); continue; case '\n': continue; case '@': /* erase the line */ move(LINES-1, 0); if (*pr) addstr(pr); clrtoeol(); *(ip = bp) = 0; continue; } addch(*ip++ = c); } *ip = 0; } /* * putline - Do a printw at the bottom of the screen */ putline(fmt, a1, a2, a3, a4) { move(LINES-1, 0); printw(fmt, a1, a2, a3, a4); } /* * cenhd - Redraw the census listing */ cenhd() { move(LINES-5, 0); addstr(" sect des eff mob cmf cmf % * "); addstr(" civ mil food min gmin fert oil"); move(LINES-3, 0); addstr("cou sgpidbolh sgpidbolh "); addstr("sh gun pl iron dust bar oil lcm hcm"); } /* * mapdr - Redraw the map */ mapdr(sflg) register int sflg; { register int x, y; register struct sector *mp; for (y = starty; y < MAPSIZE && y <= starty + MLINES; y++) for (x = startx; x < MAPSIZE && x < startx + MCOLS; x++) { if (!VALID(x, y)) continue; mp = &map[x][y]; mvaddch(y-starty, x-startx-1, (mp->mark)?mp->mark:' '); if (sflg && mp->surv) mvaddch(y-starty, x-startx, mp->surv); else if (mp->val[DES]) mvaddch(y-starty, x-startx, mp->val[DES]); } } /* * pcen - Print the census and commodities info */ pcen(y, x) register int y, x; { register struct sector *mp = &map[x][y]; move(LINES-2, 0); clrtoeol(); move(LINES-4, 0); clrtoeol(); printw("%3d,%-3d %c", x-OFF, y-OFF, mp->val[DES] ? mp->val[DES]:' '); if (mp->own) { printw("%6d%%%4d %c%c%c %c%c%c %4d%4d%5d%5d%5d%5d%4d", mp->val[EFF], mp->val[MOB], mp->del[CIV], mp->del[MIL], mp->del[FOOD], mp->cnt[CIV], mp->cnt[MIL], mp->cnt[FOOD], mp->val[CIV], mp->val[MIL], mp->val[FOOD], mp->val[MIN], mp->val[GMIN], mp->val[FERT], mp->val[PET]); move(LINES-2, 0); if (mp->val[COU] == -1) addstr(" "); else printw("%3d", mp->val[COU]); if (mp->del[SH]) printw(" %c%c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c%c", mp->del[SH], mp->del[GUN], mp->del[PL], mp->del[IRON], mp->del[DUST], mp->del[BAR], mp->del[OIL], mp->del[LCM], mp->del[HCM], mp->cnt[SH], mp->cnt[GUN], mp->cnt[PL], mp->cnt[IRON], mp->cnt[DUST], mp->cnt[BAR], mp->cnt[OIL], mp->cnt[LCM], mp->cnt[HCM]); else addstr(" "); printw("%4d%5d%4d%5d%5d%5d%5d%5d%5d", mp->val[SH], mp->val[GUN], mp->val[PL], mp->val[IRON], mp->val[DUST], mp->val[BAR], mp->val[OIL], mp->val[LCM], mp->val[HCM]); } } /* * survdr - Draw the survey map */ survdr() { register int x, y; for (y = starty; y < MAPSIZE && y <= starty + MLINES; y++) for (x = startx; x < MAPSIZE && x < startx + MCOLS; x++) { if (!VALID(x, y)) continue; if (map[x][y].surv) mvaddch(y-starty, x-startx, map[x][y].surv); } } /* * getac - Get a character. Return peekc if non-zero * otherwise read a character from the keyboard */ getac() { register char tc; if (peekc) { tc = peekc; peekc = 0; return(tc); } else return(getch()); } /* * startit - Set up the terminal */ startit() { crmode(); noecho(); nonl(); } /* * endit - Turn the terminal back to normal */ endit() { nl(); echo(); crmode(); endwin(); }