Crossfire Server, Branch 1.12  R12190
info.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_sock_info_c =
00003  *   "$Id: info.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002,2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00039 #include <global.h>
00040 #include <sproto.h>
00041 #include <stdarg.h>
00042 #include <spells.h>
00043 #include <skills.h>
00044 
00051 static void esrv_print_msg(socket_struct *ns, int color, const char *str) {
00052     SockList sl;
00053 
00054     SockList_Init(&sl);
00055     SockList_AddPrintf(&sl, "drawinfo %d %s", color, str);
00056     Send_With_Handling(ns, &sl);
00057     SockList_Term(&sl);
00058 }
00059 
00069 static void esrv_print_ext_msg(socket_struct *ns, int color, uint8 type, uint8 subtype, const char *message) {
00070     SockList sl;
00071 
00072     SockList_Init(&sl);
00073     SockList_AddPrintf(&sl, "drawextinfo %d %hhu %hhu %s", color, type, subtype, message);
00074     Send_With_Handling(ns, &sl);
00075     SockList_Term(&sl);
00076 }
00077 
00089 static void print_message(int colr, const object *pl, const char *tmp) {
00090     if (tmp == NULL) {
00091         tmp = "[NULL]";
00092     }
00093 
00094     if (!pl || (pl->type == PLAYER && pl->contr == NULL)) {
00095         fprintf(logfile, "%s\n", tmp);
00096         return;
00097     }
00098     if (pl->type == PLAYER) {
00099         esrv_print_msg(&pl->contr->socket, colr, tmp);
00100         return;
00101     }
00102 }
00103 
00108 void flush_output_element(const object *pl, Output_Buf *outputs) {
00109     char tbuf[MAX_BUF];
00110 
00111     if (outputs->buf == NULL)
00112         return;
00113     if (outputs->count > 1) {
00114         snprintf(tbuf, MAX_BUF, "%d times %s", outputs->count, outputs->buf);
00115         print_message(NDI_BLACK, pl, tbuf);
00116     } else {
00117         print_message(NDI_BLACK, pl, outputs->buf);
00118     }
00119     free_string(outputs->buf);
00120     outputs->buf = NULL;
00121     outputs->first_update = 0; /* This way, it will be reused */
00122 }
00123 
00138 static void check_output_buffers(const object *pl, const char *buf) {
00139     int i, oldest = 0;
00140 
00141     if (pl->contr->outputs_count < 2) {
00142         print_message(NDI_BLACK, pl, buf);
00143         return;
00144     } else {
00145         for (i = 0; i < NUM_OUTPUT_BUFS; i++) {
00146             if (pl->contr->outputs[i].buf
00147             && !strcmp(buf, pl->contr->outputs[i].buf))
00148                 break;
00149             else if (pl->contr->outputs[i].first_update < pl->contr->outputs[oldest].first_update)
00150                 oldest = i;
00151         }
00152         /* We found a match */
00153         if (i < NUM_OUTPUT_BUFS) {
00154             pl->contr->outputs[i].count++;
00155             if (pl->contr->outputs[i].count >= pl->contr->outputs_count) {
00156                 flush_output_element(pl, &pl->contr->outputs[i]);
00157             }
00158         }
00159         /* No match - flush the oldest, and put the new one in */
00160         else {
00161             flush_output_element(pl, &pl->contr->outputs[oldest]);
00162 
00163             pl->contr->outputs[oldest].first_update = pticks;
00164             pl->contr->outputs[oldest].count = 1;
00165             if (pl->contr->outputs[oldest].buf != NULL)
00166                 free_string(pl->contr->outputs[oldest].buf);
00167             pl->contr->outputs[oldest].buf = add_string(buf);
00168         }
00169     }
00170 }
00171 
00198 void draw_ext_info(
00199     int flags, int pri, const object *pl, uint8 type,
00200     uint8 subtype, const char *message, const char *oldmessage) {
00201 
00202     if ((flags&NDI_ALL) || (flags&NDI_ALL_DMS)) {
00203         player *tmppl;
00204 
00205         for (tmppl = first_player; tmppl != NULL; tmppl = tmppl->next) {
00206             if ((flags&NDI_ALL_DMS) && !QUERY_FLAG(tmppl->ob, FLAG_WIZ))
00207                 continue;
00208             draw_ext_info((flags&~NDI_ALL&~NDI_ALL_DMS), pri, tmppl->ob, type, subtype, message, oldmessage);
00209         }
00210 
00211         return;
00212     }
00213 
00214     if (!pl || (pl->type == PLAYER && pl->contr == NULL)) {
00215         /* Write to the socket? */
00216         print_message(0, NULL, oldmessage);
00217         return;
00218     }
00219     if (pl->type != PLAYER)
00220         return;
00221     if (pri >= pl->contr->listening)
00222         return;
00223 
00224     /* If the client doesn't support the readables, need to convert
00225      * it to old format.  If oldmessage is specified, it is presumed
00226      * that no conversion is needed (if the caller isn't sure, it
00227      * should pass NULL for oldmessage).
00228      */
00229     if (!CLIENT_SUPPORT_READABLES(&pl->contr->socket, type)) {
00230         char *buf;
00231 
00232         if (oldmessage) {
00233             buf = (char *)oldmessage;
00234         } else {
00235             buf = strdup_local(message);
00236             if (buf == NULL) {
00237                 LOG(llevError, "info::draw_ext_info -> Out of memory!\n");
00238                 return;
00239             }
00240             strip_media_tag(buf);
00241         }
00242         if ((flags&NDI_COLOR_MASK) == NDI_BLACK && !(flags&NDI_UNIQUE)) {
00243             /* following prints stuff out, as appropriate */
00244             check_output_buffers(pl, buf);
00245         } else {
00246             print_message(flags&NDI_COLOR_MASK, pl, buf);
00247         }
00248         if (!oldmessage)
00249             free(buf);
00250     } else {
00251         esrv_print_ext_msg(&pl->contr->socket, flags&NDI_COLOR_MASK, type, subtype, message);
00252     }
00253 }
00254 
00286 void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format, ...) {
00287     char newbuf[HUGE_BUF], oldbuf[HUGE_BUF];
00288     va_list ap;
00289 
00290     if (!old_format)
00291         old_format = new_format;
00292 
00293     va_start(ap, old_format);
00294     vsnprintf(oldbuf, HUGE_BUF, old_format, ap);
00295     va_end(ap);
00296     va_start(ap, old_format);
00297     vsnprintf(newbuf, HUGE_BUF, new_format, ap);
00298     va_end(ap);
00299 
00300     draw_ext_info(flags, pri, pl, type, subtype, newbuf, oldbuf);
00301 }
00302 
00306 void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2) {
00307     player *pl;
00308 
00309     for (pl = first_player; pl != NULL; pl = pl->next)
00310         if (pl->ob != NULL && pl->ob->map == map) {
00311             draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
00312         }
00313 }
00314 
00318 void ext_info_map_except(int color, const mapstruct *map, const object *op, uint8 type, uint8 subtype, const char *str1, const char *str2) {
00319     player *pl;
00320 
00321     for (pl = first_player; pl != NULL; pl = pl->next)
00322         if (pl->ob != NULL && pl->ob->map == map && pl->ob != op) {
00323             draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
00324         }
00325 }
00326 
00330 void ext_info_map_except2(int color, const mapstruct *map, const object *op1, const object *op2, int type, int subtype, const char *str1, const char *str2) {
00331     player *pl;
00332 
00333     for (pl = first_player; pl != NULL; pl = pl->next)
00334         if (pl->ob != NULL && pl->ob->map == map
00335                 && pl->ob != op1 && pl->ob != op2) {
00336             draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
00337         }
00338 }
00339 
00343 void rangetostring(const object *pl, char *obuf, size_t len) {
00344     char name[MAX_BUF];
00345 
00346     switch (pl->contr->shoottype) {
00347     case range_none:
00348         strncpy(obuf, "Range: nothing", len);
00349         break;
00350 
00351     case range_bow: {
00352             object *op;
00353 
00354             for (op = pl->inv; op; op = op->below)
00355                 if (op->type == BOW && QUERY_FLAG(op, FLAG_APPLIED))
00356                     break;
00357             if (op == NULL)
00358                 break;
00359 
00360             query_base_name(op, 0, name, MAX_BUF);
00361             snprintf(obuf, len, "Range: %s (%s)", name, op->race ? op->race : "nothing");
00362         }
00363         break;
00364 
00365     case range_magic:
00366         if (settings.casting_time == TRUE) {
00367             if (pl->casting_time > -1) {
00368                 if (pl->casting_time == 0)
00369                     snprintf(obuf, len, "Range: Holding spell (%s)", pl->spell->name);
00370                 else
00371                     snprintf(obuf, len, "Range: Casting spell (%s)", pl->spell->name);
00372             } else
00373                 snprintf(obuf, len, "Range: spell (%s)", pl->contr->ranges[range_magic]->name);
00374         } else
00375             snprintf(obuf, len, "Range: spell (%s)", pl->contr->ranges[range_magic]->name);
00376         break;
00377 
00378     case range_misc:
00379         if (pl->contr->ranges[range_misc])
00380             query_base_name(pl->contr->ranges[range_misc], 0, name, MAX_BUF);
00381         else
00382             strncpy(name, "none", MAX_BUF);
00383         snprintf(obuf, len, "Range: %s", name);
00384         break;
00385 
00386         /* range_scroll is only used for controlling golems.  If the
00387          * the player does not have a golem, reset some things.
00388          */
00389     case range_golem:
00390         if (pl->contr->ranges[range_golem] != NULL)
00391             snprintf(obuf, len, "Range: golem (%s)", pl->contr->ranges[range_golem]->name);
00392         else {
00393             pl->contr->shoottype = range_none;
00394             strncpy(obuf, "Range: nothing", len);
00395         }
00396         break;
00397 
00398     case range_skill:
00399         snprintf(obuf, len, "Skill: %s", pl->chosen_skill != NULL ? pl->chosen_skill->name : "none");
00400         break;
00401 
00402     case range_builder:
00403         query_base_name(pl->contr->ranges[range_builder], 0, name, MAX_BUF);
00404         snprintf(obuf, len, "Builder: %s", name);
00405         break;
00406 
00407     default:
00408         strncpy(obuf, "Range: illegal", len);
00409     }
00410 }
00411 
00415 void set_title(const object *pl, char *buf, size_t len) {
00416     /* Eneq(@csd.uu.se): Let players define their own titles. */
00417     if (pl->contr->own_title[0] == '\0')
00418         snprintf(buf, len, "Player: %s the %s", pl->name, pl->contr->title);
00419     else
00420         snprintf(buf, len, "Player: %s %s", pl->name, pl->contr->own_title);
00421 }
00422 
00434 static void magic_mapping_mark_recursive(object *pl, char *map_mark, int px, int py) {
00435     int x, y, dx, dy, mflags, l;
00436     sint16 nx, ny;
00437     mapstruct *mp;
00438     New_Face *f;
00439     object *ob;
00440 
00441     for (dx = -1; dx <= 1; dx++) {
00442         for (dy = -1; dy <= 1; dy++) {
00443             x = px+dx;
00444             y = py+dy;
00445 
00446             if (FABS(x) >= MAGIC_MAP_HALF || FABS(y) >= MAGIC_MAP_HALF)
00447                 continue;
00448 
00449             mp = pl->map;
00450             nx = pl->x+x;
00451             ny = pl->y+y;
00452 
00453             mflags = get_map_flags(pl->map, &mp, nx, ny, &nx, &ny);
00454             if (mflags&P_OUT_OF_MAP)
00455                 continue;
00456 
00457             if (map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] == 0) {
00458                 for (l = 0; l < MAP_LAYERS; l++) {
00459                     ob = GET_MAP_FACE_OBJ(mp, nx, ny, l);
00460                     if (ob && !ob->invisible && ob->face != blank_face)
00461                         break;
00462                 }
00463                 if (ob)
00464                     f = ob->face;
00465                 else
00466                     f = blank_face;
00467 
00468                 /* Should probably have P_NO_MAGIC here also, but then shops don't
00469                  * work.
00470                  */
00471                 if (mflags&P_BLOCKSVIEW)
00472                     map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_WALL|(f ? f->magicmap : 0);
00473                 else {
00474                     map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_FLOOR|(f ? f->magicmap : 0);
00475                     magic_mapping_mark_recursive(pl, map_mark, x, y);
00476                 }
00477             }
00478         }
00479     }
00480 }
00481 
00497 void magic_mapping_mark(object *pl, char *map_mark, int strength) {
00498     int x, y, mflags, l;
00499     sint16 nx, ny;
00500     mapstruct *mp;
00501     New_Face *f;
00502     object *ob;
00503 
00504     for (x = -strength; x < strength; x++) {
00505         for (y = -strength; y < strength; y++) {
00506             mp = pl->map;
00507             nx = pl->x+x;
00508             ny = pl->y+y;
00509             mflags = get_map_flags(pl->map, &mp, nx, ny, &nx, &ny);
00510             if (mflags&P_OUT_OF_MAP)
00511                 continue;
00512             else {
00513                 for (l = 0; l < MAP_LAYERS; l++) {
00514                     ob = GET_MAP_FACE_OBJ(mp, nx, ny, l);
00515                     if (ob && !ob->invisible && ob->face != blank_face)
00516                         break;
00517                 }
00518                 if (ob)
00519                     f = ob->face;
00520                 else
00521                     f = blank_face;
00522             }
00523 
00524             if (mflags&P_BLOCKSVIEW)
00525                 map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_WALL|(f ? f->magicmap : 0);
00526             else {
00527                 map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_FLOOR|(f ? f->magicmap : 0);
00528                 magic_mapping_mark_recursive(pl, map_mark, x, y);
00529             }
00530         }
00531     }
00532 }
00533 
00542 void draw_magic_map(object *pl) {
00543     int x, y;
00544     char map_mark[MAGIC_MAP_SIZE*MAGIC_MAP_SIZE];
00545     int xmin, xmax, ymin, ymax;
00546     /* This is to prevent stack smashing below. */
00547     int max_width;
00548     SockList sl;
00549 
00550     if (pl->type != PLAYER) {
00551         LOG(llevError, "Non player object called draw_map.\n");
00552         return;
00553     }
00554 
00555     /* First, we figure out what spaces are 'reachable' by the player */
00556     memset(map_mark, 0, MAGIC_MAP_SIZE*MAGIC_MAP_SIZE);
00557     magic_mapping_mark(pl, map_mark, 3);
00558 
00559     /* We now go through and figure out what spaces have been
00560      * marked, and thus figure out rectangular region we send
00561      * to the client (eg, if only a 10x10 area is visible, we only
00562      * want to send those 100 spaces.)
00563      */
00564     xmin = MAGIC_MAP_SIZE;
00565     ymin = MAGIC_MAP_SIZE;
00566     xmax = 0;
00567     ymax = 0;
00568     max_width = (MAP_WIDTH(pl->map) > MAGIC_MAP_SIZE) ? MAGIC_MAP_SIZE : MAP_WIDTH(pl->map);
00569     for (x = 0; x < MAGIC_MAP_SIZE; x++) {
00570         for (y = 0; y < MAGIC_MAP_SIZE; y++) {
00571             if (map_mark[x+max_width*y]&~FACE_FLOOR) {
00572                 xmin = MIN(x, xmin);
00573                 xmax = MAX(x, xmax);
00574                 ymin = MIN(y, ymin);
00575                 ymax = MAX(y, ymax);
00576             }
00577         }
00578     }
00579 
00580     SockList_Init(&sl);
00581     SockList_AddPrintf(&sl, "magicmap %d %d %d %d ", (xmax-xmin+1), (ymax-ymin+1), MAGIC_MAP_HALF-xmin, MAGIC_MAP_HALF-ymin);
00582 
00583     for (y = ymin; y <= ymax; y++) {
00584         for (x = xmin; x <= xmax; x++) {
00585             SockList_AddChar(&sl, map_mark[x+MAGIC_MAP_SIZE*y]&~FACE_FLOOR);
00586         } /* x loop */
00587     } /* y loop */
00588 
00589     Send_With_Handling(&pl->contr->socket, &sl);
00590     SockList_Term(&sl);
00591 }