ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.6
Committed: Tue Sep 12 19:20:09 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +0 -6 lines
Log Message:
- improve assign to prepend "..."
- make more use of assign
- implement op->debug_desc() and make some more use of it

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     The author can be reached via e-mail to crossfire-devel@real-time.com
22     */
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     if (!QUERY_FLAG (op, FLAG_CLIENT_SENT))
401     {
402     /* FLAG_CLIENT_SENT is debug only. We are using it to see where
403     * this is happening - we can set a breakpoint here in the debugger
404     * and track back the call.
405     */
406     LOG (llevDebug, "We have not sent item %s (%d)\n", &op->name, op->count);
407     }
408     sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
409    
410     strcpy ((char *) sl.buf, "upditem ");
411     sl.len = strlen ((char *) sl.buf);
412    
413     SockList_AddChar (&sl, (char) flags);
414    
415     if (op->head)
416     op = op->head;
417    
418     SockList_AddInt (&sl, op->count);
419    
420     if (flags & UPD_LOCATION)
421     SockList_AddInt (&sl, op->env ? op->env->count : 0);
422    
423     if (flags & UPD_FLAGS)
424     SockList_AddInt (&sl, query_flags (op));
425    
426     if (flags & UPD_WEIGHT)
427     {
428     sint32 weight = WEIGHT (op);
429    
430     SockList_AddInt (&sl, QUERY_FLAG (op, FLAG_NO_PICK) ? -1 : weight);
431     if (pl == op)
432     {
433     op->contr->last_weight = weight;
434     }
435     }
436    
437     if (flags & UPD_FACE)
438     {
439     if (!(pl->contr->socket.faces_sent[op->face->number] & NS_FACESENT_FACE))
440     esrv_send_face (&pl->contr->socket, op->face->number, 0);
441     SockList_AddInt (&sl, op->face->number);
442     }
443     if (flags & UPD_NAME)
444     {
445     int len;
446     const char *item_p;
447     char item_n[MAX_BUF];
448    
449     if (!op->custom_name)
450     {
451     strncpy (item_n, query_base_name (op, 0), 127);
452     item_n[127] = 0;
453     len = strlen (item_n);
454     item_p = query_base_name (op, 1);
455     }
456     else
457     {
458     strncpy (item_n, op->custom_name, 127);
459     item_n[127] = 0;
460     len = strlen (item_n);
461     item_p = op->custom_name;
462     }
463    
464     strncpy (item_n + len + 1, item_p, 127);
465     item_n[254] = 0;
466     len += strlen (item_n + 1 + len) + 1;
467     SockList_AddChar (&sl, (char) len);
468     memcpy (sl.buf + sl.len, item_n, len);
469     sl.len += len;
470     }
471     if (flags & UPD_ANIM)
472     SockList_AddShort (&sl, op->animation_id);
473    
474     if (flags & UPD_ANIMSPEED)
475     {
476     int anim_speed = 0;
477    
478     if (QUERY_FLAG (op, FLAG_ANIMATE))
479     {
480     if (op->anim_speed)
481     anim_speed = op->anim_speed;
482     else
483     {
484     if (FABS (op->speed) < 0.001)
485     anim_speed = 255;
486     else if (FABS (op->speed) >= 1.0)
487     anim_speed = 1;
488     else
489     anim_speed = (int) (1.0 / FABS (op->speed));
490 root 1.3 }
491 root 1.5 if (anim_speed > 255)
492     anim_speed = 255;
493 root 1.3 }
494 root 1.5 SockList_AddChar (&sl, (char) anim_speed);
495 elmex 1.1 }
496 root 1.5 if (flags & UPD_NROF)
497     SockList_AddInt (&sl, op->nrof);
498 elmex 1.1
499 root 1.5 Send_With_Handling (&pl->contr->socket, &sl);
500     free (sl.buf);
501 elmex 1.1 }
502    
503     /**
504     * Sends item's info to player.
505     */
506 root 1.5 void
507     esrv_send_item (object *pl, object *op)
508 elmex 1.1 {
509 root 1.5 SockList sl;
510    
511     /* If this is not the player object, do some more checks */
512     if (op != pl)
513     {
514     /* We only send 'visibile' objects to the client */
515     if (!LOOK_OBJ (op))
516     return;
517     /* if the item is on the ground, mark that the look needs to
518     * be updated.
519     */
520     if (!op->env)
521     {
522     pl->contr->socket.update_look = 1;
523     return;
524 root 1.3 }
525 elmex 1.1 }
526    
527 root 1.5 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
528 elmex 1.1
529 root 1.5 sprintf ((char *) sl.buf, "item%d ", pl->contr->socket.itemcmd);
530     sl.len = strlen ((char *) sl.buf);
531 elmex 1.1
532 root 1.5 if (op->head)
533     op = op->head;
534 elmex 1.1
535 root 1.5 SockList_AddInt (&sl, op->env ? op->env->count : 0);
536 elmex 1.1
537 root 1.5 add_object_to_socklist (&pl->contr->socket, &sl, op);
538 elmex 1.1
539 root 1.5 Send_With_Handling (&pl->contr->socket, &sl);
540     SET_FLAG (op, FLAG_CLIENT_SENT);
541     free (sl.buf);
542 elmex 1.1 }
543    
544     /**
545     * Tells the client to delete an item. Uses the item
546     * command with a -1 location.
547     */
548    
549 root 1.5 void
550     esrv_del_item (player *pl, int tag)
551 elmex 1.1 {
552 root 1.5 SockList sl;
553 elmex 1.1
554 root 1.5 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
555 elmex 1.1
556 root 1.5 strcpy ((char *) sl.buf, "delitem ");
557     sl.len = strlen ((char *) sl.buf);
558     SockList_AddInt (&sl, tag);
559 elmex 1.1
560 root 1.5 Send_With_Handling (&pl->socket, &sl);
561     free (sl.buf);
562 elmex 1.1 }
563    
564    
565     /*******************************************************************************
566     *
567     * Client has requested us to do something with an object.
568     *
569     ******************************************************************************/
570    
571     /**
572     * Takes a player and object count (tag) and returns the actual object
573     * pointer, or null if it can't be found.
574     */
575    
576 root 1.5 object *
577     esrv_get_ob_from_count (object *pl, tag_t count)
578 elmex 1.1 {
579 root 1.5 object *op, *tmp;
580 elmex 1.1
581 root 1.5 if (pl->count == count)
582     return pl;
583 elmex 1.1
584 root 1.5 for (op = pl->inv; op; op = op->below)
585     if (op->count == count)
586     return op;
587     else if (op->type == CONTAINER && pl->container == op)
588     for (tmp = op->inv; tmp; tmp = tmp->below)
589     if (tmp->count == count)
590     return tmp;
591    
592     for (op = get_map_ob (pl->map, pl->x, pl->y); op; op = op->above)
593     if (op->head != NULL && op->head->count == count)
594     return op;
595     else if (op->count == count)
596     return op;
597     else if (op->type == CONTAINER && pl->container == op)
598     for (tmp = op->inv; tmp; tmp = tmp->below)
599     if (tmp->count == count)
600     return tmp;
601 elmex 1.1
602 root 1.5 return NULL;
603 elmex 1.1 }
604    
605    
606     /** Client wants to examine some object. So lets do so. */
607 root 1.5 void
608     ExamineCmd (char *buf, int len, player *pl)
609 elmex 1.1 {
610 root 1.5 long tag = atoi (buf);
611     object *op = esrv_get_ob_from_count (pl->ob, tag);
612 elmex 1.1
613 root 1.5 if (!op)
614     {
615     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
616     return;
617 elmex 1.1 }
618 root 1.5 examine (pl->ob, op);
619 elmex 1.1 }
620    
621     /** Client wants to apply some object. Lets do so. */
622 root 1.5 void
623     ApplyCmd (char *buf, int len, player *pl)
624 elmex 1.1 {
625 root 1.5 uint32 tag = atoi (buf);
626     object *op = esrv_get_ob_from_count (pl->ob, tag);
627 elmex 1.1
628 root 1.5 /* sort of a hack, but if the player saves and the player then manually
629     * applies a savebed (or otherwise tries to do stuff), we run into trouble.
630     */
631     if (QUERY_FLAG (pl->ob, FLAG_REMOVED))
632     return;
633    
634     /* If the high bit is set, player applied a pseudo object. */
635     if (tag & 0x80000000)
636     {
637     pl->socket.look_position = tag & 0x7fffffff;
638     pl->socket.update_look = 1;
639     return;
640     }
641    
642     if (!op)
643     {
644     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
645     return;
646 elmex 1.1 }
647 root 1.5 player_apply (pl->ob, op, 0, 0);
648 elmex 1.1 }
649    
650     /** Client wants to apply some object. Lets do so. */
651 root 1.5 void
652     LockItem (uint8 * data, int len, player *pl)
653 elmex 1.1 {
654 root 1.5 int flag, tag;
655     object *op;
656 elmex 1.1
657 root 1.5 flag = data[0];
658     tag = GetInt_String (data + 1);
659     op = esrv_get_ob_from_count (pl->ob, tag);
660    
661     if (!op)
662     {
663     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to lock/unlock");
664     return;
665     }
666     if (!flag)
667     CLEAR_FLAG (op, FLAG_INV_LOCKED);
668     else
669     SET_FLAG (op, FLAG_INV_LOCKED);
670     esrv_update_item (UPD_FLAGS, pl->ob, op);
671 elmex 1.1 }
672    
673     /** Client wants to apply some object. Lets do so. */
674 root 1.5 void
675     MarkItem (uint8 * data, int len, player *pl)
676 elmex 1.1 {
677 root 1.5 int tag;
678     object *op;
679 elmex 1.1
680 root 1.5 tag = GetInt_String (data);
681     op = esrv_get_ob_from_count (pl->ob, tag);
682     if (!op)
683     {
684     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to mark");
685     return;
686     }
687     pl->mark = op;
688     pl->mark_count = op->count;
689     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name (op));
690 elmex 1.1 }
691    
692    
693     /**
694     * look_at prints items on the specified square.
695     *
696     * [ removed EARTHWALL check and added check for containers inventory.
697     * Tero.Haatanen@lut.fi ]
698     */
699 root 1.5 void
700     look_at (object *op, int dx, int dy)
701     {
702     object *tmp;
703     int flag = 0;
704     sint16 x, y;
705     mapstruct *m;
706    
707     x = op->x + dx;
708     y = op->y + dy;
709    
710     if (out_of_map (op->map, x, y))
711     return;
712    
713     m = get_map_from_coord (op->map, &x, &y);
714     if (!m)
715     return;
716    
717     for (tmp = get_map_ob (m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above);
718    
719     for (; tmp != NULL; tmp = tmp->below)
720     {
721     if (tmp->invisible && !QUERY_FLAG (op, FLAG_WIZ))
722     continue;
723    
724     if (!flag)
725     {
726     if (dx || dy)
727     new_draw_info (NDI_UNIQUE, 0, op, "There you see:");
728     else
729     {
730     clear_win_info (op);
731     new_draw_info (NDI_UNIQUE, 0, op, "You see:");
732 root 1.3 }
733 root 1.5 flag = 1;
734     }
735 root 1.3
736 root 1.5 if (QUERY_FLAG (op, FLAG_WIZ))
737     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s (%d).", query_name (tmp), tmp->count);
738     else
739     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s.", query_name (tmp));
740    
741     if (((tmp->inv != NULL || (tmp->head && tmp->head->inv)) &&
742     (tmp->type != CONTAINER && tmp->type != FLESH)) || QUERY_FLAG (op, FLAG_WIZ))
743     inventory (op, tmp->head == NULL ? tmp : tmp->head);
744    
745     if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (op, FLAG_WIZ)) /* don't continue under the floor */
746     break;
747     }
748    
749     if (!flag)
750     {
751     if (dx || dy)
752     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing there.");
753     else
754     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing.");
755 elmex 1.1 }
756     }
757    
758    
759    
760     /** Client wants to look at some object. Lets do so. */
761 root 1.5 void
762     LookAt (char *buf, int len, player *pl)
763 elmex 1.1 {
764 root 1.5 int dx, dy;
765     char *cp;
766 elmex 1.1
767 root 1.5 dx = atoi (buf);
768     if (!(cp = strchr (buf, ' ')))
769     {
770     return;
771 elmex 1.1 }
772 root 1.5 dy = atoi (cp);
773 elmex 1.1
774 root 1.5 if (FABS (dx) > pl->socket.mapx / 2 || FABS (dy) > pl->socket.mapy / 2)
775     return;
776 elmex 1.1
777 root 1.5 if (pl->blocked_los[dx + pl->socket.mapx / 2][dy + pl->socket.mapy / 2])
778     return;
779 elmex 1.1
780 root 1.5 look_at (pl->ob, dx, dy);
781 elmex 1.1 }
782    
783     /** Move an object to a new location */
784 root 1.5 void
785     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
786 elmex 1.1 {
787 root 1.5 object *op, *env;
788 elmex 1.1
789 root 1.5 op = esrv_get_ob_from_count (pl, tag);
790     if (!op)
791     {
792     LOG (llevDebug, "Player '%s' tried to move an unknown object (%ld)\n", &pl->name, tag);
793     return;
794 elmex 1.1 }
795    
796 root 1.5 if (!to)
797     { /* drop it to the ground */
798    
799 elmex 1.1 /* LOG(llevDebug, "Drop it on the ground.\n");*/
800    
801 root 1.5 if (op->map && !op->env)
802     {
803    
804 elmex 1.1 /* LOG(llevDebug,"Dropping object to ground that is already on ground\n");*/
805 root 1.5 return;
806 root 1.3 }
807 root 1.5 /* If it is an active container, then we should drop all objects
808     * in the container and not the container itself.
809     */
810     if (op->inv && QUERY_FLAG (op, FLAG_APPLIED))
811     {
812     object *current, *next;
813    
814     for (current = op->inv; current != NULL; current = next)
815     {
816     next = current->below;
817     drop_object (pl, current, 0);
818 root 1.3 }
819 root 1.5 esrv_update_item (UPD_WEIGHT, pl, op);
820 root 1.3 }
821 root 1.5 else
822     {
823     drop_object (pl, op, nrof);
824     }
825     return;
826     }
827     else if (to == pl->count)
828     { /* pick it up to the inventory */
829     /* return if player has already picked it up */
830     if (op->env == pl)
831 root 1.3 return;
832 root 1.5
833     pl->contr->count = nrof;
834     pick_up (pl, op);
835     return;
836     }
837     env = esrv_get_ob_from_count (pl, to);
838     if (!env)
839     {
840     LOG (llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", &pl->name, to);
841     return;
842     }
843     /* put_object_in_sack presumes that necessary sanity checking
844     * has already been done (eg, it can be picked up and fits in
845     * in a sack, so check for those things. We should also check
846     * an make sure env is in fact a container for that matter.
847     */
848     if (env->type == CONTAINER && can_pick (pl, op) && sack_can_hold (pl, env, op, nrof))
849     {
850     put_object_in_sack (pl, env, op, nrof);
851 elmex 1.1 }
852     }