empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/*
 * 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) */
signed short int 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)			(grid[room] % MAP_WIDTH)
#define Y_COORD(room)			(grid[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] = 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] = 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)] = sect;
		for (i = 0; i <= last_s; i++)
			grid[shift(isle->loc, j, -i)] = 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)] = sect;
		for (i = 0; i <= last_s; i++)
			grid[shift(isle->loc, -j, -i)] = 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)] != 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] == OCEAN)
			return;
		grid[room] = MOUNTAIN;

		if (number(0, 10) && (grid[shift(room, 0, 1)] == FIELD || grid[shift(room, 0, 1)] == DESERT))
			grid[shift(room, 0, 1)] = MOUNTAIN;
		if (number(0, 10) && (grid[shift(room, 1, 0)] == FIELD || grid[shift(room, 1, 0)] == DESERT))
			grid[shift(room, 1, 0)] = MOUNTAIN;
		if (number(0, 10) && (grid[shift(room, 0, -1)] == FIELD || grid[shift(room, 0, -1)] == DESERT))
			grid[shift(room, 0, -1)] = MOUNTAIN;
		if (number(0, 10) && (grid[shift(room, -1, 0)] == FIELD || grid[shift(room, -1, 0)] == DESERT))
			grid[shift(room, -1, 0)] = 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] == OCEAN)
			return;
		grid[room] = RIVER;

		if (grid[shift(room, 0, 1)] != OCEAN)
			grid[shift(room, 0, 1)] = RIVER;
		if (grid[shift(room, 1, 0)] != OCEAN)
			grid[shift(room, 1, 0)] = 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] == OCEAN)
			return;
		if (grid[room] == MOUNTAIN)
			grid[room] = sect;

		if (grid[shift(room, 0, 1)] == MOUNTAIN)
			grid[shift(room, 0, 1)] = sect;
		if (grid[shift(room, 1, 0)] == MOUNTAIN)
			grid[shift(room, 1, 0)] = 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)] == sect)
				grid[shift(room, j, i)] = type;
		for (i = 0; i <= last_s; i++)
			if (grid[shift(room, j, -i)] == sect)
				grid[shift(room, j, -i)] = 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)] == sect)
				grid[shift(room, -j, i)] = type;
		for (i = 0; i <= last_s; i++)
			if (grid[shift(room, -j, -i)] == sect)
				grid[shift(room, -j, -i)] = 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] != FIELD && grid[r] != 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] = 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, r, x, y;

	for (isle = island_list; isle; isle = isle->next)
		if (!number(0, 2) && (grid[isle->loc] == FIELD || grid[isle->loc] == DESERT)) {
			for (x = -3; x <= 3; x++) {
				for (y = -3; y <= 3; y++) {
					r = shift(isle->loc, x, y);
					if (grid[r] == FIELD || grid[r] == DESERT)
						grid[r] = WASTELAND;
						}
					}

			grid[isle->loc] = 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] + 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]) {
				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 WASTELAND:	fprintf(out, "19\n");			break;
				case TOWER:		fprintf(out, "18\n");			break;
				case DESERT_TREE:fprintf(out, "1\nA1\n");		break;
				default:		fprintf(out, "6\n");			break;
				}
			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]][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);
	}