ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.53
Committed: Sun Sep 30 20:22:25 2007 UTC (16 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.52: +26 -46 lines
Log Message:
- clean up message system, combine all boxes into one.
- suppress too long messages (we need a more robust solution to this problem).
- get rid of INS_MAP_LOAD, leading to slightly cleaner/faster code
  and certainly one special case less.
- insert objects manually at load time, this is both faster and also more
  correct, as loading a map is never supposed to trigger anything (and
  also for symmetry to the save code).

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.51 ns.send_face (head->face, -50);
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.51 pl->ns->send_face (empty_face, -50);
363 root 1.46 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.52 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.52 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.51 ns->send_face (op->face, -50);
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.53 dynbuf_text buf;
844 root 1.47 object *ob = pl->ob;
845 root 1.5
846 root 1.53 mapxy pos (pl->observe);
847     pos.move (dx, dy);
848 root 1.5
849 root 1.53 if (pos.normalise ())
850     for (object *tmp = pos->top; tmp; tmp = tmp->below)
851     {
852     if (tmp->invisible && !QUERY_FLAG (ob, FLAG_WIZ))
853     continue;
854 root 1.5
855 root 1.53 if (QUERY_FLAG (ob, FLAG_WIZ))
856     buf.printf ("- %s (%d).\n", query_name (tmp), tmp->count);
857     else
858     buf.printf ("- %s.\n", query_name (tmp));
859    
860     object *head = tmp->head_ ();
861    
862     if (head->inv)
863     if ((head->type != CONTAINER && head->type != FLESH)
864     || QUERY_FLAG (ob, FLAG_WIZ))
865     buf << head->query_inventory (ob, " ");
866 root 1.5
867 root 1.53 if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (ob, FLAG_WIZ)) /* don't continue under the floor */
868     break;
869     }
870 root 1.47
871 root 1.53 if (buf.empty ())
872     pl->failmsg ("You see nothing there.");
873     else
874     pl->infobox (MSG_CHANNEL ("lookat"), buf);
875 elmex 1.1 }
876    
877     /** Client wants to look at some object. Lets do so. */
878 root 1.5 void
879     LookAt (char *buf, int len, player *pl)
880 elmex 1.1 {
881 root 1.5 char *cp;
882 elmex 1.1
883 root 1.48 int dx = atoi (buf);
884 root 1.5 if (!(cp = strchr (buf, ' ')))
885 root 1.44 return;
886    
887 root 1.48 int dy = atoi (cp);
888 elmex 1.1
889 root 1.48 if (player *opl = pl->observe->contr)
890     if (client *ns = opl->ns)
891     {
892     if (fabs (dx) > ns->mapx / 2 || fabs (dy) > ns->mapy / 2)
893     return;
894    
895     if (opl->blocked_los[dx + ns->mapx / 2][dy + ns->mapy / 2])
896     return;
897     }
898 elmex 1.1
899 root 1.47 look_at (pl, dx, dy);
900 elmex 1.1 }
901    
902     /** Move an object to a new location */
903 root 1.5 void
904     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
905 elmex 1.1 {
906 root 1.5 object *op, *env;
907 elmex 1.1
908 root 1.5 op = esrv_get_ob_from_count (pl, tag);
909     if (!op)
910     {
911     LOG (llevDebug, "Player '%s' tried to move an unknown object (%ld)\n", &pl->name, tag);
912     return;
913 elmex 1.1 }
914    
915 root 1.5 if (!to)
916     { /* drop it to the ground */
917     if (op->map && !op->env)
918 root 1.14 return;
919 root 1.5
920     /* If it is an active container, then we should drop all objects
921     * in the container and not the container itself.
922     */
923     if (op->inv && QUERY_FLAG (op, FLAG_APPLIED))
924     {
925     object *current, *next;
926    
927     for (current = op->inv; current != NULL; current = next)
928     {
929     next = current->below;
930     drop_object (pl, current, 0);
931 root 1.3 }
932 root 1.14
933 root 1.5 esrv_update_item (UPD_WEIGHT, pl, op);
934 root 1.3 }
935 root 1.5 else
936 root 1.24 drop_object (pl, op, nrof);
937    
938 root 1.5 return;
939     }
940     else if (to == pl->count)
941     { /* pick it up to the inventory */
942     /* return if player has already picked it up */
943     if (op->env == pl)
944 root 1.3 return;
945 root 1.5
946     pl->contr->count = nrof;
947     pick_up (pl, op);
948     return;
949     }
950 root 1.14
951 root 1.5 env = esrv_get_ob_from_count (pl, to);
952     if (!env)
953     {
954     LOG (llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", &pl->name, to);
955     return;
956     }
957 root 1.14
958 root 1.5 /* put_object_in_sack presumes that necessary sanity checking
959     * has already been done (eg, it can be picked up and fits in
960     * in a sack, so check for those things. We should also check
961     * an make sure env is in fact a container for that matter.
962     */
963     if (env->type == CONTAINER && can_pick (pl, op) && sack_can_hold (pl, env, op, nrof))
964 root 1.24 put_object_in_sack (pl, env, op, nrof);
965 elmex 1.1 }
966 root 1.14