/*
* 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);
}