/* * static char *rcsid_special_c = * "$Id: special.c 11578 2009-02-23 22:02:27Z lalo $"; */ /* CrossFire, A Multiplayer game for X-windows Copyright (C) 2002 Mark Wedel & Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at crossfire-devel@real-time.com */ /** * @file * Those functions handle placement of fountains, submaps, and so on. */ #include #include #include /** * @defgroup SPECIAL_xxx Special objects in random map */ /*@{*/ #define NUM_OF_SPECIAL_TYPES 4 #define NO_SPECIAL 0 #define SPECIAL_SUBMAP 1 #define SPECIAL_FOUNTAIN 2 #define SPECIAL_EXIT 3 /*@}*/ /** * @defgroup HOLE_xxx Random map holes */ /*@{*/ #define GLORY_HOLE 1 #define ORC_ZONE 2 #define MINING_ZONE 3 #define NR_OF_HOLE_TYPES 3 /*@}*/ /** * Erases all objects (except floor) in the given rectangle. * @param map * map to process. * @param xstart * @param ystart * top-left coordinates of zone. * @param xsize * @param ysize * size of zone. */ void nuke_map_region(mapstruct *map, int xstart, int ystart, int xsize, int ysize) { int i, j; object *tmp; for (i = xstart; i < xstart+xsize; i++) for (j = ystart; j < ystart+ysize; j++) { for (tmp = GET_MAP_OB(map, i, j); tmp != NULL; tmp = tmp->above) { if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR)) { if (tmp->head) tmp = tmp->head; remove_ob(tmp); free_object(tmp); tmp = GET_MAP_OB(map, i, j); } if (tmp == NULL) break; } } } /** * Copy in_map into dest_map at point x,y * @param dest_map * map where to copy to. * @param in_map * map to copy from. * @param x * @param y * coordinates to put in_map to. */ void include_map_in_map(mapstruct *dest_map, mapstruct *in_map, int x, int y) { int i, j; object *tmp; object *new_ob; /* First, splatter everything in the dest map at the location */ nuke_map_region(dest_map, x, y, MAP_WIDTH(in_map), MAP_HEIGHT(in_map)); for (i = 0; i < MAP_WIDTH(in_map); i++) for (j = 0; j < MAP_HEIGHT(in_map); j++) { for (tmp = GET_MAP_OB(in_map, i, j); tmp != NULL; tmp = tmp->above) { /* don't copy things with multiple squares: must be dealt with specially. */ if (tmp->head != NULL) continue; new_ob = arch_to_object(tmp->arch); copy_object_with_inv(tmp, new_ob); if (QUERY_FLAG(tmp, FLAG_IS_LINKED)) add_button_link(new_ob, dest_map, tmp->path_attuned); new_ob->x = i+x; new_ob->y = j+y; insert_multisquare_ob_in_map(new_ob, dest_map); } } } /** * Finds a place to put a submap into. * @param map * map to insert. * @param layout * where to insert into. * @param ix * @param iy * coordinates of suitable location if return is 1. * @param xsize * @param ysize * size of layout. * @return * 0 if no space found, 1 else. */ int find_spot_for_submap(mapstruct *map, char **layout, int *ix, int *iy, int xsize, int ysize) { int tries; int i = 0, j = 0; /* initialization may not be needed but prevents compiler warnings */ int is_occupied = 0; int l, m; /* don't even try to place a submap into a map if the big map isn't sufficiently large. */ if (2*xsize > MAP_WIDTH(map) || 2*ysize > MAP_HEIGHT(map)) return 0; /* search a bit for a completely free spot. */ for (tries = 0; tries < 20; tries++) { /* pick a random location in the layout */ i = RANDOM()%(MAP_WIDTH(map)-xsize-2)+1; j = RANDOM()%(MAP_HEIGHT(map)-ysize-2)+1; is_occupied = 0; for (l = i; l < i+xsize; l++) for (m = j; m < j+ysize; m++) is_occupied |= layout[l][m]; if (!is_occupied) break; } /* if we failed, relax the restrictions */ if (is_occupied) { /* failure, try a relaxed placer. */ /* pick a random location in the layout */ for (tries = 0; tries < 10; tries++) { i = RANDOM()%(MAP_WIDTH(map)-xsize-2)+1; j = RANDOM()%(MAP_HEIGHT(map)-ysize-2)+1; is_occupied = 0; for (l = i; l < i+xsize; l++) for (m = j; m < j+ysize; m++) if (layout[l][m] == 'C' || layout[l][m] == '>' || layout[l][m] == '<') is_occupied |= 1; } } if (is_occupied) return 0; *ix = i; *iy = j; return 1; } /** * Places a special fountain on the map. * @param map * map to place to. * @todo * change logic to allocate potion only if success? */ void place_fountain_with_specials(mapstruct *map) { int ix, iy, i = -1, tries = 0; mapstruct *fountain_style = find_style("/styles/misc", "fountains", -1); object *fountain = create_archetype("fountain"); object *potion = get_object(); copy_object(pick_random_object(fountain_style), potion); while (i < 0 && tries < 10) { ix = RANDOM()%(MAP_WIDTH(map)-2)+1; iy = RANDOM()%(MAP_HEIGHT(map)-2)+1; i = find_first_free_spot(fountain, map, ix, iy); tries++; }; if (i == -1) { /* can't place fountain */ free_object(fountain); free_object(potion); return; } ix += freearr_x[i]; iy += freearr_y[i]; potion->face = fountain->face; SET_FLAG(potion, FLAG_NO_PICK); SET_FLAG(potion, FLAG_IDENTIFIED); potion->name = add_string("fountain"); potion->name_pl = add_string("fountain"); potion->x = ix; potion->y = iy; potion->material = M_ADAMANT; fountain->x = ix; fountain->y = iy; insert_ob_in_map(fountain, map, NULL, 0); insert_ob_in_map(potion, map, NULL, 0); } /** * Place an exit with a resource map. * @param map * where to put the exit. * @param hole_type * type of random map to link to, see @ref HOLE_xxx "HOLE_xxx". * @param RP * parameters from which map was generated. */ void place_special_exit(mapstruct *map, int hole_type, RMParms *RP) { int ix, iy, i = -1; char buf[HUGE_BUF]; const char *style, *decor, *mon; mapstruct *exit_style = find_style("/styles/misc", "obscure_exits", -1); int g_xsize, g_ysize; object *the_exit = get_object(); if (!exit_style) return; copy_object(pick_random_object(exit_style), the_exit); while (i < 0) { ix = RANDOM()%(MAP_WIDTH(map)-2)+1; iy = RANDOM()%(MAP_HEIGHT(map)-2)+1; i = find_first_free_spot(the_exit, map, ix, iy); } ix += freearr_x[i]; iy += freearr_y[i]; the_exit->x = ix; the_exit->y = iy; if (!hole_type) hole_type = RANDOM()%NR_OF_HOLE_TYPES+1; switch (hole_type) { case GLORY_HOLE: { /* treasures */ g_xsize = RANDOM()%3+4+RP->difficulty/4; g_ysize = RANDOM()%3+4+RP->difficulty/4; style = "onion"; decor = "wealth2"; mon = "none"; break; } case ORC_ZONE: { /* hole with orcs in it. */ g_xsize = RANDOM()%3+4+RP->difficulty/4; g_ysize = RANDOM()%3+4+RP->difficulty/4; style = "onion"; decor = "wealth2"; mon = "orc"; break; } case MINING_ZONE: { /* hole with orcs in it. */ g_xsize = RANDOM()%9+4+RP->difficulty/4; g_ysize = RANDOM()%9+4+RP->difficulty/4; style = "maze"; decor = "minerals2"; mon = "none"; break; } default: /* undefined */ LOG(llevError, "place_special_exit: undefined hole type %d\n", hole_type); return; break; } /* Need to be at least this size, otherwise the load * code will generate new size values which are too large. */ if (g_xsize < MIN_RANDOM_MAP_SIZE) g_xsize = MIN_RANDOM_MAP_SIZE; if (g_ysize < MIN_RANDOM_MAP_SIZE) g_ysize = MIN_RANDOM_MAP_SIZE; write_parameters_to_string(buf, g_xsize, g_ysize, RP->wallstyle, RP->floorstyle, mon, "none", style, decor, "none", RP->exitstyle, NULL, NULL, NULL, OPT_WALLS_ONLY, 0, 0, 1, RP->dungeon_level, RP->dungeon_level, RP->difficulty, RP->difficulty, -1, 1, 0, 0, 0, 0, RP->difficulty_increase); the_exit->slaying = add_string("/!"); the_exit->msg = add_string(buf); insert_ob_in_map(the_exit, map, NULL, 0); } /** * Main function for specials. * @param map * map to alter. * @param layout * layout the map was generated from. * @param RP * parameters the map was generated from. */ void place_specials_in_map(mapstruct *map, char **layout, RMParms *RP) { mapstruct *special_map; int ix, iy; /* map insertion locatons */ int special_type; /* type of special to make */ special_type = RANDOM()%NUM_OF_SPECIAL_TYPES; switch (special_type) { /* includes a special map into the random map being made. */ case SPECIAL_SUBMAP: { special_map = find_style("/styles/specialmaps", NULL, RP->difficulty); if (special_map == NULL) return; if (find_spot_for_submap(map, layout, &ix, &iy, MAP_WIDTH(special_map), MAP_HEIGHT(special_map))) include_map_in_map(map, special_map, ix, iy); break; } /* Make a special fountain: an unpickable potion disguised as * a fountain, or rather, colocated with a fountain. */ case SPECIAL_FOUNTAIN: { place_fountain_with_specials(map); break; } /* Make an exit to another random map, e.g. a gloryhole. */ case SPECIAL_EXIT: { place_special_exit(map, 0, RP); break; } } }