Atrinik Server 2.5
types/container.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 
00030 #include <global.h>
00031 
00032 static int container_trap(object *op, object *container);
00033 
00043 int esrv_apply_container(object *op, object *sack)
00044 {
00045     object *cont, *tmp;
00046 
00047     if (op->type != PLAYER)
00048     {
00049         LOG(llevBug, "esrv_apply_container: called from non player: <%s>!\n", query_name(op, NULL));
00050         return 0;
00051     }
00052 
00053     /* cont is NULL or the container player already has opened */
00054     cont = CONTR(op)->container;
00055 
00056     if (sack == NULL || sack->type != CONTAINER || (cont && cont->type != CONTAINER))
00057     {
00058         LOG(llevBug, "esrv_apply_container: object *sack = %s is not container (cont:<%s>)!\n", query_name(sack, NULL), query_name(cont, NULL));
00059         return 0;
00060     }
00061 
00062     /* close container?
00063      * if cont != sack || cont == sack - in both cases we close cont */
00064     if (cont)
00065     {
00066         /* Trigger the CLOSE event */
00067         if (trigger_event(EVENT_CLOSE, op, cont, NULL, NULL, 0, 0, 0, SCRIPT_FIX_ALL))
00068         {
00069             return 1;
00070         }
00071 
00072         if (container_unlink(CONTR(op), cont))
00073         {
00074             new_draw_info_format(0, COLOR_WHITE, op, "You close %s.", query_name(cont, op));
00075         }
00076         else
00077         {
00078             new_draw_info_format(0, COLOR_WHITE, op, "You leave %s.", query_name(cont, op));
00079         }
00080 
00081         /* we closing the one we applied */
00082         if (cont == sack)
00083         {
00084             return 1;
00085         }
00086     }
00087 
00088     /* at this point we ready a container OR we open it! */
00089 
00090     /* If the player is trying to open it (which he must be doing if we got here),
00091      * and it is locked, check to see if player has the equipment to open it. */
00092     if (sack->slaying || sack->stats.maxhp)
00093     {
00094         /* Locked container */
00095         if (sack->sub_type == ST1_CONTAINER_NORMAL)
00096         {
00097             tmp = find_key(op, sack);
00098 
00099             if (tmp)
00100             {
00101                 if (tmp->type == KEY)
00102                 {
00103                     new_draw_info_format(0, COLOR_WHITE, op, "You unlock %s with %s.", query_name(sack, op), query_name(tmp, op));
00104                 }
00105                 else if (tmp->type == FORCE)
00106                 {
00107                     new_draw_info_format(0, COLOR_WHITE, op, "The %s is unlocked for you.", query_name(sack, op));
00108                 }
00109             }
00110             else
00111             {
00112                 new_draw_info_format(0, COLOR_WHITE, op, "You don't have the key to unlock %s.", query_name(sack, op));
00113                 return 0;
00114             }
00115         }
00116         /* Personalized container */
00117         else
00118         {
00119             /* Party corpse */
00120             if (sack->sub_type == ST1_CONTAINER_CORPSE_party && !party_can_open_corpse(op, sack))
00121             {
00122                 return 0;
00123             }
00124             /* Only give player with right name access */
00125             else if (sack->sub_type == ST1_CONTAINER_CORPSE_player && sack->slaying != op->name)
00126             {
00127                 new_draw_info(0, COLOR_WHITE, op, "It's not your bounty.");
00128                 return 0;
00129             }
00130         }
00131     }
00132 
00133     /* By the time we get here, we have made sure any other container has been closed and
00134      * if this is a locked container, the player they key to open it. */
00135 
00136     /* There are really two cases - the sack is either on the ground, or the sack is
00137      * part of the players inventory.  If on the ground, we assume that the player is
00138      * opening it, since if it was being closed, that would have been taken care of above.
00139      * If it in the players inventory, we can READY the container. */
00140     /* container is NOT in players inventory */
00141     if (sack->env != op)
00142     {
00143         /* this is not possible - opening a container inside another container or an another player */
00144         if (sack->env)
00145         {
00146             new_draw_info_format(0, COLOR_WHITE, op, "You can't open %s.", query_name(sack, op));
00147             return 0;
00148         }
00149 
00150         new_draw_info_format(0, COLOR_WHITE, op, "You open %s.", query_name(sack, op));
00151         container_link(CONTR(op), sack);
00152 
00153         if (sack->slaying && sack->sub_type == ST1_CONTAINER_CORPSE_party)
00154         {
00155             party_handle_corpse(op, sack);
00156         }
00157     }
00158     /* Sack is in player's inventory */
00159     else
00160     {
00161         /* readied sack becoming open */
00162         if (QUERY_FLAG(sack, FLAG_APPLIED))
00163         {
00164             new_draw_info_format(0, COLOR_WHITE, op, "You open %s.", query_name(sack, op));
00165             container_link(CONTR(op), sack);
00166         }
00167         else
00168         {
00169             CLEAR_FLAG(sack, FLAG_APPLIED);
00170             new_draw_info_format(0, COLOR_WHITE, op, "You readied %s.", query_name(sack, op));
00171             SET_FLAG(sack, FLAG_APPLIED);
00172 
00173             update_object(sack, UP_OBJ_FACE);
00174             esrv_update_item(UPD_FLAGS, op, sack);
00175             /* search & explode a rune in the container */
00176             container_trap(op, sack);
00177         }
00178     }
00179 
00180     if ((sack->sub_type == ST1_CONTAINER_CORPSE_party || sack->sub_type == ST1_CONTAINER_CORPSE_player) && !QUERY_FLAG(sack, FLAG_BEEN_APPLIED))
00181     {
00182         CONTR(op)->stat_corpses_searched++;
00183     }
00184 
00185     /* Only after actually readying/opening the container we know more
00186      * about it. */
00187     SET_FLAG(sack, FLAG_BEEN_APPLIED);
00188 
00189     return 1;
00190 }
00191 
00198 int container_link(player *pl, object *sack)
00199 {
00200     int ret = 0;
00201 
00202     /* For safety reasons, let's check this is valid */
00203     if (sack->attacked_by)
00204     {
00205         if (sack->attacked_by->type != PLAYER || !CONTR(sack->attacked_by) || CONTR(sack->attacked_by)->container != sack)
00206         {
00207             LOG(llevBug, "container_link() - invalid player linked: <%s>\n", query_name(sack->attacked_by, NULL));
00208             sack->attacked_by = NULL;
00209         }
00210     }
00211 
00212     /* the open/close logic should be handled elsewhere.
00213      * for that reason, this function should only be called
00214      * when valid - broken open/close logic elsewhere is bad.
00215      * so, give a bug warning out! */
00216     if (pl->container)
00217     {
00218         LOG(llevBug, "container_link() - called from player with open container!: <%s> sack:<%s>\n", query_name(sack->attacked_by, NULL), query_name(sack, NULL));
00219         container_unlink(pl, sack);
00220     }
00221 
00222     /* Check for quest containers. */
00223     if (HAS_EVENT(sack, EVENT_QUEST))
00224     {
00225         object *tmp;
00226 
00227         for (tmp = sack->inv; tmp; tmp = tmp->below)
00228         {
00229             if (tmp->type == QUEST_CONTAINER)
00230             {
00231                 check_quest(pl->ob, tmp);
00232             }
00233         }
00234     }
00235 
00236     pl->container = sack;
00237     pl->container_count = sack->count;
00238 
00239     pl->container_above = sack->attacked_by;
00240 
00241     if (sack->attacked_by)
00242     {
00243         CONTR(sack->attacked_by)->container_below = pl->ob;
00244     }
00245     /* we are the first one opening this container */
00246     else
00247     {
00248         SET_FLAG(sack, FLAG_APPLIED);
00249 
00250         /* faking open container face */
00251         if (sack->other_arch)
00252         {
00253             sack->face = sack->other_arch->clone.face;
00254             sack->animation_id = sack->other_arch->clone.animation_id;
00255 
00256             if (sack->animation_id)
00257             {
00258                 SET_ANIMATION(sack, (NUM_ANIMATIONS(sack) / NUM_FACINGS(sack)) * sack->direction);
00259             }
00260 
00261             update_object(sack, UP_OBJ_FACE);
00262         }
00263 
00264         update_object(sack, UP_OBJ_FACE);
00265         esrv_update_item (UPD_FLAGS|UPD_FACE, pl->ob, sack);
00266         container_trap(pl->ob, sack);
00267         ret = 1;
00268     }
00269 
00270     esrv_send_inventory(pl->ob, sack);
00271     /* we are first element */
00272     pl->container_below = NULL;
00273     sack->attacked_by = pl->ob;
00274     sack->attacked_by_count = pl->ob->count;
00275 
00276     return ret;
00277 }
00278 
00287 int container_unlink(player *pl, object *sack)
00288 {
00289     object *tmp, *tmp2;
00290 
00291     if (pl == NULL && sack == NULL)
00292     {
00293         LOG(llevBug, "container_unlink() - *pl AND *sack == NULL!\n");
00294         return 0;
00295     }
00296 
00297     if (pl)
00298     {
00299         if (!pl->container)
00300         {
00301             return 0;
00302         }
00303 
00304         if (pl->container->count != pl->container_count)
00305         {
00306             pl->container = NULL;
00307             pl->container_count = 0;
00308             return 0;
00309         }
00310 
00311         sack = pl->container;
00312         update_object(sack, UP_OBJ_FACE);
00313         esrv_close_container(pl->ob);
00314 
00315         /* ok, there is a valid container - unlink the player now */
00316 
00317         /* we are only applier */
00318         if (!pl->container_below && !pl->container_above)
00319         {
00320             /* we should be that object... */
00321             if (pl->container->attacked_by != pl->ob)
00322             {
00323                 LOG(llevBug, "container_unlink() - container link don't match player!: <%s> sack:<%s> (%s)\n", query_name(pl->ob, NULL), query_name(sack->attacked_by, NULL), query_name(sack, NULL));
00324                 return 0;
00325             }
00326 
00327             pl->container = NULL;
00328             pl->container_count = 0;
00329 
00330             CLEAR_FLAG(sack, FLAG_APPLIED);
00331 
00332             if (sack->other_arch)
00333             {
00334                 sack->face = sack->arch->clone.face;
00335                 sack->animation_id = sack->arch->clone.animation_id;
00336 
00337                 if (sack->animation_id)
00338                 {
00339                     SET_ANIMATION(sack, (NUM_ANIMATIONS(sack) / NUM_FACINGS(sack)) * sack->direction);
00340                 }
00341 
00342                 update_object(sack, UP_OBJ_FACE);
00343             }
00344 
00345             sack->attacked_by = NULL;
00346             sack->attacked_by_count = 0;
00347             esrv_update_item (UPD_FLAGS|UPD_FACE, pl->ob, sack);
00348             return 1;
00349         }
00350 
00351         /* because there is another player applying that container, we don't close it */
00352 
00353         /* we are first player in list */
00354         if (!pl->container_below)
00355         {
00356             /* mark above as first player applying this container */
00357             sack->attacked_by = pl->container_above;
00358             sack->attacked_by_count = pl->container_above->count;
00359             CONTR(pl->container_above)->container_below = NULL;
00360 
00361             pl->container_above = NULL;
00362             pl->container = NULL;
00363             pl->container_count = 0;
00364             return 0;
00365         }
00366 
00367         /* we are somewhere in the middle or last one - it don't matter */
00368         CONTR(pl->container_below)->container_above = pl->container_above;
00369 
00370         if (pl->container_above)
00371         {
00372             CONTR(pl->container_above)->container_below = pl->container_below;
00373         }
00374 
00375         pl->container_below=NULL;
00376         pl->container_above=NULL;
00377         pl->container = NULL;
00378         pl->container_count = 0;
00379         return 0;
00380     }
00381 
00382     CLEAR_FLAG(sack, FLAG_APPLIED);
00383 
00384     if (sack->other_arch)
00385     {
00386         sack->face = sack->arch->clone.face;
00387         sack->animation_id = sack->arch->clone.animation_id;
00388 
00389         if (sack->animation_id)
00390         {
00391             SET_ANIMATION(sack, (NUM_ANIMATIONS(sack) / NUM_FACINGS(sack)) * sack->direction);
00392         }
00393 
00394         update_object(sack, UP_OBJ_FACE);
00395     }
00396 
00397     tmp = sack->attacked_by;
00398     sack->attacked_by = NULL;
00399     sack->attacked_by_count = 0;
00400 
00401     /* if we are here, we are called with (NULL,sack) */
00402     while (tmp)
00403     {
00404         /* valid player in list? */
00405         if (!CONTR(tmp) || CONTR(tmp)->container != sack)
00406         {
00407             LOG(llevBug,"container_unlink() - container link list mismatch!: player?:<%s> sack:<%s> (%s)\n", query_name(tmp, NULL), query_name(sack, NULL), query_name(sack->attacked_by, NULL));
00408             return 1;
00409         }
00410 
00411         tmp2 = CONTR(tmp)->container_above;
00412         CONTR(tmp)->container = NULL;
00413         CONTR(tmp)->container_count = 0;
00414         CONTR(tmp)->container_below = NULL;
00415         CONTR(tmp)->container_above = NULL;
00416         esrv_update_item(UPD_FLAGS|UPD_FACE, tmp, sack);
00417         esrv_close_container(tmp);
00418         tmp = tmp2;
00419     }
00420 
00421     return 1;
00422 }
00423 
00428 void free_container_monster(object *monster, object *op)
00429 {
00430     int i;
00431     object *container = monster->env;
00432 
00433     if (container == NULL)
00434     {
00435         return;
00436     }
00437 
00438     /* in container, no walk off check */
00439     remove_ob(monster);
00440     monster->x = container->x;
00441     monster->y = container->y;
00442     i = find_free_spot(monster->arch, NULL, op->map, monster->x, monster->y, 0, 9);
00443 
00444     if (i != -1)
00445     {
00446         monster->x += freearr_x[i];
00447         monster->y += freearr_y[i];
00448     }
00449 
00450     fix_monster(monster);
00451 
00452     if (insert_ob_in_map(monster, op->map, monster, 0))
00453     {
00454         new_draw_info_format(0, COLOR_WHITE, op, "A %s jumps out of the %s.", query_name(monster, NULL), query_name(container, NULL));
00455     }
00456 }
00457 
00468 static int container_trap(object *op, object *container)
00469 {
00470     int ret = 0;
00471     object *tmp;
00472 
00473     for (tmp = container->inv; tmp; tmp = tmp->below)
00474     {
00475         /* Search for traps and runes */
00476         if (tmp->type == RUNE)
00477         {
00478             ret++;
00479             spring_trap(tmp, op);
00480         }
00481         /* Search for monsters living in containers */
00482         else if (tmp->type == MONSTER)
00483         {
00484             ret++;
00485             free_container_monster(tmp, op);
00486         }
00487     }
00488 
00489     return ret;
00490 }
00491 
00498 int check_magical_container(object *op, object *container)
00499 {
00500     if (op->type == CONTAINER && container->type == CONTAINER && op->weapon_speed != 1.0f && container->weapon_speed != 1.0f)
00501     {
00502         return 1;
00503     }
00504 
00505     return 0;
00506 }