ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/item.C
Revision: 1.64
Committed: Mon May 5 22:03:22 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.63: +99 -62 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.55 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.45 *
4 root 1.59 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.45 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7     *
8 root 1.55 * Deliantra is free software: you can redistribute it and/or modify
9 root 1.50 * 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 root 1.55 * The authors can be reached via e-mail to <support@deliantra.net>
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 root 1.63 switch (op->type)
199     {
200     case BOW:
201     case WAND:
202     case ROD:
203     case HORN:
204     flags = a_readied;
205     break;
206     case WEAPON:
207     flags = a_wielded;
208     break;
209     case SKILL:
210     case ARMOUR:
211     case HELMET:
212     case SHIELD:
213     case RING:
214     case BOOTS:
215     case GLOVES:
216     case AMULET:
217     case GIRDLE:
218     case BRACERS:
219     case CLOAK:
220     flags = a_worn;
221     break;
222     case CONTAINER:
223     flags = a_active;
224     break;
225     default:
226     flags = a_applied;
227     break;
228     }
229 root 1.24
230 root 1.5 if (op->type == CONTAINER && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG (op, FLAG_APPLIED))))
231     flags |= F_OPEN;
232    
233     if (QUERY_FLAG (op, FLAG_KNOWN_CURSED))
234     {
235     if (QUERY_FLAG (op, FLAG_DAMNED))
236     flags |= F_DAMNED;
237     else if (QUERY_FLAG (op, FLAG_CURSED))
238     flags |= F_CURSED;
239     }
240 root 1.24
241 root 1.5 if (QUERY_FLAG (op, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (op, FLAG_IDENTIFIED))
242     flags |= F_MAGIC;
243     if (QUERY_FLAG (op, FLAG_UNPAID))
244     flags |= F_UNPAID;
245     if (QUERY_FLAG (op, FLAG_INV_LOCKED))
246     flags |= F_LOCKED;
247 elmex 1.1
248 root 1.5 return flags;
249 elmex 1.1 }
250    
251 root 1.16 /* Used in the send_look to put object head into packet
252 elmex 1.1 * sl for socket ns. Need socket to know if we need to send
253     * animation of face to the client.
254     */
255 root 1.5 static void
256 root 1.23 add_object_to_socklist (client &ns, packet &sl, object *head)
257 elmex 1.1 {
258 root 1.5 int flags, len, anim_speed;
259     char item_n[MAX_BUF];
260     const char *item_p;
261    
262     flags = query_flags (head);
263     if (QUERY_FLAG (head, FLAG_NO_PICK))
264     flags |= F_NOPICK;
265    
266 root 1.51 ns.send_face (head->face, -50);
267 root 1.43 ns.flush_fx ();
268 root 1.5
269 root 1.13 if (QUERY_FLAG (head, FLAG_ANIMATE) && !ns.anims_sent[head->animation_id])
270 root 1.42 ns.send_animation (head->animation_id);
271 root 1.5
272 root 1.13 sl << uint32 (head->count)
273     << uint32 (flags)
274 root 1.60 << uint32 (QUERY_FLAG (head, FLAG_NO_PICK) ? -1 : head->client_weight ())
275 root 1.41 << uint32 (head->face);
276 root 1.5
277     if (!head->custom_name)
278     {
279     strncpy (item_n, query_base_name (head, 0), 127);
280     item_n[127] = 0;
281     len = strlen (item_n);
282     item_p = query_base_name (head, 1);
283     }
284     else
285     {
286     strncpy (item_n, head->custom_name, 127);
287     item_n[127] = 0;
288     len = strlen (item_n);
289     item_p = head->custom_name;
290     }
291 root 1.13
292 root 1.5 strncpy (item_n + len + 1, item_p, 127);
293     item_n[254] = 0;
294     len += strlen (item_n + 1 + len) + 1;
295    
296 root 1.13 sl << data8 (item_n, len)
297     << uint16 (head->animation_id);
298    
299 root 1.5 anim_speed = 0;
300     if (QUERY_FLAG (head, FLAG_ANIMATE))
301     {
302     if (head->anim_speed)
303     anim_speed = head->anim_speed;
304     else
305     {
306 root 1.35 if (fabs (head->speed) < 0.001)
307 root 1.5 anim_speed = 255;
308 root 1.35 else if (fabs (head->speed) >= 1.0)
309 root 1.5 anim_speed = 1;
310     else
311 root 1.35 anim_speed = (int) (1.0 / fabs (head->speed));
312 root 1.5 }
313 root 1.13
314 root 1.5 if (anim_speed > 255)
315     anim_speed = 255;
316 elmex 1.1 }
317    
318 root 1.13 sl << uint8 (anim_speed)
319     << uint32 (head->nrof);
320    
321     if (ns.itemcmd == 2)
322     sl << uint16 (head->client_type);
323 elmex 1.1
324 root 1.5 SET_FLAG (head, FLAG_CLIENT_SENT);
325 elmex 1.1 }
326    
327 root 1.64 static faceidx
328     need_face_now (player *pl, const char *name)
329     {
330     faceidx face = face_find (name, empty_face);
331    
332     pl->ns->send_face (face, -50);
333     pl->ns->flush_fx ();
334    
335     return face;
336     }
337    
338     #define FINGER_UP "finger_up.x11"
339     #define FINGER_DOWN "finger_down.x11"
340    
341 elmex 1.1 /**
342 root 1.24 * Send the look window. Don't need to do animations here
343     * This sends all the faces to the client, not just updates. This is
344     * because object ordering would otherwise be inconsistent.
345 elmex 1.1 */
346 root 1.5 void
347 root 1.46 esrv_draw_look (player *pl)
348 elmex 1.1 {
349 root 1.46 object *ob = pl->ob;
350    
351     if (!pl->ns->update_look)
352 root 1.5 {
353 root 1.46 LOG (llevDebug, "esrv_draw_look called when update_look was not set (player %s)\n", &ob->name);
354 root 1.5 return;
355     }
356 root 1.63
357     pl->ns->update_look = 0;
358 root 1.5
359 root 1.46 if (QUERY_FLAG (ob, FLAG_REMOVED)
360     || !ob->map
361 root 1.58 || ob->map->in_memory != MAP_ACTIVE
362 root 1.46 || out_of_map (ob->map, ob->x, ob->y))
363 root 1.5 return;
364    
365 root 1.46 pl->ns->send_packet ("delinv 0");
366 root 1.5
367 root 1.27 packet sl;
368 root 1.46 sl.printf ("item%d ", pl->ns->itemcmd);
369 root 1.5
370 root 1.13 sl << uint32 (0);
371 root 1.5
372 root 1.64 int start_pos = pl->ns->look_position;
373     bool dirty = false;
374    
375     mapspace &ms = ob->ms ();
376    
377     // manage a ring buffer of the "last FLOORBOX_PAGESIZE" items and
378     // start from the top
379     object *items [FLOORBOX_PAGESIZE];
380     int item_idx = 0, item_cnt = 0;
381     int pos = 0;
382    
383     // find our items by walking down
384     object *item = ms.top;
385 root 1.5
386 root 1.64 for (; item && item_cnt < FLOORBOX_PAGESIZE; item = item->below)
387 root 1.5 {
388 root 1.64 if (!item->client_visible ())
389     continue;
390 root 1.21
391 root 1.64 if (++pos < start_pos)
392     continue;
393    
394     // record item
395     items [item_idx] = item; item_idx = item_idx < FLOORBOX_PAGESIZE ? item_idx + 1 : 0;
396     item_cnt++;
397    
398     // stop at first floor
399     if (item->flag [FLAG_IS_FLOOR])
400     {
401     // we are finished, don't append "next group of items"
402     // by setting item to ms.bot it becomes zero which is checked after the while loop
403     item = ms.bot;
404     }
405     }
406    
407     // see if there are more if we cared - we ignore invisible objects
408     if (item)
409     {
410     /* What we basically do is make a 'fake' object - when the user applies it,
411     * we notice the special tag the object has, and act accordingly.
412     */
413     sl << uint32 (0x80000000 | (start_pos + FLOORBOX_PAGESIZE))
414 root 1.13 << uint32 (0)
415 root 1.64 << uint32 ((uint32) - 1)
416     << uint32 (need_face_now (pl, FINGER_DOWN))
417     << data8 ("Apply this to see the items below")
418 root 1.21 << uint16 (0)
419 root 1.13 << uint8 (0)
420     << uint32 (0);
421    
422 root 1.46 if (pl->ns->itemcmd == 2)
423 root 1.13 sl << uint16 (0);
424 root 1.64
425     dirty = true;
426 root 1.5 }
427    
428 root 1.64 // now send out all items in the ring buffer in reverse order
429     while (item_cnt)
430 root 1.5 {
431 root 1.64 --item_cnt;
432     item_idx = (item_idx ? item_idx : FLOORBOX_PAGESIZE) - 1;
433     object *item = items [item_idx];
434 root 1.13
435 root 1.64 add_object_to_socklist (*pl->ns, sl, item->head_ ());
436 root 1.13
437 root 1.64 dirty = true;
438 root 1.13
439 root 1.64 // if packet got too large, send it and begin a new one
440     if (sl.length () > MAXSOCKBUF - MAXITEMLEN)
441     {
442     pl->ns->send_packet (sl);
443 root 1.14
444 root 1.64 sl.reset ();
445     sl.printf ("item%d ", pl->ns->itemcmd);
446     sl << uint32 (0);
447 root 1.14
448 root 1.64 dirty = false;
449     }
450     }
451    
452     if (start_pos)
453     {
454     sl << uint32 (0x80000000 | (start_pos - FLOORBOX_PAGESIZE))
455     << uint32 (0)
456     << sint32 (-1)
457     << uint32 (need_face_now (pl, FINGER_UP))
458     << data8 ("Apply this to see the items higher up")
459     << uint16 (0)
460     << uint8 (0)
461     << uint32 (0);
462 root 1.14
463 root 1.64 if (pl->ns->itemcmd == 2)
464     sl << uint16 (0);
465 root 1.5
466 root 1.64 dirty = true;
467     }
468 root 1.14
469 root 1.64 if (dirty)
470 root 1.46 pl->ns->send_packet (sl);
471 elmex 1.1 }
472    
473     /**
474     * Sends whole inventory.
475     */
476 root 1.5 void
477     esrv_send_inventory (object *pl, object *op)
478 elmex 1.1 {
479 root 1.33 if (!pl->contr->ns)//D
480     return;
481    
482 root 1.5 int got_one = 0;
483    
484 root 1.29 pl->contr->ns->send_packet_printf ("delinv %d", op->count);
485 root 1.5
486 root 1.27 packet sl;
487 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
488 root 1.5
489 root 1.14 sl << uint32 (op->count);
490 root 1.5
491 root 1.32 for (object *tmp = op->inv; tmp; tmp = tmp->below)
492 root 1.5 {
493     object *head;
494    
495     if (tmp->head)
496     head = tmp->head;
497     else
498     head = tmp;
499    
500 root 1.32 if (head->client_visible ())
501 root 1.5 {
502 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, head);
503 root 1.5
504     got_one++;
505    
506     /* IT is possible for players to accumulate a huge amount of
507     * items (especially with some of the bags out there) to
508     * overflow the buffer. IF so, send multiple item commands.
509     */
510 root 1.52 if (sl.length () > MAXSOCKBUF - MAXITEMLEN)
511 root 1.5 {
512 root 1.29 pl->contr->ns->send_packet (sl);
513 root 1.17
514     sl.reset ();
515 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
516 root 1.14 sl << uint32 (op->count);
517 root 1.5 got_one = 0;
518 root 1.3 }
519 root 1.32 }
520 elmex 1.1 }
521 root 1.14
522 root 1.5 if (got_one)
523 root 1.29 pl->contr->ns->send_packet (sl);
524 elmex 1.1 }
525    
526     /**
527     * Updates object *op for player *pl.
528     *
529     * flags is a list of values to update
530     * to the client (as defined in newclient.h - might as well use the
531     * same value both places.
532     */
533 root 1.5 void
534     esrv_update_item (int flags, object *pl, object *op)
535 elmex 1.1 {
536 root 1.5 /* If we have a request to send the player item, skip a few checks. */
537 root 1.61 if (op != pl && !op->client_visible ())
538     return;
539 root 1.11
540 root 1.31 client *ns = pl->contr->ns;
541     if (!ns)
542     return;
543    
544 root 1.5 if (!QUERY_FLAG (op, FLAG_CLIENT_SENT))
545     {
546     /* FLAG_CLIENT_SENT is debug only. We are using it to see where
547     * this is happening - we can set a breakpoint here in the debugger
548     * and track back the call.
549     */
550     LOG (llevDebug, "We have not sent item %s (%d)\n", &op->name, op->count);
551     }
552 root 1.11
553 root 1.27 packet sl ("upditem");
554 root 1.5
555 root 1.27 sl << uint8 (flags);
556 root 1.5
557 root 1.63 op = op->head_ ();
558 root 1.5
559 root 1.14 sl << uint32 (op->count);
560 root 1.5
561     if (flags & UPD_LOCATION)
562 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
563 root 1.5
564     if (flags & UPD_FLAGS)
565 root 1.14 sl << uint32 (query_flags (op));
566 root 1.5
567     if (flags & UPD_WEIGHT)
568     {
569 root 1.61 sint32 weight = op->flag [FLAG_NO_PICK] ? -1 : op->client_weight ();
570 root 1.5
571 root 1.61 if (op)
572     ns->last_weight = weight;
573 root 1.14
574 root 1.61 sl << uint32 (weight);
575 root 1.5 }
576    
577     if (flags & UPD_FACE)
578     {
579 root 1.51 ns->send_face (op->face, -50);
580 root 1.43 ns->flush_fx ();
581 root 1.41 sl << uint32 (op->face);
582 root 1.5 }
583 root 1.11
584 root 1.5 if (flags & UPD_NAME)
585     {
586     int len;
587     const char *item_p;
588     char item_n[MAX_BUF];
589    
590     if (!op->custom_name)
591     {
592     strncpy (item_n, query_base_name (op, 0), 127);
593     item_n[127] = 0;
594     len = strlen (item_n);
595     item_p = query_base_name (op, 1);
596     }
597     else
598     {
599     strncpy (item_n, op->custom_name, 127);
600     item_n[127] = 0;
601     len = strlen (item_n);
602     item_p = op->custom_name;
603     }
604    
605     strncpy (item_n + len + 1, item_p, 127);
606     item_n[254] = 0;
607     len += strlen (item_n + 1 + len) + 1;
608 root 1.14
609     sl << data8 (item_n, len);
610 root 1.5 }
611 root 1.11
612 root 1.5 if (flags & UPD_ANIM)
613 root 1.14 sl << uint16 (op->animation_id);
614 root 1.5
615     if (flags & UPD_ANIMSPEED)
616     {
617     int anim_speed = 0;
618    
619     if (QUERY_FLAG (op, FLAG_ANIMATE))
620     {
621     if (op->anim_speed)
622     anim_speed = op->anim_speed;
623     else
624     {
625 root 1.35 if (fabs (op->speed) < 0.001)
626 root 1.5 anim_speed = 255;
627 root 1.35 else if (fabs (op->speed) >= 1.0)
628 root 1.5 anim_speed = 1;
629     else
630 root 1.35 anim_speed = (int) (1.0 / fabs (op->speed));
631 root 1.3 }
632 root 1.14
633 root 1.5 if (anim_speed > 255)
634     anim_speed = 255;
635 root 1.3 }
636 root 1.14
637     sl << uint8 (anim_speed);
638 elmex 1.1 }
639 root 1.14
640 root 1.5 if (flags & UPD_NROF)
641 root 1.14 sl << uint32 (op->nrof);
642 elmex 1.1
643 root 1.29 pl->contr->ns->send_packet (sl);
644 elmex 1.1 }
645    
646     /**
647     * Sends item's info to player.
648     */
649 root 1.5 void
650     esrv_send_item (object *pl, object *op)
651 elmex 1.1 {
652 root 1.34 if (!pl->contr->ns)
653     return;
654    
655 root 1.63 /* We only send 'visible' objects to the client, and sometimes the player */
656     if (!op->client_visible () && op->type != PLAYER)
657     return;
658 elmex 1.1
659 root 1.16 packet sl;
660 elmex 1.1
661 root 1.29 sl.printf ("item%d ", pl->contr->ns->itemcmd);
662 elmex 1.1
663 root 1.63 op = op->head_ ();
664 elmex 1.1
665 root 1.14 sl << uint32 (op->env ? op->env->count : 0);
666 elmex 1.1
667 root 1.29 add_object_to_socklist (*pl->contr->ns, sl, op);
668 elmex 1.1
669 root 1.29 pl->contr->ns->send_packet (sl);
670 root 1.5 SET_FLAG (op, FLAG_CLIENT_SENT);
671 elmex 1.1 }
672    
673     /**
674 root 1.59 * Tells the client to delete an item.
675 elmex 1.1 */
676 root 1.5 void
677     esrv_del_item (player *pl, int tag)
678 elmex 1.1 {
679 root 1.36 if (!pl->ns)
680     return;
681    
682 root 1.27 packet sl ("delitem");
683 elmex 1.1
684 root 1.27 sl << uint32 (tag);
685 elmex 1.1
686 root 1.29 pl->ns->send_packet (sl);
687 elmex 1.1 }
688    
689    
690     /*******************************************************************************
691     *
692     * Client has requested us to do something with an object.
693     *
694     ******************************************************************************/
695    
696     /**
697     * Takes a player and object count (tag) and returns the actual object
698     * pointer, or null if it can't be found.
699     */
700 root 1.5 object *
701     esrv_get_ob_from_count (object *pl, tag_t count)
702 elmex 1.1 {
703 root 1.5 if (pl->count == count)
704     return pl;
705 elmex 1.1
706 root 1.44 for (object *op = pl->inv; op; op = op->below)
707 root 1.5 if (op->count == count)
708     return op;
709     else if (op->type == CONTAINER && pl->container == op)
710 root 1.44 for (object *tmp = op->inv; tmp; tmp = tmp->below)
711 root 1.5 if (tmp->count == count)
712     return tmp;
713    
714 root 1.44 for (object *op = GET_MAP_OB (pl->map, pl->x, pl->y); op; op = op->above)
715 root 1.24 if (op->head && op->head->count == count)
716 root 1.5 return op;
717     else if (op->count == count)
718     return op;
719     else if (op->type == CONTAINER && pl->container == op)
720 root 1.44 for (object *tmp = op->inv; tmp; tmp = tmp->below)
721 root 1.5 if (tmp->count == count)
722     return tmp;
723 elmex 1.1
724 root 1.44 #if 0
725     /* If the high bit is set, player examined a pseudo object. */
726     if (count & 0x80000000)
727     return 0;
728     #endif
729    
730 root 1.24 return 0;
731 elmex 1.1 }
732    
733     /** Client wants to examine some object. So lets do so. */
734 root 1.5 void
735     ExamineCmd (char *buf, int len, player *pl)
736 elmex 1.1 {
737 root 1.10 tag_t tag = atoi (buf);
738    
739 root 1.64 /* If the high bit is set, player applied a pseudo object. */
740     if (tag & 0x80000000)
741     {
742     pl->ns->look_position = tag & 0x7fffffff;
743     pl->ns->floorbox_update ();
744     return;
745     }
746    
747 root 1.5 object *op = esrv_get_ob_from_count (pl->ob, tag);
748 elmex 1.1
749 root 1.5 if (!op)
750     {
751     LOG (llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", &pl->ob->name, tag);
752     return;
753 elmex 1.1 }
754 root 1.10
755 root 1.5 examine (pl->ob, op);
756 elmex 1.1 }
757    
758 root 1.44 /** Client wants to examine some object. So lets do so. */
759     void
760     ExCmd (char *buf, int len, player *pl)
761     {
762     tag_t tag = atoi (buf);
763    
764     if (object *op = esrv_get_ob_from_count (pl->ob, tag))
765     {
766     std::string s = op->describe (pl->ob);
767    
768     packet sl ("ex");
769     sl << ber32 (tag) << s.c_str ();
770    
771     pl->ns->send_packet (sl);
772     }
773     }
774    
775 root 1.24 /** Client wants to apply some object. Lets do so. */
776 root 1.5 void
777     ApplyCmd (char *buf, int len, player *pl)
778 elmex 1.1 {
779 root 1.10 tag_t tag = atoi (buf);
780 elmex 1.1
781 root 1.5 /* If the high bit is set, player applied a pseudo object. */
782     if (tag & 0x80000000)
783     {
784 root 1.29 pl->ns->look_position = tag & 0x7fffffff;
785     pl->ns->floorbox_update ();
786 root 1.5 return;
787     }
788    
789 root 1.10 object *op = esrv_get_ob_from_count (pl->ob, tag);
790    
791 root 1.5 if (!op)
792     {
793     LOG (llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", &pl->ob->name, tag);
794     return;
795 elmex 1.1 }
796 root 1.10
797 root 1.5 player_apply (pl->ob, op, 0, 0);
798 elmex 1.1 }
799    
800 root 1.26 /** Client wants to lock some object. Lets do so. */
801 root 1.5 void
802 root 1.22 LockItem (char *data, int len, player *pl)
803 elmex 1.1 {
804 root 1.12 int flag = data[0];
805 root 1.22 tag_t tag = net_uint32 ((uint8 *)data + 1);
806 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
807 root 1.5
808     if (!op)
809     {
810 elmex 1.57 pl->failmsg ("Could not find object to lock/unlock");
811 root 1.5 return;
812     }
813 root 1.12
814 root 1.5 if (!flag)
815     CLEAR_FLAG (op, FLAG_INV_LOCKED);
816     else
817     SET_FLAG (op, FLAG_INV_LOCKED);
818 root 1.12
819 root 1.5 esrv_update_item (UPD_FLAGS, pl->ob, op);
820 elmex 1.1 }
821    
822 root 1.26 /** Client wants to mark some object. Lets do so. */
823 root 1.5 void
824 root 1.22 MarkItem (char *data, int len, player *pl)
825 elmex 1.1 {
826 root 1.22 tag_t tag = net_uint32 ((uint8 *)data);
827 root 1.12 object *op = esrv_get_ob_from_count (pl->ob, tag);
828 root 1.9
829 root 1.5 if (!op)
830     {
831 elmex 1.57 pl->failmsg ("Could not find object to mark");
832 root 1.5 return;
833     }
834 root 1.9
835 root 1.5 pl->mark = op;
836 elmex 1.57 pl->ob->statusmsg (format ("Marked item %s", query_name (op)));
837 elmex 1.1 }
838    
839     /**
840     * look_at prints items on the specified square.
841     *
842     * [ removed EARTHWALL check and added check for containers inventory.
843     * Tero.Haatanen@lut.fi ]
844     */
845 root 1.47 static void
846     look_at (player *pl, int dx, int dy)
847 root 1.5 {
848 root 1.53 dynbuf_text buf;
849 root 1.47 object *ob = pl->ob;
850 root 1.5
851 root 1.54 if (!pl->observe->map)
852     return;
853    
854 root 1.53 mapxy pos (pl->observe);
855     pos.move (dx, dy);
856 root 1.5
857 root 1.53 if (pos.normalise ())
858     for (object *tmp = pos->top; tmp; tmp = tmp->below)
859     {
860     if (tmp->invisible && !QUERY_FLAG (ob, FLAG_WIZ))
861     continue;
862 root 1.5
863 root 1.53 if (QUERY_FLAG (ob, FLAG_WIZ))
864     buf.printf ("- %s (%d).\n", query_name (tmp), tmp->count);
865     else
866     buf.printf ("- %s.\n", query_name (tmp));
867    
868     object *head = tmp->head_ ();
869    
870     if (head->inv)
871     if ((head->type != CONTAINER && head->type != FLESH)
872     || QUERY_FLAG (ob, FLAG_WIZ))
873     buf << head->query_inventory (ob, " ");
874 root 1.5
875 root 1.53 if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) && !QUERY_FLAG (ob, FLAG_WIZ)) /* don't continue under the floor */
876     break;
877     }
878 root 1.47
879 root 1.53 if (buf.empty ())
880     pl->failmsg ("You see nothing there.");
881     else
882     pl->infobox (MSG_CHANNEL ("lookat"), buf);
883 elmex 1.1 }
884    
885     /** Client wants to look at some object. Lets do so. */
886 root 1.5 void
887     LookAt (char *buf, int len, player *pl)
888 elmex 1.1 {
889 root 1.5 char *cp;
890 elmex 1.1
891 root 1.48 int dx = atoi (buf);
892 root 1.5 if (!(cp = strchr (buf, ' ')))
893 root 1.44 return;
894    
895 root 1.48 int dy = atoi (cp);
896 elmex 1.1
897 root 1.48 if (player *opl = pl->observe->contr)
898     if (client *ns = opl->ns)
899     {
900     if (fabs (dx) > ns->mapx / 2 || fabs (dy) > ns->mapy / 2)
901     return;
902    
903     if (opl->blocked_los[dx + ns->mapx / 2][dy + ns->mapy / 2])
904     return;
905     }
906 elmex 1.1
907 root 1.47 look_at (pl, dx, dy);
908 elmex 1.1 }
909    
910     /** Move an object to a new location */
911 root 1.5 void
912     esrv_move_object (object *pl, tag_t to, tag_t tag, long nrof)
913 elmex 1.1 {
914 root 1.5 object *op, *env;
915 elmex 1.1
916 root 1.5 op = esrv_get_ob_from_count (pl, tag);
917     if (!op)
918     {
919     LOG (llevDebug, "Player '%s' tried to move an unknown object (%ld)\n", &pl->name, tag);
920     return;
921 elmex 1.1 }
922    
923 root 1.5 if (!to)
924     { /* drop it to the ground */
925     if (op->map && !op->env)
926 root 1.14 return;
927 root 1.5
928     /* If it is an active container, then we should drop all objects
929     * in the container and not the container itself.
930     */
931     if (op->inv && QUERY_FLAG (op, FLAG_APPLIED))
932     {
933 elmex 1.56 int cnt = MAX_ITEM_PER_DROP;
934    
935 root 1.60 for (object *current = op->inv; current; )
936 root 1.5 {
937 root 1.60 object *next = current->below;
938    
939 root 1.5 drop_object (pl, current, 0);
940 elmex 1.56
941     if (--cnt <= 0) break;
942 root 1.60
943     current = next;
944 root 1.3 }
945 root 1.14
946 elmex 1.56 if (cnt <= 0)
947 elmex 1.57 op->failmsg ("Only dropped some items, can't drop that many items at once.");
948 root 1.3 }
949 root 1.5 else
950 root 1.24 drop_object (pl, op, nrof);
951    
952 root 1.5 return;
953     }
954     else if (to == pl->count)
955     { /* pick it up to the inventory */
956     /* return if player has already picked it up */
957     if (op->env == pl)
958 root 1.3 return;
959 root 1.5
960     pl->contr->count = nrof;
961     pick_up (pl, op);
962     return;
963     }
964 root 1.14
965 root 1.5 env = esrv_get_ob_from_count (pl, to);
966     if (!env)
967     {
968     LOG (llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", &pl->name, to);
969     return;
970     }
971 root 1.14
972 root 1.5 /* put_object_in_sack presumes that necessary sanity checking
973     * has already been done (eg, it can be picked up and fits in
974     * in a sack, so check for those things. We should also check
975     * an make sure env is in fact a container for that matter.
976     */
977 root 1.62 if (env->type == CONTAINER
978     && can_pick (pl, op)
979     && sack_can_hold (pl, env, op, nrof))
980     put_object_in_sack (pl, env, op, nrof);
981 elmex 1.1 }
982 root 1.14