/**
* This is the handler to help control the world map. It co-ordinates
* all the terrains connecting themselves together. All terrains are
* rectangular.
* @author Pinkfish
* @started Fri Mar 22 16:58:35 PST 2002
*/
#include <terrain_map.h>
class region {
mixed* terrains;
string* features;
int last_touched;
}
#define SAVE_DIR "/save/world_map/"
#define BOUNDARY TERRAIN_MAP_WORLD_BOUNDARY
#define REGION_SIZE TERRAIN_MAP_WORLD_REGION_SIZE
#define REGION_BOUNDARY TERRAIN_MAP_WORLD_REGION_BOUNDARY
#define OFFSET TERRAIN_MAP_WORLD_OFFSET
#define REGION_WIDTH TERRAIN_MAP_WORLD_REGION_WIDTH
void load_me();
private void generate_main_templates();
private void generate_blocking_templates();
// Array of array of regions
private nosave mixed* _regions;
//private string* _features;
private nosave mixed* _templates;
private nosave mixed* _blocking_templates;
void create() {
// Make a 10 x 10 grid.
_regions = allocate(REGION_WIDTH, (: allocate(REGION_WIDTH) :));
//_features = ({ });
seteuid(getuid());
generate_main_templates();
generate_blocking_templates();
//load_me();
}
private void generate_main_templates() {
int size;
mixed* last;
int x;
int y;
int u;
int v;
int e;
int i;
_templates = allocate(12);
last = ({ ({ 1 }) });
for (size = 0; size < sizeof(_templates); size++) {
for (i = 0; i < sizeof(last); i++) {
last[i] = ({ 0 }) + last[i] + ({ 0 });
}
_templates[size] = ({ allocate((size + 1) * 2 + 1) }) + last +
({ allocate((size + 1) * 2 + 1) });
// Draw a circle of the right size.
x = 0;
y = size + 1;
u = 1;
v = 2 * (size + 1) - 1;
e = 0;
while (x <= y) {
_templates[size][size + 1 + x][size + 1 + y] = 1;
_templates[size][size + 1 + x][size + 1 - y] = 1;
_templates[size][size + 1 - x][size + 1 + y] = 1;
_templates[size][size + 1 - x][size + 1 - y] = 1;
_templates[size][size + 1 + y][size + 1 + x] = 1;
_templates[size][size + 1 + y][size + 1 - x] = 1;
_templates[size][size + 1 - y][size + 1 + x] = 1;
_templates[size][size + 1 - y][size + 1 - x] = 1;
if (size > 0) {
_templates[size][size + 1 + x - 1][size + 1 + y] = 1;
_templates[size][size + 1 + x - 1][size + 1 - y] = 1;
_templates[size][size + 1 - x + 1][size + 1 + y] = 1;
_templates[size][size + 1 - x + 1][size + 1 - y] = 1;
_templates[size][size + 1 + y - 1][size + 1 + x] = 1;
_templates[size][size + 1 + y - 1][size + 1 - x] = 1;
_templates[size][size + 1 - y + 1][size + 1 + x] = 1;
_templates[size][size + 1 - y + 1][size + 1 - x] = 1;
}
x++;
e += u;
u += 2;
if (v < 2 * e) {
y--;
e -= v;
v -= 2;
}
if (x > y) {
break;
}
}
last = copy(_templates[size]);
}
}
mixed* generate_blocking(int x, int y, string* start,
string* straight, string* diagonal) {
int i;
int dx;
int ex;
int dy;
int balance;
int sx;
int sy;
int blocked;
int blocked_straight;
int blocked_diagonal;
int first_block;
first_block = -1;
for (i = 0; i <= sizeof(start); i++) {
// Generate a line from 0,0 to 0,i
sx = 0;
sy = 0;
ex = sizeof(start);
dx = sizeof(start);
dy = i;
dy <<= 1;
balance = dy - dx;
dx <<= 1;
blocked_straight = blocked_diagonal = blocked = 0;
while (sx != ex) {
if (blocked) {
if (first_block == -1) {
first_block = i;
}
start[sy] = start[sy][0..sx-1] + "*" + start[sy][sx+1..];
} else {
start[sy] = start[sy][0..sx-1] + " " + start[sy][sx+1..];
}
if (blocked_straight) {
straight[sy] = straight[sy][0..sx-1] + "*" + straight[sy][sx+1..];
} else {
straight[sy] = straight[sy][0..sx-1] + " " + straight[sy][sx+1..];
}
if (blocked_diagonal) {
diagonal[sy] = diagonal[sy][0..sx-1] + "*" + diagonal[sy][sx+1..];
} else {
diagonal[sy] = diagonal[sy][0..sx-1] + " " + diagonal[sy][sx+1..];
}
if (sx == x && sy == y) {
blocked_diagonal = blocked_straight = blocked = 1;
diagonal[sy] = diagonal[sy][0..sx-1] + "@" + diagonal[sy][sx+1..];
start[sy] = start[sy][0..sx-1] + "@" + start[sy][sx+1..];
straight[sy] = straight[sy][0..sx-1] + "@" + straight[sy][sx+1..];
}
if (sx == x && sy == y + 1) {
blocked_straight = 1;
straight[sy] = straight[sy][0..sx-1] + "|" + straight[sy][sx+1..];
}
if (sx == x + 1 && sy == y + 1) {
blocked_diagonal = 1;
diagonal[sy] = diagonal[sy][0..sx-1] + "\\" + diagonal[sy][sx+1..];
}
if (balance >= 0) {
sy++;
balance -= dx;
}
balance += dy;
sx++;
}
}
// Need to redo back in the other direction to remove extra unneeded blocking
// bits.
if (first_block >= (sizeof(start) / 2)) {
for (i = sizeof(start); i >= 6; i--) {
// Generate a line from 0,0 to 0,i
sx = 0;
sy = 0;
ex = sizeof(start);
dx = sizeof(start);
dy = i;
dy <<= 1;
balance = dy - dx;
dx <<= 1;
blocked = 0;
while (sx != ex) {
start[sy] = start[sy][0..sx-1] + " " + start[sy][sx+1..];
if (sx == x && sy == y) {
break;
}
if (balance >= 0) {
sy++;
balance -= dx;
}
balance += dy;
sx++;
}
}
}
return ({ start, straight, diagonal });
}
private void generate_blocking_templates() {
int x;
int y;
int size;
mixed* basic;
string* start;
string line;
basic = _templates[<1];
size = sizeof(_templates);
// Chop it into just a quarter.
line = "";
for (x = 0; x < size + 1; x++) {
line += " ";
}
start = allocate(size + 1, line);
_blocking_templates = allocate(size + 1, ({ }));
for (x = 0; x < size + 1; x++) {
_blocking_templates[x] = allocate(size + 1);
}
// We only need to generate for one octant.
// That is from the straight line to the x == y line.
for (x = 0; x < size + 1; x++) {
for (y = x; y < size + 1; y++) {
// Only if it is in the circle.
if (basic[x + size][y + size] && (x != 0 || y != 0)) {
// Generate the template for here.
_blocking_templates[x][y] = generate_blocking(y, x, copy(start), copy(start), copy(start));
}
}
}
}
/**
* Find the template to use for the area blocked by an obstacle.
* This will do nifty template fitting stuff for obstacles.
* @param x the x co-ordinate of the blockage
* @param y the y co-ordinate of the blockage
* @param type the type is 0 for normal, 1 for straight, 2 for diagonal
*/
string *query_blocking_template(int x, int y, int type) {
if (arrayp(_blocking_templates[x][y])) {
return copy(_blocking_templates[x][y][type]);
}
return 0;
}
/**
* This method returns the template for the specific size of the
* map. This is for visibility considerations.
* @param size the size of the template
* @return the template
*/
mixed* query_map_template(int size) {
return copy(_templates[size - 1]);
}
/** @ignore yes */
void load_region(int x, int y) {
class region region;
string fname;
if (_regions[x][y]) {
_regions[x][y]->last_touched = time();
return ;
}
fname = SAVE_DIR + "region_" + x + "_" + y;
if (unguarded((: file_size($(fname)) :)) > 0) {
region = unguarded( (: restore_variable(read_file($(fname), 1)) :));
} else {
region = new(class region,
terrains : allocate(REGION_SIZE, (: allocate(REGION_SIZE) :)),
features : ({ }));
}
_regions[x][y] = region;
_regions[x][y]->last_touched = time();
}
/** @ignore yes */
void save_region(int x, int y) {
string fname;
class region region;
if (_regions[x][y]) {
region = _regions[x][y];
fname = SAVE_DIR + "region_" + x + "_" + y;
unguarded( (: write_file($(fname), save_variable($(region)), 1) :));
}
}
/**
* This method adds the specified terrain to the world map.
*/
int add_terrain(string path, int x1, int y1, int x2, int y2) {
int tmp;
int region_x;
int region_y;
if (x1 > x2) {
tmp = x1;
x1 = x2;
x2 = tmp;
}
if (y1 > y2) {
tmp = y1;
y1 = y2;
y2 = tmp;
}
if (x2 - x1 != BOUNDARY ||
y2 - y1 != BOUNDARY) {
// Not the correct size.
debug_printf("Incorrect terrain size (%d x %d): %s\n",
x2 - x1, y2 - y1, path);
return 0;
}
if (x1 % BOUNDARY ||
y1 % BOUNDARY) {
// Not on a correct boundary.
debug_printf("Incorrect boundary (%d - %d): %s\n",
x1 % BOUNDARY, y1 % BOUNDARY, path);
return 0;
}
x1 = ((x1 + OFFSET) / BOUNDARY);
y1 = ((y1 + OFFSET) / BOUNDARY);
region_x = x1 / REGION_SIZE;
region_y = y1 / REGION_SIZE;
load_region(region_x, region_y);
x2 = x1 % REGION_SIZE;
y2 = y1 % REGION_SIZE;
_regions[region_x][region_y]->terrains[x2][y2] = path;
save_region(region_x, region_y);
debug_printf("Added terrain (%d {%d}, %d {%d}) [%d, %d]: %s\n",
x1, x2, y1, y2, region_x, region_y, path);
return 1;
} /* add_terrain() */
/**
* This method finds the terrain at the specific location.
* @param x the x co-ordinate
* @param y the y co-ordinate
* @return the path to the terrain
*/
string find_terrain_at(int x, int y) {
int region_x;
int region_y;
x = ((x + OFFSET) / BOUNDARY);
y = ((y + OFFSET) / BOUNDARY);
region_x = x / REGION_SIZE;
region_y = y / REGION_SIZE;
//debug_printf("Finding terrain (%d, %d) [%d, %d]\n",
// x, y, region_x, region_y);
load_region(region_x, region_y);
return _regions[region_x][region_y]->terrains[x % REGION_SIZE][y % REGION_SIZE];
}
/**
* This method finds the specific room at the specific location
* @param x the x co-ordinate
* @param y the y co-ordinate
* @return the path to the room
*/
string find_room_at(int x, int y) {
string path;
path = find_terrain_at(x, y);
if (path) {
return path->find_room_at_real_coord(x, y);
}
return 0;
}
/**
* This method adds a feature into the world map.
* @param feature the feature to add
*/
void add_feature(string feature) {
int x;
int y;
//_features += ({ feature });
// Go through the regionsand figure out which ones this feature is in.
for (x = 0; x < REGION_WIDTH; x++) {
for (y = 0; y < REGION_WIDTH; y++) {
if (feature->is_inside_region(
(x * REGION_WIDTH) * BOUNDARY - OFFSET,
(y * REGION_WIDTH) * BOUNDARY - OFFSET,
((x + 1) * REGION_WIDTH) * BOUNDARY - OFFSET,
((y + 1) * REGION_WIDTH) * BOUNDARY - OFFSET)) {
load_region(x, y);
_regions[x][y]->features |= ({ feature });
save_region(x, y);
debug_printf("Added feature to region %d, %d\n", x, y);
}
}
}
}
/**
* This method removes a feature from the world map.
* @param feature the feature to remove
*/
void remove_feature(string feature) {
int x;
int y;
for (x = 0; x < REGION_WIDTH; x++) {
for (y = 0; y < REGION_WIDTH; y++) {
load_region(x, y);
if (member_array(feature, _regions[x][y]->features) != -1) {
_regions[x][y]->features -= ({ feature });
save_region(x, y);
}
}
}
}
/**
* This method returns the list of all the features available at the
* specific location.
* @param x the x position
* @param y the y position
* @return the features at the location
*/
string* query_features_at(int x, int y) {
int region_x;
int region_y;
x = ((x + OFFSET) / BOUNDARY);
y = ((y + OFFSET) / BOUNDARY);
region_x = x / REGION_WIDTH;
region_y = y / REGION_WIDTH;
load_region(region_x, region_y);
return _regions[region_x][region_y]->features;
}
/**
* This method finds all the features inside the specified region. This
* is quite expensive in terms of evaluation cost, so do not try and call
* this too much. Cache the results if possible.
* @param x1 the top x location
* @param y1 the top y location
* @param x2 the bottom x location
* @param y2 the bottom y location
* @return the features in the region
*/
string* query_features_in_region(int x1_orig, int y1_orig, int x2_orig, int y2_orig) {
int region_x;
int region_y;
int x1;
int y1;
int x2;
int y2;
int tmp;
string* features;
string feat;
x1 = ((x1_orig + OFFSET) / BOUNDARY) / REGION_WIDTH;
y1 = ((y1_orig + OFFSET) / BOUNDARY) / REGION_WIDTH;
x2 = ((x2_orig + OFFSET) / BOUNDARY) / REGION_WIDTH;
y2 = ((y2_orig + OFFSET) / BOUNDARY) / REGION_WIDTH;
if (x1 > x2) {
tmp = x1;
x1 = x2;
x2 = tmp;
}
if (y1 > y2) {
tmp = y1;
y1 = y2;
y2 = tmp;
}
features = ({ });
if (x1 < 0) {
x1 = 0;
}
if (y1 < 0) {
y1 = 0;
}
if (x2 >= REGION_WIDTH) {
x2 = REGION_WIDTH - 1;
}
if (y2 >= REGION_WIDTH) {
y2 = REGION_WIDTH - 1;
}
// Now we loop through the possible regions.
for (region_x = x1; region_x <= x2; region_x++) {
for (region_y = y1; region_y <= y2; region_y++) {
load_region(region_x, region_y);
foreach (feat in _regions[region_x][region_y]->features) {
if (feat->is_inside_region(x1_orig, y1_orig, x2_orig, y2_orig)) {
features |= ({ feat });
}
}
}
}
return features;
}