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

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.33 * 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 elmex 1.1
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 root 1.5 static void
48 root 1.22 esrv_print_msg (client *ns, int color, const char *str)
49 elmex 1.1 {
50 root 1.21 ns->send_packet_printf ("drawinfo %d %s", color, str);
51 elmex 1.1 }
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 root 1.5 static void
63 root 1.22 esrv_print_ext_msg (client *ns, int color, uint8 type, uint8 subtype, const char *message)
64 elmex 1.1 {
65 root 1.21 ns->send_packet_printf ("drawextinfo %d %hhu %hhu %s", color, type, subtype, message);
66 elmex 1.1 }
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 root 1.5 static void
79     print_message (int colr, const object *pl, const char *tmp)
80     {
81 root 1.11 if (!tmp)
82     tmp = "[NULL]";
83 elmex 1.1
84     if (!pl || (pl->type == PLAYER && pl->contr == NULL))
85     return;
86    
87 root 1.5 if (pl->type == PLAYER)
88 root 1.23 esrv_print_msg (pl->contr->ns, colr, (char *) tmp);
89 elmex 1.1 }
90    
91 root 1.38 bool
92     client::msg_suppressed (const char *msg)
93 elmex 1.1 {
94 root 1.38 if (!pl)
95     return false;
96 elmex 1.1
97 root 1.38 if (pl->outputs_count <= 1 || !pl->outputs_sync)
98     return false;
99 elmex 1.1
100 root 1.38 int len = strlen (msg);
101 elmex 1.1
102 root 1.38 if (len > MSG_BUF_SIZE)
103     return false;
104 elmex 1.1
105 root 1.38 msg_buf *lru = msgbuf;
106     for (msg_buf *buf = msgbuf; buf < msgbuf + MSG_BUF_COUNT; ++buf)
107 root 1.5 {
108 root 1.38 if (len == buf->len && !memcmp (msg, buf->msg, len))
109 root 1.5 {
110 root 1.38 // found matching buf, see if expired
111     if (buf->expire <= pticks || !buf->count)
112 root 1.5 {
113 root 1.38 // yes, take over matching buffer, print
114     buf->expire = pticks + pl->outputs_sync;
115     buf->count = pl->outputs_count;
116    
117     return false;
118 root 1.2 }
119 root 1.38
120     // no, suppress
121     --buf->count;
122     return true;
123 root 1.2 }
124 root 1.5
125 root 1.38 if (lru->expire > buf->expire)
126     lru = buf;
127 elmex 1.1 }
128 root 1.38
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 elmex 1.1 }
137 root 1.5
138 elmex 1.1 /**
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 root 1.5 void
155     new_draw_info (int flags, int pri, const object *pl, const char *buf)
156 elmex 1.1 {
157 root 1.5 if (flags & NDI_ALL)
158     {
159 root 1.27 for_all_players (pl)
160     new_draw_info (flags & ~NDI_ALL, pri, pl->ob, buf);
161 elmex 1.1
162 root 1.5 return;
163 elmex 1.1 }
164 root 1.11
165 root 1.28 if (!pl || pl->type != PLAYER || !pl->contr || !pl->contr->ns)
166 root 1.5 return;
167 root 1.11
168 root 1.5 if (pri >= pl->contr->listening)
169     return;
170    
171 root 1.38 if ((flags & (NDI_COLOR_MASK | NDI_UNIQUE)) != NDI_BLACK
172     || !pl->contr->ns->msg_suppressed (buf))
173 root 1.11 print_message (flags & NDI_COLOR_MASK, pl, buf);
174 elmex 1.1 }
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 root 1.5 void
185     new_draw_info_format (int flags, int pri, const object *pl, const char *format, ...)
186 elmex 1.1 {
187 root 1.5 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 elmex 1.1
194 root 1.5 new_draw_info (flags, pri, pl, buf);
195 elmex 1.1 }
196    
197 root 1.5 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 root 1.17
207 root 1.23 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
208 root 1.5 {
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 elmex 1.1 }
220 root 1.5 }
221     else
222 root 1.23 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, message);
223 elmex 1.1 }
224    
225 root 1.5 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 root 1.17
236 root 1.23 if (!CLIENT_SUPPORT_READABLES (pl->contr->ns, type))
237 root 1.5 {
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 root 1.23 esrv_print_ext_msg (pl->contr->ns, flags & NDI_COLOR_MASK, type, subtype, buf);
256 elmex 1.1 }
257     }
258 root 1.5
259 elmex 1.1 /**
260     * Writes to everyone on the map *except* op. This is useful for emotions.
261     */
262    
263 root 1.5 void
264 root 1.8 new_info_map_except (int color, maptile * map, object *op, const char *str)
265 root 1.5 {
266 root 1.24 for_all_players (pl)
267 root 1.5 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op)
268 root 1.31 new_draw_info (color, 0, pl->ob, str);
269 elmex 1.1 }
270    
271     /**
272     * Writes to everyone on the map except op1 and op2
273     */
274    
275 root 1.5 void
276 root 1.8 new_info_map_except2 (int color, maptile * map, object *op1, object *op2, const char *str)
277 root 1.5 {
278 root 1.24 for_all_players (pl)
279 root 1.5 if (pl->ob != NULL && pl->ob->map == map && pl->ob != op1 && pl->ob != op2)
280 root 1.31 new_draw_info (color, 0, pl->ob, str);
281 elmex 1.1 }
282    
283     /**
284     * Writes to everyone on the specified map
285     */
286    
287 root 1.5 void
288 root 1.8 new_info_map (int color, maptile * map, const char *str)
289 root 1.5 {
290 root 1.24 for_all_players (pl)
291 root 1.5 if (pl->ob != NULL && pl->ob->map == map)
292 root 1.31 new_draw_info (color, 0, pl->ob, str);
293 elmex 1.1 }
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 root 1.5 void
301     clear_win_info (object *op)
302 elmex 1.1 {
303     }
304    
305     /**
306     * Sets player title.
307     */
308 root 1.5 void
309     set_title (object *pl, char *buf)
310 elmex 1.1 {
311 root 1.5 /* 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 elmex 1.1 }
317    
318 root 1.36 // 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 elmex 1.1 /**
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 root 1.5 static void
340     magic_mapping_mark_recursive (object *pl, char *map_mark, int px, int py)
341 elmex 1.1 {
342 root 1.5 int x, y, dx, dy, mflags;
343     sint16 nx, ny;
344 root 1.7 maptile *mp;
345 root 1.5
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 root 1.35 int f = GET_MAP_FACE (mp, nx, ny, 0);
367 root 1.5 if (f == blank_face)
368 root 1.35 {
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 root 1.5
376     /* Should probably have P_NO_MAGIC here also, but then shops don't
377     * work.
378     */
379     if (mflags & P_BLOCKSVIEW)
380 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
381 root 1.5 else
382     {
383 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
384 root 1.5 magic_mapping_mark_recursive (pl, map_mark, x, y);
385 root 1.2 }
386     }
387     }
388 elmex 1.1 }
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 pippijn 1.29 * is for open spaces. map_mark should already have been initialised
402 elmex 1.1 * 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 root 1.5 void
407     magic_mapping_mark (object *pl, char *map_mark, int strength)
408 elmex 1.1 {
409 root 1.5 int x, y, mflags;
410     sint16 nx, ny;
411 root 1.7 maptile *mp;
412 root 1.5
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 root 1.35
421 root 1.5 mflags = get_map_flags (pl->map, &mp, nx, ny, &nx, &ny);
422     if (mflags & P_OUT_OF_MAP)
423     continue;
424 root 1.35
425     int f = GET_MAP_FACE (mp, nx, ny, 0);
426     if (f == blank_face)
427 root 1.5 {
428 root 1.35 f = GET_MAP_FACE (mp, nx, ny, 1);
429 root 1.5 if (f == blank_face)
430     f = GET_MAP_FACE (mp, nx, ny, 2);
431 root 1.2 }
432    
433 root 1.35 int magicmap = faces [f].magicmap;
434    
435 root 1.5 if (mflags & P_BLOCKSVIEW)
436 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_WALL | magicmap;
437 root 1.5 else
438     {
439 root 1.35 map_mark[MAGIC_MAP_HALF + x + MAGIC_MAP_SIZE * (MAGIC_MAP_HALF + y)] = FACE_FLOOR | magicmap;
440 root 1.5 magic_mapping_mark_recursive (pl, map_mark, x, y);
441 root 1.2 }
442     }
443 elmex 1.1 }
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 root 1.5 void
455     draw_magic_map (object *pl)
456 elmex 1.1 {
457 root 1.13 char *map_mark = (char *)calloc (MAGIC_MAP_SIZE * MAGIC_MAP_SIZE, 1);
458 root 1.5 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 root 1.13
479     for (int x = 0; x < MAGIC_MAP_SIZE; x++)
480     for (int y = 0; y < MAGIC_MAP_SIZE; y++)
481 root 1.25 if (map_mark[x + pl->map->width * y] | FACE_FLOOR)
482 root 1.5 {
483 root 1.13 xmin = x < xmin ? x : xmin;
484     xmax = x > xmax ? x : xmax;
485     ymin = y < ymin ? y : ymin;
486     ymax = y > ymax ? y : ymax;
487 root 1.2 }
488 elmex 1.1
489 root 1.15 packet sl;
490 root 1.13 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 root 1.23 pl->contr->ns->send_packet (sl);
498 root 1.5
499     free (map_mark);
500 elmex 1.1 }
501    
502     /**
503     * Send a kill log record to sockets
504     */
505 root 1.5 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 root 1.11 snprintf (buf, MAX_BUF, "%s\t%s\t%d\t%s\t%d\n", Who, What, WhatType, With, WithType);
513 root 1.5 else
514 root 1.11 snprintf (buf, MAX_BUF, "%s\t%s\t%d\n", Who, What, WhatType);
515    
516 root 1.5 len = strlen (buf);
517 elmex 1.1 }
518 root 1.22