Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

mob_prog.c

Go to the documentation of this file.
00001 /***************************************************************************
00002  *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
00003  *  Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe.    *
00004  *                                                                         *
00005  *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
00006  *  Chastain, Michael Quan, and Mitchell Tse.                              *
00007  *                                                                         *
00008  *  In order to use any part of this Merc Diku Mud, you must comply with   *
00009  *  both the original Diku license in 'license.doc' as well the Merc       *
00010  *  license in 'license.txt'.  In particular, you may not remove either of *
00011  *  these copyright notices.                                               *
00012  *                                                                         *
00013  *  Much time and thought has gone into this software and you are          *
00014  *  benefitting.  We hope that you share your changes too.  What goes      *
00015  *  around, comes around.                                                  *
00016  ***************************************************************************/
00017 
00018 /***************************************************************************
00019  *  ROM 2.4 is copyright 1993-1998 Russ Taylor                             *
00020  *  ROM has been brought to you by the ROM consortium                      *
00021  *      Russ Taylor (rtaylor@hypercube.org)                                *
00022  *      Gabrielle Taylor (gtaylor@hypercube.org)                           *
00023  *      Brian Moore (zump@rom.org)                                         *
00024  *  By using this code, you have agreed to follow the terms of the         *
00025  *  ROM license, in the file Rom24/doc/rom.license                         *
00026  ***************************************************************************/
00027 
00028 /***************************************************************************
00029  *                                                                         *
00030  *  MOBprograms for ROM 2.4 v0.98g (C) M.Nylander 1996                     *
00031  *  Based on MERC 2.2 MOBprograms concept by N'Atas-ha.                    *
00032  *  Written and adapted to ROM 2.4 by                                      *
00033  *          Markku Nylander (markku.nylander@uta.fi)                       *
00034  *  This code may be copied and distributed as per the ROM license.        *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <time.h>
00042 #include <sys/types.h>
00043 #include <ctype.h>
00044 #include "merc.h"
00045 #include "tables.h"
00046 #include "lookup.h"
00047 
00048 extern int flag_lookup (const char *word, const struct flag_type *flag_table);
00049 
00050 /*
00051  * These defines correspond to the entries in fn_keyword[] table.
00052  * If you add a new if_check, you must also add a #define here.
00053  */
00054 #define CHK_RAND       (0)
00055 #define CHK_MOBHERE     (1)
00056 #define CHK_OBJHERE     (2)
00057 #define CHK_MOBEXISTS   (3)
00058 #define CHK_OBJEXISTS   (4)
00059 #define CHK_PEOPLE      (5)
00060 #define CHK_PLAYERS     (6)
00061 #define CHK_MOBS        (7)
00062 #define CHK_CLONES      (8)
00063 #define CHK_ORDER       (9)
00064 #define CHK_HOUR        (10)
00065 #define CHK_ISPC        (11)
00066 #define CHK_ISNPC       (12)
00067 #define CHK_ISGOOD      (13)
00068 #define CHK_ISEVIL      (14)
00069 #define CHK_ISNEUTRAL   (15)
00070 #define CHK_ISIMMORT    (16)
00071 #define CHK_ISCHARM     (17)
00072 #define CHK_ISFOLLOW    (18)
00073 #define CHK_ISACTIVE    (19)
00074 #define CHK_ISDELAY     (20)
00075 #define CHK_ISVISIBLE   (21)
00076 #define CHK_HASTARGET   (22)
00077 #define CHK_ISTARGET    (23)
00078 #define CHK_EXISTS      (24)
00079 #define CHK_AFFECTED    (25)
00080 #define CHK_ACT         (26)
00081 #define CHK_OFF         (27)
00082 #define CHK_IMM         (28)
00083 #define CHK_CARRIES     (29)
00084 #define CHK_WEARS       (30)
00085 #define CHK_HAS         (31)
00086 #define CHK_USES        (32)
00087 #define CHK_NAME        (33)
00088 #define CHK_POS         (34)
00089 #define CHK_CLAN        (35)
00090 #define CHK_RACE        (36)
00091 #define CHK_CLASS       (37)
00092 #define CHK_OBJTYPE     (38)
00093 #define CHK_VNUM        (39)
00094 #define CHK_HPCNT       (40)
00095 #define CHK_ROOM        (41)
00096 #define CHK_SEX         (42)
00097 #define CHK_LEVEL       (43)
00098 #define CHK_ALIGN       (44)
00099 #define CHK_MONEY       (45)
00100 #define CHK_OBJVAL0     (46)
00101 #define CHK_OBJVAL1     (47)
00102 #define CHK_OBJVAL2     (48)
00103 #define CHK_OBJVAL3     (49)
00104 #define CHK_OBJVAL4     (50)
00105 #define CHK_GRPSIZE     (51)
00106 
00107 /*
00108  * These defines correspond to the entries in fn_evals[] table.
00109  */
00110 #define EVAL_EQ            0
00111 #define EVAL_GE            1
00112 #define EVAL_LE            2
00113 #define EVAL_GT            3
00114 #define EVAL_LT            4
00115 #define EVAL_NE            5
00116 
00117 /*
00118  * if-check keywords:
00119  */
00120 const char *fn_keyword[] = {
00121     "rand",                        /* if rand 30       - if random number < 30 */
00122     "mobhere",                    /* if mobhere fido  - is there a 'fido' here */
00123     "objhere",                    /* if objhere bottle    - is there a 'bottle' here */
00124     /* if mobhere 1233  - is there mob vnum 1233 here */
00125     /* if objhere 1233  - is there obj vnum 1233 here */
00126     "mobexists",                /* if mobexists fido    - is there a fido somewhere */
00127     "objexists",                /* if objexists sword   - is there a sword somewhere */
00128 
00129     "people",                    /* if people > 4    - does room contain > 4 people */
00130     "players",                    /* if players > 1   - does room contain > 1 pcs */
00131     "mobs",                        /* if mobs > 2      - does room contain > 2 mobiles */
00132     "clones",                    /* if clones > 3    - are there > 3 mobs of same vnum here */
00133     "order",                    /* if order == 0    - is mob the first in room */
00134     "hour",                        /* if hour > 11     - is the time > 11 o'clock */
00135 
00136 
00137     "ispc",                        /* if ispc $n       - is $n a pc */
00138     "isnpc",                    /* if isnpc $n      - is $n a mobile */
00139     "isgood",                    /* if isgood $n     - is $n good */
00140     "isevil",                    /* if isevil $n     - is $n evil */
00141     "isneutral",                /* if isneutral $n  - is $n neutral */
00142     "isimmort",                    /* if isimmort $n   - is $n immortal */
00143     "ischarm",                    /* if ischarm $n    - is $n charmed */
00144     "isfollow",                    /* if isfollow $n   - is $n following someone */
00145     "isactive",                    /* if isactive $n   - is $n's position > SLEEPING */
00146     "isdelay",                    /* if isdelay $i    - does $i have mobprog pending */
00147     "isvisible",                /* if isvisible $n  - can mob see $n */
00148     "hastarget",                /* if hastarget $i  - does $i have a valid target */
00149     "istarget",                    /* if istarget $n   - is $n mob's target */
00150     "exists",                    /* if exists $n     - does $n exist somewhere */
00151 
00152     "affected",                    /* if affected $n blind - is $n affected by blind */
00153     "act",                        /* if act $i sentinel   - is $i flagged sentinel */
00154     "off",                        /* if off $i berserk    - is $i flagged berserk */
00155     "imm",                        /* if imm $i fire   - is $i immune to fire */
00156     "carries",                    /* if carries $n sword  - does $n have a 'sword' */
00157     /* if carries $n 1233   - does $n have obj vnum 1233 */
00158     "wears",                    /* if wears $n lantern  - is $n wearing a 'lantern' */
00159     /* if wears $n 1233 - is $n wearing obj vnum 1233 */
00160     "has",                        /* if has $n weapon - does $n have obj of type weapon */
00161     "uses",                        /* if uses $n armor - is $n wearing obj of type armor */
00162     "name",                        /* if name $n puff  - is $n's name 'puff' */
00163     "pos",                        /* if pos $n standing   - is $n standing */
00164     "clan",                        /* if clan $n 'whatever'- does $n belong to clan 'whatever' */
00165     "race",                        /* if race $n dragon    - is $n of 'dragon' race */
00166     "class",                    /* if class $n mage - is $n's class 'mage' */
00167     "objtype",                    /* if objtype $p scroll - is $p a scroll */
00168 
00169     "vnum",                        /* if vnum $i == 1233   - virtual number check */
00170     "hpcnt",                    /* if hpcnt $i > 30 - hit point percent check */
00171     "room",                        /* if room $i == 1233   - room virtual number */
00172     "sex",                        /* if sex $i == 0   - sex check */
00173     "level",                    /* if level $n < 5  - level check */
00174     "align",                    /* if align $n < -1000  - alignment check */
00175     "money",                    /* if money $n */
00176     "objval0",                    /* if objval0 > 1000    - object value[] checks 0..4 */
00177     "objval1",
00178     "objval2",
00179     "objval3",
00180     "objval4",
00181     "grpsize",                    /* if grpsize $n > 6    - group size check */
00182 
00183     "\n"                        /* Table terminator */
00184 };
00185 
00186 const char *fn_evals[] = {
00187     "==",
00188     ">=",
00189     "<=",
00190     ">",
00191     "<",
00192     "!=",
00193     "\n"
00194 };
00195 
00196 /*
00197  * Return a valid keyword from a keyword table
00198  */
00199 int keyword_lookup (const char **table, char *keyword)
00200 {
00201     register int i;
00202     for (i = 0; table[i][0] != '\n'; i++)
00203         if (!str_cmp (table[i], keyword))
00204             return (i);
00205     return -1;
00206 }
00207 
00208 /*
00209  * Perform numeric evaluation.
00210  * Called by cmd_eval()
00211  */
00212 int num_eval (int lval, int oper, int rval)
00213 {
00214     switch (oper)
00215     {
00216         case EVAL_EQ:
00217             return (lval == rval);
00218         case EVAL_GE:
00219             return (lval >= rval);
00220         case EVAL_LE:
00221             return (lval <= rval);
00222         case EVAL_NE:
00223             return (lval != rval);
00224         case EVAL_GT:
00225             return (lval > rval);
00226         case EVAL_LT:
00227             return (lval < rval);
00228         default:
00229             bug ("num_eval: invalid oper", 0);
00230             return 0;
00231     }
00232 }
00233 
00234 /*
00235  * ---------------------------------------------------------------------
00236  * UTILITY FUNCTIONS USED BY CMD_EVAL()
00237  * ----------------------------------------------------------------------
00238  */
00239 
00240 /*
00241  * Get a random PC in the room (for $r parameter)
00242  */
00243 CHAR_DATA *get_random_char (CHAR_DATA * mob)
00244 {
00245     CHAR_DATA *vch, *victim = NULL;
00246     int now = 0, highest = 0;
00247     for (vch = mob->in_room->people; vch; vch = vch->next_in_room)
00248     {
00249         if (mob != vch && !IS_NPC (vch)
00250             && can_see (mob, vch) && (now = number_percent ()) > highest)
00251         {
00252             victim = vch;
00253             highest = now;
00254         }
00255     }
00256     return victim;
00257 }
00258 
00259 /* 
00260  * How many other players / mobs are there in the room
00261  * iFlag: 0: all, 1: players, 2: mobiles 3: mobs w/ same vnum 4: same group
00262  */
00263 int count_people_room (CHAR_DATA * mob, int iFlag)
00264 {
00265     CHAR_DATA *vch;
00266     int count;
00267     for (count = 0, vch = mob->in_room->people; vch; vch = vch->next_in_room)
00268         if (mob != vch && (iFlag == 0 || (iFlag == 1 && !IS_NPC (vch))
00269                            || (iFlag == 2 && IS_NPC (vch))
00270                            || (iFlag == 3 && IS_NPC (mob) && IS_NPC (vch)
00271                                && mob->pIndexData->vnum ==
00272                                vch->pIndexData->vnum) || (iFlag == 4
00273                                                           &&
00274                                                           is_same_group (mob,
00275                                                                          vch)))
00276             && can_see (mob, vch))
00277             count++;
00278     return (count);
00279 }
00280 
00281 /*
00282  * Get the order of a mob in the room. Useful when several mobs in
00283  * a room have the same trigger and you want only the first of them
00284  * to act 
00285  */
00286 int get_order (CHAR_DATA * ch)
00287 {
00288     CHAR_DATA *vch;
00289     int i;
00290 
00291     if (!IS_NPC (ch))
00292         return 0;
00293     for (i = 0, vch = ch->in_room->people; vch; vch = vch->next_in_room)
00294     {
00295         if (vch == ch)
00296             return i;
00297         if (IS_NPC (vch) && vch->pIndexData->vnum == ch->pIndexData->vnum)
00298             i++;
00299     }
00300     return 0;
00301 }
00302 
00303 /*
00304  * Check if ch has a given item or item type
00305  * vnum: item vnum or -1
00306  * item_type: item type or -1
00307  * fWear: TRUE: item must be worn, FALSE: don't care
00308  */
00309 bool has_item (CHAR_DATA * ch, int vnum, sh_int item_type, bool fWear)
00310 {
00311     OBJ_DATA *obj;
00312     for (obj = ch->carrying; obj; obj = obj->next_content)
00313         if ((vnum < 0 || obj->pIndexData->vnum == vnum)
00314             && (item_type < 0 || obj->pIndexData->item_type == item_type)
00315             && (!fWear || obj->wear_loc != WEAR_NONE))
00316             return TRUE;
00317     return FALSE;
00318 }
00319 
00320 /*
00321  * Check if there's a mob with given vnum in the room
00322  */
00323 bool get_mob_vnum_room (CHAR_DATA * ch, int vnum)
00324 {
00325     CHAR_DATA *mob;
00326     for (mob = ch->in_room->people; mob; mob = mob->next_in_room)
00327         if (IS_NPC (mob) && mob->pIndexData->vnum == vnum)
00328             return TRUE;
00329     return FALSE;
00330 }
00331 
00332 /*
00333  * Check if there's an object with given vnum in the room
00334  */
00335 bool get_obj_vnum_room (CHAR_DATA * ch, int vnum)
00336 {
00337     OBJ_DATA *obj;
00338     for (obj = ch->in_room->contents; obj; obj = obj->next_content)
00339         if (obj->pIndexData->vnum == vnum)
00340             return TRUE;
00341     return FALSE;
00342 }
00343 
00344 /* ---------------------------------------------------------------------
00345  * CMD_EVAL
00346  * This monster evaluates an if/or/and statement
00347  * There are five kinds of statement:
00348  * 1) keyword and value (no $-code)        if random 30
00349  * 2) keyword, comparison and value        if people > 2
00350  * 3) keyword and actor                    if isnpc $n
00351  * 4) keyword, actor and value            if carries $n sword
00352  * 5) keyword, actor, comparison and value  if level $n >= 10
00353  *
00354  *----------------------------------------------------------------------
00355  */
00356 int cmd_eval (int vnum, char *line, int check,
00357               CHAR_DATA * mob, CHAR_DATA * ch,
00358               const void *arg1, const void *arg2, CHAR_DATA * rch)
00359 {
00360     CHAR_DATA *lval_char = mob;
00361     CHAR_DATA *vch = (CHAR_DATA *) arg2;
00362     OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
00363     OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
00364     OBJ_DATA *lval_obj = NULL;
00365 
00366     char *original, buf[MAX_INPUT_LENGTH], code;
00367     int lval = 0, oper = 0, rval = -1;
00368 
00369     original = line;
00370     line = one_argument (line, buf);
00371     if (buf[0] == '\0' || mob == NULL)
00372         return FALSE;
00373 
00374     /*
00375      * If this mobile has no target, let's assume our victim is the one
00376      */
00377     if (mob->mprog_target == NULL)
00378         mob->mprog_target = ch;
00379 
00380     switch (check)
00381     {
00382             /*
00383              * Case 1: keyword and value
00384              */
00385         case CHK_RAND:
00386             return (atoi (buf) < number_percent ());
00387         case CHK_MOBHERE:
00388             if (is_number (buf))
00389                 return (get_mob_vnum_room (mob, atoi (buf)));
00390             else
00391                 return ((bool) (get_char_room (mob, buf) != NULL));
00392         case CHK_OBJHERE:
00393             if (is_number (buf))
00394                 return (get_obj_vnum_room (mob, atoi (buf)));
00395             else
00396                 return ((bool) (get_obj_here (mob, buf) != NULL));
00397         case CHK_MOBEXISTS:
00398             return ((bool) (get_char_world (mob, buf) != NULL));
00399         case CHK_OBJEXISTS:
00400             return ((bool) (get_obj_world (mob, buf) != NULL));
00401             /*
00402              * Case 2 begins here: We sneakily use rval to indicate need
00403              *             for numeric eval...
00404              */
00405         case CHK_PEOPLE:
00406             rval = count_people_room (mob, 0);
00407             break;
00408         case CHK_PLAYERS:
00409             rval = count_people_room (mob, 1);
00410             break;
00411         case CHK_MOBS:
00412             rval = count_people_room (mob, 2);
00413             break;
00414         case CHK_CLONES:
00415             rval = count_people_room (mob, 3);
00416             break;
00417         case CHK_ORDER:
00418             rval = get_order (mob);
00419             break;
00420         case CHK_HOUR:
00421             rval = time_info.hour;
00422             break;
00423         default:;
00424     }
00425 
00426     /*
00427      * Case 2 continued: evaluate expression
00428      */
00429     if (rval >= 0)
00430     {
00431         if ((oper = keyword_lookup (fn_evals, buf)) < 0)
00432         {
00433             sprintf (buf, "Cmd_eval: prog %d syntax error(2) '%s'",
00434                      vnum, original);
00435             bug (buf, 0);
00436             return FALSE;
00437         }
00438         one_argument (line, buf);
00439         lval = rval;
00440         rval = atoi (buf);
00441         return (num_eval (lval, oper, rval));
00442     }
00443 
00444     /*
00445      * Case 3,4,5: Grab actors from $* codes
00446      */
00447     if (buf[0] != '$' || buf[1] == '\0')
00448     {
00449         sprintf (buf, "Cmd_eval: prog %d syntax error(3) '%s'",
00450                  vnum, original);
00451         bug (buf, 0);
00452         return FALSE;
00453     }
00454     else
00455         code = buf[1];
00456     switch (code)
00457     {
00458         case 'i':
00459             lval_char = mob;
00460             break;
00461         case 'n':
00462             lval_char = ch;
00463             break;
00464         case 't':
00465             lval_char = vch;
00466             break;
00467         case 'r':
00468             lval_char = rch == NULL ? get_random_char (mob) : rch;
00469             break;
00470         case 'o':
00471             lval_obj = obj1;
00472             break;
00473         case 'p':
00474             lval_obj = obj2;
00475             break;
00476         case 'q':
00477             lval_char = mob->mprog_target;
00478             break;
00479         default:
00480             sprintf (buf, "Cmd_eval: prog %d syntax error(4) '%s'",
00481                      vnum, original);
00482             bug (buf, 0);
00483             return FALSE;
00484     }
00485     /*
00486      * From now on, we need an actor, so if none was found, bail out
00487      */
00488     if (lval_char == NULL && lval_obj == NULL)
00489         return FALSE;
00490 
00491     /*
00492      * Case 3: Keyword, comparison and value
00493      */
00494     switch (check)
00495     {
00496         case CHK_ISPC:
00497             return (lval_char != NULL && !IS_NPC (lval_char));
00498         case CHK_ISNPC:
00499             return (lval_char != NULL && IS_NPC (lval_char));
00500         case CHK_ISGOOD:
00501             return (lval_char != NULL && IS_GOOD (lval_char));
00502         case CHK_ISEVIL:
00503             return (lval_char != NULL && IS_EVIL (lval_char));
00504         case CHK_ISNEUTRAL:
00505             return (lval_char != NULL && IS_NEUTRAL (lval_char));
00506         case CHK_ISIMMORT:
00507             return (lval_char != NULL && IS_IMMORTAL (lval_char));
00508         case CHK_ISCHARM:        /* A relic from MERC 2.2 MOBprograms */
00509             return (lval_char != NULL && IS_AFFECTED (lval_char, AFF_CHARM));
00510         case CHK_ISFOLLOW:
00511             return (lval_char != NULL && lval_char->master != NULL
00512                     && lval_char->master->in_room == lval_char->in_room);
00513         case CHK_ISACTIVE:
00514             return (lval_char != NULL && lval_char->position > POS_SLEEPING);
00515         case CHK_ISDELAY:
00516             return (lval_char != NULL && lval_char->mprog_delay > 0);
00517         case CHK_ISVISIBLE:
00518             switch (code)
00519             {
00520                 default:
00521                 case 'i':
00522                 case 'n':
00523                 case 't':
00524                 case 'r':
00525                 case 'q':
00526                     return (lval_char != NULL && can_see (mob, lval_char));
00527                 case 'o':
00528                 case 'p':
00529                     return (lval_obj != NULL && can_see_obj (mob, lval_obj));
00530             }
00531         case CHK_HASTARGET:
00532             return (lval_char != NULL && lval_char->mprog_target != NULL
00533                     && lval_char->in_room ==
00534                     lval_char->mprog_target->in_room);
00535         case CHK_ISTARGET:
00536             return (lval_char != NULL && mob->mprog_target == lval_char);
00537         default:;
00538     }
00539 
00540     /* 
00541      * Case 4: Keyword, actor and value
00542      */
00543     line = one_argument (line, buf);
00544     switch (check)
00545     {
00546         case CHK_AFFECTED:
00547             return (lval_char != NULL
00548                     && IS_SET (lval_char->affected_by,
00549                                flag_lookup (buf, affect_flags)));
00550         case CHK_ACT:
00551             return (lval_char != NULL
00552                     && IS_SET (lval_char->act, flag_lookup (buf, act_flags)));
00553         case CHK_IMM:
00554             return (lval_char != NULL
00555                     && IS_SET (lval_char->imm_flags,
00556                                flag_lookup (buf, imm_flags)));
00557         case CHK_OFF:
00558             return (lval_char != NULL
00559                     && IS_SET (lval_char->off_flags,
00560                                flag_lookup (buf, off_flags)));
00561         case CHK_CARRIES:
00562             if (is_number (buf))
00563                 return (lval_char != NULL
00564                         && has_item (lval_char, atoi (buf), -1, FALSE));
00565             else
00566                 return (lval_char != NULL
00567                         && (get_obj_carry (lval_char, buf, lval_char) !=
00568                             NULL));
00569         case CHK_WEARS:
00570             if (is_number (buf))
00571                 return (lval_char != NULL
00572                         && has_item (lval_char, atoi (buf), -1, TRUE));
00573             else
00574                 return (lval_char != NULL
00575                         && (get_obj_wear (lval_char, buf) != NULL));
00576         case CHK_HAS:
00577             return (lval_char != NULL
00578                     && has_item (lval_char, -1, item_lookup (buf), FALSE));
00579         case CHK_USES:
00580             return (lval_char != NULL
00581                     && has_item (lval_char, -1, item_lookup (buf), TRUE));
00582         case CHK_NAME:
00583             switch (code)
00584             {
00585                 default:
00586                 case 'i':
00587                 case 'n':
00588                 case 't':
00589                 case 'r':
00590                 case 'q':
00591                     return (lval_char != NULL
00592                             && is_name (buf, lval_char->name));
00593                 case 'o':
00594                 case 'p':
00595                     return (lval_obj != NULL
00596                             && is_name (buf, lval_obj->name));
00597             }
00598         case CHK_POS:
00599             return (lval_char != NULL
00600                     && lval_char->position == position_lookup (buf));
00601         case CHK_CLAN:
00602             return (lval_char != NULL
00603                     && lval_char->clan == clan_lookup (buf));
00604         case CHK_RACE:
00605             return (lval_char != NULL
00606                     && lval_char->race == race_lookup (buf));
00607         case CHK_CLASS:
00608             return (lval_char != NULL
00609                     && lval_char->class == class_lookup (buf));
00610         case CHK_OBJTYPE:
00611             return (lval_obj != NULL
00612                     && lval_obj->item_type == item_lookup (buf));
00613         default:;
00614     }
00615 
00616     /*
00617      * Case 5: Keyword, actor, comparison and value
00618      */
00619     if ((oper = keyword_lookup (fn_evals, buf)) < 0)
00620     {
00621         sprintf (buf, "Cmd_eval: prog %d syntax error(5): '%s'",
00622                  vnum, original);
00623         bug (buf, 0);
00624         return FALSE;
00625     }
00626     one_argument (line, buf);
00627     rval = atoi (buf);
00628 
00629     switch (check)
00630     {
00631         case CHK_VNUM:
00632             switch (code)
00633             {
00634                 default:
00635                 case 'i':
00636                 case 'n':
00637                 case 't':
00638                 case 'r':
00639                 case 'q':
00640                     if (lval_char != NULL && IS_NPC (lval_char))
00641                         lval = lval_char->pIndexData->vnum;
00642                     break;
00643                 case 'o':
00644                 case 'p':
00645                     if (lval_obj != NULL)
00646                         lval = lval_obj->pIndexData->vnum;
00647             }
00648             break;
00649         case CHK_HPCNT:
00650             if (lval_char != NULL)
00651                 lval =
00652                     (lval_char->hit * 100) / (UMAX (1, lval_char->max_hit));
00653             break;
00654         case CHK_ROOM:
00655             if (lval_char != NULL && lval_char->in_room != NULL)
00656                 lval = lval_char->in_room->vnum;
00657             break;
00658         case CHK_SEX:
00659             if (lval_char != NULL)
00660                 lval = lval_char->sex;
00661             break;
00662         case CHK_LEVEL:
00663             if (lval_char != NULL)
00664                 lval = lval_char->level;
00665             break;
00666         case CHK_ALIGN:
00667             if (lval_char != NULL)
00668                 lval = lval_char->alignment;
00669             break;
00670         case CHK_MONEY:        /* Money is converted to silver... */
00671             if (lval_char != NULL)
00672                 lval = lval_char->gold + (lval_char->silver * 100);
00673             break;
00674         case CHK_OBJVAL0:
00675             if (lval_obj != NULL)
00676                 lval = lval_obj->value[0];
00677             break;
00678         case CHK_OBJVAL1:
00679             if (lval_obj != NULL)
00680                 lval = lval_obj->value[1];
00681             break;
00682         case CHK_OBJVAL2:
00683             if (lval_obj != NULL)
00684                 lval = lval_obj->value[2];
00685             break;
00686         case CHK_OBJVAL3:
00687             if (lval_obj != NULL)
00688                 lval = lval_obj->value[3];
00689             break;
00690         case CHK_OBJVAL4:
00691             if (lval_obj != NULL)
00692                 lval = lval_obj->value[4];
00693             break;
00694         case CHK_GRPSIZE:
00695             if (lval_char != NULL)
00696                 lval = count_people_room (lval_char, 4);
00697             break;
00698         default:
00699             return FALSE;
00700     }
00701     return (num_eval (lval, oper, rval));
00702 }
00703 
00704 /*
00705  * ------------------------------------------------------------------------
00706  * EXPAND_ARG
00707  * This is a hack of act() in comm.c. I've added some safety guards,
00708  * so that missing or invalid $-codes do not crash the server
00709  * ------------------------------------------------------------------------
00710  */
00711 void expand_arg (char *buf,
00712                  const char *format,
00713                  CHAR_DATA * mob, CHAR_DATA * ch,
00714                  const void *arg1, const void *arg2, CHAR_DATA * rch)
00715 {
00716     static char *const he_she[] = { "it", "he", "she" };
00717     static char *const him_her[] = { "it", "him", "her" };
00718     static char *const his_her[] = { "its", "his", "her" };
00719     const char *someone = "someone";
00720     const char *something = "something";
00721     const char *someones = "someone's";
00722 
00723     char fname[MAX_INPUT_LENGTH];
00724     CHAR_DATA *vch = (CHAR_DATA *) arg2;
00725     OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
00726     OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
00727     const char *str;
00728     const char *i;
00729     char *point;
00730 
00731     /*
00732      * Discard null and zero-length messages.
00733      */
00734     if (format == NULL || format[0] == '\0')
00735         return;
00736 
00737     point = buf;
00738     str = format;
00739     while (*str != '\0')
00740     {
00741         if (*str != '$')
00742         {
00743             *point++ = *str++;
00744             continue;
00745         }
00746         ++str;
00747 
00748         switch (*str)
00749         {
00750             default:
00751                 bug ("Expand_arg: bad code %d.", *str);
00752                 i = " <@@@> ";
00753                 break;
00754             case 'i':
00755                 one_argument (mob->name, fname);
00756                 i = fname;
00757                 break;
00758             case 'I':
00759                 i = mob->short_descr;
00760                 break;
00761             case 'n':
00762                 i = someone;
00763                 if (ch != NULL && can_see (mob, ch))
00764                 {
00765                     one_argument (ch->name, fname);
00766                     i = capitalize (fname);
00767                 }
00768                 break;
00769             case 'N':
00770                 i = (ch != NULL && can_see (mob, ch))
00771                     ? (IS_NPC (ch) ? ch->short_descr : ch->name) : someone;
00772                 break;
00773             case 't':
00774                 i = someone;
00775                 if (vch != NULL && can_see (mob, vch))
00776                 {
00777                     one_argument (vch->name, fname);
00778                     i = capitalize (fname);
00779                 }
00780                 break;
00781             case 'T':
00782                 i = (vch != NULL && can_see (mob, vch))
00783                     ? (IS_NPC (vch) ? vch->short_descr : vch->name) : someone;
00784                 break;
00785             case 'r':
00786                 if (rch == NULL)
00787                     rch = get_random_char (mob);
00788                 i = someone;
00789                 if (rch != NULL && can_see (mob, rch))
00790                 {
00791                     one_argument (rch->name, fname);
00792                     i = capitalize (fname);
00793                 }
00794                 break;
00795             case 'R':
00796                 if (rch == NULL)
00797                     rch = get_random_char (mob);
00798                 i = (rch != NULL && can_see (mob, rch))
00799                     ? (IS_NPC (ch) ? ch->short_descr : ch->name) : someone;
00800                 break;
00801             case 'q':
00802                 i = someone;
00803                 if (mob->mprog_target != NULL
00804                     && can_see (mob, mob->mprog_target))
00805                 {
00806                     one_argument (mob->mprog_target->name, fname);
00807                     i = capitalize (fname);
00808                 }
00809                 break;
00810             case 'Q':
00811                 i = (mob->mprog_target != NULL
00812                      && can_see (mob,
00813                                  mob->
00814                                  mprog_target)) ? (IS_NPC (mob->mprog_target)
00815                                                    ? mob->
00816                                                    mprog_target->short_descr :
00817                                                    mob->mprog_target->name) :
00818                     someone;
00819                 break;
00820             case 'j':
00821                 i = he_she[URANGE (0, mob->sex, 2)];
00822                 break;
00823             case 'e':
00824                 i = (ch != NULL && can_see (mob, ch))
00825                     ? he_she[URANGE (0, ch->sex, 2)] : someone;
00826                 break;
00827             case 'E':
00828                 i = (vch != NULL && can_see (mob, vch))
00829                     ? he_she[URANGE (0, vch->sex, 2)] : someone;
00830                 break;
00831             case 'J':
00832                 i = (rch != NULL && can_see (mob, rch))
00833                     ? he_she[URANGE (0, rch->sex, 2)] : someone;
00834                 break;
00835             case 'X':
00836                 i = (mob->mprog_target != NULL
00837                      && can_see (mob, mob->mprog_target)) ? he_she[URANGE (0,
00838                                                                            mob->mprog_target->sex,
00839                                                                            2)]
00840                     : someone; break;
00841             case 'k':
00842                 i = him_her[URANGE (0, mob->sex, 2)];
00843                 break;
00844             case 'm':
00845                 i = (ch != NULL && can_see (mob, ch))
00846                     ? him_her[URANGE (0, ch->sex, 2)] : someone;
00847                 break;
00848             case 'M':
00849                 i = (vch != NULL && can_see (mob, vch))
00850                     ? him_her[URANGE (0, vch->sex, 2)] : someone;
00851                 break;
00852             case 'K':
00853                 if (rch == NULL)
00854                     rch = get_random_char (mob);
00855                 i = (rch != NULL && can_see (mob, rch))
00856                     ? him_her[URANGE (0, rch->sex, 2)] : someone;
00857                 break;
00858             case 'Y':
00859                 i = (mob->mprog_target != NULL
00860                      && can_see (mob, mob->mprog_target)) ? him_her[URANGE (0,
00861                                                                             mob->mprog_target->sex,
00862                                                                             2)]
00863                     : someone; break;
00864             case 'l':
00865                 i = his_her[URANGE (0, mob->sex, 2)];
00866                 break;
00867             case 's':
00868                 i = (ch != NULL && can_see (mob, ch))
00869                     ? his_her[URANGE (0, ch->sex, 2)] : someones;
00870                 break;
00871             case 'S':
00872                 i = (vch != NULL && can_see (mob, vch))
00873                     ? his_her[URANGE (0, vch->sex, 2)] : someones;
00874                 break;
00875             case 'L':
00876                 if (rch == NULL)
00877                     rch = get_random_char (mob);
00878                 i = (rch != NULL && can_see (mob, rch))
00879                     ? his_her[URANGE (0, rch->sex, 2)] : someones;
00880                 break;
00881             case 'Z':
00882                 i = (mob->mprog_target != NULL
00883                      && can_see (mob, mob->mprog_target)) ? his_her[URANGE (0,
00884                                                                             mob->mprog_target->sex,
00885                                                                             2)]
00886                     : someones; break;
00887             case 'o':
00888                 i = something;
00889                 if (obj1 != NULL && can_see_obj (mob, obj1))
00890                 {
00891                     one_argument (obj1->name, fname);
00892                     i = fname;
00893                 }
00894                 break;
00895             case 'O':
00896                 i = (obj1 != NULL && can_see_obj (mob, obj1))
00897                     ? obj1->short_descr : something;
00898                 break;
00899             case 'p':
00900                 i = something;
00901                 if (obj2 != NULL && can_see_obj (mob, obj2))
00902                 {
00903                     one_argument (obj2->name, fname);
00904                     i = fname;
00905                 }
00906                 break;
00907             case 'P':
00908                 i = (obj2 != NULL && can_see_obj (mob, obj2))
00909                     ? obj2->short_descr : something;
00910                 break;
00911         }
00912 
00913         ++str;
00914         while ((*point = *i) != '\0')
00915             ++point, ++i;
00916 
00917     }
00918     *point = '\0';
00919 
00920     return;
00921 }
00922 
00923 /*
00924  * ------------------------------------------------------------------------
00925  *  PROGRAM_FLOW
00926  *  This is the program driver. It parses the mob program code lines
00927  *  and passes "executable" commands to interpret()
00928  *  Lines beginning with 'mob' are passed to mob_interpret() to handle
00929  *  special mob commands (in mob_cmds.c)
00930  *-------------------------------------------------------------------------
00931  */
00932 
00933 #define MAX_NESTED_LEVEL 12        /* Maximum nested if-else-endif's (stack size) */
00934 #define BEGIN_BLOCK       0        /* Flag: Begin of if-else-endif block */
00935 #define IN_BLOCK         -1        /* Flag: Executable statements */
00936 #define END_BLOCK        -2        /* Flag: End of if-else-endif block */
00937 #define MAX_CALL_LEVEL    10        /* Maximum nested calls */
00938 
00939 void program_flow (int pvnum,    /* For diagnostic purposes */
00940                    char *source,    /* the actual MOBprog code */
00941                    CHAR_DATA * mob, CHAR_DATA * ch, const void *arg1,
00942                    const void *arg2)
00943 {
00944     CHAR_DATA *rch = NULL;
00945     char *code, *line;
00946     char buf[MAX_STRING_LENGTH];
00947     char control[MAX_INPUT_LENGTH], data[MAX_STRING_LENGTH];
00948 
00949     static int call_level;        /* Keep track of nested "mpcall"s */
00950 
00951     int level, eval, check;
00952     int state[MAX_NESTED_LEVEL],    /* Block state (BEGIN,IN,END) */
00953       cond[MAX_NESTED_LEVEL];    /* Boolean value based on the last if-check */
00954 
00955     sh_int mvnum = mob->pIndexData->vnum;
00956 
00957     if (++call_level > MAX_CALL_LEVEL)
00958     {
00959         bug ("MOBprogs: MAX_CALL_LEVEL exceeded, vnum %d",
00960              mob->pIndexData->vnum);
00961         return;
00962     }
00963 
00964     /*
00965      * Reset "stack"
00966      */
00967     for (level = 0; level < MAX_NESTED_LEVEL; level++)
00968     {
00969         state[level] = IN_BLOCK;
00970         cond[level] = TRUE;
00971     }
00972     level = 0;
00973 
00974     code = source;
00975     /*
00976      * Parse the MOBprog code
00977      */
00978     while (*code)
00979     {
00980         bool first_arg = TRUE;
00981         char *b = buf, *c = control, *d = data;
00982         /*
00983          * Get a command line. We sneakily get both the control word
00984          * (if/and/or) and the rest of the line in one pass.
00985          */
00986         while (isspace (*code) && *code)
00987             code++;
00988         while (*code)
00989         {
00990             if (*code == '\n' || *code == '\r')
00991                 break;
00992             else if (isspace (*code))
00993             {
00994                 if (first_arg)
00995                     first_arg = FALSE;
00996                 else
00997                     *d++ = *code;
00998             }
00999             else
01000             {
01001                 if (first_arg)
01002                     *c++ = *code;
01003                 else
01004                     *d++ = *code;
01005             }
01006             *b++ = *code++;
01007         }
01008         *b = *c = *d = '\0';
01009 
01010         if (buf[0] == '\0')
01011             break;
01012         if (buf[0] == '*')        /* Comment */
01013             continue;
01014 
01015         line = data;
01016         /* 
01017          * Match control words
01018          */
01019         if (!str_cmp (control, "if"))
01020         {
01021             if (state[level] == BEGIN_BLOCK)
01022             {
01023                 sprintf (buf,
01024                          "Mobprog: misplaced if statement, mob %d prog %d",
01025                          mvnum, pvnum);
01026                 bug (buf, 0);
01027                 return;
01028             }
01029             state[level] = BEGIN_BLOCK;
01030             if (++level >= MAX_NESTED_LEVEL)
01031             {
01032                 sprintf (buf,
01033                          "Mobprog: Max nested level exceeded, mob %d prog %d",
01034                          mvnum, pvnum);
01035                 bug (buf, 0);
01036                 return;
01037             }
01038             if (level && cond[level - 1] == FALSE)
01039             {
01040                 cond[level] = FALSE;
01041                 continue;
01042             }
01043             line = one_argument (line, control);
01044             if ((check = keyword_lookup (fn_keyword, control)) >= 0)
01045             {
01046                 cond[level] =
01047                     cmd_eval (pvnum, line, check, mob, ch, arg1, arg2, rch);
01048             }
01049             else
01050             {
01051                 sprintf (buf,
01052                          "Mobprog: invalid if_check (if), mob %d prog %d",
01053                          mvnum, pvnum);
01054                 bug (buf, 0);
01055                 return;
01056             }
01057             state[level] = END_BLOCK;
01058         }
01059         else if (!str_cmp (control, "or"))
01060         {
01061             if (!level || state[level - 1] != BEGIN_BLOCK)
01062             {
01063                 sprintf (buf, "Mobprog: or without if, mob %d prog %d",
01064                          mvnum, pvnum);
01065                 bug (buf, 0);
01066                 return;
01067             }
01068             if (level && cond[level - 1] == FALSE)
01069                 continue;
01070             line = one_argument (line, control);
01071             if ((check = keyword_lookup (fn_keyword, control)) >= 0)
01072             {
01073                 eval =
01074                     cmd_eval (pvnum, line, check, mob, ch, arg1, arg2, rch);
01075             }
01076             else
01077             {
01078                 sprintf (buf,
01079                          "Mobprog: invalid if_check (or), mob %d prog %d",
01080                          mvnum, pvnum);
01081                 bug (buf, 0);
01082                 return;
01083             }
01084             cond[level] = (eval == TRUE) ? TRUE : cond[level];
01085         }
01086         else if (!str_cmp (control, "and"))
01087         {
01088             if (!level || state[level - 1] != BEGIN_BLOCK)
01089             {
01090                 sprintf (buf, "Mobprog: and without if, mob %d prog %d",
01091                          mvnum, pvnum);
01092                 bug (buf, 0);
01093                 return;
01094             }
01095             if (level && cond[level - 1] == FALSE)
01096                 continue;
01097             line = one_argument (line, control);
01098             if ((check = keyword_lookup (fn_keyword, control)) >= 0)
01099             {
01100                 eval =
01101                     cmd_eval (pvnum, line, check, mob, ch, arg1, arg2, rch);
01102             }
01103             else
01104             {
01105                 sprintf (buf,
01106                          "Mobprog: invalid if_check (and), mob %d prog %d",
01107                          mvnum, pvnum);
01108                 bug (buf, 0);
01109                 return;
01110             }
01111             cond[level] = (cond[level] == TRUE)
01112                 && (eval == TRUE) ? TRUE : FALSE;
01113         }
01114         else if (!str_cmp (control, "endif"))
01115         {
01116             if (!level || state[level - 1] != BEGIN_BLOCK)
01117             {
01118                 sprintf (buf, "Mobprog: endif without if, mob %d prog %d",
01119                          mvnum, pvnum);
01120                 bug (buf, 0);
01121                 return;
01122             }
01123             cond[level] = TRUE;
01124             state[level] = IN_BLOCK;
01125             state[--level] = END_BLOCK;
01126         }
01127         else if (!str_cmp (control, "else"))
01128         {
01129             if (!level || state[level - 1] != BEGIN_BLOCK)
01130             {
01131                 sprintf (buf, "Mobprog: else without if, mob %d prog %d",
01132                          mvnum, pvnum);
01133                 bug (buf, 0);
01134                 return;
01135             }
01136             if (level && cond[level - 1] == FALSE)
01137                 continue;
01138             state[level] = IN_BLOCK;
01139             cond[level] = (cond[level] == TRUE) ? FALSE : TRUE;
01140         }
01141         else if (cond[level] == TRUE
01142                  && (!str_cmp (control, "break")
01143                      || !str_cmp (control, "end")))
01144         {
01145             call_level--;
01146             return;
01147         }
01148         else if ((!level || cond[level] == TRUE) && buf[0] != '\0')
01149         {
01150             state[level] = IN_BLOCK;
01151             expand_arg (data, buf, mob, ch, arg1, arg2, rch);
01152             if (!str_cmp (control, "mob"))
01153             {
01154                 /* 
01155                  * Found a mob restricted command, pass it to mob interpreter
01156                  */
01157                 line = one_argument (data, control);
01158                 mob_interpret (mob, line);
01159             }
01160             else
01161             {
01162                 /* 
01163                  * Found a normal mud command, pass it to interpreter
01164                  */
01165                 interpret (mob, data);
01166             }
01167         }
01168     }
01169     call_level--;
01170 }
01171 
01172 /* 
01173  * ---------------------------------------------------------------------
01174  * Trigger handlers. These are called from various parts of the code
01175  * when an event is triggered.
01176  * ---------------------------------------------------------------------
01177  */
01178 
01179 /*
01180  * A general purpose string trigger. Matches argument to a string trigger
01181  * phrase.
01182  */
01183 void mp_act_trigger (
01184                      char *argument, CHAR_DATA * mob, CHAR_DATA * ch,
01185                      const void *arg1, const void *arg2, int type)
01186 {
01187     MPROG_LIST *prg;
01188 
01189     if (!IS_NPC(mob)) return;
01190 
01191     for (prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next)
01192     {
01193         if (prg->trig_type == type
01194             && strstr (argument, prg->trig_phrase) != NULL)
01195         {
01196             program_flow (prg->vnum, prg->code, mob, ch, arg1, arg2);
01197             break;
01198         }
01199     }
01200     return;
01201 }
01202 
01203 /*
01204  * A general purpose percentage trigger. Checks if a random percentage
01205  * number is less than trigger phrase
01206  */
01207 bool mp_percent_trigger (CHAR_DATA * mob, CHAR_DATA * ch,
01208                          const void *arg1, const void *arg2, int type)
01209 {
01210     MPROG_LIST *prg;
01211 
01212     for (prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next)
01213     {
01214         if (prg->trig_type == type
01215             && number_percent () < atoi (prg->trig_phrase))
01216         {
01217             program_flow (prg->vnum, prg->code, mob, ch, arg1, arg2);
01218             return (TRUE);
01219         }
01220     }
01221     return (FALSE);
01222 }
01223 
01224 void mp_bribe_trigger (CHAR_DATA * mob, CHAR_DATA * ch, int amount)
01225 {
01226     MPROG_LIST *prg;
01227 
01228     /*
01229      * Original MERC 2.2 MOBprograms used to create a money object
01230      * and give it to the mobile. WFT was that? Funcs in act_obj()
01231      * handle it just fine.
01232      */
01233     for (prg = mob->pIndexData->mprogs; prg; prg = prg->next)
01234     {
01235         if (prg->trig_type == TRIG_BRIBE && amount >= atoi (prg->trig_phrase))
01236         {
01237             program_flow (prg->vnum, prg->code, mob, ch, NULL, NULL);
01238             break;
01239         }
01240     }
01241     return;
01242 }
01243 
01244 bool mp_exit_trigger (CHAR_DATA * ch, int dir)
01245 {
01246     CHAR_DATA *mob;
01247     MPROG_LIST *prg;
01248 
01249     for (mob = ch->in_room->people; mob != NULL; mob = mob->next_in_room)
01250     {
01251         if (IS_NPC (mob)
01252             && (HAS_TRIGGER (mob, TRIG_EXIT)
01253                 || HAS_TRIGGER (mob, TRIG_EXALL)))
01254         {
01255             for (prg = mob->pIndexData->mprogs; prg; prg = prg->next)
01256             {
01257                 /*
01258                  * Exit trigger works only if the mobile is not busy
01259                  * (fighting etc.). If you want to be sure all players
01260                  * are caught, use ExAll trigger
01261                  */
01262                 if (prg->trig_type == TRIG_EXIT
01263                     && dir == atoi (prg->trig_phrase)
01264                     && mob->position == mob->pIndexData->default_pos
01265                     && can_see (mob, ch))
01266                 {
01267                     program_flow (prg->vnum, prg->code, mob, ch, NULL, NULL);
01268                     return TRUE;
01269                 }
01270                 else
01271                     if (prg->trig_type == TRIG_EXALL
01272                         && dir == atoi (prg->trig_phrase))
01273                 {
01274                     program_flow (prg->vnum, prg->code, mob, ch, NULL, NULL);
01275                     return TRUE;
01276                 }
01277             }
01278         }
01279     }
01280     return FALSE;
01281 }
01282 
01283 void mp_give_trigger (CHAR_DATA * mob, CHAR_DATA * ch, OBJ_DATA * obj)
01284 {
01285 
01286     char buf[MAX_INPUT_LENGTH], *p;
01287     MPROG_LIST *prg;
01288 
01289     for (prg = mob->pIndexData->mprogs; prg; prg = prg->next)
01290         if (prg->trig_type == TRIG_GIVE)
01291         {
01292             p = prg->trig_phrase;
01293             /*
01294              * Vnum argument
01295              */
01296             if (is_number (p))
01297             {
01298                 if (obj->pIndexData->vnum == atoi (p))
01299                 {
01300                     program_flow (prg->vnum, prg->code, mob, ch, (void *) obj,
01301                                   NULL);
01302                     return;
01303                 }
01304             }
01305             /*
01306              * Object name argument, e.g. 'sword'
01307              */
01308             else
01309             {
01310                 while (*p)
01311                 {
01312                     p = one_argument (p, buf);
01313 
01314                     if (is_name (buf, obj->name) || !str_cmp ("all", buf))
01315                     {
01316                         program_flow (prg->vnum, prg->code, mob, ch,
01317                                       (void *) obj, NULL);
01318                         return;
01319                     }
01320                 }
01321             }
01322         }
01323 }
01324 
01325 void mp_greet_trigger (CHAR_DATA * ch)
01326 {
01327     CHAR_DATA *mob;
01328 
01329     for (mob = ch->in_room->people; mob != NULL; mob = mob->next_in_room)
01330     {
01331         if (IS_NPC (mob)
01332             && (HAS_TRIGGER (mob, TRIG_GREET)
01333                 || HAS_TRIGGER (mob, TRIG_GRALL)))
01334         {
01335             /*
01336              * Greet trigger works only if the mobile is not busy
01337              * (fighting etc.). If you want to catch all players, use
01338              * GrAll trigger
01339              */
01340             if (HAS_TRIGGER (mob, TRIG_GREET)
01341                 && mob->position == mob->pIndexData->default_pos
01342                 && can_see (mob, ch))
01343                 mp_percent_trigger (mob, ch, NULL, NULL, TRIG_GREET);
01344             else if (HAS_TRIGGER (mob, TRIG_GRALL))
01345                 mp_percent_trigger (mob, ch, NULL, NULL, TRIG_GRALL);
01346         }
01347     }
01348     return;
01349 }
01350 
01351 void mp_hprct_trigger (CHAR_DATA * mob, CHAR_DATA * ch)
01352 {
01353     MPROG_LIST *prg;
01354 
01355     for (prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next)
01356         if ((prg->trig_type == TRIG_HPCNT)
01357             && ((100 * mob->hit / mob->max_hit) < atoi (prg->trig_phrase)))
01358         {
01359             program_flow (prg->vnum, prg->code, mob, ch, NULL, NULL);
01360             break;
01361         }
01362 }

Generated on Thu Jan 13 21:48:12 2005 for Beyond the Shadows by  doxygen 1.4.0