ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.29
Committed: Thu Dec 21 23:37:06 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.28: +34 -34 lines
Log Message:
- made state a per-client variable
  (that does not magically make state a per-client thing!)
- rename player->socket to player->ns. its not a good name for "client",
  but it is an historical artifact, and better than "socket".

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21 root 1.7 The author can be reached via e-mail to <crossfire@schmorp.de>
22 elmex 1.1 */
23    
24     /**
25     * \file
26     * Client/server logic.
27     *
28     * \date 2003-12-02
29     *
30     * This containes 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 root 1.5 #include <object.h> /* LOOK_OBJ */
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.13 if (!(ns.faces_sent[head->face->number] & NS_FACESENT_FACE))
269     esrv_send_face (&ns, head->face->number, 0);
270 root 1.5
271 root 1.13 if (QUERY_FLAG (head, FLAG_ANIMATE) && !ns.anims_sent[head->animation_id])
272     esrv_send_animation (&ns, 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     << uint32 (head->face->number);
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     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 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     /**
331 root 1.24 * Send the look window. Don't need to do animations here
332     * This sends all the faces to the client, not just updates. This is
333     * because object ordering would otherwise be inconsistent.
334 elmex 1.1 */
335 root 1.5 void
336     esrv_draw_look (object *pl)
337 elmex 1.1 {
338 root 1.5 object *tmp, *last;
339     int got_one = 0, start_look = 0, end_look = 0;
340     char buf[MAX_BUF];
341    
342 root 1.29 if (!pl->contr->ns->update_look)
343 root 1.5 {
344     LOG (llevDebug, "esrv_draw_look called when update_look was not set\n");
345     return;
346     }
347     else
348 root 1.29 pl->contr->ns->update_look = 0;
349 root 1.5
350 root 1.13 if (QUERY_FLAG (pl, FLAG_REMOVED)
351     || !pl->map
352     || pl->map->in_memory != MAP_IN_MEMORY
353     || out_of_map (pl->map, pl->x, pl->y))
354 root 1.5 return;
355    
356 root 1.28 for (tmp = GET_MAP_OB (pl->map, pl->x, pl->y); tmp && tmp->above; tmp = tmp->above)
357 root 1.14 ;
358 root 1.5
359 root 1.29 pl->contr->ns->send_packet ("delinv 0");
360 root 1.5
361 root 1.27 packet sl;
362 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
363 root 1.5
364 root 1.13 sl << uint32 (0);
365 root 1.5
366 root 1.29 if (!(pl->contr->ns->faces_sent[empty_face->number] & NS_FACESENT_FACE))
367     esrv_send_face (pl->contr->ns, empty_face->number, 0);
368 root 1.5
369 root 1.29 if (pl->contr->ns->look_position)
370 root 1.5 {
371 root 1.21 char buf[80];
372 root 1.26 snprintf (buf, 80, "Apply this to see %d previous items", FLOORBOX_PAGESIZE);
373 root 1.21
374 root 1.29 sl << uint32 (0x80000000 | (pl->contr->ns->look_position - FLOORBOX_PAGESIZE))
375 root 1.13 << uint32 (0)
376     << sint32 (-1)
377 root 1.21 << uint32 (empty_face->number)
378     << data8 (buf)
379     << uint16 (0)
380 root 1.13 << uint8 (0)
381     << uint32 (0);
382    
383 root 1.29 if (pl->contr->ns->itemcmd == 2)
384 root 1.13 sl << uint16 (0);
385 root 1.5 }
386    
387     for (last = NULL; tmp != last; tmp = tmp->below)
388     {
389     object *head;
390    
391     if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !last)
392     {
393     last = tmp->below; /* assumes double floor mode */
394     if (last && QUERY_FLAG (last, FLAG_IS_FLOOR))
395     last = last->below;
396     }
397 root 1.13
398 root 1.5 if (LOOK_OBJ (tmp))
399     {
400 root 1.29 if (++start_look < pl->contr->ns->look_position)
401 root 1.5 continue;
402 root 1.13
403 root 1.5 end_look++;
404 root 1.13
405 root 1.26 if (end_look > FLOORBOX_PAGESIZE)
406 root 1.5 {
407     /* What we basically do is make a 'fake' object - when the user applies it,
408     * we notice the special tag the object has, and act accordingly.
409     */
410 root 1.29 sl << uint32 (0x80000000 | (pl->contr->ns->look_position + FLOORBOX_PAGESIZE))
411 root 1.14 << uint32 (0)
412     << uint32 ((uint32) - 1)
413 root 1.21 << uint32 (empty_face->number)
414     << data8 ("Apply this to see next group of items")
415     << uint16 (0)
416 root 1.14 << uint8 (0)
417     << uint32 (0);
418    
419 root 1.29 if (pl->contr->ns->itemcmd == 2)
420 root 1.14 sl << uint16 (0);
421    
422 root 1.5 break;
423 root 1.3 }
424 root 1.14
425 root 1.5 if (tmp->head)
426     head = tmp->head;
427     else
428     head = tmp;
429    
430 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
431 root 1.5 got_one++;
432    
433 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
434 root 1.5 {
435 root 1.29 pl->contr->ns->send_packet (sl);
436 root 1.14
437     sl.reset ();
438 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
439 root 1.14 sl << uint32 (0);
440 root 1.5 got_one = 0;
441 root 1.3 }
442 root 1.24 }
443 elmex 1.1 }
444 root 1.13
445 root 1.5 if (got_one)
446 root 1.29 pl->contr->ns->send_packet (sl);
447 elmex 1.1
448     }
449    
450     /**
451     * Sends whole inventory.
452     */
453 root 1.5 void
454     esrv_send_inventory (object *pl, object *op)
455 elmex 1.1 {
456 root 1.5 object *tmp;
457     int got_one = 0;
458    
459    
460 root 1.29 pl->contr->ns->send_packet_printf ("delinv %d", op->count);
461 root 1.5
462 root 1.27 packet sl;
463 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
464 root 1.5
465 root 1.14 sl << uint32 (op->count);
466 root 1.5
467     for (tmp = op->inv; tmp; tmp = tmp->below)
468     {
469     object *head;
470    
471     if (tmp->head)
472     head = tmp->head;
473     else
474     head = tmp;
475    
476     if (LOOK_OBJ (head))
477     {
478 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
479 root 1.5
480     got_one++;
481    
482     /* IT is possible for players to accumulate a huge amount of
483     * items (especially with some of the bags out there) to
484     * overflow the buffer. IF so, send multiple item commands.
485     */
486 root 1.17 if (sl.length () >= (MAXSOCKBUF - MAXITEMLEN))
487 root 1.5 {
488 root 1.29 pl->contr->ns->send_packet (sl);
489 root 1.17
490     sl.reset ();
491 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
492 root 1.14 sl << uint32 (op->count);
493 root 1.5 got_one = 0;
494 root 1.3 }
495 root 1.5 } /* If LOOK_OBJ() */
496 elmex 1.1 }
497 root 1.14
498 root 1.5 if (got_one)
499 root 1.29 pl->contr->ns->send_packet (sl);
500 elmex 1.1 }
501    
502     /**
503     * Updates object *op for player *pl.
504     *
505     * flags is a list of values to update
506     * to the client (as defined in newclient.h - might as well use the
507     * same value both places.
508     */
509    
510 root 1.5 void
511     esrv_update_item (int flags, object *pl, object *op)
512 elmex 1.1 {
513 root 1.5 /* If we have a request to send the player item, skip a few checks. */
514     if (op != pl)
515     {
516     if (!LOOK_OBJ (op))
517     return;
518     /* we remove the check for op->env, because in theory, the object
519     * is hopefully in the same place, so the client should preserve
520     * order.
521     */
522     }
523 root 1.11
524 root 1.5 if (!QUERY_FLAG (op, FLAG_CLIENT_SENT))
525     {
526     /* FLAG_CLIENT_SENT is debug only. We are using it to see where
527     * this is happening - we can set a breakpoint here in the debugger
528     * and track back the call.
529     */
530     LOG (llevDebug, "We have not sent item %s (%d)\n", &op->name, op->count);
531     }
532 root 1.11
533 root 1.27 packet sl ("upditem");
534 root 1.5
535 root 1.27 sl << uint8 (flags);
536 root 1.5
537     if (op->head)
538     op = op->head;
539    
540 root 1.14 sl << uint32 (op->count);
541 root 1.5
542     if (flags & UPD_LOCATION)
543 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
544 root 1.5
545     if (flags & UPD_FLAGS)
546 root 1.14 sl << uint32 (query_flags (op));
547 root 1.5
548     if (flags & UPD_WEIGHT)
549     {
550     sint32 weight = WEIGHT (op);
551    
552 root 1.14 sl << uint32 (QUERY_FLAG (op, FLAG_NO_PICK) ? -1 : weight);
553    
554 root 1.5 if (pl == op)
555 root 1.14 op->contr->last_weight = weight;
556 root 1.5 }
557    
558     if (flags & UPD_FACE)
559     {
560 root 1.29 if (!(pl->contr->ns->faces_sent[op->face->number] & NS_FACESENT_FACE))
561     esrv_send_face (pl->contr->ns, op->face->number, 0);
562 root 1.14
563     sl << uint32 (op->face->number);
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     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 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.5 /* If this is not the player object, do some more checks */
635     if (op != pl)
636     {
637     /* We only send 'visibile' objects to the client */
638     if (!LOOK_OBJ (op))
639     return;
640     /* 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    
670 root 1.5 void
671     esrv_del_item (player *pl, int tag)
672 elmex 1.1 {
673 root 1.27 packet sl ("delitem");
674 elmex 1.1
675 root 1.27 sl << uint32 (tag);
676 elmex 1.1
677 root 1.29 pl->ns->send_packet (sl);
678 elmex 1.1 }
679    
680    
681     /*******************************************************************************
682     *
683     * Client has requested us to do something with an object.
684     *
685     ******************************************************************************/
686    
687     /**
688     * Takes a player and object count (tag) and returns the actual object
689     * pointer, or null if it can't be found.
690     */
691    
692 root 1.5 object *
693     esrv_get_ob_from_count (object *pl, tag_t count)
694 elmex 1.1 {
695 root 1.5 object *op, *tmp;
696 elmex 1.1
697 root 1.5 if (pl->count == count)
698     return pl;
699 elmex 1.1
700 root 1.5 for (op = pl->inv; op; op = op->below)
701     if (op->count == count)
702     return op;
703     else if (op->type == CONTAINER && pl->container == op)
704     for (tmp = op->inv; tmp; tmp = tmp->below)
705     if (tmp->count == count)
706     return tmp;
707    
708 root 1.28 for (op = GET_MAP_OB (pl->map, pl->x, pl->y); op; op = op->above)
709 root 1.24 if (op->head && op->head->count == count)
710 root 1.5 return op;
711     else if (op->count == count)
712     return op;
713     else if (op->type == CONTAINER && pl->container == op)
714     for (tmp = op->inv; tmp; tmp = tmp->below)
715     if (tmp->count == count)
716     return tmp;
717 elmex 1.1
718 root 1.24 return 0;
719 elmex 1.1 }
720    
721    
722     /** Client wants to examine some object. So lets do so. */
723 root 1.5 void
724     ExamineCmd (char *buf, int len, player *pl)
725 elmex 1.1 {
726 root 1.10 tag_t tag = atoi (buf);
727    
728     /* If the high bit is set, player examined a pseudo object. */
729     if (tag & 0x80000000)
730     return;
731    
732 root 1.5 object *op = esrv_get_ob_from_count (pl->ob, tag);
733 elmex 1.1
734 root 1.5 if (!op)
735     {
736     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
737     return;
738 elmex 1.1 }
739 root 1.10
740 root 1.5 examine (pl->ob, op);
741 elmex 1.1 }
742    
743 root 1.24 /** Client wants to apply some object. Lets do so. */
744 root 1.5 void
745     ApplyCmd (char *buf, int len, player *pl)
746 elmex 1.1 {
747 root 1.10 tag_t tag = atoi (buf);
748 elmex 1.1
749 root 1.5 /* sort of a hack, but if the player saves and the player then manually
750     * applies a savebed (or otherwise tries to do stuff), we run into trouble.
751     */
752     if (QUERY_FLAG (pl->ob, FLAG_REMOVED))
753     return;
754    
755     /* If the high bit is set, player applied a pseudo object. */
756     if (tag & 0x80000000)
757     {
758 root 1.29 pl->ns->look_position = tag & 0x7fffffff;
759     pl->ns->floorbox_update ();
760 root 1.5 return;
761     }
762    
763 root 1.10 object *op = esrv_get_ob_from_count (pl->ob, tag);
764    
765 root 1.5 if (!op)
766     {
767     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
768     return;
769 elmex 1.1 }
770 root 1.10
771 root 1.5 player_apply (pl->ob, op, 0, 0);
772 elmex 1.1 }
773    
774 root 1.26 /** Client wants to lock some object. Lets do so. */
775 root 1.5 void
776 root 1.22 LockItem (char *data, int len, player *pl)
777 elmex 1.1 {
778 root 1.12 int flag = data[0];
779 root 1.22 tag_t tag = net_uint32 ((uint8 *)data + 1);
780 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
781 root 1.5
782     if (!op)
783     {
784     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to lock/unlock");
785     return;
786     }
787 root 1.12
788 root 1.5 if (!flag)
789     CLEAR_FLAG (op, FLAG_INV_LOCKED);
790     else
791     SET_FLAG (op, FLAG_INV_LOCKED);
792 root 1.12
793 root 1.5 esrv_update_item (UPD_FLAGS, pl->ob, op);
794 elmex 1.1 }
795    
796 root 1.26 /** Client wants to mark some object. Lets do so. */
797 root 1.5 void
798 root 1.22 MarkItem (char *data, int len, player *pl)
799 elmex 1.1 {
800 root 1.22 tag_t tag = net_uint32 ((uint8 *)data);
801 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
802 root 1.9
803 root 1.5 if (!op)
804     {
805     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Could not find object to mark");
806     return;
807     }
808 root 1.9
809 root 1.5 pl->mark = op;
810     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Marked item %s", query_name (op));
811 elmex 1.1 }
812    
813     /**
814     * look_at prints items on the specified square.
815     *
816     * [ removed EARTHWALL check and added check for containers inventory.
817     * Tero.Haatanen@lut.fi ]
818     */
819 root 1.5 void
820     look_at (object *op, int dx, int dy)
821     {
822     object *tmp;
823     int flag = 0;
824     sint16 x, y;
825 root 1.8 maptile *m;
826 root 1.5
827     x = op->x + dx;
828     y = op->y + dy;
829    
830     if (out_of_map (op->map, x, y))
831     return;
832    
833     m = get_map_from_coord (op->map, &x, &y);
834     if (!m)
835     return;
836    
837 root 1.28 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above);
838 root 1.5
839     for (; tmp != NULL; tmp = tmp->below)
840     {
841     if (tmp->invisible && !QUERY_FLAG (op, FLAG_WIZ))
842     continue;
843    
844     if (!flag)
845     {
846     if (dx || dy)
847     new_draw_info (NDI_UNIQUE, 0, op, "There you see:");
848     else
849     {
850     clear_win_info (op);
851     new_draw_info (NDI_UNIQUE, 0, op, "You see:");
852 root 1.3 }
853 root 1.5 flag = 1;
854     }
855 root 1.3
856 root 1.5 if (QUERY_FLAG (op, FLAG_WIZ))
857     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s (%d).", query_name (tmp), tmp->count);
858     else
859     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s.", query_name (tmp));
860    
861     if (((tmp->inv != NULL || (tmp->head && tmp->head->inv)) &&
862     (tmp->type != CONTAINER && tmp->type != FLESH)) || QUERY_FLAG (op, FLAG_WIZ))
863     inventory (op, tmp->head == NULL ? tmp : tmp->head);
864    
865     if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (op, FLAG_WIZ)) /* don't continue under the floor */
866     break;
867     }
868    
869     if (!flag)
870     {
871     if (dx || dy)
872     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing there.");
873     else
874     new_draw_info (NDI_UNIQUE, 0, op, "You see nothing.");
875 elmex 1.1 }
876     }
877    
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.29 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