/* * EmpireMAP 1.0 * by Paul Clarke * Map generator for EmpireMUD * * usage (from lib/world/wld): ./map <# islands> * * Some code contained in this file is extracted from CircleMUD 3.0 * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include "../conf.h" #include "../sysdep.h" #include "../structs.h" /* The master grid (MAP_WIDTH, MAP_HEIGHT) */ struct grid_type { signed short int type; short int no_claim; } grid[MAP_SIZE]; #ifndef NULL #define NULL (void *)0 #endif /* Define the sectors we're going to use */ #define FIELD 0 #define FOREST 1 #define RIVER 2 #define OCEAN 3 #define MOUNTAIN 4 #define FRUIT 5 #define WHEAT 6 #define CORN 7 #define DESERT 8 #define TOWER 9 #define WASTELAND 10 #define OASIS 11 #define DESERT_TREE 12 #define NUM_MAP_SECTS 13 /* Total */ /* Directions */ #define NORTH 0 #define EAST 1 #define SOUTH 2 #define WEST 3 #define NOEA 4 #define NOWE 5 #define SOEA 6 #define SOWE 7 #define s_NORTH(room) shift(room, 0, 1) #define s_EAST(room) shift(room, 1, 0) #define s_SOUTH(room) shift(room, 0, -1) #define s_WEST(room) shift(room, -1, 0) #define s_NOEA(room) shift(room, 1, 1) #define s_NOWE(room) shift(room, -1, 1) #define s_SOEA(room) shift(room, 1, -1) #define s_SOWE(room) shift(room, -1, -1) #define X_COORD(room) (room % MAP_WIDTH) #define Y_COORD(room) (room / MAP_HEIGHT) #define CREATE(result, type, number) do {\ if ((number) * sizeof(type) <= 0) \ perror("SYSERR: Zero bytes or less requested"); \ if (!((result) = (type *) calloc ((number), sizeof(type)))) \ { perror("SYSERR: malloc failure"); abort(); } } while(0) /* Characters to output for the sectors */ const char *symbols[][2] = { { ".", "Field" }, { "^", "Forest" }, { "-", "River" }, { "~", "Ocean" }, { "A", "Mountain" }, { ",", "Fruit" }, { ",", "Wheat" }, { ",", "Corn" }, { "D", "Desert" }, { "T", "Tower" }, { "W", "Wastelands" }, { "~", "Oasis" }, { "*", "Des Tree" } }; /* Island data structure */ struct island_data { int loc; /* Location of island in grid[] */ int desert; int width[4]; /* Maximum width of island */ int crop; /* Crop to use on island */ struct island_data *next; }; struct island_data *island_list = NULL; /* Master list */ int global_crop = FRUIT; /* For crop-circling */ /* quick-running mock-up distance formula */ #define distance(x, y, a, b) ((x - a) * (x - a) + (y - b) * (y - b)) /* Find an island */ struct island_data *closest_island(int x, int y) { struct island_data *isle, *best = NULL; int b = MAP_SIZE; for (isle = island_list; isle; isle = isle->next) if (distance(X_COORD(isle->loc), Y_COORD(isle->loc), x, y) < b) { best = isle; b = distance(X_COORD(isle->loc), Y_COORD(isle->loc), x, y); } return best; } /* Set the whole world to one GIANT ocean */ void init_grid(void) { int i; for (i = 0; i < MAP_SIZE; i++) grid[i].type = OCEAN; } /* This takes an initial point 'origin' and translates it by x,y */ int shift(int origin, int x, int y) { int o = origin; if (X_COORD(origin) + x < 0) o = origin + x + MAP_WIDTH; else if (X_COORD(origin) + x >= MAP_WIDTH) o = origin + x - MAP_WIDTH; else o = origin + x; if ((o + y * MAP_HEIGHT) >= MAP_SIZE) o = o + y * MAP_HEIGHT - MAP_SIZE; else if ((o + y * MAP_HEIGHT) < 0) o = o + y * MAP_HEIGHT + MAP_SIZE; else o = o + y * MAP_HEIGHT; if (o < 0) return 0; return o; } /* Definitions leading up to random number code ************************/ /* This program is public domain and was written by William S. England (Oct 1988) */ #define mM (unsigned long)2147483647 #define qQ (unsigned long)127773 #define aA (unsigned int)16807 #define rR (unsigned int)2836 static unsigned long seed; void empire_srandom(unsigned long initial_seed) { seed = initial_seed; } unsigned long empire_random(void) { register int lo, hi, test; hi = seed/qQ; lo = seed%qQ; test = aA*lo - rR*hi; if (test > 0) seed = test; else seed = test+ mM; return (seed); } int number(int from, int to) { /* error checking in case people call number() incorrectly */ if (from > to) { int tmp = from; from = to; to = tmp; } return ((empire_random() % (to - from + 1)) + from); } /* End random number code ********************************************/ /* Add land to an island */ void land_mass(struct island_data *isle) { int i, j, last_s, last_n, a, b; int sect = FIELD; if (!number(0, 2)) { isle->desert = 1; sect = DESERT; } else isle->desert = 0; /* Island's heart */ grid[isle->loc].type = sect; isle->width[EAST] = number(10, 35); isle->width[WEST] = number(10, 35); a = last_n = isle->width[NORTH] = number(10, isle->width[EAST]); b = last_s = isle->width[SOUTH] = number(10, isle->width[EAST]); for (j = 0; j <= isle->width[EAST]; j++) { for (i = 0; i <= last_n; i++) grid[shift(isle->loc, j, i)].type = sect; for (i = 0; i <= last_s; i++) grid[shift(isle->loc, j, -i)].type = sect; last_n += number(last_n <= 0 ? 0 : -2, ((isle->width[EAST] - j) < last_n) ? -2 : 2); last_s += number(last_s <= 0 ? 0 : -2, ((isle->width[EAST] - j) < last_s) ? -2 : 2); } last_n = a; last_s = b; for (j = 0; j <= isle->width[WEST]; j++) { for (i = 0; i <= last_n; i++) grid[shift(isle->loc, -j, i)].type = sect; for (i = 0; i <= last_s; i++) grid[shift(isle->loc, -j, -i)].type = sect; last_n += number(last_n <= 0 ? 0 : -2, ((isle->width[WEST] - j) < last_n) ? -2 : 2); last_s += number(last_s <= 0 ? 0 : -2, ((isle->width[WEST] - j) < last_s) ? -2 : 2); } } /* Build all of the islands in memory */ void create_islands(int num) { struct island_data *isle; int i; for (i = 0; i < num; i++) { CREATE(isle, struct island_data, 1); isle->loc = number(0, MAP_SIZE - 1); isle->crop = global_crop++; if (global_crop > CORN) global_crop = FRUIT; isle->next = island_list; island_list = isle; land_mass(isle); } } /* Returns a room on the border in a given dir */ int find_border(struct island_data *isle, int x_dir, int y_dir) { int i; /* Track to the border of the island */ for (i = 0; grid[shift(isle->loc, i*x_dir, i*y_dir)].type != OCEAN; i++); /* We went 1 too far */ i--; return (shift(isle->loc, i*x_dir, i*y_dir)); } /* Add a mountain range to an island */ void add_mountains(struct island_data *isle) { int x, y, room; do { x = number(-1, 1); y = number(-1, 1); } while (x == 0 && y == 0); room = find_border(isle, x, y); /* invert directions */ x *= -1; y *= -1; for (;;) { if (grid[room].type == OCEAN) return; grid[room].type = MOUNTAIN; if (number(0, 10) && (grid[shift(room, 0, 1)].type == FIELD || grid[shift(room, 0, 1)].type == DESERT)) grid[shift(room, 0, 1)].type = MOUNTAIN; if (number(0, 10) && (grid[shift(room, 1, 0)].type == FIELD || grid[shift(room, 1, 0)].type == DESERT)) grid[shift(room, 1, 0)].type = MOUNTAIN; if (number(0, 10) && (grid[shift(room, 0, -1)].type == FIELD || grid[shift(room, 0, -1)].type == DESERT)) grid[shift(room, 0, -1)].type = MOUNTAIN; if (number(0, 10) && (grid[shift(room, -1, 0)].type == FIELD || grid[shift(room, -1, 0)].type == DESERT)) grid[shift(room, -1, 0)].type = MOUNTAIN; /* Alter course */ if (!number(0, 2)) { if (x == 0) x += number(-1, 1); else if (y == 0) y += number(-1, 1); else if (x > 0 && y > 0) { if ((y -= number(0, 1))) x -= number(0, 1); } else if (x < 0 && y < 0) { if ((y += number(0, 1))) x += number(0, 1); } else if (x < 0 && y > 0) { if ((y -= number(0, 1))) x += number(0, 1); } else if (y < 0 && x > 0) { if ((y += number(0, 1))) x -= number(0, 1); } } room = shift(room, x, y); } } /* Add a river to the island */ void add_river(struct island_data *isle) { int x, y, room; if (isle->desert) return; do { x = number(-1, 1); y = number(-1, 1); } while (x == 0 && y == 0); room = find_border(isle, x, y); /* invert directions */ x *= -1; y *= -1; for (;;) { if (grid[room].type == OCEAN) return; grid[room].type = RIVER; if (grid[shift(room, 0, 1)].type != OCEAN) grid[shift(room, 0, 1)].type = RIVER; if (grid[shift(room, 1, 0)].type != OCEAN) grid[shift(room, 1, 0)].type = RIVER; /* Alter course */ if (!number(0, 2)) { if (x == 0) x += number(-1, 1); else if (y == 0) y += number(-1, 1); else if (x > 0 && y > 0) { if ((y -= number(0, 1))) x -= number(0, 1); } else if (x < 0 && y < 0) { if ((y += number(0, 1))) x += number(0, 1); } else if (x < 0 && y > 0) { if ((y -= number(0, 1))) x += number(0, 1); } else if (y < 0 && x > 0) { if ((y += number(0, 1))) x -= number(0, 1); } } room = shift(room, x, y); } } /* Add a pass through a mountain (at random) */ void add_pass(struct island_data *isle) { int x, y, room; int sect = (isle->desert ? DESERT : FIELD); do { x = number(-1, 1); y = number(-1, 1); } while (x == 0 && y == 0); room = find_border(isle, x, y); /* invert directions */ x *= -1; y *= -1; for (;;) { if (grid[room].type == OCEAN) return; if (grid[room].type == MOUNTAIN) grid[room].type = sect; if (grid[shift(room, 0, 1)].type == MOUNTAIN) grid[shift(room, 0, 1)].type = sect; if (grid[shift(room, 1, 0)].type == MOUNTAIN) grid[shift(room, 1, 0)].type = sect; room = shift(room, x, y); } } /* Add a splotch of a given sect in a nice splotchy shape */ void splotch(int room, int type, struct island_data *isle) { int i, j, last_s, last_n, a, b, width_e, width_w, width_n, width_s; int sect = (isle->desert ? DESERT : FIELD); if (sect == DESERT) { width_e = number(1, 3); width_w = number(1, 3); } else { width_e = number(2, 8); width_w = number(2, 8); } a = last_n = width_n = number((sect == DESERT ? 1 : 2), width_e); b = last_s = width_s = number((sect == DESERT ? 1 : 2), width_e); for (j = 0; j <= width_e; j++) { for (i = 0; i <= last_n; i++) if (grid[shift(room, j, i)].type == sect) grid[shift(room, j, i)].type = type; for (i = 0; i <= last_s; i++) if (grid[shift(room, j, -i)].type == sect) grid[shift(room, j, -i)].type = type; last_n += number(last_n <= 0 ? 0 : -2, ((width_e - j) < last_n) ? -2 : 2); last_s += number(last_s <= 0 ? 0 : -2, ((width_e - j) < last_s) ? -2 : 2); } last_n = a; last_s = b; for (j = 0; j <= width_w; j++) { for (i = 0; i <= last_n; i++) if (grid[shift(room, -j, i)].type == sect) grid[shift(room, -j, i)].type = type; for (i = 0; i <= last_s; i++) if (grid[shift(room, -j, -i)].type == sect) grid[shift(room, -j, -i)].type = type; last_n += number(last_n <= 0 ? 0 : -2, ((width_w - j) < last_n) ? -2 : 2); last_s += number(last_s <= 0 ? 0 : -2, ((width_w - j) < last_s) ? -2 : 2); } } /* Add a splotch of forest or crop */ void paint_ball(void) { struct island_data *isle; int r, sect = 0; do { r = number(0, MAP_SIZE - 1); } while (grid[r].type != FIELD && grid[r].type != DESERT); if (!(isle = closest_island(X_COORD(r), Y_COORD(r)))) return; if (isle->desert) switch (number(0, 3)) { case 0: sect = DESERT_TREE; break; case 1: sect = DESERT_TREE; break; case 2: sect = DESERT_TREE; break; case 3: grid[r].type = OASIS; return; } else switch (number(0, 6)) { case 0: sect = isle->crop; break; case 1: sect = FOREST; break; case 2: sect = isle->crop; break; case 3: sect = FOREST; break; case 4: sect = isle->crop; break; default: return; } splotch(r, sect, isle); } void add_start_points(void) { struct island_data *isle; int count = 0, x, y; for (isle = island_list; isle; isle = isle->next) if (!number(0, 2) && (grid[isle->loc].type == FIELD || grid[isle->loc].type == DESERT)) { for (x = -3; x <= 3; x++) { for (y = -3; y <= 3; y++) grid[shift(isle->loc, x, y)].no_claim = 1; } grid[isle->loc].type = TOWER; count++; } if (count == 0) add_start_points(); } void create_map(int num) { struct island_data *isle; int i; init_grid(); create_islands(num); for (isle = island_list; isle; isle = isle->next) { add_mountains(isle); add_river(isle); if (!number(0, 3)) add_river(isle); add_pass(isle); } add_start_points(); for (i = 0; i < num*10; i++) paint_ball(); } /* Display the usage to the user */ void output_stats(void) { int total[NUM_MAP_SECTS+1]; int i; for (i = 0; i < NUM_MAP_SECTS + 1; i++) total[i] = 0; for (i = 0; i < MAP_SIZE; i++) total[(int) grid[i].type + 1]++; for (i = 0; i < NUM_MAP_SECTS + 1; i++) printf("%-10.10s: %6d %s", i == 0 ? "ERROR" : symbols[i-1][1], total[i], !((i+1)%3) ? "\n" : " "); printf("\n"); } void print_map_to_files(void) { FILE *out; int i, j; char fname[256], buf[256]; for (i = 0; i < NUM_MAP_ZONES; i++) { sprintf(fname, "%d.wld", i); out = fopen(fname, "w"); for (j = 0; j < SIZE_MAP_ZONES; j++) { fprintf(out, "#%d\n", i * SIZE_MAP_ZONES + j); switch(grid[i * SIZE_MAP_ZONES + j].type) { case FIELD: fprintf(out, "0\n"); break; case FOREST: fprintf(out, "4\n"); break; case RIVER: fprintf(out, "5\n"); break; case OCEAN: fprintf(out, "6\n"); break; case MOUNTAIN: fprintf(out, "8\n"); break; case FRUIT: fprintf(out, "7\nC0\n"); break; case WHEAT: fprintf(out, "7\nC1\n"); break; case CORN: fprintf(out, "7\nC2\n"); break; case DESERT: fprintf(out, "20\n"); break; case OASIS: fprintf(out, "21\n"); break; case TOWER: fprintf(out, "18\n"); break; case DESERT_TREE:fprintf(out, "1\nA1\n"); break; default: fprintf(out, "6\n"); break; } if (grid[i * SIZE_MAP_ZONES + j].no_claim) fprintf(out, "O-1\n"); fprintf(out, "S\n"); } fprintf(out, "$~\n"); fclose(out); } sprintf(buf, "%d.wld", NUM_MAP_ZONES); out = fopen(buf, "w"); fprintf(out, "#%d\n8\nS\n$~\n", MAP_SIZE); fclose(out); return; } void print_map_graphic(void) { FILE *out; int i; out = fopen("map.txt", "w"); for (i = 0; i < MAP_SIZE; i++) { fprintf(out, "%s", symbols[grid[i].type][0]); if (!((i+1) % MAP_WIDTH)) fprintf(out, "\n"); } fclose(out); return; } int main(int argc, char **argv) { int num; if (argc != 2 && argc != 3) { printf("Format: %s <# of islands> [graphic]\n", argv[0]); exit(0); } if ((num = atoi(argv[1])) <= 0 || num > NUM_MAP_ZONES) { printf("You must pick a number of islands between 1 and %d.\n", NUM_MAP_ZONES); exit(0); } empire_srandom(time(0)); create_map(num); if (argv[2] && *argv[2] && !strcmp(argv[2], "graphic")) print_map_graphic(); else print_map_to_files(); printf("Done.\n"); output_stats(); return (0); }