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

# User Rev Content
1 elmex 1.1 /*
2 root 1.50 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 root 1.45 *
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 root 1.50 * 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 root 1.45 *
13 root 1.50 * 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 root 1.45 *
18 root 1.50 * 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 root 1.45 *
21     * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 root 1.32 */
23 elmex 1.1
24     /**
25     * \file
26     * Client/server logic.
27     *
28     * \date 2003-12-02
29     *
30 root 1.32 * This contains item logic for client/server. It doesn't contain
31 elmex 1.1 * 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 root 1.32 #include <object.h>
37 elmex 1.1 #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 root 1.28 #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 elmex 1.1 /*******************************************************************************
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 root 1.24 * to be sent to client.
191 elmex 1.1 */
192 root 1.5 unsigned int
193     query_flags (object *op)
194 elmex 1.1 {
195 root 1.5 unsigned int flags = 0;
196 elmex 1.1
197 root 1.5 if (QUERY_FLAG (op, FLAG_APPLIED))
198     {
199     switch (op->type)
200     {
201 root 1.24 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 root 1.5 }
230     }
231 root 1.24
232 root 1.5 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 root 1.24
243 root 1.5 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 elmex 1.1
250 root 1.5 return flags;
251 elmex 1.1 }
252    
253 root 1.16 /* Used in the send_look to put object head into packet
254 elmex 1.1 * sl for socket ns. Need socket to know if we need to send
255     * animation of face to the client.
256     */
257 root 1.5 static void
258 root 1.23 add_object_to_socklist (client &ns, packet &sl, object *head)
259 elmex 1.1 {
260 root 1.5 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 root 1.42 ns.send_face (head->face);
269 root 1.43 ns.flush_fx ();
270 root 1.5
271 root 1.13 if (QUERY_FLAG (head, FLAG_ANIMATE) && !ns.anims_sent[head->animation_id])
272 root 1.42 ns.send_animation (head->animation_id);
273 root 1.5
274 root 1.13 sl << uint32 (head->count)
275     << uint32 (flags)
276     << uint32 (QUERY_FLAG (head, FLAG_NO_PICK) ? -1 : WEIGHT (head))
277 root 1.41 << uint32 (head->face);
278 root 1.5
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 root 1.13
294 root 1.5 strncpy (item_n + len + 1, item_p, 127);
295     item_n[254] = 0;
296     len += strlen (item_n + 1 + len) + 1;
297    
298 root 1.13 sl << data8 (item_n, len)
299     << uint16 (head->animation_id);
300    
301 root 1.5 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 root 1.35 if (fabs (head->speed) < 0.001)
309 root 1.5 anim_speed = 255;
310 root 1.35 else if (fabs (head->speed) >= 1.0)
311 root 1.5 anim_speed = 1;
312     else
313 root 1.35 anim_speed = (int) (1.0 / fabs (head->speed));
314 root 1.5 }
315 root 1.13
316 root 1.5 if (anim_speed > 255)
317     anim_speed = 255;
318 elmex 1.1 }
319    
320 root 1.13 sl << uint8 (anim_speed)
321     << uint32 (head->nrof);
322    
323     if (ns.itemcmd == 2)
324     sl << uint16 (head->client_type);
325 elmex 1.1
326 root 1.5 SET_FLAG (head, FLAG_CLIENT_SENT);
327 elmex 1.1 }
328    
329     /**
330 root 1.24 * 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 elmex 1.1 */
334 root 1.5 void
335 root 1.46 esrv_draw_look (player *pl)
336 elmex 1.1 {
337 root 1.5 int got_one = 0, start_look = 0, end_look = 0;
338    
339 root 1.46 object *ob = pl->ob;
340    
341     if (!pl->ns->update_look)
342 root 1.5 {
343 root 1.46 LOG (llevDebug, "esrv_draw_look called when update_look was not set (player %s)\n", &ob->name);
344 root 1.5 return;
345     }
346     else
347 root 1.46 pl->ns->update_look = 0;
348 root 1.5
349 root 1.46 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 root 1.5 return;
354    
355 root 1.46 pl->ns->send_packet ("delinv 0");
356 root 1.5
357 root 1.27 packet sl;
358 root 1.46 sl.printf ("item%d ", pl->ns->itemcmd);
359 root 1.5
360 root 1.13 sl << uint32 (0);
361 root 1.5
362 root 1.46 pl->ns->send_face (empty_face);
363     pl->ns->flush_fx ();
364 root 1.5
365 root 1.46 if (pl->ns->look_position)
366 root 1.5 {
367 root 1.21 char buf[80];
368 root 1.26 snprintf (buf, 80, "Apply this to see %d previous items", FLOORBOX_PAGESIZE);
369 root 1.21
370 root 1.46 sl << uint32 (0x80000000 | (pl->ns->look_position - FLOORBOX_PAGESIZE))
371 root 1.13 << uint32 (0)
372     << sint32 (-1)
373 root 1.41 << uint32 (empty_face)
374 root 1.21 << data8 (buf)
375     << uint16 (0)
376 root 1.13 << uint8 (0)
377     << uint32 (0);
378    
379 root 1.46 if (pl->ns->itemcmd == 2)
380 root 1.13 sl << uint16 (0);
381 root 1.5 }
382    
383 root 1.46 object *tmp = ob->ms ().top;
384 root 1.32 for (object *last = 0; tmp != last; tmp = tmp->below)
385 root 1.5 {
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 root 1.13
395 root 1.32 if (tmp->client_visible ())
396 root 1.5 {
397 root 1.46 if (++start_look < pl->ns->look_position)
398 root 1.5 continue;
399 root 1.13
400 root 1.5 end_look++;
401 root 1.13
402 root 1.26 if (end_look > FLOORBOX_PAGESIZE)
403 root 1.5 {
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 root 1.46 sl << uint32 (0x80000000 | (pl->ns->look_position + FLOORBOX_PAGESIZE))
408 root 1.14 << uint32 (0)
409     << uint32 ((uint32) - 1)
410 root 1.41 << uint32 (empty_face)
411 root 1.21 << data8 ("Apply this to see next group of items")
412     << uint16 (0)
413 root 1.14 << uint8 (0)
414     << uint32 (0);
415    
416 root 1.46 if (pl->ns->itemcmd == 2)
417 root 1.14 sl << uint16 (0);
418    
419 root 1.5 break;
420 root 1.3 }
421 root 1.14
422 root 1.5 if (tmp->head)
423     head = tmp->head;
424     else
425     head = tmp;
426    
427 root 1.46 add_object_to_socklist (*pl->ns, sl, head);
428 root 1.5 got_one++;
429    
430 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
431 root 1.5 {
432 root 1.46 pl->ns->send_packet (sl);
433 root 1.14
434     sl.reset ();
435 root 1.46 sl.printf ("item%d ", pl->ns->itemcmd);
436 root 1.14 sl << uint32 (0);
437 root 1.5 got_one = 0;
438 root 1.3 }
439 root 1.24 }
440 elmex 1.1 }
441 root 1.13
442 root 1.5 if (got_one)
443 root 1.46 pl->ns->send_packet (sl);
444 elmex 1.1
445     }
446    
447     /**
448     * Sends whole inventory.
449     */
450 root 1.5 void
451     esrv_send_inventory (object *pl, object *op)
452 elmex 1.1 {
453 root 1.33 if (!pl->contr->ns)//D
454     return;
455    
456 root 1.5 int got_one = 0;
457    
458 root 1.29 pl->contr->ns->send_packet_printf ("delinv %d", op->count);
459 root 1.5
460 root 1.27 packet sl;
461 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
462 root 1.5
463 root 1.14 sl << uint32 (op->count);
464 root 1.5
465 root 1.32 for (object *tmp = op->inv; tmp; tmp = tmp->below)
466 root 1.5 {
467     object *head;
468    
469     if (tmp->head)
470     head = tmp->head;
471     else
472     head = tmp;
473    
474 root 1.32 if (head->client_visible ())
475 root 1.5 {
476 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
477 root 1.5
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 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
485 root 1.5 {
486 root 1.29 pl->contr->ns->send_packet (sl);
487 root 1.17
488     sl.reset ();
489 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
490 root 1.14 sl << uint32 (op->count);
491 root 1.5 got_one = 0;
492 root 1.3 }
493 root 1.32 }
494 elmex 1.1 }
495 root 1.14
496 root 1.5 if (got_one)
497 root 1.29 pl->contr->ns->send_packet (sl);
498 elmex 1.1 }
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 root 1.5 void
508     esrv_update_item (int flags, object *pl, object *op)
509 elmex 1.1 {
510 root 1.5 /* If we have a request to send the player item, skip a few checks. */
511     if (op != pl)
512     {
513 root 1.32 if (!op->client_visible ())
514 root 1.5 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 root 1.11
521 root 1.31 client *ns = pl->contr->ns;
522     if (!ns)
523     return;
524    
525 root 1.5 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 root 1.11
534 root 1.27 packet sl ("upditem");
535 root 1.5
536 root 1.27 sl << uint8 (flags);
537 root 1.5
538     if (op->head)
539     op = op->head;
540    
541 root 1.14 sl << uint32 (op->count);
542 root 1.5
543     if (flags & UPD_LOCATION)
544 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
545 root 1.5
546     if (flags & UPD_FLAGS)
547 root 1.14 sl << uint32 (query_flags (op));
548 root 1.5
549     if (flags & UPD_WEIGHT)
550     {
551     sint32 weight = WEIGHT (op);
552    
553 root 1.14 sl << uint32 (QUERY_FLAG (op, FLAG_NO_PICK) ? -1 : weight);
554    
555 root 1.5 if (pl == op)
556 root 1.31 ns->last_weight = weight;
557 root 1.5 }
558    
559     if (flags & UPD_FACE)
560     {
561 root 1.42 ns->send_face (op->face);
562 root 1.43 ns->flush_fx ();
563 root 1.41 sl << uint32 (op->face);
564 root 1.5 }
565 root 1.11
566 root 1.5 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 root 1.14
591     sl << data8 (item_n, len);
592 root 1.5 }
593 root 1.11
594 root 1.5 if (flags & UPD_ANIM)
595 root 1.14 sl << uint16 (op->animation_id);
596 root 1.5
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 root 1.35 if (fabs (op->speed) < 0.001)
608 root 1.5 anim_speed = 255;
609 root 1.35 else if (fabs (op->speed) >= 1.0)
610 root 1.5 anim_speed = 1;
611     else
612 root 1.35 anim_speed = (int) (1.0 / fabs (op->speed));
613 root 1.3 }
614 root 1.14
615 root 1.5 if (anim_speed > 255)
616     anim_speed = 255;
617 root 1.3 }
618 root 1.14
619     sl << uint8 (anim_speed);
620 elmex 1.1 }
621 root 1.14
622 root 1.5 if (flags & UPD_NROF)
623 root 1.14 sl << uint32 (op->nrof);
624 elmex 1.1
625 root 1.29 pl->contr->ns->send_packet (sl);
626 elmex 1.1 }
627    
628     /**
629     * Sends item's info to player.
630     */
631 root 1.5 void
632     esrv_send_item (object *pl, object *op)
633 elmex 1.1 {
634 root 1.34 if (!pl->contr->ns)
635     return;
636    
637 root 1.5 /* 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 root 1.32 if (!op->client_visible ())
642 root 1.5 return;
643 root 1.32
644 root 1.5 /* if the item is on the ground, mark that the look needs to
645     * be updated.
646     */
647     if (!op->env)
648     {
649 root 1.29 pl->contr->ns->floorbox_update ();
650 root 1.5 return;
651 root 1.3 }
652 elmex 1.1 }
653    
654 root 1.16 packet sl;
655 elmex 1.1
656 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
657 elmex 1.1
658 root 1.5 if (op->head)
659     op = op->head;
660 elmex 1.1
661 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
662 elmex 1.1
663 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, op);
664 elmex 1.1
665 root 1.29 pl->contr->ns->send_packet (sl);
666 root 1.5 SET_FLAG (op, FLAG_CLIENT_SENT);
667 elmex 1.1 }
668    
669     /**
670     * Tells the client to delete an item. Uses the item
671     * command with a -1 location.
672     */
673 root 1.5 void
674     esrv_del_item (player *pl, int tag)
675 elmex 1.1 {
676 root 1.36 if (!pl->ns)
677     return;
678    
679 root 1.27 packet sl ("delitem");
680 elmex 1.1
681 root 1.27 sl << uint32 (tag);
682 elmex 1.1
683 root 1.29 pl->ns->send_packet (sl);
684 elmex 1.1 }
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 root 1.5 object *
698     esrv_get_ob_from_count (object *pl, tag_t count)
699 elmex 1.1 {
700 root 1.5 if (pl->count == count)
701     return pl;
702 elmex 1.1
703 root 1.44 for (object *op = pl->inv; op; op = op->below)
704 root 1.5 if (op->count == count)
705     return op;
706     else if (op->type == CONTAINER && pl->container == op)
707 root 1.44 for (object *tmp = op->inv; tmp; tmp = tmp->below)
708 root 1.5 if (tmp->count == count)
709     return tmp;
710    
711 root 1.44 for (object *op = GET_MAP_OB (pl->map, pl->x, pl->y); op; op = op->above)
712 root 1.24 if (op->head && op->head->count == count)
713 root 1.5 return op;
714     else if (op->count == count)
715     return op;
716     else if (op->type == CONTAINER && pl->container == op)
717 root 1.44 for (object *tmp = op->inv; tmp; tmp = tmp->below)
718 root 1.5 if (tmp->count == count)
719     return tmp;
720 elmex 1.1
721 root 1.44 #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 root 1.24 return 0;
728 elmex 1.1 }
729    
730     /** Client wants to examine some object. So lets do so. */
731 root 1.5 void
732     ExamineCmd (char *buf, int len, player *pl)
733 elmex 1.1 {
734 root 1.10 tag_t tag = atoi (buf);
735    
736 root 1.5 object *op = esrv_get_ob_from_count (pl->ob, tag);
737 elmex 1.1
738 root 1.5 if (!op)
739     {
740     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
741     return;
742 elmex 1.1 }
743 root 1.10
744 root 1.5 examine (pl->ob, op);
745 elmex 1.1 }
746    
747 root 1.44 /** 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 root 1.24 /** Client wants to apply some object. Lets do so. */
765 root 1.5 void
766     ApplyCmd (char *buf, int len, player *pl)
767 elmex 1.1 {
768 root 1.10 tag_t tag = atoi (buf);
769 elmex 1.1
770 root 1.5 /* 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 root 1.29 pl->ns->look_position = tag & 0x7fffffff;
780     pl->ns->floorbox_update ();
781 root 1.5 return;
782     }
783    
784 root 1.10 object *op = esrv_get_ob_from_count (pl->ob, tag);
785    
786 root 1.5 if (!op)
787     {
788     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
789     return;
790 elmex 1.1 }
791 root 1.10
792 root 1.5 player_apply (pl->ob, op, 0, 0);
793 elmex 1.1 }
794    
795 root 1.26 /** Client wants to lock some object. Lets do so. */
796 root 1.5 void
797 root 1.22 LockItem (char *data, int len, player *pl)
798 elmex 1.1 {
799 root 1.12 int flag = data[0];
800 root 1.22 tag_t tag = net_uint32 ((uint8 *)data + 1);
801 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
802 root 1.5
803     if (!op)
804     {
805     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to lock/unlock");
806     return;
807     }
808 root 1.12
809 root 1.5 if (!flag)
810     CLEAR_FLAG (op, FLAG_INV_LOCKED);
811     else
812     SET_FLAG (op, FLAG_INV_LOCKED);
813 root 1.12
814 root 1.5 esrv_update_item (UPD_FLAGS, pl->ob, op);
815 elmex 1.1 }
816    
817 root 1.26 /** Client wants to mark some object. Lets do so. */
818 root 1.5 void
819 root 1.22 MarkItem (char *data, int len, player *pl)
820 elmex 1.1 {
821 root 1.22 tag_t tag = net_uint32 ((uint8 *)data);
822 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
823 root 1.9
824 root 1.5 if (!op)
825     {
826     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to mark");
827     return;
828     }
829 root 1.9
830 root 1.5 pl->mark = op;
831     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name (op));
832 elmex 1.1 }
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 root 1.47 static void
841     look_at (player *pl, int dx, int dy)
842 root 1.5 {
843 root 1.47 object *ob = pl->ob;
844 root 1.5
845 root 1.47 maptile *m = pl->observe->map;
846     sint16 x = pl->observe->x + dx;
847     sint16 y = pl->observe->y + dy;
848 root 1.5
849 root 1.49 if (!m)
850     return;
851    
852 root 1.47 if (!xy_normalise (m, x, y))
853     {
854     new_draw_info (NDI_UNIQUE, 0, ob, "You see nothing there.");
855     return;
856     }
857 root 1.5
858 root 1.47 int flag = 0;
859 root 1.5
860 root 1.47 for (object *tmp = m->at (x, y).top; tmp; tmp = tmp->below)
861 root 1.5 {
862 root 1.47 if (tmp->invisible && !QUERY_FLAG (ob, FLAG_WIZ))
863 root 1.5 continue;
864    
865     if (!flag)
866     {
867     if (dx || dy)
868 root 1.47 new_draw_info (NDI_UNIQUE, 0, ob, "There you see:");
869 root 1.5 else
870 root 1.47 new_draw_info (NDI_UNIQUE, 0, ob, "You see:");
871    
872 root 1.5 flag = 1;
873     }
874 root 1.3
875 root 1.47 if (QUERY_FLAG (ob, FLAG_WIZ))
876     new_draw_info_format (NDI_UNIQUE, 0, ob, "- %s (%d).", query_name (tmp), tmp->count);
877 root 1.5 else
878 root 1.47 new_draw_info_format (NDI_UNIQUE, 0, ob, "- %s.", query_name (tmp));
879 root 1.5
880     if (((tmp->inv != NULL || (tmp->head && tmp->head->inv)) &&
881 root 1.47 (tmp->type != CONTAINER && tmp->type != FLESH)) || QUERY_FLAG (ob, FLAG_WIZ))
882     inventory (ob, tmp->head == NULL ? tmp : tmp->head);
883 root 1.5
884 root 1.47 if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (ob, FLAG_WIZ)) /* don't continue under the floor */
885 root 1.5 break;
886     }
887    
888     if (!flag)
889     {
890     if (dx || dy)
891 root 1.47 new_draw_info (NDI_UNIQUE, 0, ob, "You see nothing there.");
892 root 1.5 else
893 root 1.47 new_draw_info (NDI_UNIQUE, 0, ob, "You see nothing.");
894 elmex 1.1 }
895     }
896    
897     /** Client wants to look at some object. Lets do so. */
898 root 1.5 void
899     LookAt (char *buf, int len, player *pl)
900 elmex 1.1 {
901 root 1.5 char *cp;
902 elmex 1.1
903 root 1.48 int dx = atoi (buf);
904 root 1.5 if (!(cp = strchr (buf, ' ')))
905 root 1.44 return;
906    
907 root 1.48 int dy = atoi (cp);
908 elmex 1.1
909 root 1.48 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 elmex 1.1
919 root 1.47 look_at (pl, dx, dy);
920 elmex 1.1 }
921    
922     /** Move an object to a new location */
923 root 1.5 void
924     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
925 elmex 1.1 {
926 root 1.5 object *op, *env;
927 elmex 1.1
928 root 1.5 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 elmex 1.1 }
934    
935 root 1.5 if (!to)
936     { /* drop it to the ground */
937     if (op->map && !op->env)
938 root 1.14 return;
939 root 1.5
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 root 1.3 }
952 root 1.14
953 root 1.5 esrv_update_item (UPD_WEIGHT, pl, op);
954 root 1.3 }
955 root 1.5 else
956 root 1.24 drop_object (pl, op, nrof);
957    
958 root 1.5 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 root 1.3 return;
965 root 1.5
966     pl->contr->count = nrof;
967     pick_up (pl, op);
968     return;
969     }
970 root 1.14
971 root 1.5 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 root 1.14
978 root 1.5 /* 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 root 1.24 put_object_in_sack (pl, env, op, nrof);
985 elmex 1.1 }
986 root 1.14