Crossfire Server, Branch 1.12  R12190
item.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_item_c =
00003  *    "$Id: item.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007   CrossFire, A Multiplayer game for X-windows
00008 
00009   Copyright (C) 2002,2006 Mark Wedel & Crossfire Development Team
00010   Copyright (C) 1992 Frank Tore Johansen
00011 
00012   This program is free software; you can redistribute it and/or modify
00013   it under the terms of the GNU General Public License as published by
00014   the Free Software Foundation; either version 2 of the License, or
00015   (at your option) any later version.
00016 
00017   This program is distributed in the hope that it will be useful,
00018   but WITHOUT ANY WARRANTY; without even the implied warranty of
00019   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020   GNU General Public License for more details.
00021 
00022   You should have received a copy of the GNU General Public License
00023   along with this program; if not, write to the Free Software
00024   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026   The author can be reached via e-mail to crossfire-devel@real-time.com
00027 */
00028 
00040 #include <global.h>
00041 #include <object.h> /* LOOK_OBJ */
00042 #include <newclient.h>
00043 #include <newserver.h>
00044 #include <sproto.h>
00045 
00047 #define MAXITEMLEN  300
00048 
00049 /*************************************************************************
00050  *
00051  * Functions related to sending object data to the client.
00052  *
00053  *************************************************************************
00054  */
00055 
00060 static unsigned int query_flags(const object *op) {
00061     unsigned int flags = 0;
00062 
00063     if (QUERY_FLAG(op, FLAG_APPLIED)) {
00064         switch (op->type) {
00065         case BOW:
00066         case WAND:
00067         case ROD:
00068         case HORN:
00069             flags = a_readied;
00070             break;
00071 
00072         case WEAPON:
00073             flags = a_wielded;
00074             break;
00075 
00076         case SKILL:
00077         case ARMOUR:
00078         case HELMET:
00079         case SHIELD:
00080         case RING:
00081         case BOOTS:
00082         case GLOVES:
00083         case AMULET:
00084         case GIRDLE:
00085         case BRACERS:
00086         case CLOAK:
00087             flags = a_worn;
00088             break;
00089 
00090         case CONTAINER:
00091             flags = a_active;
00092             break;
00093 
00094         default:
00095             flags = a_applied;
00096             break;
00097         }
00098     }
00099     if (op->type == CONTAINER
00100     && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
00101         flags |= F_OPEN;
00102 
00103     if (QUERY_FLAG(op, FLAG_KNOWN_CURSED)) {
00104         if (QUERY_FLAG(op, FLAG_DAMNED))
00105             flags |= F_DAMNED;
00106         else if (QUERY_FLAG(op, FLAG_CURSED))
00107             flags |= F_CURSED;
00108     }
00109     if (QUERY_FLAG(op, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(op, FLAG_IDENTIFIED))
00110         flags |= F_MAGIC;
00111     if (QUERY_FLAG(op, FLAG_UNPAID))
00112         flags |= F_UNPAID;
00113     if (QUERY_FLAG(op, FLAG_INV_LOCKED))
00114         flags |= F_LOCKED;
00115     if (QUERY_FLAG(op, FLAG_KNOWN_BLESSED) && QUERY_FLAG(op, FLAG_BLESSED))
00116         flags |= F_BLESSED;
00117 
00118     return flags;
00119 }
00120 
00126 static void add_object_to_socklist(socket_struct *ns, SockList *sl, object *head) {
00127     int flags, len, anim_speed;
00128     char item_n[MAX_BUF], item_p[MAX_BUF];
00129 
00130     flags = query_flags(head);
00131     if (QUERY_FLAG(head, FLAG_NO_PICK))
00132         flags |= F_NOPICK;
00133 
00134     if (!(ns->faces_sent[head->face->number]&NS_FACESENT_FACE))
00135         esrv_send_face(ns, head->face->number, 0);
00136 
00137     if (QUERY_FLAG(head, FLAG_ANIMATE) && !ns->anims_sent[head->animation_id])
00138         esrv_send_animation(ns, head->animation_id);
00139 
00140     SockList_AddInt(sl, head->count);
00141     SockList_AddInt(sl, flags);
00142     SockList_AddInt(sl, QUERY_FLAG(head, FLAG_NO_PICK) ? -1 : WEIGHT(head));
00143     SockList_AddInt(sl, head->face->number);
00144 
00145     if (!head->custom_name) {
00146         query_base_name(head, 0, item_n, 126);
00147         item_n[127] = 0;
00148         len = strlen(item_n);
00149         query_base_name(head, 1, item_p, MAX_BUF);
00150     } else {
00151         strncpy(item_n, head->custom_name, 127);
00152         item_n[127] = 0;
00153         len = strlen(item_n);
00154         strncpy(item_p, head->custom_name, MAX_BUF);
00155     }
00156     strncpy(item_n+len+1, item_p, 127);
00157     /* This is needed because strncpy may not add a ending \0 if the string is long enough. */
00158     item_n[len+1+127] = 0;
00159     len += strlen(item_n+1+len)+1;
00160     SockList_AddLen8Data(sl, item_n, len);
00161 
00162     SockList_AddShort(sl, head->animation_id);
00163     anim_speed = 0;
00164     if (QUERY_FLAG(head, FLAG_ANIMATE)) {
00165         if (head->anim_speed)
00166             anim_speed = head->anim_speed;
00167         else {
00168             if (FABS(head->speed) < 0.001)
00169                 anim_speed = 255;
00170             else if (FABS(head->speed) >= 1.0)
00171                 anim_speed = 1;
00172             else
00173                 anim_speed = (int)(1.0/FABS(head->speed));
00174         }
00175         if (anim_speed > 255)
00176             anim_speed = 255;
00177     }
00178     SockList_AddChar(sl, (char)anim_speed);
00179     SockList_AddInt(sl, head->nrof);
00180 
00181     SockList_AddShort(sl, head->client_type);
00182 
00183     SET_FLAG(head, FLAG_CLIENT_SENT);
00184 }
00185 
00191 void esrv_draw_look(object *pl) {
00192     object *tmp, *last;
00193     int got_one = 0, start_look = 0, end_look = 0, objects_sent = 0;
00194     SockList sl;
00195     char buf[MAX_BUF];
00196 
00197     if (!pl->contr->socket.update_look) {
00198         LOG(llevDebug, "esrv_draw_look called when update_look was not set\n");
00199         return;
00200     } else {
00201         pl->contr->socket.update_look = 0;
00202     }
00203 
00204     if (QUERY_FLAG(pl, FLAG_REMOVED)
00205     || pl->map == NULL
00206     || pl->map->in_memory != MAP_IN_MEMORY
00207     || out_of_map(pl->map, pl->x, pl->y))
00208         return;
00209 
00210     if (pl->contr->transport)
00211         for (tmp = pl->contr->transport->inv; tmp && tmp->above; tmp = tmp->above)
00212             ;
00213     else
00214         for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp && tmp->above; tmp = tmp->above)
00215             ;
00216 
00217     SockList_Init(&sl);
00218     SockList_AddString(&sl, "delinv 0");
00219     Send_With_Handling(&pl->contr->socket, &sl);
00220 
00221     SockList_Reset(&sl);
00222     SockList_AddPrintf(&sl, "item2 ");
00223     SockList_AddInt(&sl, 0);
00224 
00225     if (!(pl->contr->socket.faces_sent[empty_face->number]&NS_FACESENT_FACE))
00226         esrv_send_face(&pl->contr->socket, empty_face->number, 0);
00227 
00228     if (pl->contr->socket.look_position) {
00229         int overhead = 1+(pl->contr->transport != NULL);
00230         int prev_len = pl->contr->socket.num_look_objects-overhead-(pl->contr->socket.look_position > pl->contr->socket.num_look_objects-overhead);
00231         SockList_AddInt(&sl, 0x80000000|MAX(0, pl->contr->socket.look_position-prev_len));
00232         SockList_AddInt(&sl, 0);
00233         SockList_AddInt(&sl, -1);
00234         SockList_AddInt(&sl, empty_face->number);
00235         snprintf(buf, sizeof(buf), "Click here to see previous group of items");
00236         SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
00237         SockList_AddShort(&sl, 0);
00238         SockList_AddChar(&sl, 0);
00239         SockList_AddInt(&sl, 0);
00240         SockList_AddShort(&sl, 0);
00241         objects_sent++;
00242         got_one++;
00243     }
00244 
00245     if (pl->contr->transport) {
00246         add_object_to_socklist(&pl->contr->socket, &sl, pl->contr->transport);
00247         objects_sent++;
00248         got_one++;
00249     }
00250 
00251     for (last = NULL; tmp != last; tmp = tmp->below) {
00252         object *head;
00253 
00254         if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !last) {
00255             last = tmp->below;  /* assumes double floor mode */
00256             if (last && QUERY_FLAG(last, FLAG_IS_FLOOR))
00257                 last = last->below;
00258         }
00259         if (LOOK_OBJ(tmp)) {
00260             if (start_look++ < pl->contr->socket.look_position)
00261                 continue;
00262             end_look++;
00263             objects_sent++;
00264             if (objects_sent >= pl->contr->socket.num_look_objects) {
00265                 /* What we basically do is make a 'fake' object -
00266                  * when the user applies it, we notice the special
00267                  * tag the object has, and act accordingly.
00268                  */
00269                 SockList_AddInt(&sl, 0x80000000|(pl->contr->socket.look_position+end_look-1));
00270                 SockList_AddInt(&sl, 0);
00271                 SockList_AddInt(&sl, -1);
00272                 SockList_AddInt(&sl, empty_face->number);
00273                 snprintf(buf, sizeof(buf), "Click here to see next group of items");
00274                 SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
00275                 SockList_AddShort(&sl, 0);
00276                 SockList_AddChar(&sl, 0);
00277                 SockList_AddInt(&sl, 0);
00278                 SockList_AddShort(&sl, 0);
00279                 break;
00280             }
00281             if (tmp->head)
00282                 head = tmp->head;
00283             else
00284                 head = tmp;
00285 
00286             add_object_to_socklist(&pl->contr->socket, &sl, head);
00287             got_one++;
00288 
00289             if (SockList_Avail(&sl) < MAXITEMLEN) {
00290                 Send_With_Handling(&pl->contr->socket, &sl);
00291                 SockList_Reset(&sl);
00292                 SockList_AddPrintf(&sl, "item2 ");
00293                 SockList_AddInt(&sl, 0);
00294                 got_one = 0;
00295             }
00296         } /* If LOOK_OBJ() */
00297     }
00298     if (got_one)
00299         Send_With_Handling(&pl->contr->socket, &sl);
00300 
00301     SockList_Term(&sl);
00302 }
00303 
00307 void esrv_send_inventory(object *pl, object *op) {
00308     object *tmp;
00309     int got_one = 0;
00310     SockList sl;
00311 
00312     SockList_Init(&sl);
00313     SockList_AddPrintf(&sl, "delinv %u", op->count);
00314     Send_With_Handling(&pl->contr->socket, &sl);
00315 
00316     SockList_Reset(&sl);
00317     SockList_AddString(&sl, "item2 ");
00318     SockList_AddInt(&sl, op->count);
00319 
00320     for (tmp = op->inv; tmp; tmp = tmp->below) {
00321         object *head;
00322 
00323         if (tmp->head)
00324             head = tmp->head;
00325         else
00326             head = tmp;
00327 
00328         if (LOOK_OBJ(head)) {
00329             add_object_to_socklist(&pl->contr->socket, &sl, head);
00330 
00331             got_one++;
00332 
00333             /* It is possible for players to accumulate a huge amount of
00334              * items (especially with some of the bags out there) to
00335              * overflow the buffer.  IF so, send multiple item commands.
00336              */
00337             if (SockList_Avail(&sl) < MAXITEMLEN) {
00338                 Send_With_Handling(&pl->contr->socket, &sl);
00339                 SockList_Reset(&sl);
00340                 SockList_AddString(&sl, "item2 ");
00341                 SockList_AddInt(&sl, op->count);
00342                 got_one = 0;
00343             }
00344         } /* If LOOK_OBJ() */
00345     }
00346     if (got_one)
00347         Send_With_Handling(&pl->contr->socket, &sl);
00348     SockList_Term(&sl);
00349 }
00350 
00359 void esrv_update_item(int flags, object *pl, object *op) {
00360     SockList sl;
00361 
00362     if (!pl->contr)
00363         return;
00364 
00365     /* If we have a request to send the player item, skip a few checks. */
00366     if (op != pl) {
00367         if (!LOOK_OBJ(op))
00368             return;
00369         /* we remove the check for op->env, because in theory, the object
00370          * is hopefully in the same place, so the client should preserve
00371          * order.
00372          */
00373     }
00374     if (!QUERY_FLAG(op, FLAG_CLIENT_SENT)) {
00375         /* FLAG_CLIENT_SENT is debug only.  We are using it to see where
00376          * this is happening - we can set a breakpoint here in the debugger
00377          * and track back the call.
00378          */
00379         LOG(llevDebug, "We have not sent item %s (%d)\n", op->name, op->count);
00380     }
00381 
00382     SockList_Init(&sl);
00383     SockList_AddString(&sl, "upditem ");
00384     SockList_AddChar(&sl, (char)flags);
00385 
00386     if (op->head)
00387         op = op->head;
00388 
00389     SockList_AddInt(&sl, op->count);
00390 
00391     if (flags&UPD_LOCATION)
00392         SockList_AddInt(&sl, op->env ? op->env->count : 0);
00393 
00394     if (flags&UPD_FLAGS)
00395         SockList_AddInt(&sl, query_flags(op));
00396 
00397     if (flags&UPD_WEIGHT) {
00398         sint32 weight = WEIGHT(op);
00399 
00400         /* TRANSPORTS are odd - they sort of look like containers,
00401          * yet can't be picked up.  So we don't to send the weight,
00402          * as it is odd that you see weight sometimes and not other
00403          * (the draw_look won't send it for example.
00404          */
00405         SockList_AddInt(&sl, QUERY_FLAG(op, FLAG_NO_PICK) ? -1 : weight);
00406         if (pl == op) {
00407             op->contr->last_weight = weight;
00408         }
00409     }
00410 
00411     if (flags&UPD_FACE) {
00412         if (!(pl->contr->socket.faces_sent[op->face->number]&NS_FACESENT_FACE))
00413             esrv_send_face(&pl->contr->socket, op->face->number, 0);
00414         SockList_AddInt(&sl, op->face->number);
00415     }
00416     if (flags&UPD_NAME) {
00417         int len;
00418         char item_p[MAX_BUF];
00419         char item_n[MAX_BUF];
00420 
00421         if (!op->custom_name) {
00422             query_base_name(op, 0, item_n, MAX_BUF);
00423             len = strlen(item_n);
00424             query_base_name(op, 1, item_p, MAX_BUF);
00425         } else {
00426             strncpy(item_n, op->custom_name, MAX_BUF-1);
00427             item_n[MAX_BUF-1] = 0;
00428             len = strlen(item_n);
00429             strncpy(item_p, op->custom_name, MAX_BUF-1);
00430             item_p[MAX_BUF-1] = 0;
00431         }
00432 
00433         strncpy(item_n+len+1, item_p, 127);
00434         item_n[254] = 0;
00435         len += strlen(item_n+1+len)+1;
00436         SockList_AddLen8Data(&sl, item_n, len);
00437     }
00438     if (flags&UPD_ANIM)
00439         SockList_AddShort(&sl, op->animation_id);
00440 
00441     if (flags&UPD_ANIMSPEED) {
00442         int anim_speed = 0;
00443 
00444         if (QUERY_FLAG(op, FLAG_ANIMATE)) {
00445             if (op->anim_speed)
00446                 anim_speed = op->anim_speed;
00447             else {
00448                 if (FABS(op->speed) < 0.001)
00449                     anim_speed = 255;
00450                 else if (FABS(op->speed) >= 1.0)
00451                     anim_speed = 1;
00452                 else
00453                     anim_speed = (int)(1.0/FABS(op->speed));
00454             }
00455             if (anim_speed > 255)
00456                 anim_speed = 255;
00457         }
00458         SockList_AddChar(&sl, (char)anim_speed);
00459     }
00460     if (flags&UPD_NROF)
00461         SockList_AddInt(&sl, op->nrof);
00462 
00463     Send_With_Handling(&pl->contr->socket, &sl);
00464     SockList_Term(&sl);
00465 }
00466 
00470 void esrv_send_item(object *pl, object*op) {
00471     SockList sl;
00472 
00473     /* If this is not the player object, do some more checks */
00474     if (op != pl) {
00475         /* We only send 'visibile' objects to the client */
00476         if (!LOOK_OBJ(op))
00477             return;
00478         /* if the item is on the ground, mark that the look needs to
00479          * be updated.
00480          */
00481         if (!op->env) {
00482             pl->contr->socket.update_look = 1;
00483             return;
00484         }
00485     }
00486 
00487     SockList_Init(&sl);
00488     SockList_AddString(&sl, "item2 ");
00489 
00490     if (op->head)
00491         op = op->head;
00492 
00493     SockList_AddInt(&sl, op->env ? op->env->count : 0);
00494 
00495     add_object_to_socklist(&pl->contr->socket, &sl, op);
00496 
00497     Send_With_Handling(&pl->contr->socket, &sl);
00498     SET_FLAG(op, FLAG_CLIENT_SENT);
00499     SockList_Term(&sl);
00500 }
00501 
00507 void esrv_del_item(player *pl, int tag) {
00508     SockList sl;
00509 
00510     SockList_Init(&sl);
00511     SockList_AddString(&sl, "delitem ");
00512     SockList_AddInt(&sl, tag);
00513     Send_With_Handling(&pl->socket, &sl);
00514     SockList_Term(&sl);
00515 }
00516 
00517 /**************************************************************************
00518  *
00519  * Client has requested us to do something with an object.
00520  *
00521  **************************************************************************
00522  */
00523 
00529 static object *esrv_get_ob_from_count(object *pl, tag_t count) {
00530     object *op, *tmp;
00531 
00532     if (pl->count == count)
00533         return pl;
00534 
00535     for (op = pl->inv; op; op = op->below)
00536         if (op->count == count)
00537             return op;
00538         else if (op->type == CONTAINER && pl->container == op)
00539             for (tmp = op->inv; tmp; tmp = tmp->below)
00540                 if (tmp->count == count)
00541                     return tmp;
00542 
00543     for (op = GET_MAP_OB(pl->map, pl->x, pl->y); op; op = op->above)
00544         if (op->head != NULL && op->head->count == count)
00545             return op;
00546         else if (op->count == count)
00547             return op;
00548         else if (op->type == CONTAINER && pl->container == op)
00549             for (tmp = op->inv; tmp; tmp = tmp->below)
00550                 if (tmp->count == count)
00551                     return tmp;
00552 
00553     if (pl->contr->transport) {
00554         for (tmp = pl->contr->transport->inv; tmp; tmp = tmp->below)
00555             if (tmp->count == count)
00556                 return tmp;
00557     }
00558     return NULL;
00559 }
00560 
00562 void examine_cmd(char *buf, int len, player *pl) {
00563     long tag;
00564     object *op;
00565 
00566     if (len <= 0 || !buf) {
00567         LOG(llevDebug, "Player '%s' sent bogus examine_cmd information\n", pl->ob->name);
00568         return;
00569     }
00570 
00571     tag = atoi(buf);
00572     op = esrv_get_ob_from_count(pl->ob, tag);
00573     if (!op) {
00574         LOG(llevDebug, "Player '%s' tried to examine the unknown object (%ld)\n", pl->ob->name, tag);
00575         return;
00576     }
00577     examine(pl->ob, op);
00578 }
00579 
00581 void apply_cmd(char *buf, int len, player *pl) {
00582     uint32 tag;
00583     object *op;
00584 
00585     if (!buf || len <= 0) {
00586         LOG(llevDebug, "Player '%s' sent bogus apply_cmd information\n", pl->ob->name);
00587         return;
00588     }
00589 
00590     tag = atoi(buf);
00591     op = esrv_get_ob_from_count(pl->ob, tag);
00592 
00593     /* sort of a hack, but if the player saves and the player then
00594      * manually applies a savebed (or otherwise tries to do stuff),
00595      * we run into trouble.
00596      */
00597     if (QUERY_FLAG(pl->ob, FLAG_REMOVED))
00598         return;
00599 
00600     /* If the high bit is set, player applied a pseudo object. */
00601     if (tag&0x80000000) {
00602         pl->socket.look_position = tag&0x7fffffff;
00603         pl->socket.update_look = 1;
00604         return;
00605     }
00606 
00607     if (!op) {
00608         LOG(llevDebug, "Player '%s' tried to apply the unknown object (%d)\n", pl->ob->name, tag);
00609         return;
00610     }
00611     player_apply(pl->ob, op, 0, 0);
00612 }
00613 
00615 void lock_item_cmd(uint8 *data, int len, player *pl) {
00616     int flag, tag;
00617     object *op;
00618     object *tmp;
00619 
00620     if (len != 5) {
00621         LOG(llevDebug, "Player '%s' sent bogus lock_item_cmd information\n", pl->ob->name);
00622         return;
00623     }
00624     flag = data[0];
00625     tag = GetInt_String(data+1);
00626     op = esrv_get_ob_from_count(pl->ob, tag);
00627 
00628     if (!op) {
00629         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00630                       "Could not find object to lock/unlock", NULL);
00631         return;
00632     }
00633     if (!flag)
00634         CLEAR_FLAG(op, FLAG_INV_LOCKED);
00635     else
00636         SET_FLAG(op, FLAG_INV_LOCKED);
00637 
00638     tmp = merge_ob(op, NULL);
00639     if (tmp == NULL) {
00640         /* object was not merged - if it was, merge_ob sent updates for us. */
00641         esrv_update_item(UPD_FLAGS, pl->ob, op);
00642     }
00643 }
00644 
00655 void mark_item_cmd(uint8 *data, int len, player *pl) {
00656     int tag;
00657     object *op;
00658     char name[MAX_BUF];
00659 
00660     if (len != 4) {
00661         LOG(llevDebug, "Player '%s' sent bogus mark_item_cmd information\n", pl->ob->name);
00662         return;
00663     }
00664 
00665     tag = GetInt_String(data);
00666     op = esrv_get_ob_from_count(pl->ob, tag);
00667     if (!op) {
00668         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00669                       "Could not find object to mark", NULL);
00670         return;
00671     }
00672     pl->mark = op;
00673     pl->mark_count = op->count;
00674     query_name(op, name, MAX_BUF);
00675     draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00676                          "Marked item %s",
00677                          "Marked item %s",
00678                          name);
00679 }
00680 
00687 void look_at(object *op, int dx, int dy) {
00688     object *tmp;
00689     int flag = 0;
00690     sint16 x, y;
00691     mapstruct *m;
00692     char name[MAX_BUF];
00693 
00694     if (out_of_map(op->map, op->x+dx, op->y+dy))
00695         return;
00696 
00697     x = op->x+dx;
00698     y = op->y+dy;
00699 
00700     m = get_map_from_coord(op->map, &x, &y);
00701     if (!m)
00702         return;
00703 
00704     for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above)
00705         ;
00706 
00707     for (; tmp != NULL; tmp = tmp->below) {
00708         if (tmp->invisible && !QUERY_FLAG(op, FLAG_WIZ))
00709             continue;
00710 
00711         if (!flag) {
00712             if (dx || dy)
00713                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00714                               "There you see:", NULL);
00715             else {
00716                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00717                               "You see:", NULL);
00718             }
00719             flag = 1;
00720         }
00721 
00722         query_name(tmp, name, MAX_BUF);
00723         if (QUERY_FLAG(op, FLAG_WIZ))
00724             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
00725                                  "- %s (%d).",
00726                                  "- %s (%d).",
00727                                  name, tmp->count);
00728         else
00729             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE,
00730                                  "- %s.",
00731                                  "- %s.",
00732                                  name);
00733 
00734         if (((tmp->inv != NULL || (tmp->head && tmp->head->inv)) && (tmp->type != CONTAINER && tmp->type != FLESH))
00735         || QUERY_FLAG(op, FLAG_WIZ))
00736             inventory(op, tmp->head == NULL ? tmp : tmp->head);
00737 
00738         /* don't continue under the floor */
00739         if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) && !QUERY_FLAG(op, FLAG_WIZ))
00740             break;
00741     }
00742 
00743     if (!flag) {
00744         if (dx || dy)
00745             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00746                           "You see nothing there.", NULL);
00747         else
00748             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
00749                           "You see nothing.", NULL);
00750     }
00751 }
00752 
00754 void look_at_cmd(char *buf, int len, player *pl) {
00755     int dx, dy;
00756     char *cp;
00757 
00758     dx = atoi(buf);
00759     if (!(cp = strchr(buf, ' '))) {
00760         return;
00761     }
00762     dy = atoi(cp);
00763 
00764     if (FABS(dx) > MAP_CLIENT_X/2 || FABS(dy) > MAP_CLIENT_Y/2)
00765         return;
00766 
00767     if (pl->blocked_los[dx+(pl->socket.mapx/2)][dy+(pl->socket.mapy/2)])
00768         return;
00769     look_at(pl->ob, dx, dy);
00770 }
00771 
00773 void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof) {
00774     object *op, *env;
00775 
00776     op = esrv_get_ob_from_count(pl, tag);
00777     if (!op) {
00778         LOG(llevDebug, "Player '%s' tried to move an unknown object (%lu)\n", pl->name, (unsigned long)tag);
00779         return;
00780     }
00781 
00782     /* If on a transport, you don't drop to the ground - you drop to the
00783      * transport.
00784      */
00785     if (!to && !pl->contr->transport) { /* drop it to the ground */
00786         /*  LOG(llevDebug, "Drop it on the ground.\n");*/
00787 
00788         if (op->map && !op->env) {
00789 /*          LOG(llevDebug, "Dropping object to ground that is already on ground\n");*/
00790             return;
00791         }
00792         /* If it is an active container, then we should drop all objects
00793          * in the container and not the container itself.
00794          */
00795         if (op->inv && QUERY_FLAG(op, FLAG_APPLIED)) {
00796             object *current, *next;
00797 
00798             for (current = op->inv; current != NULL; current = next) {
00799                 next = current->below;
00800                 drop_object(pl, current, 0);
00801             }
00802             esrv_update_item(UPD_WEIGHT, pl, op);
00803         } else {
00804             drop_object(pl, op, nrof);
00805         }
00806         return;
00807     } else if (to == pl->count) {     /* pick it up to the inventory */
00808         /* return if player has already picked it up */
00809         if (op->env == pl)
00810             return;
00811 
00812         pl->contr->count = nrof;
00813         pick_up(pl, op);
00814         return;
00815     }
00816     /* If not dropped or picked up, we are putting it into a sack */
00817     if (pl->contr->transport) {
00818         if (can_pick(pl, op)
00819         && transport_can_hold(pl->contr->transport, op, nrof)) {
00820             put_object_in_sack(pl, pl->contr->transport, op, nrof);
00821         }
00822     } else {
00823         env = esrv_get_ob_from_count(pl, to);
00824         if (!env) {
00825             LOG(llevDebug, "Player '%s' tried to move object to the unknown location (%d)\n", pl->name, to);
00826             return;
00827         }
00828         /* put_object_in_sack presumes that necessary sanity checking
00829          * has already been done (eg, it can be picked up and fits in
00830          * in a sack, so check for those things.  We should also check
00831          * an make sure env is in fact a container for that matter.
00832          */
00833         if (env->type == CONTAINER
00834         && can_pick(pl, op)
00835         && sack_can_hold(pl, env, op, nrof)) {
00836             put_object_in_sack(pl, env, op, nrof);
00837         }
00838     }
00839 }
00840 
00841 void inscribe_scroll_cmd(char *buf, int len, player *pl) {
00842     object *scroll, *spell, *marked, *inscription, *currentspell;
00843     tag_t tscroll, tspell, tmarked;
00844     char type;
00845 
00846     if (len < 1) {
00847         LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
00848         return;
00849     }
00850 
00851     type = buf[0];
00852 
00853     inscription = find_skill_by_name(pl->ob, "inscription");
00854     if (!inscription) {
00855         draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, "You don't know how to write!", NULL);
00856         return;
00857     }
00858 
00859     if (type == 0) {
00860         if (len != 9) {
00861             LOG(llevDebug, "Player %s sent an invalid inscribe command.\n", pl->ob->name);
00862             return;
00863         }
00864         tscroll = GetInt_String((uint8 *)buf+1);
00865         tspell = GetInt_String((uint8 *)buf+5);
00866 
00867         scroll = esrv_get_ob_from_count(pl->ob, tscroll);
00868         if (!scroll) {
00869             LOG(llevDebug, "Player %s sent an invalid scroll for inscribe command.\n", pl->ob->name);
00870             return;
00871         }
00872 
00873         spell = esrv_get_ob_from_count(pl->ob, tspell);
00874         if (!spell) {
00875             LOG(llevDebug, "Player %s sent an invalid spell for inscribe command.\n", pl->ob->name);
00876             return;
00877         }
00878 
00879         tmarked = pl->mark_count;
00880         marked = pl->mark;
00881         currentspell = pl->ranges[range_magic];
00882 
00883         pl->mark_count = tscroll;
00884         pl->mark = scroll;
00885         pl->ranges[range_magic] = spell;
00886 
00887         write_on_item(pl->ob, "", inscription);
00888 
00889         pl->mark_count = tmarked;
00890         pl->mark = marked;
00891         pl->ranges[range_magic] = currentspell;
00892     } else {
00893     }
00894 }