ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.42
Committed: Wed Mar 14 00:04:59 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.41: +4 -8 lines
Log Message:
- rewrote smooth face handling, as a side-effect, smoothing seems to work
  again and smooth faces can be reloaded.
- the server now sends the full animation for an object the first time
  it is seen, this uses slightly more bandwidth initially, but avoids
  the flickering for objects change their face later.

File Contents

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