ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.2
Committed: Tue Aug 29 07:34:01 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +18 -46 lines
Log Message:
nuke transports

File Contents

# User Rev Content
1 elmex 1.1
2     /*
3     * static char *rcsid_item_c =
4 root 1.2 * "$Id: item.C,v 1.1 2006-08-13 17:16:06 elmex 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     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     /* 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     /**
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     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 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     esrv_send_face(&pl->contr->socket, empty_face->number,0);
236    
237     if (pl->contr->socket.look_position) {
238     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     }
250    
251     for (last=NULL; tmp!=last; tmp=tmp->below) {
252     object *head;
253    
254     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     }
294     if (got_one)
295     Send_With_Handling(&pl->contr->socket, &sl);
296    
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     object *head;
322    
323     if (tmp->head) head = tmp->head;
324     else head = tmp;
325    
326     if (LOOK_OBJ(head)) {
327     add_object_to_socklist(&pl->contr->socket, &sl, head);
328    
329     got_one++;
330    
331     /* 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     }
344     if (got_one)
345     Send_With_Handling(&pl->contr->socket, &sl);
346     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     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     }
370     if (!QUERY_FLAG(op, FLAG_CLIENT_SENT)) {
371     /* 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     LOG(llevDebug,"We have not sent item %s (%d)\n", op->name, op->count);
376     }
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     SockList_AddInt(&sl, op->env? op->env->count:0);
390    
391     if (flags & UPD_FLAGS)
392     SockList_AddInt(&sl, query_flags(op));
393    
394     if (flags & UPD_WEIGHT) {
395     sint32 weight = WEIGHT(op);
396    
397     SockList_AddInt(&sl, QUERY_FLAG(op, FLAG_NO_PICK) ? -1 : weight);
398     if (pl == op) {
399     op->contr->last_weight = weight;
400     }
401     }
402    
403     if (flags & UPD_FACE) {
404     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     }
408     if (flags & UPD_NAME) {
409     int len;
410     const char *item_p;
411     char item_n[MAX_BUF];
412    
413     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     }
433     if (flags & UPD_ANIM)
434     SockList_AddShort(&sl,op->animation_id);
435    
436     if (flags & UPD_ANIMSPEED) {
437     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     }
449     if (flags & UPD_NROF)
450     SockList_AddInt(&sl, op->nrof);
451    
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     /* 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     }
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     return pl;
530    
531     for(op = pl->inv; op; op = op->below)
532     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    
539     for(op = get_map_ob (pl->map, pl->x, pl->y); op; op = op->above)
540     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    
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     LOG(llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n",
561     pl->ob->name, tag);
562     return;
563     }
564     examine (pl->ob, op);
565     }
566    
567     /** Client wants to apply some object. Lets do so. */
568     void ApplyCmd(char *buf, int len,player *pl)
569     {
570     uint32 tag = atoi(buf);
571     object *op = esrv_get_ob_from_count(pl->ob, tag);
572    
573     /* sort of a hack, but if the player saves and the player then manually
574     * applies a savebed (or otherwise tries to do stuff), we run into trouble.
575     */
576     if (QUERY_FLAG(pl->ob, FLAG_REMOVED)) return;
577    
578     /* If the high bit is set, player applied a pseudo object. */
579     if (tag & 0x80000000) {
580     pl->socket.look_position = tag & 0x7fffffff;
581     pl->socket.update_look = 1;
582     return;
583     }
584    
585     if (!op) {
586     LOG(llevDebug, "Player '%s' tried to apply the unknown object (%d)\n",
587     pl->ob->name, tag);
588     return;
589     }
590     player_apply (pl->ob, op, 0, 0);
591     }
592    
593     /** Client wants to apply some object. Lets do so. */
594     void LockItem(uint8 *data, int len,player *pl)
595     {
596     int flag, tag;
597     object *op;
598    
599     flag = data[0];
600     tag = GetInt_String(data+1);
601     op = esrv_get_ob_from_count(pl->ob, tag);
602    
603     if (!op) {
604     new_draw_info(NDI_UNIQUE, 0, pl->ob,"Could not find object to lock/unlock");
605     return;
606     }
607     if (!flag)
608     CLEAR_FLAG(op,FLAG_INV_LOCKED);
609     else
610     SET_FLAG(op,FLAG_INV_LOCKED);
611     esrv_update_item(UPD_FLAGS, pl->ob, op);
612     }
613    
614     /** Client wants to apply some object. Lets do so. */
615     void MarkItem(uint8 *data, int len,player *pl)
616     {
617     int tag;
618     object *op;
619    
620     tag = GetInt_String(data);
621     op = esrv_get_ob_from_count(pl->ob, tag);
622     if (!op) {
623     new_draw_info(NDI_UNIQUE, 0, pl->ob,"Could not find object to mark");
624     return;
625     }
626     pl->mark = op;
627     pl->mark_count = op->count;
628     new_draw_info_format(NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name(op));
629     }
630    
631    
632     /**
633     * look_at prints items on the specified square.
634     *
635     * [ removed EARTHWALL check and added check for containers inventory.
636     * Tero.Haatanen@lut.fi ]
637     */
638     void look_at(object *op,int dx,int dy) {
639     object *tmp;
640     int flag=0;
641     sint16 x,y;
642     mapstruct *m;
643    
644     x = op->x + dx;
645     y = op->y + dy;
646    
647     if (out_of_map(op->map, x, y)) return;
648    
649     m = get_map_from_coord(op->map, &x, &y);
650     if (!m) return;
651    
652     for(tmp=get_map_ob(m, x ,y);tmp!=NULL&&tmp->above!=NULL;
653     tmp=tmp->above);
654    
655     for ( ; tmp != NULL; tmp=tmp->below ) {
656     if (tmp->invisible && !QUERY_FLAG(op, FLAG_WIZ)) continue;
657    
658     if(!flag) {
659     if(dx||dy)
660     new_draw_info(NDI_UNIQUE, 0,op,"There you see:");
661     else {
662     clear_win_info(op);
663     new_draw_info(NDI_UNIQUE, 0,op,"You see:");
664     }
665     flag=1;
666     }
667    
668     if (QUERY_FLAG(op, FLAG_WIZ))
669     new_draw_info_format(NDI_UNIQUE,0, op, "- %s (%d).",query_name(tmp),tmp->count);
670     else
671     new_draw_info_format(NDI_UNIQUE,0, op, "- %s.",query_name(tmp));
672    
673     if (((tmp->inv!=NULL || (tmp->head && tmp->head->inv)) &&
674     (tmp->type != CONTAINER && tmp->type!=FLESH)) || QUERY_FLAG(op, FLAG_WIZ))
675     inventory(op,tmp->head==NULL?tmp:tmp->head);
676    
677     if(QUERY_FLAG(tmp, FLAG_IS_FLOOR)&&!QUERY_FLAG(op, FLAG_WIZ)) /* don't continue under the floor */
678     break;
679     }
680    
681     if(!flag) {
682     if(dx||dy)
683     new_draw_info(NDI_UNIQUE, 0,op,"You see nothing there.");
684     else
685     new_draw_info(NDI_UNIQUE, 0,op,"You see nothing.");
686     }
687     }
688    
689    
690    
691     /** Client wants to look at some object. Lets do so. */
692     void LookAt(char *buf, int len,player *pl)
693     {
694     int dx, dy;
695     char *cp;
696    
697     dx=atoi(buf);
698     if (!(cp=strchr(buf,' '))) {
699     return;
700     }
701     dy=atoi(cp);
702    
703     if (FABS(dx) > pl->socket.mapx / 2 || FABS(dy) > pl->socket.mapy / 2)
704     return;
705    
706     if(pl->blocked_los[dx + pl->socket.mapx / 2][dy + pl->socket.mapy / 2])
707     return;
708    
709     look_at(pl->ob, dx, dy);
710     }
711    
712     /** Move an object to a new location */
713     void esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
714     {
715     object *op, *env;
716    
717     op = esrv_get_ob_from_count(pl, tag);
718     if (!op) {
719     LOG(llevDebug, "Player '%s' tried to move an unknown object (%ld)\n",
720     pl->name, tag);
721     return;
722     }
723    
724 root 1.2 if (!to) { /* drop it to the ground */
725 elmex 1.1 /* LOG(llevDebug, "Drop it on the ground.\n");*/
726    
727     if (op->map && !op->env) {
728     /* LOG(llevDebug,"Dropping object to ground that is already on ground\n");*/
729     return;
730     }
731     /* If it is an active container, then we should drop all objects
732     * in the container and not the container itself.
733     */
734     if (op->inv && QUERY_FLAG(op, FLAG_APPLIED)) {
735     object *current, *next;
736     for (current=op->inv; current!=NULL; current=next) {
737     next=current->below;
738     drop_object(pl, current, 0);
739     }
740     esrv_update_item(UPD_WEIGHT, pl, op);
741     }
742     else {
743     drop_object (pl, op, nrof);
744     }
745     return;
746     } else if (to == pl->count) { /* pick it up to the inventory */
747     /* return if player has already picked it up */
748     if (op->env == pl) return;
749    
750     pl->contr->count = nrof;
751     pick_up(pl, op);
752     return ;
753     }
754 root 1.2 env = esrv_get_ob_from_count(pl, to);
755     if (!env) {
756     LOG(llevDebug,
757     "Player '%s' tried to move object to the unknown location (%d)\n",
758     pl->name, to);
759     return;
760     }
761     /* put_object_in_sack presumes that necessary sanity checking
762     * has already been done (eg, it can be picked up and fits in
763     * in a sack, so check for those things. We should also check
764     * an make sure env is in fact a container for that matter.
765     */
766     if (env->type == CONTAINER
767     && can_pick(pl, op) && sack_can_hold(pl, env, op, nrof)) {
768     put_object_in_sack (pl, env, op, nrof);
769 elmex 1.1 }
770     }
771    
772