ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.12
Committed: Thu Dec 14 00:01:37 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.11: +8 -13 lines
Log Message:
- further cleanup and rewrite
- fix a potential crash in "lock" and "mark" packets

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 root 1.7 The author can be reached via e-mail to <crossfire@schmorp.de>
22 elmex 1.1 */
23    
24     /**
25     * \file
26     * Client/server logic.
27     *
28     * \date 2003-12-02
29     *
30     * This containes item logic for client/server. It doesn't contain
31     * the actual commands that send the data, but does contain
32     * the logic for what items should be sent.
33     */
34    
35    
36     #include <global.h>
37 root 1.5 #include <object.h> /* LOOK_OBJ */
38 elmex 1.1 #include <newclient.h>
39     #include <newserver.h>
40     #include <sproto.h>
41    
42     /** This is the maximum number of bytes we expect any one item to take up */
43     #define MAXITEMLEN 300
44    
45     /*******************************************************************************
46     *
47     * Functions related to sending object data to the client.
48     *
49     ******************************************************************************/
50    
51     /**
52     * Adds string to socklist.
53     *
54     * This is a simple function that we use a lot here. It basically
55     * adds the specified buffer into the socklist, but prepends a
56     * single byte in length. If the data is longer than that byte, it is
57     * truncated approprately.
58     */
59 root 1.5 inline void
60     add_stringlen_to_sockbuf (const char *buf, SockList * sl)
61 elmex 1.1 {
62 root 1.5 int len;
63 elmex 1.1
64 root 1.5 len = strlen (buf);
65     if (len > 255)
66     len = 255;
67     SockList_AddChar (sl, (char) len);
68     strncpy ((char *) sl->buf + sl->len, buf, len);
69     sl->len += len;
70 elmex 1.1 }
71    
72     /**
73     * This is a similar to query_name, but returns flags
74     * to be sended to client.
75     */
76 root 1.5 unsigned int
77     query_flags (object *op)
78 elmex 1.1 {
79 root 1.5 unsigned int flags = 0;
80 elmex 1.1
81 root 1.5 if (QUERY_FLAG (op, FLAG_APPLIED))
82     {
83     switch (op->type)
84     {
85     case BOW:
86     case WAND:
87     case ROD:
88     case HORN:
89     flags = a_readied;
90     break;
91     case WEAPON:
92     flags = a_wielded;
93     break;
94     case SKILL:
95     case ARMOUR:
96     case HELMET:
97     case SHIELD:
98     case RING:
99     case BOOTS:
100     case GLOVES:
101     case AMULET:
102     case GIRDLE:
103     case BRACERS:
104     case CLOAK:
105     flags = a_worn;
106     break;
107     case CONTAINER:
108     flags = a_active;
109     break;
110     default:
111     flags = a_applied;
112     break;
113     }
114     }
115     if (op->type == CONTAINER && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG (op, FLAG_APPLIED))))
116     flags |= F_OPEN;
117    
118     if (QUERY_FLAG (op, FLAG_KNOWN_CURSED))
119     {
120     if (QUERY_FLAG (op, FLAG_DAMNED))
121     flags |= F_DAMNED;
122     else if (QUERY_FLAG (op, FLAG_CURSED))
123     flags |= F_CURSED;
124     }
125     if (QUERY_FLAG (op, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (op, FLAG_IDENTIFIED))
126     flags |= F_MAGIC;
127     if (QUERY_FLAG (op, FLAG_UNPAID))
128     flags |= F_UNPAID;
129     if (QUERY_FLAG (op, FLAG_INV_LOCKED))
130     flags |= F_LOCKED;
131 elmex 1.1
132 root 1.5 return flags;
133 elmex 1.1 }
134    
135     /* Used in the send_look to put object head into SockList
136     * sl for socket ns. Need socket to know if we need to send
137     * animation of face to the client.
138     */
139 root 1.5 static void
140     add_object_to_socklist (NewSocket * ns, SockList * sl, object *head)
141 elmex 1.1 {
142 root 1.5 int flags, len, anim_speed;
143     char item_n[MAX_BUF];
144     const char *item_p;
145    
146     flags = query_flags (head);
147     if (QUERY_FLAG (head, FLAG_NO_PICK))
148     flags |= F_NOPICK;
149    
150     if (!(ns->faces_sent[head->face->number] & NS_FACESENT_FACE))
151     esrv_send_face (ns, head->face->number, 0);
152    
153     if (QUERY_FLAG (head, FLAG_ANIMATE) && !ns->anims_sent[head->animation_id])
154     esrv_send_animation (ns, head->animation_id);
155    
156     SockList_AddInt (sl, head->count);
157     SockList_AddInt (sl, flags);
158     SockList_AddInt (sl, QUERY_FLAG (head, FLAG_NO_PICK) ? -1 : WEIGHT (head));
159     SockList_AddInt (sl, head->face->number);
160    
161     if (!head->custom_name)
162     {
163     strncpy (item_n, query_base_name (head, 0), 127);
164     item_n[127] = 0;
165     len = strlen (item_n);
166     item_p = query_base_name (head, 1);
167     }
168     else
169     {
170     strncpy (item_n, head->custom_name, 127);
171     item_n[127] = 0;
172     len = strlen (item_n);
173     item_p = head->custom_name;
174     }
175     strncpy (item_n + len + 1, item_p, 127);
176     item_n[254] = 0;
177     len += strlen (item_n + 1 + len) + 1;
178     SockList_AddChar (sl, (char) len);
179     memcpy (sl->buf + sl->len, item_n, len);
180     sl->len += len;
181    
182     SockList_AddShort (sl, head->animation_id);
183     anim_speed = 0;
184     if (QUERY_FLAG (head, FLAG_ANIMATE))
185     {
186     if (head->anim_speed)
187     anim_speed = head->anim_speed;
188     else
189     {
190     if (FABS (head->speed) < 0.001)
191     anim_speed = 255;
192     else if (FABS (head->speed) >= 1.0)
193     anim_speed = 1;
194     else
195     anim_speed = (int) (1.0 / FABS (head->speed));
196     }
197     if (anim_speed > 255)
198     anim_speed = 255;
199 elmex 1.1 }
200 root 1.5 SockList_AddChar (sl, (char) anim_speed);
201     SockList_AddInt (sl, head->nrof);
202 elmex 1.1
203 root 1.5 if (ns->itemcmd == 2)
204     SockList_AddShort (sl, head->client_type);
205 elmex 1.1
206 root 1.5 SET_FLAG (head, FLAG_CLIENT_SENT);
207 elmex 1.1 }
208    
209    
210     /**
211     * Send the look window. Don't need to do animations here
212     * This sends all the faces to the client, not just updates. This is
213     * because object ordering would otherwise be inconsistent
214     */
215    
216 root 1.5 void
217     esrv_draw_look (object *pl)
218 elmex 1.1 {
219 root 1.5 object *tmp, *last;
220     int got_one = 0, start_look = 0, end_look = 0;
221     SockList sl;
222     char buf[MAX_BUF];
223    
224     if (!pl->contr->socket.update_look)
225     {
226     LOG (llevDebug, "esrv_draw_look called when update_look was not set\n");
227     return;
228     }
229     else
230     {
231     pl->contr->socket.update_look = 0;
232     }
233    
234     if (QUERY_FLAG (pl, FLAG_REMOVED) || pl->map == NULL || pl->map->in_memory != MAP_IN_MEMORY || out_of_map (pl->map, pl->x, pl->y))
235     return;
236    
237     for (tmp = get_map_ob (pl->map, pl->x, pl->y); tmp && tmp->above; tmp = tmp->above);
238    
239     sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
240    
241     Write_String_To_Socket (&pl->contr->socket, "delinv 0", strlen ("delinv 0"));
242     sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
243     sl.len = strlen ((char *) sl.buf);
244    
245     SockList_AddInt (&sl, 0);
246    
247     if (!(pl->contr->socket.faces_sent[empty_face->number] & NS_FACESENT_FACE))
248     esrv_send_face (&pl->contr->socket, empty_face->number, 0);
249    
250     if (pl->contr->socket.look_position)
251     {
252     SockList_AddInt (&sl, 0x80000000 | (pl->contr->socket.look_position - NUM_LOOK_OBJECTS));
253     SockList_AddInt (&sl, 0);
254     SockList_AddInt (&sl, (uint32) - 1);
255     SockList_AddInt (&sl, empty_face->number);
256     sprintf (buf, "Click here to see %d previous items", NUM_LOOK_OBJECTS);
257     add_stringlen_to_sockbuf (buf, &sl);
258     SockList_AddShort (&sl, 0);
259     SockList_AddChar (&sl, 0);
260     SockList_AddInt (&sl, 0);
261     if (pl->contr->socket.itemcmd == 2)
262     SockList_AddShort (&sl, 0);
263     }
264    
265     for (last = NULL; tmp != last; tmp = tmp->below)
266     {
267     object *head;
268    
269     if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !last)
270     {
271     last = tmp->below; /* assumes double floor mode */
272     if (last && QUERY_FLAG (last, FLAG_IS_FLOOR))
273     last = last->below;
274     }
275     if (LOOK_OBJ (tmp))
276     {
277     if (++start_look < pl->contr->socket.look_position)
278     continue;
279     end_look++;
280     if (end_look > NUM_LOOK_OBJECTS)
281     {
282     /* What we basically do is make a 'fake' object - when the user applies it,
283     * we notice the special tag the object has, and act accordingly.
284     */
285     SockList_AddInt (&sl, 0x80000000 | (pl->contr->socket.look_position + NUM_LOOK_OBJECTS));
286     SockList_AddInt (&sl, 0);
287     SockList_AddInt (&sl, (uint32) - 1);
288     SockList_AddInt (&sl, empty_face->number);
289     sprintf (buf, "Click here to see next group of items");
290     add_stringlen_to_sockbuf (buf, &sl);
291     SockList_AddShort (&sl, 0);
292     SockList_AddChar (&sl, 0);
293     SockList_AddInt (&sl, 0);
294     if (pl->contr->socket.itemcmd == 2)
295     SockList_AddShort (&sl, 0);
296     break;
297 root 1.3 }
298 root 1.5 if (tmp->head)
299     head = tmp->head;
300     else
301     head = tmp;
302    
303     add_object_to_socklist (&pl->contr->socket, &sl, head);
304     got_one++;
305    
306     if (sl.len >= (MAXSOCKBUF - MAXITEMLEN))
307     {
308     Send_With_Handling (&pl->contr->socket, &sl);
309     sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
310     sl.len = strlen ((char *) sl.buf);
311     SockList_AddInt (&sl, 0);
312     got_one = 0;
313 root 1.3 }
314 root 1.5 } /* If LOOK_OBJ() */
315 elmex 1.1 }
316 root 1.5 if (got_one)
317     Send_With_Handling (&pl->contr->socket, &sl);
318 elmex 1.1
319 root 1.5 free (sl.buf);
320 elmex 1.1 }
321    
322     /**
323     * Sends whole inventory.
324     */
325 root 1.5 void
326     esrv_send_inventory (object *pl, object *op)
327 elmex 1.1 {
328 root 1.5 object *tmp;
329     int got_one = 0;
330     SockList sl;
331    
332     sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
333    
334     sprintf ((char *) sl.buf, "delinv %d", op->count);
335     sl.len = strlen ((char *) sl.buf);
336     Send_With_Handling (&pl->contr->socket, &sl);
337    
338     sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
339     sl.len = strlen ((char *) sl.buf);
340    
341     SockList_AddInt (&sl, op->count);
342    
343     for (tmp = op->inv; tmp; tmp = tmp->below)
344     {
345     object *head;
346    
347     if (tmp->head)
348     head = tmp->head;
349     else
350     head = tmp;
351    
352     if (LOOK_OBJ (head))
353     {
354     add_object_to_socklist (&pl->contr->socket, &sl, head);
355    
356     got_one++;
357    
358     /* IT is possible for players to accumulate a huge amount of
359     * items (especially with some of the bags out there) to
360     * overflow the buffer. IF so, send multiple item commands.
361     */
362     if (sl.len >= (MAXSOCKBUF - MAXITEMLEN))
363     {
364     Send_With_Handling (&pl->contr->socket, &sl);
365     sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
366     sl.len = strlen ((char *) sl.buf);
367     SockList_AddInt (&sl, op->count);
368     got_one = 0;
369 root 1.3 }
370 root 1.5 } /* If LOOK_OBJ() */
371 elmex 1.1 }
372 root 1.5 if (got_one)
373     Send_With_Handling (&pl->contr->socket, &sl);
374     free (sl.buf);
375 elmex 1.1 }
376    
377     /**
378     * Updates object *op for player *pl.
379     *
380     * flags is a list of values to update
381     * to the client (as defined in newclient.h - might as well use the
382     * same value both places.
383     */
384    
385 root 1.5 void
386     esrv_update_item (int flags, object *pl, object *op)
387 elmex 1.1 {
388 root 1.5 SockList sl;
389 elmex 1.1
390 root 1.5 /* If we have a request to send the player item, skip a few checks. */
391     if (op != pl)
392     {
393     if (!LOOK_OBJ (op))
394     return;
395     /* we remove the check for op->env, because in theory, the object
396     * is hopefully in the same place, so the client should preserve
397     * order.
398     */
399     }
400 root 1.11
401 root 1.5 if (!QUERY_FLAG (op, FLAG_CLIENT_SENT))
402     {
403     /* FLAG_CLIENT_SENT is debug only. We are using it to see where
404     * this is happening - we can set a breakpoint here in the debugger
405     * and track back the call.
406     */
407     LOG (llevDebug, "We have not sent item %s (%d)\n", &op->name, op->count);
408     }
409 root 1.11
410 root 1.5 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
411    
412     strcpy ((char *) sl.buf, "upditem ");
413     sl.len = strlen ((char *) sl.buf);
414    
415     SockList_AddChar (&sl, (char) flags);
416    
417     if (op->head)
418     op = op->head;
419    
420     SockList_AddInt (&sl, op->count);
421    
422     if (flags & UPD_LOCATION)
423     SockList_AddInt (&sl, op->env ? op->env->count : 0);
424    
425     if (flags & UPD_FLAGS)
426     SockList_AddInt (&sl, query_flags (op));
427    
428     if (flags & UPD_WEIGHT)
429     {
430     sint32 weight = WEIGHT (op);
431    
432     SockList_AddInt (&sl, QUERY_FLAG (op, FLAG_NO_PICK) ? -1 : weight);
433     if (pl == op)
434     {
435     op->contr->last_weight = weight;
436     }
437     }
438    
439     if (flags & UPD_FACE)
440     {
441     if (!(pl->contr->socket.faces_sent[op->face->number] & NS_FACESENT_FACE))
442     esrv_send_face (&pl->contr->socket, op->face->number, 0);
443     SockList_AddInt (&sl, op->face->number);
444     }
445 root 1.11
446 root 1.5 if (flags & UPD_NAME)
447     {
448     int len;
449     const char *item_p;
450     char item_n[MAX_BUF];
451    
452     if (!op->custom_name)
453     {
454     strncpy (item_n, query_base_name (op, 0), 127);
455     item_n[127] = 0;
456     len = strlen (item_n);
457     item_p = query_base_name (op, 1);
458     }
459     else
460     {
461     strncpy (item_n, op->custom_name, 127);
462     item_n[127] = 0;
463     len = strlen (item_n);
464     item_p = op->custom_name;
465     }
466    
467     strncpy (item_n + len + 1, item_p, 127);
468     item_n[254] = 0;
469     len += strlen (item_n + 1 + len) + 1;
470     SockList_AddChar (&sl, (char) len);
471     memcpy (sl.buf + sl.len, item_n, len);
472     sl.len += len;
473     }
474 root 1.11
475 root 1.5 if (flags & UPD_ANIM)
476     SockList_AddShort (&sl, op->animation_id);
477    
478     if (flags & UPD_ANIMSPEED)
479     {
480     int anim_speed = 0;
481    
482     if (QUERY_FLAG (op, FLAG_ANIMATE))
483     {
484     if (op->anim_speed)
485     anim_speed = op->anim_speed;
486     else
487     {
488     if (FABS (op->speed) < 0.001)
489     anim_speed = 255;
490     else if (FABS (op->speed) >= 1.0)
491     anim_speed = 1;
492     else
493     anim_speed = (int) (1.0 / FABS (op->speed));
494 root 1.3 }
495 root 1.5 if (anim_speed > 255)
496     anim_speed = 255;
497 root 1.3 }
498 root 1.5 SockList_AddChar (&sl, (char) anim_speed);
499 elmex 1.1 }
500 root 1.5 if (flags & UPD_NROF)
501     SockList_AddInt (&sl, op->nrof);
502 elmex 1.1
503 root 1.5 Send_With_Handling (&pl->contr->socket, &sl);
504     free (sl.buf);
505 elmex 1.1 }
506    
507     /**
508     * Sends item's info to player.
509     */
510 root 1.5 void
511     esrv_send_item (object *pl, object *op)
512 elmex 1.1 {
513 root 1.5 SockList sl;
514    
515     /* If this is not the player object, do some more checks */
516     if (op != pl)
517     {
518     /* We only send 'visibile' objects to the client */
519     if (!LOOK_OBJ (op))
520     return;
521     /* if the item is on the ground, mark that the look needs to
522     * be updated.
523     */
524     if (!op->env)
525     {
526     pl->contr->socket.update_look = 1;
527     return;
528 root 1.3 }
529 elmex 1.1 }
530    
531 root 1.5 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
532 elmex 1.1
533 root 1.5 sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
534     sl.len = strlen ((char *) sl.buf);
535 elmex 1.1
536 root 1.5 if (op->head)
537     op = op->head;
538 elmex 1.1
539 root 1.5 SockList_AddInt (&sl, op->env ? op->env->count : 0);
540 elmex 1.1
541 root 1.5 add_object_to_socklist (&pl->contr->socket, &sl, op);
542 elmex 1.1
543 root 1.5 Send_With_Handling (&pl->contr->socket, &sl);
544     SET_FLAG (op, FLAG_CLIENT_SENT);
545     free (sl.buf);
546 elmex 1.1 }
547    
548     /**
549     * Tells the client to delete an item. Uses the item
550     * command with a -1 location.
551     */
552    
553 root 1.5 void
554     esrv_del_item (player *pl, int tag)
555 elmex 1.1 {
556 root 1.5 SockList sl;
557 elmex 1.1
558 root 1.5 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
559 elmex 1.1
560 root 1.5 strcpy ((char *) sl.buf, "delitem ");
561     sl.len = strlen ((char *) sl.buf);
562     SockList_AddInt (&sl, tag);
563 elmex 1.1
564 root 1.5 Send_With_Handling (&pl->socket, &sl);
565     free (sl.buf);
566 elmex 1.1 }
567    
568    
569     /*******************************************************************************
570     *
571     * Client has requested us to do something with an object.
572     *
573     ******************************************************************************/
574    
575     /**
576     * Takes a player and object count (tag) and returns the actual object
577     * pointer, or null if it can't be found.
578     */
579    
580 root 1.5 object *
581     esrv_get_ob_from_count (object *pl, tag_t count)
582 elmex 1.1 {
583 root 1.5 object *op, *tmp;
584 elmex 1.1
585 root 1.5 if (pl->count == count)
586     return pl;
587 elmex 1.1
588 root 1.5 for (op = pl->inv; op; op = op->below)
589     if (op->count == count)
590     return op;
591     else if (op->type == CONTAINER && pl->container == op)
592     for (tmp = op->inv; tmp; tmp = tmp->below)
593     if (tmp->count == count)
594     return tmp;
595    
596     for (op = get_map_ob (pl->map, pl->x, pl->y); op; op = op->above)
597     if (op->head != NULL && op->head->count == count)
598     return op;
599     else if (op->count == count)
600     return op;
601     else if (op->type == CONTAINER && pl->container == op)
602     for (tmp = op->inv; tmp; tmp = tmp->below)
603     if (tmp->count == count)
604     return tmp;
605 elmex 1.1
606 root 1.5 return NULL;
607 elmex 1.1 }
608    
609    
610     /** Client wants to examine some object. So lets do so. */
611 root 1.5 void
612     ExamineCmd (char *buf, int len, player *pl)
613 elmex 1.1 {
614 root 1.10 tag_t tag = atoi (buf);
615    
616     /* If the high bit is set, player examined a pseudo object. */
617     if (tag & 0x80000000)
618     return;
619    
620 root 1.5 object *op = esrv_get_ob_from_count (pl->ob, tag);
621 elmex 1.1
622 root 1.5 if (!op)
623     {
624     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
625     return;
626 elmex 1.1 }
627 root 1.10
628 root 1.5 examine (pl->ob, op);
629 elmex 1.1 }
630    
631     /** Client wants to apply some object. Lets do so. */
632 root 1.5 void
633     ApplyCmd (char *buf, int len, player *pl)
634 elmex 1.1 {
635 root 1.10 tag_t tag = atoi (buf);
636 elmex 1.1
637 root 1.5 /* sort of a hack, but if the player saves and the player then manually
638     * applies a savebed (or otherwise tries to do stuff), we run into trouble.
639     */
640     if (QUERY_FLAG (pl->ob, FLAG_REMOVED))
641     return;
642    
643     /* If the high bit is set, player applied a pseudo object. */
644     if (tag & 0x80000000)
645     {
646     pl->socket.look_position = tag & 0x7fffffff;
647     pl->socket.update_look = 1;
648     return;
649     }
650    
651 root 1.10 object *op = esrv_get_ob_from_count (pl->ob, tag);
652    
653 root 1.5 if (!op)
654     {
655     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
656     return;
657 elmex 1.1 }
658 root 1.10
659 root 1.5 player_apply (pl->ob, op, 0, 0);
660 elmex 1.1 }
661    
662     /** Client wants to apply some object. Lets do so. */
663 root 1.5 void
664 root 1.12 LockItem (uint8 *data, int len, player *pl)
665 elmex 1.1 {
666 root 1.12 int flag = data[0];
667     tag_t tag = net_uint32 (data + 1);
668     object *op = esrv_get_ob_from_count (pl->ob, tag);
669 root 1.5
670     if (!op)
671     {
672     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to lock/unlock");
673     return;
674     }
675 root 1.12
676 root 1.5 if (!flag)
677     CLEAR_FLAG (op, FLAG_INV_LOCKED);
678     else
679     SET_FLAG (op, FLAG_INV_LOCKED);
680 root 1.12
681 root 1.5 esrv_update_item (UPD_FLAGS, pl->ob, op);
682 elmex 1.1 }
683    
684     /** Client wants to apply some object. Lets do so. */
685 root 1.5 void
686     MarkItem (uint8 * data, int len, player *pl)
687 elmex 1.1 {
688 root 1.12 tag_t tag = net_uint32 (data);
689     object *op = esrv_get_ob_from_count (pl->ob, tag);
690 root 1.9
691 root 1.5 if (!op)
692     {
693     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to mark");
694     return;
695     }
696 root 1.9
697 root 1.5 pl->mark = op;
698     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name (op));
699 elmex 1.1 }
700    
701     /**
702     * look_at prints items on the specified square.
703     *
704     * [ removed EARTHWALL check and added check for containers inventory.
705     * Tero.Haatanen@lut.fi ]
706     */
707 root 1.5 void
708     look_at (object *op, int dx, int dy)
709     {
710     object *tmp;
711     int flag = 0;
712     sint16 x, y;
713 root 1.8 maptile *m;
714 root 1.5
715     x = op->x + dx;
716     y = op->y + dy;
717    
718     if (out_of_map (op->map, x, y))
719     return;
720    
721     m = get_map_from_coord (op->map, &x, &y);
722     if (!m)
723     return;
724    
725     for (tmp = get_map_ob (m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above);
726    
727     for (; tmp != NULL; tmp = tmp->below)
728     {
729     if (tmp->invisible && !QUERY_FLAG (op, FLAG_WIZ))
730     continue;
731    
732     if (!flag)
733     {
734     if (dx || dy)
735     new_draw_info (NDI_UNIQUE, 0, op, "There you see:");
736     else
737     {
738     clear_win_info (op);
739     new_draw_info (NDI_UNIQUE, 0, op, "You see:");
740 root 1.3 }
741 root 1.5 flag = 1;
742     }
743 root 1.3
744 root 1.5 if (QUERY_FLAG (op, FLAG_WIZ))
745     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s (%d).", query_name (tmp), tmp->count);
746     else
747     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s.", query_name (tmp));
748    
749     if (((tmp->inv != NULL || (tmp->head && tmp->head->inv)) &&
750     (tmp->type != CONTAINER && tmp->type != FLESH)) || QUERY_FLAG (op, FLAG_WIZ))
751     inventory (op, tmp->head == NULL ? tmp : tmp->head);
752    
753     if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (op, FLAG_WIZ)) /* don't continue under the floor */
754     break;
755     }
756    
757     if (!flag)
758     {
759     if (dx || dy)
760     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing there.");
761     else
762     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing.");
763 elmex 1.1 }
764     }
765    
766    
767    
768     /** Client wants to look at some object. Lets do so. */
769 root 1.5 void
770     LookAt (char *buf, int len, player *pl)
771 elmex 1.1 {
772 root 1.5 int dx, dy;
773     char *cp;
774 elmex 1.1
775 root 1.5 dx = atoi (buf);
776     if (!(cp = strchr (buf, ' ')))
777     {
778     return;
779 elmex 1.1 }
780 root 1.5 dy = atoi (cp);
781 elmex 1.1
782 root 1.5 if (FABS (dx) > pl->socket.mapx / 2 || FABS (dy) > pl->socket.mapy / 2)
783     return;
784 elmex 1.1
785 root 1.5 if (pl->blocked_los[dx + pl->socket.mapx / 2][dy + pl->socket.mapy / 2])
786     return;
787 elmex 1.1
788 root 1.5 look_at (pl->ob, dx, dy);
789 elmex 1.1 }
790    
791     /** Move an object to a new location */
792 root 1.5 void
793     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
794 elmex 1.1 {
795 root 1.5 object *op, *env;
796 elmex 1.1
797 root 1.5 op = esrv_get_ob_from_count (pl, tag);
798     if (!op)
799     {
800     LOG (llevDebug, "Player '%s' tried to move an unknown object (%ld)\n", &pl->name, tag);
801     return;
802 elmex 1.1 }
803    
804 root 1.5 if (!to)
805     { /* drop it to the ground */
806    
807 elmex 1.1 /* LOG(llevDebug, "Drop it on the ground.\n");*/
808    
809 root 1.5 if (op->map && !op->env)
810     {
811    
812 elmex 1.1 /* LOG(llevDebug,"Dropping object to ground that is already on ground\n");*/
813 root 1.5 return;
814 root 1.3 }
815 root 1.5 /* If it is an active container, then we should drop all objects
816     * in the container and not the container itself.
817     */
818     if (op->inv && QUERY_FLAG (op, FLAG_APPLIED))
819     {
820     object *current, *next;
821    
822     for (current = op->inv; current != NULL; current = next)
823     {
824     next = current->below;
825     drop_object (pl, current, 0);
826 root 1.3 }
827 root 1.5 esrv_update_item (UPD_WEIGHT, pl, op);
828 root 1.3 }
829 root 1.5 else
830     {
831     drop_object (pl, op, nrof);
832     }
833     return;
834     }
835     else if (to == pl->count)
836     { /* pick it up to the inventory */
837     /* return if player has already picked it up */
838     if (op->env == pl)
839 root 1.3 return;
840 root 1.5
841     pl->contr->count = nrof;
842     pick_up (pl, op);
843     return;
844     }
845     env = esrv_get_ob_from_count (pl, to);
846     if (!env)
847     {
848     LOG (llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", &pl->name, to);
849     return;
850     }
851     /* put_object_in_sack presumes that necessary sanity checking
852     * has already been done (eg, it can be picked up and fits in
853     * in a sack, so check for those things. We should also check
854     * an make sure env is in fact a container for that matter.
855     */
856     if (env->type == CONTAINER && can_pick (pl, op) && sack_can_hold (pl, env, op, nrof))
857     {
858     put_object_in_sack (pl, env, op, nrof);
859 elmex 1.1 }
860     }