ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.c
Revision: 1.2
Committed: Mon May 1 12:22:03 2006 UTC (18 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +121 -172 lines
Log Message:
Fix another crash.

Some clueless idiot decided to name the constant that defines the maximum
buffer size PLUS ONE to MAXSOCKBUF, and some naive people believed that
would actually be the max sock buf size. It's not.

File Contents

# User Rev Content
1 root 1.1
2     /*
3     * static char *rcsid_item_c =
4 root 1.2 * "$Id$";
5 root 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     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     }
116     if (op->type == CONTAINER && ((op->env && op->env->container == op) ||
117     (!op->env && QUERY_FLAG(op,FLAG_APPLIED))))
118     flags |= F_OPEN;
119    
120     if (QUERY_FLAG(op,FLAG_KNOWN_CURSED)) {
121     if(QUERY_FLAG(op,FLAG_DAMNED))
122     flags |= F_DAMNED;
123     else if(QUERY_FLAG(op,FLAG_CURSED))
124     flags |= F_CURSED;
125     }
126     if (QUERY_FLAG(op,FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(op,FLAG_IDENTIFIED))
127     flags |= F_MAGIC;
128     if (QUERY_FLAG(op,FLAG_UNPAID))
129     flags |= F_UNPAID;
130     if (QUERY_FLAG(op,FLAG_INV_LOCKED))
131     flags |= F_LOCKED;
132    
133     return flags;
134     }
135    
136 root 1.2 /* 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     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     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     } else {
167     strncpy(item_n,head->custom_name,127);
168     item_n[127]=0;
169     len=strlen(item_n);
170     item_p=head->custom_name;
171     }
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     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     }
190     SockList_AddChar(sl, (char) anim_speed);
191     SockList_AddInt(sl, head->nrof);
192    
193     if (ns->itemcmd == 2)
194     SockList_AddShort(sl, head->client_type);
195    
196     SET_FLAG(head, FLAG_CLIENT_SENT);
197     }
198    
199    
200 root 1.1 /**
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 root 1.2 int got_one=0,start_look=0, end_look=0;
210 root 1.1 SockList sl;
211 root 1.2 char buf[MAX_BUF];
212 root 1.1
213     if (!pl->contr->socket.update_look) {
214     LOG(llevDebug,"esrv_draw_look called when update_look was not set\n");
215     return;
216     } else {
217     pl->contr->socket.update_look=0;
218     }
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     return;
223    
224 root 1.2 if (pl->contr->transport)
225     for (tmp=pl->contr->transport->inv; tmp && tmp->above;tmp=tmp->above) ;
226     else
227     for (tmp=get_map_ob(pl->map,pl->x,pl->y); tmp && tmp->above;tmp=tmp->above) ;
228 root 1.1
229     sl.buf=malloc(MAXSOCKBUF);
230    
231     Write_String_To_Socket(&pl->contr->socket, "delinv 0", strlen("delinv 0"));
232     sprintf((char*)sl.buf,"item%d ", pl->contr->socket.itemcmd);
233     sl.len=strlen((char*)sl.buf);
234    
235     SockList_AddInt(&sl, 0);
236    
237     if (!(pl->contr->socket.faces_sent[empty_face->number]&NS_FACESENT_FACE))
238     esrv_send_face(&pl->contr->socket, empty_face->number,0);
239    
240     if (pl->contr->socket.look_position) {
241     SockList_AddInt(&sl, 0x80000000 | (pl->contr->socket.look_position- NUM_LOOK_OBJECTS));
242     SockList_AddInt(&sl, 0);
243     SockList_AddInt(&sl, -1);
244     SockList_AddInt(&sl, empty_face->number);
245     sprintf(buf,"Click here to see %d previous items", NUM_LOOK_OBJECTS);
246     add_stringlen_to_sockbuf(buf, &sl);
247     SockList_AddShort(&sl,0);
248     SockList_AddChar(&sl, 0);
249     SockList_AddInt(&sl, 0);
250     if (pl->contr->socket.itemcmd == 2)
251     SockList_AddShort(&sl, 0);
252     }
253    
254 root 1.2 if (pl->contr->transport) {
255     add_object_to_socklist(&pl->contr->socket, &sl, pl->contr->transport);
256     got_one++;
257     }
258    
259 root 1.1 for (last=NULL; tmp!=last; tmp=tmp->below) {
260     object *head;
261    
262     if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !last) {
263     last = tmp->below; /* assumes double floor mode */
264     if (last && QUERY_FLAG(last, FLAG_IS_FLOOR))
265     last = last->below;
266     }
267     if (LOOK_OBJ(tmp)) {
268     if (++start_look < pl->contr->socket.look_position) continue;
269     end_look++;
270     if (end_look > NUM_LOOK_OBJECTS) {
271     /* What we basically do is make a 'fake' object - when the user applies it,
272     * we notice the special tag the object has, and act accordingly.
273     */
274     SockList_AddInt(&sl, 0x80000000 | (pl->contr->socket.look_position+ NUM_LOOK_OBJECTS));
275     SockList_AddInt(&sl, 0);
276     SockList_AddInt(&sl, -1);
277     SockList_AddInt(&sl, empty_face->number);
278     sprintf(buf,"Click here to see next group of items");
279     add_stringlen_to_sockbuf(buf, &sl);
280     SockList_AddShort(&sl,0);
281     SockList_AddChar(&sl, 0);
282     SockList_AddInt(&sl, 0);
283     if (pl->contr->socket.itemcmd == 2)
284     SockList_AddShort(&sl, 0);
285     break;
286     }
287     if (tmp->head) head = tmp->head;
288     else head = tmp;
289    
290 root 1.2 add_object_to_socklist(&pl->contr->socket, &sl, head);
291 root 1.1 got_one++;
292    
293 root 1.2 if (sl.len >= (MAXSOCKBUF-MAXITEMLEN)) {
294 root 1.1 Send_With_Handling(&pl->contr->socket, &sl);
295     sprintf((char*)sl.buf,"item%d ", pl->contr->socket.itemcmd);
296     sl.len=strlen((char*)sl.buf);
297     SockList_AddInt(&sl, 0);
298     got_one=0;
299     }
300     } /* If LOOK_OBJ() */
301     }
302     if (got_one)
303     Send_With_Handling(&pl->contr->socket, &sl);
304    
305     free(sl.buf);
306     }
307    
308     /**
309     * Sends whole inventory.
310     */
311     void esrv_send_inventory(object *pl, object *op)
312     {
313     object *tmp;
314 root 1.2 int got_one=0;
315 root 1.1 SockList sl;
316    
317     sl.buf=malloc(MAXSOCKBUF);
318    
319     sprintf((char*)sl.buf,"delinv %d", op->count);
320     sl.len=strlen((char*)sl.buf);
321     Send_With_Handling(&pl->contr->socket, &sl);
322    
323     sprintf((char*)sl.buf,"item%d ", pl->contr->socket.itemcmd);
324     sl.len=strlen((char*)sl.buf);
325    
326     SockList_AddInt(&sl, op->count);
327    
328     for (tmp=op->inv; tmp; tmp=tmp->below) {
329     object *head;
330    
331     if (tmp->head) head = tmp->head;
332     else head = tmp;
333    
334     if (LOOK_OBJ(head)) {
335 root 1.2 add_object_to_socklist(&pl->contr->socket, &sl, head);
336 root 1.1
337     got_one++;
338    
339     /* IT is possible for players to accumulate a huge amount of
340     * items (especially with some of the bags out there) to
341     * overflow the buffer. IF so, send multiple item commands.
342     */
343 root 1.2 if (sl.len >= (MAXSOCKBUF-MAXITEMLEN)) {
344 root 1.1 Send_With_Handling(&pl->contr->socket, &sl);
345     sprintf((char*)sl.buf,"item%d ", pl->contr->socket.itemcmd);
346     sl.len=strlen((char*)sl.buf);
347     SockList_AddInt(&sl, op->count);
348     got_one=0;
349     }
350     } /* If LOOK_OBJ() */
351     }
352     if (got_one)
353     Send_With_Handling(&pl->contr->socket, &sl);
354     free(sl.buf);
355     }
356    
357     /**
358     * Updates object *op for player *pl.
359     *
360     * flags is a list of values to update
361     * to the client (as defined in newclient.h - might as well use the
362     * same value both places.
363     */
364    
365     void esrv_update_item(int flags, object *pl, object *op)
366     {
367     SockList sl;
368    
369     /* If we have a request to send the player item, skip a few checks. */
370     if (op!=pl) {
371     if (! LOOK_OBJ(op))
372     return;
373     /* we remove the check for op->env, because in theory, the object
374     * is hopefully in the same place, so the client should preserve
375     * order.
376     */
377     }
378     if (!QUERY_FLAG(op, FLAG_CLIENT_SENT)) {
379     /* FLAG_CLIENT_SENT is debug only. We are using it to see where
380     * this is happening - we can set a breakpoint here in the debugger
381     * and track back the call.
382     */
383     LOG(llevDebug,"We have not sent item %s (%d)\n", op->name, op->count);
384     }
385     sl.buf=malloc(MAXSOCKBUF);
386    
387     strcpy((char*)sl.buf,"upditem ");
388     sl.len=strlen((char*)sl.buf);
389    
390     SockList_AddChar(&sl, (char) flags);
391    
392     if (op->head) op=op->head;
393    
394     SockList_AddInt(&sl, op->count);
395    
396     if (flags & UPD_LOCATION)
397     SockList_AddInt(&sl, op->env? op->env->count:0);
398    
399     if (flags & UPD_FLAGS)
400     SockList_AddInt(&sl, query_flags(op));
401    
402     if (flags & UPD_WEIGHT) {
403     sint32 weight = WEIGHT(op);
404 root 1.2
405     /* TRANSPORTS are odd - they sort of look like containers, yet can't be
406     * picked up. So we don't to send the weight, as it is odd that you see
407     * weight sometimes and not other (the draw_look won't send it
408     * for example.
409     */
410     SockList_AddInt(&sl, QUERY_FLAG(op, FLAG_NO_PICK) ? -1 : weight);
411 root 1.1 if (pl == op) {
412     op->contr->last_weight = weight;
413     }
414     }
415    
416     if (flags & UPD_FACE) {
417     if (!(pl->contr->socket.faces_sent[op->face->number] & NS_FACESENT_FACE))
418     esrv_send_face(&pl->contr->socket, op->face->number,0);
419     SockList_AddInt(&sl, op->face->number);
420     }
421     if (flags & UPD_NAME) {
422     int len;
423     const char *item_p;
424     char item_n[MAX_BUF];
425    
426     if (!op->custom_name) {
427     strncpy(item_n,query_base_name(op, 0),127);
428     item_n[127]=0;
429     len=strlen(item_n);
430     item_p=query_base_name(op, 1);
431     }
432     else {
433     strncpy(item_n,op->custom_name,127);
434     item_n[127]=0;
435     len=strlen(item_n);
436     item_p=op->custom_name;
437     }
438    
439     strncpy(item_n+len+1, item_p, 127);
440     item_n[254]=0;
441     len += strlen(item_n+1+len) + 1;
442     SockList_AddChar(&sl, (char)len);
443     memcpy(sl.buf+sl.len, item_n, len);
444     sl.len += len;
445     }
446     if (flags & UPD_ANIM)
447     SockList_AddShort(&sl,op->animation_id);
448    
449     if (flags & UPD_ANIMSPEED) {
450     int anim_speed=0;
451     if (QUERY_FLAG(op,FLAG_ANIMATE)) {
452     if (op->anim_speed) anim_speed=op->anim_speed;
453     else {
454     if (FABS(op->speed)<0.001) anim_speed=255;
455     else if (FABS(op->speed)>=1.0) anim_speed=1;
456     else anim_speed = (int) (1.0/FABS(op->speed));
457     }
458     if (anim_speed>255) anim_speed=255;
459     }
460     SockList_AddChar(&sl, (char)anim_speed);
461     }
462     if (flags & UPD_NROF)
463     SockList_AddInt(&sl, op->nrof);
464    
465     Send_With_Handling(&pl->contr->socket, &sl);
466     free(sl.buf);
467     }
468    
469     /**
470     * Sends item's info to player.
471     */
472     void esrv_send_item(object *pl, object*op)
473     {
474     SockList sl;
475    
476     /* If this is not the player object, do some more checks */
477     if (op!=pl) {
478     /* We only send 'visibile' objects to the client */
479     if (! LOOK_OBJ(op))
480     return;
481     /* if the item is on the ground, mark that the look needs to
482     * be updated.
483     */
484     if (!op->env) {
485     pl->contr->socket.update_look=1;
486     return;
487     }
488     }
489    
490     sl.buf=malloc(MAXSOCKBUF);
491    
492     sprintf((char*)sl.buf,"item%d ", pl->contr->socket.itemcmd);
493     sl.len=strlen((char*)sl.buf);
494    
495     if (op->head) op=op->head;
496    
497     SockList_AddInt(&sl, op->env? op->env->count:0);
498    
499 root 1.2 add_object_to_socklist(&pl->contr->socket, &sl, op);
500 root 1.1
501     Send_With_Handling(&pl->contr->socket, &sl);
502     SET_FLAG(op, FLAG_CLIENT_SENT);
503     free(sl.buf);
504     }
505    
506     /**
507     * Tells the client to delete an item. Uses the item
508     * command with a -1 location.
509     */
510    
511     void esrv_del_item(player *pl, int tag)
512     {
513     SockList sl;
514    
515     sl.buf=malloc(MAXSOCKBUF);
516    
517     strcpy((char*)sl.buf,"delitem ");
518     sl.len=strlen((char*)sl.buf);
519     SockList_AddInt(&sl, tag);
520    
521     Send_With_Handling(&pl->socket, &sl);
522     free(sl.buf);
523     }
524    
525    
526     /*******************************************************************************
527     *
528     * Client has requested us to do something with an object.
529     *
530     ******************************************************************************/
531    
532     /**
533     * Takes a player and object count (tag) and returns the actual object
534     * pointer, or null if it can't be found.
535     */
536    
537     object *esrv_get_ob_from_count(object *pl, tag_t count)
538     {
539     object *op, *tmp;
540    
541     if (pl->count == count)
542     return pl;
543    
544     for(op = pl->inv; op; op = op->below)
545     if (op->count == count)
546     return op;
547     else if (op->type == CONTAINER && pl->container == op)
548     for(tmp = op->inv; tmp; tmp = tmp->below)
549     if (tmp->count == count)
550     return tmp;
551    
552     for(op = get_map_ob (pl->map, pl->x, pl->y); op; op = op->above)
553     if (op->head != NULL && op->head->count == count)
554     return op;
555     else if (op->count == count)
556     return op;
557     else if (op->type == CONTAINER && pl->container == op)
558     for(tmp = op->inv; tmp; tmp = tmp->below)
559     if (tmp->count == count)
560     return tmp;
561 root 1.2
562     if (pl->contr->transport) {
563     for(tmp = pl->contr->transport->inv; tmp; tmp = tmp->below)
564     if (tmp->count == count)
565     return tmp;
566     }
567 root 1.1 return NULL;
568     }
569    
570    
571     /** Client wants to examine some object. So lets do so. */
572     void ExamineCmd(char *buf, int len,player *pl)
573     {
574     long tag = atoi(buf);
575     object *op = esrv_get_ob_from_count(pl->ob, tag);
576    
577     if (!op) {
578     LOG(llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n",
579     pl->ob->name, tag);
580     return;
581     }
582     examine (pl->ob, op);
583     }
584    
585     /** Client wants to apply some object. Lets do so. */
586     void ApplyCmd(char *buf, int len,player *pl)
587     {
588     uint32 tag = atoi(buf);
589     object *op = esrv_get_ob_from_count(pl->ob, tag);
590    
591     /* sort of a hack, but if the player saves and the player then manually
592     * applies a savebed (or otherwise tries to do stuff), we run into trouble.
593     */
594     if (QUERY_FLAG(pl->ob, FLAG_REMOVED)) return;
595    
596     /* If the high bit is set, player applied a pseudo object. */
597     if (tag & 0x80000000) {
598     pl->socket.look_position = tag & 0x7fffffff;
599     pl->socket.update_look = 1;
600     return;
601     }
602    
603     if (!op) {
604     LOG(llevDebug, "Player '%s' tried to apply the unknown object (%d)\n",
605     pl->ob->name, tag);
606     return;
607     }
608     player_apply (pl->ob, op, 0, 0);
609     }
610    
611     /** Client wants to apply some object. Lets do so. */
612     void LockItem(uint8 *data, int len,player *pl)
613     {
614     int flag, tag;
615     object *op;
616    
617     flag = data[0];
618     tag = GetInt_String(data+1);
619     op = esrv_get_ob_from_count(pl->ob, tag);
620    
621     if (!op) {
622     new_draw_info(NDI_UNIQUE, 0, pl->ob,"Could not find object to lock/unlock");
623     return;
624     }
625     if (!flag)
626     CLEAR_FLAG(op,FLAG_INV_LOCKED);
627     else
628     SET_FLAG(op,FLAG_INV_LOCKED);
629     esrv_update_item(UPD_FLAGS, pl->ob, op);
630     }
631    
632     /** Client wants to apply some object. Lets do so. */
633     void MarkItem(uint8 *data, int len,player *pl)
634     {
635     int tag;
636     object *op;
637    
638     tag = GetInt_String(data);
639     op = esrv_get_ob_from_count(pl->ob, tag);
640     if (!op) {
641     new_draw_info(NDI_UNIQUE, 0, pl->ob,"Could not find object to mark");
642     return;
643     }
644     pl->mark = op;
645     pl->mark_count = op->count;
646     new_draw_info_format(NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name(op));
647     }
648    
649    
650     /**
651     * look_at prints items on the specified square.
652     *
653     * [ removed EARTHWALL check and added check for containers inventory.
654     * Tero.Haatanen@lut.fi ]
655     */
656     void look_at(object *op,int dx,int dy) {
657     object *tmp;
658     int flag=0;
659     sint16 x,y;
660     mapstruct *m;
661    
662    
663     if (out_of_map(op->map, op->x+dx, op->y+dy)) return;
664    
665     x = op->x + dx;
666     y = op->y + dy;
667    
668     m = get_map_from_coord(op->map, &x, &y);
669     if (!m) return;
670    
671     for(tmp=get_map_ob(m, x ,y);tmp!=NULL&&tmp->above!=NULL;
672     tmp=tmp->above);
673    
674     for ( ; tmp != NULL; tmp=tmp->below ) {
675     if (tmp->invisible && !QUERY_FLAG(op, FLAG_WIZ)) continue;
676    
677     if(!flag) {
678     if(dx||dy)
679     new_draw_info(NDI_UNIQUE, 0,op,"There you see:");
680     else {
681     clear_win_info(op);
682     new_draw_info(NDI_UNIQUE, 0,op,"You see:");
683     }
684     flag=1;
685     }
686    
687     if (QUERY_FLAG(op, FLAG_WIZ))
688     new_draw_info_format(NDI_UNIQUE,0, op, "- %s (%d).",query_name(tmp),tmp->count);
689     else
690     new_draw_info_format(NDI_UNIQUE,0, op, "- %s.",query_name(tmp));
691    
692     if (((tmp->inv!=NULL || (tmp->head && tmp->head->inv)) &&
693     (tmp->type != CONTAINER && tmp->type!=FLESH)) || QUERY_FLAG(op, FLAG_WIZ))
694     inventory(op,tmp->head==NULL?tmp:tmp->head);
695    
696     if(QUERY_FLAG(tmp, FLAG_IS_FLOOR)&&!QUERY_FLAG(op, FLAG_WIZ)) /* don't continue under the floor */
697     break;
698     }
699    
700     if(!flag) {
701     if(dx||dy)
702     new_draw_info(NDI_UNIQUE, 0,op,"You see nothing there.");
703     else
704     new_draw_info(NDI_UNIQUE, 0,op,"You see nothing.");
705     }
706     }
707    
708    
709    
710     /** Client wants to look at some object. Lets do so. */
711     void LookAt(char *buf, int len,player *pl)
712     {
713     int dx, dy;
714     char *cp;
715    
716     dx=atoi(buf);
717     if (!(cp=strchr(buf,' '))) {
718     return;
719     }
720     dy=atoi(cp);
721    
722     if (FABS(dx)>MAP_CLIENT_X/2 || FABS(dy)>MAP_CLIENT_Y/2)
723     return;
724    
725     if(pl->blocked_los[dx+(pl->socket.mapx/2)][dy+(pl->socket.mapy/2)])
726     return;
727     look_at(pl->ob, dx, dy);
728     }
729    
730     /** Move an object to a new location */
731     void esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
732     {
733     object *op, *env;
734    
735     op = esrv_get_ob_from_count(pl, tag);
736     if (!op) {
737 root 1.2 LOG(llevDebug, "Player '%s' tried to move an unknown object (%ld)\n",
738 root 1.1 pl->name, tag);
739     return;
740     }
741    
742 root 1.2 /* If on a transport, you don't drop to the ground - you drop to the
743     * transport.
744     */
745     if (!to && !pl->contr->transport) { /* drop it to the ground */
746 root 1.1 /* LOG(llevDebug, "Drop it on the ground.\n");*/
747    
748     if (op->map && !op->env) {
749     /* LOG(llevDebug,"Dropping object to ground that is already on ground\n");*/
750     return;
751     }
752     /* If it is an active container, then we should drop all objects
753     * in the container and not the container itself.
754     */
755     if (op->inv && QUERY_FLAG(op, FLAG_APPLIED)) {
756     object *current, *next;
757     for (current=op->inv; current!=NULL; current=next) {
758     next=current->below;
759     drop_object(pl, current, 0);
760     }
761     esrv_update_item(UPD_WEIGHT, pl, op);
762     }
763     else {
764     drop_object (pl, op, nrof);
765     }
766     return;
767     } else if (to == pl->count) { /* pick it up to the inventory */
768     /* return if player has already picked it up */
769     if (op->env == pl) return;
770    
771     pl->contr->count = nrof;
772     pick_up(pl, op);
773     return ;
774     }
775     /* If not dropped or picked up, we are putting it into a sack */
776 root 1.2 if (pl->contr->transport) {
777     if (can_pick(pl, op) && transport_can_hold(pl->contr->transport, op, nrof)) {
778     put_object_in_sack (pl, pl->contr->transport, op, nrof);
779     }
780     } else {
781     env = esrv_get_ob_from_count(pl, to);
782     if (!env) {
783     LOG(llevDebug,
784     "Player '%s' tried to move object to the unknown location (%d)\n",
785     pl->name, to);
786     return;
787     }
788     /* put_object_in_sack presumes that necessary sanity checking
789     * has already been done (eg, it can be picked up and fits in
790     * in a sack, so check for those things. We should also check
791     * an make sure env is in fact a container for that matter.
792     */
793     if (env->type == CONTAINER
794     && can_pick(pl, op) && sack_can_hold(pl, env, op, nrof)) {
795     put_object_in_sack (pl, env, op, nrof);
796     }
797 root 1.1 }
798     }
799    
800