ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/info.C
Revision: 1.40
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.39: +0 -75 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game for X-windows
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The authors can be reached via e-mail at <crossfire@schmorp.de>
23 */
24
25 /**
26 * \file
27 * Basic client output functions.
28 *
29 * \date 2003-12-02
30 *
31 * This file implements some of the simpler output functions to the
32 * client. Basically, things like sending text strings along
33 */
34
35 #include <global.h>
36 #include <sproto.h>
37 #include <stdarg.h>
38 #include <spells.h>
39 #include <skills.h>
40
41 /**
42 * Draws a normal message on the client. It is pretty
43 * much the same thing as the draw_info above, but takes a color
44 * parameter. the esrv_drawinfo functions should probably be
45 * replaced with this, just using black as the color.
46 */
47 static void
48 esrv_print_msg (client *ns, int color, const char *str)
49 {
50 ns->send_packet_printf ("drawinfo %d %s", color, str);
51 }
52
53 /**
54 * Draws an extended message on the client.
55 * ns the socket to send message to
56 * color color informations (used mainly if client does not support message type)
57 * type,
58 * subtype type and subtype of text message
59 * intro Intro message to send with main message if client does not support the message type
60 * message The main message
61 */
62 static void
63 esrv_print_ext_msg (client *ns, int color, uint8 type, uint8 subtype, const char *message)
64 {
65 ns->send_packet_printf ("drawextinfo %d %hhu %hhu %s", color, type, subtype, message);
66 }
67
68 /**
69 * Frontend for esrv_print_msg
70 * \param colr message color
71 * \param pl player to send to. Can be NULL
72 * \param tmp message to send. Can be NULL
73 *
74 * If pl is NULL or without contr set, writes message to log.
75 *
76 * Else sends message to player via esrv_print_msg
77 */
78 static void
79 print_message (int colr, const object *pl, const char *tmp)
80 {
81 if (!tmp)
82 tmp = "[NULL]";
83
84 if (!pl || (pl->type == PLAYER && pl->contr == NULL))
85 return;
86
87 if (pl->type == PLAYER)
88 esrv_print_msg (pl->contr->ns, colr, (char *) tmp);
89 }
90
91 bool
92 client::msg_suppressed (const char *msg)
93 {
94 if (!pl)
95 return false;
96
97 if (pl->outputs_count <= 1 || !pl->outputs_sync)
98 return false;
99
100 int len = strlen (msg);
101
102 if (len > MSG_BUF_SIZE)
103 return false;
104
105 msg_buf *lru = msgbuf;
106 for (msg_buf *buf = msgbuf; buf < msgbuf + MSG_BUF_COUNT; ++buf)
107 {
108 if (len == buf->len && !memcmp (msg, buf->msg, len))
109 {
110 // found matching buf, see if expired
111 if (buf->expire <= pticks || !buf->count)
112 {
113 // yes, take over matching buffer, print
114 buf->expire = pticks + pl->outputs_sync;
115 buf->count = pl->outputs_count;
116
117 return false;
118 }
119
120 // no, suppress
121 --buf->count;
122 return true;
123 }
124
125 if (lru->expire > buf->expire)
126 lru = buf;
127 }
128
129 // new message, evoke oldest buffer
130 lru->expire = pticks + pl->outputs_sync;
131 lru->count = pl->outputs_count;
132 lru->len = len;
133 memcpy (lru->msg, msg, len);
134
135 return false;
136 }
137
138 /**
139 * Sends message to player(s).
140 *
141 * flags is various flags - mostly color, plus a few specials.
142 *
143 * pri is priority. It is a little odd - the lower the value, the more
144 * important it is. Thus, 0 gets sent no matter what. Otherwise, the
145 * value must be less than the listening level that the player has set.
146 * Unfortunately, there is no clear guideline on what each level does what.
147 *
148 * pl can be passed as NULL - in fact, this will be done if NDI_ALL is set
149 * in the flags.
150 *
151 * If message is black, and not NDI_UNIQUE, gets sent through output buffers.
152 *
153 */
154 void
155 new_draw_info (int flags, int pri, const object *pl, const char *buf)
156 {
157 if (flags & NDI_ALL)
158 {
159 for_all_players (pl)
160 new_draw_info (flags & ~NDI_ALL, pri, pl->ob, buf);
161
162 return;
163 }
164
165 if (!pl || pl->type != PLAYER || !pl->contr || !pl->contr->ns)
166 return;
167
168 if (pri >= pl->contr->listening)
169 return;
170
171 if ((flags & (NDI_COLOR_MASK | NDI_UNIQUE)) != NDI_BLACK
172 || !pl->contr->ns->msg_suppressed (buf))
173 print_message (flags & NDI_COLOR_MASK, pl, buf);
174 }
175
176 /**
177 * Wrapper for new_draw_info printf-like.
178 *
179 * This is a pretty trivial function, but it allows us to use printf style
180 * formatting, so instead of the calling function having to do it, we do
181 * it here. It may also have advantages in the future for reduction of
182 * client/server bandwidth (client could keep track of various strings
183 */
184 void
185 new_draw_info_format (int flags, int pri, const object *pl, const char *format, ...)
186 {
187 char buf[HUGE_BUF];
188
189 va_list ap;
190 va_start (ap, format);
191 vsnprintf (buf, HUGE_BUF, format, ap);
192 va_end (ap);
193
194 new_draw_info (flags, pri, pl, buf);
195 }
196
197 void
198 draw_ext_info (int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
199 {
200
201 if (!pl || (pl->type != PLAYER) || (pl->contr == NULL))
202 return;
203
204 if (pri >= pl->contr->listening)
205 return;
206
207 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
208 {
209 char *buf = (char *) malloc (strlen (oldmessage == NULL ? message : oldmessage) + 1);
210
211 if (buf == NULL)
212 LOG (llevError, "info::draw_ext_info -> Out of memory!");
213 else
214 {
215 strcpy (buf, oldmessage == NULL ? message : oldmessage);
216 strip_media_tag (buf);
217 new_draw_info (flags, pri, pl, buf);
218 free (buf);
219 }
220 }
221 else
222 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, message);
223 }
224
225 void
226 draw_ext_info_format (int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *old_format, char *new_format, ...)
227 {
228 char buf[HUGE_BUF];
229
230 if (!pl || (pl->type != PLAYER) || (pl->contr == NULL))
231 return;
232
233 if (pri >= pl->contr->listening)
234 return;
235
236 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
237 {
238 va_list ap;
239
240 LOG (llevDebug, "Non supported extension text type for client.\n");
241 va_start (ap, new_format);
242 vsnprintf (buf, HUGE_BUF, old_format, ap);
243 va_end (ap);
244 new_draw_info (flags, pri, pl, buf);
245 return;
246 }
247 else
248 {
249 va_list ap;
250
251 va_start (ap, new_format);
252 vsnprintf (buf, HUGE_BUF, new_format, ap);
253 va_end (ap);
254 strip_media_tag (buf);
255 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, buf);
256 }
257 }
258
259 /**
260 * Writes to everyone on the map *except* op. This is useful for emotions.
261 */
262
263 void
264 new_info_map_except (int color, maptile * map, object *op, const char *str)
265 {
266 for_all_players (pl)
267 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op)
268 new_draw_info (color, 0, pl->ob, str);
269 }
270
271 /**
272 * Writes to everyone on the map except op1 and op2
273 */
274
275 void
276 new_info_map_except2 (int color, maptile * map, object *op1, object *op2, const char *str)
277 {
278 for_all_players (pl)
279 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op1 && pl->ob != op2)
280 new_draw_info (color, 0, pl->ob, str);
281 }
282
283 /**
284 * Writes to everyone on the specified map
285 */
286
287 void
288 new_info_map (int color, maptile * map, const char *str)
289 {
290 for_all_players (pl)
291 if (pl->ob != NULL && pl->ob->map == map)
292 new_draw_info (color, 0, pl->ob, str);
293 }
294
295 /**
296 * This does nothing now. However, in theory, we should probably send
297 * something to the client and let the client figure out how it might want
298 * to handle this
299 */
300 void
301 clear_win_info (object *op)
302 {
303 }
304
305 /**
306 * Sets player title.
307 */
308 void
309 set_title (object *pl, char *buf)
310 {
311 /* Eneq(@csd.uu.se): Let players define their own titles. */
312 if (pl->contr->own_title[0] == '\0')
313 sprintf (buf, "Player: %s the %s", (const char *) pl->name, (const char *) pl->contr->title);
314 else
315 sprintf (buf, "Player: %s %s", (const char *) pl->name, (const char *) pl->contr->own_title);
316 }
317
318 // formerly a macro, used only by magic map, so optimised it out
319 static inline faceidx
320 GET_MAP_FACE (maptile *m, int x, int y, int layer)
321 {
322 if (object *op = GET_MAP_FACE_OBJ (m, x, y, layer))
323 return op->face;
324 else
325 return 0;
326 }
327
328 /**
329 * Helper for magic map creation.
330 *
331 * Takes a player, the map_mark array and an x and y starting position.
332 * pl is the player.
333 * px, py are offsets from the player.
334 *
335 * This function examines all the adjacant spaces next to px, py.
336 * It updates the map_mark arrow with the color and high bits set
337 * for various code values.
338 */
339 static void
340 magic_mapping_mark_recursive (object *pl, char *map_mark, int px, int py)
341 {
342 int x, y, dx, dy, mflags;
343 sint16 nx, ny;
344 maptile *mp;
345
346 for (dx = -1; dx <= 1; dx++)
347 {
348 for (dy = -1; dy <= 1; dy++)
349 {
350 x = px + dx;
351 y = py + dy;
352
353 if (FABS (x) >= MAGIC_MAP_HALF || FABS (y) >= MAGIC_MAP_HALF)
354 continue;
355
356 mp = pl->map;
357 nx = pl->x + x;
358 ny = pl->y + y;
359
360 mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
361 if (mflags & P_OUT_OF_MAP)
362 continue;
363
364 if (map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] == 0)
365 {
366 int f = GET_MAP_FACE (mp, nx, ny, 0);
367 if (f == blank_face)
368 {
369 f = GET_MAP_FACE (mp, nx, ny, 1);
370 if (f == blank_face)
371 f = GET_MAP_FACE (mp, nx, ny, 2);
372 }
373
374 int magicmap = faces [f].magicmap;
375
376 /* Should probably have P_NO_MAGIC here also, but then shops don't
377 * work.
378 */
379 if (mflags & P_BLOCKSVIEW)
380 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
381 else
382 {
383 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
384 magic_mapping_mark_recursive (pl, map_mark, x, y);
385 }
386 }
387 }
388 }
389 }
390
391 /**
392 * Creates magic map for player.
393 *
394 * Note: For improved magic mapping display, the space that blocks
395 * the view is now marked with value 2. Any dependencies of map_mark
396 * being nonzero have been changed to check for 1. Also, since
397 * map_mark is a char value, putting 2 in should cause no problems.
398 *
399 * This function examines the map the player is on, and determines what
400 * is visible. 2 is set for walls or objects that blocks view. 1
401 * is for open spaces. map_mark should already have been initialised
402 * to zero before this is called.
403 * strength is an initial strength*2 rectangular area that we automatically
404 * see in/penetrate through.
405 */
406 void
407 magic_mapping_mark (object *pl, char *map_mark, int strength)
408 {
409 int x, y, mflags;
410 sint16 nx, ny;
411 maptile *mp;
412
413 for (x = -strength; x < strength; x++)
414 {
415 for (y = -strength; y < strength; y++)
416 {
417 mp = pl->map;
418 nx = pl->x + x;
419 ny = pl->y + y;
420
421 mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
422 if (mflags & P_OUT_OF_MAP)
423 continue;
424
425 int f = GET_MAP_FACE (mp, nx, ny, 0);
426 if (f == blank_face)
427 {
428 f = GET_MAP_FACE (mp, nx, ny, 1);
429 if (f == blank_face)
430 f = GET_MAP_FACE (mp, nx, ny, 2);
431 }
432
433 int magicmap = faces [f].magicmap;
434
435 if (mflags & P_BLOCKSVIEW)
436 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
437 else
438 {
439 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
440 magic_mapping_mark_recursive (pl, map_mark, x, y);
441 }
442 }
443 }
444 }
445
446 /**
447 * Creates and sends magic map to player.
448 *
449 * The following function is a lot messier than it really should be,
450 * but there is no real easy solution.
451 *
452 * Mark Wedel
453 */
454 void
455 draw_magic_map (object *pl)
456 {
457 char *map_mark = (char *)calloc (MAGIC_MAP_SIZE * MAGIC_MAP_SIZE, 1);
458 int xmin, xmax, ymin, ymax;
459
460 if (pl->type != PLAYER)
461 {
462 LOG (llevError, "Non player object called draw_map.\n");
463 return;
464 }
465
466 /* First, we figure out what spaces are 'reachable' by the player */
467 magic_mapping_mark (pl, map_mark, 3);
468
469 /* We now go through and figure out what spaces have been
470 * marked, and thus figure out rectangular region we send
471 * to the client (eg, if only a 10x10 area is visible, we only
472 * want to send those 100 spaces.)
473 */
474 xmin = MAGIC_MAP_SIZE;
475 ymin = MAGIC_MAP_SIZE;
476 xmax = 0;
477 ymax = 0;
478
479 for (int x = 0; x < MAGIC_MAP_SIZE; x++)
480 for (int y = 0; y < MAGIC_MAP_SIZE; y++)
481 if (map_mark[x + pl->map->width * y] | FACE_FLOOR)
482 {
483 xmin = x < xmin ? x : xmin;
484 xmax = x > xmax ? x : xmax;
485 ymin = y < ymin ? y : ymin;
486 ymax = y > ymax ? y : ymax;
487 }
488
489 packet sl;
490 sl.printf ("magicmap %d %d %d %d ", (xmax - xmin + 1), (ymax - ymin + 1),
491 MAGIC_MAP_HALF - xmin, MAGIC_MAP_HALF - ymin);
492
493 for (int y = ymin; y <= ymax; y++)
494 for (int x = xmin; x <= xmax; x++)
495 sl << uint8 (map_mark[x + MAGIC_MAP_SIZE * y] & ~FACE_FLOOR);
496
497 pl->contr->ns->send_packet (sl);
498
499 free (map_mark);
500 }
501
502 /**
503 * Send a kill log record to sockets
504 */
505 void
506 Log_Kill (const char *Who, const char *What, int WhatType, const char *With, int WithType)
507 {
508 size_t len;
509 char buf[MAX_BUF];
510
511 if (With != NULL)
512 snprintf (buf, MAX_BUF, "%s\t%s\t%d\t%s\t%d\n", Who, What, WhatType, With, WithType);
513 else
514 snprintf (buf, MAX_BUF, "%s\t%s\t%d\n", Who, What, WhatType);
515
516 len = strlen (buf);
517 }
518