ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.50
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.49: +11 -12 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

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