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

# User Rev Content
1 elmex 1.1 /*
2 root 1.32 * 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 elmex 1.1
25     /**
26     * \file
27     * Client/server logic.
28     *
29     * \date 2003-12-02
30     *
31 root 1.32 * This contains item logic for client/server. It doesn't contain
32 elmex 1.1 * 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 root 1.32 #include <object.h>
38 elmex 1.1 #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 root 1.28 #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 elmex 1.1 /*******************************************************************************
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 root 1.24 * to be sent to client.
192 elmex 1.1 */
193 root 1.5 unsigned int
194     query_flags (object *op)
195 elmex 1.1 {
196 root 1.5 unsigned int flags = 0;
197 elmex 1.1
198 root 1.5 if (QUERY_FLAG (op, FLAG_APPLIED))
199     {
200     switch (op->type)
201     {
202 root 1.24 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 root 1.5 }
231     }
232 root 1.24
233 root 1.5 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 root 1.24
244 root 1.5 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 elmex 1.1
251 root 1.5 return flags;
252 elmex 1.1 }
253    
254 root 1.16 /* Used in the send_look to put object head into packet
255 elmex 1.1 * sl for socket ns. Need socket to know if we need to send
256     * animation of face to the client.
257     */
258 root 1.5 static void
259 root 1.23 add_object_to_socklist (client &ns, packet &sl, object *head)
260 elmex 1.1 {
261 root 1.5 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 root 1.42 ns.send_face (head->face);
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     esrv_draw_look (object *pl)
336 elmex 1.1 {
337 root 1.5 int got_one = 0, start_look = 0, end_look = 0;
338    
339 root 1.29 if (!pl->contr->ns->update_look)
340 root 1.5 {
341 root 1.39 LOG (llevDebug, "esrv_draw_look called when update_look was not set (player %s)\n", &pl->name);
342 root 1.5 return;
343     }
344     else
345 root 1.29 pl->contr->ns->update_look = 0;
346 root 1.5
347 root 1.13 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 root 1.5 return;
352    
353 root 1.29 pl->contr->ns->send_packet ("delinv 0");
354 root 1.5
355 root 1.27 packet sl;
356 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
357 root 1.5
358 root 1.13 sl << uint32 (0);
359 root 1.5
360 root 1.42 pl->contr->ns->send_face (empty_face);
361 root 1.5
362 root 1.29 if (pl->contr->ns->look_position)
363 root 1.5 {
364 root 1.21 char buf[80];
365 root 1.26 snprintf (buf, 80, "Apply this to see %d previous items", FLOORBOX_PAGESIZE);
366 root 1.21
367 root 1.29 sl << uint32 (0x80000000 | (pl->contr->ns->look_position - FLOORBOX_PAGESIZE))
368 root 1.13 << uint32 (0)
369     << sint32 (-1)
370 root 1.41 << uint32 (empty_face)
371 root 1.21 << data8 (buf)
372     << uint16 (0)
373 root 1.13 << uint8 (0)
374     << uint32 (0);
375    
376 root 1.29 if (pl->contr->ns->itemcmd == 2)
377 root 1.13 sl << uint16 (0);
378 root 1.5 }
379    
380 root 1.32 object *tmp = pl->ms ().top;
381     for (object *last = 0; tmp != last; tmp = tmp->below)
382 root 1.5 {
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 root 1.13
392 root 1.32 if (tmp->client_visible ())
393 root 1.5 {
394 root 1.29 if (++start_look < pl->contr->ns->look_position)
395 root 1.5 continue;
396 root 1.13
397 root 1.5 end_look++;
398 root 1.13
399 root 1.26 if (end_look > FLOORBOX_PAGESIZE)
400 root 1.5 {
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 root 1.29 sl << uint32 (0x80000000 | (pl->contr->ns->look_position + FLOORBOX_PAGESIZE))
405 root 1.14 << uint32 (0)
406     << uint32 ((uint32) - 1)
407 root 1.41 << uint32 (empty_face)
408 root 1.21 << data8 ("Apply this to see next group of items")
409     << uint16 (0)
410 root 1.14 << uint8 (0)
411     << uint32 (0);
412    
413 root 1.29 if (pl->contr->ns->itemcmd == 2)
414 root 1.14 sl << uint16 (0);
415    
416 root 1.5 break;
417 root 1.3 }
418 root 1.14
419 root 1.5 if (tmp->head)
420     head = tmp->head;
421     else
422     head = tmp;
423    
424 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
425 root 1.5 got_one++;
426    
427 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
428 root 1.5 {
429 root 1.29 pl->contr->ns->send_packet (sl);
430 root 1.14
431     sl.reset ();
432 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
433 root 1.14 sl << uint32 (0);
434 root 1.5 got_one = 0;
435 root 1.3 }
436 root 1.24 }
437 elmex 1.1 }
438 root 1.13
439 root 1.5 if (got_one)
440 root 1.29 pl->contr->ns->send_packet (sl);
441 elmex 1.1
442     }
443    
444     /**
445     * Sends whole inventory.
446     */
447 root 1.5 void
448     esrv_send_inventory (object *pl, object *op)
449 elmex 1.1 {
450 root 1.33 if (!pl->contr->ns)//D
451     return;
452    
453 root 1.5 int got_one = 0;
454    
455 root 1.29 pl->contr->ns->send_packet_printf ("delinv %d", op->count);
456 root 1.5
457 root 1.27 packet sl;
458 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
459 root 1.5
460 root 1.14 sl << uint32 (op->count);
461 root 1.5
462 root 1.32 for (object *tmp = op->inv; tmp; tmp = tmp->below)
463 root 1.5 {
464     object *head;
465    
466     if (tmp->head)
467     head = tmp->head;
468     else
469     head = tmp;
470    
471 root 1.32 if (head->client_visible ())
472 root 1.5 {
473 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
474 root 1.5
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 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
482 root 1.5 {
483 root 1.29 pl->contr->ns->send_packet (sl);
484 root 1.17
485     sl.reset ();
486 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
487 root 1.14 sl << uint32 (op->count);
488 root 1.5 got_one = 0;
489 root 1.3 }
490 root 1.32 }
491 elmex 1.1 }
492 root 1.14
493 root 1.5 if (got_one)
494 root 1.29 pl->contr->ns->send_packet (sl);
495 elmex 1.1 }
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 root 1.5 void
505     esrv_update_item (int flags, object *pl, object *op)
506 elmex 1.1 {
507 root 1.5 /* If we have a request to send the player item, skip a few checks. */
508     if (op != pl)
509     {
510 root 1.32 if (!op->client_visible ())
511 root 1.5 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 root 1.11
518 root 1.31 client *ns = pl->contr->ns;
519     if (!ns)
520     return;
521    
522 root 1.5 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 root 1.11
531 root 1.27 packet sl ("upditem");
532 root 1.5
533 root 1.27 sl << uint8 (flags);
534 root 1.5
535     if (op->head)
536     op = op->head;
537    
538 root 1.14 sl << uint32 (op->count);
539 root 1.5
540     if (flags & UPD_LOCATION)
541 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
542 root 1.5
543     if (flags & UPD_FLAGS)
544 root 1.14 sl << uint32 (query_flags (op));
545 root 1.5
546     if (flags & UPD_WEIGHT)
547     {
548     sint32 weight = WEIGHT (op);
549    
550 root 1.14 sl << uint32 (QUERY_FLAG (op, FLAG_NO_PICK) ? -1 : weight);
551    
552 root 1.5 if (pl == op)
553 root 1.31 ns->last_weight = weight;
554 root 1.5 }
555    
556     if (flags & UPD_FACE)
557     {
558 root 1.42 ns->send_face (op->face);
559 root 1.41 sl << uint32 (op->face);
560 root 1.5 }
561 root 1.11
562 root 1.5 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 root 1.14
587     sl << data8 (item_n, len);
588 root 1.5 }
589 root 1.11
590 root 1.5 if (flags & UPD_ANIM)
591 root 1.14 sl << uint16 (op->animation_id);
592 root 1.5
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 root 1.35 if (fabs (op->speed) < 0.001)
604 root 1.5 anim_speed = 255;
605 root 1.35 else if (fabs (op->speed) >= 1.0)
606 root 1.5 anim_speed = 1;
607     else
608 root 1.35 anim_speed = (int) (1.0 / fabs (op->speed));
609 root 1.3 }
610 root 1.14
611 root 1.5 if (anim_speed > 255)
612     anim_speed = 255;
613 root 1.3 }
614 root 1.14
615     sl << uint8 (anim_speed);
616 elmex 1.1 }
617 root 1.14
618 root 1.5 if (flags & UPD_NROF)
619 root 1.14 sl << uint32 (op->nrof);
620 elmex 1.1
621 root 1.29 pl->contr->ns->send_packet (sl);
622 elmex 1.1 }
623    
624     /**
625     * Sends item's info to player.
626     */
627 root 1.5 void
628     esrv_send_item (object *pl, object *op)
629 elmex 1.1 {
630 root 1.34 if (!pl->contr->ns)
631     return;
632    
633 root 1.5 /* 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 root 1.32 if (!op->client_visible ())
638 root 1.5 return;
639 root 1.32
640 root 1.5 /* if the item is on the ground, mark that the look needs to
641     * be updated.
642     */
643     if (!op->env)
644     {
645 root 1.29 pl->contr->ns->floorbox_update ();
646 root 1.5 return;
647 root 1.3 }
648 elmex 1.1 }
649    
650 root 1.16 packet sl;
651 elmex 1.1
652 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
653 elmex 1.1
654 root 1.5 if (op->head)
655     op = op->head;
656 elmex 1.1
657 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
658 elmex 1.1
659 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, op);
660 elmex 1.1
661 root 1.29 pl->contr->ns->send_packet (sl);
662 root 1.5 SET_FLAG (op, FLAG_CLIENT_SENT);
663 elmex 1.1 }
664    
665     /**
666     * Tells the client to delete an item. Uses the item
667     * command with a -1 location.
668     */
669 root 1.5 void
670     esrv_del_item (player *pl, int tag)
671 elmex 1.1 {
672 root 1.36 if (!pl->ns)
673     return;
674    
675 root 1.27 packet sl ("delitem");
676 elmex 1.1
677 root 1.27 sl << uint32 (tag);
678 elmex 1.1
679 root 1.29 pl->ns->send_packet (sl);
680 elmex 1.1 }
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 root 1.5 object *
695     esrv_get_ob_from_count (object *pl, tag_t count)
696 elmex 1.1 {
697 root 1.5 object *op, *tmp;
698 elmex 1.1
699 root 1.5 if (pl->count == count)
700     return pl;
701 elmex 1.1
702 root 1.5 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 root 1.28 for (op = GET_MAP_OB (pl->map, pl->x, pl->y); op; op = op->above)
711 root 1.24 if (op->head && op->head->count == count)
712 root 1.5 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 elmex 1.1
720 root 1.24 return 0;
721 elmex 1.1 }
722    
723    
724     /** Client wants to examine some object. So lets do so. */
725 root 1.5 void
726     ExamineCmd (char *buf, int len, player *pl)
727 elmex 1.1 {
728 root 1.10 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 root 1.5 object *op = esrv_get_ob_from_count (pl->ob, tag);
735 elmex 1.1
736 root 1.5 if (!op)
737     {
738     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
739     return;
740 elmex 1.1 }
741 root 1.10
742 root 1.5 examine (pl->ob, op);
743 elmex 1.1 }
744    
745 root 1.24 /** Client wants to apply some object. Lets do so. */
746 root 1.5 void
747     ApplyCmd (char *buf, int len, player *pl)
748 elmex 1.1 {
749 root 1.10 tag_t tag = atoi (buf);
750 elmex 1.1
751 root 1.5 /* 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 root 1.29 pl->ns->look_position = tag & 0x7fffffff;
761     pl->ns->floorbox_update ();
762 root 1.5 return;
763     }
764    
765 root 1.10 object *op = esrv_get_ob_from_count (pl->ob, tag);
766    
767 root 1.5 if (!op)
768     {
769     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
770     return;
771 elmex 1.1 }
772 root 1.10
773 root 1.5 player_apply (pl->ob, op, 0, 0);
774 elmex 1.1 }
775    
776 root 1.26 /** Client wants to lock some object. Lets do so. */
777 root 1.5 void
778 root 1.22 LockItem (char *data, int len, player *pl)
779 elmex 1.1 {
780 root 1.12 int flag = data[0];
781 root 1.22 tag_t tag = net_uint32 ((uint8 *)data + 1);
782 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
783 root 1.5
784     if (!op)
785     {
786     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to lock/unlock");
787     return;
788     }
789 root 1.12
790 root 1.5 if (!flag)
791     CLEAR_FLAG (op, FLAG_INV_LOCKED);
792     else
793     SET_FLAG (op, FLAG_INV_LOCKED);
794 root 1.12
795 root 1.5 esrv_update_item (UPD_FLAGS, pl->ob, op);
796 elmex 1.1 }
797    
798 root 1.26 /** Client wants to mark some object. Lets do so. */
799 root 1.5 void
800 root 1.22 MarkItem (char *data, int len, player *pl)
801 elmex 1.1 {
802 root 1.22 tag_t tag = net_uint32 ((uint8 *)data);
803 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
804 root 1.9
805 root 1.5 if (!op)
806     {
807     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to mark");
808     return;
809     }
810 root 1.9
811 root 1.5 pl->mark = op;
812     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name (op));
813 elmex 1.1 }
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 root 1.5 void
822     look_at (object *op, int dx, int dy)
823     {
824     object *tmp;
825     int flag = 0;
826     sint16 x, y;
827 root 1.8 maptile *m;
828 root 1.5
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 root 1.28 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above);
840 root 1.5
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 root 1.3 }
855 root 1.5 flag = 1;
856     }
857 root 1.3
858 root 1.5 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 elmex 1.1 }
878     }
879    
880     /** Client wants to look at some object. Lets do so. */
881 root 1.5 void
882     LookAt (char *buf, int len, player *pl)
883 elmex 1.1 {
884 root 1.5 int dx, dy;
885     char *cp;
886 elmex 1.1
887 root 1.5 dx = atoi (buf);
888     if (!(cp = strchr (buf, ' ')))
889     {
890     return;
891 elmex 1.1 }
892 root 1.5 dy = atoi (cp);
893 elmex 1.1
894 root 1.35 if (fabs (dx) > pl->ns->mapx / 2 || fabs (dy) > pl->ns->mapy / 2)
895 root 1.5 return;
896 elmex 1.1
897 root 1.29 if (pl->blocked_los[dx + pl->ns->mapx / 2][dy + pl->ns->mapy / 2])
898 root 1.5 return;
899 elmex 1.1
900 root 1.5 look_at (pl->ob, dx, dy);
901 elmex 1.1 }
902    
903     /** Move an object to a new location */
904 root 1.5 void
905     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
906 elmex 1.1 {
907 root 1.5 object *op, *env;
908 elmex 1.1
909 root 1.5 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 elmex 1.1 }
915    
916 root 1.5 if (!to)
917     { /* drop it to the ground */
918     if (op->map && !op->env)
919 root 1.14 return;
920 root 1.5
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 root 1.3 }
933 root 1.14
934 root 1.5 esrv_update_item (UPD_WEIGHT, pl, op);
935 root 1.3 }
936 root 1.5 else
937 root 1.24 drop_object (pl, op, nrof);
938    
939 root 1.5 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 root 1.3 return;
946 root 1.5
947     pl->contr->count = nrof;
948     pick_up (pl, op);
949     return;
950     }
951 root 1.14
952 root 1.5 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 root 1.14
959 root 1.5 /* 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 root 1.24 put_object_in_sack (pl, env, op, nrof);
966 elmex 1.1 }
967 root 1.14