Initial import of new world along with scorn and editor picks.
Info directory contains some utils (like the program made to make the map), information, images of the map, and a script to update the exits. MSW 2001-12-17 git-svn-id: svn://svn.code.sf.net/p/crossfire/code/trunk/maps@1459 282e977c-c81d-0410-88c4-b93c2d0d6712master
commit
b4ee7ba788
|
|
@ -0,0 +1,83 @@
|
|||
In this directory is utilities, gif images of the maps, and other notes.
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Developer guidelines:
|
||||
|
||||
Read the mapguide for guidelines on making a good map.
|
||||
|
||||
Use the comment field that the maps have - put in who the author is,
|
||||
what level the map is, and any other special notes.
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Naming Scheme:
|
||||
|
||||
Note that these rules are ordered in importance, eg, if a rule conflicts,
|
||||
the lower number rule takes precedence.
|
||||
|
||||
1) If the map is currently off the main world (eg, pupland), it should have
|
||||
its own top level ldirectory which all the maps in that area are located.
|
||||
|
||||
2) Each city should have it own top level directory (eg, scorn,
|
||||
navar_city, santo_dominion). All buildings in the city and located
|
||||
nearby or related to it are in the respectice city directory.
|
||||
|
||||
For maps that are placed directly on the world map and are not in the city,
|
||||
the follow guidelines should be used:
|
||||
|
||||
3) If the map is part of a larger quest, a /quest/name_of_quest directory
|
||||
should be made, and all the maps for the quest placed in there. If
|
||||
some portions of the quest has maps in cities or other places, a README
|
||||
should be included explaining this. Note in general, having README's
|
||||
for all quests explaining the flow probably isn't a bad idea in the
|
||||
case someone else needs to work on it.
|
||||
|
||||
4) If a map is independent (eg, the map is one you just go there, kill and
|
||||
get exp), it should be in the /dungeons directory (Ill named I know).
|
||||
If the dungeon is comprised of several map (eg, multilevel dungeon), a
|
||||
sub directory should be made to hold all of these maps.
|
||||
|
||||
5) Maps should fall into one of the category above - if it does not, and
|
||||
you are not sure, send a message to crossfire-devel@lists.real-time.com.
|
||||
Maps should not be sorted by author, as this does not make maintaining
|
||||
them easy.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Important landmarks
|
||||
|
||||
If you are going to use these world maps, you should probably update
|
||||
the following values in the server include/config.h file:
|
||||
#define EMERGENCY_MAPPATH "/world/world_105_115"
|
||||
#define EMERGENCY_X 5
|
||||
#define EMERGENCY_Y 37
|
||||
|
||||
|
||||
Scorn is located on world maps _104_115 and _105_115. Some touchup of the
|
||||
terrain immediately surrounding the city has been touched up some.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
land.c:
|
||||
This is the program that generated the new world map.
|
||||
|
||||
The file land.c just gets compiled - if your system has a math
|
||||
library (-lm) you probably need to link that in.
|
||||
|
||||
Given the same seed and other parameters, it will generate the same
|
||||
results if on the same platform. Eg, two different intel systems running
|
||||
redhat 7.2 should generate the same results, but a redhat compared to
|
||||
solaris system, or linux intel vs linux power pc may very well generate
|
||||
different results.
|
||||
|
||||
The generated map was run with the follow parameters on my system:
|
||||
-x 1500 -y 1500 -s 1007623715 -p 300 -n 170 -w 40000 -l 200000
|
||||
------------------------------------------------------------------------------
|
||||
GIF files:
|
||||
|
||||
el_map.gif is an elevation map - it is color coded based on elevations.
|
||||
|
||||
terrain_map.gif is color coded based on the type of terrain - I tried to match
|
||||
the coloration on what the image looks like in the png set.
|
||||
|
||||
ann_map.gif is a annotated version of the map, showing the roads and
|
||||
locations of the main cities.
|
||||
------------------------------------------------------------------------------
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 830 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 459 KiB |
|
|
@ -0,0 +1,541 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_SIZE 3000
|
||||
#define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
|
||||
#define BASE_ALT -100
|
||||
|
||||
/* make this a global to avoid stack overflow */
|
||||
int altitude[MAX_SIZE][MAX_SIZE];
|
||||
|
||||
/* This function writes out the crossfire maps. So shoot me for
|
||||
* using compiled in constants - I'm not going to use this so much
|
||||
* that I wanted to do anything too easy.
|
||||
*/
|
||||
|
||||
#define MAP_FORMAT "world_%03d_%03d"
|
||||
|
||||
/* Maps are square */
|
||||
|
||||
#define MAP_SIZE 50
|
||||
|
||||
/* There will be a total of 2500 maps (eek) - 50 in
|
||||
* each diretion. STARTX and STARTY are where to start
|
||||
* numbering from. I chose 100 for a few reasons - 1) it
|
||||
* gives room to the left and above to add some things (another
|
||||
* continent for that matter), and 2) since the format allows
|
||||
* for up to 1000 in each direction, this seemed reasonable.
|
||||
* Note - if you do the math, and have 1000 * 1000 maps, each
|
||||
* with 50*50 spaces, you have a total of 2.5 billion spaces.
|
||||
* So hopefully that should be large enough.
|
||||
*/
|
||||
|
||||
#define STARTX 100
|
||||
#define STARTY 100
|
||||
|
||||
typedef enum {
|
||||
None=0,
|
||||
DeepWater=1,
|
||||
MediumWater=2,
|
||||
ShallowWater=3,
|
||||
Swamp=4,
|
||||
DeepSwamp=5,
|
||||
Grass=6,
|
||||
Desert=7,
|
||||
Brush=8,
|
||||
EverGreens=9,
|
||||
Jungle=10,
|
||||
Tree1=11,
|
||||
Tree2=12,
|
||||
Woods1=13,
|
||||
Woods2=14,
|
||||
Woods3=15,
|
||||
Hills=16,
|
||||
HillsRocky=17,
|
||||
Steppe=17,
|
||||
Mountain=19,
|
||||
HighMountain=20,
|
||||
WasteLand=21
|
||||
} Terrain_Types;
|
||||
|
||||
char *Terrain_Names[][2] = {
|
||||
/* these are the archetype names. They are in the same order
|
||||
* as the Terrain_Types above. Note many terrain types are not
|
||||
* included because handling them would be too difficult.
|
||||
*/
|
||||
{"None", "0, 0, 0 "},
|
||||
{"deep_sea", "0 0 127 "},
|
||||
{"sea", "0 0 192 "},
|
||||
{"shallow_sea", "0 0 255 "}, /* wading depth */
|
||||
{"swamp", "12 161 64 "},
|
||||
{"deep_swamp", "155 175 164 "},
|
||||
{"grass", "0 255 0 "},
|
||||
{"desert", "222 218 135 "},
|
||||
{"brush", "1 144 1 "},
|
||||
{"evergreens", "0 128 0 "},
|
||||
{"jungle_1", "0 176 0 "},
|
||||
{"tree", "4 133 01 "},
|
||||
{"evergreen", "20 209 0 "},
|
||||
{"woods", "4 115 01 "},
|
||||
{"woods_2", "1 182 02 "},
|
||||
{"woods_3", "4 153 02 "},
|
||||
{"hills", "166 160 70 "},
|
||||
{"hills_rocky", "166 155 70 "},
|
||||
{"steppe", "150 97 34 "},
|
||||
{"mountain", "183 190 190 "},
|
||||
{"mountain2", "191 196 185 "},
|
||||
{"wasteland", "255 255 255 "},
|
||||
};
|
||||
|
||||
|
||||
|
||||
void write_crossfire_maps(int mapx, int mapy)
|
||||
{
|
||||
int x, y,n,q, nx, ny,r1,r2,ax=0,ay=0, j, k;
|
||||
char name[512];
|
||||
FILE *fp;
|
||||
Terrain_Types *terrain;
|
||||
|
||||
terrain = calloc(mapx * mapy * sizeof(Terrain_Types), sizeof(Terrain_Types));
|
||||
|
||||
/* First fill in the water and the highest of peaks */
|
||||
for (x=0; x<mapx; x++) {
|
||||
for (y=0; y<mapy; y++) {
|
||||
if (altitude[y][x] < -5000) {
|
||||
terrain[x + y * mapx] = DeepWater;
|
||||
} else if (altitude[y][x] < -99) {
|
||||
terrain[x + y * mapx] = MediumWater;
|
||||
} else if (altitude[y][x] < 1) {
|
||||
terrain[x + y * mapx] = ShallowWater;
|
||||
} else if (altitude[y][x] >=12000) {
|
||||
/* Not really precisely wasteland, but wastelands are impassable */
|
||||
terrain[x + y * mapx] = WasteLand;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Basically, take a random bit and populate the area with terrain.
|
||||
* We do this so it won't be totally monolythic (have several forest types
|
||||
* for example), yet patches will be the same thing, eg, a stretch of
|
||||
* desert, which wouldn't work very well if we just chose randomly
|
||||
* for each space.
|
||||
*/
|
||||
|
||||
for (n=0; n<(mapx * mapy) / 100; n++) {
|
||||
do {
|
||||
x = random() % mapx;
|
||||
y = random() % mapy;
|
||||
} while ( terrain[x + y * mapx] == None);
|
||||
|
||||
nx = x + 40;
|
||||
if (nx > mapx) nx=mapx;
|
||||
ny = y + 40;
|
||||
if (ny > mapy) ny = mapy;
|
||||
r1 = random();
|
||||
r2 = random();
|
||||
for (x = nx-40; x<nx; x++) {
|
||||
for (y=ny-40; y<ny; y++) {
|
||||
if (terrain[x + y * mapx] != None) continue;
|
||||
|
||||
/* near the edges, don't always fill in so that hopefully something
|
||||
* else will fill in and smooth these out some
|
||||
*/
|
||||
if ( (x < (nx -30) || y < (ny - 30) || x > (nx -10) || y > (ny - 10)) &&
|
||||
random() % 2) continue;
|
||||
|
||||
if (altitude[y][x] < 10) {
|
||||
terrain[x + y * mapx] = Swamp + (r1 % 2);
|
||||
}
|
||||
else if (altitude[y][x] < 1000) {
|
||||
terrain[x + y * mapx] = Grass + (r1 % 3);
|
||||
} else if (altitude[y][x] < 3000) {
|
||||
terrain[x + y * mapx] = EverGreens + (r1 % 9);
|
||||
} else if (altitude[y][x] < 5000) {
|
||||
terrain[x + y * mapx] = Hills + (r2 % 3);
|
||||
} else if (altitude[y][x] < 9000) {
|
||||
terrain[x + y * mapx] = Mountain;
|
||||
} else if (altitude[y][x] < 12000) {
|
||||
terrain[x + y * mapx] = HighMountain;
|
||||
}
|
||||
else fprintf(stderr,"altitude %d did not get filled in?\n", altitude[y][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now just fill in the spaces randomly. */
|
||||
n=0;
|
||||
r1 = random();
|
||||
r2 = random();
|
||||
for (x=0; x<mapx; x++) {
|
||||
for (y=0; y<mapy; y++) {
|
||||
if (terrain[x + y * mapx] != None) continue;
|
||||
n++;
|
||||
if (altitude[y][x] < 10) {
|
||||
terrain[x + y * mapx] = Swamp + (r1 % 2);
|
||||
}
|
||||
if (altitude[y][x] < 1000) {
|
||||
terrain[x + y * mapx] = Grass + (r1 % 3);
|
||||
} else if (altitude[y][x] < 3000) {
|
||||
terrain[x + y * mapx] = EverGreens + (r2 % 9);
|
||||
} else if (altitude[y][x] < 5000) {
|
||||
terrain[x + y * mapx] = Hills + (r2 % 3);
|
||||
} else if (altitude[y][x] < 9000) {
|
||||
terrain[x + y * mapx] = Mountain;
|
||||
} else if (altitude[y][x] < 12000) {
|
||||
terrain[x + y * mapx] = HighMountain;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"Filled in %d spaces\n",n);
|
||||
if ((mapx / MAP_SIZE) * MAP_SIZE != mapx ||
|
||||
(mapy / MAP_SIZE) * MAP_SIZE != mapy) {
|
||||
fprintf(stderr,"Warning - generated map does not evenly tile.\n");
|
||||
}
|
||||
for (nx= STARTX; nx<(STARTX + (mapx/ MAP_SIZE)); nx++) {
|
||||
for (ny= STARTY; ny<(STARTY + (mapy/ MAP_SIZE)); ny++) {
|
||||
ax = (nx - STARTX) * MAP_SIZE;
|
||||
ay = (ny - STARTY) * MAP_SIZE;
|
||||
|
||||
sprintf(name,MAP_FORMAT,nx,ny);
|
||||
if ((fp=fopen(name, "w"))==NULL) {
|
||||
fprintf(stderr,"unable to open %s\n", name);
|
||||
}
|
||||
/* Write the header for the map */
|
||||
fprintf(fp,"arch map\n");
|
||||
fprintf(fp,"name %s\n", name);
|
||||
fprintf(fp,"width %d\n", MAP_SIZE);
|
||||
fprintf(fp,"height %d\n", MAP_SIZE);
|
||||
/* Not used right now, but useful to include */
|
||||
fprintf(fp,"outdoor 1\n", MAP_SIZE);
|
||||
|
||||
/* don't do difficult, reset time, or enter coordinates */
|
||||
/* Set up the tile paths */
|
||||
if (ny != STARTY) {
|
||||
fprintf(fp,"tile_path_1 ");
|
||||
fprintf(fp,MAP_FORMAT,nx, ny-1);
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
if ((nx+1) < STARTX + (mapx/ MAP_SIZE)) {
|
||||
fprintf(fp,"tile_path_2 ");
|
||||
fprintf(fp,MAP_FORMAT,nx+1, ny);
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
if ((ny+1) < STARTY + (mapy/ MAP_SIZE)) {
|
||||
fprintf(fp,"tile_path_3 ");
|
||||
fprintf(fp,MAP_FORMAT,nx, ny+1);
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
if (nx != STARTX) {
|
||||
fprintf(fp,"tile_path_4 ");
|
||||
fprintf(fp,MAP_FORMAT,nx-1, ny);
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
fprintf(fp,"end\n");
|
||||
for (x = 0; x<50; x++) {
|
||||
for (y = 0; y < 50; y++) {
|
||||
q = terrain[x + ax + (y + ay)* mapx];
|
||||
fprintf(fp, "arch %s\n",Terrain_Names[q][0]);
|
||||
fprintf(fp,"x %d\n", x);
|
||||
fprintf(fp,"y %d\n", y);
|
||||
q = altitude[y + ay ][x + ax];
|
||||
if (q< -32000) q = -32000;
|
||||
if (q > 32000) q = 32000;
|
||||
fprintf(fp,"elevation %d\n", q);
|
||||
fprintf(fp,"end\n");
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
fp = fopen("cmap", "w");
|
||||
fprintf(fp, "P3 %d %d 255\n", mapy, mapx);
|
||||
for (y=0; y < mapy; y++) {
|
||||
for (x=0; x < mapx; x++) {
|
||||
fprintf(fp, Terrain_Names[terrain[x + y * mapx]][1]);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
main(int argc, char *argv)
|
||||
{
|
||||
int x, y, max_x=500, max_y=500, seed, land=300000, npasses=40, newalt, wpasses=50, water=50000;
|
||||
int n, i, j, k, l, z, w, r, a, write_maps=0;
|
||||
FILE *fp, *lp;
|
||||
int junk;
|
||||
char c;
|
||||
extern char *optarg;
|
||||
|
||||
seed = time(NULL);
|
||||
while ((c = getopt(argc, argv,"x:y:s:l:n:w:p:m"))!=-1) {
|
||||
switch (c) {
|
||||
case 'l':
|
||||
land = atoi(optarg);
|
||||
if (land < 11 ) {
|
||||
fprintf(stderr,"-l must be at least 11\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
water = atoi(optarg);
|
||||
if (water < 1 ) {
|
||||
fprintf(stderr,"-w must be at least 1\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
wpasses = atoi(optarg);
|
||||
if (wpasses < 1 ) {
|
||||
fprintf(stderr,"-w must be at least 1\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
npasses = atoi(optarg);
|
||||
if (npasses < 10 ) {
|
||||
fprintf(stderr,"-n must be at least 10\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
max_x = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
max_y = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
seed = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
write_maps=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (max_x > MAX_SIZE || max_y > MAX_SIZE) {
|
||||
fprintf(stderr,"Max X and Y size is %d\n", MAX_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stderr,"Making %d X %d map, seed %d, land %d, passes = %d\n", max_x, max_y, seed, land, npasses);
|
||||
fprintf(stderr,"wpasses =%d, water=%d\n", wpasses, water);
|
||||
fprintf(stderr,"-x %d -y %d -s %d -p %d -n %d -w %d -l %d\n",
|
||||
max_x, max_y, seed, wpasses, npasses, water, land);
|
||||
|
||||
srandom(seed);
|
||||
|
||||
for (x=20; x < max_x-20; x++)
|
||||
for (y=20; y < max_y-20; y++)
|
||||
altitude[x][y] = BASE_ALT;
|
||||
|
||||
for (x=0; x<max_x; x++) {
|
||||
for (y=0; y<20; y++) {
|
||||
altitude[x][y] = (y -20 ) * 100;
|
||||
altitude[x][max_y - y] = (y -20 ) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
for (y=10; y<max_y-10; y++) {
|
||||
for (x=0; x<20; x++) {
|
||||
altitude[x][y] = (x - 20) * 100;
|
||||
altitude[max_x - x][y] = (x - 20) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
/* This basically produces areas of high varience (eg, islands, peaks, valleys, etc) */
|
||||
|
||||
for (l=0; l<npasses; l++) {
|
||||
x = random()%max_x;
|
||||
y = random()%max_y;
|
||||
/* Weigh our selected starting position a little more towards the center
|
||||
* so the continent is more in the center
|
||||
*/
|
||||
if (random() % 2) {
|
||||
x += random()%max_x;
|
||||
y += random()%max_y;
|
||||
x /=2;
|
||||
y /=2;
|
||||
}
|
||||
n = random()%500+800;
|
||||
|
||||
/* For some portion, try to find a pixel we have yet to modify */
|
||||
if (l> (npasses * 15) / 20) {
|
||||
int tries=0;
|
||||
while (altitude[x][y] == BASE_ALT) {
|
||||
x = random()%max_x;
|
||||
y = random()%max_y;
|
||||
if (random() % 2) {
|
||||
x += random()%max_x;
|
||||
y += random()%max_y;
|
||||
x /=2;
|
||||
y /=2;
|
||||
}
|
||||
tries++;
|
||||
if (tries > 50) {
|
||||
fprintf(stderr,"did not find free space within %d tries\n", tries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (k=1; k< land ; k++) {
|
||||
r = random()%4;
|
||||
switch (r) {
|
||||
case 0: if (x<max_x-1) x++; else x -= random() % (max_x/2); break;
|
||||
case 1: if (y<max_y-1) y++; else y -= random() % (max_y/2); break;
|
||||
case 2: if (x) x--; else x+= random() % (max_x/2); break;
|
||||
case 3: if (y) y--; else y+= random() % (max_y/2); break;
|
||||
}
|
||||
altitude[x][y] += n;
|
||||
if (random()%k < 100)
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make lakes and ocean trenches.
|
||||
* General note - it works better to have more passes, but each
|
||||
* pass doing less working - this results in more consistent lakes
|
||||
* and ocen trenching.
|
||||
*/
|
||||
for (l=0; l<wpasses; l++) {
|
||||
/* for a small portion, we lower the area we make */
|
||||
n = random()%1500-2000;
|
||||
|
||||
x = random()% max_x;
|
||||
y = random()% max_y;
|
||||
|
||||
while (altitude[x][y] > BASE_ALT || altitude[x][y]<-7000) {
|
||||
x = random()% max_x;
|
||||
y = random()% max_y;
|
||||
}
|
||||
for (k=1; k< water ; k++) {
|
||||
r = random()%4;
|
||||
switch (r) {
|
||||
case 0: if (x<max_x-1) x++; break;
|
||||
case 1: if (y<max_y-1) y++; break;
|
||||
case 2: if (x) x--; break;
|
||||
case 3: if (y) y--; break;
|
||||
}
|
||||
altitude[x][y] += n;
|
||||
if (random()%k < 100)
|
||||
n += 1; /*less dramatic as things go on */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This block seems to average out the spaces somewhat to prevent
|
||||
* cliffs and the like.
|
||||
*/
|
||||
#define NUM_PASSES 3
|
||||
r = 10;
|
||||
for (k=0; k<NUM_PASSES; k++) {
|
||||
for (x=2; x<max_x-2; x++) {
|
||||
for (y=2; y<max_y - 2; y++) {
|
||||
newalt = (altitude[x][y] * r + altitude[x-1][y] +
|
||||
altitude[x][y-1] + altitude[x-1][y-1] +
|
||||
altitude[x+1][y] + altitude[x][y+1] +
|
||||
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
||||
altitude[x-1][y+1]) / (r+8);
|
||||
if (altitude[x][y] < 10 || altitude[x][y] > newalt) altitude[x][y] = newalt;
|
||||
}
|
||||
}
|
||||
for (x=max_x-2; x>2; x--) {
|
||||
for (y=max_y-2; y>2; y--) {
|
||||
newalt = (altitude[x][y] * r + altitude[x-1][y] +
|
||||
altitude[x][y-1] + altitude[x-1][y-1] +
|
||||
altitude[x+1][y] + altitude[x][y+1] +
|
||||
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
||||
altitude[x-1][y+1]) / (r+8);
|
||||
if (altitude[x][y] < 10 || altitude[x][y] > newalt) altitude[x][y] = newalt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make this 100 so that we eliminate/reduce the lakiness of
|
||||
* the map that is otherwise generated - otherwise, the map
|
||||
* looks like an archipelligo
|
||||
*/
|
||||
#define AVG_PT -10
|
||||
|
||||
/* water - does the same as above, but try to more equally balnace the spaces*/
|
||||
r = 1;
|
||||
for (k=0; k<40; k++) {
|
||||
for (x=2; x<max_x-2; x++) {
|
||||
for (y=2; y<max_y -2; y++) {
|
||||
if (altitude[x][y] < AVG_PT)
|
||||
altitude[x][y] = (altitude[x][y] * r + altitude[x-1][y] +
|
||||
altitude[x][y-1] + altitude[x-1][y-1] +
|
||||
altitude[x+1][y] + altitude[x][y+1] +
|
||||
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
||||
altitude[x-1][y+1]) / (r+8);
|
||||
}
|
||||
}
|
||||
for (x=max_x-2; x>2; x--) {
|
||||
for (y=max_y-2; y>2; y--) {
|
||||
if (altitude[x][y] < AVG_PT)
|
||||
altitude[x][y] = (altitude[x][y] * r + altitude[x-1][y] +
|
||||
altitude[x][y-1] + altitude[x-1][y-1] +
|
||||
altitude[x+1][y] + altitude[x][y+1] +
|
||||
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
||||
altitude[x-1][y+1]) / (r+8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (write_maps)
|
||||
write_crossfire_maps(max_x, max_y);
|
||||
|
||||
/* Now write the data out */
|
||||
|
||||
fp = fopen("lmap", "w");
|
||||
lp = fopen("pmap", "w");
|
||||
fprintf(fp, "P3 %d %d 255\n", max_y, max_x);
|
||||
for (j=0; j < max_x; j++) {
|
||||
for (k=0; k < max_y; k++) {
|
||||
junk = altitude[j][k];
|
||||
fprintf(lp, "%d ", altitude[j][k]);
|
||||
if (junk < -5000)
|
||||
fprintf(fp, "0 0 127 ");
|
||||
/* Shallow water should really be just at the coastal
|
||||
* area, so put a big gap between shallow and deep.
|
||||
* this also evens out the occurance of the different types
|
||||
* to be more equal
|
||||
*/
|
||||
else if (junk < -99)
|
||||
fprintf(fp, "0 0 192 ");
|
||||
else if (junk < 1)
|
||||
fprintf(fp, "0 0 255 ");
|
||||
else if (junk < 1000)
|
||||
fprintf(fp, "0 240 0 ");
|
||||
else if (junk < 2000)
|
||||
fprintf(fp, "0 220 0 ");
|
||||
else if (junk < 3000)
|
||||
fprintf(fp, "0 200 0 ");
|
||||
else if (junk < 4000)
|
||||
fprintf(fp, "0 180 0 ");
|
||||
else if (junk < 5000)
|
||||
fprintf(fp, "0 160 0 ");
|
||||
else if (junk < 6000)
|
||||
fprintf(fp, "255 130 71 ");
|
||||
else if (junk < 8000)
|
||||
fprintf(fp, "238 121 66 ");
|
||||
else if (junk < 10000)
|
||||
fprintf(fp, "205 104 57 ");
|
||||
else if (junk < 12000)
|
||||
fprintf(fp, "139 71 38 ");
|
||||
else
|
||||
fprintf(fp, "255 255 255 ");
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
This is a guide on what is an acceptable map and what is unacceptable.
|
||||
Only acceptable maps will be put in the official Crossfire map distribution
|
||||
|
||||
1) Check that all exits lead where they are supposed to. Unless there is
|
||||
a specific reason an exit leads only one direction (like a trap door or
|
||||
perhaps a teleporter), players should be able to exit back from where they
|
||||
came from right when they enter the map.
|
||||
|
||||
One way exits/entrances should only be used on objects in which it is
|
||||
obvious it is one way. A house is not an obvious one way entrance. Remember,
|
||||
players may not have the three hours of time it takes to find the exit
|
||||
after being trapped in a map (a work around for this can be have the trap
|
||||
lead to a safe place with no exit which contains a savebed. Thus, the
|
||||
player could save and come back at a later time to find the exit.)
|
||||
|
||||
2.1) Try to make sure the maps are multi player accessible. In towns, this
|
||||
means the road should be at least a couple squares wide, buildings should not
|
||||
be trapped in corners in which case one character standing in front blocks
|
||||
access, etc.
|
||||
|
||||
2.2) Try to make corridors in dungeons or mazes a few squares wide -
|
||||
especially if there is only a single path. If it is a maze with several
|
||||
different paths, single width corridors are acceptable. The main problem
|
||||
here are big labyrinths in which only one monster attacks at a time, and
|
||||
which there is only 1 or two routes. If two players enter such a map, the
|
||||
one that went in first will be in the lead the entire time.
|
||||
|
||||
2.3) Avoid spiral or single path mazes that just have monsters lining the
|
||||
corridor. These are not very good for multiple players, not particularly
|
||||
interesting (map justs consists of killing all the monsters), and tend to be
|
||||
an easy and safe way to gain experience.
|
||||
|
||||
3) Don't put:
|
||||
|
||||
3.1) extremely valuable treasure right next to the entrance, or
|
||||
nearby. Players should need to work to get treasure. If the treasure is
|
||||
fairly worthless (food, or non magical items), this would be acceptable.
|
||||
But a character should not be able to pop in, pick up a potion, spellbook,
|
||||
or a lot of diamonds, and then pop out again, without ever meeting
|
||||
a monster.
|
||||
|
||||
3.2) Don't put monsters of high experience point near to entrance where they
|
||||
are trapped. Low level player could boost their experience high by using some
|
||||
weapons or spells from distance without danger. For example find a trapped
|
||||
troll and get wand of fireball.
|
||||
|
||||
3.3) monsters on top of other monsters. A troll should not be sitting on
|
||||
top of an oriental dragon. The only exception to this would be if a monster
|
||||
could be on top of another monster (making sense) and hiding it at the same
|
||||
time. A troll on top of an oriental dragon does not make sense (could not
|
||||
fit), nor can the troll hide the oriental dragon. Using tricks like these
|
||||
which are only applicable due to display limitations is something that
|
||||
should not be done, nor should the player need to click on every monster he
|
||||
encounters to see if something is below it. (as a side note, doing this
|
||||
will tend to lock the monsters into position, making them unable to move.)
|
||||
|
||||
3.4) Large groups of monsters that can be killed quickly with spells. A
|
||||
fairly popular tactic to make high level maps is just to put 30 dragons (or
|
||||
other tough monsters) in a big room. Do not do this. All the player needs
|
||||
to do is cast a dozen icestorms, and quickly gets millions of experience.
|
||||
Likewise, it is unlikely that any more than 2 or 3 large (multisquare)
|
||||
monsters will be able to attack a player or party at once - the remaining 25
|
||||
will be blocked from doing anything. This then makes it so that having 30
|
||||
dragons is not any tougher than having 3.
|
||||
|
||||
If you want to make a high level map, instead of tossing a lot of monsters
|
||||
on it, take existing monsters and make them tougher. Increase their
|
||||
hit points, level (which then means spells they use do more damage), add
|
||||
immunities or protections, remove vulnerabilities, change attack types, etc.
|
||||
Try not to totally change the characteristics of a known monster - a normal
|
||||
dragon should still be dragon like. Also, remember to adjust experience
|
||||
that the monster gives.
|
||||
|
||||
4) Try to keep the treasure in line with the difficulty. 5 potions should
|
||||
not be given out for defeating orcs or gnolls (even if there are a lot
|
||||
of them), but if you need to defeat several dragons to get to the
|
||||
potions, that is fine. Likewise, if it is likely a lot of spells will be
|
||||
needed to defeat the monster, and those spells have a chance of destroying
|
||||
the items, then perhaps a few extra items to take this into consideration
|
||||
is not a bad idea.
|
||||
|
||||
5) If use of a specific skill/class/spell is needed to complete the map,
|
||||
that should be stated near the map entrance. How clearly this is stated
|
||||
depends on the circumstance. If use of a certain skill is needed, there is
|
||||
probably no good way other than to state that a skill is needed. If use of
|
||||
a certain spell is needed, stating that a spell caster of XX level might be
|
||||
sufficient, with the assumption that a spellcaster of that level would have
|
||||
the spell. It is safe to assume that all characters can fight, but
|
||||
spellcasting (especially certain spells) should not be assumed, and thus
|
||||
should be stated.
|
||||
|
||||
Also, don't put in hidden rooms requiring dimension door if they only real
|
||||
way to know about them is pure luck or looking at the map. If you want to
|
||||
do something like that, at least put some clues in.
|
||||
|
||||
If a certain skill would make a map easier, but is not required, you don't
|
||||
need to necessary state it. The idea of this is that it can be frustrating
|
||||
to wander into some map, complete most of it, but find out you can't
|
||||
finish the map because you lack some skill or spell.
|
||||
|
||||
5.1) A map should be designed so that a character can never be
|
||||
trapped in a room (except via other player interaction.) A character should
|
||||
never be forced to dimension door or word of recall out of a map because
|
||||
some gate closed behind him. For a character without these spells,
|
||||
it would mean death. A simple method around this is put a lever on
|
||||
both sides of the door. If the door is opened by special actions (saying
|
||||
things, dropping things), just put the lever on the hard to get side of
|
||||
the gate.
|
||||
|
||||
6) If a map require multiple players to simultaneous be on it to solve
|
||||
the map, put a sign or message so players know. Such maps would be those
|
||||
that require manipulation of levers or buttons in certain sequences in
|
||||
order to get through gates.
|
||||
|
||||
Don't make ends of maps require multi users. This ruins that map for
|
||||
single players (not able to complete it), and makes a map that requires
|
||||
multiple players for only a small portion.
|
||||
|
||||
7) Try not to make the maps too many levels deep. To get to the goal,
|
||||
it should not require a 6 hour continous sitting, as the player works
|
||||
through each map to get to the next. Multi level maps are fine - just
|
||||
don't over do it. One way to do this is have several maps with a key
|
||||
or other special item at the end. The final map could have the various
|
||||
battles, and then a series of gates/altars which uses up these keys.
|
||||
|
||||
8) Shops:
|
||||
|
||||
8.1) Don't put super stores in any towns or villages you create. With the
|
||||
growing number of maps, players can already make a trip to all the different
|
||||
towns to try and find certain items. A one stop find all shop is not
|
||||
interesting. A good maximum size is about the same size of the shops
|
||||
in the starting village.
|
||||
|
||||
Also, making six magic shops of that size and putting them in the same
|
||||
town is not any better than one large magic shop. If you want to have
|
||||
specialized shops, then make each shop smaller. If you just want one
|
||||
shop that sells every type of item (magic, armor, weapons, food, etc), then
|
||||
a large shop is permissable.
|
||||
|
||||
8.2) Make sure the entire interior the shop is covered with tiles. Likewise,
|
||||
don't put shops that lead to areas without tiles without going over one of
|
||||
the 'magic doormats'. A player should never be able to get an unpaid
|
||||
item out of a shop, whether via exit that does not go over the magic
|
||||
doormat, or through spells.
|
||||
|
||||
|
||||
9) Don't make maps which require high level characters that low level
|
||||
characters can wonder into without warning. Put a warning sign nearby,
|
||||
or gates or doors so the player can see they are in over their head, instead
|
||||
of instantly getting toasted the second they enter the map.
|
||||
|
||||
|
||||
10) The structure of the map should make sense. That is to say,
|
||||
if you enter a house, the house should then not have a tower inside. Or
|
||||
a door to a shop. In other words, if a map has an exit to another map,
|
||||
that exit should make sense (ie, another level, tunnels, dungeons
|
||||
all make sense. However, another building the size of the original
|
||||
does not make sense.
|
||||
|
||||
|
||||
11) Try to keep the difficulty throughout the map(s) about the same.
|
||||
The first monster in the map should not be the most difficult monster,
|
||||
nor should the last monster be orders of magnitude more difficult
|
||||
than anything before it.
|
||||
|
||||
It is very frustating to play a map, killing most every monster without
|
||||
much difficulty, only to find that last monster unkillable.
|
||||
|
||||
It is reasonable to have the monster increase in difficulty. Also, if the
|
||||
map has no quest or end goal, then having a very difficult monster around is
|
||||
not unreasonable, as long as it does prevent the player from progressing to
|
||||
the next map.
|
||||
|
||||
12) Do not put directors with bullet, lightning, fireball, etc. that
|
||||
are a loop or continuous. Example: Do not have two directors, each
|
||||
facing each other, with a bullet wall firing into them at the side.
|
||||
|
||||
Having numerous directors is fine. But make sure that eventually,
|
||||
there will be an exit/detonation point for the fired spell. Having
|
||||
loops that go for over typically bring the game to a halt, as the
|
||||
objects just multiply and the game consumes more and more cpu time.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
The following are various suggestions for making good or interesting
|
||||
maps. A map that does not need to follow all these hints to be accepted,
|
||||
but following these hints will make for more interesting or playable maps.
|
||||
|
||||
|
||||
1) Try to create only small maps. If you have a large map in mind, try to
|
||||
see if you can possible split it up in several separate sections, and place
|
||||
those sections in different maps. Many small maps use much less memory than
|
||||
one large map, since crossfire doesn't yet support swapping of portions of
|
||||
maps. Also, with small maps, the time to load it from and store it to disc
|
||||
becomes so short that it's impossible to notice. In this context, small
|
||||
means about 32x32, though it's actually the number of objects in the map
|
||||
which count.
|
||||
|
||||
What is potentially more critical than the size of the map is the number
|
||||
of objects (memory usage), and live objects (cpu usage, as each would need
|
||||
to be processed.)
|
||||
|
||||
Also, remember that if you make very large maps, all generators will be
|
||||
cranking out monsters whenever anyone is on it. This could mean that a lot
|
||||
of monsters have been generated before a player even gets to the area where
|
||||
they are being created.
|
||||
|
||||
Related to this: If a map contains multiple levels, make multiple maps.
|
||||
Many times, if the level is small, the mapmaker may think I will just put
|
||||
all the levels on one larger map. This makes the map a little less readable
|
||||
to others. Also, things like magic mapping and dimension door can lead to
|
||||
unexpected results.
|
||||
|
||||
2) Make a plot! A map withot a plot becomes just another mindless
|
||||
"Kill'em all". For instance, create a story which explains why there
|
||||
are npc's here and monsters there, fragment the story up and put
|
||||
bits and hints of it in various writables (books) and npc-conversations.
|
||||
|
||||
If you are going to make a mindless kill them all map, at least put some
|
||||
reward in the map that can only be accessed after all the monsters have been
|
||||
killed. The only thing worse than a kill them all map is a kill them all map
|
||||
which you get nothing out of.
|
||||
|
||||
Avoid maps where all the monsters are lined up, and only one can attack
|
||||
you at a time. This just makes an easy (and relatively safe) way for
|
||||
a character to gain experience and treasure, and is not especially
|
||||
interesting or challenging.
|
||||
|
||||
2.1) A good idea for the rewards at the end of quests are specific
|
||||
items (luggage, spellbook of some otherwise not available spell,
|
||||
special weapon, spellcrystal, etc.) It is much more interesting to
|
||||
put a specific item instead of something like a random artifact. Feel
|
||||
free to mutate or otherwise change existing artifacts to create your own.
|
||||
|
||||
This has two advantages: one, the player will get to know where certain
|
||||
items are. Having to search endlessly for a specific item gets tedious.
|
||||
Two, it reduces the incentive to keep repeating the quest (repeating
|
||||
quests is not inherently bad) If the reward is a random artifact, a player
|
||||
may very well keep repeating the quest until the item he looks for comes up.
|
||||
By doing specific items, this will not happen.
|
||||
|
||||
3) Make puzzles! Use all those different object types: buttons, handles,
|
||||
doors, altars, pedestals, triggers, timed gates, etc... Hide special "keys"
|
||||
needed to get further in special places, and use text-puzzles to describe
|
||||
where they are hidden and how they must be used. The possibilities are
|
||||
endless! Remember, you can also hide buttons under floors, making it more
|
||||
difficult for the character to find the trigger points.
|
||||
|
||||
|
||||
4) But don't make too much big labyrinths. Making of labyrinths is (too)
|
||||
easy with crossedit, just select auto-joining and make zig-zag with mouse.
|
||||
But the results of these are quite tiring. If you make ones, try make
|
||||
some idea into it.
|
||||
|
||||
Related: Don't make maps where the only way to find something is examination
|
||||
of each and every wall. For example, don't have a big map with lots of walls,
|
||||
but the key to moving onward is to find the weak wall and pass through it.
|
||||
Nor should big mazes full of invisible walls be made where the way to get
|
||||
through it is just by going in some direction, finding out you can't move
|
||||
anymore in that direction, go some other one, etc.
|
||||
|
||||
5) Give the npc's information! An npc's knowledge about hidden treasure surely
|
||||
makes it interesting to have a conversation with it.
|
||||
|
||||
|
||||
6) Feel free to add some traps, but be careful to not make them too
|
||||
deadly without adequate warning.
|
||||
|
||||
|
||||
7) Don't mix the monsters too badly. Let there be at least some logic
|
||||
behind why they are grouped in a single room. Undeads together with
|
||||
undeads, for instance, but not together with kobolds...
|
||||
Big dragons usually don't live together with mice... Fire immune creatures
|
||||
generally dislike ice immune creatures.
|
||||
|
||||
Also, limit use of monsters that multiply rapidly (mice, slimes). A map
|
||||
that is easily overwhelmed with these creatures quickly becomes useless.
|
||||
|
||||
8) Give your maps a meaningfull name (like John's tower, level 1).
|
||||
This way, these can be used instead of the map paths in the highscore
|
||||
file. Also, in terms of the actual file name, try to use numeric
|
||||
level identifiers (ie, maze.1, maze.2, ... instead of maze.first, maze.second,
|
||||
etc.) The former maps the levels sorted a little bit nicer in the
|
||||
directory.
|
||||
|
||||
9) Try to make the map so that it links in with the existing world. Most
|
||||
people want to make their own continent, which is then accessed by ship
|
||||
or other fast means. While convenient, this creates many island
|
||||
continents. The problems with this are that any feeling of relation is lost
|
||||
(where is that island continent), and it makes item searching in shops very
|
||||
easy - if you can access half a dozen shops quickly and safely by taking
|
||||
boats, you have a decent chance of finding the item you want.
|
||||
|
||||
Also, it seems that when most people start making maps, the first thing they
|
||||
do is create a new town or village. There are already a lot of towns and
|
||||
villages out there. If you are just going to create a few new buildings,
|
||||
instead of going to the effort and time of creating your own island with a
|
||||
town, just create the buildings, and plug them into one of the existing
|
||||
towns or the terrain someplace. Many of the towns right now have many
|
||||
unused buildings.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Technical map hints:
|
||||
|
||||
1) If you are creating a new archetype, it only needs to go into the general
|
||||
archetype distribution if it has an image associated with it, or it has
|
||||
general use (a new monster). Something that uses already existing images
|
||||
can be set up in the map file itself (through setting various variables).
|
||||
|
||||
2) When modifying an existing archetype into a new one (either new face
|
||||
or new type), use the archetype that has the most variables in common.
|
||||
Thus, if you want to create a monster called a 'bouldar', it is probably
|
||||
best to take a monster of some sort and change its face instead of taking
|
||||
the existing boulder archetype and changing its type, hit points, speed,
|
||||
etc.
|
||||
|
||||
3) Changing color is no longer possible in maps - instead, a new face
|
||||
and image must be created, and then put in the standard distribution.
|
||||
The archetype collection script will automatically pull out face information
|
||||
from archetype files.
|
||||
|
||||
4) Try to keep maps readable by other people who might edit them. Thus,
|
||||
instead of modifying a woods space so it also acts as an exit, just put an
|
||||
invisible exit under the woods space. This has the same functionality, but
|
||||
it makes it much easier for other players to see what this space does. (Side
|
||||
note - if you want it so that players actually need to apply the space
|
||||
to enter, you will need to change the face of exit for this to work. If
|
||||
you do this, you should also accompany it with a magic mouth.)
|
||||
|
||||
5) Make sure you set the difficulty field in the map attributes to
|
||||
somethign meaningful. Crossfire will calculate a default dificulty,
|
||||
but its formula is hardly ideal. The difficulty of a map determines how
|
||||
magical the treasure will be (and some treasure types won't show up
|
||||
unless the map has a certain difficulty level.)
|
||||
|
||||
6) Don't be too intimidated about writing new code if there is something
|
||||
you would like to be able to do, but just isn't supported. If you are not
|
||||
the code writing time, make a suggestion. Worst case is it gets ignored.
|
||||
But many times, I have written code because I had some idea which just
|
||||
was not possible at the time (ie, the apartment in the starting town
|
||||
required an expansion/change of the unique item code.)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 568 KiB |
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# This script goes through and updates the exits for maps.
|
||||
# First pass, so a lot of the options need to be set in this
|
||||
# script. It will search all directories in and below your current
|
||||
# working directory, so run from the directory you want to update.
|
||||
|
||||
# Written by Mark Wedel (mwedel@sonic.net)
|
||||
# This borrows some amount of code from the map_info script written
|
||||
# by Tero Haatanen <Tero.Haatanen@lut.fi>
|
||||
|
||||
# Name of the old map that we update exits on
|
||||
# Note that this can be a regexp.
|
||||
$OLD_MAP_NAME="(/city/city|../city|../../city)";
|
||||
|
||||
# OLD_MAP_STARTX/Y and OLD_MAP_ENDX/Y determine the range for the
|
||||
# updates. For example, scorn/city was broken up on two of the
|
||||
# map tiles, so this gets used to correspond that properly.
|
||||
# you can use very large END values just to make sure the entire
|
||||
# map is covered
|
||||
$OLD_MAP_STARTX=10;
|
||||
$OLD_MAP_STARTY=0;
|
||||
$OLD_MAP_ENDX=100;
|
||||
$OLD_MAP_ENDY=100;
|
||||
|
||||
# New map names. OFFX/Y is the offset compared to the old values - these
|
||||
# can be negative provided that STARTX above is positive (eg, the
|
||||
# map is being shifted.)
|
||||
|
||||
$NEW_MAP_NAME="/world/world_105_115";
|
||||
$NEW_MAP_OFFX=-10;
|
||||
$NEW_MAP_OFFY=18;
|
||||
|
||||
$VERBOSE=0;
|
||||
|
||||
|
||||
if ((($OLD_MAP_STARTX + $NEW_MAP_OFFX) < 0) ||
|
||||
(($OLD_MAP_STARTY + $NEW_MAP_OFFY) < 0 )) {
|
||||
print "Current settings will result in negative destination coordinates.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
&maplist(".");
|
||||
|
||||
|
||||
while ($file = shift (@maps)) {
|
||||
&updatemap;
|
||||
}
|
||||
|
||||
|
||||
exit;
|
||||
|
||||
# return table containing all objects in the map
|
||||
sub updatemap {
|
||||
local ($m, $made_change=0);
|
||||
$last = "";
|
||||
$parent = "";
|
||||
|
||||
# Note that $/ is the input record seperator. By changing
|
||||
# this to \nend\n, it means that when we read from the file,
|
||||
# we basically read an entire arch at the same time. Note that
|
||||
# given this, $ in regexps matches this value below, and not
|
||||
# a newline. \n should generally be used instead of $ in
|
||||
# regexps if you really want the end of line.
|
||||
# Similary, ^ matches start of record, which means the arch line.
|
||||
|
||||
$/ = "\nend\n";
|
||||
if (! open (IN, $file)) {
|
||||
print "Can't open map file $file\n";
|
||||
return;
|
||||
}
|
||||
$_ = <IN>;
|
||||
if (! /^arch map\n/) {
|
||||
print "Error: file $file isn't mapfile. ($_)\n";
|
||||
return;
|
||||
}
|
||||
if (! open(OUT, ">$file.new")) {
|
||||
print "Can't open output file $file.new\n";
|
||||
return;
|
||||
}
|
||||
print OUT $_;
|
||||
if ($VERBOSE) {
|
||||
print "Testing $file, ";
|
||||
print /^name (.+)$/ ? $1 : "No mapname";
|
||||
print ", size [", /^x (\d+)$/ ? $1 : 16;
|
||||
print ",", /^y (\d+)/ ? $1 : 16, "]";
|
||||
|
||||
if (! /^msg$/) {
|
||||
print ", No message\n";
|
||||
} elsif (/(\w+@\S+)/) {
|
||||
print ", $1\n";
|
||||
} else {
|
||||
print ", Unknown\n";
|
||||
}
|
||||
$printmap=0;
|
||||
}
|
||||
else {
|
||||
$name= /^name (.+)$/ ? $1 : "No mapname";
|
||||
$x= /^x (\d+)$/ ? $1 : 16;
|
||||
$y= /^y (\d+)/ ? $1 : 16;
|
||||
$mapname="Map $file, $name, size [$x, $y]\n" ;
|
||||
$printmap=1;
|
||||
}
|
||||
|
||||
while (<IN>) {
|
||||
if (($m = (@_ = /^arch \S+\s*$/g)) > 1) {
|
||||
$parent = /^arch (\S+)\s*$/;
|
||||
print OUT $_;
|
||||
|
||||
# Object has an inventory. Just read through until we get
|
||||
# an end
|
||||
while (<IN>) {
|
||||
last if (/((.|\n)*end\n)(arch (.|\n)*\nend\n)/);
|
||||
print OUT $_;
|
||||
}
|
||||
$parent="";
|
||||
# Objects with inventory should not contain exits, so
|
||||
# do not need to try and process them. Likewise, the objects
|
||||
# in the inventory should not contain exits.
|
||||
} else {
|
||||
if (m#\nslaying $OLD_MAP_NAME\n#) {
|
||||
$destx = /\nhp (\d+)\n/ ? $1 : 0;
|
||||
$desty = /\nsp (\d+)\n/ ? $1 : 0;
|
||||
if ($destx >= $OLD_MAP_STARTX && $destx <= $OLD_MAP_ENDX &&
|
||||
$desty >= $OLD_MAP_STARTY && $desty <= $OLD_MAP_ENDY) {
|
||||
# Ok. This exit matches our criteria. Substitute in
|
||||
# the new values
|
||||
s/slaying $OLD_MAP_NAME\n/slaying $NEW_MAP_NAME\n/;
|
||||
$destx += $NEW_MAP_OFFX;
|
||||
$desty += $NEW_MAP_OFFY;
|
||||
s/\nhp \d+\n/\nhp $destx\n/;
|
||||
s/\nsp \d+\n/\nsp $desty\n/;
|
||||
$made_change=1;
|
||||
}
|
||||
}
|
||||
print OUT $_;
|
||||
} # else not an object with inventory
|
||||
} # while <IN> LOOP
|
||||
close (IN);
|
||||
close(OUT);
|
||||
if ($made_change) {
|
||||
print "$file has changed\n";
|
||||
unlink($file);
|
||||
rename("$file.new", $file);
|
||||
}
|
||||
else {
|
||||
unlink("$file.new");
|
||||
}
|
||||
}
|
||||
|
||||
# @maps contains all filenames
|
||||
sub maplist {
|
||||
local ($dir, $file, @dirs) = shift;
|
||||
|
||||
opendir (DIR , $dir) || die "Can't open directory : $dir\n";
|
||||
while ($file = readdir (DIR)) {
|
||||
next if ($file eq "." || $file eq ".." || $file eq "CVS");
|
||||
|
||||
$file = "$dir/$file";
|
||||
push (@dirs, $file) if (-d $file);
|
||||
push (@maps, $file) if (-f $file);
|
||||
}
|
||||
closedir (DIR);
|
||||
|
||||
# recurcive handle sub-dirs too
|
||||
while ($_ = shift @dirs) {
|
||||
&maplist ($_);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue