/* Melee fleet randomizer for the Star Control II rerelease, * Ur-Quan Masters. * * (c) 1996, 2002 Juho Snellman */ #include #include #include #include #include #include enum { MAX_FLEET = 14 }; enum { FLEET_ARRAY_SIZE = 70 }; typedef struct { int id; int price; char name[9]; } ship_s; typedef struct { char *name; int cost; } fleet_s; static int SHIPS = 25; ship_s ships[] = { { 13, 5, "shofixti" }, { 24, 6, "zoqfot" }, { 19, 7, "umgah" }, { 18, 10, "thradd" }, { 6, 10, "ilwrath" }, { 5, 11, "human" }, { 22, 12, "vux" }, { 17, 13, "syreen" }, { 0, 15, "androsyn" }, { 1, 16, "arilou" }, { 16, 16, "supox" }, { 14, 17, "slylandr" }, { 4, 17, "druuge" }, { 15, 18, "spathi" }, { 8, 18, "melnorme" }, { 9, 19, "mmrnmhrm" }, { 10, 21, "mycon" }, { 12, 20, "pkunk" }, { 21, 22, "utwig" }, { 11, 23, "orz" }, { 23, 23, "yehat" }, { 2, 28, "chenjesu" }, { 3, 30, "chmmr" }, { 20, 30, "urquan" }, { 7, 30, "blackurq" }, }; void die (char* format, ...) { va_list argp; va_start(argp, format); vfprintf(stderr, format, argp); va_end(argp); exit(EXIT_FAILURE); } char *fleet_name(char *fleet) { fprintf(stderr, "%s\n", fleet); return strdup(&fleet[MAX_FLEET]); } int random_ship (int min_cost, int max_cost) { int top = 0; int bottom = 0; if (min_cost > max_cost) { die("Internal error. Tried to create ship of max_cost %d, min_cost %d\n", max_cost, min_cost); } for (; top < SHIPS; top++) { if (ships[top].price < min_cost) bottom++; if (ships[top].price > max_cost) break; } if (top == 0) { die("Internal error. Tried to create ship of cost %d\n", max_cost); } return rand() % (top-bottom) + bottom; } int find_cheap_slots (int fleet[MAX_FLEET], int cheap_slots[MAX_FLEET]) { int i = 0; int j = 0; for (i = 0; i < MAX_FLEET; i++) { if (ships[fleet[i]].price < ships[SHIPS-1].price) cheap_slots[j++] = i; } return j; } void generate_ships (char *fleet_str, int limit) { int gen = 0; int cost = 0; int fleet[MAX_FLEET]; int j; // fprintf(stderr, "New fleet (%d)\n", limit); while (gen < MAX_FLEET && cost < limit - ships[0].price) { int i = random_ship(0, limit - cost); fleet[gen] = i; cost += ships[i].price; // fprintf(stderr, "Created '%s'\n", ships[fleet[gen]].name); gen++; } while (cost < limit - ships[0].price) { int cheap_slots[MAX_FLEET]; int amount = find_cheap_slots(fleet, cheap_slots); int r; if (amount == 0) die("Impossible to create fleet worth %d points (got to %d)\n", limit, cost); r = cheap_slots[rand() % amount]; // fprintf(stderr, "Replacing '%s' with ", ships[fleet[r]].name); cost -= ships[fleet[r]].price; fleet[r] = random_ship(ships[fleet[r]].price + 1, limit - cost); cost += ships[fleet[r]].price; // fprintf(stderr, "'%s'\n", ships[fleet[r]].name); } fprintf(stderr, "Generated fleet for %d points\n", cost); for (j = 0; j < gen; j++) fleet_str[j] = ships[fleet[j]].id; } void replace_fleet (char *fleet, int limit) { fleet++; memset(fleet, 0xff, MAX_FLEET); generate_ships(fleet, limit); } void read_melee_cfg (int p1, int p2, char *fname) { FILE *f = fopen(fname, "r+b"); char melee_cfg[141]; int len; if (f == NULL) die("Error opening %s: %s\n", fname, strerror(errno)); len = fread(melee_cfg, 1, sizeof(melee_cfg), f); if (len < 140) die("Expected %d bytes from melee.cfg, only got %d\n", 140, len); if (!feof(f)) die("melee.cfg not exactly 140 bytes long\n"); replace_fleet(&melee_cfg[0], p1); replace_fleet(&melee_cfg[FLEET_ARRAY_SIZE], p2); fseek(f, 0, SEEK_SET); fwrite(melee_cfg, 1, 106, f); fclose(f); } void remove_ship (char *shipname) { int i; for (i = 0; i < SHIPS; i++) { if (strcmp(ships[i].name, shipname) == 0) break; } if (i == SHIPS) die("No ship '%s' found\n", shipname); memmove(&ships[i], &ships[i+1], sizeof(ship_s)*(SHIPS-1-i)); SHIPS--; } void list_ships () { int i; for (i = 0; i < SHIPS; i++) { printf("%-9s (%d)\n", ships[i].name, ships[i].price); } exit(0); } int mstrtol (char *s) { char *end; int i = strtol(s, &end, 10); if (*end != '\0') die("Invalid number: %s\n", s); return i; } void usage (char *name) { die("Usage: %s [options]\n" " Options:\n" " -1 number maximum cost of fleet 1 (defaults to 150)\n" " -2 number maximum cost of fleet 2 (defaults to 150)\n" " -e shipname don't generate any 'shipname':s in either fleet\n" " -f filename path to melee.cfg (default to ~/.uqm/melee.cfg\n" " -h this message\n" " -l list all valid shipnames, then quit\n" "\n" "Send bug reports and feature requests to .\n" , name); } int main (int argc, char **argv) { int p1 = 150, p2 = 150; char *fname = NULL; int i = 1; srand(time(NULL)); while (i < argc) { if (strcmp(argv[i], "-1") == 0) { p1 = mstrtol(argv[++i]); } else if (strcmp(argv[i], "-2") == 0) { p2 = mstrtol(argv[++i]); } else if (strcmp(argv[i], "-e") == 0) { remove_ship(argv[++i]); } else if (strcmp(argv[i], "-f") == 0) { fname = argv[++i]; } else if (strcmp(argv[i], "-f") == 0) { usage(argv[0]); } else if (strcmp(argv[i], "-l") == 0) { list_ships(); } else { fprintf(stderr, "Invalid parameter: %s\n", argv[i]); usage(argv[0]); } i++; } if (fname == NULL) { char *home = getenv("HOME"); if (home == NULL) die("No path to melee.cfg given, and HOME not set\n"); fname = malloc(strlen(home) + 16); sprintf(fname, "%s%s", home, "/.uqm/melee.cfg"); fprintf(stderr, "No path to melee.cfg given. Defaulting to '%s'.\n", fname); } read_melee_cfg(p1, p2, fname); return 0; }