/* * CrossFire, A Multiplayer game for X-windows * * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team * 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 */ #include #include #include #include #include #include "../include/autoconf.h" static int pointer_strcmp (const void *p1, const void *p2) { const char *s1 = *(const char **) p1; const char *s2 = *(const char **) p2; return (strcmp (s1, s2)); } /* This is our own version of scandir/select_regular_files/sort. * To support having subdirectories in styles, we need to know * if in fact the directory we read is a subdirectory. However, * we can't get that through the normal dirent entry. So * instead, we do our own where we do have the full directory * path so can do stat calls to see if in fact it is a directory. * dir is the name of the director to scan. * namelist is the array of file names returned. IT needs to be * freed by the caller. * skip_dirs controls our behavioru - if nonzero, we don't * skip any subdirectories - if zero, we store those away, * since there are cases where we want to choose a random * directory. */ int load_dir (const char *dir, char ***namelist, int skip_dirs) { DIR *dp; struct dirent *d; int entries = 0, entry_size = 0; char name[NAME_MAX + 1], **rn = NULL; struct stat sb; dp = opendir (dir); if (!dp) return -1; while ((d = readdir (dp))) { sprintf (name, "%s/%s", dir, d->d_name); if (skip_dirs) { stat (name, &sb); if (S_ISDIR (sb.st_mode)) continue; } if (entries == entry_size) { entry_size += 10; rn = (char **) realloc (rn, sizeof (char *) * entry_size); } rn[entries] = strdup (d->d_name); entries++; } (void) closedir (dp); qsort (rn, entries, sizeof (char *), pointer_strcmp); *namelist = rn; return entries; } maptile * find_style (const char *dirname, const char *stylename, int difficulty) { char style_file_path[1024]; char style_file_full_path[1024]; maptile *style_map = NULL; struct stat file_stat; int i, only_subdirs = 0; /* if stylename exists, set style_file_path to that file. */ if (stylename && strlen (stylename) > 0) sprintf (style_file_path, "%s/%s", dirname, stylename); else /* otherwise, just use the dirname. We'll pick a random stylefile. */ sprintf (style_file_path, "%s", dirname); /* is what we were given a directory, or a file? */ sprintf (style_file_full_path, "%s/maps%s.map", settings.datadir, style_file_path); if (stat (style_file_full_path, &file_stat) == 0 && !S_ISDIR (file_stat.st_mode)) style_map = maptile::find_sync (style_file_path); if (!style_map) /* maybe we were given a directory! */ { char **namelist; int n; char style_dir_full_path[256]; /* get the names of all the files in that directory */ sprintf (style_dir_full_path, "%s/maps%s", settings.datadir, style_file_path); /* First, skip subdirectories. If we don't find anything, then try again * without skipping subdirs. */ n = load_dir (style_dir_full_path, &namelist, 1); if (n <= 0) { n = load_dir (style_dir_full_path, &namelist, 0); only_subdirs = 1; } if (n <= 0) return 0; /* nothing to load. Bye. */ /* Picks a random map. Note that if this is all directories, * we know it won't be able to load, so save a few ticks. * the door handling checks for this failure and handles * it properly. */ if (difficulty == -1) { /* pick a random style from this dir. */ if (only_subdirs) style_map = 0; else { strcat (style_file_path, "/"); strcat (style_file_path, namelist[rndm (n)]); style_map = maptile::find_sync (style_file_path); } } else { /* find the map closest in difficulty */ int min_dist = 32000, min_index = -1; for (i = 0; i < n; i++) { int dist; char *mfile_name = strrchr (namelist[i], '_') + 1; if ((mfile_name - 1) == NULL) { /* since there isn't a sequence, */ int q; /*pick one at random to recurse */ style_map = find_style (style_file_path, namelist[rndm (n)], difficulty); for (q = 0; q < n; q++) free (namelist[q]); free (namelist); return style_map; } else { dist = abs (difficulty - atoi (mfile_name)); if (dist < min_dist) { min_dist = dist; min_index = i; } } } /* presumably now we've found the "best" match for the difficulty. */ strcat (style_file_path, "/"); strcat (style_file_path, namelist[min_index]); style_map = maptile::find_sync (style_file_path); } for (i = 0; i < n; i++) free (namelist[i]); free (namelist); } if (style_map) { style_map->load_sync (); style_map->deactivate (); } return style_map; } /* picks a random object from a style map. * Redone by MSW so it should be faster and not use static * variables to generate tables. */ object * pick_random_object (maptile *style) { /* while returning a null object will result in a crash, that * is actually preferable to an infinite loop. That is because * most servers will automatically restart in case of crash. * Change the logic on getting the random space - shouldn't make * any difference, but this seems clearer to me. */ for (int i = 1000; --i;) { object *new_obj = style->at (rndm (style->width), rndm (style->height)).bot; if (new_obj) return new_obj->head_ (); } // instead of crashing in the unlikely case, try to return *something* return get_archetype ("blocked"); }