Crossfire Server, Branches 1.12  R18729
info.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_sock_info_c =
3  * "$Id: info.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2002,2006 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
39 #include <global.h>
40 #include <sproto.h>
41 #include <stdarg.h>
42 #include <spells.h>
43 #include <skills.h>
44 
51 static void esrv_print_msg(socket_struct *ns, int color, const char *str) {
52  SockList sl;
53 
54  SockList_Init(&sl);
55  SockList_AddPrintf(&sl, "drawinfo %d %s", color, str);
56  Send_With_Handling(ns, &sl);
57  SockList_Term(&sl);
58 }
59 
69 static void esrv_print_ext_msg(socket_struct *ns, int color, uint8 type, uint8 subtype, const char *message) {
70  SockList sl;
71 
72  SockList_Init(&sl);
73  SockList_AddPrintf(&sl, "drawextinfo %d %hhu %hhu %s", color, type, subtype, message);
74  Send_With_Handling(ns, &sl);
75  SockList_Term(&sl);
76 }
77 
89 static void print_message(int colr, const object *pl, const char *tmp) {
90  if (tmp == NULL) {
91  tmp = "[NULL]";
92  }
93 
94  if (!pl || (pl->type == PLAYER && pl->contr == NULL)) {
95  fprintf(logfile, "%s\n", tmp);
96  return;
97  }
98  if (pl->type == PLAYER) {
99  esrv_print_msg(&pl->contr->socket, colr, tmp);
100  return;
101  }
102 }
103 
108 void flush_output_element(const object *pl, Output_Buf *outputs) {
109  char tbuf[MAX_BUF];
110 
111  if (outputs->buf == NULL)
112  return;
113  if (outputs->count > 1) {
114  snprintf(tbuf, MAX_BUF, "%d times %s", outputs->count, outputs->buf);
115  print_message(NDI_BLACK, pl, tbuf);
116  } else {
117  print_message(NDI_BLACK, pl, outputs->buf);
118  }
119  free_string(outputs->buf);
120  outputs->buf = NULL;
121  outputs->first_update = 0; /* This way, it will be reused */
122 }
123 
138 static void check_output_buffers(const object *pl, const char *buf) {
139  int i, oldest = 0;
140 
141  if (pl->contr->outputs_count < 2) {
142  print_message(NDI_BLACK, pl, buf);
143  return;
144  } else {
145  for (i = 0; i < NUM_OUTPUT_BUFS; i++) {
146  if (pl->contr->outputs[i].buf
147  && !strcmp(buf, pl->contr->outputs[i].buf))
148  break;
149  else if (pl->contr->outputs[i].first_update < pl->contr->outputs[oldest].first_update)
150  oldest = i;
151  }
152  /* We found a match */
153  if (i < NUM_OUTPUT_BUFS) {
154  pl->contr->outputs[i].count++;
155  if (pl->contr->outputs[i].count >= pl->contr->outputs_count) {
156  flush_output_element(pl, &pl->contr->outputs[i]);
157  }
158  }
159  /* No match - flush the oldest, and put the new one in */
160  else {
161  flush_output_element(pl, &pl->contr->outputs[oldest]);
162 
163  pl->contr->outputs[oldest].first_update = pticks;
164  pl->contr->outputs[oldest].count = 1;
165  if (pl->contr->outputs[oldest].buf != NULL)
166  free_string(pl->contr->outputs[oldest].buf);
167  pl->contr->outputs[oldest].buf = add_string(buf);
168  }
169  }
170 }
171 
199  int flags, int pri, const object *pl, uint8 type,
200  uint8 subtype, const char *message, const char *oldmessage) {
201 
202  if ((flags&NDI_ALL) || (flags&NDI_ALL_DMS)) {
203  player *tmppl;
204 
205  for (tmppl = first_player; tmppl != NULL; tmppl = tmppl->next) {
206  if ((flags&NDI_ALL_DMS) && !QUERY_FLAG(tmppl->ob, FLAG_WIZ))
207  continue;
208  draw_ext_info((flags&~NDI_ALL&~NDI_ALL_DMS), pri, tmppl->ob, type, subtype, message, oldmessage);
209  }
210 
211  return;
212  }
213 
214  if (!pl || (pl->type == PLAYER && pl->contr == NULL)) {
215  /* Write to the socket? */
216  print_message(0, NULL, oldmessage);
217  return;
218  }
219  if (pl->type != PLAYER)
220  return;
221  if (pri >= pl->contr->listening)
222  return;
223 
224  /* If the client doesn't support the readables, need to convert
225  * it to old format. If oldmessage is specified, it is presumed
226  * that no conversion is needed (if the caller isn't sure, it
227  * should pass NULL for oldmessage).
228  */
229  if (!CLIENT_SUPPORT_READABLES(&pl->contr->socket, type)) {
230  char *buf;
231 
232  if (oldmessage) {
233  buf = (char *)oldmessage;
234  } else {
235  buf = strdup_local(message);
236  if (buf == NULL) {
237  LOG(llevError, "info::draw_ext_info -> Out of memory!\n");
238  return;
239  }
240  strip_media_tag(buf);
241  }
242  if ((flags&NDI_COLOR_MASK) == NDI_BLACK && !(flags&NDI_UNIQUE)) {
243  /* following prints stuff out, as appropriate */
244  check_output_buffers(pl, buf);
245  } else {
246  print_message(flags&NDI_COLOR_MASK, pl, buf);
247  }
248  if (!oldmessage)
249  free(buf);
250  } else {
251  esrv_print_ext_msg(&pl->contr->socket, flags&NDI_COLOR_MASK, type, subtype, message);
252  }
253 }
254 
286 void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format, ...) {
287  char newbuf[HUGE_BUF], oldbuf[HUGE_BUF];
288  va_list ap;
289 
290  if (!old_format)
291  old_format = new_format;
292 
293  va_start(ap, old_format);
294  vsnprintf(oldbuf, HUGE_BUF, old_format, ap);
295  va_end(ap);
296  va_start(ap, old_format);
297  vsnprintf(newbuf, HUGE_BUF, new_format, ap);
298  va_end(ap);
299 
300  draw_ext_info(flags, pri, pl, type, subtype, newbuf, oldbuf);
301 }
302 
306 void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2) {
307  player *pl;
308 
309  for (pl = first_player; pl != NULL; pl = pl->next)
310  if (pl->ob != NULL && pl->ob->map == map) {
311  draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
312  }
313 }
314 
318 void ext_info_map_except(int color, const mapstruct *map, const object *op, uint8 type, uint8 subtype, const char *str1, const char *str2) {
319  player *pl;
320 
321  for (pl = first_player; pl != NULL; pl = pl->next)
322  if (pl->ob != NULL && pl->ob->map == map && pl->ob != op) {
323  draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
324  }
325 }
326 
330 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) {
331  player *pl;
332 
333  for (pl = first_player; pl != NULL; pl = pl->next)
334  if (pl->ob != NULL && pl->ob->map == map
335  && pl->ob != op1 && pl->ob != op2) {
336  draw_ext_info(color, 0, pl->ob, type, subtype, str1, str2);
337  }
338 }
339 
343 void rangetostring(const object *pl, char *obuf, size_t len) {
344  char name[MAX_BUF];
345 
346  switch (pl->contr->shoottype) {
347  case range_none:
348  strncpy(obuf, "Range: nothing", len);
349  break;
350 
351  case range_bow: {
352  object *op;
353 
354  for (op = pl->inv; op; op = op->below)
355  if (op->type == BOW && QUERY_FLAG(op, FLAG_APPLIED))
356  break;
357  if (op == NULL)
358  break;
359 
360  query_base_name(op, 0, name, MAX_BUF);
361  snprintf(obuf, len, "Range: %s (%s)", name, op->race ? op->race : "nothing");
362  }
363  break;
364 
365  case range_magic:
366  if (settings.casting_time == TRUE) {
367  if (pl->casting_time > -1) {
368  if (pl->casting_time == 0)
369  snprintf(obuf, len, "Range: Holding spell (%s)", pl->spell->name);
370  else
371  snprintf(obuf, len, "Range: Casting spell (%s)", pl->spell->name);
372  } else
373  snprintf(obuf, len, "Range: spell (%s)", pl->contr->ranges[range_magic]->name);
374  } else
375  snprintf(obuf, len, "Range: spell (%s)", pl->contr->ranges[range_magic]->name);
376  break;
377 
378  case range_misc:
379  if (pl->contr->ranges[range_misc])
380  query_base_name(pl->contr->ranges[range_misc], 0, name, MAX_BUF);
381  else
382  strncpy(name, "none", MAX_BUF);
383  snprintf(obuf, len, "Range: %s", name);
384  break;
385 
386  /* range_scroll is only used for controlling golems. If the
387  * the player does not have a golem, reset some things.
388  */
389  case range_golem:
390  if (pl->contr->ranges[range_golem] != NULL)
391  snprintf(obuf, len, "Range: golem (%s)", pl->contr->ranges[range_golem]->name);
392  else {
393  pl->contr->shoottype = range_none;
394  strncpy(obuf, "Range: nothing", len);
395  }
396  break;
397 
398  case range_skill:
399  snprintf(obuf, len, "Skill: %s", pl->chosen_skill != NULL ? pl->chosen_skill->name : "none");
400  break;
401 
402  case range_builder:
404  snprintf(obuf, len, "Builder: %s", name);
405  break;
406 
407  default:
408  strncpy(obuf, "Range: illegal", len);
409  }
410 }
411 
415 void set_title(const object *pl, char *buf, size_t len) {
416  /* Eneq(@csd.uu.se): Let players define their own titles. */
417  if (pl->contr->own_title[0] == '\0')
418  snprintf(buf, len, "Player: %s the %s", pl->name, pl->contr->title);
419  else
420  snprintf(buf, len, "Player: %s %s", pl->name, pl->contr->own_title);
421 }
422 
434 static void magic_mapping_mark_recursive(object *pl, char *map_mark, int px, int py) {
435  int x, y, dx, dy, mflags, l;
436  sint16 nx, ny;
437  mapstruct *mp;
438  New_Face *f;
439  object *ob;
440 
441  for (dx = -1; dx <= 1; dx++) {
442  for (dy = -1; dy <= 1; dy++) {
443  x = px+dx;
444  y = py+dy;
445 
446  if (FABS(x) >= MAGIC_MAP_HALF || FABS(y) >= MAGIC_MAP_HALF)
447  continue;
448 
449  mp = pl->map;
450  nx = pl->x+x;
451  ny = pl->y+y;
452 
453  mflags = get_map_flags(pl->map, &mp, nx, ny, &nx, &ny);
454  if (mflags&P_OUT_OF_MAP)
455  continue;
456 
457  if (map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] == 0) {
458  for (l = 0; l < MAP_LAYERS; l++) {
459  ob = GET_MAP_FACE_OBJ(mp, nx, ny, l);
460  if (ob && !ob->invisible && ob->face != blank_face)
461  break;
462  }
463  if (ob)
464  f = ob->face;
465  else
466  f = blank_face;
467 
468  /* Should probably have P_NO_MAGIC here also, but then shops don't
469  * work.
470  */
471  if (mflags&P_BLOCKSVIEW)
472  map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_WALL|(f ? f->magicmap : 0);
473  else {
474  map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_FLOOR|(f ? f->magicmap : 0);
475  magic_mapping_mark_recursive(pl, map_mark, x, y);
476  }
477  }
478  }
479  }
480 }
481 
497 void magic_mapping_mark(object *pl, char *map_mark, int strength) {
498  int x, y, mflags, l;
499  sint16 nx, ny;
500  mapstruct *mp;
501  New_Face *f;
502  object *ob;
503 
504  for (x = -strength; x < strength; x++) {
505  for (y = -strength; y < strength; y++) {
506  mp = pl->map;
507  nx = pl->x+x;
508  ny = pl->y+y;
509  mflags = get_map_flags(pl->map, &mp, nx, ny, &nx, &ny);
510  if (mflags&P_OUT_OF_MAP)
511  continue;
512  else {
513  for (l = 0; l < MAP_LAYERS; l++) {
514  ob = GET_MAP_FACE_OBJ(mp, nx, ny, l);
515  if (ob && !ob->invisible && ob->face != blank_face)
516  break;
517  }
518  if (ob)
519  f = ob->face;
520  else
521  f = blank_face;
522  }
523 
524  if (mflags&P_BLOCKSVIEW)
525  map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_WALL|(f ? f->magicmap : 0);
526  else {
527  map_mark[MAGIC_MAP_HALF+x+MAGIC_MAP_SIZE*(MAGIC_MAP_HALF+y)] = FACE_FLOOR|(f ? f->magicmap : 0);
528  magic_mapping_mark_recursive(pl, map_mark, x, y);
529  }
530  }
531  }
532 }
533 
542 void draw_magic_map(object *pl) {
543  int x, y;
544  char map_mark[MAGIC_MAP_SIZE*MAGIC_MAP_SIZE];
545  int xmin, xmax, ymin, ymax;
546  /* This is to prevent stack smashing below. */
547  int max_width;
548  SockList sl;
549 
550  if (pl->type != PLAYER) {
551  LOG(llevError, "Non player object called draw_map.\n");
552  return;
553  }
554 
555  /* First, we figure out what spaces are 'reachable' by the player */
556  memset(map_mark, 0, MAGIC_MAP_SIZE*MAGIC_MAP_SIZE);
557  magic_mapping_mark(pl, map_mark, 3);
558 
559  /* We now go through and figure out what spaces have been
560  * marked, and thus figure out rectangular region we send
561  * to the client (eg, if only a 10x10 area is visible, we only
562  * want to send those 100 spaces.)
563  */
564  xmin = MAGIC_MAP_SIZE;
565  ymin = MAGIC_MAP_SIZE;
566  xmax = 0;
567  ymax = 0;
568  max_width = (MAP_WIDTH(pl->map) > MAGIC_MAP_SIZE) ? MAGIC_MAP_SIZE : MAP_WIDTH(pl->map);
569  for (x = 0; x < MAGIC_MAP_SIZE; x++) {
570  for (y = 0; y < MAGIC_MAP_SIZE; y++) {
571  if (map_mark[x+max_width*y]&~FACE_FLOOR) {
572  xmin = MIN(x, xmin);
573  xmax = MAX(x, xmax);
574  ymin = MIN(y, ymin);
575  ymax = MAX(y, ymax);
576  }
577  }
578  }
579 
580  SockList_Init(&sl);
581  SockList_AddPrintf(&sl, "magicmap %d %d %d %d ", (xmax-xmin+1), (ymax-ymin+1), MAGIC_MAP_HALF-xmin, MAGIC_MAP_HALF-ymin);
582 
583  for (y = ymin; y <= ymax; y++) {
584  for (x = xmin; x <= xmax; x++) {
585  SockList_AddChar(&sl, map_mark[x+MAGIC_MAP_SIZE*y]&~FACE_FLOOR);
586  } /* x loop */
587  } /* y loop */
588 
589  Send_With_Handling(&pl->contr->socket, &sl);
590  SockList_Term(&sl);
591 }
EXTERN FILE * logfile
Definition: global.h:220
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.c:187
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
Definition: player.h:146
void strip_media_tag(char *message)
Definition: utils.c:388
signed short sint16
Definition: global.h:72
uint32 pticks
Definition: time.c:56
const char * race
Definition: object.h:171
void SockList_Init(SockList *sl)
Definition: lowlevel.c:67
#define NDI_ALL
Definition: newclient.h:220
char title[BIG_NAME]
Definition: player.h:216
#define FABS(x)
Definition: define.h:61
void flush_output_element(const object *pl, Output_Buf *outputs)
Definition: info.c:108
char * name
Definition: image.c:53
#define FACE_WALL
Definition: newclient.h:249
uint8 casting_time
Definition: global.h:358
void ext_info_map_except(int color, const mapstruct *map, const object *op, uint8 type, uint8 subtype, const char *str1, const char *str2)
Definition: info.c:318
void free_string(sstring str)
Definition: shstr.c:272
#define HUGE_BUF
Definition: define.h:83
#define CLIENT_SUPPORT_READABLES(__sockPtr, __type)
Definition: newserver.h:155
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:732
socket_struct socket
Definition: player.h:148
sint16 invisible
Definition: object.h:211
rangetype shoottype
Definition: player.h:153
static void esrv_print_msg(socket_struct *ns, int color, const char *str)
Definition: info.c:51
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)
Definition: info.c:330
Output_Buf outputs[NUM_OUTPUT_BUFS]
Definition: player.h:244
object * ranges[range_size]
Definition: player.h:157
static void print_message(int colr, const object *pl, const char *tmp)
Definition: info.c:89
uint16 count
Definition: player.h:43
sint16 x
Definition: object.h:179
#define NDI_BLACK
Definition: newclient.h:195
uint16 outputs_count
Definition: player.h:246
#define PLAYER
Definition: define.h:113
void set_title(const object *pl, char *buf, size_t len)
Definition: info.c:415
#define NUM_OUTPUT_BUFS
Definition: player.h:38
struct obj * chosen_skill
Definition: object.h:237
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: info.c:198
struct obj * spell
Definition: object.h:259
static void esrv_print_ext_msg(socket_struct *ns, int color, uint8 type, uint8 subtype, const char *message)
Definition: info.c:69
#define NDI_ALL_DMS
Definition: newclient.h:221
struct mapdef * map
Definition: object.h:155
void SockList_Term(SockList *sl)
Definition: lowlevel.c:77
void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2)
Definition: info.c:306
#define MAP_LAYERS
Definition: map.h:60
const char * name
Definition: object.h:167
struct obj * below
Definition: object.h:145
#define TRUE
Definition: exp.c:41
void rangetostring(const object *pl, char *obuf, size_t len)
Definition: info.c:343
unsigned char uint8
Definition: global.h:75
#define NDI_COLOR_MASK
Definition: newclient.h:212
#define P_OUT_OF_MAP
Definition: map.h:272
sint16 y
Definition: object.h:179
struct pl * contr
Definition: object.h:134
#define MAX(x, y)
Definition: define.h:70
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
void draw_magic_map(object *pl)
Definition: info.c:542
uint32 first_update
Definition: player.h:42
char * strdup_local(const char *str)
Definition: porting.c:310
#define FLAG_WIZ
Definition: define.h:527
#define MAX_BUF
Definition: define.h:81
static void check_output_buffers(const object *pl, const char *buf)
Definition: info.c:138
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: info.c:286
char own_title[MAX_NAME]
Definition: player.h:214
void SockList_AddChar(SockList *sl, char c)
Definition: lowlevel.c:103
static const flag_definition flags[]
#define MIN(x, y)
Definition: define.h:67
#define MAGIC_MAP_HALF
Definition: map.h:53
object * ob
Definition: player.h:207
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define GET_MAP_FACE_OBJ(M, X, Y, L)
Definition: map.h:205
static void magic_mapping_mark_recursive(object *pl, char *map_mark, int px, int py)
Definition: info.c:434
#define vsnprintf
Definition: win32.h:72
New_Face * blank_face
Definition: image.c:66
#define MAGIC_MAP_SIZE
Definition: map.h:52
#define MAP_WIDTH(m)
Definition: map.h:97
struct Settings settings
Definition: init.c:48
#define FLAG_APPLIED
Definition: define.h:531
void magic_mapping_mark(object *pl, char *map_mark, int strength)
Definition: info.c:497
#define FACE_FLOOR
Definition: newclient.h:248
sint16 casting_time
Definition: object.h:253
#define BOW
Definition: define.h:126
sstring add_string(const char *str)
Definition: shstr.c:116
EXTERN player * first_player
Definition: global.h:190
struct pl * next
Definition: player.h:147
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
#define P_BLOCKSVIEW
Definition: map.h:247
uint8 listening
Definition: player.h:174
Definition: map.h:346
New_Face * face
Definition: object.h:183
uint8 magicmap
Definition: face.h:46
uint8 type
Definition: object.h:189
const char * buf
Definition: player.h:41
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.c:541