Atrinik Server 2.5
server/button.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 void animate_turning(object *op);
00033 static void trigger_move(object *op, int state);
00034 static objectlink *get_button_links(object *button);
00035 
00040 void push_button(object *op)
00041 {
00042     object *tmp;
00043     objectlink *ol;
00044 
00045     /*LOG(llevDebug, "push_button: %s (%d)\n", op->name, op->count);*/
00046     for (ol = get_button_links(op); ol; ol = ol->next)
00047     {
00048         tmp = ol->objlink.ob;
00049 
00050         if (!tmp || tmp->count != ol->id)
00051         {
00052             LOG(llevBug, "Internal error in push_button (%s).\n", op->name);
00053             continue;
00054         }
00055 
00056         /* a button link object can become freed when the map is saving.  As
00057          * a map is saved, objects are removed and freed, and if an object is
00058          * on top of a button, this function is eventually called.  If a map
00059          * is getting moved out of memory, the status of buttons and levers
00060          * probably isn't important - it will get sorted out when the map is
00061          * re-loaded.  As such, just exit this function if that is the case. */
00062         if (!OBJECT_ACTIVE(tmp))
00063         {
00064             LOG(llevDebug, "push_button: button link with invalid object! (%x - %x)", QUERY_FLAG(tmp, FLAG_REMOVED), tmp->count);
00065             return;
00066         }
00067 
00068         /* If the criteria isn't appropriate, don't do anything. */
00069         if (op->value && QUERY_FLAG(tmp, FLAG_CONNECT_NO_PUSH))
00070         {
00071             continue;
00072         }
00073 
00074         if (!op->value && QUERY_FLAG(tmp, FLAG_CONNECT_NO_RELEASE))
00075         {
00076             continue;
00077         }
00078 
00079         if (HAS_EVENT(tmp, EVENT_TRIGGER) && trigger_event(EVENT_TRIGGER, tmp, op, NULL, NULL, 0, 0, 0, SCRIPT_FIX_NOTHING))
00080         {
00081             continue;
00082         }
00083 
00084         switch (tmp->type)
00085         {
00086             case GATE:
00087             case PIT:
00088                 tmp->value = tmp->stats.maxsp ? !op->value : op->value;
00089                 tmp->speed = 0.5;
00090                 update_ob_speed(tmp);
00091                 break;
00092 
00093             case HANDLE:
00094                 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + (tmp->value = tmp->stats.maxsp ? !op->value : op->value));
00095                 update_object(tmp, UP_OBJ_FACE);
00096                 break;
00097 
00098             case SIGN:
00099                 if (!tmp->stats.food || tmp->last_eat < tmp->stats.food)
00100                 {
00101                     if (tmp->title)
00102                     {
00103                         play_sound_map(tmp->map, CMD_SOUND_EFFECT, tmp->title, tmp->x, tmp->y, 0, 0);
00104                     }
00105 
00106                     if (tmp->msg)
00107                     {
00108                         new_info_map(0, COLOR_NAVY, tmp->map, tmp->x, tmp->y, MAP_INFO_NORMAL, tmp->msg);
00109                     }
00110 
00111                     if (tmp->stats.food)
00112                     {
00113                         tmp->last_eat++;
00114                     }
00115                 }
00116 
00117                 break;
00118 
00119             case ALTAR:
00120                 tmp->value = 1;
00121                 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + tmp->value);
00122                 update_object(tmp, UP_OBJ_FACE);
00123                 break;
00124 
00125             case BUTTON:
00126             case PEDESTAL:
00127                 tmp->value = op->value;
00128                 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + tmp->value);
00129                 update_object(tmp, UP_OBJ_FACE);
00130                 break;
00131 
00132             case TIMED_GATE:
00133                 tmp->speed = tmp->arch->clone.speed;
00134                 /* original values */
00135                 update_ob_speed(tmp);
00136                 tmp->value = 1;
00137                 tmp->stats.sp = 1;
00138                 tmp->stats.hp = tmp->stats.maxhp;
00139                 break;
00140 
00141             case FIREWALL:
00142                 /* connection flag1 = on/off */
00143                 if (op->last_eat)
00144                 {
00145                     tmp->last_eat != 0 ? (tmp->last_eat = 0) : (tmp->last_eat = 1);
00146                 }
00147                 /* "normal" connection - turn wall */
00148                 else
00149                 {
00150                     /* next direction */
00151                     if (tmp->stats.maxsp)
00152                     {
00153                         tmp->direction = absdir(tmp->direction + tmp->stats.maxsp);
00154                         animate_turning(tmp);
00155                     }
00156                 }
00157 
00158                 break;
00159 
00160             case DIRECTOR:
00161                 /* next direction */
00162                 if (tmp->stats.maxsp)
00163                 {
00164                     tmp->direction = absdir(tmp->direction + tmp->stats.maxsp);
00165                     animate_turning(tmp);
00166                 }
00167 
00168                 break;
00169 
00170             case TELEPORTER:
00171                 move_teleporter(tmp);
00172                 break;
00173 
00174             case CREATOR:
00175                 move_creator(tmp);
00176                 break;
00177 
00178             case SPAWN_POINT:
00179                 spawn_point(tmp);
00180                 break;
00181 
00182             case DUPLICATOR:
00183                 move_duplicator(tmp);
00184                 break;
00185         }
00186     }
00187 }
00188 
00195 void update_button(object *op)
00196 {
00197     object *ab, *tmp, *head;
00198     int move, fly, tot, any_down = 0, old_value = op->value;
00199     objectlink *ol;
00200 
00201     for (ol = get_button_links(op); ol; ol = ol->next)
00202     {
00203         tmp = ol->objlink.ob;
00204 
00205         if (!tmp || tmp->count != ol->id)
00206         {
00207             LOG(llevDebug, "Internal error in update_button (%s).\n", op->name);
00208             continue;
00209         }
00210 
00211         if (tmp->type == BUTTON)
00212         {
00213             fly = QUERY_FLAG(tmp, FLAG_FLY_ON);
00214             move = QUERY_FLAG(tmp, FLAG_WALK_ON);
00215 
00216             for (ab = GET_BOTTOM_MAP_OB(tmp), tot = 0; ab != NULL; ab = ab->above)
00217             {
00218                 if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move))
00219                 {
00220                     tot += ab->weight * (ab->nrof ? ab->nrof : 1) + ab->carrying;
00221                 }
00222             }
00223 
00224             tmp->value = (tot >= tmp->weight) ? 1 : 0;
00225 
00226             if (tmp->value)
00227             {
00228                 any_down = 1;
00229 
00230                 if (QUERY_FLAG(tmp, FLAG_CONNECT_RESET))
00231                 {
00232                     tmp->value = 0;
00233                 }
00234             }
00235         }
00236         else if (tmp->type == PEDESTAL)
00237         {
00238             tmp->value = 0;
00239             fly = QUERY_FLAG(tmp, FLAG_FLY_ON);
00240             move = QUERY_FLAG(tmp, FLAG_WALK_ON);
00241 
00242             for (ab = GET_BOTTOM_MAP_OB(tmp); ab != NULL; ab = ab->above)
00243             {
00244                 head = ab->head ? ab->head : ab;
00245 
00246                 if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move) && (head->race == tmp->slaying || (!strcmp(tmp->slaying, "player") && head->type == PLAYER)))
00247                 {
00248                     tmp->value = 1;
00249                 }
00250             }
00251 
00252             if (tmp->value)
00253             {
00254                 any_down = 1;
00255 
00256                 if (QUERY_FLAG(tmp, FLAG_CONNECT_RESET))
00257                 {
00258                     tmp->value = 0;
00259                 }
00260             }
00261         }
00262     }
00263 
00264     /* If any other buttons were down, force this to remain down */
00265     if (any_down)
00266     {
00267         op->value = 1;
00268     }
00269 
00270     /* If this button hasn't changed, don't do anything */
00271     if (op->value != old_value)
00272     {
00273         SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->value);
00274         update_object(op, UP_OBJ_FACE);
00275         push_button(op);
00276     }
00277 
00278     if (op->value && QUERY_FLAG(op, FLAG_CONNECT_RESET))
00279     {
00280         op->value = 0;
00281         SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->value);
00282         update_object(op, UP_OBJ_FACE);
00283     }
00284 }
00285 
00290 void update_buttons(mapstruct *m)
00291 {
00292     objectlink *ol, *obp;
00293     object *ab, *tmp;
00294     int fly, move;
00295 
00296     for (obp = m->buttons; obp; obp = obp->next)
00297     {
00298         for (ol = obp->objlink.link; ol; ol = ol->next)
00299         {
00300             if (!ol->objlink.ob || ol->objlink.ob->count != ol->id)
00301             {
00302                 LOG(llevBug, "Internal error in update_button (%s (%dx%d):%d, connected %ld ).\n", ol->objlink.ob ? ol->objlink.ob->name : "null", ol->objlink.ob ? ol->objlink.ob->x:-1, ol->objlink.ob ? ol->objlink.ob->y:-1, ol->id, obp->value);
00303                 continue;
00304             }
00305 
00306             if (ol->objlink.ob->type == BUTTON || ol->objlink.ob->type == PEDESTAL)
00307             {
00308                 update_button(ol->objlink.ob);
00309             }
00310             else if (ol->objlink.ob->type == CHECK_INV)
00311             {
00312                 tmp = ol->objlink.ob;
00313                 fly = QUERY_FLAG(tmp, FLAG_FLY_ON);
00314                 move = QUERY_FLAG(tmp, FLAG_WALK_ON);
00315 
00316                 for (ab = GET_BOTTOM_MAP_OB(tmp); ab != NULL; ab = ab->above)
00317                 {
00318                     if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move))
00319                     {
00320                         check_inv(ab, tmp);
00321                     }
00322                 }
00323             }
00324             else if (ol->objlink.ob->type == TRIGGER_BUTTON || ol->objlink.ob->type == TRIGGER_PEDESTAL || ol->objlink.ob->type == TRIGGER_ALTAR)
00325             {
00326                 /* check_trigger will itself sort out the numbers of
00327                  * items above the trigger */
00328                 check_trigger(ol->objlink.ob, ol->objlink.ob);
00329             }
00330         }
00331     }
00332 }
00333 
00337 void use_trigger(object *op)
00338 {
00339     /* Toggle value */
00340     op->value = !op->value;
00341     push_button(op);
00342 }
00343 
00347 static void animate_turning(object *op)
00348 {
00349     SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->state);
00350     update_object(op, UP_OBJ_FACE);
00351 }
00352 
00353 /* 1 down and 0 up */
00354 static void trigger_move(object *op, int state)
00355 {
00356     op->stats.wc = state;
00357 
00358     if (state)
00359     {
00360         use_trigger(op);
00361 
00362         if (op->stats.exp)
00363         {
00364             op->speed = 1.0f / op->stats.exp;
00365             update_ob_speed(op);
00366             op->speed_left = -1;
00367         }
00368     }
00369     else
00370     {
00371         use_trigger(op);
00372         op->speed = 0;
00373         update_ob_speed(op);
00374     }
00375 }
00376 
00377 /* cause != NULL: something has moved on top of op
00378  *
00379  * cause == NULL: nothing has moved, we have been called from
00380  * animate_trigger().
00381  *
00382  * TRIGGER_ALTAR: Returns 1 if 'cause' was destroyed, 0 if not.
00383  *
00384  * TRIGGER: Returns 1 if handle could be moved, 0 if not.
00385  *
00386  * TRIGGER_BUTTON, TRIGGER_PEDESTAL: Returns 0. */
00387 int check_trigger(object *op, object *cause)
00388 {
00389     object *tmp;
00390     int push = 0, tot = 0;
00391     int in_movement = op->stats.wc || op->speed;
00392 
00393     switch (op->type)
00394     {
00395         case TRIGGER_BUTTON:
00396             if (op->weight > 0)
00397             {
00398                 if (cause)
00399                 {
00400                     for (tmp = GET_BOTTOM_MAP_OB(op); tmp; tmp = tmp->above)
00401                     {
00402                         if (!QUERY_FLAG(tmp, FLAG_FLYING))
00403                         {
00404                             tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1) + tmp->carrying;
00405                         }
00406                     }
00407 
00408                     if (tot >= op->weight)
00409                     {
00410                         push = 1;
00411                     }
00412 
00413                     if (op->stats.ac == push)
00414                     {
00415                         return 0;
00416                     }
00417 
00418                     op->stats.ac = push;
00419                     SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push);
00420                     update_object(op, UP_OBJ_FACE);
00421 
00422                     if (in_movement || !push)
00423                     {
00424                         return 0;
00425                     }
00426                 }
00427 
00428                 trigger_move(op, push);
00429             }
00430 
00431             return 0;
00432 
00433         case TRIGGER_PEDESTAL:
00434             if (cause)
00435             {
00436                 for (tmp = GET_BOTTOM_MAP_OB(op); tmp; tmp = tmp->above)
00437                 {
00438                     object *head = tmp->head ? tmp->head : tmp;
00439 
00440                     if ((!QUERY_FLAG(head, FLAG_FLYING) || QUERY_FLAG(op, FLAG_FLY_ON)) && (head->race == op->slaying || (!strcmp(op->slaying, "player") && head->type == PLAYER)))
00441                     {
00442                         push = 1;
00443                         break;
00444                     }
00445                 }
00446 
00447                 if (op->stats.ac == push)
00448                 {
00449                     return 0;
00450                 }
00451 
00452                 op->stats.ac = push;
00453                 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push);
00454                 update_object(op, UP_OBJ_FACE);
00455 
00456                 if (in_movement || !push)
00457                 {
00458                     return 0;
00459                 }
00460             }
00461 
00462             trigger_move(op, push);
00463             return 0;
00464 
00465         case TRIGGER_ALTAR:
00466             if (cause)
00467             {
00468                 if (in_movement)
00469                 {
00470                     return 0;
00471                 }
00472 
00473                 if (operate_altar(op, &cause))
00474                 {
00475                     SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + 1);
00476                     update_object(op, UP_OBJ_FACE);
00477 
00478                     if (op->last_sp >= 0)
00479                     {
00480                         trigger_move (op, 1);
00481 
00482                         if (op->last_sp > 0)
00483                         {
00484                             op->last_sp = -op->last_sp;
00485                         }
00486                     }
00487                     else
00488                     {
00489                         /* for trigger altar with last_sp, the ON/OFF
00490                          * status (-> +/- value) is "simulated": */
00491                         op->value = !op->value;
00492                         trigger_move(op, 1);
00493                         op->last_sp = -op->last_sp;
00494                         op->value = !op->value;
00495                     }
00496 
00497                     return cause == NULL;
00498                 }
00499                 else
00500                 {
00501                     return 0;
00502                 }
00503             }
00504             else
00505             {
00506                 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction);
00507                 update_object(op, UP_OBJ_FACE);
00508 
00509                 /* If trigger_altar has "last_sp > 0" set on the map,
00510                  * it will push the connected value only once per sacrifice.
00511                  * Otherwise (default), the connected value will be
00512                  * pushed twice: First by sacrifice, second by reset! -AV */
00513                 if (!op->last_sp)
00514                 {
00515                     trigger_move(op, 0);
00516                 }
00517                 else
00518                 {
00519                     op->stats.wc = 0;
00520                     op->value = !op->value;
00521                     op->speed = 0;
00522                     update_ob_speed(op);
00523                 }
00524             }
00525 
00526             return 0;
00527 
00528         case TRIGGER:
00529             if (cause)
00530             {
00531                 if (in_movement)
00532                 {
00533                     return 0;
00534                 }
00535 
00536                 push = 1;
00537             }
00538 
00539             SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push);
00540             update_object(op, UP_OBJ_FACE);
00541             trigger_move(op, push);
00542             return 1;
00543 
00544         default:
00545             LOG(llevDebug, "Unknown trigger type: %s (%d)\n", op->name, op->type);
00546             return 0;
00547     }
00548 }
00549 
00555 void add_button_link(object *button, mapstruct *map, int connected)
00556 {
00557     objectlink *obp, *ol = get_objectlink();
00558 
00559     if (!map)
00560     {
00561         LOG(llevBug, "Tried to add button-link without map.\n");
00562         return;
00563     }
00564 
00565     button->path_attuned = connected;
00566 
00567     SET_FLAG(button, FLAG_IS_LINKED);
00568 
00569     ol->objlink.ob = button;
00570     ol->id = button->count;
00571 
00572     for (obp = map->buttons; obp && obp->value != connected; obp = obp->next);
00573     {
00574         if (obp)
00575         {
00576             ol->next = obp->objlink.link;
00577             obp->objlink.link = ol;
00578         }
00579         else
00580         {
00581             obp = get_objectlink();
00582             obp->value = connected;
00583 
00584             obp->next = map->buttons;
00585             map->buttons = obp;
00586             obp->objlink.link = ol;
00587         }
00588     }
00589 }
00590 
00594 void remove_button_link(object *op)
00595 {
00596     objectlink *obp, **olp, *ol;
00597 
00598     if (op->map == NULL)
00599     {
00600         LOG(llevBug, "remove_button_link(): Object without map.\n");
00601         return;
00602     }
00603 
00604     if (!QUERY_FLAG(op, FLAG_IS_LINKED))
00605     {
00606         LOG(llevBug, "remove_button_link(): Unlinked object.\n");
00607         return;
00608     }
00609 
00610     for (obp = op->map->buttons; obp; obp = obp->next)
00611     {
00612         for (olp = &obp->objlink.link; (ol = *olp); olp = &ol->next)
00613         {
00614             if (ol->objlink.ob == op)
00615             {
00616                 *olp = ol->next;
00617                 free_objectlink_simple(ol);
00618                 return;
00619             }
00620         }
00621     }
00622 
00623     LOG(llevBug, "remove_button_link(): Couldn't find object.\n");
00624     CLEAR_FLAG(op, FLAG_IS_LINKED);
00625 }
00626 
00631 static objectlink *get_button_links(object *button)
00632 {
00633     objectlink *obp, *ol;
00634 
00635     if (!button->map)
00636     {
00637         return NULL;
00638     }
00639 
00640     for (obp = button->map->buttons; obp; obp = obp->next)
00641     {
00642         for (ol = obp->objlink.link; ol; ol = ol->next)
00643         {
00644             if (ol->objlink.ob == button && ol->id == button->count)
00645             {
00646                 return obp->objlink.link;
00647             }
00648         }
00649     }
00650 
00651     return NULL;
00652 }
00653 
00659 int get_button_value(object *button)
00660 {
00661     objectlink *obp, *ol;
00662 
00663     if (!button->map)
00664     {
00665         return 0;
00666     }
00667 
00668     for (obp = button->map->buttons; obp; obp = obp->next)
00669     {
00670         for (ol = obp->objlink.link; ol; ol = ol->next)
00671         {
00672             if (ol->objlink.ob == button && ol->id == button->count)
00673             {
00674                 return obp->value;
00675             }
00676         }
00677     }
00678 
00679     return 0;
00680 }
00681 
00692 void do_mood_floor(object *op)
00693 {
00694     object *tmp;
00695 
00696     for (tmp = op->above; tmp; tmp = tmp->above)
00697     {
00698         if (QUERY_FLAG(tmp, FLAG_MONSTER))
00699         {
00700             break;
00701         }
00702     }
00703 
00704     /* Doesn't affect players, and if there is a player on this space, won't also
00705      * be a monster here. */
00706     if (!tmp || tmp->type == PLAYER)
00707     {
00708         return;
00709     }
00710 
00711     switch (op->last_sp)
00712     {
00713         /* Furious -- makes all monsters mad */
00714         case 0:
00715             if (QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
00716             {
00717                 CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
00718             }
00719 
00720             if (QUERY_FLAG(tmp, FLAG_FRIENDLY))
00721             {
00722                 CLEAR_FLAG(tmp, FLAG_FRIENDLY);
00723                 tmp->move_type = 0;
00724                 tmp->owner = NULL;
00725             }
00726 
00727             break;
00728 
00729         /* Angry -- get neutral monsters mad */
00730         case 1:
00731             if (QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY))
00732             {
00733                 CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
00734             }
00735 
00736             break;
00737 
00738         /* Calm -- pacify unfriendly monsters */
00739         case 2:
00740             if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
00741             {
00742                 SET_FLAG(tmp, FLAG_UNAGGRESSIVE);
00743             }
00744 
00745             break;
00746 
00747         /* Make all monsters fall asleep */
00748         case 3:
00749             if (!QUERY_FLAG(tmp, FLAG_SLEEP))
00750             {
00751                 SET_FLAG(tmp, FLAG_SLEEP);
00752             }
00753 
00754             break;
00755 
00756         default:
00757             break;
00758     }
00759 }
00760 
00772 object *check_inv_recursive(object *op, const object *trig)
00773 {
00774     object *tmp, *ret = NULL;
00775 
00776     for (tmp = op->inv; tmp; tmp = tmp->below)
00777     {
00778         if (tmp->inv && !IS_SYS_INVISIBLE(tmp))
00779         {
00780             ret = check_inv_recursive(tmp, trig);
00781 
00782             if (ret)
00783             {
00784                 return ret;
00785             }
00786         }
00787         else if ((trig->stats.hp && tmp->type == trig->stats.hp) || (trig->slaying && trig->stats.sp ? (tmp->slaying && trig->slaying == tmp->slaying) : (tmp->name && trig->slaying == tmp->name)) || (trig->race && trig->race == tmp->arch->name))
00788         {
00789             return tmp;
00790         }
00791     }
00792 
00793     return NULL;
00794 }
00795 
00805 void check_inv(object *op, object *trig)
00806 {
00807     object *match;
00808 
00809     if (op->type != PLAYER)
00810     {
00811         return;
00812     }
00813 
00814     match = check_inv_recursive(op, trig);
00815 
00816     if (match && trig->last_sp)
00817     {
00818         if (trig->last_heal)
00819         {
00820             decrease_ob(match);
00821         }
00822 
00823         use_trigger(trig);
00824     }
00825     else if (!match && !trig->last_sp)
00826     {
00827         use_trigger(trig);
00828     }
00829 }