Crossfire Server, Branch 1.12
R12190
|
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 }