Atrinik Server 2.5
socket/item.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00032 #include <global.h>
00033 
00034 static object *esrv_get_ob_from_count_DM(object *pl, tag_t count);
00035 static int check_container(object *pl, object *con);
00036 
00038 #define MAXITEMLEN 300
00039 
00045 unsigned int query_flags(object *op)
00046 {
00047     unsigned int flags = 0;
00048 
00049     if (QUERY_FLAG(op, FLAG_APPLIED))
00050     {
00051         switch (op->type)
00052         {
00053             case BOW:
00054             case WAND:
00055             case ROD:
00056             case HORN:
00057                 flags = a_readied;
00058                 break;
00059 
00060             case WEAPON:
00061                 flags = a_wielded;
00062                 break;
00063 
00064             case SKILL:
00065             case ARMOUR:
00066             case HELMET:
00067             case SHIELD:
00068             case RING:
00069             case BOOTS:
00070             case GLOVES:
00071             case AMULET:
00072             case GIRDLE:
00073             case BRACERS:
00074             case CLOAK:
00075                 flags = a_worn;
00076                 break;
00077 
00078             case CONTAINER:
00079                 flags = a_active;
00080                 break;
00081 
00082             default:
00083                 flags = a_applied;
00084                 break;
00085         }
00086     }
00087 
00088     if (op->type == CONTAINER && (op->attacked_by || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
00089     {
00090         flags |= F_OPEN;
00091     }
00092 
00093     if (QUERY_FLAG(op, FLAG_IS_TRAPPED))
00094     {
00095         flags |= F_TRAPPED;
00096     }
00097 
00098     if (QUERY_FLAG(op, FLAG_IDENTIFIED) || QUERY_FLAG(op, FLAG_APPLIED))
00099     {
00100         if (QUERY_FLAG(op, FLAG_DAMNED))
00101         {
00102             flags |= F_DAMNED;
00103         }
00104         else if (QUERY_FLAG(op, FLAG_CURSED))
00105         {
00106             flags |= F_CURSED;
00107         }
00108     }
00109 
00110     if (QUERY_FLAG(op, FLAG_IS_MAGICAL) && QUERY_FLAG(op, FLAG_IDENTIFIED))
00111     {
00112         flags |= F_MAGIC;
00113     }
00114 
00115     if (QUERY_FLAG(op, FLAG_UNPAID))
00116     {
00117         flags |= F_UNPAID;
00118     }
00119 
00120     if (QUERY_FLAG(op, FLAG_INV_LOCKED))
00121     {
00122         flags |= F_LOCKED;
00123     }
00124 
00125     if (QUERY_FLAG(op, FLAG_IS_INVISIBLE))
00126     {
00127         flags |= F_INVISIBLE;
00128     }
00129 
00130     if (QUERY_FLAG(op, FLAG_IS_ETHEREAL))
00131     {
00132         flags |= F_ETHEREAL;
00133     }
00134 
00135     if (QUERY_FLAG(op, FLAG_NO_PICK))
00136     {
00137         flags |= F_NOPICK;
00138     }
00139 
00140     return flags;
00141 }
00142 
00149 static void add_object_to_socklist(SockList *sl, object *op, object *pl, uint32 flags)
00150 {
00151     SockList_AddInt(sl, op->count);
00152 
00153     if (flags & UPD_LOCATION)
00154     {
00155         SockList_AddInt(sl, op->env ? op->env->count : 0);
00156     }
00157 
00158     if (flags & UPD_FLAGS)
00159     {
00160         SockList_AddInt(sl, query_flags(op));
00161     }
00162 
00163     if (flags & UPD_WEIGHT)
00164     {
00165         SockList_AddInt(sl, WEIGHT(op));
00166     }
00167 
00168     if (flags & UPD_FACE)
00169     {
00170         if (op->inv_face && QUERY_FLAG(op, FLAG_IDENTIFIED))
00171         {
00172             SockList_AddInt(sl, op->inv_face->number);
00173         }
00174         else
00175         {
00176             SockList_AddInt(sl, op->face->number);
00177         }
00178     }
00179 
00180     if (flags & UPD_DIRECTION)
00181     {
00182         SockList_AddChar(sl, op->facing);
00183     }
00184 
00185     if (flags & UPD_TYPE)
00186     {
00187         SockList_AddChar(sl, op->type);
00188         SockList_AddChar(sl, op->sub_type);
00189 
00190         if (QUERY_FLAG(op, FLAG_IDENTIFIED))
00191         {
00192             SockList_AddChar(sl, op->item_quality);
00193             SockList_AddChar(sl, op->item_condition);
00194             SockList_AddChar(sl, op->item_level);
00195             SockList_AddChar(sl, op->item_skill);
00196         }
00197         else
00198         {
00199             SockList_AddChar(sl, (char) 255);
00200             SockList_AddChar(sl, (char) 255);
00201             SockList_AddChar(sl, (char) 255);
00202             SockList_AddChar(sl, (char) 255);
00203         }
00204     }
00205 
00206     if (flags & UPD_NAME)
00207     {
00208         size_t len;
00209         char item_name[MAX_BUF];
00210 
00211         if (op->custom_name)
00212         {
00213             memcpy(item_name, op->custom_name, 127);
00214         }
00215         else
00216         {
00217             memcpy(item_name, query_base_name(op, pl), 127);
00218         }
00219 
00220         item_name[127] = '\0';
00221         len = strlen(item_name);
00222         SockList_AddLen8Data(sl, item_name, len);
00223     }
00224 
00225     if (flags & UPD_ANIM)
00226     {
00227         if (!(flags & UPD_ANIM_NO_INV) && op->inv_animation_id)
00228         {
00229             SockList_AddShort(sl, op->inv_animation_id);
00230         }
00231         else
00232         {
00233             SockList_AddShort(sl, op->animation_id);
00234         }
00235     }
00236 
00237     if (flags & UPD_ANIMSPEED)
00238     {
00239         int anim_speed = 0;
00240 
00241         if (QUERY_FLAG(op, FLAG_ANIMATE))
00242         {
00243             if (op->anim_speed)
00244             {
00245                 anim_speed = op->anim_speed;
00246             }
00247             else
00248             {
00249                 if (FABS(op->speed) < 0.001)
00250                 {
00251                     anim_speed = 255;
00252                 }
00253                 else if (FABS(op->speed) >= 1.0)
00254                 {
00255                     anim_speed = 1;
00256                 }
00257                 else
00258                 {
00259                     anim_speed = (int) (1.0 / FABS(op->speed));
00260                 }
00261             }
00262 
00263             if (anim_speed > 255)
00264             {
00265                 anim_speed = 255;
00266             }
00267         }
00268 
00269         SockList_AddChar(sl, (char) anim_speed);
00270     }
00271 
00272     if (flags & UPD_NROF)
00273     {
00274         SockList_AddInt(sl, op->nrof);
00275     }
00276 }
00277 
00284 static int esrv_draw_look_rec(object *pl, SockList *sl, object *op)
00285 {
00286     char buf[MAX_BUF];
00287     object *tmp;
00288     int got_one = 0;
00289 
00290     SockList_AddInt(sl, 0);
00291     SockList_AddInt(sl, 0);
00292     SockList_AddInt(sl, -1);
00293     SockList_AddInt(sl, (uint32) blank_face->number);
00294     SockList_AddChar(sl, 0);
00295     strncpy(buf, "in inventory", sizeof(buf));
00296     buf[sizeof(buf) - 1] = '\0';
00297     SockList_AddLen8Data(sl, buf, MIN(strlen(buf), 255));
00298     SockList_AddShort(sl, 0);
00299     SockList_AddChar(sl, 0);
00300     SockList_AddInt(sl, 0);
00301 
00302     for (tmp = op->inv; tmp; tmp = tmp->below)
00303     {
00304         add_object_to_socklist(sl, HEAD(tmp), pl, UPD_FLAGS | UPD_WEIGHT | UPD_FACE | UPD_DIRECTION | UPD_NAME | UPD_ANIM | UPD_ANIM_NO_INV | UPD_ANIMSPEED | UPD_NROF);
00305         got_one++;
00306 
00307         if (sl->len > (MAXSOCKBUF - MAXITEMLEN))
00308         {
00309             Send_With_Handling(&CONTR(pl)->socket, sl);
00310             SOCKET_SET_BINARY_CMD(sl, BINARY_CMD_ITEMY);
00311             SockList_AddInt(sl, -2);
00312             SockList_AddInt(sl, 0);
00313             got_one = 0;
00314         }
00315 
00316         if (tmp->inv && tmp->type != PLAYER)
00317         {
00318             got_one = esrv_draw_look_rec(pl, sl, tmp);
00319         }
00320     }
00321 
00322     SockList_AddInt(sl, 0);
00323     SockList_AddInt(sl, 0);
00324     SockList_AddInt(sl, -1);
00325     SockList_AddInt(sl, (uint32) blank_face->number);
00326     SockList_AddChar(sl, 0);
00327     strncpy(buf, "end inventory", sizeof(buf));
00328     buf[sizeof(buf) - 1] = '\0';
00329     SockList_AddLen8Data(sl, buf, MIN(strlen(buf), 255));
00330     SockList_AddShort(sl, 0);
00331     SockList_AddChar(sl, 0);
00332     SockList_AddInt(sl, 0);
00333     return got_one;
00334 }
00335 
00342 void esrv_draw_look(object *pl)
00343 {
00344     socket_struct *ns = &CONTR(pl)->socket;
00345     char buf[MAX_BUF];
00346     object *tmp, *last;
00347     int got_one = 0, start_look = 0, end_look = 0, wiz;
00348     SockList sl;
00349 
00350     if (QUERY_FLAG(pl, FLAG_REMOVED) || pl->map == NULL || pl->map->in_memory != MAP_IN_MEMORY || OUT_OF_REAL_MAP(pl->map, pl->x, pl->y))
00351     {
00352         return;
00353     }
00354 
00355     wiz = QUERY_FLAG(pl, FLAG_WIZ);
00356     /* Grab last (top) object without browsing the objects. */
00357     tmp = GET_MAP_OB_LAST(pl->map, pl->x, pl->y);
00358 
00359     sl.buf = malloc(MAXSOCKBUF);
00360 
00361     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMY);
00362 
00363     SockList_AddInt(&sl, 0);
00364     SockList_AddInt(&sl, 0);
00365 
00366     if (CONTR(pl)->socket.look_position)
00367     {
00368         SockList_AddInt(&sl, 0x80000000 | (CONTR(pl)->socket.look_position - NUM_LOOK_OBJECTS));
00369         SockList_AddInt(&sl, 0);
00370         SockList_AddInt(&sl, -1);
00371         SockList_AddInt(&sl, prev_item_face->number);
00372         SockList_AddChar(&sl, 0);
00373         snprintf(buf, sizeof(buf), "Apply to see %d previous items", NUM_LOOK_OBJECTS);
00374         SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
00375         SockList_AddShort(&sl, 0);
00376         SockList_AddChar(&sl, 0);
00377         SockList_AddInt(&sl, 0);
00378     }
00379 
00380     for (last = NULL; tmp != last; tmp = tmp->below)
00381     {
00382         if (tmp == pl)
00383         {
00384             continue;
00385         }
00386 
00387         /* Skip map mask, sys_objects and invisible objects when we can't
00388          * see them. */
00389         if (tmp->layer <= LAYER_FMASK || IS_SYS_INVISIBLE(tmp) || (!QUERY_FLAG(pl, FLAG_SEE_INVISIBLE) && QUERY_FLAG(tmp, FLAG_IS_INVISIBLE)))
00390         {
00391             /* But only when we are not a DM */
00392             if (!QUERY_FLAG(pl, FLAG_WIZ))
00393             {
00394                 continue;
00395             }
00396         }
00397 
00398         if (++start_look < CONTR(pl)->socket.look_position)
00399         {
00400             continue;
00401         }
00402 
00403         /* If we have too many items to send, send a 'next group' object
00404          * and leave here. */
00405         if (++end_look > NUM_LOOK_OBJECTS)
00406         {
00407             SockList_AddInt(&sl, 0x80000000 | (CONTR(pl)->socket.look_position + NUM_LOOK_OBJECTS));
00408             SockList_AddInt(&sl, 0);
00409             SockList_AddInt(&sl, -1);
00410             SockList_AddInt(&sl, next_item_face->number);
00411             SockList_AddChar(&sl, 0);
00412             snprintf(buf, sizeof(buf), "Apply to see next group of items");
00413             SockList_AddLen8Data(&sl, buf, MIN(strlen(buf), 255));
00414             SockList_AddShort(&sl, 0);
00415             SockList_AddChar(&sl, 0);
00416             SockList_AddInt(&sl, 0);
00417             break;
00418         }
00419 
00420         add_object_to_socklist(&sl, HEAD(tmp), pl, UPD_FLAGS | UPD_WEIGHT | UPD_FACE | UPD_DIRECTION | UPD_NAME | UPD_ANIM | UPD_ANIM_NO_INV | UPD_ANIMSPEED | UPD_NROF);
00421         got_one++;
00422 
00423         if (sl.len > (MAXSOCKBUF - MAXITEMLEN))
00424         {
00425             Send_With_Handling(ns, &sl);
00426             SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMY);
00427             /* do no delinv */
00428             SockList_AddInt(&sl, -2);
00429             SockList_AddInt(&sl, 0);
00430             got_one = 0;
00431         }
00432 
00433         if (wiz && tmp->inv && tmp->type != PLAYER)
00434         {
00435             got_one = esrv_draw_look_rec(pl, &sl, tmp);
00436         }
00437     }
00438 
00439     if (got_one || (!got_one && !ns->below_clear))
00440     {
00441         Send_With_Handling(ns, &sl);
00442         ns->below_clear = 0;
00443     }
00444 
00445     free(sl.buf);
00446 }
00447 
00451 void esrv_close_container(object *op)
00452 {
00453     SockList sl;
00454 
00455     sl.buf = malloc(256);
00456     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMX);
00457     /* Container mode flag */
00458     SockList_AddInt(&sl, -1);
00459     SockList_AddInt(&sl, -1);
00460 
00461     Send_With_Handling(&CONTR(op)->socket, &sl);
00462     free(sl.buf);
00463 }
00464 
00470 void esrv_send_inventory(object *pl, object *op)
00471 {
00472     object *tmp;
00473     int got_one = 0;
00474     SockList sl;
00475 
00476     sl.buf = malloc(MAXSOCKBUF);
00477 
00478     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMY);
00479 
00480     /* In this case we're sending a container inventory */
00481     if (pl != op)
00482     {
00483         /* Container mode flag */
00484         SockList_AddInt(&sl, -1);
00485     }
00486     else
00487     {
00488         SockList_AddInt(&sl, op->count);
00489     }
00490 
00491     SockList_AddInt(&sl, op->count);
00492 
00493     for (tmp = op->inv; tmp; tmp = tmp->below)
00494     {
00495         if (!QUERY_FLAG(pl, FLAG_SEE_INVISIBLE) && QUERY_FLAG(tmp, FLAG_IS_INVISIBLE))
00496         {
00497             /* Skip this for DMs */
00498             if (!QUERY_FLAG(pl, FLAG_WIZ))
00499             {
00500                 continue;
00501             }
00502         }
00503 
00504         if (LOOK_OBJ(tmp) || QUERY_FLAG(pl, FLAG_WIZ))
00505         {
00506             add_object_to_socklist(&sl, tmp, pl, UPD_FLAGS | UPD_WEIGHT | UPD_FACE | UPD_DIRECTION | UPD_TYPE | UPD_NAME | UPD_ANIM | UPD_ANIMSPEED | UPD_NROF);
00507             got_one++;
00508 
00509             /* It is possible for players to accumulate a huge amount of
00510              * items (especially with some of the bags out there) to
00511              * overflow the buffer. If so, send multiple item1
00512              * commands. */
00513             if (sl.len > (MAXSOCKBUF - MAXITEMLEN))
00514             {
00515                 Send_With_Handling(&CONTR(pl)->socket, &sl);
00516                 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMY);
00517                 /* no del inv */
00518                 SockList_AddInt(&sl, -3);
00519                 SockList_AddInt(&sl, op->count);
00520                 got_one = 0;
00521             }
00522         }
00523     }
00524 
00525     /* Container can be empty */
00526     if (got_one || pl != op)
00527     {
00528         Send_With_Handling(&CONTR(pl)->socket, &sl);
00529     }
00530 
00531     free(sl.buf);
00532 }
00533 
00539 static void esrv_update_item_send(int flags, object *pl, object *op)
00540 {
00541     SockList sl;
00542 
00543     /* If we have a request to send the player item, skip a few checks. */
00544     if (op != pl)
00545     {
00546         if (!LOOK_OBJ(op) && !QUERY_FLAG(pl, FLAG_WIZ))
00547         {
00548             return;
00549         }
00550     }
00551 
00552     sl.buf = malloc(MAXSOCKBUF);
00553 
00554     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_UPITEM);
00555     SockList_AddShort(&sl, (uint16) flags);
00556     add_object_to_socklist(&sl, op, pl, flags);
00557     Send_With_Handling(&CONTR(pl)->socket, &sl);
00558     free(sl.buf);
00559 }
00560 
00566 void esrv_update_item(int flags, object *pl, object *op)
00567 {
00568     object *tmp;
00569 
00570     /* Update something in a container. */
00571     if (op->env && op->env->type == CONTAINER)
00572     {
00573         for (tmp = op->env->attacked_by; tmp; tmp = CONTR(tmp)->container_above)
00574         {
00575             esrv_update_item_send(flags, tmp, op);
00576         }
00577 
00578         return;
00579     }
00580 
00581     esrv_update_item_send(flags, pl, op);
00582 }
00583 
00588 static void esrv_send_item_send(object *pl, object *op)
00589 {
00590     SockList sl;
00591 
00592     /* If this is not the player object, do some more checks. */
00593     if (op != pl)
00594     {
00595         /* We only send 'visible' objects to the client. */
00596         if (!LOOK_OBJ(op))
00597         {
00598             return;
00599         }
00600     }
00601 
00602     sl.buf = malloc(MAXSOCKBUF);
00603 
00604     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_ITEMX);
00605     /* no delinv */
00606     SockList_AddInt(&sl, -4);
00607     SockList_AddInt(&sl, op->env ? op->env->count : 0);
00608 
00609     /* If not below */
00610     if (op->env)
00611     {
00612         add_object_to_socklist(&sl, HEAD(op), pl, UPD_FLAGS | UPD_WEIGHT | UPD_FACE | UPD_DIRECTION | UPD_TYPE | UPD_NAME | UPD_ANIM | UPD_ANIMSPEED | UPD_NROF);
00613     }
00614     else
00615     {
00616         add_object_to_socklist(&sl, HEAD(op), pl, UPD_FLAGS | UPD_WEIGHT | UPD_FACE | UPD_DIRECTION | UPD_NAME | UPD_ANIM | UPD_ANIM_NO_INV | UPD_ANIMSPEED | UPD_NROF);
00617     }
00618 
00619     Send_With_Handling(&CONTR(pl)->socket, &sl);
00620     free(sl.buf);
00621 }
00622 
00627 void esrv_send_item(object *pl, object *op)
00628 {
00629     object *tmp;
00630 
00631     /* Update something in a container. */
00632     if (op->env && op->env->type == CONTAINER)
00633     {
00634         for (tmp = op->env->attacked_by; tmp; tmp = CONTR(tmp)->container_above)
00635         {
00636             esrv_send_item_send(tmp, op);
00637         }
00638 
00639         return;
00640     }
00641 
00642     if (pl->type != PLAYER)
00643     {
00644         LOG(llevBug, "esrv_send_item(): called for non PLAYER/CONTAINER object! (%s) (%s)\n", query_name(pl, NULL), query_name(op, NULL));
00645         return;
00646     }
00647 
00648     esrv_send_item_send(pl, op);
00649 }
00650 
00655 static void esrv_del_item_send(player *pl, int tag)
00656 {
00657     SockList sl;
00658 
00659     sl.buf = malloc(MAXSOCKBUF);
00660 
00661     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_DELITEM);
00662     SockList_AddInt(&sl, tag);
00663 
00664     Send_With_Handling(&pl->socket, &sl);
00665     free(sl.buf);
00666 }
00667 
00673 void esrv_del_item(player *pl, int tag, object *cont)
00674 {
00675     object *tmp;
00676 
00677     if (cont && cont->type == CONTAINER)
00678     {
00679         for (tmp = cont->attacked_by; tmp; tmp = CONTR(tmp)->container_above)
00680         {
00681             esrv_del_item_send(CONTR(tmp), tag);
00682         }
00683 
00684         return;
00685     }
00686 
00687     esrv_del_item_send(pl, tag);
00688 }
00689 
00696 object *esrv_get_ob_from_count(object *pl, tag_t count)
00697 {
00698     object *op, *tmp;
00699 
00700     /* Easy case */
00701     if (pl->count == count)
00702     {
00703         return pl;
00704     }
00705 
00706     /* Special case, we can examine deep inside every inventory even from
00707      * non containers. */
00708     if (QUERY_FLAG(pl, FLAG_WIZ))
00709     {
00710         for (op = pl->inv; op; op = op->below)
00711         {
00712             if (op->count == count)
00713             {
00714                 return op;
00715             }
00716             else if (op->inv)
00717             {
00718                 if ((tmp = esrv_get_ob_from_count_DM(op->inv, count)))
00719                 {
00720                     return tmp;
00721                 }
00722             }
00723         }
00724 
00725         for (op = get_map_ob(pl->map, pl->x, pl->y); op; op = op->above)
00726         {
00727             if (op->count == count)
00728             {
00729                 return op;
00730             }
00731             else if (op->inv)
00732             {
00733                 if ((tmp = esrv_get_ob_from_count_DM(op->inv, count)))
00734                 {
00735                     return tmp;
00736                 }
00737             }
00738         }
00739 
00740         return NULL;
00741     }
00742 
00743     if (pl->count == count)
00744     {
00745         return pl;
00746     }
00747 
00748     for (op = pl->inv; op; op = op->below)
00749     {
00750         if (op->count == count)
00751         {
00752             return op;
00753         }
00754         else if (op->type == CONTAINER && CONTR(pl)->container == op)
00755         {
00756             for (tmp = op->inv; tmp; tmp = tmp->below)
00757             {
00758                 if (tmp->count == count)
00759                 {
00760                     return tmp;
00761                 }
00762             }
00763         }
00764     }
00765 
00766     for (op = get_map_ob(pl->map, pl->x, pl->y); op; op = op->above)
00767     {
00768         if (op->count == count)
00769         {
00770             return op;
00771         }
00772         else if (op->type == CONTAINER && CONTR(pl)->container == op)
00773         {
00774             for (tmp = op->inv; tmp; tmp = tmp->below)
00775             {
00776                 if (tmp->count == count)
00777                 {
00778                     return tmp;
00779                 }
00780             }
00781         }
00782     }
00783 
00784     return NULL;
00785 }
00786 
00792 static object *esrv_get_ob_from_count_DM(object *pl, tag_t count)
00793 {
00794     object *tmp, *op;
00795 
00796     for (op = pl; op; op = op->below)
00797     {
00798         if (op->count == count)
00799         {
00800             return op;
00801         }
00802         else if (op->inv)
00803         {
00804             if ((tmp = esrv_get_ob_from_count_DM(op->inv, count)))
00805             {
00806                 return tmp;
00807             }
00808         }
00809     }
00810 
00811     return NULL;
00812 }
00813 
00819 void ExamineCmd(char *buf, int len, player *pl)
00820 {
00821     long tag;
00822     object *op;
00823 
00824     if (!buf || !len)
00825     {
00826         return;
00827     }
00828 
00829     tag = atoi(buf);
00830     op = esrv_get_ob_from_count(pl->ob, tag);
00831 
00832     if (!op)
00833     {
00834         return;
00835     }
00836 
00837     examine(pl->ob, op);
00838 }
00839 
00844 static void remove_quickslot(uint8 slot, player *pl)
00845 {
00846     object *tmp;
00847 
00848     if (pl->spell_quickslots[slot - 1] != SP_NO_SPELL)
00849     {
00850         pl->spell_quickslots[slot - 1] = SP_NO_SPELL;
00851         return;
00852     }
00853 
00854     for (tmp = pl->ob->inv; tmp; tmp = tmp->below)
00855     {
00856         if (tmp->quickslot && tmp->quickslot == slot)
00857         {
00858             tmp->quickslot = 0;
00859         }
00860     }
00861 }
00862 
00866 void send_quickslots(player *pl)
00867 {
00868     SockList sl;
00869     unsigned char buf[MAXSOCKBUF];
00870     object *tmp;
00871     uint8 i;
00872 
00873     sl.buf = buf;
00874     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_QUICKSLOT);
00875 
00876     for (tmp = pl->ob->inv; tmp; tmp = tmp->below)
00877     {
00878         if (tmp->quickslot)
00879         {
00880             SockList_AddChar(&sl, QUICKSLOT_TYPE_ITEM);
00881             SockList_AddChar(&sl, tmp->quickslot - 1);
00882             SockList_AddInt(&sl, tmp->count);
00883         }
00884     }
00885 
00886     for (i = 0; i < MAX_QUICKSLOT; i++)
00887     {
00888         if (pl->spell_quickslots[i] != SP_NO_SPELL)
00889         {
00890             SockList_AddChar(&sl, QUICKSLOT_TYPE_SPELL);
00891             SockList_AddChar(&sl, i);
00892             SockList_AddString(&sl, spells[pl->spell_quickslots[i]].name);
00893         }
00894     }
00895 
00896     Send_With_Handling(&pl->socket, &sl);
00897 }
00898 
00904 void QuickSlotCmd(uint8 *buf, int len, player *pl)
00905 {
00906     uint8 command, quickslot;
00907 
00908     if (!buf || len < 2)
00909     {
00910         return;
00911     }
00912 
00913     command = buf[0];
00914     quickslot = buf[1];
00915 
00916     if (quickslot < 1 || quickslot > MAX_QUICKSLOT)
00917     {
00918         return;
00919     }
00920 
00921     if (command == CMD_QUICKSLOT_SET)
00922     {
00923         tag_t tag;
00924         object *op;
00925 
00926         if (len < 6)
00927         {
00928             return;
00929         }
00930 
00931         tag = GetInt_String(buf + 2);
00932         op = esrv_get_ob_from_count(pl->ob, tag);
00933 
00934         if (!op)
00935         {
00936             return;
00937         }
00938 
00939         remove_quickslot(quickslot, pl);
00940         op->quickslot = quickslot;
00941     }
00942     else if (command == CMD_QUICKSLOT_SETSPELL)
00943     {
00944         sint16 spell_id;
00945 
00946         /* Assumes that all spells have at least 2 letters. */
00947         if (len < 4)
00948         {
00949             return;
00950         }
00951 
00952         spell_id = look_up_spell_name((char *) buf + 2);
00953 
00954         if (spell_id == SP_NO_SPELL)
00955         {
00956             return;
00957         }
00958 
00959         remove_quickslot(quickslot, pl);
00960         pl->spell_quickslots[quickslot - 1] = spell_id;
00961     }
00962     else if (command == CMD_QUICKSLOT_UNSET)
00963     {
00964         remove_quickslot(quickslot, pl);
00965     }
00966     else
00967     {
00968         LOG(llevSystem, "Client %s@%s sent invalid quickslot command.\n", pl->ob->name, pl->socket.host);
00969         pl->socket.status = Ns_Dead;
00970     }
00971 }
00972 
00978 void ApplyCmd(char *buf, int len, player *pl)
00979 {
00980     uint32 tag;
00981     object *op;
00982 
00983     if (!buf || !len)
00984     {
00985         return;
00986     }
00987 
00988     tag = atoi(buf);
00989     op = esrv_get_ob_from_count(pl->ob, tag);
00990 
00991     if (QUERY_FLAG(pl->ob, FLAG_REMOVED))
00992     {
00993         return;
00994     }
00995 
00996     /* If the high bit is set, player applied a pseudo object. */
00997     if (tag & 0x80000000)
00998     {
00999         pl->socket.look_position = tag & 0x7fffffff;
01000         pl->socket.update_tile = 0;
01001         return;
01002     }
01003 
01004     if (!op)
01005     {
01006         return;
01007     }
01008 
01009     player_apply(pl->ob, op, 0, 0);
01010 }
01011 
01017 void LockItem(uint8 *data, int len, player *pl)
01018 {
01019     int flag, tag;
01020     object *op;
01021 
01022     if (!data || !len)
01023     {
01024         return;
01025     }
01026 
01027     flag = data[0];
01028     tag = GetInt_String(data + 1);
01029     op = esrv_get_ob_from_count(pl->ob, tag);
01030 
01031     /* Can happen as result of latency or client/server async. */
01032     if (!op)
01033     {
01034         return;
01035     }
01036 
01037     /* Only lock item inside the player's own inventory */
01038     if (is_player_inv(op) != pl->ob)
01039     {
01040         new_draw_info(0, COLOR_WHITE, pl->ob, "You can't lock items outside your inventory!");
01041         return;
01042     }
01043 
01044     if (!flag)
01045     {
01046         CLEAR_FLAG(op, FLAG_INV_LOCKED);
01047     }
01048     else
01049     {
01050         SET_FLAG(op, FLAG_INV_LOCKED);
01051     }
01052 
01053     esrv_update_item(UPD_FLAGS, pl->ob, op);
01054 }
01055 
01061 void MarkItem(uint8 *data, int len, player *pl)
01062 {
01063     int tag;
01064     object *op;
01065 
01066     if (!data || !len)
01067     {
01068         return;
01069     }
01070 
01071     tag = GetInt_String(data);
01072     op = esrv_get_ob_from_count(pl->ob, tag);
01073 
01074     if (!op)
01075     {
01076         return;
01077     }
01078 
01079     if (pl->mark_count == op->count)
01080     {
01081         new_draw_info_format(0, COLOR_WHITE, pl->ob, "Unmarked item %s.", query_name(op, NULL));
01082         pl->mark = NULL;
01083         pl->mark_count = -1;
01084     }
01085     else
01086     {
01087         new_draw_info_format(0, COLOR_WHITE, pl->ob, "Marked item %s.", query_name(op, NULL));
01088         pl->mark_count = op->count;
01089         pl->mark = op;
01090     }
01091 }
01092 
01099 void esrv_move_object(object *pl, tag_t to, tag_t tag, long nrof)
01100 {
01101     object *op, *env;
01102     int tmp;
01103 
01104     op = esrv_get_ob_from_count(pl, tag);
01105 
01106     if (!op)
01107     {
01108         return;
01109     }
01110 
01111     /* drop it to the ground */
01112     if (!to)
01113     {
01114         if (op->map && !op->env)
01115         {
01116             return;
01117         }
01118 
01119         CLEAR_FLAG(pl, FLAG_INV_LOCKED);
01120 
01121         if ((tmp = check_container(pl, op)))
01122         {
01123             new_draw_info(0, COLOR_WHITE, pl, "First remove all god-given items from this container!");
01124         }
01125         else if (QUERY_FLAG(pl, FLAG_INV_LOCKED))
01126         {
01127             new_draw_info(0, COLOR_WHITE, pl, "You can't drop a container with locked items inside!");
01128         }
01129         else
01130         {
01131             drop_object(pl, op, nrof, 0);
01132         }
01133 
01134         CLEAR_FLAG(pl, FLAG_INV_LOCKED);
01135 
01136         return;
01137     }
01138     /* Pick it up to the inventory */
01139     else if (to == pl->count || (to == op->count && !op->env))
01140     {
01141         /* Return if player has already picked it up */
01142         if (op->env == pl)
01143         {
01144             return;
01145         }
01146 
01147         CONTR(pl)->count = nrof;
01148         /* It goes in player inv or readied container */
01149         pick_up(pl, op, 0);
01150         return;
01151     }
01152 
01153     /* If not dropped or picked up, we are putting it into a sack */
01154     env = esrv_get_ob_from_count(pl, to);
01155 
01156     if (!env)
01157     {
01158         return;
01159     }
01160 
01161     /* put_object_in_sack() presumes that necessary sanity checking has
01162      * already been done (eg, it can be picked up and fits in in a sack,
01163      * so check for those things. We should also check and make sure env
01164      * is in fact a container for that matter. */
01165     if (env->type == CONTAINER && can_pick(pl, op) && sack_can_hold(pl, env, op, nrof))
01166     {
01167         CLEAR_FLAG(pl, FLAG_INV_LOCKED);
01168         tmp = check_container(pl, op);
01169 
01170         if (QUERY_FLAG(pl, FLAG_INV_LOCKED) && env->env != pl)
01171         {
01172             new_draw_info(0, COLOR_WHITE, pl, "You can't drop a container with locked items inside!");
01173         }
01174         else if (tmp && env->env != pl)
01175         {
01176             new_draw_info(0, COLOR_WHITE, pl, "First remove all god-given items from this container!");
01177         }
01178         else if (QUERY_FLAG(op, FLAG_STARTEQUIP) && env->env != pl)
01179         {
01180             new_draw_info(0, COLOR_WHITE, pl, "You can't store god-given items outside your inventory!");
01181         }
01182         else
01183         {
01184             put_object_in_sack(pl, env, op, nrof);
01185         }
01186 
01187         CLEAR_FLAG(pl, FLAG_INV_LOCKED);
01188 
01189         return;
01190     }
01191 }
01192 
01201 static int check_container(object *pl, object *con)
01202 {
01203     object *current, *next;
01204     int ret = 0;
01205 
01206     /* Only check stuff *inside* a container */
01207     if (con->type != CONTAINER)
01208     {
01209         return 0;
01210     }
01211 
01212     for (current = con->inv; current != NULL; current = next)
01213     {
01214         next = current->below;
01215         ret += check_container(pl, current);
01216 
01217         if (QUERY_FLAG(current, FLAG_STARTEQUIP))
01218         {
01219             ret += 1;
01220         }
01221 
01222         if (QUERY_FLAG(current, FLAG_INV_LOCKED))
01223         {
01224             SET_FLAG(pl, FLAG_INV_LOCKED);
01225         }
01226     }
01227 
01228     return ret;
01229 }
01230 
01237 void cmd_ready_send(player *pl, tag_t tag, int type)
01238 {
01239     SockList sl;
01240     unsigned char sockbuf[6];
01241 
01242     sl.buf = sockbuf;
01243     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_READY);
01244     SockList_AddChar(&sl, type);
01245     SockList_AddInt(&sl, tag);
01246     Send_With_Handling(&pl->socket, &sl);
01247 }
01248 
01254 int cmd_ready_determine(object *tmp)
01255 {
01256     /* System object, can't be any of the below. */
01257     if (QUERY_FLAG(tmp, FLAG_SYS_OBJECT))
01258     {
01259         return -1;
01260     }
01261 
01262     /* Objects that can be thrown. */
01263     if (QUERY_FLAG(tmp, FLAG_IS_THROWN))
01264     {
01265         return READY_OBJ_THROW;
01266     }
01267     /* Arrows, or containers like quivers. */
01268     else if (tmp->type == ARROW || tmp->type == CONTAINER)
01269     {
01270         return READY_OBJ_ARROW;
01271     }
01272     else
01273     {
01274         return -1;
01275     }
01276 }
01277 
01284 void cmd_ready_clear(object *op, int type)
01285 {
01286     object *tmp;
01287 
01288     for (tmp = op->inv; tmp; tmp = tmp->below)
01289     {
01290         if (QUERY_FLAG(tmp, FLAG_IS_READY) && cmd_ready_determine(tmp) == type)
01291         {
01292             CLEAR_FLAG(tmp, FLAG_IS_READY);
01293             break;
01294         }
01295     }
01296 }