commit b4ee7ba788ed9b84ad592b0e5ea328b93b20b2ec Author: mwedel Date: Tue Dec 18 04:42:23 2001 +0000 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-b93c2d0d6712 diff --git a/Info/README b/Info/README new file mode 100644 index 000000000..2c8e79796 --- /dev/null +++ b/Info/README @@ -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. +------------------------------------------------------------------------------ diff --git a/Info/ann_map.gif b/Info/ann_map.gif new file mode 100644 index 000000000..b1f3376c8 Binary files /dev/null and b/Info/ann_map.gif differ diff --git a/Info/el_map.gif b/Info/el_map.gif new file mode 100644 index 000000000..0075debae Binary files /dev/null and b/Info/el_map.gif differ diff --git a/Info/land.c b/Info/land.c new file mode 100644 index 000000000..35d2faf79 --- /dev/null +++ b/Info/land.c @@ -0,0 +1,541 @@ +#include +#include + +#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=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 -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 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 (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 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 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; x2; 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); +} diff --git a/Info/mapguide b/Info/mapguide new file mode 100644 index 000000000..a532ba736 --- /dev/null +++ b/Info/mapguide @@ -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.) + diff --git a/Info/terrain_map.gif b/Info/terrain_map.gif new file mode 100644 index 000000000..dd3cddb7c Binary files /dev/null and b/Info/terrain_map.gif differ diff --git a/Info/update_exits.pl b/Info/update_exits.pl new file mode 100755 index 000000000..3885e67a7 --- /dev/null +++ b/Info/update_exits.pl @@ -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 + +# 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; + } + $_ = ; + 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 () { + 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 () { + 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 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 ($_); + } +} +