ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.4
Committed: Sun Sep 3 00:18:43 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.3: +6 -10 lines
Log Message:
THIS CODE WILL NOT COMPILE
use the STABLE tag instead.

- major changes in object lifetime and memory management
- replaced manual refcounting by shstr class
- removed quest system
- many optimisations
- major changes

File Contents

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