Atrinik Server 2.5
modules/world_maker.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 #include <loader.h>
00032 #include <gd.h>
00033 
00044 static int **wm_face_colors;
00045 
00047 #define MAX_PIXELS 3
00048 
00050 typedef struct wm_region
00051 {
00053     struct
00054     {
00056         mapstruct *m;
00057 
00059         int xpos;
00060 
00062         int ypos;
00063     } *maps;
00064 
00066     size_t num_maps;
00067 
00069     int xpos;
00070 
00072     int ypos;
00073 
00075     int xpos_lowest;
00076 
00078     int ypos_lowest;
00079 
00081     int w;
00082 
00084     int h;
00085 } wm_region;
00086 
00089 static void wm_images_init()
00090 {
00091     int i, x, y;
00092     gdImagePtr im, im2;
00093     uint8 *data;
00094     uint16 len;
00095 
00096     wm_face_colors = malloc(sizeof(*wm_face_colors) * nrofpixmaps);
00097 
00098     for (i = 0; i < nrofpixmaps; i++)
00099     {
00100         uint64 total = 0, r = 0, g = 0, b = 0;
00101 
00102         /* Get the face's data. */
00103         face_get_data(i, &data, &len);
00104 
00105         /* Create the image from the data. */
00106         im = gdImageCreateFromPngPtr(len, data);
00107         gdImageAlphaBlending(im, 1);
00108         gdImageSaveAlpha(im, 1);
00109 
00110         im2 = gdImageCreateTrueColor(im->sx, im->sy);
00111         gdImageCopyResized(im2, im, 0, 0, 0, 0, im2->sx, im2->sy, im->sx, im->sy);
00112 
00113         wm_face_colors[i] = calloc(1, sizeof(**wm_face_colors) * 5);
00114 
00115         /* Get the average pixel colors. */
00116         for (x = 0; x < im2->sx; x++)
00117         {
00118             for (y = 0; y < im2->sy; y++)
00119             {
00120                 int color = gdImageGetPixel(im2, x, y);
00121 
00122                 if (color)
00123                 {
00124                     r += gdImageRed(im2, color);
00125                     g += gdImageGreen(im2, color);
00126                     b += gdImageBlue(im2, color);
00127                     total++;
00128                 }
00129             }
00130         }
00131 
00132         /* Store the total. */
00133         if (total)
00134         {
00135             r /= total;
00136             g /= total;
00137             b /= total;
00138             wm_face_colors[i][0] = gdImageColorResolve(im2, r, g, b);
00139             wm_face_colors[i][1] = gdImageColorResolve(im2, MIN(r + 10, 255), MIN(g + 10, 255), MIN(b + 10, 255));
00140             wm_face_colors[i][2] = r;
00141             wm_face_colors[i][3] = g;
00142             wm_face_colors[i][4] = b;
00143         }
00144 
00145         gdImageDestroy(im);
00146         gdImageDestroy(im2);
00147     }
00148 }
00149 
00157 static int render_object(gdImagePtr im, int x, int y, object *ob)
00158 {
00159     /* Sanity check. */
00160     if (!ob)
00161     {
00162         return 0;
00163     }
00164 
00165     /* No need to do this for tail parts. */
00166     if (ob->head)
00167     {
00168         return 0;
00169     }
00170 
00171     /* Only render the following objects:
00172      * - floor (the terrain, obviously)
00173      * - walls
00174      * - doors
00175      * - exits (stairs, but also useful to see portals)
00176      * - shop mats
00177      * - misc objects blocking passage (rocks, trees, etc)
00178      * - holy altars*/
00179     if (ob->layer == LAYER_FLOOR || ob->type == WALL || ob->type == DOOR || ob->type == EXIT || ob->type == SHOP_MAT || (ob->type == MISC_OBJECT && QUERY_FLAG(ob, FLAG_NO_PASS)) || ob->type == HOLY_ALTAR || ob->type == SIGN)
00180     {
00181         int px, py, j = 0, max;
00182 
00183         max = MAX_PIXELS;
00184 
00185         /* Adjust the maximum position if this is a multi-arch object. */
00186         if (ob->quick_pos)
00187         {
00188             max *= ((ob->quick_pos >> 4) + 1) / 2;
00189         }
00190 
00191         /* Draw the pixels. */
00192         for (px = 0; px < max; px++)
00193         {
00194             for (py = 0; py < max; py++)
00195             {
00196                 /* Do shading based on the floor's z. */
00197                 if (ob->type == FLOOR && ob->z)
00198                 {
00199                     gdImageSetPixel(im, x + px, y + py, gdImageColorResolve(im, MAX(0, wm_face_colors[ob->face->number][2] - ob->z / 2.5), MAX(0, wm_face_colors[ob->face->number][3] - ob->z / 2.5), MAX(0, wm_face_colors[ob->face->number][4] - ob->z / 2.5)));
00200                 }
00201                 else
00202                 {
00203                     gdImageSetPixel(im, x + px, y + py, wm_face_colors[ob->face->number][py == 1 && px == 1 ? 1 : 0]);
00204                 }
00205 
00206                 j++;
00207             }
00208         }
00209 
00210         return 1;
00211     }
00212 
00213     return 0;
00214 }
00215 
00220 static void region_add_map(wm_region *r, mapstruct *m)
00221 {
00222     /* Resize the array. */
00223     r->maps = realloc(r->maps, sizeof(*r->maps) * (r->num_maps + 1));
00224     r->maps[r->num_maps].m = m;
00225     r->maps[r->num_maps].xpos = r->xpos;
00226     r->maps[r->num_maps].ypos = r->ypos;
00227     r->num_maps++;
00228 }
00229 
00236 static int map_in_region(mapstruct *m, const char *name)
00237 {
00238     region *r;
00239 
00240     for (r = m->region; r; r = r->parent)
00241     {
00242         if (!strcmp(r->name, name))
00243         {
00244             return 1;
00245         }
00246     }
00247 
00248     return 0;
00249 }
00250 
00256 static void region_add_rec(wm_region *r, mapstruct *m, const char *region_name)
00257 {
00258     int i;
00259 
00260     region_add_map(r, m);
00261 
00262     /* Going through 4 tile paths should be enough. */
00263     for (i = 0; i < 4; i++)
00264     {
00265         /* No such tile path. */
00266         if (!m->tile_path[i])
00267         {
00268             continue;
00269         }
00270 
00271         /* Load the map if needed. */
00272         if (!m->tile_map[i])
00273         {
00274             m->tile_map[i] = ready_map_name(m->tile_path[i], MAP_NAME_SHARED);
00275 
00276             if (!m->tile_map[i])
00277             {
00278                 continue;
00279             }
00280         }
00281 
00282         /* If there is a map and it wasn't traversed yet... */
00283         if (m->tile_map[i] && !m->tile_map[i]->traversed)
00284         {
00285             m->tile_map[i]->traversed = 1;
00286 
00287             /* Is the map in region? */
00288             if (map_in_region(m->tile_map[i], region_name))
00289             {
00290                 switch (i)
00291                 {
00292                     case 0:
00293                         r->ypos -= MAP_HEIGHT(m->tile_map[i]) * MAX_PIXELS;
00294                         break;
00295 
00296                     case 1:
00297                         r->xpos += MAP_WIDTH(m->tile_map[i]) * MAX_PIXELS;
00298                         break;
00299 
00300                     case 2:
00301                         r->ypos += MAP_HEIGHT(m->tile_map[i]) * MAX_PIXELS;
00302                         break;
00303 
00304                     case 3:
00305                         r->xpos -= MAP_WIDTH(m->tile_map[i]) * MAX_PIXELS;
00306                         break;
00307                 }
00308 
00309                 /* Store the lowest x/y positions to do adjustments later. */
00310                 if (r->xpos < r->xpos_lowest)
00311                 {
00312                     r->xpos_lowest = r->xpos;
00313                 }
00314 
00315                 if (r->ypos < r->ypos_lowest)
00316                 {
00317                     r->ypos_lowest = r->ypos;
00318                 }
00319 
00320                 /* Go on recursively. */
00321                 region_add_rec(r, m->tile_map[i], region_name);
00322 
00323                 switch (i)
00324                 {
00325                     case 0:
00326                         r->ypos += MAP_HEIGHT(m->tile_map[i]) * MAX_PIXELS;
00327                         break;
00328 
00329                     case 1:
00330                         r->xpos -= MAP_WIDTH(m->tile_map[i]) * MAX_PIXELS;
00331                         break;
00332 
00333                     case 2:
00334                         r->ypos -= MAP_HEIGHT(m->tile_map[i]) * MAX_PIXELS;
00335                         break;
00336 
00337                     case 3:
00338                         r->xpos += MAP_WIDTH(m->tile_map[i]) * MAX_PIXELS;
00339                         break;
00340                 }
00341             }
00342         }
00343     }
00344 }
00345 
00348 void world_maker()
00349 {
00350     mapstruct *m;
00351     gdImagePtr im;
00352     FILE *out;
00353     size_t i;
00354     region *r;
00355     wm_region *wm_r;
00356     char buf[MAX_BUF];
00357     int x, y, layer, got_one;
00358     int xpos = 0, ypos = 0;
00359     FILE *def_fp;
00360     object **info_objects = NULL;
00361     size_t num_info_objects = 0;
00362 
00363     /* Initialize the image colors. */
00364     wm_images_init();
00365 
00366     /* Go through regions. */
00367     for (r = first_region; r; r = r->next)
00368     {
00369         /* No first map? */
00370         if (!r->map_first)
00371         {
00372             continue;
00373         }
00374 
00375         /* Initialize the region. */
00376         wm_r = calloc(1, sizeof(wm_region));
00377         /* Open the definitions file. */
00378         snprintf(buf, sizeof(buf), "%s/%s.def", settings.world_maker_dir, r->name);
00379         def_fp = fopen(buf, "w");
00380 
00381         if (!def_fp)
00382         {
00383             LOG(llevError, "world_maker(): Could not open '%s': %s\n", buf, strerror_local(errno));
00384         }
00385 
00386         /* Load the first map. */
00387         m = ready_map_name(r->map_first, 0);
00388         /* Parse the maps recursively. */
00389         region_add_rec(wm_r, m, r->name);
00390 
00391         snprintf(buf, sizeof(buf), "%s/%s.png", settings.world_maker_dir, r->name);
00392 
00393         /* Store defaults in the definitions file. */
00394         fprintf(def_fp, "pixel_size %d\n", MAX_PIXELS);
00395         fprintf(def_fp, "map_size_x %d\n", MAP_WIDTH(m));
00396         fprintf(def_fp, "map_size_y %d\n", MAP_HEIGHT(m));
00397 
00398         /* Go through the loaded maps. */
00399         for (i = 0; i < wm_r->num_maps; i++)
00400         {
00401             int map_w, map_h;
00402 
00403             /* Adjust X and Y positions. */
00404             wm_r->maps[i].xpos -= wm_r->xpos_lowest;
00405             wm_r->maps[i].ypos -= wm_r->ypos_lowest;
00406 
00407             /* Calculate the maximum width needed for the actual image. */
00408             map_w = MAP_WIDTH(wm_r->maps[i].m) * MAX_PIXELS + wm_r->maps[i].xpos;
00409             map_h = MAP_HEIGHT(wm_r->maps[i].m) * MAX_PIXELS + wm_r->maps[i].ypos;
00410 
00411             if (map_w > wm_r->w)
00412             {
00413                 wm_r->w = map_w;
00414             }
00415 
00416             if (map_h > wm_r->h)
00417             {
00418                 wm_r->h = map_h;
00419             }
00420 
00421             /* Store the map path, labels, etc. */
00422             fprintf(def_fp, "map %x %x %s\n", wm_r->maps[i].xpos, wm_r->maps[i].ypos, wm_r->maps[i].m->path);
00423         }
00424 
00425         /* Create the image. */
00426         im = gdImageCreateTrueColor(wm_r->w, wm_r->h);
00427 
00428         /* Custom background to use? */
00429         if (r->map_bg)
00430         {
00431             uint32 im_r, im_g, im_b;
00432 
00433             /* Parse HTML color and fill the image with it. */
00434             if (sscanf(r->map_bg, "#%2X%2X%2X", &im_r, &im_g, &im_b) == 3)
00435             {
00436                 gdImageFill(im, 0, 0, gdImageColorAllocate(im, im_r, im_g, im_b));
00437             }
00438         }
00439         /* Transparency otherwise. */
00440         else
00441         {
00442             gdImageAlphaBlending(im, 1);
00443             gdImageSaveAlpha(im, 1);
00444             gdImageFill(im, 0, 0, gdTransparent);
00445         }
00446 
00447         /* Go through the maps. */
00448         for (i = 0; i < wm_r->num_maps; i++)
00449         {
00450             object *tmp;
00451 
00452             m = wm_r->maps[i].m;
00453 
00454             /* Draw layer 1 and layer 2 objects. */
00455             for (x = 0; x < MAP_WIDTH(m); x++)
00456             {
00457                 for (y = 0; y < MAP_HEIGHT(m); y++)
00458                 {
00459                     got_one = 0;
00460                     xpos = x * MAX_PIXELS + wm_r->maps[i].xpos;
00461                     ypos = y * MAX_PIXELS + wm_r->maps[i].ypos;
00462 
00463                     /* Look for map info objects. */
00464                     for (tmp = GET_MAP_OB(m, x, y); tmp && tmp->layer == LAYER_SYS; tmp = tmp->above)
00465                     {
00466                         if (tmp->type != CLIENT_MAP_INFO)
00467                         {
00468                             continue;
00469                         }
00470 
00471                         if (tmp->sub_type != CLIENT_MAP_HIDE && (!tmp->name || strstr(tmp->name, " ") || !strcmp(tmp->name, tmp->arch->name)))
00472                         {
00473                             continue;
00474                         }
00475 
00476                         /* Transform newline characters in the message to
00477                          * literal "\n". */
00478                         if (tmp->msg)
00479                         {
00480                             char msg[HUGE_BUF * 4];
00481 
00482                             replace(tmp->msg, "\n", "\\n", msg, sizeof(msg));
00483                             FREE_AND_COPY_HASH(tmp->msg, msg);
00484                         }
00485 
00486                         /* Label. */
00487                         if (tmp->sub_type == CLIENT_MAP_LABEL)
00488                         {
00489                             fprintf(def_fp, "label %x %x %s %s\n", xpos + tmp->last_heal * MAX_PIXELS, ypos + tmp->last_sp * MAX_PIXELS, tmp->name, tmp->msg ? tmp->msg : "???");
00490 
00491                             if (QUERY_FLAG(tmp, FLAG_CURSED))
00492                             {
00493                                 fprintf(def_fp, "label_hide\n");
00494                             }
00495                         }
00496                         else
00497                         {
00498                             info_objects = realloc(info_objects, sizeof(*info_objects) * (num_info_objects + 1));
00499                             info_objects[num_info_objects] = tmp;
00500                             num_info_objects++;
00501                         }
00502                     }
00503 
00504                     for (layer = LAYER_FLOOR; layer <= LAYER_FMASK; layer++)
00505                     {
00506                         if (render_object(im, xpos, ypos, GET_MAP_OB_LAYER(m, x, y, layer - 1)))
00507                         {
00508                             got_one = 1;
00509                         }
00510                     }
00511 
00512                     /* Didn't get an object, fill this square with black. */
00513                     if (!got_one)
00514                     {
00515                         gdImageFilledRectangle(im, xpos, ypos, xpos + MAX_PIXELS - 1, ypos + MAX_PIXELS - 1, gdImageColorAllocate(im, 0, 0, 0));
00516                     }
00517                 }
00518             }
00519 
00520             /* Draw the rest of the objects. */
00521             for (x = 0; x < MAP_WIDTH(m); x++)
00522             {
00523                 for (y = 0; y < MAP_HEIGHT(m); y++)
00524                 {
00525                     xpos = x * MAX_PIXELS + wm_r->maps[i].xpos;
00526                     ypos = y * MAX_PIXELS + wm_r->maps[i].ypos;
00527 
00528                     for (layer = LAYER_ITEM; layer < NUM_LAYERS; layer++)
00529                     {
00530                         render_object(im, xpos, ypos, GET_MAP_OB_LAYER(m, x, y, layer - 1));
00531                     }
00532                 }
00533             }
00534         }
00535 
00536         /* Parse other information objects. */
00537         for (i = 0; i < num_info_objects; i++)
00538         {
00539             object *tmp = info_objects[i];
00540             size_t j;
00541 
00542             /* Already parsed this object. */
00543             if (!tmp)
00544             {
00545                 continue;
00546             }
00547 
00548             /* Get the correct X/Y positions. */
00549             for (j = 0; j < wm_r->num_maps; j++)
00550             {
00551                 if (wm_r->maps[j].m == tmp->map)
00552                 {
00553                     xpos = tmp->x * MAX_PIXELS + wm_r->maps[j].xpos;
00554                     ypos = tmp->y * MAX_PIXELS + wm_r->maps[j].ypos;
00555                     break;
00556                 }
00557             }
00558 
00559             /* Hiding part of the map. */
00560             if (tmp->sub_type == CLIENT_MAP_HIDE)
00561             {
00562                 gdImageFilledRectangle(im, xpos, ypos, MIN(xpos + ((tmp->path_attuned + 1) * MAX_PIXELS), (uint32) wm_r->w), MIN(ypos + ((tmp->path_repelled + 1) * MAX_PIXELS), (uint32) wm_r->h), gdImageColorAllocate(im, 0, 0, 0));
00563             }
00564 
00565             /* Tooltip. */
00566             if (tmp->msg && tmp->sub_type == CLIENT_MAP_TOOLTIP)
00567             {
00568                 /* Tooltip with automatic width/height detection? */
00569                 if (!tmp->path_attuned && !tmp->path_repelled && !tmp->item_level && QUERY_FLAG(tmp, FLAG_STAND_STILL))
00570                 {
00571                     for (j = 0; j < num_info_objects; j++)
00572                     {
00573                         rv_vector rv;
00574 
00575                         /* Already parsed object or same as the master info object. */
00576                         if (!info_objects[j] || info_objects[j] == tmp)
00577                         {
00578                             continue;
00579                         }
00580 
00581                         /* Not the same name as the master info object? */
00582                         if (strcmp(info_objects[j]->name, tmp->name))
00583                         {
00584                             continue;
00585                         }
00586 
00587                         /* Get range vector from the master info object to this one. */
00588                         if (!get_rangevector(tmp, info_objects[j], &rv, RV_RECURSIVE_SEARCH))
00589                         {
00590                             continue;
00591                         }
00592 
00593                         /* Set the calculated distances. */
00594                         if (!tmp->path_attuned)
00595                         {
00596                             tmp->path_attuned = rv.distance_x;
00597                         }
00598 
00599                         if (!tmp->path_repelled)
00600                         {
00601                             tmp->path_repelled = rv.distance_y;
00602                         }
00603 
00604                         /* Mark this object as parsed. */
00605                         info_objects[j] = NULL;
00606                     }
00607                 }
00608 
00609                 /* Write out information about this tooltip. */
00610                 fprintf(def_fp, "tooltip %x %x %x %x %s %s\n", MAX(0, xpos - ((tmp->item_level) * MAX_PIXELS)), MAX(0, ypos - ((tmp->item_level) * MAX_PIXELS)), MIN(xpos + ((tmp->item_level * 2) * MAX_PIXELS + MAX_PIXELS) + (tmp->path_attuned * MAX_PIXELS), (uint32) wm_r->w) - xpos, MIN(ypos + ((tmp->item_level * 2) * MAX_PIXELS + MAX_PIXELS) + (tmp->path_repelled * MAX_PIXELS), (uint32) wm_r->h) - ypos, tmp->name, tmp->msg);
00611 
00612                 /* Outline set? */
00613                 if (tmp->item_skill)
00614                 {
00615                     fprintf(def_fp, "t_outline");
00616 
00617                     /* Store outline's color, if any. */
00618                     if (tmp->slaying)
00619                     {
00620                         fprintf(def_fp, " %s", tmp->slaying);
00621                     }
00622                     /* No outline color, but there is non-standard size,
00623                      * so we must include the default color as well. */
00624                     else if (tmp->item_skill != 1)
00625                     {
00626                         fprintf(def_fp, " #ff0000");
00627                     }
00628 
00629                     if (tmp->item_skill != 1)
00630                     {
00631                         fprintf(def_fp, " %d", tmp->item_skill);
00632                     }
00633 
00634                     fprintf(def_fp, "\n");
00635                 }
00636 
00637                 if (QUERY_FLAG(tmp, FLAG_CURSED))
00638                 {
00639                     fprintf(def_fp, "tooltip_hide\n");
00640                 }
00641             }
00642         }
00643 
00644         if (info_objects)
00645         {
00646             free(info_objects);
00647             info_objects = NULL;
00648         }
00649 
00650         num_info_objects = 0;
00651 
00652         /* Free all maps to save memory. */
00653         free_all_maps();
00654 
00655         /* Write out the image. */
00656         out = fopen(buf, "wb");
00657         gdImagePng(im, out);
00658         fclose(out);
00659 
00660         /* Free data. */
00661         gdImageDestroy(im);
00662         fclose(def_fp);
00663         free(wm_r->maps);
00664         free(wm_r);
00665     }
00666 }